mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-02-11 19:20:40 +08:00
As proposed, here is the current version of PL/pgSQL. The
test isn't that complete up to now, but I think it shows enough of the capabilities of the module. The Makefile assumes it is located in a directory under pgsql/src/pl. Since it includes Makefile.global and Makefile.port and doesn't use any own compiler/linker calls, it should build on most of our supported platforms (I only tested under Linux up to now). It requires flex and bison I think. Maybe we should ship prepared gram.c etc. like for the main parser too? Jan
This commit is contained in:
parent
212f7bdfe3
commit
863a62064c
@ -58,6 +58,10 @@ mSQL-interface -
|
||||
noupdate -
|
||||
trigger to prevent updates on single columns
|
||||
|
||||
plpgsql -
|
||||
Postgres procedural language
|
||||
by Jan Wieck <jwieck@debis.com>
|
||||
|
||||
pginterface -
|
||||
A crude C/4GL
|
||||
by Bruce Momjian <root@candle.pha.pa.us>
|
||||
|
448
contrib/plpgsql/doc/plpgsql.txt
Normal file
448
contrib/plpgsql/doc/plpgsql.txt
Normal file
@ -0,0 +1,448 @@
|
||||
PL/pgSQL
|
||||
A procedural language for the PostgreSQL RDBMS
|
||||
|
||||
Jan Wieck <jwieck@debis.com>
|
||||
|
||||
|
||||
|
||||
Preface
|
||||
|
||||
PL/pgSQL is a procedural language based on SQL designed for
|
||||
the PostgreSQL database system.
|
||||
|
||||
The extensibility features of PostgreSQL are mostly based on
|
||||
the ability to define functions for various operations.
|
||||
Functions could have been written in PostgreSQL's SQL dialect
|
||||
or in the C programming language. Functions written in C are
|
||||
compiled into a shared object and loaded by the database
|
||||
backend process on demand. Also the trigger features of
|
||||
PostgreSQL are based on functions but required the use of the
|
||||
C language.
|
||||
|
||||
Since version 6.3 PostgreSQL supports the definition of
|
||||
procedural languages. In the case of a function or trigger
|
||||
procedure defined in a procedural language, the database has
|
||||
no builtin knowlege how to interpret the functions source
|
||||
text. Instead, the function and trigger calls are passed into
|
||||
a handler that knows the details of the language. The
|
||||
handler itself is a function compiled into a shared object
|
||||
and loaded on demand.
|
||||
|
||||
|
||||
Overview
|
||||
|
||||
The PL/pgSQL language is case insensitive. All keywords and
|
||||
identifiers can be used in upper-/lowercase mixed.
|
||||
|
||||
PL/pgSQL is a block oriented language. A block is defined as
|
||||
|
||||
[<<label>>]
|
||||
[DECLARE
|
||||
-- declarations]
|
||||
BEGIN
|
||||
-- statements
|
||||
END;
|
||||
|
||||
There can be any number of subblocks in the statements
|
||||
section of a block. Subblocks can be used to hide variables
|
||||
from outside a block of statements (see Scope and visability
|
||||
below). 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.
|
||||
|
||||
It is important not to misunderstand the meaning of BEGIN/END
|
||||
for grouping statements in PL/pgSQL and the database commands
|
||||
for transaction control. Functions or trigger procedures
|
||||
cannot start or commit transactions and PostgreSQL
|
||||
transactions cannot have subtransactions.
|
||||
|
||||
|
||||
Comments
|
||||
|
||||
There are two types of comments in PL/pgSQL. A double dash
|
||||
'--' starts a comment that extends to the end of the line. A
|
||||
'/*' starts a block comment that extends to the next '*/'.
|
||||
Block comments cannot be nested, but double dash comments can
|
||||
be enclosed into a block comment and double dashes can hide
|
||||
'/*' and '*/'.
|
||||
|
||||
|
||||
Declarations
|
||||
|
||||
All variables, rows and records used in a block or it's
|
||||
subblocks must be declared in the declarations section of the
|
||||
block except for the loop variable of a FOR loop iterating
|
||||
over a range of integer values. The parameters given to the
|
||||
function are automatically declared with the usual
|
||||
identifiers $n. The declarations have the following syntax:
|
||||
|
||||
<name> [CONSTANT] <type> [NOT NULL]
|
||||
[DEFAULT | := <value>];
|
||||
|
||||
Declares a variable of the specified type. If the
|
||||
variable is declared as CONSTANT, the value cannot be
|
||||
changed. If NOT NULL is specified, an assignment of a
|
||||
NULL value results in a runtime error. Since the
|
||||
default value of a variable is the SQL NULL value,
|
||||
all variables declared as NOT NULL must also have a
|
||||
default value.
|
||||
|
||||
The default value is evaluated at the actual function
|
||||
call. So assigning 'now' to an abstime variable
|
||||
causes the variable to have the time of the actual
|
||||
function call, not when the function was compiled
|
||||
(during it's first call since the lifetime of the
|
||||
database connection).
|
||||
|
||||
<name> <class>%ROWTYPE;
|
||||
|
||||
Declares a row with the structure of the given class.
|
||||
Class must be an existing table- or viewname of the
|
||||
database. The fields of the row are accessed in the
|
||||
dot notation. Parameters to a procedure could be
|
||||
tuple types. In that case the corresponding
|
||||
identifier $n will be a rowtype. Only the user
|
||||
attributes of a tuple are accessible in the row.
|
||||
There must be no whitespaces between the classname,
|
||||
the percent and the ROWTYPE keyword.
|
||||
|
||||
The fields of the rowtype inherit the tables
|
||||
fieldsizes for char() etc. data types (atttypmod
|
||||
from pg_attribute).
|
||||
|
||||
<name> RECORD;
|
||||
|
||||
Records are similar to rowtypes, but they have no
|
||||
predefined structure They are used in selections and
|
||||
FOR loops to hold one actual database tuple from a
|
||||
select operation. One and the same record can be used
|
||||
in different selections. Accessing a record or an
|
||||
attempt to assign a value to a record field when
|
||||
there's no actual tuple in it results in a runtime
|
||||
error.
|
||||
|
||||
The new and old tuples in triggers are given to the
|
||||
trigger procedure as records. This is necessary,
|
||||
because under PostgreSQL one and the same trigger
|
||||
procedure can handle trigger events for different
|
||||
tables.
|
||||
|
||||
<name> ALIAS FOR $n;
|
||||
|
||||
For better readability of the code it's possible to
|
||||
define an alias for a positional parameter to the
|
||||
function.
|
||||
|
||||
RENAME <oldname> TO <newname>;
|
||||
|
||||
Change the name of a variable, record or rowtype.
|
||||
This is useful if new or old should be referenced by
|
||||
another name inside a trigger procedure.
|
||||
|
||||
Datatypes
|
||||
|
||||
The type of a variable can be any of the existing data types
|
||||
of the database. <type> above is defined as:
|
||||
|
||||
postgesql-basetype
|
||||
or variable%TYPE
|
||||
or rowtype.field%TYPE
|
||||
or class.field%TYPE
|
||||
|
||||
As for the rowtype declaration, there must be no whitespaces
|
||||
between the classname, the percent and the TYPE keyword.
|
||||
|
||||
Expressions
|
||||
|
||||
All expressions used in PL/pgSQL statements are processed
|
||||
using the backends executor. Since even a constant looking
|
||||
expression can have a totally different meaning for a
|
||||
particular data type (as 'now' for abstime), it is impossible
|
||||
for the PL/pgSQL parser to identify real constant values
|
||||
other than the NULL keyword. The expressions are evaluated by
|
||||
internally executing a query
|
||||
|
||||
SELECT <expr>
|
||||
|
||||
over the SPI manager. In the expression, occurences of
|
||||
variable identifiers are substituted by parameters and the
|
||||
actual values from the variables are passed to the executor
|
||||
as query parameters. All the expressions used in a PL/pgSQL
|
||||
function are only prepared and saved once.
|
||||
|
||||
If record fields are used in expressions or database
|
||||
statements, the data types of the fields should not change
|
||||
between calls of one and the same expression. Keep this in
|
||||
mind when writing trigger procedures that handle events for
|
||||
more than one table.
|
||||
|
||||
Statements
|
||||
|
||||
Anything not understood by the parser as specified below will
|
||||
be put into a query and sent down to the database engine to
|
||||
execute. The resulting query should not return any data
|
||||
(insert, update, delete queries and all utility statements).
|
||||
|
||||
Assignment
|
||||
|
||||
An assignment of a value to a variable or rowtype field
|
||||
is written as:
|
||||
|
||||
<identifier> := <expr>;
|
||||
|
||||
If the expressions result data type doesn't match the
|
||||
variables data type, or the variables atttypmod value is
|
||||
known (as for char(20)), the result value will be
|
||||
implicitly casted by the PL/pgSQL executor using the
|
||||
result types output- and the variables type input-
|
||||
functions. Note that this could potentially result in
|
||||
runtime errors generated by the types input functions.
|
||||
|
||||
An assignment of a complete selection into a record or
|
||||
rowtype can be done as:
|
||||
|
||||
SELECT expressions INTO <target> FROM fromlist;
|
||||
|
||||
Target can be a record or rowtype variable, or a comma
|
||||
separated list of variables and record/row fields.
|
||||
|
||||
If a rowtype 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 fromlist can be
|
||||
followed by any valid qualification, grouping, sorting
|
||||
etc.
|
||||
|
||||
There is a special condition [NOT] FOUND that can be used
|
||||
immediately after a SELECT INTO to check if the data has
|
||||
been found.
|
||||
|
||||
SELECT * INTO myrec FROM EMP WHERE empname = myname;
|
||||
IF NOT FOUND THEN
|
||||
RAISE EXCEPTION 'employee % not found', myname;
|
||||
END IF;
|
||||
|
||||
If the selection returns multiple rows, only the first is
|
||||
moved into the target fields. All others are discarded.
|
||||
|
||||
|
||||
Calling another function
|
||||
|
||||
If a function should be called, this is normally done by
|
||||
a SELECT query. But there are cases where someone isn't
|
||||
interested in the functions result.
|
||||
|
||||
PERFORM querystring;
|
||||
|
||||
executes a 'SELECT querystring' over the SPI manager and
|
||||
discards the result.
|
||||
|
||||
|
||||
Returning from the function
|
||||
|
||||
RETURN <expr>;
|
||||
|
||||
The function terminates and the value of <expr> will be
|
||||
returned to the upper executor. The return value of a
|
||||
function cannot be undefined. If control reaches the end
|
||||
of the toplevel block of the function without hitting a
|
||||
RETURN statement, a runtime error will occur.
|
||||
|
||||
|
||||
Aborting and messages
|
||||
|
||||
As indicated above there is an RAISE statement that can
|
||||
throw messages into the PostgreSQL elog mechanism.
|
||||
|
||||
RAISE level 'format' [, identifier [...]];
|
||||
|
||||
Inside the format, % can be used as a placeholder for the
|
||||
following, comma separated identifiers. The identifiers
|
||||
must specify an existing variable or row/record field.
|
||||
|
||||
|
||||
Conditionals
|
||||
|
||||
IF <expr> THEN
|
||||
-- statements
|
||||
[ELSE
|
||||
-- statements]
|
||||
END IF;
|
||||
|
||||
The expression <expr> must return a value that at least
|
||||
can be casted into a boolean.
|
||||
|
||||
|
||||
Loops
|
||||
|
||||
There are multiple types of loops.
|
||||
|
||||
[<<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
|
||||
nesting should be terminated.
|
||||
|
||||
[<<label>>]
|
||||
WHILE <expr> LOOP
|
||||
-- statements
|
||||
END LOOP;
|
||||
|
||||
A conditional loop that is executed as long as the
|
||||
evaluation of <expr> returns true.
|
||||
|
||||
[<<label>>]
|
||||
FOR <name> IN [REVERSE] <expr>..<expr> 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 1.
|
||||
|
||||
FOR <recname|rowname> IN <select_clause> LOOP
|
||||
-- statements
|
||||
END LOOP;
|
||||
|
||||
The record or row is assigned all the rows resulting from
|
||||
the select clause and the statements executed for each.
|
||||
If the loop is terminated with an EXIT statement, the
|
||||
last accessed row is still accessible in the record or
|
||||
rowtype.
|
||||
|
||||
EXIT [label] [WHEN <expr>];
|
||||
|
||||
If no label given, the innermost loop is terminated and
|
||||
the statement following END LOOP is executed next. If
|
||||
label is given, it must be the label of the current or an
|
||||
upper level of nested loops or blocks. Then the named
|
||||
loop or block is terminated and control continues with
|
||||
the statement after the loops/blocks corresponding END.
|
||||
|
||||
Trigger procedures
|
||||
|
||||
PL/pgSQL can also be used to define trigger procedures. They
|
||||
are created using CREATE FUNCTION as a function with no
|
||||
arguments and a return type of opaque.
|
||||
|
||||
There are some PostgreSQL specific details in functions used
|
||||
as trigger procedures.
|
||||
|
||||
First they have some special variables created above the
|
||||
toplevel statement block. These are:
|
||||
|
||||
new (record)
|
||||
The new database tuple on INSERT/UPDATE operations at
|
||||
ROW level.
|
||||
|
||||
old (record)
|
||||
The old database tuple on UPDATE/DELETE operations at
|
||||
ROW level.
|
||||
|
||||
tg_name (type name)
|
||||
The triggers name from pg_trigger.
|
||||
|
||||
tg_when (type text)
|
||||
A string of either 'BEFORE' or 'AFTER' depending on
|
||||
the triggers definition.
|
||||
|
||||
tg_level (type text)
|
||||
A string of either 'ROW' or 'STATEMENT' depending on
|
||||
the triggers definition.
|
||||
|
||||
tg_op (type text)
|
||||
A string of 'INSERT', 'UPDATE' or 'DELETE' telling
|
||||
for which operation the trigger is actually fired.
|
||||
|
||||
tg_relid (type oid)
|
||||
The Oid of the relation for which the trigger is
|
||||
actually fired.
|
||||
|
||||
tg_relname (type name)
|
||||
The relations name for which the trigger is actually
|
||||
fired.
|
||||
|
||||
tg_nargs (type integer)
|
||||
The number of arguments given to the trigger
|
||||
procedure in the CREATE TRIGGER statement.
|
||||
|
||||
tg_argv[] (types text)
|
||||
The arguments from the CREATE TRIGGER statement. The
|
||||
index counts from 0 and can be given as expression.
|
||||
Invalid indices (< 0 or >= tg_nargs) result in a NULL
|
||||
value.
|
||||
|
||||
Second, they must return either NULL, or a record/rowtype
|
||||
containing exactly the structure of the table the trigger was
|
||||
fired for. Triggers fired AFTER might allways return NULL
|
||||
with no effect. Triggers fired BEFORE signal the trigger
|
||||
manager to skip the operation for this actual row when
|
||||
returning NULL. Otherwise, the returned record/rowtype
|
||||
replaces the inserted/updated tuple in the operation. It is
|
||||
possible to replace single values directly in new and return
|
||||
that, or to build a complete new record/rowtype to return.
|
||||
|
||||
Exceptions
|
||||
|
||||
PostgreSQL doesn't have a very smart exception handling
|
||||
model. Whenever the parser, planner/optimizer or executor
|
||||
decide that a statement cannot be processed any longer, the
|
||||
whole transaction gets aborted and the the system jumps back
|
||||
into the mainloop using longjmp() to get the next query from
|
||||
the client application.
|
||||
|
||||
It is possible to hook into the longjmp() mechanism to notice
|
||||
that this happens. But currently it's impossible to tell what
|
||||
really caused the abort (input/output conversion error,
|
||||
floating point error, parse error) And it's possible that the
|
||||
backend is in an inconsistent state at this point so
|
||||
returning to the upper executor or issuing more commands
|
||||
might corrupt the whole database.
|
||||
|
||||
Thus, the only thing PL/pgSQL currently does when it
|
||||
encounters an abort during execution of a function or trigger
|
||||
procedure is to write some additional DEBUG log messages
|
||||
telling in which function and where (line number and type of
|
||||
statement) this happened.
|
||||
|
||||
This might change in the future.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
22
contrib/plpgsql/src/INSTALL
Normal file
22
contrib/plpgsql/src/INSTALL
Normal file
@ -0,0 +1,22 @@
|
||||
Installation of PL/pgSQL
|
||||
|
||||
|
||||
1) Type 'make' to build the shared plpgsql object.
|
||||
|
||||
2) Type 'make install' to install the shared object in
|
||||
the PostgreSQL library directory.
|
||||
|
||||
3) Declare the PL/pgSQL procedural language in your
|
||||
database by
|
||||
|
||||
psql dbname <mklang.sql
|
||||
|
||||
If the PostgreSQL library directory is different from
|
||||
/usr/local/pgsql/lib you must edit mklang.sql prior.
|
||||
|
||||
If you declare the language in the template1 database,
|
||||
any subsequently created database will have PL/pgSQL
|
||||
support installed automatically.
|
||||
|
||||
|
||||
|
1460
contrib/plpgsql/src/gram.y
Normal file
1460
contrib/plpgsql/src/gram.y
Normal file
File diff suppressed because it is too large
Load Diff
14
contrib/plpgsql/src/mklang.sql
Normal file
14
contrib/plpgsql/src/mklang.sql
Normal file
@ -0,0 +1,14 @@
|
||||
--
|
||||
-- PL/pgSQL language declaration
|
||||
--
|
||||
-- $Header: /cvsroot/pgsql/contrib/plpgsql/src/Attic/mklang.sql,v 1.1 1998/08/22 12:38:31 momjian Exp $
|
||||
--
|
||||
|
||||
create function plpgsql_call_handler() returns opaque
|
||||
as '/usr/local/pgsql/lib/plpgsql.so'
|
||||
language 'C';
|
||||
|
||||
create trusted procedural language 'plpgsql'
|
||||
handler plpgsql_call_handler
|
||||
lancompiler 'PL/pgSQL';
|
||||
|
1313
contrib/plpgsql/src/pl_comp.c
Normal file
1313
contrib/plpgsql/src/pl_comp.c
Normal file
File diff suppressed because it is too large
Load Diff
2241
contrib/plpgsql/src/pl_exec.c
Normal file
2241
contrib/plpgsql/src/pl_exec.c
Normal file
File diff suppressed because it is too large
Load Diff
675
contrib/plpgsql/src/pl_funcs.c
Normal file
675
contrib/plpgsql/src/pl_funcs.c
Normal file
@ -0,0 +1,675 @@
|
||||
/**********************************************************************
|
||||
* pl_funcs.c - Misc functins for the PL/pgSQL
|
||||
* procedural language
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/contrib/plpgsql/src/Attic/pl_funcs.c,v 1.1 1998/08/22 12:38:32 momjian Exp $
|
||||
*
|
||||
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||
*
|
||||
* The author hereby grants permission to use, copy, modify,
|
||||
* distribute, and license this software and its documentation
|
||||
* for any purpose, provided that existing copyright notices are
|
||||
* retained in all copies and that this notice is included
|
||||
* verbatim in any distributions. No written agreement, license,
|
||||
* or royalty fee is required for any of the authorized uses.
|
||||
* Modifications to this software may be copyrighted by their
|
||||
* author and need not follow the licensing terms described
|
||||
* here, provided that the new terms are clearly indicated on
|
||||
* the first page of each file where they apply.
|
||||
*
|
||||
* IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTORS BE LIABLE TO ANY
|
||||
* PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR
|
||||
* CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS
|
||||
* SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN
|
||||
* IF THE AUTHOR HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*
|
||||
* THE AUTHOR AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON
|
||||
* AN "AS IS" BASIS, AND THE AUTHOR AND DISTRIBUTORS HAVE NO
|
||||
* OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
|
||||
* ENHANCEMENTS, OR MODIFICATIONS.
|
||||
*
|
||||
**********************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "plpgsql.h"
|
||||
#include "pl.tab.h"
|
||||
|
||||
|
||||
/* ----------
|
||||
* Local variables for the namestack handling
|
||||
* ----------
|
||||
*/
|
||||
static PLpgSQL_ns *ns_current = NULL;
|
||||
static bool ns_localmode = false;
|
||||
|
||||
|
||||
/* ----------
|
||||
* plpgsql_dstring_init Dynamic string initialization
|
||||
* ----------
|
||||
*/
|
||||
void plpgsql_dstring_init(PLpgSQL_dstring *ds)
|
||||
{
|
||||
ds->value = palloc(ds->alloc = 512);
|
||||
ds->used = 0;
|
||||
}
|
||||
|
||||
|
||||
/* ----------
|
||||
* plpgsql_dstring_free Dynamic string destruction
|
||||
* ----------
|
||||
*/
|
||||
void plpgsql_dstring_free(PLpgSQL_dstring *ds)
|
||||
{
|
||||
pfree(ds->value);
|
||||
}
|
||||
|
||||
|
||||
/* ----------
|
||||
* plpgsql_dstring_append Dynamic string extending
|
||||
* ----------
|
||||
*/
|
||||
void plpgsql_dstring_append(PLpgSQL_dstring *ds, char *str)
|
||||
{
|
||||
int len = strlen(str);
|
||||
|
||||
if (ds->used + len + 1 > ds->alloc) {
|
||||
ds->alloc *= 2;
|
||||
ds->value = repalloc(ds->value, ds->alloc);
|
||||
}
|
||||
|
||||
strcpy(&(ds->value[ds->used]), str);
|
||||
ds->used += len;
|
||||
}
|
||||
|
||||
|
||||
/* ----------
|
||||
* plpgsql_dstring_get Dynamic string get value
|
||||
* ----------
|
||||
*/
|
||||
char *plpgsql_dstring_get(PLpgSQL_dstring *ds)
|
||||
{
|
||||
return ds->value;
|
||||
}
|
||||
|
||||
|
||||
/* ----------
|
||||
* plpgsql_ns_init Initialize the namestack
|
||||
* ----------
|
||||
*/
|
||||
void plpgsql_ns_init(void)
|
||||
{
|
||||
ns_current = NULL;
|
||||
ns_localmode = false;
|
||||
}
|
||||
|
||||
|
||||
/* ----------
|
||||
* plpgsql_ns_setlocal Tell plpgsql_ns_lookup to or to
|
||||
* not look into the current level
|
||||
* only.
|
||||
* ----------
|
||||
*/
|
||||
bool plpgsql_ns_setlocal(bool flag)
|
||||
{
|
||||
bool oldstate;
|
||||
|
||||
oldstate = ns_localmode;
|
||||
ns_localmode = flag;
|
||||
return oldstate;
|
||||
}
|
||||
|
||||
|
||||
/* ----------
|
||||
* plpgsql_ns_push Enter a new namestack level
|
||||
* ----------
|
||||
*/
|
||||
void plpgsql_ns_push(char *label)
|
||||
{
|
||||
PLpgSQL_ns *new;
|
||||
|
||||
new = palloc(sizeof(PLpgSQL_ns));
|
||||
memset(new, 0, sizeof(PLpgSQL_ns));
|
||||
new->upper = ns_current;
|
||||
ns_current = new;
|
||||
|
||||
plpgsql_ns_additem(PLPGSQL_NSTYPE_LABEL, 0, label);
|
||||
}
|
||||
|
||||
|
||||
/* ----------
|
||||
* plpgsql_ns_pop Return to the previous level
|
||||
* ----------
|
||||
*/
|
||||
void plpgsql_ns_pop()
|
||||
{
|
||||
int i;
|
||||
PLpgSQL_ns *old;
|
||||
|
||||
old = ns_current;
|
||||
ns_current = old->upper;
|
||||
|
||||
for (i = 0; i < old->items_used; i++) {
|
||||
pfree(old->items[i]);
|
||||
}
|
||||
pfree(old->items);
|
||||
pfree(old);
|
||||
}
|
||||
|
||||
|
||||
/* ----------
|
||||
* plpgsql_ns_additem Add an item to the current
|
||||
* namestack level
|
||||
* ----------
|
||||
*/
|
||||
void plpgsql_ns_additem(int itemtype, int itemno, char *name)
|
||||
{
|
||||
PLpgSQL_ns *ns = ns_current;
|
||||
PLpgSQL_nsitem *nse;
|
||||
|
||||
if (name == NULL)
|
||||
name = "";
|
||||
|
||||
if (ns->items_used == ns->items_alloc) {
|
||||
if (ns->items_alloc == 0) {
|
||||
ns->items_alloc = 32;
|
||||
ns->items = palloc(sizeof(PLpgSQL_nsitem *) * ns->items_alloc);
|
||||
} else {
|
||||
ns->items_alloc *= 2;
|
||||
ns->items = repalloc(ns->items,
|
||||
sizeof(PLpgSQL_nsitem *) * ns->items_alloc);
|
||||
}
|
||||
}
|
||||
|
||||
nse = palloc(sizeof(PLpgSQL_nsitem) + strlen(name));
|
||||
nse->itemtype = itemtype;
|
||||
nse->itemno = itemno;
|
||||
strcpy(nse->name, name);
|
||||
ns->items[ns->items_used++] = nse;
|
||||
}
|
||||
|
||||
|
||||
/* ----------
|
||||
* plpgsql_ns_lookup Lookup for a word in the namestack
|
||||
* ----------
|
||||
*/
|
||||
PLpgSQL_nsitem *plpgsql_ns_lookup(char *name, char *label)
|
||||
{
|
||||
PLpgSQL_ns *ns;
|
||||
int i;
|
||||
|
||||
/* ----------
|
||||
* If a label is specified, lookup only in that
|
||||
* ----------
|
||||
*/
|
||||
if (label != NULL) {
|
||||
for (ns = ns_current; ns != NULL; ns = ns->upper) {
|
||||
if (!strcmp(ns->items[0]->name, label)) {
|
||||
for (i = 1; i < ns->items_used; i++) {
|
||||
if (!strcmp(ns->items[i]->name, name)) {
|
||||
return ns->items[i];
|
||||
}
|
||||
}
|
||||
return NULL; /* name not found in specified label */
|
||||
}
|
||||
}
|
||||
return NULL; /* label not found */
|
||||
}
|
||||
|
||||
/* ----------
|
||||
* No label given, lookup for visible labels ignoring localmode
|
||||
* ----------
|
||||
*/
|
||||
for (ns = ns_current; ns != NULL; ns = ns->upper) {
|
||||
if (!strcmp(ns->items[0]->name, name)) {
|
||||
return ns->items[0];
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------
|
||||
* Finally lookup name in the namestack
|
||||
* ----------
|
||||
*/
|
||||
for (ns = ns_current; ns != NULL; ns = ns->upper) {
|
||||
for (i = 1; i < ns->items_used; i++) {
|
||||
if (!strcmp(ns->items[i]->name, name))
|
||||
return ns->items[i];
|
||||
}
|
||||
if (ns_localmode) {
|
||||
return NULL; /* name not found in current namespace */
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* ----------
|
||||
* plpgsql_ns_rename Rename a namespace entry
|
||||
* ----------
|
||||
*/
|
||||
void plpgsql_ns_rename(char *oldname, char *newname)
|
||||
{
|
||||
PLpgSQL_ns *ns;
|
||||
PLpgSQL_nsitem *newitem;
|
||||
int i;
|
||||
|
||||
/* ----------
|
||||
* Lookup in the current namespace only
|
||||
* ----------
|
||||
*/
|
||||
/* ----------
|
||||
* Lookup name in the namestack
|
||||
* ----------
|
||||
*/
|
||||
for (ns = ns_current; ns != NULL; ns = ns->upper) {
|
||||
for (i = 1; i < ns->items_used; i++) {
|
||||
if (!strcmp(ns->items[i]->name, oldname)) {
|
||||
newitem = palloc(sizeof(PLpgSQL_nsitem) + strlen(newname));
|
||||
newitem->itemtype = ns->items[i]->itemtype;
|
||||
newitem->itemno = ns->items[i]->itemno;
|
||||
strcpy(newitem->name, newname);
|
||||
|
||||
pfree(oldname);
|
||||
pfree(newname);
|
||||
|
||||
pfree(ns->items[i]);
|
||||
ns->items[i] = newitem;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
elog(ERROR, "there is no variable '%s' in the current block", oldname);
|
||||
}
|
||||
|
||||
|
||||
/* ----------
|
||||
* plpgsql_tolower Translate a string in place to
|
||||
* lower case
|
||||
* ----------
|
||||
*/
|
||||
char *plpgsql_tolower(char *s)
|
||||
{
|
||||
char *cp;
|
||||
|
||||
for (cp = s; *cp; cp++) {
|
||||
if (isupper(*cp)) *cp = tolower(*cp);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* Debug functions for analyzing the compiled code
|
||||
**********************************************************************/
|
||||
static int dump_indent;
|
||||
|
||||
static void dump_ind();
|
||||
static void dump_stmt(PLpgSQL_stmt *stmt);
|
||||
static void dump_block(PLpgSQL_stmt_block *block);
|
||||
static void dump_assign(PLpgSQL_stmt_assign *stmt);
|
||||
static void dump_if(PLpgSQL_stmt_if *stmt);
|
||||
static void dump_loop(PLpgSQL_stmt_loop *stmt);
|
||||
static void dump_while(PLpgSQL_stmt_while *stmt);
|
||||
static void dump_fori(PLpgSQL_stmt_fori *stmt);
|
||||
static void dump_fors(PLpgSQL_stmt_fors *stmt);
|
||||
static void dump_select(PLpgSQL_stmt_select *stmt);
|
||||
static void dump_exit(PLpgSQL_stmt_exit *stmt);
|
||||
static void dump_return(PLpgSQL_stmt_return *stmt);
|
||||
static void dump_raise(PLpgSQL_stmt_raise *stmt);
|
||||
static void dump_execsql(PLpgSQL_stmt_execsql *stmt);
|
||||
static void dump_expr(PLpgSQL_expr *expr);
|
||||
|
||||
|
||||
static void dump_ind()
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < dump_indent; i++) {
|
||||
printf(" ");
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_stmt(PLpgSQL_stmt *stmt)
|
||||
{
|
||||
printf("%3d:", stmt->lineno);
|
||||
switch (stmt->cmd_type) {
|
||||
case PLPGSQL_STMT_BLOCK:
|
||||
dump_block((PLpgSQL_stmt_block *)stmt);
|
||||
break;
|
||||
case PLPGSQL_STMT_ASSIGN:
|
||||
dump_assign((PLpgSQL_stmt_assign *)stmt);
|
||||
break;
|
||||
case PLPGSQL_STMT_IF:
|
||||
dump_if((PLpgSQL_stmt_if *)stmt);
|
||||
break;
|
||||
case PLPGSQL_STMT_LOOP:
|
||||
dump_loop((PLpgSQL_stmt_loop *)stmt);
|
||||
break;
|
||||
case PLPGSQL_STMT_WHILE:
|
||||
dump_while((PLpgSQL_stmt_while *)stmt);
|
||||
break;
|
||||
case PLPGSQL_STMT_FORI:
|
||||
dump_fori((PLpgSQL_stmt_fori *)stmt);
|
||||
break;
|
||||
case PLPGSQL_STMT_FORS:
|
||||
dump_fors((PLpgSQL_stmt_fors *)stmt);
|
||||
break;
|
||||
case PLPGSQL_STMT_SELECT:
|
||||
dump_select((PLpgSQL_stmt_select *)stmt);
|
||||
break;
|
||||
case PLPGSQL_STMT_EXIT:
|
||||
dump_exit((PLpgSQL_stmt_exit *)stmt);
|
||||
break;
|
||||
case PLPGSQL_STMT_RETURN:
|
||||
dump_return((PLpgSQL_stmt_return *)stmt);
|
||||
break;
|
||||
case PLPGSQL_STMT_RAISE:
|
||||
dump_raise((PLpgSQL_stmt_raise *)stmt);
|
||||
break;
|
||||
case PLPGSQL_STMT_EXECSQL:
|
||||
dump_execsql((PLpgSQL_stmt_execsql *)stmt);
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "plpgsql_dump: unknown cmd_type %d\n", stmt->cmd_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_block(PLpgSQL_stmt_block *block)
|
||||
{
|
||||
int i;
|
||||
char *name;
|
||||
|
||||
if (block->label == NULL) {
|
||||
name = "*unnamed*";
|
||||
} else {
|
||||
name = block->label;
|
||||
}
|
||||
|
||||
dump_ind();
|
||||
printf("BLOCK <<%s>>\n", name);
|
||||
|
||||
dump_indent += 2;
|
||||
for (i = 0; i < block->body->stmts_used; i++) {
|
||||
dump_stmt((PLpgSQL_stmt *)(block->body->stmts[i]));
|
||||
}
|
||||
dump_indent -= 2;
|
||||
|
||||
dump_ind();
|
||||
printf(" END -- %s\n", name);
|
||||
}
|
||||
|
||||
static void dump_assign(PLpgSQL_stmt_assign *stmt)
|
||||
{
|
||||
dump_ind();
|
||||
printf("ASSIGN var %d := ", stmt->varno);
|
||||
dump_expr(stmt->expr);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void dump_if(PLpgSQL_stmt_if *stmt)
|
||||
{
|
||||
int i;
|
||||
|
||||
dump_ind();
|
||||
printf("IF ");
|
||||
dump_expr(stmt->cond);
|
||||
printf(" THEN\n");
|
||||
|
||||
dump_indent += 2;
|
||||
for (i = 0; i < stmt->true_body->stmts_used; i++) {
|
||||
dump_stmt((PLpgSQL_stmt *)(stmt->true_body->stmts[i]));
|
||||
}
|
||||
dump_indent -= 2;
|
||||
|
||||
dump_ind();
|
||||
printf(" ELSE\n");
|
||||
|
||||
dump_indent += 2;
|
||||
for (i = 0; i < stmt->false_body->stmts_used; i++) {
|
||||
dump_stmt((PLpgSQL_stmt *)(stmt->false_body->stmts[i]));
|
||||
}
|
||||
dump_indent -= 2;
|
||||
|
||||
dump_ind();
|
||||
printf(" ENDIF\n");
|
||||
}
|
||||
|
||||
static void dump_loop(PLpgSQL_stmt_loop *stmt)
|
||||
{
|
||||
int i;
|
||||
|
||||
dump_ind();
|
||||
printf("LOOP\n");
|
||||
|
||||
dump_indent += 2;
|
||||
for (i = 0; i < stmt->body->stmts_used; i++) {
|
||||
dump_stmt((PLpgSQL_stmt *)(stmt->body->stmts[i]));
|
||||
}
|
||||
dump_indent -= 2;
|
||||
|
||||
dump_ind();
|
||||
printf(" ENDLOOP\n");
|
||||
}
|
||||
|
||||
static void dump_while(PLpgSQL_stmt_while *stmt)
|
||||
{
|
||||
int i;
|
||||
|
||||
dump_ind();
|
||||
printf("WHILE ");
|
||||
dump_expr(stmt->cond);
|
||||
printf("\n");
|
||||
|
||||
dump_indent += 2;
|
||||
for (i = 0; i < stmt->body->stmts_used; i++) {
|
||||
dump_stmt((PLpgSQL_stmt *)(stmt->body->stmts[i]));
|
||||
}
|
||||
dump_indent -= 2;
|
||||
|
||||
dump_ind();
|
||||
printf(" ENDWHILE\n");
|
||||
}
|
||||
|
||||
static void dump_fori(PLpgSQL_stmt_fori *stmt)
|
||||
{
|
||||
int i;
|
||||
|
||||
dump_ind();
|
||||
printf("FORI %s %s\n", stmt->var->refname, (stmt->reverse) ? "REVERSE" : "NORMAL");
|
||||
|
||||
dump_indent += 2;
|
||||
dump_ind();
|
||||
printf(" lower = ");
|
||||
dump_expr(stmt->lower);
|
||||
printf("\n");
|
||||
dump_ind();
|
||||
printf(" upper = ");
|
||||
dump_expr(stmt->upper);
|
||||
printf("\n");
|
||||
|
||||
for (i = 0; i < stmt->body->stmts_used; i++) {
|
||||
dump_stmt((PLpgSQL_stmt *)(stmt->body->stmts[i]));
|
||||
}
|
||||
dump_indent -= 2;
|
||||
|
||||
dump_ind();
|
||||
printf(" ENDFORI\n");
|
||||
}
|
||||
|
||||
static void dump_fors(PLpgSQL_stmt_fors *stmt)
|
||||
{
|
||||
int i;
|
||||
|
||||
dump_ind();
|
||||
printf("FORS %s ", (stmt->rec != NULL) ? stmt->rec->refname : stmt->row->refname);
|
||||
dump_expr(stmt->query);
|
||||
printf("\n");
|
||||
|
||||
dump_indent += 2;
|
||||
for (i = 0; i < stmt->body->stmts_used; i++) {
|
||||
dump_stmt((PLpgSQL_stmt *)(stmt->body->stmts[i]));
|
||||
}
|
||||
dump_indent -= 2;
|
||||
|
||||
dump_ind();
|
||||
printf(" ENDFORS\n");
|
||||
}
|
||||
|
||||
static void dump_select(PLpgSQL_stmt_select *stmt)
|
||||
{
|
||||
dump_ind();
|
||||
printf("SELECT ");
|
||||
dump_expr(stmt->query);
|
||||
printf("\n");
|
||||
|
||||
dump_indent += 2;
|
||||
if (stmt->rec != NULL) {
|
||||
dump_ind();
|
||||
printf(" target = %d %s\n", stmt->rec->recno, stmt->rec->refname);
|
||||
}
|
||||
if (stmt->row != NULL) {
|
||||
dump_ind();
|
||||
printf(" target = %d %s\n", stmt->row->rowno, stmt->row->refname);
|
||||
}
|
||||
dump_indent -= 2;
|
||||
|
||||
}
|
||||
|
||||
static void dump_exit(PLpgSQL_stmt_exit *stmt)
|
||||
{
|
||||
dump_ind();
|
||||
printf("EXIT lbl='%s'", stmt->label);
|
||||
if (stmt->cond != NULL) {
|
||||
printf(" WHEN ");
|
||||
dump_expr(stmt->cond);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void dump_return(PLpgSQL_stmt_return *stmt)
|
||||
{
|
||||
dump_ind();
|
||||
printf("RETURN ");
|
||||
if (stmt->retrecno >= 0) {
|
||||
printf("record %d", stmt->retrecno);
|
||||
} else {
|
||||
if (stmt->expr == NULL) {
|
||||
printf("NULL");
|
||||
} else {
|
||||
dump_expr(stmt->expr);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void dump_raise(PLpgSQL_stmt_raise *stmt)
|
||||
{
|
||||
int i;
|
||||
|
||||
dump_ind();
|
||||
printf("RAISE '%s'", stmt->message);
|
||||
for (i = 0; i < stmt->nparams; i++) {
|
||||
printf(" %d", stmt->params[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void dump_execsql(PLpgSQL_stmt_execsql *stmt)
|
||||
{
|
||||
dump_ind();
|
||||
printf("EXECSQL ");
|
||||
dump_expr(stmt->sqlstmt);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void dump_expr(PLpgSQL_expr *expr)
|
||||
{
|
||||
int i;
|
||||
printf("'%s", expr->query);
|
||||
if (expr->nparams > 0) {
|
||||
printf(" {");
|
||||
for(i = 0; i < expr->nparams; i++) {
|
||||
if (i > 0) printf(", ");
|
||||
printf("$%d=%d", i+1, expr->params[i]);
|
||||
}
|
||||
printf("}");
|
||||
}
|
||||
printf("'");
|
||||
}
|
||||
|
||||
void plpgsql_dumptree(PLpgSQL_function *func)
|
||||
{
|
||||
int i;
|
||||
PLpgSQL_datum *d;
|
||||
|
||||
printf("\nExecution tree of successfully compiled PL/pgSQL function %s:\n",
|
||||
func->fn_name);
|
||||
|
||||
printf("\nFunctions data area:\n");
|
||||
for (i = 0; i < func->ndatums; i++) {
|
||||
d = func->datums[i];
|
||||
|
||||
printf(" entry %d: ", i);
|
||||
switch (d->dtype) {
|
||||
case PLPGSQL_DTYPE_VAR:
|
||||
{
|
||||
PLpgSQL_var *var = (PLpgSQL_var *)d;
|
||||
printf("VAR %-16s type %s (typoid %d) atttypmod %d\n",
|
||||
var->refname, var->datatype->typname,
|
||||
var->datatype->typoid,
|
||||
var->datatype->atttypmod);
|
||||
}
|
||||
break;
|
||||
case PLPGSQL_DTYPE_ROW:
|
||||
{
|
||||
PLpgSQL_row *row = (PLpgSQL_row *)d;
|
||||
int i;
|
||||
printf("ROW %-16s fields", row->refname);
|
||||
for (i = 0; i < row->nfields; i++) {
|
||||
printf(" %s=var %d", row->fieldnames[i],
|
||||
row->varnos[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
break;
|
||||
case PLPGSQL_DTYPE_REC:
|
||||
printf("REC %s\n", ((PLpgSQL_rec *)d)->refname);
|
||||
break;
|
||||
case PLPGSQL_DTYPE_RECFIELD:
|
||||
printf("RECFIELD %-16s of REC %d\n", ((PLpgSQL_recfield *)d)->fieldname, ((PLpgSQL_recfield *)d)->recno);
|
||||
break;
|
||||
case PLPGSQL_DTYPE_TRIGARG:
|
||||
printf("TRIGARG ");
|
||||
dump_expr(((PLpgSQL_trigarg *)d)->argnum);
|
||||
printf("\n");
|
||||
break;
|
||||
default:
|
||||
printf("??? unknown data type %d\n", d->dtype);
|
||||
}
|
||||
}
|
||||
printf("\nFunctions statements:\n");
|
||||
|
||||
dump_indent = 0;
|
||||
printf("%3d:", func->action->lineno);
|
||||
dump_block(func->action);
|
||||
printf("\nEnd of execution tree of function %s\n\n", func->fn_name);
|
||||
}
|
||||
|
||||
|
193
contrib/plpgsql/src/pl_handler.c
Normal file
193
contrib/plpgsql/src/pl_handler.c
Normal file
@ -0,0 +1,193 @@
|
||||
/**********************************************************************
|
||||
* pl_handler.c - Handler for the PL/pgSQL
|
||||
* procedural language
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/contrib/plpgsql/src/Attic/pl_handler.c,v 1.1 1998/08/22 12:38:32 momjian Exp $
|
||||
*
|
||||
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||
*
|
||||
* The author hereby grants permission to use, copy, modify,
|
||||
* distribute, and license this software and its documentation
|
||||
* for any purpose, provided that existing copyright notices are
|
||||
* retained in all copies and that this notice is included
|
||||
* verbatim in any distributions. No written agreement, license,
|
||||
* or royalty fee is required for any of the authorized uses.
|
||||
* Modifications to this software may be copyrighted by their
|
||||
* author and need not follow the licensing terms described
|
||||
* here, provided that the new terms are clearly indicated on
|
||||
* the first page of each file where they apply.
|
||||
*
|
||||
* IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTORS BE LIABLE TO ANY
|
||||
* PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR
|
||||
* CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS
|
||||
* SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN
|
||||
* IF THE AUTHOR HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*
|
||||
* THE AUTHOR AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON
|
||||
* AN "AS IS" BASIS, AND THE AUTHOR AND DISTRIBUTORS HAVE NO
|
||||
* OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
|
||||
* ENHANCEMENTS, OR MODIFICATIONS.
|
||||
*
|
||||
**********************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "plpgsql.h"
|
||||
#include "pl.tab.h"
|
||||
|
||||
#include "executor/spi.h"
|
||||
#include "commands/trigger.h"
|
||||
#include "utils/elog.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "fmgr.h"
|
||||
#include "access/heapam.h"
|
||||
|
||||
#include "utils/syscache.h"
|
||||
#include "catalog/pg_proc.h"
|
||||
#include "catalog/pg_type.h"
|
||||
|
||||
|
||||
static PLpgSQL_function *compiled_functions = NULL;
|
||||
|
||||
|
||||
Datum plpgsql_call_handler(FmgrInfo *proinfo,
|
||||
FmgrValues *proargs, bool *isNull);
|
||||
|
||||
static Datum plpgsql_func_handler(FmgrInfo *proinfo,
|
||||
FmgrValues *proargs, bool *isNull);
|
||||
|
||||
static HeapTuple plpgsql_trigger_handler(FmgrInfo *proinfo);
|
||||
|
||||
|
||||
/* ----------
|
||||
* plpgsql_call_handler - This is the only visible function
|
||||
* of the PL interpreter. The PostgreSQL
|
||||
* function manager and trigger manager
|
||||
* call this function for execution of
|
||||
* PL/pgSQL procedures.
|
||||
* ----------
|
||||
*/
|
||||
Datum
|
||||
plpgsql_call_handler(FmgrInfo *proinfo,
|
||||
FmgrValues *proargs,
|
||||
bool *isNull)
|
||||
{
|
||||
Datum retval;
|
||||
|
||||
/* ----------
|
||||
* Connect to SPI manager
|
||||
* ----------
|
||||
*/
|
||||
if (SPI_connect() != SPI_OK_CONNECT) {
|
||||
elog(ERROR, "plpgsql: cannot connect to SPI manager");
|
||||
}
|
||||
|
||||
/* ----------
|
||||
* Determine if called as function or trigger and
|
||||
* call appropriate subhandler
|
||||
* ----------
|
||||
*/
|
||||
if (CurrentTriggerData == NULL) {
|
||||
retval = plpgsql_func_handler(proinfo, proargs, isNull);
|
||||
} else {
|
||||
retval = (Datum)plpgsql_trigger_handler(proinfo);
|
||||
}
|
||||
|
||||
/* ----------
|
||||
* Disconnect from SPI manager
|
||||
* ----------
|
||||
*/
|
||||
if (SPI_finish() != SPI_OK_FINISH) {
|
||||
elog(ERROR, "plpgsql: SPI_finish() failed");
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/* ----------
|
||||
* plpgsql_func_handler() - Handler for regular function calls
|
||||
* ----------
|
||||
*/
|
||||
static Datum
|
||||
plpgsql_func_handler(FmgrInfo *proinfo,
|
||||
FmgrValues *proargs,
|
||||
bool *isNull)
|
||||
{
|
||||
PLpgSQL_function *func;
|
||||
|
||||
/* ----------
|
||||
* Check if we already compiled this function
|
||||
* ----------
|
||||
*/
|
||||
for (func = compiled_functions; func != NULL; func = func->next) {
|
||||
if (proinfo->fn_oid == func->fn_oid)
|
||||
break;
|
||||
}
|
||||
|
||||
/* ----------
|
||||
* If not, do so and add it to the compiled ones
|
||||
* ----------
|
||||
*/
|
||||
if (func == NULL) {
|
||||
func = plpgsql_compile(proinfo->fn_oid, T_FUNCTION);
|
||||
|
||||
func->next = compiled_functions;
|
||||
compiled_functions = func;
|
||||
}
|
||||
|
||||
return plpgsql_exec_function(func, proargs, isNull);
|
||||
}
|
||||
|
||||
|
||||
/* ----------
|
||||
* plpgsql_trigger_handler() - Handler for trigger calls
|
||||
* ----------
|
||||
*/
|
||||
static HeapTuple
|
||||
plpgsql_trigger_handler(FmgrInfo *proinfo)
|
||||
{
|
||||
TriggerData *trigdata;
|
||||
PLpgSQL_function *func;
|
||||
|
||||
/* ----------
|
||||
* Save the current trigger data local
|
||||
* ----------
|
||||
*/
|
||||
trigdata = CurrentTriggerData;
|
||||
CurrentTriggerData = NULL;
|
||||
|
||||
/* ----------
|
||||
* Check if we already compiled this trigger procedure
|
||||
* ----------
|
||||
*/
|
||||
for (func = compiled_functions; func != NULL; func = func->next) {
|
||||
if (proinfo->fn_oid == func->fn_oid)
|
||||
break;
|
||||
}
|
||||
|
||||
/* ----------
|
||||
* If not, do so and add it to the compiled ones
|
||||
* ----------
|
||||
*/
|
||||
if (func == NULL) {
|
||||
func = plpgsql_compile(proinfo->fn_oid, T_TRIGGER);
|
||||
|
||||
func->next = compiled_functions;
|
||||
compiled_functions = func;
|
||||
}
|
||||
|
||||
return plpgsql_exec_trigger(func, trigdata);
|
||||
}
|
||||
|
||||
|
475
contrib/plpgsql/src/plpgsql.h
Normal file
475
contrib/plpgsql/src/plpgsql.h
Normal file
@ -0,0 +1,475 @@
|
||||
/**********************************************************************
|
||||
* plpgsql.h - Definitions for the PL/pgSQL
|
||||
* procedural language
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/contrib/plpgsql/src/Attic/plpgsql.h,v 1.1 1998/08/22 12:38:33 momjian Exp $
|
||||
*
|
||||
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||
*
|
||||
* The author hereby grants permission to use, copy, modify,
|
||||
* distribute, and license this software and its documentation
|
||||
* for any purpose, provided that existing copyright notices are
|
||||
* retained in all copies and that this notice is included
|
||||
* verbatim in any distributions. No written agreement, license,
|
||||
* or royalty fee is required for any of the authorized uses.
|
||||
* Modifications to this software may be copyrighted by their
|
||||
* author and need not follow the licensing terms described
|
||||
* here, provided that the new terms are clearly indicated on
|
||||
* the first page of each file where they apply.
|
||||
*
|
||||
* IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTORS BE LIABLE TO ANY
|
||||
* PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR
|
||||
* CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS
|
||||
* SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN
|
||||
* IF THE AUTHOR HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*
|
||||
* THE AUTHOR AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON
|
||||
* AN "AS IS" BASIS, AND THE AUTHOR AND DISTRIBUTORS HAVE NO
|
||||
* OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
|
||||
* ENHANCEMENTS, OR MODIFICATIONS.
|
||||
*
|
||||
**********************************************************************/
|
||||
#ifndef PLPGSQL_H
|
||||
#define PLPGSQL_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include "postgres.h"
|
||||
#include "executor/spi.h"
|
||||
#include "commands/trigger.h"
|
||||
#include "fmgr.h"
|
||||
|
||||
/**********************************************************************
|
||||
* Definitions
|
||||
**********************************************************************/
|
||||
|
||||
/* ----------
|
||||
* Compilers namestack item types
|
||||
* ----------
|
||||
*/
|
||||
enum {
|
||||
PLPGSQL_NSTYPE_LABEL,
|
||||
PLPGSQL_NSTYPE_VAR,
|
||||
PLPGSQL_NSTYPE_ROW,
|
||||
PLPGSQL_NSTYPE_REC,
|
||||
PLPGSQL_NSTYPE_RECFIELD
|
||||
};
|
||||
|
||||
/* ----------
|
||||
* Datum array node types
|
||||
* ----------
|
||||
*/
|
||||
enum {
|
||||
PLPGSQL_DTYPE_VAR,
|
||||
PLPGSQL_DTYPE_ROW,
|
||||
PLPGSQL_DTYPE_REC,
|
||||
PLPGSQL_DTYPE_RECFIELD,
|
||||
PLPGSQL_DTYPE_EXPR,
|
||||
PLPGSQL_DTYPE_TRIGARG
|
||||
};
|
||||
|
||||
/* ----------
|
||||
* Execution tree node types
|
||||
* ----------
|
||||
*/
|
||||
enum {
|
||||
PLPGSQL_STMT_BLOCK,
|
||||
PLPGSQL_STMT_ASSIGN,
|
||||
PLPGSQL_STMT_IF,
|
||||
PLPGSQL_STMT_LOOP,
|
||||
PLPGSQL_STMT_WHILE,
|
||||
PLPGSQL_STMT_FORI,
|
||||
PLPGSQL_STMT_FORS,
|
||||
PLPGSQL_STMT_SELECT,
|
||||
PLPGSQL_STMT_EXIT,
|
||||
PLPGSQL_STMT_RETURN,
|
||||
PLPGSQL_STMT_RAISE,
|
||||
PLPGSQL_STMT_EXECSQL
|
||||
};
|
||||
|
||||
|
||||
/* ----------
|
||||
* Execution node return codes
|
||||
* ----------
|
||||
*/
|
||||
enum {
|
||||
PLPGSQL_RC_OK,
|
||||
PLPGSQL_RC_EXIT,
|
||||
PLPGSQL_RC_RETURN
|
||||
};
|
||||
|
||||
/**********************************************************************
|
||||
* Node and structure definitions
|
||||
**********************************************************************/
|
||||
|
||||
|
||||
typedef struct { /* Dynamic string control structure */
|
||||
int alloc;
|
||||
int used;
|
||||
char *value;
|
||||
} PLpgSQL_dstring;
|
||||
|
||||
|
||||
typedef struct { /* Postgres base data type */
|
||||
char *typname;
|
||||
Oid typoid;
|
||||
FmgrInfo typinput;
|
||||
bool typbyval;
|
||||
int16 atttypmod;
|
||||
} PLpgSQL_type;
|
||||
|
||||
|
||||
typedef struct { /* Generic datum array item */
|
||||
int dtype;
|
||||
int dno;
|
||||
} PLpgSQL_datum;
|
||||
|
||||
|
||||
typedef struct { /* SQL Query to plan and execute */
|
||||
int dtype;
|
||||
int exprno;
|
||||
char *query;
|
||||
void *plan;
|
||||
Oid *plan_argtypes;
|
||||
int nparams;
|
||||
int params[1];
|
||||
} PLpgSQL_expr;
|
||||
|
||||
|
||||
typedef struct { /* Local variable */
|
||||
int dtype;
|
||||
int varno;
|
||||
char *refname;
|
||||
int lineno;
|
||||
|
||||
PLpgSQL_type *datatype;
|
||||
int isconst;
|
||||
int notnull;
|
||||
PLpgSQL_expr *default_val;
|
||||
|
||||
Datum value;
|
||||
bool isnull;
|
||||
int shouldfree;
|
||||
} PLpgSQL_var;
|
||||
|
||||
|
||||
typedef struct { /* Rowtype */
|
||||
int dtype;
|
||||
int rowno;
|
||||
char *refname;
|
||||
int lineno;
|
||||
Oid rowtypeclass;
|
||||
|
||||
int nfields;
|
||||
char **fieldnames;
|
||||
int *varnos;
|
||||
} PLpgSQL_row;
|
||||
|
||||
|
||||
typedef struct { /* Record of undefined structure */
|
||||
int dtype;
|
||||
int recno;
|
||||
char *refname;
|
||||
int lineno;
|
||||
|
||||
HeapTuple tup;
|
||||
TupleDesc tupdesc;
|
||||
} PLpgSQL_rec;
|
||||
|
||||
|
||||
typedef struct { /* Field in record */
|
||||
int dtype;
|
||||
int rfno;
|
||||
char *fieldname;
|
||||
int recno;
|
||||
} PLpgSQL_recfield;
|
||||
|
||||
|
||||
typedef struct { /* Positional argument to trigger */
|
||||
int dtype;
|
||||
int dno;
|
||||
PLpgSQL_expr *argnum;
|
||||
} PLpgSQL_trigarg;
|
||||
|
||||
|
||||
typedef struct { /* Item in the compilers namestack */
|
||||
int itemtype;
|
||||
int itemno;
|
||||
char name[1];
|
||||
} PLpgSQL_nsitem;
|
||||
|
||||
|
||||
typedef struct PLpgSQL_ns { /* Compiler namestack level */
|
||||
int items_alloc;
|
||||
int items_used;
|
||||
PLpgSQL_nsitem **items;
|
||||
struct PLpgSQL_ns *upper;
|
||||
} PLpgSQL_ns;
|
||||
|
||||
|
||||
typedef struct { /* List of execution nodes */
|
||||
int stmts_alloc;
|
||||
int stmts_used;
|
||||
struct PLpgSQL_stmt **stmts;
|
||||
} PLpgSQL_stmts;
|
||||
|
||||
|
||||
typedef struct { /* Generic execution node */
|
||||
int cmd_type;
|
||||
int lineno;
|
||||
} PLpgSQL_stmt;
|
||||
|
||||
|
||||
typedef struct { /* Block of statements */
|
||||
int cmd_type;
|
||||
int lineno;
|
||||
char *label;
|
||||
PLpgSQL_stmts *body;
|
||||
int n_initvars;
|
||||
int *initvarnos;
|
||||
} PLpgSQL_stmt_block;
|
||||
|
||||
|
||||
typedef struct { /* Assign statement */
|
||||
int cmd_type;
|
||||
int lineno;
|
||||
int varno;
|
||||
PLpgSQL_expr *expr;
|
||||
} PLpgSQL_stmt_assign;
|
||||
|
||||
|
||||
typedef struct { /* IF statement */
|
||||
int cmd_type;
|
||||
int lineno;
|
||||
PLpgSQL_expr *cond;
|
||||
PLpgSQL_stmts *true_body;
|
||||
PLpgSQL_stmts *false_body;
|
||||
} PLpgSQL_stmt_if;
|
||||
|
||||
|
||||
typedef struct { /* Unconditional LOOP statement */
|
||||
int cmd_type;
|
||||
int lineno;
|
||||
char *label;
|
||||
PLpgSQL_stmts *body;
|
||||
} PLpgSQL_stmt_loop;
|
||||
|
||||
|
||||
typedef struct { /* WHILE cond LOOP statement */
|
||||
int cmd_type;
|
||||
int lineno;
|
||||
char *label;
|
||||
PLpgSQL_expr *cond;
|
||||
PLpgSQL_stmts *body;
|
||||
} PLpgSQL_stmt_while;
|
||||
|
||||
|
||||
typedef struct { /* FOR statement with integer loopvar */
|
||||
int cmd_type;
|
||||
int lineno;
|
||||
char *label;
|
||||
PLpgSQL_var *var;
|
||||
PLpgSQL_expr *lower;
|
||||
PLpgSQL_expr *upper;
|
||||
int reverse;
|
||||
PLpgSQL_stmts *body;
|
||||
} PLpgSQL_stmt_fori;
|
||||
|
||||
|
||||
typedef struct { /* FOR statement running over SELECT */
|
||||
int cmd_type;
|
||||
int lineno;
|
||||
char *label;
|
||||
PLpgSQL_rec *rec;
|
||||
PLpgSQL_row *row;
|
||||
PLpgSQL_expr *query;
|
||||
PLpgSQL_stmts *body;
|
||||
} PLpgSQL_stmt_fors;
|
||||
|
||||
|
||||
typedef struct { /* SELECT ... INTO statement */
|
||||
int cmd_type;
|
||||
int lineno;
|
||||
PLpgSQL_rec *rec;
|
||||
PLpgSQL_row *row;
|
||||
PLpgSQL_expr *query;
|
||||
} PLpgSQL_stmt_select;
|
||||
|
||||
|
||||
typedef struct { /* EXIT statement */
|
||||
int cmd_type;
|
||||
int lineno;
|
||||
char *label;
|
||||
PLpgSQL_expr *cond;
|
||||
} PLpgSQL_stmt_exit;
|
||||
|
||||
|
||||
typedef struct { /* RETURN statement */
|
||||
int cmd_type;
|
||||
int lineno;
|
||||
bool retistuple;
|
||||
PLpgSQL_expr *expr;
|
||||
int retrecno;
|
||||
} PLpgSQL_stmt_return;
|
||||
|
||||
|
||||
typedef struct { /* RAISE statement */
|
||||
int cmd_type;
|
||||
int lineno;
|
||||
int elog_level;
|
||||
char *message;
|
||||
int nparams;
|
||||
int *params;
|
||||
} PLpgSQL_stmt_raise;
|
||||
|
||||
|
||||
typedef struct { /* Generic SQL statement to execute */
|
||||
int cmd_type;
|
||||
int lineno;
|
||||
PLpgSQL_expr *sqlstmt;
|
||||
} PLpgSQL_stmt_execsql;
|
||||
|
||||
|
||||
typedef struct PLpgSQL_function { /* Complete compiled function */
|
||||
Oid fn_oid;
|
||||
char *fn_name;
|
||||
int fn_functype;
|
||||
Oid fn_rettype;
|
||||
int fn_rettyplen;
|
||||
bool fn_retbyval;
|
||||
FmgrInfo fn_retinput;
|
||||
bool fn_retistuple;
|
||||
bool fn_retset;
|
||||
|
||||
int fn_nargs;
|
||||
int fn_argvarnos[MAXFMGRARGS];
|
||||
int found_varno;
|
||||
int new_varno;
|
||||
int old_varno;
|
||||
int tg_name_varno;
|
||||
int tg_when_varno;
|
||||
int tg_level_varno;
|
||||
int tg_op_varno;
|
||||
int tg_relid_varno;
|
||||
int tg_relname_varno;
|
||||
int tg_nargs_varno;
|
||||
|
||||
int ndatums;
|
||||
PLpgSQL_datum **datums;
|
||||
PLpgSQL_stmt_block *action;
|
||||
struct PLpgSQL_function *next;
|
||||
} PLpgSQL_function;
|
||||
|
||||
|
||||
typedef struct { /* Runtime execution data */
|
||||
Datum retval;
|
||||
bool retisnull;
|
||||
Oid rettype;
|
||||
bool retistuple;
|
||||
TupleDesc rettupdesc;
|
||||
bool retisset;
|
||||
char *exitlabel;
|
||||
|
||||
int trig_nargs;
|
||||
Datum *trig_argv;
|
||||
|
||||
int found_varno;
|
||||
int ndatums;
|
||||
PLpgSQL_datum **datums;
|
||||
} PLpgSQL_execstate;
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* Global variable declarations
|
||||
**********************************************************************/
|
||||
|
||||
extern int plpgsql_DumpExecTree;
|
||||
extern int plpgsql_SpaceScanned;
|
||||
extern int plpgsql_nDatums;
|
||||
extern PLpgSQL_datum **plpgsql_Datums;
|
||||
|
||||
extern int plpgsql_error_lineno;
|
||||
extern char *plpgsql_error_funcname;
|
||||
|
||||
extern PLpgSQL_function *plpgsql_curr_compile;
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* Function declarations
|
||||
**********************************************************************/
|
||||
|
||||
|
||||
extern char *pstrdup(char *s);
|
||||
|
||||
|
||||
/* ----------
|
||||
* Functions in pl_comp.c
|
||||
* ----------
|
||||
*/
|
||||
extern PLpgSQL_function *plpgsql_compile(Oid fn_oid, int functype);
|
||||
extern int plpgsql_parse_word(char *word);
|
||||
extern int plpgsql_parse_dblword(char *string);
|
||||
extern int plpgsql_parse_tripword(char *string);
|
||||
extern int plpgsql_parse_wordtype(char *string);
|
||||
extern int plpgsql_parse_dblwordtype(char *string);
|
||||
extern int plpgsql_parse_wordrowtype(char *string);
|
||||
extern void plpgsql_adddatum(PLpgSQL_datum *new);
|
||||
extern int plpgsql_add_initdatums(int **varnos);
|
||||
extern void plpgsql_comperrinfo(void);
|
||||
|
||||
|
||||
/* ----------
|
||||
* Functions in pl_exec.c
|
||||
* ----------
|
||||
*/
|
||||
extern Datum plpgsql_exec_function(PLpgSQL_function *func,
|
||||
FmgrValues *args, bool *isNull);
|
||||
extern HeapTuple plpgsql_exec_trigger(PLpgSQL_function *func,
|
||||
TriggerData *trigdata);
|
||||
|
||||
|
||||
/* ----------
|
||||
* Functions for the dynamic string handling in pl_funcs.c
|
||||
* ----------
|
||||
*/
|
||||
extern void plpgsql_dstring_init(PLpgSQL_dstring *ds);
|
||||
extern void plpgsql_dstring_free(PLpgSQL_dstring *ds);
|
||||
extern void plpgsql_dstring_append(PLpgSQL_dstring *ds, char *str);
|
||||
extern char *plpgsql_dstring_get(PLpgSQL_dstring *ds);
|
||||
|
||||
/* ----------
|
||||
* Functions for the namestack handling in pl_funcs.c
|
||||
* ----------
|
||||
*/
|
||||
extern void plpgsql_ns_init(void);
|
||||
extern bool plpgsql_ns_setlocal(bool flag);
|
||||
extern void plpgsql_ns_push(char *label);
|
||||
extern void plpgsql_ns_pop(void);
|
||||
extern void plpgsql_ns_additem(int itemtype, int itemno, char *name);
|
||||
extern PLpgSQL_nsitem *plpgsql_ns_lookup(char *name, char *nsname);
|
||||
extern void plpgsql_ns_rename(char *oldname, char *newname);
|
||||
|
||||
/* ----------
|
||||
* Other functions in pl_funcs.c
|
||||
* ----------
|
||||
*/
|
||||
extern void plpgsql_dumptree(PLpgSQL_function *func);
|
||||
extern char *plpgsql_tolower(char *s);
|
||||
|
||||
/* ----------
|
||||
* Externs in gram.y and scan.l
|
||||
* ----------
|
||||
*/
|
||||
extern PLpgSQL_expr *plpgsql_read_expression(int until, char *s);
|
||||
extern void plpgsql_yyrestart(FILE *fp);
|
||||
extern int plpgsql_yylex();
|
||||
extern void plpgsql_setinput(char *s, int functype);
|
||||
extern int plpgsql_yyparse();
|
||||
|
||||
|
||||
#endif /* PLPGSQL_H */
|
227
contrib/plpgsql/src/scan.l
Normal file
227
contrib/plpgsql/src/scan.l
Normal file
@ -0,0 +1,227 @@
|
||||
%{
|
||||
/**********************************************************************
|
||||
* scan.l - Scanner for the PL/pgSQL
|
||||
* procedural language
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/contrib/plpgsql/src/Attic/scan.l,v 1.1 1998/08/22 12:38:33 momjian Exp $
|
||||
*
|
||||
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||
*
|
||||
* The author hereby grants permission to use, copy, modify,
|
||||
* distribute, and license this software and its documentation
|
||||
* for any purpose, provided that existing copyright notices are
|
||||
* retained in all copies and that this notice is included
|
||||
* verbatim in any distributions. No written agreement, license,
|
||||
* or royalty fee is required for any of the authorized uses.
|
||||
* Modifications to this software may be copyrighted by their
|
||||
* author and need not follow the licensing terms described
|
||||
* here, provided that the new terms are clearly indicated on
|
||||
* the first page of each file where they apply.
|
||||
*
|
||||
* IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTORS BE LIABLE TO ANY
|
||||
* PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR
|
||||
* CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS
|
||||
* SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN
|
||||
* IF THE AUTHOR HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*
|
||||
* THE AUTHOR AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON
|
||||
* AN "AS IS" BASIS, AND THE AUTHOR AND DISTRIBUTORS HAVE NO
|
||||
* OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
|
||||
* ENHANCEMENTS, OR MODIFICATIONS.
|
||||
*
|
||||
**********************************************************************/
|
||||
|
||||
static char *plpgsql_source;
|
||||
static int plpgsql_bytes_left;
|
||||
static int scanner_functype;
|
||||
static int scanner_typereported;
|
||||
int plpgsql_SpaceScanned = 0;
|
||||
|
||||
static void plpgsql_input(char *buf, int *result, int max);
|
||||
#define YY_INPUT(buf,res,max) plpgsql_input(buf, &res, max)
|
||||
%}
|
||||
|
||||
WS [[:alpha:]_]
|
||||
WC [[:alnum:]_]
|
||||
|
||||
%x IN_STRING IN_COMMENT
|
||||
|
||||
%%
|
||||
/* ----------
|
||||
* Local variable in scanner to remember where
|
||||
* a string or comment started
|
||||
* ----------
|
||||
*/
|
||||
int start_lineno = 0;
|
||||
|
||||
/* ----------
|
||||
* Reset the state when entering the scanner
|
||||
* ----------
|
||||
*/
|
||||
BEGIN INITIAL;
|
||||
plpgsql_SpaceScanned = 0;
|
||||
|
||||
/* ----------
|
||||
* On the first call to a new source report the
|
||||
* functions type (T_FUNCTION or T_TRIGGER)
|
||||
* ----------
|
||||
*/
|
||||
if (!scanner_typereported) {
|
||||
scanner_typereported = 1;
|
||||
return scanner_functype;
|
||||
}
|
||||
|
||||
/* ----------
|
||||
* The keyword rules
|
||||
* ----------
|
||||
*/
|
||||
:= { return K_ASSIGN; }
|
||||
= { return K_ASSIGN; }
|
||||
\.\. { return K_DOTDOT; }
|
||||
alias { return K_ALIAS; }
|
||||
begin { return K_BEGIN; }
|
||||
bpchar { return T_BPCHAR; }
|
||||
char { return T_CHAR; }
|
||||
constant { return K_CONSTANT; }
|
||||
debug { return K_DEBUG; }
|
||||
declare { return K_DECLARE; }
|
||||
default { return K_DEFAULT; }
|
||||
else { return K_ELSE; }
|
||||
end { return K_END; }
|
||||
exception { return K_EXCEPTION; }
|
||||
exit { return K_EXIT; }
|
||||
for { return K_FOR; }
|
||||
from { return K_FROM; }
|
||||
if { return K_IF; }
|
||||
in { return K_IN; }
|
||||
into { return K_INTO; }
|
||||
loop { return K_LOOP; }
|
||||
not { return K_NOT; }
|
||||
notice { return K_NOTICE; }
|
||||
null { return K_NULL; }
|
||||
perform { return K_PERFORM; }
|
||||
raise { return K_RAISE; }
|
||||
record { return K_RECORD; }
|
||||
rename { return K_RENAME; }
|
||||
return { return K_RETURN; }
|
||||
reverse { return K_REVERSE; }
|
||||
select { return K_SELECT; }
|
||||
then { return K_THEN; }
|
||||
to { return K_TO; }
|
||||
type { return K_TYPE; }
|
||||
varchar { return T_VARCHAR; }
|
||||
when { return K_WHEN; }
|
||||
while { return K_WHILE; }
|
||||
|
||||
^#option { return O_OPTION; }
|
||||
dump { return O_DUMP; }
|
||||
|
||||
|
||||
/* ----------
|
||||
* Special word rules
|
||||
* ----------
|
||||
*/
|
||||
{WS}{WC}* { return plpgsql_parse_word(yytext); }
|
||||
{WS}{WC}*\.{WS}{WC}* { return plpgsql_parse_dblword(yytext); }
|
||||
{WS}{WC}*\.{WS}{WC}*\.{WS}{WC}* { return plpgsql_parse_tripword(yytext); }
|
||||
{WS}{WC}*%TYPE { return plpgsql_parse_wordtype(yytext); }
|
||||
{WS}{WC}*\.{WS}{WC}*%TYPE { return plpgsql_parse_dblwordtype(yytext); }
|
||||
{WS}{WC}*%ROWTYPE { return plpgsql_parse_wordrowtype(yytext); }
|
||||
|
||||
\$[0-9]+ { return plpgsql_parse_word(yytext); }
|
||||
[0-9]+ { return T_NUMBER; }
|
||||
|
||||
/* ----------
|
||||
* Ignore whitespaces but remember this happened
|
||||
* ----------
|
||||
*/
|
||||
[ \t\n]+ { plpgsql_SpaceScanned = 1; }
|
||||
|
||||
/* ----------
|
||||
* Eat up comments
|
||||
* ----------
|
||||
*/
|
||||
--[^\n]* ;
|
||||
\/\* { start_lineno = yylineno;
|
||||
BEGIN IN_COMMENT;
|
||||
}
|
||||
<IN_COMMENT>\*\/ { BEGIN INITIAL; }
|
||||
<IN_COMMENT>\n ;
|
||||
<IN_COMMENT>. ;
|
||||
<IN_COMMENT><<EOF>> { plpgsql_comperrinfo();
|
||||
elog(ERROR, "unterminated comment starting on line %d",
|
||||
start_lineno);
|
||||
}
|
||||
|
||||
/* ----------
|
||||
* Collect anything inside of ''s and return one STRING
|
||||
* ----------
|
||||
*/
|
||||
' { start_lineno = yylineno;
|
||||
BEGIN IN_STRING;
|
||||
yymore();
|
||||
}
|
||||
<IN_STRING>\\. |
|
||||
<IN_STRING>'' { yymore(); }
|
||||
<IN_STRING>' { BEGIN INITIAL;
|
||||
return T_STRING;
|
||||
}
|
||||
<IN_STRING><<EOF>> { plpgsql_comperrinfo();
|
||||
elog(ERROR, "unterminated string starting on line %d",
|
||||
start_lineno);
|
||||
}
|
||||
<IN_STRING>[^'\\]* { yymore(); }
|
||||
|
||||
/* ----------
|
||||
* Any unmatched character is returned as is
|
||||
* ----------
|
||||
*/
|
||||
. { return yytext[0]; }
|
||||
|
||||
%%
|
||||
|
||||
int yywrap()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static void plpgsql_input(char *buf, int *result, int max)
|
||||
{
|
||||
int n = max;
|
||||
if (n > plpgsql_bytes_left) {
|
||||
n = plpgsql_bytes_left;
|
||||
}
|
||||
|
||||
if (n == 0) {
|
||||
*result = YY_NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
*result = n;
|
||||
memcpy(buf, plpgsql_source, n);
|
||||
plpgsql_source += n;
|
||||
plpgsql_bytes_left -= n;
|
||||
}
|
||||
|
||||
|
||||
void plpgsql_setinput(char *source, int functype)
|
||||
{
|
||||
yyrestart(NULL);
|
||||
yylineno = 1;
|
||||
|
||||
plpgsql_source = source;
|
||||
if (*plpgsql_source == '\n')
|
||||
plpgsql_source++;
|
||||
plpgsql_bytes_left = strlen(plpgsql_source);
|
||||
|
||||
scanner_functype = functype;
|
||||
scanner_typereported = 0;
|
||||
}
|
||||
|
||||
|
22
contrib/plpgsql/test/README
Normal file
22
contrib/plpgsql/test/README
Normal file
@ -0,0 +1,22 @@
|
||||
Test suite for PL/pgSQL
|
||||
|
||||
Scenario:
|
||||
|
||||
A building with a modern TP cabel installation where any
|
||||
of the wall connectors can be used to plug in phones,
|
||||
ethernet interfaces or local office hubs. The backside
|
||||
of the wall connectors is wired to one of several patch-
|
||||
fields in the building.
|
||||
|
||||
In the patchfields, there are hubs and all the slots
|
||||
representing the wall connectors. In addition there are
|
||||
slots that can represent a phone line from the central
|
||||
phone system.
|
||||
|
||||
Triggers ensure consistency of the patching information.
|
||||
|
||||
Functions are used to build up powerful views that let
|
||||
you look behind the wall when looking at a patchfield
|
||||
or into a room.
|
||||
|
||||
|
63
contrib/plpgsql/test/expected/tables.out
Normal file
63
contrib/plpgsql/test/expected/tables.out
Normal file
@ -0,0 +1,63 @@
|
||||
QUERY: create table Room (
|
||||
roomno char(8),
|
||||
comment text
|
||||
);
|
||||
QUERY: create unique index Room_rno on Room using btree (roomno bpchar_ops);
|
||||
QUERY: create table WSlot (
|
||||
slotname char(20),
|
||||
roomno char(8),
|
||||
slotlink char(20),
|
||||
backlink char(20)
|
||||
);
|
||||
QUERY: create unique index WSlot_name on WSlot using btree (slotname bpchar_ops);
|
||||
QUERY: create table PField (
|
||||
name text,
|
||||
comment text
|
||||
);
|
||||
QUERY: create unique index PField_name on PField using btree (name text_ops);
|
||||
QUERY: create table PSlot (
|
||||
slotname char(20),
|
||||
pfname text,
|
||||
slotlink char(20),
|
||||
backlink char(20)
|
||||
);
|
||||
QUERY: create unique index PSlot_name on PSlot using btree (slotname bpchar_ops);
|
||||
QUERY: create table PLine (
|
||||
slotname char(20),
|
||||
phonenumber char(20),
|
||||
comment text,
|
||||
backlink char(20)
|
||||
);
|
||||
QUERY: create unique index PLine_name on PLine using btree (slotname bpchar_ops);
|
||||
QUERY: create table Hub (
|
||||
name char(14),
|
||||
comment text,
|
||||
nslots integer
|
||||
);
|
||||
QUERY: create unique index Hub_name on Hub using btree (name bpchar_ops);
|
||||
QUERY: create table HSlot (
|
||||
slotname char(20),
|
||||
hubname char(14),
|
||||
slotno integer,
|
||||
slotlink char(20)
|
||||
);
|
||||
QUERY: create unique index HSlot_name on HSlot using btree (slotname bpchar_ops);
|
||||
QUERY: create index HSlot_hubname on HSlot using btree (hubname bpchar_ops);
|
||||
QUERY: create table System (
|
||||
name text,
|
||||
comment text
|
||||
);
|
||||
QUERY: create unique index System_name on System using btree (name text_ops);
|
||||
QUERY: create table IFace (
|
||||
slotname char(20),
|
||||
sysname text,
|
||||
ifname text,
|
||||
slotlink char(20)
|
||||
);
|
||||
QUERY: create unique index IFace_name on IFace using btree (slotname bpchar_ops);
|
||||
QUERY: create table PHone (
|
||||
slotname char(20),
|
||||
comment text,
|
||||
slotlink char(20)
|
||||
);
|
||||
QUERY: create unique index PHone_name on PHone using btree (slotname bpchar_ops);
|
473
contrib/plpgsql/test/expected/test.out
Normal file
473
contrib/plpgsql/test/expected/test.out
Normal file
@ -0,0 +1,473 @@
|
||||
QUERY: insert into Room values ('001', 'Entrance');
|
||||
QUERY: insert into Room values ('002', 'Office');
|
||||
QUERY: insert into Room values ('003', 'Office');
|
||||
QUERY: insert into Room values ('004', 'Technical');
|
||||
QUERY: insert into Room values ('101', 'Office');
|
||||
QUERY: insert into Room values ('102', 'Conference');
|
||||
QUERY: insert into Room values ('103', 'Restroom');
|
||||
QUERY: insert into Room values ('104', 'Technical');
|
||||
QUERY: insert into Room values ('105', 'Office');
|
||||
QUERY: insert into Room values ('106', 'Office');
|
||||
QUERY: insert into WSlot values ('WS.001.1a', '001', '', '');
|
||||
QUERY: insert into WSlot values ('WS.001.1b', '001', '', '');
|
||||
QUERY: insert into WSlot values ('WS.001.2a', '001', '', '');
|
||||
QUERY: insert into WSlot values ('WS.001.2b', '001', '', '');
|
||||
QUERY: insert into WSlot values ('WS.001.3a', '001', '', '');
|
||||
QUERY: insert into WSlot values ('WS.001.3b', '001', '', '');
|
||||
QUERY: insert into WSlot values ('WS.002.1a', '002', '', '');
|
||||
QUERY: insert into WSlot values ('WS.002.1b', '002', '', '');
|
||||
QUERY: insert into WSlot values ('WS.002.2a', '002', '', '');
|
||||
QUERY: insert into WSlot values ('WS.002.2b', '002', '', '');
|
||||
QUERY: insert into WSlot values ('WS.002.3a', '002', '', '');
|
||||
QUERY: insert into WSlot values ('WS.002.3b', '002', '', '');
|
||||
QUERY: insert into WSlot values ('WS.003.1a', '003', '', '');
|
||||
QUERY: insert into WSlot values ('WS.003.1b', '003', '', '');
|
||||
QUERY: insert into WSlot values ('WS.003.2a', '003', '', '');
|
||||
QUERY: insert into WSlot values ('WS.003.2b', '003', '', '');
|
||||
QUERY: insert into WSlot values ('WS.003.3a', '003', '', '');
|
||||
QUERY: insert into WSlot values ('WS.003.3b', '003', '', '');
|
||||
QUERY: insert into WSlot values ('WS.101.1a', '101', '', '');
|
||||
QUERY: insert into WSlot values ('WS.101.1b', '101', '', '');
|
||||
QUERY: insert into WSlot values ('WS.101.2a', '101', '', '');
|
||||
QUERY: insert into WSlot values ('WS.101.2b', '101', '', '');
|
||||
QUERY: insert into WSlot values ('WS.101.3a', '101', '', '');
|
||||
QUERY: insert into WSlot values ('WS.101.3b', '101', '', '');
|
||||
QUERY: insert into WSlot values ('WS.102.1a', '102', '', '');
|
||||
QUERY: insert into WSlot values ('WS.102.1b', '102', '', '');
|
||||
QUERY: insert into WSlot values ('WS.102.2a', '102', '', '');
|
||||
QUERY: insert into WSlot values ('WS.102.2b', '102', '', '');
|
||||
QUERY: insert into WSlot values ('WS.102.3a', '102', '', '');
|
||||
QUERY: insert into WSlot values ('WS.102.3b', '102', '', '');
|
||||
QUERY: insert into WSlot values ('WS.105.1a', '105', '', '');
|
||||
QUERY: insert into WSlot values ('WS.105.1b', '105', '', '');
|
||||
QUERY: insert into WSlot values ('WS.105.2a', '105', '', '');
|
||||
QUERY: insert into WSlot values ('WS.105.2b', '105', '', '');
|
||||
QUERY: insert into WSlot values ('WS.105.3a', '105', '', '');
|
||||
QUERY: insert into WSlot values ('WS.105.3b', '105', '', '');
|
||||
QUERY: insert into WSlot values ('WS.106.1a', '106', '', '');
|
||||
QUERY: insert into WSlot values ('WS.106.1b', '106', '', '');
|
||||
QUERY: insert into WSlot values ('WS.106.2a', '106', '', '');
|
||||
QUERY: insert into WSlot values ('WS.106.2b', '106', '', '');
|
||||
QUERY: insert into WSlot values ('WS.106.3a', '106', '', '');
|
||||
QUERY: insert into WSlot values ('WS.106.3b', '106', '', '');
|
||||
QUERY: insert into PField values ('PF0_1', 'Wallslots basement');
|
||||
QUERY: insert into PSlot values ('PS.base.a1', 'PF0_1', '', '');
|
||||
QUERY: insert into PSlot values ('PS.base.a2', 'PF0_1', '', '');
|
||||
QUERY: insert into PSlot values ('PS.base.a3', 'PF0_1', '', '');
|
||||
QUERY: insert into PSlot values ('PS.base.a4', 'PF0_1', '', '');
|
||||
QUERY: insert into PSlot values ('PS.base.a5', 'PF0_1', '', '');
|
||||
QUERY: insert into PSlot values ('PS.base.a6', 'PF0_1', '', '');
|
||||
QUERY: insert into PSlot values ('PS.base.b1', 'PF0_1', '', 'WS.002.1a');
|
||||
QUERY: insert into PSlot values ('PS.base.b2', 'PF0_1', '', 'WS.002.1b');
|
||||
QUERY: insert into PSlot values ('PS.base.b3', 'PF0_1', '', 'WS.002.2a');
|
||||
QUERY: insert into PSlot values ('PS.base.b4', 'PF0_1', '', 'WS.002.2b');
|
||||
QUERY: insert into PSlot values ('PS.base.b5', 'PF0_1', '', 'WS.002.3a');
|
||||
QUERY: insert into PSlot values ('PS.base.b6', 'PF0_1', '', 'WS.002.3b');
|
||||
QUERY: insert into PSlot values ('PS.base.c1', 'PF0_1', '', 'WS.003.1a');
|
||||
QUERY: insert into PSlot values ('PS.base.c2', 'PF0_1', '', 'WS.003.1b');
|
||||
QUERY: insert into PSlot values ('PS.base.c3', 'PF0_1', '', 'WS.003.2a');
|
||||
QUERY: insert into PSlot values ('PS.base.c4', 'PF0_1', '', 'WS.003.2b');
|
||||
QUERY: insert into PSlot values ('PS.base.c5', 'PF0_1', '', 'WS.003.3a');
|
||||
QUERY: insert into PSlot values ('PS.base.c6', 'PF0_1', '', 'WS.003.3b');
|
||||
QUERY: insert into PField values ('PF0_X', 'Phonelines basement');
|
||||
QUERY: insert into PSlot values ('PS.base.ta1', 'PF0_X', '', '');
|
||||
QUERY: insert into PSlot values ('PS.base.ta2', 'PF0_X', '', '');
|
||||
QUERY: insert into PSlot values ('PS.base.ta3', 'PF0_X', '', '');
|
||||
QUERY: insert into PSlot values ('PS.base.ta4', 'PF0_X', '', '');
|
||||
QUERY: insert into PSlot values ('PS.base.ta5', 'PF0_X', '', '');
|
||||
QUERY: insert into PSlot values ('PS.base.ta6', 'PF0_X', '', '');
|
||||
QUERY: insert into PSlot values ('PS.base.tb1', 'PF0_X', '', '');
|
||||
QUERY: insert into PSlot values ('PS.base.tb2', 'PF0_X', '', '');
|
||||
QUERY: insert into PSlot values ('PS.base.tb3', 'PF0_X', '', '');
|
||||
QUERY: insert into PSlot values ('PS.base.tb4', 'PF0_X', '', '');
|
||||
QUERY: insert into PSlot values ('PS.base.tb5', 'PF0_X', '', '');
|
||||
QUERY: insert into PSlot values ('PS.base.tb6', 'PF0_X', '', '');
|
||||
QUERY: insert into PField values ('PF1_1', 'Wallslots 1st floor');
|
||||
QUERY: insert into PSlot values ('PS.1st.a1', 'PF1_1', '', 'WS.101.1a');
|
||||
QUERY: insert into PSlot values ('PS.1st.a2', 'PF1_1', '', 'WS.101.1b');
|
||||
QUERY: insert into PSlot values ('PS.1st.a3', 'PF1_1', '', 'WS.101.2a');
|
||||
QUERY: insert into PSlot values ('PS.1st.a4', 'PF1_1', '', 'WS.101.2b');
|
||||
QUERY: insert into PSlot values ('PS.1st.a5', 'PF1_1', '', 'WS.101.3a');
|
||||
QUERY: insert into PSlot values ('PS.1st.a6', 'PF1_1', '', 'WS.101.3b');
|
||||
QUERY: insert into PSlot values ('PS.1st.b1', 'PF1_1', '', 'WS.102.1a');
|
||||
QUERY: insert into PSlot values ('PS.1st.b2', 'PF1_1', '', 'WS.102.1b');
|
||||
QUERY: insert into PSlot values ('PS.1st.b3', 'PF1_1', '', 'WS.102.2a');
|
||||
QUERY: insert into PSlot values ('PS.1st.b4', 'PF1_1', '', 'WS.102.2b');
|
||||
QUERY: insert into PSlot values ('PS.1st.b5', 'PF1_1', '', 'WS.102.3a');
|
||||
QUERY: insert into PSlot values ('PS.1st.b6', 'PF1_1', '', 'WS.102.3b');
|
||||
QUERY: insert into PSlot values ('PS.1st.c1', 'PF1_1', '', 'WS.105.1a');
|
||||
QUERY: insert into PSlot values ('PS.1st.c2', 'PF1_1', '', 'WS.105.1b');
|
||||
QUERY: insert into PSlot values ('PS.1st.c3', 'PF1_1', '', 'WS.105.2a');
|
||||
QUERY: insert into PSlot values ('PS.1st.c4', 'PF1_1', '', 'WS.105.2b');
|
||||
QUERY: insert into PSlot values ('PS.1st.c5', 'PF1_1', '', 'WS.105.3a');
|
||||
QUERY: insert into PSlot values ('PS.1st.c6', 'PF1_1', '', 'WS.105.3b');
|
||||
QUERY: insert into PSlot values ('PS.1st.d1', 'PF1_1', '', 'WS.106.1a');
|
||||
QUERY: insert into PSlot values ('PS.1st.d2', 'PF1_1', '', 'WS.106.1b');
|
||||
QUERY: insert into PSlot values ('PS.1st.d3', 'PF1_1', '', 'WS.106.2a');
|
||||
QUERY: insert into PSlot values ('PS.1st.d4', 'PF1_1', '', 'WS.106.2b');
|
||||
QUERY: insert into PSlot values ('PS.1st.d5', 'PF1_1', '', 'WS.106.3a');
|
||||
QUERY: insert into PSlot values ('PS.1st.d6', 'PF1_1', '', 'WS.106.3b');
|
||||
QUERY: update PSlot set backlink = 'WS.001.1a' where slotname = 'PS.base.a1';
|
||||
QUERY: update PSlot set backlink = 'WS.001.1b' where slotname = 'PS.base.a3';
|
||||
QUERY: select * from WSlot where roomno = '001' order by slotname;
|
||||
slotname | roomno| slotlink|backlink
|
||||
--------------------+--------+--------------------+--------------------
|
||||
WS.001.1a |001 | |PS.base.a1
|
||||
WS.001.1b |001 | |PS.base.a3
|
||||
WS.001.2a |001 | |
|
||||
WS.001.2b |001 | |
|
||||
WS.001.3a |001 | |
|
||||
WS.001.3b |001 | |
|
||||
(6 rows)
|
||||
|
||||
QUERY: select * from PSlot where slotname ~ 'PS.base.a' order by slotname;
|
||||
slotname |pfname| slotlink|backlink
|
||||
--------------------+------+--------------------+--------------------
|
||||
PS.base.a1 |PF0_1 | |WS.001.1a
|
||||
PS.base.a2 |PF0_1 | |
|
||||
PS.base.a3 |PF0_1 | |WS.001.1b
|
||||
PS.base.a4 |PF0_1 | |
|
||||
PS.base.a5 |PF0_1 | |
|
||||
PS.base.a6 |PF0_1 | |
|
||||
(6 rows)
|
||||
|
||||
QUERY: update PSlot set backlink = 'WS.001.2a' where slotname = 'PS.base.a3';
|
||||
QUERY: select * from WSlot where roomno = '001' order by slotname;
|
||||
slotname | roomno| slotlink|backlink
|
||||
--------------------+--------+--------------------+--------------------
|
||||
WS.001.1a |001 | |PS.base.a1
|
||||
WS.001.1b |001 | |
|
||||
WS.001.2a |001 | |PS.base.a3
|
||||
WS.001.2b |001 | |
|
||||
WS.001.3a |001 | |
|
||||
WS.001.3b |001 | |
|
||||
(6 rows)
|
||||
|
||||
QUERY: select * from PSlot where slotname ~ 'PS.base.a' order by slotname;
|
||||
slotname |pfname| slotlink|backlink
|
||||
--------------------+------+--------------------+--------------------
|
||||
PS.base.a1 |PF0_1 | |WS.001.1a
|
||||
PS.base.a2 |PF0_1 | |
|
||||
PS.base.a3 |PF0_1 | |WS.001.2a
|
||||
PS.base.a4 |PF0_1 | |
|
||||
PS.base.a5 |PF0_1 | |
|
||||
PS.base.a6 |PF0_1 | |
|
||||
(6 rows)
|
||||
|
||||
QUERY: update PSlot set backlink = 'WS.001.1b' where slotname = 'PS.base.a2';
|
||||
QUERY: select * from WSlot where roomno = '001' order by slotname;
|
||||
slotname | roomno| slotlink|backlink
|
||||
--------------------+--------+--------------------+--------------------
|
||||
WS.001.1a |001 | |PS.base.a1
|
||||
WS.001.1b |001 | |PS.base.a2
|
||||
WS.001.2a |001 | |PS.base.a3
|
||||
WS.001.2b |001 | |
|
||||
WS.001.3a |001 | |
|
||||
WS.001.3b |001 | |
|
||||
(6 rows)
|
||||
|
||||
QUERY: select * from PSlot where slotname ~ 'PS.base.a' order by slotname;
|
||||
slotname |pfname| slotlink|backlink
|
||||
--------------------+------+--------------------+--------------------
|
||||
PS.base.a1 |PF0_1 | |WS.001.1a
|
||||
PS.base.a2 |PF0_1 | |WS.001.1b
|
||||
PS.base.a3 |PF0_1 | |WS.001.2a
|
||||
PS.base.a4 |PF0_1 | |
|
||||
PS.base.a5 |PF0_1 | |
|
||||
PS.base.a6 |PF0_1 | |
|
||||
(6 rows)
|
||||
|
||||
QUERY: update WSlot set backlink = 'PS.base.a4' where slotname = 'WS.001.2b';
|
||||
QUERY: update WSlot set backlink = 'PS.base.a6' where slotname = 'WS.001.3a';
|
||||
QUERY: select * from WSlot where roomno = '001' order by slotname;
|
||||
slotname | roomno| slotlink|backlink
|
||||
--------------------+--------+--------------------+--------------------
|
||||
WS.001.1a |001 | |PS.base.a1
|
||||
WS.001.1b |001 | |PS.base.a2
|
||||
WS.001.2a |001 | |PS.base.a3
|
||||
WS.001.2b |001 | |PS.base.a4
|
||||
WS.001.3a |001 | |PS.base.a6
|
||||
WS.001.3b |001 | |
|
||||
(6 rows)
|
||||
|
||||
QUERY: select * from PSlot where slotname ~ 'PS.base.a' order by slotname;
|
||||
slotname |pfname| slotlink|backlink
|
||||
--------------------+------+--------------------+--------------------
|
||||
PS.base.a1 |PF0_1 | |WS.001.1a
|
||||
PS.base.a2 |PF0_1 | |WS.001.1b
|
||||
PS.base.a3 |PF0_1 | |WS.001.2a
|
||||
PS.base.a4 |PF0_1 | |WS.001.2b
|
||||
PS.base.a5 |PF0_1 | |
|
||||
PS.base.a6 |PF0_1 | |WS.001.3a
|
||||
(6 rows)
|
||||
|
||||
QUERY: update WSlot set backlink = 'PS.base.a6' where slotname = 'WS.001.3b';
|
||||
QUERY: select * from WSlot where roomno = '001' order by slotname;
|
||||
slotname | roomno| slotlink|backlink
|
||||
--------------------+--------+--------------------+--------------------
|
||||
WS.001.1a |001 | |PS.base.a1
|
||||
WS.001.1b |001 | |PS.base.a2
|
||||
WS.001.2a |001 | |PS.base.a3
|
||||
WS.001.2b |001 | |PS.base.a4
|
||||
WS.001.3a |001 | |
|
||||
WS.001.3b |001 | |PS.base.a6
|
||||
(6 rows)
|
||||
|
||||
QUERY: select * from PSlot where slotname ~ 'PS.base.a' order by slotname;
|
||||
slotname |pfname| slotlink|backlink
|
||||
--------------------+------+--------------------+--------------------
|
||||
PS.base.a1 |PF0_1 | |WS.001.1a
|
||||
PS.base.a2 |PF0_1 | |WS.001.1b
|
||||
PS.base.a3 |PF0_1 | |WS.001.2a
|
||||
PS.base.a4 |PF0_1 | |WS.001.2b
|
||||
PS.base.a5 |PF0_1 | |
|
||||
PS.base.a6 |PF0_1 | |WS.001.3b
|
||||
(6 rows)
|
||||
|
||||
QUERY: update WSlot set backlink = 'PS.base.a5' where slotname = 'WS.001.3a';
|
||||
QUERY: select * from WSlot where roomno = '001' order by slotname;
|
||||
slotname | roomno| slotlink|backlink
|
||||
--------------------+--------+--------------------+--------------------
|
||||
WS.001.1a |001 | |PS.base.a1
|
||||
WS.001.1b |001 | |PS.base.a2
|
||||
WS.001.2a |001 | |PS.base.a3
|
||||
WS.001.2b |001 | |PS.base.a4
|
||||
WS.001.3a |001 | |PS.base.a5
|
||||
WS.001.3b |001 | |PS.base.a6
|
||||
(6 rows)
|
||||
|
||||
QUERY: select * from PSlot where slotname ~ 'PS.base.a' order by slotname;
|
||||
slotname |pfname| slotlink|backlink
|
||||
--------------------+------+--------------------+--------------------
|
||||
PS.base.a1 |PF0_1 | |WS.001.1a
|
||||
PS.base.a2 |PF0_1 | |WS.001.1b
|
||||
PS.base.a3 |PF0_1 | |WS.001.2a
|
||||
PS.base.a4 |PF0_1 | |WS.001.2b
|
||||
PS.base.a5 |PF0_1 | |WS.001.3a
|
||||
PS.base.a6 |PF0_1 | |WS.001.3b
|
||||
(6 rows)
|
||||
|
||||
QUERY: insert into PField values ('PF1_2', 'Phonelines 1st floor');
|
||||
QUERY: insert into PSlot values ('PS.1st.ta1', 'PF1_2', '', '');
|
||||
QUERY: insert into PSlot values ('PS.1st.ta2', 'PF1_2', '', '');
|
||||
QUERY: insert into PSlot values ('PS.1st.ta3', 'PF1_2', '', '');
|
||||
QUERY: insert into PSlot values ('PS.1st.ta4', 'PF1_2', '', '');
|
||||
QUERY: insert into PSlot values ('PS.1st.ta5', 'PF1_2', '', '');
|
||||
QUERY: insert into PSlot values ('PS.1st.ta6', 'PF1_2', '', '');
|
||||
QUERY: insert into PSlot values ('PS.1st.tb1', 'PF1_2', '', '');
|
||||
QUERY: insert into PSlot values ('PS.1st.tb2', 'PF1_2', '', '');
|
||||
QUERY: insert into PSlot values ('PS.1st.tb3', 'PF1_2', '', '');
|
||||
QUERY: insert into PSlot values ('PS.1st.tb4', 'PF1_2', '', '');
|
||||
QUERY: insert into PSlot values ('PS.1st.tb5', 'PF1_2', '', '');
|
||||
QUERY: insert into PSlot values ('PS.1st.tb6', 'PF1_2', '', '');
|
||||
QUERY: update PField set name = 'PF0_2' where name = 'PF0_X';
|
||||
QUERY: select * from PSlot order by slotname;
|
||||
slotname |pfname| slotlink|backlink
|
||||
--------------------+------+--------------------+--------------------
|
||||
PS.1st.a1 |PF1_1 | |WS.101.1a
|
||||
PS.1st.a2 |PF1_1 | |WS.101.1b
|
||||
PS.1st.a3 |PF1_1 | |WS.101.2a
|
||||
PS.1st.a4 |PF1_1 | |WS.101.2b
|
||||
PS.1st.a5 |PF1_1 | |WS.101.3a
|
||||
PS.1st.a6 |PF1_1 | |WS.101.3b
|
||||
PS.1st.b1 |PF1_1 | |WS.102.1a
|
||||
PS.1st.b2 |PF1_1 | |WS.102.1b
|
||||
PS.1st.b3 |PF1_1 | |WS.102.2a
|
||||
PS.1st.b4 |PF1_1 | |WS.102.2b
|
||||
PS.1st.b5 |PF1_1 | |WS.102.3a
|
||||
PS.1st.b6 |PF1_1 | |WS.102.3b
|
||||
PS.1st.c1 |PF1_1 | |WS.105.1a
|
||||
PS.1st.c2 |PF1_1 | |WS.105.1b
|
||||
PS.1st.c3 |PF1_1 | |WS.105.2a
|
||||
PS.1st.c4 |PF1_1 | |WS.105.2b
|
||||
PS.1st.c5 |PF1_1 | |WS.105.3a
|
||||
PS.1st.c6 |PF1_1 | |WS.105.3b
|
||||
PS.1st.d1 |PF1_1 | |WS.106.1a
|
||||
PS.1st.d2 |PF1_1 | |WS.106.1b
|
||||
PS.1st.d3 |PF1_1 | |WS.106.2a
|
||||
PS.1st.d4 |PF1_1 | |WS.106.2b
|
||||
PS.1st.d5 |PF1_1 | |WS.106.3a
|
||||
PS.1st.d6 |PF1_1 | |WS.106.3b
|
||||
PS.1st.ta1 |PF1_2 | |
|
||||
PS.1st.ta2 |PF1_2 | |
|
||||
PS.1st.ta3 |PF1_2 | |
|
||||
PS.1st.ta4 |PF1_2 | |
|
||||
PS.1st.ta5 |PF1_2 | |
|
||||
PS.1st.ta6 |PF1_2 | |
|
||||
PS.1st.tb1 |PF1_2 | |
|
||||
PS.1st.tb2 |PF1_2 | |
|
||||
PS.1st.tb3 |PF1_2 | |
|
||||
PS.1st.tb4 |PF1_2 | |
|
||||
PS.1st.tb5 |PF1_2 | |
|
||||
PS.1st.tb6 |PF1_2 | |
|
||||
PS.base.a1 |PF0_1 | |WS.001.1a
|
||||
PS.base.a2 |PF0_1 | |WS.001.1b
|
||||
PS.base.a3 |PF0_1 | |WS.001.2a
|
||||
PS.base.a4 |PF0_1 | |WS.001.2b
|
||||
PS.base.a5 |PF0_1 | |WS.001.3a
|
||||
PS.base.a6 |PF0_1 | |WS.001.3b
|
||||
PS.base.b1 |PF0_1 | |WS.002.1a
|
||||
PS.base.b2 |PF0_1 | |WS.002.1b
|
||||
PS.base.b3 |PF0_1 | |WS.002.2a
|
||||
PS.base.b4 |PF0_1 | |WS.002.2b
|
||||
PS.base.b5 |PF0_1 | |WS.002.3a
|
||||
PS.base.b6 |PF0_1 | |WS.002.3b
|
||||
PS.base.c1 |PF0_1 | |WS.003.1a
|
||||
PS.base.c2 |PF0_1 | |WS.003.1b
|
||||
PS.base.c3 |PF0_1 | |WS.003.2a
|
||||
PS.base.c4 |PF0_1 | |WS.003.2b
|
||||
PS.base.c5 |PF0_1 | |WS.003.3a
|
||||
PS.base.c6 |PF0_1 | |WS.003.3b
|
||||
PS.base.ta1 |PF0_2 | |
|
||||
PS.base.ta2 |PF0_2 | |
|
||||
PS.base.ta3 |PF0_2 | |
|
||||
PS.base.ta4 |PF0_2 | |
|
||||
PS.base.ta5 |PF0_2 | |
|
||||
PS.base.ta6 |PF0_2 | |
|
||||
PS.base.tb1 |PF0_2 | |
|
||||
PS.base.tb2 |PF0_2 | |
|
||||
PS.base.tb3 |PF0_2 | |
|
||||
PS.base.tb4 |PF0_2 | |
|
||||
PS.base.tb5 |PF0_2 | |
|
||||
PS.base.tb6 |PF0_2 | |
|
||||
(66 rows)
|
||||
|
||||
QUERY: select * from WSlot order by slotname;
|
||||
slotname | roomno| slotlink|backlink
|
||||
--------------------+--------+--------------------+--------------------
|
||||
WS.001.1a |001 | |PS.base.a1
|
||||
WS.001.1b |001 | |PS.base.a2
|
||||
WS.001.2a |001 | |PS.base.a3
|
||||
WS.001.2b |001 | |PS.base.a4
|
||||
WS.001.3a |001 | |PS.base.a5
|
||||
WS.001.3b |001 | |PS.base.a6
|
||||
WS.002.1a |002 | |PS.base.b1
|
||||
WS.002.1b |002 | |PS.base.b2
|
||||
WS.002.2a |002 | |PS.base.b3
|
||||
WS.002.2b |002 | |PS.base.b4
|
||||
WS.002.3a |002 | |PS.base.b5
|
||||
WS.002.3b |002 | |PS.base.b6
|
||||
WS.003.1a |003 | |PS.base.c1
|
||||
WS.003.1b |003 | |PS.base.c2
|
||||
WS.003.2a |003 | |PS.base.c3
|
||||
WS.003.2b |003 | |PS.base.c4
|
||||
WS.003.3a |003 | |PS.base.c5
|
||||
WS.003.3b |003 | |PS.base.c6
|
||||
WS.101.1a |101 | |PS.1st.a1
|
||||
WS.101.1b |101 | |PS.1st.a2
|
||||
WS.101.2a |101 | |PS.1st.a3
|
||||
WS.101.2b |101 | |PS.1st.a4
|
||||
WS.101.3a |101 | |PS.1st.a5
|
||||
WS.101.3b |101 | |PS.1st.a6
|
||||
WS.102.1a |102 | |PS.1st.b1
|
||||
WS.102.1b |102 | |PS.1st.b2
|
||||
WS.102.2a |102 | |PS.1st.b3
|
||||
WS.102.2b |102 | |PS.1st.b4
|
||||
WS.102.3a |102 | |PS.1st.b5
|
||||
WS.102.3b |102 | |PS.1st.b6
|
||||
WS.105.1a |105 | |PS.1st.c1
|
||||
WS.105.1b |105 | |PS.1st.c2
|
||||
WS.105.2a |105 | |PS.1st.c3
|
||||
WS.105.2b |105 | |PS.1st.c4
|
||||
WS.105.3a |105 | |PS.1st.c5
|
||||
WS.105.3b |105 | |PS.1st.c6
|
||||
WS.106.1a |106 | |PS.1st.d1
|
||||
WS.106.1b |106 | |PS.1st.d2
|
||||
WS.106.2a |106 | |PS.1st.d3
|
||||
WS.106.2b |106 | |PS.1st.d4
|
||||
WS.106.3a |106 | |PS.1st.d5
|
||||
WS.106.3b |106 | |PS.1st.d6
|
||||
(42 rows)
|
||||
|
||||
QUERY: insert into PLine values ('PL.001', '-0', 'Central call', 'PS.base.ta1');
|
||||
QUERY: insert into PLine values ('PL.002', '-101', '', 'PS.base.ta2');
|
||||
QUERY: insert into PLine values ('PL.003', '-102', '', 'PS.base.ta3');
|
||||
QUERY: insert into PLine values ('PL.004', '-103', '', 'PS.base.ta5');
|
||||
QUERY: insert into PLine values ('PL.005', '-104', '', 'PS.base.ta6');
|
||||
QUERY: insert into PLine values ('PL.006', '-106', '', 'PS.base.tb2');
|
||||
QUERY: insert into PLine values ('PL.007', '-108', '', 'PS.base.tb3');
|
||||
QUERY: insert into PLine values ('PL.008', '-109', '', 'PS.base.tb4');
|
||||
QUERY: insert into PLine values ('PL.009', '-121', '', 'PS.base.tb5');
|
||||
QUERY: insert into PLine values ('PL.010', '-122', '', 'PS.base.tb6');
|
||||
QUERY: insert into PLine values ('PL.015', '-134', '', 'PS.1st.ta1');
|
||||
QUERY: insert into PLine values ('PL.016', '-137', '', 'PS.1st.ta3');
|
||||
QUERY: insert into PLine values ('PL.017', '-139', '', 'PS.1st.ta4');
|
||||
QUERY: insert into PLine values ('PL.018', '-362', '', 'PS.1st.tb1');
|
||||
QUERY: insert into PLine values ('PL.019', '-363', '', 'PS.1st.tb2');
|
||||
QUERY: insert into PLine values ('PL.020', '-364', '', 'PS.1st.tb3');
|
||||
QUERY: insert into PLine values ('PL.021', '-365', '', 'PS.1st.tb5');
|
||||
QUERY: insert into PLine values ('PL.022', '-367', '', 'PS.1st.tb6');
|
||||
QUERY: insert into PLine values ('PL.028', '-501', 'Fax entrance', 'PS.base.ta2');
|
||||
QUERY: insert into PLine values ('PL.029', '-502', 'Fax 1st floor', 'PS.1st.ta1');
|
||||
QUERY: insert into PHone values ('PH.hc001', 'Hicom standard', 'WS.001.1a');
|
||||
QUERY: update PSlot set slotlink = 'PS.base.ta1' where slotname = 'PS.base.a1';
|
||||
QUERY: insert into PHone values ('PH.hc002', 'Hicom standard', 'WS.002.1a');
|
||||
QUERY: update PSlot set slotlink = 'PS.base.ta5' where slotname = 'PS.base.b1';
|
||||
QUERY: insert into PHone values ('PH.hc003', 'Hicom standard', 'WS.002.2a');
|
||||
QUERY: update PSlot set slotlink = 'PS.base.tb2' where slotname = 'PS.base.b3';
|
||||
QUERY: insert into PHone values ('PH.fax001', 'Canon fax', 'WS.001.2a');
|
||||
QUERY: update PSlot set slotlink = 'PS.base.ta2' where slotname = 'PS.base.a3';
|
||||
QUERY: insert into Hub values ('base.hub1', 'Patchfield PF0_1 hub', 16);
|
||||
QUERY: insert into System values ('orion', 'PC');
|
||||
QUERY: insert into IFace values ('IF', 'orion', 'eth0', 'WS.002.1b');
|
||||
QUERY: update PSlot set slotlink = 'HS.base.hub1.1' where slotname = 'PS.base.b2';
|
||||
QUERY: select * from PField_v1 where pfname = 'PF0_1' order by slotname;
|
||||
pfname|slotname |backside |patch
|
||||
------+--------------------+--------------------------------------------------------+---------------------------------------------
|
||||
PF0_1 |PS.base.a1 |WS.001.1a in room 001 -> Phone PH.hc001 (Hicom standard)|PS.base.ta1 -> Phone line -0 (Central call)
|
||||
PF0_1 |PS.base.a2 |WS.001.1b in room 001 -> - |-
|
||||
PF0_1 |PS.base.a3 |WS.001.2a in room 001 -> Phone PH.fax001 (Canon fax) |PS.base.ta2 -> Phone line -501 (Fax entrance)
|
||||
PF0_1 |PS.base.a4 |WS.001.2b in room 001 -> - |-
|
||||
PF0_1 |PS.base.a5 |WS.001.3a in room 001 -> - |-
|
||||
PF0_1 |PS.base.a6 |WS.001.3b in room 001 -> - |-
|
||||
PF0_1 |PS.base.b1 |WS.002.1a in room 002 -> Phone PH.hc002 (Hicom standard)|PS.base.ta5 -> Phone line -103
|
||||
PF0_1 |PS.base.b2 |WS.002.1b in room 002 -> orion IF eth0 (PC) |Patchfield PF0_1 hub slot 1
|
||||
PF0_1 |PS.base.b3 |WS.002.2a in room 002 -> Phone PH.hc003 (Hicom standard)|PS.base.tb2 -> Phone line -106
|
||||
PF0_1 |PS.base.b4 |WS.002.2b in room 002 -> - |-
|
||||
PF0_1 |PS.base.b5 |WS.002.3a in room 002 -> - |-
|
||||
PF0_1 |PS.base.b6 |WS.002.3b in room 002 -> - |-
|
||||
PF0_1 |PS.base.c1 |WS.003.1a in room 003 -> - |-
|
||||
PF0_1 |PS.base.c2 |WS.003.1b in room 003 -> - |-
|
||||
PF0_1 |PS.base.c3 |WS.003.2a in room 003 -> - |-
|
||||
PF0_1 |PS.base.c4 |WS.003.2b in room 003 -> - |-
|
||||
PF0_1 |PS.base.c5 |WS.003.3a in room 003 -> - |-
|
||||
PF0_1 |PS.base.c6 |WS.003.3b in room 003 -> - |-
|
||||
(18 rows)
|
||||
|
||||
QUERY: select * from PField_v1 where pfname = 'PF0_2' order by slotname;
|
||||
pfname|slotname |backside |patch
|
||||
------+--------------------+------------------------------+----------------------------------------------------------------------
|
||||
PF0_2 |PS.base.ta1 |Phone line -0 (Central call) |PS.base.a1 -> WS.001.1a in room 001 -> Phone PH.hc001 (Hicom standard)
|
||||
PF0_2 |PS.base.ta2 |Phone line -501 (Fax entrance)|PS.base.a3 -> WS.001.2a in room 001 -> Phone PH.fax001 (Canon fax)
|
||||
PF0_2 |PS.base.ta3 |Phone line -102 |-
|
||||
PF0_2 |PS.base.ta4 |- |-
|
||||
PF0_2 |PS.base.ta5 |Phone line -103 |PS.base.b1 -> WS.002.1a in room 002 -> Phone PH.hc002 (Hicom standard)
|
||||
PF0_2 |PS.base.ta6 |Phone line -104 |-
|
||||
PF0_2 |PS.base.tb1 |- |-
|
||||
PF0_2 |PS.base.tb2 |Phone line -106 |PS.base.b3 -> WS.002.2a in room 002 -> Phone PH.hc003 (Hicom standard)
|
||||
PF0_2 |PS.base.tb3 |Phone line -108 |-
|
||||
PF0_2 |PS.base.tb4 |Phone line -109 |-
|
||||
PF0_2 |PS.base.tb5 |Phone line -121 |-
|
||||
PF0_2 |PS.base.tb6 |Phone line -122 |-
|
||||
(12 rows)
|
||||
|
||||
QUERY: insert into PField values ('PF1_1', 'should fail due to unique index');
|
||||
ERROR: Cannot insert a duplicate key into a unique index
|
||||
QUERY: update PSlot set backlink = 'WS.not.there' where slotname = 'PS.base.a1';
|
||||
ERROR: WS.not.there does not exists
|
||||
QUERY: update PSlot set backlink = 'XX.illegal' where slotname = 'PS.base.a1';
|
||||
ERROR: illegal backlink beginning with XX
|
||||
QUERY: update PSlot set slotlink = 'PS.not.there' where slotname = 'PS.base.a1';
|
||||
ERROR: PS.not.there does not exists
|
||||
QUERY: update PSlot set slotlink = 'XX.illegal' where slotname = 'PS.base.a1';
|
||||
ERROR: illegal slotlink beginning with XX
|
||||
QUERY: insert into HSlot values ('HS', 'base.hub1', 1, '');
|
||||
ERROR: Cannot insert a duplicate key into a unique index
|
||||
QUERY: insert into HSlot values ('HS', 'base.hub1', 20, '');
|
||||
ERROR: no manual manipulation of HSlot
|
||||
QUERY: delete from HSlot;
|
||||
ERROR: no manual manipulation of HSlot
|
||||
QUERY: insert into IFace values ('IF', 'notthere', 'eth0', '');
|
||||
ERROR: system "notthere" does not exist
|
||||
QUERY: insert into IFace values ('IF', 'orion', 'ethernet_interface_name_too_long', '');
|
||||
ERROR: IFace slotname "IF.orion.ethernet_interface_name_too_long" too long (20 char max)
|
680
contrib/plpgsql/test/expected/triggers.out
Normal file
680
contrib/plpgsql/test/expected/triggers.out
Normal file
@ -0,0 +1,680 @@
|
||||
QUERY: create function tg_room_au() returns opaque as '
|
||||
begin
|
||||
if new.roomno != old.roomno then
|
||||
update WSlot set roomno = new.roomno where roomno = old.roomno;
|
||||
end if;
|
||||
return new;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
QUERY: create trigger tg_room_au after update
|
||||
on Room for each row execute procedure tg_room_au();
|
||||
QUERY: create function tg_room_ad() returns opaque as '
|
||||
begin
|
||||
delete from WSlot where roomno = old.roomno;
|
||||
return old;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
QUERY: create trigger tg_room_ad after delete
|
||||
on Room for each row execute procedure tg_room_ad();
|
||||
QUERY: create function tg_wslot_biu() returns opaque as '
|
||||
begin
|
||||
if count(*) = 0 from Room where roomno = new.roomno then
|
||||
raise exception ''Room % does not exist'', new.roomno;
|
||||
end if;
|
||||
return new;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
QUERY: create trigger tg_wslot_biu before insert or update
|
||||
on WSlot for each row execute procedure tg_wslot_biu();
|
||||
QUERY: create function tg_pfield_au() returns opaque as '
|
||||
begin
|
||||
if new.name != old.name then
|
||||
update PSlot set pfname = new.name where pfname = old.name;
|
||||
end if;
|
||||
return new;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
QUERY: create trigger tg_pfield_au after update
|
||||
on PField for each row execute procedure tg_pfield_au();
|
||||
QUERY: create function tg_pfield_ad() returns opaque as '
|
||||
begin
|
||||
delete from PSlot where pfname = old.name;
|
||||
return old;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
QUERY: create trigger tg_pfield_ad after delete
|
||||
on PField for each row execute procedure tg_pfield_ad();
|
||||
QUERY: create function tg_pslot_biu() returns opaque as '
|
||||
declare
|
||||
pfrec record;
|
||||
rename new to ps;
|
||||
begin
|
||||
select into pfrec * from PField where name = ps.pfname;
|
||||
if not found then
|
||||
raise exception ''Patchfield "%" does not exist'', ps.pfname;
|
||||
end if;
|
||||
return ps;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
QUERY: create trigger tg_pslot_biu before insert or update
|
||||
on PSlot for each row execute procedure tg_pslot_biu();
|
||||
QUERY: create function tg_system_au() returns opaque as '
|
||||
begin
|
||||
if new.name != old.name then
|
||||
update IFace set sysname = new.name where sysname = old.name;
|
||||
end if;
|
||||
return new;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
QUERY: create trigger tg_system_au after update
|
||||
on System for each row execute procedure tg_system_au();
|
||||
QUERY: create function tg_iface_biu() returns opaque as '
|
||||
declare
|
||||
sname text;
|
||||
sysrec record;
|
||||
begin
|
||||
select into sysrec * from system where name = new.sysname;
|
||||
if not found then
|
||||
raise exception ''system "%" does not exist'', new.sysname;
|
||||
end if;
|
||||
sname := ''IF.'' || new.sysname;
|
||||
sname := sname || ''.'';
|
||||
sname := sname || new.ifname;
|
||||
if length(sname) > 20 then
|
||||
raise exception ''IFace slotname "%" too long (20 char max)'', sname;
|
||||
end if;
|
||||
new.slotname := sname;
|
||||
return new;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
QUERY: create trigger tg_iface_biu before insert or update
|
||||
on IFace for each row execute procedure tg_iface_biu();
|
||||
QUERY: create function tg_hub_a() returns opaque as '
|
||||
declare
|
||||
hname text;
|
||||
dummy integer;
|
||||
begin
|
||||
if tg_op = ''INSERT'' then
|
||||
dummy := tg_hub_adjustslots(new.name, 0, new.nslots);
|
||||
return new;
|
||||
end if;
|
||||
if tg_op = ''UPDATE'' then
|
||||
if new.name != old.name then
|
||||
update HSlot set hubname = new.name where hubname = old.name;
|
||||
end if;
|
||||
dummy := tg_hub_adjustslots(new.name, old.nslots, new.nslots);
|
||||
return new;
|
||||
end if;
|
||||
if tg_op = ''DELETE'' then
|
||||
dummy := tg_hub_adjustslots(old.name, old.nslots, 0);
|
||||
return old;
|
||||
end if;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
QUERY: create trigger tg_hub_a after insert or update or delete
|
||||
on Hub for each row execute procedure tg_hub_a();
|
||||
QUERY: create function tg_hub_adjustslots(bpchar, integer, integer)
|
||||
returns integer as '
|
||||
declare
|
||||
hname alias for $1;
|
||||
oldnslots alias for $2;
|
||||
newnslots alias for $3;
|
||||
begin
|
||||
if newnslots = oldnslots then
|
||||
return 0;
|
||||
end if;
|
||||
if newnslots < oldnslots then
|
||||
delete from HSlot where hubname = hname and slotno > newnslots;
|
||||
return 0;
|
||||
end if;
|
||||
for i in oldnslots + 1 .. newnslots loop
|
||||
insert into HSlot (slotname, hubname, slotno, slotlink)
|
||||
values (''HS.dummy'', hname, i, '''');
|
||||
end loop;
|
||||
return 0;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
QUERY: create function tg_hslot_biu() returns opaque as '
|
||||
declare
|
||||
sname text;
|
||||
xname HSlot.slotname%TYPE;
|
||||
hubrec record;
|
||||
begin
|
||||
select into hubrec * from Hub where name = new.hubname;
|
||||
if not found then
|
||||
raise exception ''no manual manipulation of HSlot'';
|
||||
end if;
|
||||
if new.slotno < 1 or new.slotno > hubrec.nslots then
|
||||
raise exception ''no manual manipulation of HSlot'';
|
||||
end if;
|
||||
if tg_op = ''UPDATE'' then
|
||||
if new.hubname != old.hubname then
|
||||
if count(*) > 0 from Hub where name = old.hubname then
|
||||
raise exception ''no manual manipulation of HSlot'';
|
||||
end if;
|
||||
end if;
|
||||
end if;
|
||||
sname := ''HS.'' || trim(new.hubname);
|
||||
sname := sname || ''.'';
|
||||
sname := sname || new.slotno::text;
|
||||
if length(sname) > 20 then
|
||||
raise exception ''HSlot slotname "%" too long (20 char max)'', sname;
|
||||
end if;
|
||||
new.slotname := sname;
|
||||
return new;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
QUERY: create trigger tg_hslot_biu before insert or update
|
||||
on HSlot for each row execute procedure tg_hslot_biu();
|
||||
QUERY: create function tg_hslot_bd() returns opaque as '
|
||||
declare
|
||||
hubrec record;
|
||||
begin
|
||||
select into hubrec * from Hub where name = old.hubname;
|
||||
if not found then
|
||||
return old;
|
||||
end if;
|
||||
if old.slotno > hubrec.nslots then
|
||||
return old;
|
||||
end if;
|
||||
raise exception ''no manual manipulation of HSlot'';
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
QUERY: create trigger tg_hslot_bd before delete
|
||||
on HSlot for each row execute procedure tg_hslot_bd();
|
||||
QUERY: create function tg_chkslotname() returns opaque as '
|
||||
begin
|
||||
if substr(new.slotname, 1, 2) != tg_argv[0] then
|
||||
raise exception ''slotname must begin with %'', tg_argv[0];
|
||||
end if;
|
||||
return new;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
QUERY: create trigger tg_chkslotname before insert
|
||||
on PSlot for each row execute procedure tg_chkslotname('PS');
|
||||
QUERY: create trigger tg_chkslotname before insert
|
||||
on WSlot for each row execute procedure tg_chkslotname('WS');
|
||||
QUERY: create trigger tg_chkslotname before insert
|
||||
on PLine for each row execute procedure tg_chkslotname('PL');
|
||||
QUERY: create trigger tg_chkslotname before insert
|
||||
on IFace for each row execute procedure tg_chkslotname('IF');
|
||||
QUERY: create trigger tg_chkslotname before insert
|
||||
on PHone for each row execute procedure tg_chkslotname('PH');
|
||||
QUERY: create function tg_chkslotlink() returns opaque as '
|
||||
begin
|
||||
if new.slotlink isnull then
|
||||
new.slotlink := '''';
|
||||
end if;
|
||||
return new;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
QUERY: create trigger tg_chkslotlink before insert or update
|
||||
on PSlot for each row execute procedure tg_chkslotlink();
|
||||
QUERY: create trigger tg_chkslotlink before insert or update
|
||||
on WSlot for each row execute procedure tg_chkslotlink();
|
||||
QUERY: create trigger tg_chkslotlink before insert or update
|
||||
on IFace for each row execute procedure tg_chkslotlink();
|
||||
QUERY: create trigger tg_chkslotlink before insert or update
|
||||
on HSlot for each row execute procedure tg_chkslotlink();
|
||||
QUERY: create trigger tg_chkslotlink before insert or update
|
||||
on PHone for each row execute procedure tg_chkslotlink();
|
||||
QUERY: create function tg_chkbacklink() returns opaque as '
|
||||
begin
|
||||
if new.backlink isnull then
|
||||
new.backlink := '''';
|
||||
end if;
|
||||
return new;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
QUERY: create trigger tg_chkbacklink before insert or update
|
||||
on PSlot for each row execute procedure tg_chkbacklink();
|
||||
QUERY: create trigger tg_chkbacklink before insert or update
|
||||
on WSlot for each row execute procedure tg_chkbacklink();
|
||||
QUERY: create trigger tg_chkbacklink before insert or update
|
||||
on PLine for each row execute procedure tg_chkbacklink();
|
||||
QUERY: create function tg_pslot_bu() returns opaque as '
|
||||
begin
|
||||
if new.slotname != old.slotname then
|
||||
delete from PSlot where slotname = old.slotname;
|
||||
insert into PSlot (
|
||||
slotname,
|
||||
pfname,
|
||||
slotlink,
|
||||
backlink
|
||||
) values (
|
||||
new.slotname,
|
||||
new.pfname,
|
||||
new.slotlink,
|
||||
new.backlink
|
||||
);
|
||||
return null;
|
||||
end if;
|
||||
return new;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
QUERY: create trigger tg_pslot_bu before update
|
||||
on PSlot for each row execute procedure tg_pslot_bu();
|
||||
QUERY: create function tg_wslot_bu() returns opaque as '
|
||||
begin
|
||||
if new.slotname != old.slotname then
|
||||
delete from WSlot where slotname = old.slotname;
|
||||
insert into WSlot (
|
||||
slotname,
|
||||
roomno,
|
||||
slotlink,
|
||||
backlink
|
||||
) values (
|
||||
new.slotname,
|
||||
new.roomno,
|
||||
new.slotlink,
|
||||
new.backlink
|
||||
);
|
||||
return null;
|
||||
end if;
|
||||
return new;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
QUERY: create trigger tg_wslot_bu before update
|
||||
on WSlot for each row execute procedure tg_Wslot_bu();
|
||||
QUERY: create function tg_pline_bu() returns opaque as '
|
||||
begin
|
||||
if new.slotname != old.slotname then
|
||||
delete from PLine where slotname = old.slotname;
|
||||
insert into PLine (
|
||||
slotname,
|
||||
phonenumber,
|
||||
comment,
|
||||
backlink
|
||||
) values (
|
||||
new.slotname,
|
||||
new.phonenumber,
|
||||
new.comment,
|
||||
new.backlink
|
||||
);
|
||||
return null;
|
||||
end if;
|
||||
return new;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
QUERY: create trigger tg_pline_bu before update
|
||||
on PLine for each row execute procedure tg_pline_bu();
|
||||
QUERY: create function tg_iface_bu() returns opaque as '
|
||||
begin
|
||||
if new.slotname != old.slotname then
|
||||
delete from IFace where slotname = old.slotname;
|
||||
insert into IFace (
|
||||
slotname,
|
||||
sysname,
|
||||
ifname,
|
||||
slotlink
|
||||
) values (
|
||||
new.slotname,
|
||||
new.sysname,
|
||||
new.ifname,
|
||||
new.slotlink
|
||||
);
|
||||
return null;
|
||||
end if;
|
||||
return new;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
QUERY: create trigger tg_iface_bu before update
|
||||
on IFace for each row execute procedure tg_iface_bu();
|
||||
QUERY: create function tg_hslot_bu() returns opaque as '
|
||||
begin
|
||||
if new.slotname != old.slotname or new.hubname != old.hubname then
|
||||
delete from HSlot where slotname = old.slotname;
|
||||
insert into HSlot (
|
||||
slotname,
|
||||
hubname,
|
||||
slotno,
|
||||
slotlink
|
||||
) values (
|
||||
new.slotname,
|
||||
new.hubname,
|
||||
new.slotno,
|
||||
new.slotlink
|
||||
);
|
||||
return null;
|
||||
end if;
|
||||
return new;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
QUERY: create trigger tg_hslot_bu before update
|
||||
on HSlot for each row execute procedure tg_hslot_bu();
|
||||
QUERY: create function tg_phone_bu() returns opaque as '
|
||||
begin
|
||||
if new.slotname != old.slotname then
|
||||
delete from PHone where slotname = old.slotname;
|
||||
insert into PHone (
|
||||
slotname,
|
||||
comment,
|
||||
slotlink
|
||||
) values (
|
||||
new.slotname,
|
||||
new.comment,
|
||||
new.slotlink
|
||||
);
|
||||
return null;
|
||||
end if;
|
||||
return new;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
QUERY: create trigger tg_phone_bu before update
|
||||
on PHone for each row execute procedure tg_phone_bu();
|
||||
QUERY: create function tg_backlink_a() returns opaque as '
|
||||
declare
|
||||
dummy integer;
|
||||
begin
|
||||
if tg_op = ''INSERT'' then
|
||||
if new.backlink != '''' then
|
||||
dummy := tg_backlink_set(new.backlink, new.slotname);
|
||||
end if;
|
||||
return new;
|
||||
end if;
|
||||
if tg_op = ''UPDATE'' then
|
||||
if new.backlink != old.backlink then
|
||||
if old.backlink != '''' then
|
||||
dummy := tg_backlink_unset(old.backlink, old.slotname);
|
||||
end if;
|
||||
if new.backlink != '''' then
|
||||
dummy := tg_backlink_set(new.backlink, new.slotname);
|
||||
end if;
|
||||
else
|
||||
if new.slotname != old.slotname and new.backlink != '''' then
|
||||
dummy := tg_slotlink_set(new.backlink, new.slotname);
|
||||
end if;
|
||||
end if;
|
||||
return new;
|
||||
end if;
|
||||
if tg_op = ''DELETE'' then
|
||||
if old.backlink != '''' then
|
||||
dummy := tg_backlink_unset(old.backlink, old.slotname);
|
||||
end if;
|
||||
return old;
|
||||
end if;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
QUERY: create trigger tg_backlink_a after insert or update or delete
|
||||
on PSlot for each row execute procedure tg_backlink_a('PS');
|
||||
QUERY: create trigger tg_backlink_a after insert or update or delete
|
||||
on WSlot for each row execute procedure tg_backlink_a('WS');
|
||||
QUERY: create trigger tg_backlink_a after insert or update or delete
|
||||
on PLine for each row execute procedure tg_backlink_a('PL');
|
||||
QUERY: create function tg_backlink_set(bpchar, bpchar)
|
||||
returns integer as '
|
||||
declare
|
||||
myname alias for $1;
|
||||
blname alias for $2;
|
||||
mytype char(2);
|
||||
link char(4);
|
||||
rec record;
|
||||
begin
|
||||
mytype := substr(myname, 1, 2);
|
||||
link := mytype || substr(blname, 1, 2);
|
||||
if link = ''PLPL'' then
|
||||
raise exception
|
||||
''backlink between two phone lines does not make sense'';
|
||||
end if;
|
||||
if link in (''PLWS'', ''WSPL'') then
|
||||
raise exception
|
||||
''direct link of phone line to wall slot not permitted'';
|
||||
end if;
|
||||
if mytype = ''PS'' then
|
||||
select into rec * from PSlot where slotname = myname;
|
||||
if not found then
|
||||
raise exception ''% does not exists'', myname;
|
||||
end if;
|
||||
if rec.backlink != blname then
|
||||
update PSlot set backlink = blname where slotname = myname;
|
||||
end if;
|
||||
return 0;
|
||||
end if;
|
||||
if mytype = ''WS'' then
|
||||
select into rec * from WSlot where slotname = myname;
|
||||
if not found then
|
||||
raise exception ''% does not exists'', myname;
|
||||
end if;
|
||||
if rec.backlink != blname then
|
||||
update WSlot set backlink = blname where slotname = myname;
|
||||
end if;
|
||||
return 0;
|
||||
end if;
|
||||
if mytype = ''PL'' then
|
||||
select into rec * from PLine where slotname = myname;
|
||||
if not found then
|
||||
raise exception ''% does not exists'', myname;
|
||||
end if;
|
||||
if rec.backlink != blname then
|
||||
update PLine set backlink = blname where slotname = myname;
|
||||
end if;
|
||||
return 0;
|
||||
end if;
|
||||
raise exception ''illegal backlink beginning with %'', mytype;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
QUERY: create function tg_backlink_unset(bpchar, bpchar)
|
||||
returns integer as '
|
||||
declare
|
||||
myname alias for $1;
|
||||
blname alias for $2;
|
||||
mytype char(2);
|
||||
rec record;
|
||||
begin
|
||||
mytype := substr(myname, 1, 2);
|
||||
if mytype = ''PS'' then
|
||||
select into rec * from PSlot where slotname = myname;
|
||||
if not found then
|
||||
return 0;
|
||||
end if;
|
||||
if rec.backlink = blname then
|
||||
update PSlot set backlink = '''' where slotname = myname;
|
||||
end if;
|
||||
return 0;
|
||||
end if;
|
||||
if mytype = ''WS'' then
|
||||
select into rec * from WSlot where slotname = myname;
|
||||
if not found then
|
||||
return 0;
|
||||
end if;
|
||||
if rec.backlink = blname then
|
||||
update WSlot set backlink = '''' where slotname = myname;
|
||||
end if;
|
||||
return 0;
|
||||
end if;
|
||||
if mytype = ''PL'' then
|
||||
select into rec * from PLine where slotname = myname;
|
||||
if not found then
|
||||
return 0;
|
||||
end if;
|
||||
if rec.backlink = blname then
|
||||
update PLine set backlink = '''' where slotname = myname;
|
||||
end if;
|
||||
return 0;
|
||||
end if;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
QUERY: create function tg_slotlink_a() returns opaque as '
|
||||
declare
|
||||
dummy integer;
|
||||
begin
|
||||
if tg_op = ''INSERT'' then
|
||||
if new.slotlink != '''' then
|
||||
dummy := tg_slotlink_set(new.slotlink, new.slotname);
|
||||
end if;
|
||||
return new;
|
||||
end if;
|
||||
if tg_op = ''UPDATE'' then
|
||||
if new.slotlink != old.slotlink then
|
||||
if old.slotlink != '''' then
|
||||
dummy := tg_slotlink_unset(old.slotlink, old.slotname);
|
||||
end if;
|
||||
if new.slotlink != '''' then
|
||||
dummy := tg_slotlink_set(new.slotlink, new.slotname);
|
||||
end if;
|
||||
else
|
||||
if new.slotname != old.slotname and new.slotlink != '''' then
|
||||
dummy := tg_slotlink_set(new.slotlink, new.slotname);
|
||||
end if;
|
||||
end if;
|
||||
return new;
|
||||
end if;
|
||||
if tg_op = ''DELETE'' then
|
||||
if old.slotlink != '''' then
|
||||
dummy := tg_slotlink_unset(old.slotlink, old.slotname);
|
||||
end if;
|
||||
return old;
|
||||
end if;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
QUERY: create trigger tg_slotlink_a after insert or update or delete
|
||||
on PSlot for each row execute procedure tg_slotlink_a('PS');
|
||||
QUERY: create trigger tg_slotlink_a after insert or update or delete
|
||||
on WSlot for each row execute procedure tg_slotlink_a('WS');
|
||||
QUERY: create trigger tg_slotlink_a after insert or update or delete
|
||||
on IFace for each row execute procedure tg_slotlink_a('IF');
|
||||
QUERY: create trigger tg_slotlink_a after insert or update or delete
|
||||
on HSlot for each row execute procedure tg_slotlink_a('HS');
|
||||
QUERY: create trigger tg_slotlink_a after insert or update or delete
|
||||
on PHone for each row execute procedure tg_slotlink_a('PH');
|
||||
QUERY: create function tg_slotlink_set(bpchar, bpchar)
|
||||
returns integer as '
|
||||
declare
|
||||
myname alias for $1;
|
||||
blname alias for $2;
|
||||
mytype char(2);
|
||||
link char(4);
|
||||
rec record;
|
||||
begin
|
||||
mytype := substr(myname, 1, 2);
|
||||
link := mytype || substr(blname, 1, 2);
|
||||
if link = ''PHPH'' then
|
||||
raise exception
|
||||
''slotlink between two phones does not make sense'';
|
||||
end if;
|
||||
if link in (''PHHS'', ''HSPH'') then
|
||||
raise exception
|
||||
''link of phone to hub does not make sense'';
|
||||
end if;
|
||||
if link in (''PHIF'', ''IFPH'') then
|
||||
raise exception
|
||||
''link of phone to hub does not make sense'';
|
||||
end if;
|
||||
if link in (''PSWS'', ''WSPS'') then
|
||||
raise exception
|
||||
''slotlink from patchslot to wallslot not permitted'';
|
||||
end if;
|
||||
if mytype = ''PS'' then
|
||||
select into rec * from PSlot where slotname = myname;
|
||||
if not found then
|
||||
raise exception ''% does not exists'', myname;
|
||||
end if;
|
||||
if rec.slotlink != blname then
|
||||
update PSlot set slotlink = blname where slotname = myname;
|
||||
end if;
|
||||
return 0;
|
||||
end if;
|
||||
if mytype = ''WS'' then
|
||||
select into rec * from WSlot where slotname = myname;
|
||||
if not found then
|
||||
raise exception ''% does not exists'', myname;
|
||||
end if;
|
||||
if rec.slotlink != blname then
|
||||
update WSlot set slotlink = blname where slotname = myname;
|
||||
end if;
|
||||
return 0;
|
||||
end if;
|
||||
if mytype = ''IF'' then
|
||||
select into rec * from IFace where slotname = myname;
|
||||
if not found then
|
||||
raise exception ''% does not exists'', myname;
|
||||
end if;
|
||||
if rec.slotlink != blname then
|
||||
update IFace set slotlink = blname where slotname = myname;
|
||||
end if;
|
||||
return 0;
|
||||
end if;
|
||||
if mytype = ''HS'' then
|
||||
select into rec * from HSlot where slotname = myname;
|
||||
if not found then
|
||||
raise exception ''% does not exists'', myname;
|
||||
end if;
|
||||
if rec.slotlink != blname then
|
||||
update HSlot set slotlink = blname where slotname = myname;
|
||||
end if;
|
||||
return 0;
|
||||
end if;
|
||||
if mytype = ''PH'' then
|
||||
select into rec * from PHone where slotname = myname;
|
||||
if not found then
|
||||
raise exception ''% does not exists'', myname;
|
||||
end if;
|
||||
if rec.slotlink != blname then
|
||||
update PHone set slotlink = blname where slotname = myname;
|
||||
end if;
|
||||
return 0;
|
||||
end if;
|
||||
raise exception ''illegal slotlink beginning with %'', mytype;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
QUERY: create function tg_slotlink_unset(bpchar, bpchar)
|
||||
returns integer as '
|
||||
declare
|
||||
myname alias for $1;
|
||||
blname alias for $2;
|
||||
mytype char(2);
|
||||
rec record;
|
||||
begin
|
||||
mytype := substr(myname, 1, 2);
|
||||
if mytype = ''PS'' then
|
||||
select into rec * from PSlot where slotname = myname;
|
||||
if not found then
|
||||
return 0;
|
||||
end if;
|
||||
if rec.slotlink = blname then
|
||||
update PSlot set slotlink = '''' where slotname = myname;
|
||||
end if;
|
||||
return 0;
|
||||
end if;
|
||||
if mytype = ''WS'' then
|
||||
select into rec * from WSlot where slotname = myname;
|
||||
if not found then
|
||||
return 0;
|
||||
end if;
|
||||
if rec.slotlink = blname then
|
||||
update WSlot set slotlink = '''' where slotname = myname;
|
||||
end if;
|
||||
return 0;
|
||||
end if;
|
||||
if mytype = ''IF'' then
|
||||
select into rec * from IFace where slotname = myname;
|
||||
if not found then
|
||||
return 0;
|
||||
end if;
|
||||
if rec.slotlink = blname then
|
||||
update IFace set slotlink = '''' where slotname = myname;
|
||||
end if;
|
||||
return 0;
|
||||
end if;
|
||||
if mytype = ''HS'' then
|
||||
select into rec * from HSlot where slotname = myname;
|
||||
if not found then
|
||||
return 0;
|
||||
end if;
|
||||
if rec.slotlink = blname then
|
||||
update HSlot set slotlink = '''' where slotname = myname;
|
||||
end if;
|
||||
return 0;
|
||||
end if;
|
||||
if mytype = ''PH'' then
|
||||
select into rec * from PHone where slotname = myname;
|
||||
if not found then
|
||||
return 0;
|
||||
end if;
|
||||
if rec.slotlink = blname then
|
||||
update PHone set slotlink = '''' where slotname = myname;
|
||||
end if;
|
||||
return 0;
|
||||
end if;
|
||||
end;
|
||||
' language 'plpgsql';
|
120
contrib/plpgsql/test/expected/views.out
Normal file
120
contrib/plpgsql/test/expected/views.out
Normal file
@ -0,0 +1,120 @@
|
||||
QUERY: create function pslot_backlink_view(bpchar)
|
||||
returns text as '
|
||||
<<outer>>
|
||||
declare
|
||||
rec record;
|
||||
bltype char(2);
|
||||
retval text;
|
||||
begin
|
||||
select into rec * from PSlot where slotname = $1;
|
||||
if not found then
|
||||
return '''';
|
||||
end if;
|
||||
if rec.backlink = '''' then
|
||||
return ''-'';
|
||||
end if;
|
||||
bltype := substr(rec.backlink, 1, 2);
|
||||
if bltype = ''PL'' then
|
||||
declare
|
||||
rec record;
|
||||
begin
|
||||
select into rec * from PLine where slotname = outer.rec.backlink;
|
||||
retval := ''Phone line '' || trim(rec.phonenumber);
|
||||
if rec.comment != '''' then
|
||||
retval := retval || '' ('';
|
||||
retval := retval || rec.comment;
|
||||
retval := retval || '')'';
|
||||
end if;
|
||||
return retval;
|
||||
end;
|
||||
end if;
|
||||
if bltype = ''WS'' then
|
||||
select into rec * from WSlot where slotname = rec.backlink;
|
||||
retval := trim(rec.slotname) || '' in room '';
|
||||
retval := retval || trim(rec.roomno);
|
||||
retval := retval || '' -> '';
|
||||
return retval || wslot_slotlink_view(rec.slotname);
|
||||
end if;
|
||||
return rec.backlink;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
QUERY: create function pslot_slotlink_view(bpchar)
|
||||
returns text as '
|
||||
declare
|
||||
psrec record;
|
||||
sltype char(2);
|
||||
retval text;
|
||||
begin
|
||||
select into psrec * from PSlot where slotname = $1;
|
||||
if not found then
|
||||
return '''';
|
||||
end if;
|
||||
if psrec.slotlink = '''' then
|
||||
return ''-'';
|
||||
end if;
|
||||
sltype := substr(psrec.slotlink, 1, 2);
|
||||
if sltype = ''PS'' then
|
||||
retval := trim(psrec.slotlink) || '' -> '';
|
||||
return retval || pslot_backlink_view(psrec.slotlink);
|
||||
end if;
|
||||
if sltype = ''HS'' then
|
||||
retval := comment from Hub H, HSlot HS
|
||||
where HS.slotname = psrec.slotlink
|
||||
and H.name = HS.hubname;
|
||||
retval := retval || '' slot '';
|
||||
retval := retval || slotno::text from HSlot
|
||||
where slotname = psrec.slotlink;
|
||||
return retval;
|
||||
end if;
|
||||
return psrec.slotlink;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
QUERY: create function wslot_slotlink_view(bpchar)
|
||||
returns text as '
|
||||
declare
|
||||
rec record;
|
||||
sltype char(2);
|
||||
retval text;
|
||||
begin
|
||||
select into rec * from WSlot where slotname = $1;
|
||||
if not found then
|
||||
return '''';
|
||||
end if;
|
||||
if rec.slotlink = '''' then
|
||||
return ''-'';
|
||||
end if;
|
||||
sltype := substr(rec.slotlink, 1, 2);
|
||||
if sltype = ''PH'' then
|
||||
select into rec * from PHone where slotname = rec.slotlink;
|
||||
retval := ''Phone '' || trim(rec.slotname);
|
||||
if rec.comment != '''' then
|
||||
retval := retval || '' ('';
|
||||
retval := retval || rec.comment;
|
||||
retval := retval || '')'';
|
||||
end if;
|
||||
return retval;
|
||||
end if;
|
||||
if sltype = ''IF'' then
|
||||
declare
|
||||
syrow System%RowType;
|
||||
ifrow IFace%ROWTYPE;
|
||||
begin
|
||||
select into ifrow * from IFace where slotname = rec.slotlink;
|
||||
select into syrow * from System where name = ifrow.sysname;
|
||||
retval := syrow.name || '' IF '';
|
||||
retval := retval || ifrow.ifname;
|
||||
if syrow.comment != '''' then
|
||||
retval := retval || '' ('';
|
||||
retval := retval || syrow.comment;
|
||||
retval := retval || '')'';
|
||||
end if;
|
||||
return retval;
|
||||
end;
|
||||
end if;
|
||||
return rec.slotlink;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
QUERY: create view Pfield_v1 as select PF.pfname, PF.slotname,
|
||||
pslot_backlink_view(PF.slotname) as backside,
|
||||
pslot_slotlink_view(PF.slotname) as patch
|
||||
from PSlot PF;
|
14
contrib/plpgsql/test/mklang.sql
Normal file
14
contrib/plpgsql/test/mklang.sql
Normal file
@ -0,0 +1,14 @@
|
||||
--
|
||||
-- PL/pgSQL language declaration
|
||||
--
|
||||
-- $Header: /cvsroot/pgsql/contrib/plpgsql/test/Attic/mklang.sql,v 1.1 1998/08/22 12:38:36 momjian Exp $
|
||||
--
|
||||
|
||||
create function plpgsql_call_handler() returns opaque
|
||||
as '/usr/local/pgsql/lib/plpgsql.so'
|
||||
language 'C';
|
||||
|
||||
create trusted procedural language 'plpgsql'
|
||||
handler plpgsql_call_handler
|
||||
lancompiler 'PL/pgSQL';
|
||||
|
49
contrib/plpgsql/test/runtest
Executable file
49
contrib/plpgsql/test/runtest
Executable file
@ -0,0 +1,49 @@
|
||||
#!/bin/sh
|
||||
|
||||
DB=plpgsql_test
|
||||
export DB
|
||||
|
||||
FRONTEND="psql -n -e -q"
|
||||
export FRONTEND
|
||||
|
||||
echo "*** destroy old $DB database ***"
|
||||
destroydb $DB
|
||||
|
||||
echo "*** create new $DB database ***"
|
||||
createdb $DB
|
||||
|
||||
echo "*** install PL/pgSQL ***"
|
||||
$FRONTEND -f mklang.sql -d $DB >/dev/null 2>&1
|
||||
|
||||
echo "*** create tables ***"
|
||||
$FRONTEND -f tables.sql -d $DB >output/tables.out 2>&1
|
||||
if cmp -s output/tables.out expected/tables.out ; then
|
||||
echo "OK"
|
||||
else
|
||||
echo "FAILED"
|
||||
fi
|
||||
|
||||
echo "*** create triggers ***"
|
||||
$FRONTEND -f triggers.sql -d $DB >output/triggers.out 2>&1
|
||||
if cmp -s output/triggers.out expected/triggers.out ; then
|
||||
echo "OK"
|
||||
else
|
||||
echo "FAILED"
|
||||
fi
|
||||
|
||||
echo "*** create views and support functions ***"
|
||||
$FRONTEND -f views.sql -d $DB >output/views.out 2>&1
|
||||
if cmp -s output/views.out expected/views.out ; then
|
||||
echo "OK"
|
||||
else
|
||||
echo "FAILED"
|
||||
fi
|
||||
|
||||
echo "*** running tests ***"
|
||||
$FRONTEND -f test.sql -d $DB >output/test.out 2>&1
|
||||
if cmp -s output/test.out expected/test.out ; then
|
||||
echo "OK"
|
||||
else
|
||||
echo "FAILED"
|
||||
fi
|
||||
|
101
contrib/plpgsql/test/tables.sql
Normal file
101
contrib/plpgsql/test/tables.sql
Normal file
@ -0,0 +1,101 @@
|
||||
-- ************************************************************
|
||||
-- *
|
||||
-- * Tables for the patchfield test of PL/pgSQL
|
||||
-- *
|
||||
-- * $Header: /cvsroot/pgsql/contrib/plpgsql/test/Attic/tables.sql,v 1.1 1998/08/22 12:38:36 momjian Exp $
|
||||
-- *
|
||||
-- ************************************************************
|
||||
|
||||
create table Room (
|
||||
roomno char(8),
|
||||
comment text
|
||||
);
|
||||
|
||||
create unique index Room_rno on Room using btree (roomno bpchar_ops);
|
||||
|
||||
|
||||
create table WSlot (
|
||||
slotname char(20),
|
||||
roomno char(8),
|
||||
slotlink char(20),
|
||||
backlink char(20)
|
||||
);
|
||||
|
||||
create unique index WSlot_name on WSlot using btree (slotname bpchar_ops);
|
||||
|
||||
|
||||
create table PField (
|
||||
name text,
|
||||
comment text
|
||||
);
|
||||
|
||||
create unique index PField_name on PField using btree (name text_ops);
|
||||
|
||||
|
||||
create table PSlot (
|
||||
slotname char(20),
|
||||
pfname text,
|
||||
slotlink char(20),
|
||||
backlink char(20)
|
||||
);
|
||||
|
||||
create unique index PSlot_name on PSlot using btree (slotname bpchar_ops);
|
||||
|
||||
|
||||
create table PLine (
|
||||
slotname char(20),
|
||||
phonenumber char(20),
|
||||
comment text,
|
||||
backlink char(20)
|
||||
);
|
||||
|
||||
create unique index PLine_name on PLine using btree (slotname bpchar_ops);
|
||||
|
||||
|
||||
create table Hub (
|
||||
name char(14),
|
||||
comment text,
|
||||
nslots integer
|
||||
);
|
||||
|
||||
create unique index Hub_name on Hub using btree (name bpchar_ops);
|
||||
|
||||
|
||||
create table HSlot (
|
||||
slotname char(20),
|
||||
hubname char(14),
|
||||
slotno integer,
|
||||
slotlink char(20)
|
||||
);
|
||||
|
||||
create unique index HSlot_name on HSlot using btree (slotname bpchar_ops);
|
||||
create index HSlot_hubname on HSlot using btree (hubname bpchar_ops);
|
||||
|
||||
|
||||
create table System (
|
||||
name text,
|
||||
comment text
|
||||
);
|
||||
|
||||
create unique index System_name on System using btree (name text_ops);
|
||||
|
||||
|
||||
create table IFace (
|
||||
slotname char(20),
|
||||
sysname text,
|
||||
ifname text,
|
||||
slotlink char(20)
|
||||
);
|
||||
|
||||
create unique index IFace_name on IFace using btree (slotname bpchar_ops);
|
||||
|
||||
|
||||
create table PHone (
|
||||
slotname char(20),
|
||||
comment text,
|
||||
slotlink char(20)
|
||||
);
|
||||
|
||||
create unique index PHone_name on PHone using btree (slotname bpchar_ops);
|
||||
|
||||
|
272
contrib/plpgsql/test/test.sql
Normal file
272
contrib/plpgsql/test/test.sql
Normal file
@ -0,0 +1,272 @@
|
||||
--
|
||||
-- First we build the house - so we create the rooms
|
||||
--
|
||||
insert into Room values ('001', 'Entrance');
|
||||
insert into Room values ('002', 'Office');
|
||||
insert into Room values ('003', 'Office');
|
||||
insert into Room values ('004', 'Technical');
|
||||
insert into Room values ('101', 'Office');
|
||||
insert into Room values ('102', 'Conference');
|
||||
insert into Room values ('103', 'Restroom');
|
||||
insert into Room values ('104', 'Technical');
|
||||
insert into Room values ('105', 'Office');
|
||||
insert into Room values ('106', 'Office');
|
||||
|
||||
--
|
||||
-- Second we install the wall connectors
|
||||
--
|
||||
insert into WSlot values ('WS.001.1a', '001', '', '');
|
||||
insert into WSlot values ('WS.001.1b', '001', '', '');
|
||||
insert into WSlot values ('WS.001.2a', '001', '', '');
|
||||
insert into WSlot values ('WS.001.2b', '001', '', '');
|
||||
insert into WSlot values ('WS.001.3a', '001', '', '');
|
||||
insert into WSlot values ('WS.001.3b', '001', '', '');
|
||||
|
||||
insert into WSlot values ('WS.002.1a', '002', '', '');
|
||||
insert into WSlot values ('WS.002.1b', '002', '', '');
|
||||
insert into WSlot values ('WS.002.2a', '002', '', '');
|
||||
insert into WSlot values ('WS.002.2b', '002', '', '');
|
||||
insert into WSlot values ('WS.002.3a', '002', '', '');
|
||||
insert into WSlot values ('WS.002.3b', '002', '', '');
|
||||
|
||||
insert into WSlot values ('WS.003.1a', '003', '', '');
|
||||
insert into WSlot values ('WS.003.1b', '003', '', '');
|
||||
insert into WSlot values ('WS.003.2a', '003', '', '');
|
||||
insert into WSlot values ('WS.003.2b', '003', '', '');
|
||||
insert into WSlot values ('WS.003.3a', '003', '', '');
|
||||
insert into WSlot values ('WS.003.3b', '003', '', '');
|
||||
|
||||
insert into WSlot values ('WS.101.1a', '101', '', '');
|
||||
insert into WSlot values ('WS.101.1b', '101', '', '');
|
||||
insert into WSlot values ('WS.101.2a', '101', '', '');
|
||||
insert into WSlot values ('WS.101.2b', '101', '', '');
|
||||
insert into WSlot values ('WS.101.3a', '101', '', '');
|
||||
insert into WSlot values ('WS.101.3b', '101', '', '');
|
||||
|
||||
insert into WSlot values ('WS.102.1a', '102', '', '');
|
||||
insert into WSlot values ('WS.102.1b', '102', '', '');
|
||||
insert into WSlot values ('WS.102.2a', '102', '', '');
|
||||
insert into WSlot values ('WS.102.2b', '102', '', '');
|
||||
insert into WSlot values ('WS.102.3a', '102', '', '');
|
||||
insert into WSlot values ('WS.102.3b', '102', '', '');
|
||||
|
||||
insert into WSlot values ('WS.105.1a', '105', '', '');
|
||||
insert into WSlot values ('WS.105.1b', '105', '', '');
|
||||
insert into WSlot values ('WS.105.2a', '105', '', '');
|
||||
insert into WSlot values ('WS.105.2b', '105', '', '');
|
||||
insert into WSlot values ('WS.105.3a', '105', '', '');
|
||||
insert into WSlot values ('WS.105.3b', '105', '', '');
|
||||
|
||||
insert into WSlot values ('WS.106.1a', '106', '', '');
|
||||
insert into WSlot values ('WS.106.1b', '106', '', '');
|
||||
insert into WSlot values ('WS.106.2a', '106', '', '');
|
||||
insert into WSlot values ('WS.106.2b', '106', '', '');
|
||||
insert into WSlot values ('WS.106.3a', '106', '', '');
|
||||
insert into WSlot values ('WS.106.3b', '106', '', '');
|
||||
|
||||
--
|
||||
-- Now create the patch fields and their slots
|
||||
--
|
||||
insert into PField values ('PF0_1', 'Wallslots basement');
|
||||
|
||||
--
|
||||
-- The cables for these will be made later, so they are unconnected for now
|
||||
--
|
||||
insert into PSlot values ('PS.base.a1', 'PF0_1', '', '');
|
||||
insert into PSlot values ('PS.base.a2', 'PF0_1', '', '');
|
||||
insert into PSlot values ('PS.base.a3', 'PF0_1', '', '');
|
||||
insert into PSlot values ('PS.base.a4', 'PF0_1', '', '');
|
||||
insert into PSlot values ('PS.base.a5', 'PF0_1', '', '');
|
||||
insert into PSlot values ('PS.base.a6', 'PF0_1', '', '');
|
||||
|
||||
--
|
||||
-- These are already wired to the wall connectors
|
||||
--
|
||||
insert into PSlot values ('PS.base.b1', 'PF0_1', '', 'WS.002.1a');
|
||||
insert into PSlot values ('PS.base.b2', 'PF0_1', '', 'WS.002.1b');
|
||||
insert into PSlot values ('PS.base.b3', 'PF0_1', '', 'WS.002.2a');
|
||||
insert into PSlot values ('PS.base.b4', 'PF0_1', '', 'WS.002.2b');
|
||||
insert into PSlot values ('PS.base.b5', 'PF0_1', '', 'WS.002.3a');
|
||||
insert into PSlot values ('PS.base.b6', 'PF0_1', '', 'WS.002.3b');
|
||||
|
||||
insert into PSlot values ('PS.base.c1', 'PF0_1', '', 'WS.003.1a');
|
||||
insert into PSlot values ('PS.base.c2', 'PF0_1', '', 'WS.003.1b');
|
||||
insert into PSlot values ('PS.base.c3', 'PF0_1', '', 'WS.003.2a');
|
||||
insert into PSlot values ('PS.base.c4', 'PF0_1', '', 'WS.003.2b');
|
||||
insert into PSlot values ('PS.base.c5', 'PF0_1', '', 'WS.003.3a');
|
||||
insert into PSlot values ('PS.base.c6', 'PF0_1', '', 'WS.003.3b');
|
||||
|
||||
--
|
||||
-- This patchfield will be renamed later into PF0_2 - so its
|
||||
-- slots references in pfname should follow
|
||||
--
|
||||
insert into PField values ('PF0_X', 'Phonelines basement');
|
||||
|
||||
insert into PSlot values ('PS.base.ta1', 'PF0_X', '', '');
|
||||
insert into PSlot values ('PS.base.ta2', 'PF0_X', '', '');
|
||||
insert into PSlot values ('PS.base.ta3', 'PF0_X', '', '');
|
||||
insert into PSlot values ('PS.base.ta4', 'PF0_X', '', '');
|
||||
insert into PSlot values ('PS.base.ta5', 'PF0_X', '', '');
|
||||
insert into PSlot values ('PS.base.ta6', 'PF0_X', '', '');
|
||||
|
||||
insert into PSlot values ('PS.base.tb1', 'PF0_X', '', '');
|
||||
insert into PSlot values ('PS.base.tb2', 'PF0_X', '', '');
|
||||
insert into PSlot values ('PS.base.tb3', 'PF0_X', '', '');
|
||||
insert into PSlot values ('PS.base.tb4', 'PF0_X', '', '');
|
||||
insert into PSlot values ('PS.base.tb5', 'PF0_X', '', '');
|
||||
insert into PSlot values ('PS.base.tb6', 'PF0_X', '', '');
|
||||
|
||||
insert into PField values ('PF1_1', 'Wallslots 1st floor');
|
||||
|
||||
insert into PSlot values ('PS.1st.a1', 'PF1_1', '', 'WS.101.1a');
|
||||
insert into PSlot values ('PS.1st.a2', 'PF1_1', '', 'WS.101.1b');
|
||||
insert into PSlot values ('PS.1st.a3', 'PF1_1', '', 'WS.101.2a');
|
||||
insert into PSlot values ('PS.1st.a4', 'PF1_1', '', 'WS.101.2b');
|
||||
insert into PSlot values ('PS.1st.a5', 'PF1_1', '', 'WS.101.3a');
|
||||
insert into PSlot values ('PS.1st.a6', 'PF1_1', '', 'WS.101.3b');
|
||||
|
||||
insert into PSlot values ('PS.1st.b1', 'PF1_1', '', 'WS.102.1a');
|
||||
insert into PSlot values ('PS.1st.b2', 'PF1_1', '', 'WS.102.1b');
|
||||
insert into PSlot values ('PS.1st.b3', 'PF1_1', '', 'WS.102.2a');
|
||||
insert into PSlot values ('PS.1st.b4', 'PF1_1', '', 'WS.102.2b');
|
||||
insert into PSlot values ('PS.1st.b5', 'PF1_1', '', 'WS.102.3a');
|
||||
insert into PSlot values ('PS.1st.b6', 'PF1_1', '', 'WS.102.3b');
|
||||
|
||||
insert into PSlot values ('PS.1st.c1', 'PF1_1', '', 'WS.105.1a');
|
||||
insert into PSlot values ('PS.1st.c2', 'PF1_1', '', 'WS.105.1b');
|
||||
insert into PSlot values ('PS.1st.c3', 'PF1_1', '', 'WS.105.2a');
|
||||
insert into PSlot values ('PS.1st.c4', 'PF1_1', '', 'WS.105.2b');
|
||||
insert into PSlot values ('PS.1st.c5', 'PF1_1', '', 'WS.105.3a');
|
||||
insert into PSlot values ('PS.1st.c6', 'PF1_1', '', 'WS.105.3b');
|
||||
|
||||
insert into PSlot values ('PS.1st.d1', 'PF1_1', '', 'WS.106.1a');
|
||||
insert into PSlot values ('PS.1st.d2', 'PF1_1', '', 'WS.106.1b');
|
||||
insert into PSlot values ('PS.1st.d3', 'PF1_1', '', 'WS.106.2a');
|
||||
insert into PSlot values ('PS.1st.d4', 'PF1_1', '', 'WS.106.2b');
|
||||
insert into PSlot values ('PS.1st.d5', 'PF1_1', '', 'WS.106.3a');
|
||||
insert into PSlot values ('PS.1st.d6', 'PF1_1', '', 'WS.106.3b');
|
||||
|
||||
--
|
||||
-- Now we wire the wall connectors 1a-2a in room 001 to the
|
||||
-- patchfield. In the second update we make an error, and
|
||||
-- correct it after
|
||||
--
|
||||
update PSlot set backlink = 'WS.001.1a' where slotname = 'PS.base.a1';
|
||||
update PSlot set backlink = 'WS.001.1b' where slotname = 'PS.base.a3';
|
||||
select * from WSlot where roomno = '001' order by slotname;
|
||||
select * from PSlot where slotname ~ 'PS.base.a' order by slotname;
|
||||
update PSlot set backlink = 'WS.001.2a' where slotname = 'PS.base.a3';
|
||||
select * from WSlot where roomno = '001' order by slotname;
|
||||
select * from PSlot where slotname ~ 'PS.base.a' order by slotname;
|
||||
update PSlot set backlink = 'WS.001.1b' where slotname = 'PS.base.a2';
|
||||
select * from WSlot where roomno = '001' order by slotname;
|
||||
select * from PSlot where slotname ~ 'PS.base.a' order by slotname;
|
||||
|
||||
--
|
||||
-- Same procedure for 2b-3b but this time updating the WSlot instead
|
||||
-- of the PSlot. Due to the triggers the result is the same:
|
||||
-- WSlot and corresponding PSlot point to each other.
|
||||
--
|
||||
update WSlot set backlink = 'PS.base.a4' where slotname = 'WS.001.2b';
|
||||
update WSlot set backlink = 'PS.base.a6' where slotname = 'WS.001.3a';
|
||||
select * from WSlot where roomno = '001' order by slotname;
|
||||
select * from PSlot where slotname ~ 'PS.base.a' order by slotname;
|
||||
update WSlot set backlink = 'PS.base.a6' where slotname = 'WS.001.3b';
|
||||
select * from WSlot where roomno = '001' order by slotname;
|
||||
select * from PSlot where slotname ~ 'PS.base.a' order by slotname;
|
||||
update WSlot set backlink = 'PS.base.a5' where slotname = 'WS.001.3a';
|
||||
select * from WSlot where roomno = '001' order by slotname;
|
||||
select * from PSlot where slotname ~ 'PS.base.a' order by slotname;
|
||||
|
||||
insert into PField values ('PF1_2', 'Phonelines 1st floor');
|
||||
|
||||
insert into PSlot values ('PS.1st.ta1', 'PF1_2', '', '');
|
||||
insert into PSlot values ('PS.1st.ta2', 'PF1_2', '', '');
|
||||
insert into PSlot values ('PS.1st.ta3', 'PF1_2', '', '');
|
||||
insert into PSlot values ('PS.1st.ta4', 'PF1_2', '', '');
|
||||
insert into PSlot values ('PS.1st.ta5', 'PF1_2', '', '');
|
||||
insert into PSlot values ('PS.1st.ta6', 'PF1_2', '', '');
|
||||
|
||||
insert into PSlot values ('PS.1st.tb1', 'PF1_2', '', '');
|
||||
insert into PSlot values ('PS.1st.tb2', 'PF1_2', '', '');
|
||||
insert into PSlot values ('PS.1st.tb3', 'PF1_2', '', '');
|
||||
insert into PSlot values ('PS.1st.tb4', 'PF1_2', '', '');
|
||||
insert into PSlot values ('PS.1st.tb5', 'PF1_2', '', '');
|
||||
insert into PSlot values ('PS.1st.tb6', 'PF1_2', '', '');
|
||||
|
||||
--
|
||||
-- Fix the wrong name for patchfield PF0_2
|
||||
--
|
||||
update PField set name = 'PF0_2' where name = 'PF0_X';
|
||||
|
||||
select * from PSlot order by slotname;
|
||||
select * from WSlot order by slotname;
|
||||
|
||||
--
|
||||
-- Install the central phone system and create the phone numbers.
|
||||
-- They are weired on insert to the patchfields. Again the
|
||||
-- triggers automatically tell the PSlots to update their
|
||||
-- backlink field.
|
||||
--
|
||||
insert into PLine values ('PL.001', '-0', 'Central call', 'PS.base.ta1');
|
||||
insert into PLine values ('PL.002', '-101', '', 'PS.base.ta2');
|
||||
insert into PLine values ('PL.003', '-102', '', 'PS.base.ta3');
|
||||
insert into PLine values ('PL.004', '-103', '', 'PS.base.ta5');
|
||||
insert into PLine values ('PL.005', '-104', '', 'PS.base.ta6');
|
||||
insert into PLine values ('PL.006', '-106', '', 'PS.base.tb2');
|
||||
insert into PLine values ('PL.007', '-108', '', 'PS.base.tb3');
|
||||
insert into PLine values ('PL.008', '-109', '', 'PS.base.tb4');
|
||||
insert into PLine values ('PL.009', '-121', '', 'PS.base.tb5');
|
||||
insert into PLine values ('PL.010', '-122', '', 'PS.base.tb6');
|
||||
insert into PLine values ('PL.015', '-134', '', 'PS.1st.ta1');
|
||||
insert into PLine values ('PL.016', '-137', '', 'PS.1st.ta3');
|
||||
insert into PLine values ('PL.017', '-139', '', 'PS.1st.ta4');
|
||||
insert into PLine values ('PL.018', '-362', '', 'PS.1st.tb1');
|
||||
insert into PLine values ('PL.019', '-363', '', 'PS.1st.tb2');
|
||||
insert into PLine values ('PL.020', '-364', '', 'PS.1st.tb3');
|
||||
insert into PLine values ('PL.021', '-365', '', 'PS.1st.tb5');
|
||||
insert into PLine values ('PL.022', '-367', '', 'PS.1st.tb6');
|
||||
insert into PLine values ('PL.028', '-501', 'Fax entrance', 'PS.base.ta2');
|
||||
insert into PLine values ('PL.029', '-502', 'Fax 1st floor', 'PS.1st.ta1');
|
||||
|
||||
--
|
||||
-- Buy some phones, plug them into the wall and patch the
|
||||
-- phone lines to the corresponding patchfield slots.
|
||||
--
|
||||
insert into PHone values ('PH.hc001', 'Hicom standard', 'WS.001.1a');
|
||||
update PSlot set slotlink = 'PS.base.ta1' where slotname = 'PS.base.a1';
|
||||
insert into PHone values ('PH.hc002', 'Hicom standard', 'WS.002.1a');
|
||||
update PSlot set slotlink = 'PS.base.ta5' where slotname = 'PS.base.b1';
|
||||
insert into PHone values ('PH.hc003', 'Hicom standard', 'WS.002.2a');
|
||||
update PSlot set slotlink = 'PS.base.tb2' where slotname = 'PS.base.b3';
|
||||
insert into PHone values ('PH.fax001', 'Canon fax', 'WS.001.2a');
|
||||
update PSlot set slotlink = 'PS.base.ta2' where slotname = 'PS.base.a3';
|
||||
|
||||
--
|
||||
-- Install a hub at one of the patchfields, plug a computers
|
||||
-- ethernet interface into the wall and patch it to the hub.
|
||||
--
|
||||
insert into Hub values ('base.hub1', 'Patchfield PF0_1 hub', 16);
|
||||
insert into System values ('orion', 'PC');
|
||||
insert into IFace values ('IF', 'orion', 'eth0', 'WS.002.1b');
|
||||
update PSlot set slotlink = 'HS.base.hub1.1' where slotname = 'PS.base.b2';
|
||||
|
||||
--
|
||||
-- Now we take a look at the patchfield
|
||||
--
|
||||
select * from PField_v1 where pfname = 'PF0_1' order by slotname;
|
||||
select * from PField_v1 where pfname = 'PF0_2' order by slotname;
|
||||
|
||||
--
|
||||
-- Finally we want errors
|
||||
--
|
||||
insert into PField values ('PF1_1', 'should fail due to unique index');
|
||||
update PSlot set backlink = 'WS.not.there' where slotname = 'PS.base.a1';
|
||||
update PSlot set backlink = 'XX.illegal' where slotname = 'PS.base.a1';
|
||||
update PSlot set slotlink = 'PS.not.there' where slotname = 'PS.base.a1';
|
||||
update PSlot set slotlink = 'XX.illegal' where slotname = 'PS.base.a1';
|
||||
insert into HSlot values ('HS', 'base.hub1', 1, '');
|
||||
insert into HSlot values ('HS', 'base.hub1', 20, '');
|
||||
delete from HSlot;
|
||||
insert into IFace values ('IF', 'notthere', 'eth0', '');
|
||||
insert into IFace values ('IF', 'orion', 'ethernet_interface_name_too_long', '');
|
892
contrib/plpgsql/test/triggers.sql
Normal file
892
contrib/plpgsql/test/triggers.sql
Normal file
@ -0,0 +1,892 @@
|
||||
-- ************************************************************
|
||||
-- *
|
||||
-- * Trigger procedures and functions for the patchfield
|
||||
-- * test of PL/pgSQL
|
||||
-- *
|
||||
-- * $Header: /cvsroot/pgsql/contrib/plpgsql/test/Attic/triggers.sql,v 1.1 1998/08/22 12:38:37 momjian Exp $
|
||||
-- *
|
||||
-- ************************************************************
|
||||
|
||||
|
||||
-- ************************************************************
|
||||
-- * AFTER UPDATE on Room
|
||||
-- * - If room no changes let wall slots follow
|
||||
-- ************************************************************
|
||||
create function tg_room_au() returns opaque as '
|
||||
begin
|
||||
if new.roomno != old.roomno then
|
||||
update WSlot set roomno = new.roomno where roomno = old.roomno;
|
||||
end if;
|
||||
return new;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
|
||||
create trigger tg_room_au after update
|
||||
on Room for each row execute procedure tg_room_au();
|
||||
|
||||
|
||||
-- ************************************************************
|
||||
-- * AFTER DELETE on Room
|
||||
-- * - delete wall slots in this room
|
||||
-- ************************************************************
|
||||
create function tg_room_ad() returns opaque as '
|
||||
begin
|
||||
delete from WSlot where roomno = old.roomno;
|
||||
return old;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
|
||||
create trigger tg_room_ad after delete
|
||||
on Room for each row execute procedure tg_room_ad();
|
||||
|
||||
|
||||
-- ************************************************************
|
||||
-- * BEFORE INSERT or UPDATE on WSlot
|
||||
-- * - Check that room exists
|
||||
-- ************************************************************
|
||||
create function tg_wslot_biu() returns opaque as '
|
||||
begin
|
||||
if count(*) = 0 from Room where roomno = new.roomno then
|
||||
raise exception ''Room % does not exist'', new.roomno;
|
||||
end if;
|
||||
return new;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
|
||||
create trigger tg_wslot_biu before insert or update
|
||||
on WSlot for each row execute procedure tg_wslot_biu();
|
||||
|
||||
|
||||
-- ************************************************************
|
||||
-- * AFTER UPDATE on PField
|
||||
-- * - Let PSlots of this field follow
|
||||
-- ************************************************************
|
||||
create function tg_pfield_au() returns opaque as '
|
||||
begin
|
||||
if new.name != old.name then
|
||||
update PSlot set pfname = new.name where pfname = old.name;
|
||||
end if;
|
||||
return new;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
|
||||
create trigger tg_pfield_au after update
|
||||
on PField for each row execute procedure tg_pfield_au();
|
||||
|
||||
|
||||
-- ************************************************************
|
||||
-- * AFTER DELETE on PField
|
||||
-- * - Remove all slots of this patchfield
|
||||
-- ************************************************************
|
||||
create function tg_pfield_ad() returns opaque as '
|
||||
begin
|
||||
delete from PSlot where pfname = old.name;
|
||||
return old;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
|
||||
create trigger tg_pfield_ad after delete
|
||||
on PField for each row execute procedure tg_pfield_ad();
|
||||
|
||||
|
||||
-- ************************************************************
|
||||
-- * BEFORE INSERT or UPDATE on PSlot
|
||||
-- * - Ensure that our patchfield does exist
|
||||
-- ************************************************************
|
||||
create function tg_pslot_biu() returns opaque as '
|
||||
declare
|
||||
pfrec record;
|
||||
rename new to ps;
|
||||
begin
|
||||
select into pfrec * from PField where name = ps.pfname;
|
||||
if not found then
|
||||
raise exception ''Patchfield "%" does not exist'', ps.pfname;
|
||||
end if;
|
||||
return ps;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
|
||||
create trigger tg_pslot_biu before insert or update
|
||||
on PSlot for each row execute procedure tg_pslot_biu();
|
||||
|
||||
|
||||
-- ************************************************************
|
||||
-- * AFTER UPDATE on System
|
||||
-- * - If system name changes let interfaces follow
|
||||
-- ************************************************************
|
||||
create function tg_system_au() returns opaque as '
|
||||
begin
|
||||
if new.name != old.name then
|
||||
update IFace set sysname = new.name where sysname = old.name;
|
||||
end if;
|
||||
return new;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
|
||||
create trigger tg_system_au after update
|
||||
on System for each row execute procedure tg_system_au();
|
||||
|
||||
|
||||
-- ************************************************************
|
||||
-- * BEFORE INSERT or UPDATE on IFace
|
||||
-- * - set the slotname to IF.sysname.ifname
|
||||
-- ************************************************************
|
||||
create function tg_iface_biu() returns opaque as '
|
||||
declare
|
||||
sname text;
|
||||
sysrec record;
|
||||
begin
|
||||
select into sysrec * from system where name = new.sysname;
|
||||
if not found then
|
||||
raise exception ''system "%" does not exist'', new.sysname;
|
||||
end if;
|
||||
sname := ''IF.'' || new.sysname;
|
||||
sname := sname || ''.'';
|
||||
sname := sname || new.ifname;
|
||||
if length(sname) > 20 then
|
||||
raise exception ''IFace slotname "%" too long (20 char max)'', sname;
|
||||
end if;
|
||||
new.slotname := sname;
|
||||
return new;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
|
||||
create trigger tg_iface_biu before insert or update
|
||||
on IFace for each row execute procedure tg_iface_biu();
|
||||
|
||||
|
||||
-- ************************************************************
|
||||
-- * AFTER INSERT or UPDATE or DELETE on Hub
|
||||
-- * - insert/delete/rename slots as required
|
||||
-- ************************************************************
|
||||
create function tg_hub_a() returns opaque as '
|
||||
declare
|
||||
hname text;
|
||||
dummy integer;
|
||||
begin
|
||||
if tg_op = ''INSERT'' then
|
||||
dummy := tg_hub_adjustslots(new.name, 0, new.nslots);
|
||||
return new;
|
||||
end if;
|
||||
if tg_op = ''UPDATE'' then
|
||||
if new.name != old.name then
|
||||
update HSlot set hubname = new.name where hubname = old.name;
|
||||
end if;
|
||||
dummy := tg_hub_adjustslots(new.name, old.nslots, new.nslots);
|
||||
return new;
|
||||
end if;
|
||||
if tg_op = ''DELETE'' then
|
||||
dummy := tg_hub_adjustslots(old.name, old.nslots, 0);
|
||||
return old;
|
||||
end if;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
|
||||
create trigger tg_hub_a after insert or update or delete
|
||||
on Hub for each row execute procedure tg_hub_a();
|
||||
|
||||
|
||||
-- ************************************************************
|
||||
-- * Support function to add/remove slots of Hub
|
||||
-- ************************************************************
|
||||
create function tg_hub_adjustslots(bpchar, integer, integer)
|
||||
returns integer as '
|
||||
declare
|
||||
hname alias for $1;
|
||||
oldnslots alias for $2;
|
||||
newnslots alias for $3;
|
||||
begin
|
||||
if newnslots = oldnslots then
|
||||
return 0;
|
||||
end if;
|
||||
if newnslots < oldnslots then
|
||||
delete from HSlot where hubname = hname and slotno > newnslots;
|
||||
return 0;
|
||||
end if;
|
||||
for i in oldnslots + 1 .. newnslots loop
|
||||
insert into HSlot (slotname, hubname, slotno, slotlink)
|
||||
values (''HS.dummy'', hname, i, '''');
|
||||
end loop;
|
||||
return 0;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
|
||||
|
||||
-- ************************************************************
|
||||
-- * BEFORE INSERT or UPDATE on HSlot
|
||||
-- * - prevent from manual manipulation
|
||||
-- * - set the slotname to HS.hubname.slotno
|
||||
-- ************************************************************
|
||||
create function tg_hslot_biu() returns opaque as '
|
||||
declare
|
||||
sname text;
|
||||
xname HSlot.slotname%TYPE;
|
||||
hubrec record;
|
||||
begin
|
||||
select into hubrec * from Hub where name = new.hubname;
|
||||
if not found then
|
||||
raise exception ''no manual manipulation of HSlot'';
|
||||
end if;
|
||||
if new.slotno < 1 or new.slotno > hubrec.nslots then
|
||||
raise exception ''no manual manipulation of HSlot'';
|
||||
end if;
|
||||
if tg_op = ''UPDATE'' then
|
||||
if new.hubname != old.hubname then
|
||||
if count(*) > 0 from Hub where name = old.hubname then
|
||||
raise exception ''no manual manipulation of HSlot'';
|
||||
end if;
|
||||
end if;
|
||||
end if;
|
||||
sname := ''HS.'' || trim(new.hubname);
|
||||
sname := sname || ''.'';
|
||||
sname := sname || new.slotno::text;
|
||||
if length(sname) > 20 then
|
||||
raise exception ''HSlot slotname "%" too long (20 char max)'', sname;
|
||||
end if;
|
||||
new.slotname := sname;
|
||||
return new;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
|
||||
create trigger tg_hslot_biu before insert or update
|
||||
on HSlot for each row execute procedure tg_hslot_biu();
|
||||
|
||||
|
||||
-- ************************************************************
|
||||
-- * BEFORE DELETE on HSlot
|
||||
-- * - prevent from manual manipulation
|
||||
-- ************************************************************
|
||||
create function tg_hslot_bd() returns opaque as '
|
||||
declare
|
||||
hubrec record;
|
||||
begin
|
||||
select into hubrec * from Hub where name = old.hubname;
|
||||
if not found then
|
||||
return old;
|
||||
end if;
|
||||
if old.slotno > hubrec.nslots then
|
||||
return old;
|
||||
end if;
|
||||
raise exception ''no manual manipulation of HSlot'';
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
|
||||
create trigger tg_hslot_bd before delete
|
||||
on HSlot for each row execute procedure tg_hslot_bd();
|
||||
|
||||
|
||||
-- ************************************************************
|
||||
-- * BEFORE INSERT on all slots
|
||||
-- * - Check name prefix
|
||||
-- ************************************************************
|
||||
create function tg_chkslotname() returns opaque as '
|
||||
begin
|
||||
if substr(new.slotname, 1, 2) != tg_argv[0] then
|
||||
raise exception ''slotname must begin with %'', tg_argv[0];
|
||||
end if;
|
||||
return new;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
|
||||
create trigger tg_chkslotname before insert
|
||||
on PSlot for each row execute procedure tg_chkslotname('PS');
|
||||
|
||||
create trigger tg_chkslotname before insert
|
||||
on WSlot for each row execute procedure tg_chkslotname('WS');
|
||||
|
||||
create trigger tg_chkslotname before insert
|
||||
on PLine for each row execute procedure tg_chkslotname('PL');
|
||||
|
||||
create trigger tg_chkslotname before insert
|
||||
on IFace for each row execute procedure tg_chkslotname('IF');
|
||||
|
||||
create trigger tg_chkslotname before insert
|
||||
on PHone for each row execute procedure tg_chkslotname('PH');
|
||||
|
||||
|
||||
-- ************************************************************
|
||||
-- * BEFORE INSERT or UPDATE on all slots with slotlink
|
||||
-- * - Set slotlink to empty string if NULL value given
|
||||
-- ************************************************************
|
||||
create function tg_chkslotlink() returns opaque as '
|
||||
begin
|
||||
if new.slotlink isnull then
|
||||
new.slotlink := '''';
|
||||
end if;
|
||||
return new;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
|
||||
create trigger tg_chkslotlink before insert or update
|
||||
on PSlot for each row execute procedure tg_chkslotlink();
|
||||
|
||||
create trigger tg_chkslotlink before insert or update
|
||||
on WSlot for each row execute procedure tg_chkslotlink();
|
||||
|
||||
create trigger tg_chkslotlink before insert or update
|
||||
on IFace for each row execute procedure tg_chkslotlink();
|
||||
|
||||
create trigger tg_chkslotlink before insert or update
|
||||
on HSlot for each row execute procedure tg_chkslotlink();
|
||||
|
||||
create trigger tg_chkslotlink before insert or update
|
||||
on PHone for each row execute procedure tg_chkslotlink();
|
||||
|
||||
|
||||
-- ************************************************************
|
||||
-- * BEFORE INSERT or UPDATE on all slots with backlink
|
||||
-- * - Set backlink to empty string if NULL value given
|
||||
-- ************************************************************
|
||||
create function tg_chkbacklink() returns opaque as '
|
||||
begin
|
||||
if new.backlink isnull then
|
||||
new.backlink := '''';
|
||||
end if;
|
||||
return new;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
|
||||
create trigger tg_chkbacklink before insert or update
|
||||
on PSlot for each row execute procedure tg_chkbacklink();
|
||||
|
||||
create trigger tg_chkbacklink before insert or update
|
||||
on WSlot for each row execute procedure tg_chkbacklink();
|
||||
|
||||
create trigger tg_chkbacklink before insert or update
|
||||
on PLine for each row execute procedure tg_chkbacklink();
|
||||
|
||||
|
||||
-- ************************************************************
|
||||
-- * BEFORE UPDATE on PSlot
|
||||
-- * - do delete/insert instead of update if name changes
|
||||
-- ************************************************************
|
||||
create function tg_pslot_bu() returns opaque as '
|
||||
begin
|
||||
if new.slotname != old.slotname then
|
||||
delete from PSlot where slotname = old.slotname;
|
||||
insert into PSlot (
|
||||
slotname,
|
||||
pfname,
|
||||
slotlink,
|
||||
backlink
|
||||
) values (
|
||||
new.slotname,
|
||||
new.pfname,
|
||||
new.slotlink,
|
||||
new.backlink
|
||||
);
|
||||
return null;
|
||||
end if;
|
||||
return new;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
|
||||
create trigger tg_pslot_bu before update
|
||||
on PSlot for each row execute procedure tg_pslot_bu();
|
||||
|
||||
|
||||
-- ************************************************************
|
||||
-- * BEFORE UPDATE on WSlot
|
||||
-- * - do delete/insert instead of update if name changes
|
||||
-- ************************************************************
|
||||
create function tg_wslot_bu() returns opaque as '
|
||||
begin
|
||||
if new.slotname != old.slotname then
|
||||
delete from WSlot where slotname = old.slotname;
|
||||
insert into WSlot (
|
||||
slotname,
|
||||
roomno,
|
||||
slotlink,
|
||||
backlink
|
||||
) values (
|
||||
new.slotname,
|
||||
new.roomno,
|
||||
new.slotlink,
|
||||
new.backlink
|
||||
);
|
||||
return null;
|
||||
end if;
|
||||
return new;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
|
||||
create trigger tg_wslot_bu before update
|
||||
on WSlot for each row execute procedure tg_Wslot_bu();
|
||||
|
||||
|
||||
-- ************************************************************
|
||||
-- * BEFORE UPDATE on PLine
|
||||
-- * - do delete/insert instead of update if name changes
|
||||
-- ************************************************************
|
||||
create function tg_pline_bu() returns opaque as '
|
||||
begin
|
||||
if new.slotname != old.slotname then
|
||||
delete from PLine where slotname = old.slotname;
|
||||
insert into PLine (
|
||||
slotname,
|
||||
phonenumber,
|
||||
comment,
|
||||
backlink
|
||||
) values (
|
||||
new.slotname,
|
||||
new.phonenumber,
|
||||
new.comment,
|
||||
new.backlink
|
||||
);
|
||||
return null;
|
||||
end if;
|
||||
return new;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
|
||||
create trigger tg_pline_bu before update
|
||||
on PLine for each row execute procedure tg_pline_bu();
|
||||
|
||||
|
||||
-- ************************************************************
|
||||
-- * BEFORE UPDATE on IFace
|
||||
-- * - do delete/insert instead of update if name changes
|
||||
-- ************************************************************
|
||||
create function tg_iface_bu() returns opaque as '
|
||||
begin
|
||||
if new.slotname != old.slotname then
|
||||
delete from IFace where slotname = old.slotname;
|
||||
insert into IFace (
|
||||
slotname,
|
||||
sysname,
|
||||
ifname,
|
||||
slotlink
|
||||
) values (
|
||||
new.slotname,
|
||||
new.sysname,
|
||||
new.ifname,
|
||||
new.slotlink
|
||||
);
|
||||
return null;
|
||||
end if;
|
||||
return new;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
|
||||
create trigger tg_iface_bu before update
|
||||
on IFace for each row execute procedure tg_iface_bu();
|
||||
|
||||
|
||||
-- ************************************************************
|
||||
-- * BEFORE UPDATE on HSlot
|
||||
-- * - do delete/insert instead of update if name changes
|
||||
-- ************************************************************
|
||||
create function tg_hslot_bu() returns opaque as '
|
||||
begin
|
||||
if new.slotname != old.slotname or new.hubname != old.hubname then
|
||||
delete from HSlot where slotname = old.slotname;
|
||||
insert into HSlot (
|
||||
slotname,
|
||||
hubname,
|
||||
slotno,
|
||||
slotlink
|
||||
) values (
|
||||
new.slotname,
|
||||
new.hubname,
|
||||
new.slotno,
|
||||
new.slotlink
|
||||
);
|
||||
return null;
|
||||
end if;
|
||||
return new;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
|
||||
create trigger tg_hslot_bu before update
|
||||
on HSlot for each row execute procedure tg_hslot_bu();
|
||||
|
||||
|
||||
-- ************************************************************
|
||||
-- * BEFORE UPDATE on PHone
|
||||
-- * - do delete/insert instead of update if name changes
|
||||
-- ************************************************************
|
||||
create function tg_phone_bu() returns opaque as '
|
||||
begin
|
||||
if new.slotname != old.slotname then
|
||||
delete from PHone where slotname = old.slotname;
|
||||
insert into PHone (
|
||||
slotname,
|
||||
comment,
|
||||
slotlink
|
||||
) values (
|
||||
new.slotname,
|
||||
new.comment,
|
||||
new.slotlink
|
||||
);
|
||||
return null;
|
||||
end if;
|
||||
return new;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
|
||||
create trigger tg_phone_bu before update
|
||||
on PHone for each row execute procedure tg_phone_bu();
|
||||
|
||||
|
||||
-- ************************************************************
|
||||
-- * AFTER INSERT or UPDATE or DELETE on slot with backlink
|
||||
-- * - Ensure that the opponent correctly points back to us
|
||||
-- ************************************************************
|
||||
create function tg_backlink_a() returns opaque as '
|
||||
declare
|
||||
dummy integer;
|
||||
begin
|
||||
if tg_op = ''INSERT'' then
|
||||
if new.backlink != '''' then
|
||||
dummy := tg_backlink_set(new.backlink, new.slotname);
|
||||
end if;
|
||||
return new;
|
||||
end if;
|
||||
if tg_op = ''UPDATE'' then
|
||||
if new.backlink != old.backlink then
|
||||
if old.backlink != '''' then
|
||||
dummy := tg_backlink_unset(old.backlink, old.slotname);
|
||||
end if;
|
||||
if new.backlink != '''' then
|
||||
dummy := tg_backlink_set(new.backlink, new.slotname);
|
||||
end if;
|
||||
else
|
||||
if new.slotname != old.slotname and new.backlink != '''' then
|
||||
dummy := tg_slotlink_set(new.backlink, new.slotname);
|
||||
end if;
|
||||
end if;
|
||||
return new;
|
||||
end if;
|
||||
if tg_op = ''DELETE'' then
|
||||
if old.backlink != '''' then
|
||||
dummy := tg_backlink_unset(old.backlink, old.slotname);
|
||||
end if;
|
||||
return old;
|
||||
end if;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
|
||||
|
||||
create trigger tg_backlink_a after insert or update or delete
|
||||
on PSlot for each row execute procedure tg_backlink_a('PS');
|
||||
|
||||
create trigger tg_backlink_a after insert or update or delete
|
||||
on WSlot for each row execute procedure tg_backlink_a('WS');
|
||||
|
||||
create trigger tg_backlink_a after insert or update or delete
|
||||
on PLine for each row execute procedure tg_backlink_a('PL');
|
||||
|
||||
|
||||
-- ************************************************************
|
||||
-- * Support function to set the opponents backlink field
|
||||
-- * if it does not already point to the requested slot
|
||||
-- ************************************************************
|
||||
create function tg_backlink_set(bpchar, bpchar)
|
||||
returns integer as '
|
||||
declare
|
||||
myname alias for $1;
|
||||
blname alias for $2;
|
||||
mytype char(2);
|
||||
link char(4);
|
||||
rec record;
|
||||
begin
|
||||
mytype := substr(myname, 1, 2);
|
||||
link := mytype || substr(blname, 1, 2);
|
||||
if link = ''PLPL'' then
|
||||
raise exception
|
||||
''backlink between two phone lines does not make sense'';
|
||||
end if;
|
||||
if link in (''PLWS'', ''WSPL'') then
|
||||
raise exception
|
||||
''direct link of phone line to wall slot not permitted'';
|
||||
end if;
|
||||
if mytype = ''PS'' then
|
||||
select into rec * from PSlot where slotname = myname;
|
||||
if not found then
|
||||
raise exception ''% does not exists'', myname;
|
||||
end if;
|
||||
if rec.backlink != blname then
|
||||
update PSlot set backlink = blname where slotname = myname;
|
||||
end if;
|
||||
return 0;
|
||||
end if;
|
||||
if mytype = ''WS'' then
|
||||
select into rec * from WSlot where slotname = myname;
|
||||
if not found then
|
||||
raise exception ''% does not exists'', myname;
|
||||
end if;
|
||||
if rec.backlink != blname then
|
||||
update WSlot set backlink = blname where slotname = myname;
|
||||
end if;
|
||||
return 0;
|
||||
end if;
|
||||
if mytype = ''PL'' then
|
||||
select into rec * from PLine where slotname = myname;
|
||||
if not found then
|
||||
raise exception ''% does not exists'', myname;
|
||||
end if;
|
||||
if rec.backlink != blname then
|
||||
update PLine set backlink = blname where slotname = myname;
|
||||
end if;
|
||||
return 0;
|
||||
end if;
|
||||
raise exception ''illegal backlink beginning with %'', mytype;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
|
||||
|
||||
-- ************************************************************
|
||||
-- * Support function to clear out the backlink field if
|
||||
-- * it still points to specific slot
|
||||
-- ************************************************************
|
||||
create function tg_backlink_unset(bpchar, bpchar)
|
||||
returns integer as '
|
||||
declare
|
||||
myname alias for $1;
|
||||
blname alias for $2;
|
||||
mytype char(2);
|
||||
rec record;
|
||||
begin
|
||||
mytype := substr(myname, 1, 2);
|
||||
if mytype = ''PS'' then
|
||||
select into rec * from PSlot where slotname = myname;
|
||||
if not found then
|
||||
return 0;
|
||||
end if;
|
||||
if rec.backlink = blname then
|
||||
update PSlot set backlink = '''' where slotname = myname;
|
||||
end if;
|
||||
return 0;
|
||||
end if;
|
||||
if mytype = ''WS'' then
|
||||
select into rec * from WSlot where slotname = myname;
|
||||
if not found then
|
||||
return 0;
|
||||
end if;
|
||||
if rec.backlink = blname then
|
||||
update WSlot set backlink = '''' where slotname = myname;
|
||||
end if;
|
||||
return 0;
|
||||
end if;
|
||||
if mytype = ''PL'' then
|
||||
select into rec * from PLine where slotname = myname;
|
||||
if not found then
|
||||
return 0;
|
||||
end if;
|
||||
if rec.backlink = blname then
|
||||
update PLine set backlink = '''' where slotname = myname;
|
||||
end if;
|
||||
return 0;
|
||||
end if;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
|
||||
|
||||
-- ************************************************************
|
||||
-- * AFTER INSERT or UPDATE or DELETE on slot with slotlink
|
||||
-- * - Ensure that the opponent correctly points back to us
|
||||
-- ************************************************************
|
||||
create function tg_slotlink_a() returns opaque as '
|
||||
declare
|
||||
dummy integer;
|
||||
begin
|
||||
if tg_op = ''INSERT'' then
|
||||
if new.slotlink != '''' then
|
||||
dummy := tg_slotlink_set(new.slotlink, new.slotname);
|
||||
end if;
|
||||
return new;
|
||||
end if;
|
||||
if tg_op = ''UPDATE'' then
|
||||
if new.slotlink != old.slotlink then
|
||||
if old.slotlink != '''' then
|
||||
dummy := tg_slotlink_unset(old.slotlink, old.slotname);
|
||||
end if;
|
||||
if new.slotlink != '''' then
|
||||
dummy := tg_slotlink_set(new.slotlink, new.slotname);
|
||||
end if;
|
||||
else
|
||||
if new.slotname != old.slotname and new.slotlink != '''' then
|
||||
dummy := tg_slotlink_set(new.slotlink, new.slotname);
|
||||
end if;
|
||||
end if;
|
||||
return new;
|
||||
end if;
|
||||
if tg_op = ''DELETE'' then
|
||||
if old.slotlink != '''' then
|
||||
dummy := tg_slotlink_unset(old.slotlink, old.slotname);
|
||||
end if;
|
||||
return old;
|
||||
end if;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
|
||||
|
||||
create trigger tg_slotlink_a after insert or update or delete
|
||||
on PSlot for each row execute procedure tg_slotlink_a('PS');
|
||||
|
||||
create trigger tg_slotlink_a after insert or update or delete
|
||||
on WSlot for each row execute procedure tg_slotlink_a('WS');
|
||||
|
||||
create trigger tg_slotlink_a after insert or update or delete
|
||||
on IFace for each row execute procedure tg_slotlink_a('IF');
|
||||
|
||||
create trigger tg_slotlink_a after insert or update or delete
|
||||
on HSlot for each row execute procedure tg_slotlink_a('HS');
|
||||
|
||||
create trigger tg_slotlink_a after insert or update or delete
|
||||
on PHone for each row execute procedure tg_slotlink_a('PH');
|
||||
|
||||
|
||||
-- ************************************************************
|
||||
-- * Support function to set the opponents slotlink field
|
||||
-- * if it does not already point to the requested slot
|
||||
-- ************************************************************
|
||||
create function tg_slotlink_set(bpchar, bpchar)
|
||||
returns integer as '
|
||||
declare
|
||||
myname alias for $1;
|
||||
blname alias for $2;
|
||||
mytype char(2);
|
||||
link char(4);
|
||||
rec record;
|
||||
begin
|
||||
mytype := substr(myname, 1, 2);
|
||||
link := mytype || substr(blname, 1, 2);
|
||||
if link = ''PHPH'' then
|
||||
raise exception
|
||||
''slotlink between two phones does not make sense'';
|
||||
end if;
|
||||
if link in (''PHHS'', ''HSPH'') then
|
||||
raise exception
|
||||
''link of phone to hub does not make sense'';
|
||||
end if;
|
||||
if link in (''PHIF'', ''IFPH'') then
|
||||
raise exception
|
||||
''link of phone to hub does not make sense'';
|
||||
end if;
|
||||
if link in (''PSWS'', ''WSPS'') then
|
||||
raise exception
|
||||
''slotlink from patchslot to wallslot not permitted'';
|
||||
end if;
|
||||
if mytype = ''PS'' then
|
||||
select into rec * from PSlot where slotname = myname;
|
||||
if not found then
|
||||
raise exception ''% does not exists'', myname;
|
||||
end if;
|
||||
if rec.slotlink != blname then
|
||||
update PSlot set slotlink = blname where slotname = myname;
|
||||
end if;
|
||||
return 0;
|
||||
end if;
|
||||
if mytype = ''WS'' then
|
||||
select into rec * from WSlot where slotname = myname;
|
||||
if not found then
|
||||
raise exception ''% does not exists'', myname;
|
||||
end if;
|
||||
if rec.slotlink != blname then
|
||||
update WSlot set slotlink = blname where slotname = myname;
|
||||
end if;
|
||||
return 0;
|
||||
end if;
|
||||
if mytype = ''IF'' then
|
||||
select into rec * from IFace where slotname = myname;
|
||||
if not found then
|
||||
raise exception ''% does not exists'', myname;
|
||||
end if;
|
||||
if rec.slotlink != blname then
|
||||
update IFace set slotlink = blname where slotname = myname;
|
||||
end if;
|
||||
return 0;
|
||||
end if;
|
||||
if mytype = ''HS'' then
|
||||
select into rec * from HSlot where slotname = myname;
|
||||
if not found then
|
||||
raise exception ''% does not exists'', myname;
|
||||
end if;
|
||||
if rec.slotlink != blname then
|
||||
update HSlot set slotlink = blname where slotname = myname;
|
||||
end if;
|
||||
return 0;
|
||||
end if;
|
||||
if mytype = ''PH'' then
|
||||
select into rec * from PHone where slotname = myname;
|
||||
if not found then
|
||||
raise exception ''% does not exists'', myname;
|
||||
end if;
|
||||
if rec.slotlink != blname then
|
||||
update PHone set slotlink = blname where slotname = myname;
|
||||
end if;
|
||||
return 0;
|
||||
end if;
|
||||
raise exception ''illegal slotlink beginning with %'', mytype;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
|
||||
|
||||
-- ************************************************************
|
||||
-- * Support function to clear out the slotlink field if
|
||||
-- * it still points to specific slot
|
||||
-- ************************************************************
|
||||
create function tg_slotlink_unset(bpchar, bpchar)
|
||||
returns integer as '
|
||||
declare
|
||||
myname alias for $1;
|
||||
blname alias for $2;
|
||||
mytype char(2);
|
||||
rec record;
|
||||
begin
|
||||
mytype := substr(myname, 1, 2);
|
||||
if mytype = ''PS'' then
|
||||
select into rec * from PSlot where slotname = myname;
|
||||
if not found then
|
||||
return 0;
|
||||
end if;
|
||||
if rec.slotlink = blname then
|
||||
update PSlot set slotlink = '''' where slotname = myname;
|
||||
end if;
|
||||
return 0;
|
||||
end if;
|
||||
if mytype = ''WS'' then
|
||||
select into rec * from WSlot where slotname = myname;
|
||||
if not found then
|
||||
return 0;
|
||||
end if;
|
||||
if rec.slotlink = blname then
|
||||
update WSlot set slotlink = '''' where slotname = myname;
|
||||
end if;
|
||||
return 0;
|
||||
end if;
|
||||
if mytype = ''IF'' then
|
||||
select into rec * from IFace where slotname = myname;
|
||||
if not found then
|
||||
return 0;
|
||||
end if;
|
||||
if rec.slotlink = blname then
|
||||
update IFace set slotlink = '''' where slotname = myname;
|
||||
end if;
|
||||
return 0;
|
||||
end if;
|
||||
if mytype = ''HS'' then
|
||||
select into rec * from HSlot where slotname = myname;
|
||||
if not found then
|
||||
return 0;
|
||||
end if;
|
||||
if rec.slotlink = blname then
|
||||
update HSlot set slotlink = '''' where slotname = myname;
|
||||
end if;
|
||||
return 0;
|
||||
end if;
|
||||
if mytype = ''PH'' then
|
||||
select into rec * from PHone where slotname = myname;
|
||||
if not found then
|
||||
return 0;
|
||||
end if;
|
||||
if rec.slotlink = blname then
|
||||
update PHone set slotlink = '''' where slotname = myname;
|
||||
end if;
|
||||
return 0;
|
||||
end if;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
|
||||
|
141
contrib/plpgsql/test/views.sql
Normal file
141
contrib/plpgsql/test/views.sql
Normal file
@ -0,0 +1,141 @@
|
||||
-- ************************************************************
|
||||
-- * Describe the backside of a patchfield slot
|
||||
-- ************************************************************
|
||||
create function pslot_backlink_view(bpchar)
|
||||
returns text as '
|
||||
<<outer>>
|
||||
declare
|
||||
rec record;
|
||||
bltype char(2);
|
||||
retval text;
|
||||
begin
|
||||
select into rec * from PSlot where slotname = $1;
|
||||
if not found then
|
||||
return '''';
|
||||
end if;
|
||||
if rec.backlink = '''' then
|
||||
return ''-'';
|
||||
end if;
|
||||
bltype := substr(rec.backlink, 1, 2);
|
||||
if bltype = ''PL'' then
|
||||
declare
|
||||
rec record;
|
||||
begin
|
||||
select into rec * from PLine where slotname = outer.rec.backlink;
|
||||
retval := ''Phone line '' || trim(rec.phonenumber);
|
||||
if rec.comment != '''' then
|
||||
retval := retval || '' ('';
|
||||
retval := retval || rec.comment;
|
||||
retval := retval || '')'';
|
||||
end if;
|
||||
return retval;
|
||||
end;
|
||||
end if;
|
||||
if bltype = ''WS'' then
|
||||
select into rec * from WSlot where slotname = rec.backlink;
|
||||
retval := trim(rec.slotname) || '' in room '';
|
||||
retval := retval || trim(rec.roomno);
|
||||
retval := retval || '' -> '';
|
||||
return retval || wslot_slotlink_view(rec.slotname);
|
||||
end if;
|
||||
return rec.backlink;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
|
||||
|
||||
-- ************************************************************
|
||||
-- * Describe the front of a patchfield slot
|
||||
-- ************************************************************
|
||||
create function pslot_slotlink_view(bpchar)
|
||||
returns text as '
|
||||
declare
|
||||
psrec record;
|
||||
sltype char(2);
|
||||
retval text;
|
||||
begin
|
||||
select into psrec * from PSlot where slotname = $1;
|
||||
if not found then
|
||||
return '''';
|
||||
end if;
|
||||
if psrec.slotlink = '''' then
|
||||
return ''-'';
|
||||
end if;
|
||||
sltype := substr(psrec.slotlink, 1, 2);
|
||||
if sltype = ''PS'' then
|
||||
retval := trim(psrec.slotlink) || '' -> '';
|
||||
return retval || pslot_backlink_view(psrec.slotlink);
|
||||
end if;
|
||||
if sltype = ''HS'' then
|
||||
retval := comment from Hub H, HSlot HS
|
||||
where HS.slotname = psrec.slotlink
|
||||
and H.name = HS.hubname;
|
||||
retval := retval || '' slot '';
|
||||
retval := retval || slotno::text from HSlot
|
||||
where slotname = psrec.slotlink;
|
||||
return retval;
|
||||
end if;
|
||||
return psrec.slotlink;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
|
||||
|
||||
-- ************************************************************
|
||||
-- * Describe the front of a wall connector slot
|
||||
-- ************************************************************
|
||||
create function wslot_slotlink_view(bpchar)
|
||||
returns text as '
|
||||
declare
|
||||
rec record;
|
||||
sltype char(2);
|
||||
retval text;
|
||||
begin
|
||||
select into rec * from WSlot where slotname = $1;
|
||||
if not found then
|
||||
return '''';
|
||||
end if;
|
||||
if rec.slotlink = '''' then
|
||||
return ''-'';
|
||||
end if;
|
||||
sltype := substr(rec.slotlink, 1, 2);
|
||||
if sltype = ''PH'' then
|
||||
select into rec * from PHone where slotname = rec.slotlink;
|
||||
retval := ''Phone '' || trim(rec.slotname);
|
||||
if rec.comment != '''' then
|
||||
retval := retval || '' ('';
|
||||
retval := retval || rec.comment;
|
||||
retval := retval || '')'';
|
||||
end if;
|
||||
return retval;
|
||||
end if;
|
||||
if sltype = ''IF'' then
|
||||
declare
|
||||
syrow System%RowType;
|
||||
ifrow IFace%ROWTYPE;
|
||||
begin
|
||||
select into ifrow * from IFace where slotname = rec.slotlink;
|
||||
select into syrow * from System where name = ifrow.sysname;
|
||||
retval := syrow.name || '' IF '';
|
||||
retval := retval || ifrow.ifname;
|
||||
if syrow.comment != '''' then
|
||||
retval := retval || '' ('';
|
||||
retval := retval || syrow.comment;
|
||||
retval := retval || '')'';
|
||||
end if;
|
||||
return retval;
|
||||
end;
|
||||
end if;
|
||||
return rec.slotlink;
|
||||
end;
|
||||
' language 'plpgsql';
|
||||
|
||||
|
||||
|
||||
-- ************************************************************
|
||||
-- * View of a patchfield describing backside and patches
|
||||
-- ************************************************************
|
||||
create view Pfield_v1 as select PF.pfname, PF.slotname,
|
||||
pslot_backlink_view(PF.slotname) as backside,
|
||||
pslot_slotlink_view(PF.slotname) as patch
|
||||
from PSlot PF;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user