Commit 863a6206 authored by Bruce Momjian's avatar Bruce Momjian

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
parent 212f7bdf
...@@ -58,6 +58,10 @@ mSQL-interface - ...@@ -58,6 +58,10 @@ mSQL-interface -
noupdate - noupdate -
trigger to prevent updates on single columns trigger to prevent updates on single columns
plpgsql -
Postgres procedural language
by Jan Wieck <jwieck@debis.com>
pginterface - pginterface -
A crude C/4GL A crude C/4GL
by Bruce Momjian <root@candle.pha.pa.us> by Bruce Momjian <root@candle.pha.pa.us>
......
This diff is collapsed.
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.
This diff is collapsed.
--
-- 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';
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/**********************************************************************
* 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);
}
This diff is collapsed.
%{
/**********************************************************************
* 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;
}
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.
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);
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
--
-- 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';
#!/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
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment