mirror of https://github.com/postgres/postgres
PLs to use the standard pg_regress infrastructure. No changes in the tests themselves. Andrew DunstanREL8_1_STABLE
parent
1ea069b1f6
commit
0ff7a2c2ad
@ -1,26 +0,0 @@ |
|||||||
#!/bin/sh |
|
||||||
|
|
||||||
DBNAME=plperl_test |
|
||||||
export DBNAME |
|
||||||
|
|
||||||
echo "**** Destroy old database $DBNAME ****" |
|
||||||
dropdb $DBNAME |
|
||||||
|
|
||||||
sleep 1 |
|
||||||
|
|
||||||
echo "**** Create test database $DBNAME ****" |
|
||||||
createdb $DBNAME || exit 1 |
|
||||||
|
|
||||||
echo "**** Create procedural language plperl ****" |
|
||||||
createlang plperl $DBNAME || exit 1 |
|
||||||
|
|
||||||
echo "**** Running test queries ****" |
|
||||||
psql -q -n -e $DBNAME <test_queries.sql > test.out 2>&1 |
|
||||||
|
|
||||||
if diff test.expected test.out >/dev/null 2>&1 ; then |
|
||||||
echo " Tests passed O.K." |
|
||||||
rm test.out |
|
||||||
else |
|
||||||
echo " Tests failed - look at diffs between" |
|
||||||
echo " test.expected and test.out" |
|
||||||
fi |
|
@ -0,0 +1,5 @@ |
|||||||
|
-- |
||||||
|
-- For paranoia's sake, don't leave an untrusted language sitting around |
||||||
|
-- |
||||||
|
SET client_min_messages = WARNING; |
||||||
|
DROP PROCEDURAL LANGUAGE plpythonu CASCADE; |
@ -0,0 +1,276 @@ |
|||||||
|
CREATE FUNCTION global_test_one() returns text |
||||||
|
AS |
||||||
|
'if not SD.has_key("global_test"): |
||||||
|
SD["global_test"] = "set by global_test_one" |
||||||
|
if not GD.has_key("global_test"): |
||||||
|
GD["global_test"] = "set by global_test_one" |
||||||
|
return "SD: " + SD["global_test"] + ", GD: " + GD["global_test"]' |
||||||
|
LANGUAGE plpythonu; |
||||||
|
CREATE FUNCTION global_test_two() returns text |
||||||
|
AS |
||||||
|
'if not SD.has_key("global_test"): |
||||||
|
SD["global_test"] = "set by global_test_two" |
||||||
|
if not GD.has_key("global_test"): |
||||||
|
GD["global_test"] = "set by global_test_two" |
||||||
|
return "SD: " + SD["global_test"] + ", GD: " + GD["global_test"]' |
||||||
|
LANGUAGE plpythonu; |
||||||
|
CREATE FUNCTION static_test() returns int4 |
||||||
|
AS |
||||||
|
'if SD.has_key("call"): |
||||||
|
SD["call"] = SD["call"] + 1 |
||||||
|
else: |
||||||
|
SD["call"] = 1 |
||||||
|
return SD["call"] |
||||||
|
' |
||||||
|
LANGUAGE plpythonu; |
||||||
|
-- import python modules |
||||||
|
CREATE FUNCTION import_fail() returns text |
||||||
|
AS |
||||||
|
'try: |
||||||
|
import foosocket |
||||||
|
except Exception, ex: |
||||||
|
plpy.notice("import socket failed -- %s" % str(ex)) |
||||||
|
return "failed as expected" |
||||||
|
return "succeeded, that wasn''t supposed to happen"' |
||||||
|
LANGUAGE plpythonu; |
||||||
|
CREATE FUNCTION import_succeed() returns text |
||||||
|
AS |
||||||
|
'try: |
||||||
|
import array |
||||||
|
import bisect |
||||||
|
import calendar |
||||||
|
import cmath |
||||||
|
import errno |
||||||
|
import math |
||||||
|
import md5 |
||||||
|
import operator |
||||||
|
import random |
||||||
|
import re |
||||||
|
import sha |
||||||
|
import string |
||||||
|
import time |
||||||
|
import whrandom |
||||||
|
except Exception, ex: |
||||||
|
plpy.notice("import failed -- %s" % str(ex)) |
||||||
|
return "failed, that wasn''t supposed to happen" |
||||||
|
return "succeeded, as expected"' |
||||||
|
LANGUAGE plpythonu; |
||||||
|
CREATE FUNCTION import_test_one(text) RETURNS text |
||||||
|
AS |
||||||
|
'import sha |
||||||
|
digest = sha.new(args[0]) |
||||||
|
return digest.hexdigest()' |
||||||
|
LANGUAGE plpythonu; |
||||||
|
CREATE FUNCTION import_test_two(users) RETURNS text |
||||||
|
AS |
||||||
|
'import sha |
||||||
|
plain = args[0]["fname"] + args[0]["lname"] |
||||||
|
digest = sha.new(plain); |
||||||
|
return "sha hash of " + plain + " is " + digest.hexdigest()' |
||||||
|
LANGUAGE plpythonu; |
||||||
|
CREATE FUNCTION argument_test_one(users, text, text) RETURNS text |
||||||
|
AS |
||||||
|
'keys = args[0].keys() |
||||||
|
keys.sort() |
||||||
|
out = [] |
||||||
|
for key in keys: |
||||||
|
out.append("%s: %s" % (key, args[0][key])) |
||||||
|
words = args[1] + " " + args[2] + " => {" + ", ".join(out) + "}" |
||||||
|
return words' |
||||||
|
LANGUAGE plpythonu; |
||||||
|
-- these triggers are dedicated to HPHC of RI who |
||||||
|
-- decided that my kid's name was william not willem, and |
||||||
|
-- vigorously resisted all efforts at correction. they have |
||||||
|
-- since gone bankrupt... |
||||||
|
CREATE FUNCTION users_insert() returns trigger |
||||||
|
AS |
||||||
|
'if TD["new"]["fname"] == None or TD["new"]["lname"] == None: |
||||||
|
return "SKIP" |
||||||
|
if TD["new"]["username"] == None: |
||||||
|
TD["new"]["username"] = TD["new"]["fname"][:1] + "_" + TD["new"]["lname"] |
||||||
|
rv = "MODIFY" |
||||||
|
else: |
||||||
|
rv = None |
||||||
|
if TD["new"]["fname"] == "william": |
||||||
|
TD["new"]["fname"] = TD["args"][0] |
||||||
|
rv = "MODIFY" |
||||||
|
return rv' |
||||||
|
LANGUAGE plpythonu; |
||||||
|
CREATE FUNCTION users_update() returns trigger |
||||||
|
AS |
||||||
|
'if TD["event"] == "UPDATE": |
||||||
|
if TD["old"]["fname"] != TD["new"]["fname"] and TD["old"]["fname"] == TD["args"][0]: |
||||||
|
return "SKIP" |
||||||
|
return None' |
||||||
|
LANGUAGE plpythonu; |
||||||
|
CREATE FUNCTION users_delete() RETURNS trigger |
||||||
|
AS |
||||||
|
'if TD["old"]["fname"] == TD["args"][0]: |
||||||
|
return "SKIP" |
||||||
|
return None' |
||||||
|
LANGUAGE plpythonu; |
||||||
|
CREATE TRIGGER users_insert_trig BEFORE INSERT ON users FOR EACH ROW |
||||||
|
EXECUTE PROCEDURE users_insert ('willem'); |
||||||
|
CREATE TRIGGER users_update_trig BEFORE UPDATE ON users FOR EACH ROW |
||||||
|
EXECUTE PROCEDURE users_update ('willem'); |
||||||
|
CREATE TRIGGER users_delete_trig BEFORE DELETE ON users FOR EACH ROW |
||||||
|
EXECUTE PROCEDURE users_delete ('willem'); |
||||||
|
-- nested calls |
||||||
|
-- |
||||||
|
CREATE FUNCTION nested_call_one(text) RETURNS text |
||||||
|
AS |
||||||
|
'q = "SELECT nested_call_two(''%s'')" % args[0] |
||||||
|
r = plpy.execute(q) |
||||||
|
return r[0]' |
||||||
|
LANGUAGE plpythonu ; |
||||||
|
CREATE FUNCTION nested_call_two(text) RETURNS text |
||||||
|
AS |
||||||
|
'q = "SELECT nested_call_three(''%s'')" % args[0] |
||||||
|
r = plpy.execute(q) |
||||||
|
return r[0]' |
||||||
|
LANGUAGE plpythonu ; |
||||||
|
CREATE FUNCTION nested_call_three(text) RETURNS text |
||||||
|
AS |
||||||
|
'return args[0]' |
||||||
|
LANGUAGE plpythonu ; |
||||||
|
-- some spi stuff |
||||||
|
CREATE FUNCTION spi_prepared_plan_test_one(text) RETURNS text |
||||||
|
AS |
||||||
|
'if not SD.has_key("myplan"): |
||||||
|
q = "SELECT count(*) FROM users WHERE lname = $1" |
||||||
|
SD["myplan"] = plpy.prepare(q, [ "text" ]) |
||||||
|
try: |
||||||
|
rv = plpy.execute(SD["myplan"], [args[0]]) |
||||||
|
return "there are " + str(rv[0]["count"]) + " " + str(args[0]) + "s" |
||||||
|
except Exception, ex: |
||||||
|
plpy.error(str(ex)) |
||||||
|
return None |
||||||
|
' |
||||||
|
LANGUAGE plpythonu; |
||||||
|
CREATE FUNCTION spi_prepared_plan_test_nested(text) RETURNS text |
||||||
|
AS |
||||||
|
'if not SD.has_key("myplan"): |
||||||
|
q = "SELECT spi_prepared_plan_test_one(''%s'') as count" % args[0] |
||||||
|
SD["myplan"] = plpy.prepare(q) |
||||||
|
try: |
||||||
|
rv = plpy.execute(SD["myplan"]) |
||||||
|
if len(rv): |
||||||
|
return rv[0]["count"] |
||||||
|
except Exception, ex: |
||||||
|
plpy.error(str(ex)) |
||||||
|
return None |
||||||
|
' |
||||||
|
LANGUAGE plpythonu; |
||||||
|
/* really stupid function just to get the module loaded |
||||||
|
*/ |
||||||
|
CREATE FUNCTION stupid() RETURNS text AS 'return "zarkon"' LANGUAGE plpythonu; |
||||||
|
/* a typo |
||||||
|
*/ |
||||||
|
CREATE FUNCTION invalid_type_uncaught(text) RETURNS text |
||||||
|
AS |
||||||
|
'if not SD.has_key("plan"): |
||||||
|
q = "SELECT fname FROM users WHERE lname = $1" |
||||||
|
SD["plan"] = plpy.prepare(q, [ "test" ]) |
||||||
|
rv = plpy.execute(SD["plan"], [ args[0] ]) |
||||||
|
if len(rv): |
||||||
|
return rv[0]["fname"] |
||||||
|
return None |
||||||
|
' |
||||||
|
LANGUAGE plpythonu; |
||||||
|
/* for what it's worth catch the exception generated by |
||||||
|
* the typo, and return None |
||||||
|
*/ |
||||||
|
CREATE FUNCTION invalid_type_caught(text) RETURNS text |
||||||
|
AS |
||||||
|
'if not SD.has_key("plan"): |
||||||
|
q = "SELECT fname FROM users WHERE lname = $1" |
||||||
|
try: |
||||||
|
SD["plan"] = plpy.prepare(q, [ "test" ]) |
||||||
|
except plpy.SPIError, ex: |
||||||
|
plpy.notice(str(ex)) |
||||||
|
return None |
||||||
|
rv = plpy.execute(SD["plan"], [ args[0] ]) |
||||||
|
if len(rv): |
||||||
|
return rv[0]["fname"] |
||||||
|
return None |
||||||
|
' |
||||||
|
LANGUAGE plpythonu; |
||||||
|
/* for what it's worth catch the exception generated by |
||||||
|
* the typo, and reraise it as a plain error |
||||||
|
*/ |
||||||
|
CREATE FUNCTION invalid_type_reraised(text) RETURNS text |
||||||
|
AS |
||||||
|
'if not SD.has_key("plan"): |
||||||
|
q = "SELECT fname FROM users WHERE lname = $1" |
||||||
|
try: |
||||||
|
SD["plan"] = plpy.prepare(q, [ "test" ]) |
||||||
|
except plpy.SPIError, ex: |
||||||
|
plpy.error(str(ex)) |
||||||
|
rv = plpy.execute(SD["plan"], [ args[0] ]) |
||||||
|
if len(rv): |
||||||
|
return rv[0]["fname"] |
||||||
|
return None |
||||||
|
' |
||||||
|
LANGUAGE plpythonu; |
||||||
|
/* no typo no messing about |
||||||
|
*/ |
||||||
|
CREATE FUNCTION valid_type(text) RETURNS text |
||||||
|
AS |
||||||
|
'if not SD.has_key("plan"): |
||||||
|
SD["plan"] = plpy.prepare("SELECT fname FROM users WHERE lname = $1", [ "text" ]) |
||||||
|
rv = plpy.execute(SD["plan"], [ args[0] ]) |
||||||
|
if len(rv): |
||||||
|
return rv[0]["fname"] |
||||||
|
return None |
||||||
|
' |
||||||
|
LANGUAGE plpythonu; |
||||||
|
/* Flat out syntax error |
||||||
|
*/ |
||||||
|
CREATE FUNCTION sql_syntax_error() RETURNS text |
||||||
|
AS |
||||||
|
'plpy.execute("syntax error")' |
||||||
|
LANGUAGE plpythonu; |
||||||
|
/* check the handling of uncaught python exceptions |
||||||
|
*/ |
||||||
|
CREATE FUNCTION exception_index_invalid(text) RETURNS text |
||||||
|
AS |
||||||
|
'return args[1]' |
||||||
|
LANGUAGE plpythonu; |
||||||
|
/* check handling of nested exceptions |
||||||
|
*/ |
||||||
|
CREATE FUNCTION exception_index_invalid_nested() RETURNS text |
||||||
|
AS |
||||||
|
'rv = plpy.execute("SELECT test5(''foo'')") |
||||||
|
return rv[0]' |
||||||
|
LANGUAGE plpythonu; |
||||||
|
CREATE FUNCTION join_sequences(sequences) RETURNS text |
||||||
|
AS |
||||||
|
'if not args[0]["multipart"]: |
||||||
|
return args[0]["sequence"] |
||||||
|
q = "SELECT sequence FROM xsequences WHERE pid = ''%s''" % args[0]["pid"] |
||||||
|
rv = plpy.execute(q) |
||||||
|
seq = args[0]["sequence"] |
||||||
|
for r in rv: |
||||||
|
seq = seq + r["sequence"] |
||||||
|
return seq |
||||||
|
' |
||||||
|
LANGUAGE plpythonu; |
||||||
|
CREATE OR REPLACE FUNCTION read_file(text) RETURNS text AS ' |
||||||
|
return open(args[0]).read() |
||||||
|
' LANGUAGE plpythonu; |
||||||
|
CREATE OR REPLACE FUNCTION write_file(text,text) RETURNS text AS ' |
||||||
|
open(args[0],"w").write(args[1]) |
||||||
|
return "Wrote to file: %s" % args[0] |
||||||
|
' LANGUAGE plpythonu; |
||||||
|
-- |
||||||
|
-- Universal Newline Support |
||||||
|
-- |
||||||
|
CREATE OR REPLACE FUNCTION newline_lf() RETURNS integer AS |
||||||
|
'x = 100\ny = 23\nreturn x + y\n' |
||||||
|
LANGUAGE plpythonu; |
||||||
|
CREATE OR REPLACE FUNCTION newline_cr() RETURNS integer AS |
||||||
|
'x = 100\ry = 23\rreturn x + y\r' |
||||||
|
LANGUAGE plpythonu; |
||||||
|
CREATE OR REPLACE FUNCTION newline_crlf() RETURNS integer AS |
||||||
|
'x = 100\r\ny = 23\r\nreturn x + y\r\n' |
||||||
|
LANGUAGE plpythonu; |
@ -0,0 +1,22 @@ |
|||||||
|
INSERT INTO users (fname, lname, username) VALUES ('jane', 'doe', 'j_doe'); |
||||||
|
INSERT INTO users (fname, lname, username) VALUES ('john', 'doe', 'johnd'); |
||||||
|
INSERT INTO users (fname, lname, username) VALUES ('willem', 'doe', 'w_doe'); |
||||||
|
INSERT INTO users (fname, lname, username) VALUES ('rick', 'smith', 'slash'); |
||||||
|
-- multi table tests |
||||||
|
-- |
||||||
|
INSERT INTO taxonomy (name) VALUES ('HIV I') ; |
||||||
|
INSERT INTO taxonomy (name) VALUES ('HIV II') ; |
||||||
|
INSERT INTO taxonomy (name) VALUES ('HCV') ; |
||||||
|
INSERT INTO entry (accession, txid) VALUES ('A00001', '1') ; |
||||||
|
INSERT INTO entry (accession, txid) VALUES ('A00002', '1') ; |
||||||
|
INSERT INTO entry (accession, txid) VALUES ('A00003', '1') ; |
||||||
|
INSERT INTO entry (accession, txid) VALUES ('A00004', '2') ; |
||||||
|
INSERT INTO entry (accession, txid) VALUES ('A00005', '2') ; |
||||||
|
INSERT INTO entry (accession, txid) VALUES ('A00006', '3') ; |
||||||
|
INSERT INTO sequences (sequence, eid, product, multipart) VALUES ('ABCDEF', 1, 'env', 'true') ; |
||||||
|
INSERT INTO xsequences (sequence, pid) VALUES ('GHIJKL', 1) ; |
||||||
|
INSERT INTO sequences (sequence, eid, product) VALUES ('ABCDEF', 2, 'env') ; |
||||||
|
INSERT INTO sequences (sequence, eid, product) VALUES ('ABCDEF', 3, 'env') ; |
||||||
|
INSERT INTO sequences (sequence, eid, product) VALUES ('ABCDEF', 4, 'gag') ; |
||||||
|
INSERT INTO sequences (sequence, eid, product) VALUES ('ABCDEF', 5, 'env') ; |
||||||
|
INSERT INTO sequences (sequence, eid, product) VALUES ('ABCDEF', 6, 'ns1') ; |
@ -0,0 +1,43 @@ |
|||||||
|
CREATE TABLE users ( |
||||||
|
fname text not null, |
||||||
|
lname text not null, |
||||||
|
username text, |
||||||
|
userid serial, |
||||||
|
PRIMARY KEY(lname, fname) |
||||||
|
) ; |
||||||
|
NOTICE: CREATE TABLE will create implicit sequence "users_userid_seq" for serial column "users.userid" |
||||||
|
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "users_pkey" for table "users" |
||||||
|
CREATE INDEX users_username_idx ON users(username); |
||||||
|
CREATE INDEX users_fname_idx ON users(fname); |
||||||
|
CREATE INDEX users_lname_idx ON users(lname); |
||||||
|
CREATE INDEX users_userid_idx ON users(userid); |
||||||
|
CREATE TABLE taxonomy ( |
||||||
|
id serial primary key, |
||||||
|
name text unique |
||||||
|
) ; |
||||||
|
NOTICE: CREATE TABLE will create implicit sequence "taxonomy_id_seq" for serial column "taxonomy.id" |
||||||
|
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "taxonomy_pkey" for table "taxonomy" |
||||||
|
NOTICE: CREATE TABLE / UNIQUE will create implicit index "taxonomy_name_key" for table "taxonomy" |
||||||
|
CREATE TABLE entry ( |
||||||
|
accession text not null primary key, |
||||||
|
eid serial unique, |
||||||
|
txid int2 not null references taxonomy(id) |
||||||
|
) ; |
||||||
|
NOTICE: CREATE TABLE will create implicit sequence "entry_eid_seq" for serial column "entry.eid" |
||||||
|
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "entry_pkey" for table "entry" |
||||||
|
NOTICE: CREATE TABLE / UNIQUE will create implicit index "entry_eid_key" for table "entry" |
||||||
|
CREATE TABLE sequences ( |
||||||
|
eid int4 not null references entry(eid), |
||||||
|
pid serial primary key, |
||||||
|
product text not null, |
||||||
|
sequence text not null, |
||||||
|
multipart bool default 'false' |
||||||
|
) ; |
||||||
|
NOTICE: CREATE TABLE will create implicit sequence "sequences_pid_seq" for serial column "sequences.pid" |
||||||
|
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "sequences_pkey" for table "sequences" |
||||||
|
CREATE INDEX sequences_product_idx ON sequences(product) ; |
||||||
|
CREATE TABLE xsequences ( |
||||||
|
pid int4 not null references sequences(pid), |
||||||
|
sequence text not null |
||||||
|
) ; |
||||||
|
CREATE INDEX xsequences_pid_idx ON xsequences(pid) ; |
@ -1,2 +0,0 @@ |
|||||||
|
|
||||||
DELETE FROM users ; |
|
@ -1,17 +0,0 @@ |
|||||||
DROP INDEX xsequences_pid_idx ; |
|
||||||
DROP TABLE xsequences ; |
|
||||||
DROP INDEX sequences_product_idx ; |
|
||||||
DROP TABLE sequences ; |
|
||||||
DROP SEQUENCE sequences_pid_seq ; |
|
||||||
DROP TABLE taxonomy ; |
|
||||||
DROP SEQUENCE taxonomy_id_seq ; |
|
||||||
DROP TABLE entry ; |
|
||||||
DROP SEQUENCE entry_eid_seq ; |
|
||||||
DROP INDEX logins_userid_idx ; |
|
||||||
DROP TABLE logins; |
|
||||||
DROP INDEX users_username_idx ; |
|
||||||
DROP INDEX users_fname_idx ; |
|
||||||
DROP INDEX users_lname_idx ; |
|
||||||
DROP INDEX users_userid_idx ; |
|
||||||
DROP TABLE users ; |
|
||||||
DROP SEQUENCE users_userid_seq ; |
|
@ -1,11 +0,0 @@ |
|||||||
DROP FUNCTION plglobals() ; |
|
||||||
DROP FUNCTION plstatic() ; |
|
||||||
DROP FUNCTION plfail() ; |
|
||||||
DROP TRIGGER users_insert_trig on users ; |
|
||||||
DROP FUNCTION users_insert() ; |
|
||||||
DROP TRIGGER users_update_trig on users ; |
|
||||||
DROP FUNCTION users_update() ; |
|
||||||
DROP TRIGGER users_delete_trig on users ; |
|
||||||
DROP FUNCTION users_delete() ; |
|
||||||
DROP PROCEDURAL LANGUAGE plpythonu ; |
|
||||||
DROP FUNCTION plpython_call_handler() ; |
|
@ -1,11 +0,0 @@ |
|||||||
|
|
||||||
CREATE FUNCTION test_setof() returns setof text |
|
||||||
AS |
|
||||||
'if GD.has_key("calls"): |
|
||||||
GD["calls"] = GD["calls"] + 1 |
|
||||||
if GD["calls"] > 2: |
|
||||||
return None |
|
||||||
else: |
|
||||||
GD["calls"] = 1 |
|
||||||
return str(GD["calls"])' |
|
||||||
LANGUAGE plpythonu; |
|
@ -0,0 +1,6 @@ |
|||||||
|
-- |
||||||
|
-- For paranoia's sake, don't leave an untrusted language sitting around |
||||||
|
-- |
||||||
|
SET client_min_messages = WARNING; |
||||||
|
|
||||||
|
DROP PROCEDURAL LANGUAGE plpythonu CASCADE; |
@ -1,58 +0,0 @@ |
|||||||
#!/bin/sh |
|
||||||
|
|
||||||
DBNAME=pltest |
|
||||||
|
|
||||||
echo -n "*** Destroy $DBNAME." |
|
||||||
dropdb $DBNAME > test.log 2>&1 |
|
||||||
# drop failure is ok... |
|
||||||
echo " Done. ***" |
|
||||||
|
|
||||||
sleep 1 |
|
||||||
|
|
||||||
echo -n "*** Create $DBNAME." |
|
||||||
if createdb $DBNAME >> test.log 2>&1 ; then |
|
||||||
echo " Done. ***" |
|
||||||
else |
|
||||||
echo " Failed! See test.log. ***" |
|
||||||
exit 1 |
|
||||||
fi |
|
||||||
|
|
||||||
echo -n "*** Create plpython." |
|
||||||
if createlang plpythonu $DBNAME >> test.log 2>&1 ; then |
|
||||||
echo " Done. ***" |
|
||||||
else |
|
||||||
echo " Failed! See test.log. ***" |
|
||||||
exit 1 |
|
||||||
fi |
|
||||||
|
|
||||||
echo -n "*** Create tables" |
|
||||||
psql -q $DBNAME < plpython_schema.sql >> test.log 2>&1 |
|
||||||
echo -n ", data" |
|
||||||
psql -q $DBNAME < plpython_populate.sql >> test.log 2>&1 |
|
||||||
echo -n ", and functions and triggers." |
|
||||||
psql -q $DBNAME < plpython_function.sql >> test.log 2>&1 |
|
||||||
echo " Done. ***" |
|
||||||
|
|
||||||
echo -n "*** Running feature tests." |
|
||||||
psql -q -e $DBNAME < plpython_test.sql > feature.output 2>&1 |
|
||||||
echo " Done. ***" |
|
||||||
|
|
||||||
echo -n "*** Running error handling tests." |
|
||||||
psql -q -e $DBNAME < plpython_error.sql > error.output 2>&1 |
|
||||||
echo " Done. ***" |
|
||||||
|
|
||||||
echo -n "*** Checking the results of the feature tests." |
|
||||||
if diff -c feature.expected feature.output > feature.diff 2>&1 ; then |
|
||||||
echo -n " passed!" |
|
||||||
else |
|
||||||
echo -n " failed! Please examine feature.diff." |
|
||||||
fi |
|
||||||
echo " Done. ***" |
|
||||||
|
|
||||||
echo -n "*** Checking the results of the error handling tests." |
|
||||||
if diff -c error.expected error.output > error.diff 2>&1 ; then |
|
||||||
echo -n " passed!" |
|
||||||
else |
|
||||||
echo -n " failed! Please examine error.diff." |
|
||||||
fi |
|
||||||
echo " Done. ***" |
|
@ -0,0 +1,404 @@ |
|||||||
|
-- |
||||||
|
-- checkpoint so that if we have a crash in the tests, replay of the |
||||||
|
-- just-completed CREATE DATABASE won't discard the core dump file |
||||||
|
-- |
||||||
|
checkpoint; |
||||||
|
-- |
||||||
|
-- Create the tables used in the test queries |
||||||
|
-- |
||||||
|
-- T_pkey1 is the primary key table for T_dta1. Entries from T_pkey1 |
||||||
|
-- Cannot be changed or deleted if they are referenced from T_dta1. |
||||||
|
-- |
||||||
|
-- T_pkey2 is the primary key table for T_dta2. If the key values in |
||||||
|
-- T_pkey2 are changed, the references in T_dta2 follow. If entries |
||||||
|
-- are deleted, the referencing entries from T_dta2 are deleted too. |
||||||
|
-- The values for field key2 in T_pkey2 are silently converted to |
||||||
|
-- upper case on insert/update. |
||||||
|
-- |
||||||
|
create table T_pkey1 ( |
||||||
|
key1 int4, |
||||||
|
key2 char(20), |
||||||
|
txt char(40) |
||||||
|
); |
||||||
|
create table T_pkey2 ( |
||||||
|
key1 int4, |
||||||
|
key2 char(20), |
||||||
|
txt char(40) |
||||||
|
); |
||||||
|
create table T_dta1 ( |
||||||
|
tkey char(10), |
||||||
|
ref1 int4, |
||||||
|
ref2 char(20) |
||||||
|
); |
||||||
|
create table T_dta2 ( |
||||||
|
tkey char(10), |
||||||
|
ref1 int4, |
||||||
|
ref2 char(20) |
||||||
|
); |
||||||
|
-- |
||||||
|
-- Function to check key existance in T_pkey1 |
||||||
|
-- |
||||||
|
create function check_pkey1_exists(int4, bpchar) returns bool as ' |
||||||
|
if {![info exists GD]} { |
||||||
|
set GD(plan) [spi_prepare \\ |
||||||
|
"select 1 from T_pkey1 \\ |
||||||
|
where key1 = \\$1 and key2 = \\$2" \\ |
||||||
|
{int4 bpchar}] |
||||||
|
} |
||||||
|
|
||||||
|
set n [spi_execp -count 1 $GD(plan) [list $1 $2]] |
||||||
|
|
||||||
|
if {$n > 0} { |
||||||
|
return "t" |
||||||
|
} |
||||||
|
return "f" |
||||||
|
' language 'pltcl'; |
||||||
|
-- |
||||||
|
-- Trigger function on every change to T_pkey1 |
||||||
|
-- |
||||||
|
create function trig_pkey1_before() returns trigger as ' |
||||||
|
# |
||||||
|
# Create prepared plans on the first call |
||||||
|
# |
||||||
|
if {![info exists GD]} { |
||||||
|
# |
||||||
|
# Plan to check for duplicate key in T_pkey1 |
||||||
|
# |
||||||
|
set GD(plan_pkey1) [spi_prepare \\ |
||||||
|
"select check_pkey1_exists(\\$1, \\$2) as ret" \\ |
||||||
|
{int4 bpchar}] |
||||||
|
# |
||||||
|
# Plan to check for references from T_dta1 |
||||||
|
# |
||||||
|
set GD(plan_dta1) [spi_prepare \\ |
||||||
|
"select 1 from T_dta1 \\ |
||||||
|
where ref1 = \\$1 and ref2 = \\$2" \\ |
||||||
|
{int4 bpchar}] |
||||||
|
} |
||||||
|
|
||||||
|
# |
||||||
|
# Initialize flags |
||||||
|
# |
||||||
|
set check_old_ref 0 |
||||||
|
set check_new_dup 0 |
||||||
|
|
||||||
|
switch $TG_op { |
||||||
|
INSERT { |
||||||
|
# |
||||||
|
# Must check for duplicate key on INSERT |
||||||
|
# |
||||||
|
set check_new_dup 1 |
||||||
|
} |
||||||
|
UPDATE { |
||||||
|
# |
||||||
|
# Must check for duplicate key on UPDATE only if |
||||||
|
# the key changes. In that case we must check for |
||||||
|
# references to OLD values too. |
||||||
|
# |
||||||
|
if {[string compare $NEW(key1) $OLD(key1)] != 0} { |
||||||
|
set check_old_ref 1 |
||||||
|
set check_new_dup 1 |
||||||
|
} |
||||||
|
if {[string compare $NEW(key2) $OLD(key2)] != 0} { |
||||||
|
set check_old_ref 1 |
||||||
|
set check_new_dup 1 |
||||||
|
} |
||||||
|
} |
||||||
|
DELETE { |
||||||
|
# |
||||||
|
# Must only check for references to OLD on DELETE |
||||||
|
# |
||||||
|
set check_old_ref 1 |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if {$check_new_dup} { |
||||||
|
# |
||||||
|
# Check for duplicate key |
||||||
|
# |
||||||
|
spi_execp -count 1 $GD(plan_pkey1) [list $NEW(key1) $NEW(key2)] |
||||||
|
if {$ret == "t"} { |
||||||
|
elog ERROR \\ |
||||||
|
"duplicate key ''$NEW(key1)'', ''$NEW(key2)'' for T_pkey1" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if {$check_old_ref} { |
||||||
|
# |
||||||
|
# Check for references to OLD |
||||||
|
# |
||||||
|
set n [spi_execp -count 1 $GD(plan_dta1) [list $OLD(key1) $OLD(key2)]] |
||||||
|
if {$n > 0} { |
||||||
|
elog ERROR \\ |
||||||
|
"key ''$OLD(key1)'', ''$OLD(key2)'' referenced by T_dta1" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
# |
||||||
|
# Anything is fine - let operation pass through |
||||||
|
# |
||||||
|
return OK |
||||||
|
' language 'pltcl'; |
||||||
|
create trigger pkey1_before before insert or update or delete on T_pkey1 |
||||||
|
for each row execute procedure |
||||||
|
trig_pkey1_before(); |
||||||
|
-- |
||||||
|
-- Trigger function to check for duplicate keys in T_pkey2 |
||||||
|
-- and to force key2 to be upper case only without leading whitespaces |
||||||
|
-- |
||||||
|
create function trig_pkey2_before() returns trigger as ' |
||||||
|
# |
||||||
|
# Prepare plan on first call |
||||||
|
# |
||||||
|
if {![info exists GD]} { |
||||||
|
set GD(plan_pkey2) [spi_prepare \\ |
||||||
|
"select 1 from T_pkey2 \\ |
||||||
|
where key1 = \\$1 and key2 = \\$2" \\ |
||||||
|
{int4 bpchar}] |
||||||
|
} |
||||||
|
|
||||||
|
# |
||||||
|
# Convert key2 value |
||||||
|
# |
||||||
|
set NEW(key2) [string toupper [string trim $NEW(key2)]] |
||||||
|
|
||||||
|
# |
||||||
|
# Check for duplicate key |
||||||
|
# |
||||||
|
set n [spi_execp -count 1 $GD(plan_pkey2) [list $NEW(key1) $NEW(key2)]] |
||||||
|
if {$n > 0} { |
||||||
|
elog ERROR \\ |
||||||
|
"duplicate key ''$NEW(key1)'', ''$NEW(key2)'' for T_pkey2" |
||||||
|
} |
||||||
|
|
||||||
|
# |
||||||
|
# Return modified tuple in NEW |
||||||
|
# |
||||||
|
return [array get NEW] |
||||||
|
' language 'pltcl'; |
||||||
|
create trigger pkey2_before before insert or update on T_pkey2 |
||||||
|
for each row execute procedure |
||||||
|
trig_pkey2_before(); |
||||||
|
-- |
||||||
|
-- Trigger function to force references from T_dta2 follow changes |
||||||
|
-- in T_pkey2 or be deleted too. This must be done AFTER the changes |
||||||
|
-- in T_pkey2 are done so the trigger for primkey check on T_dta2 |
||||||
|
-- fired on our updates will see the new key values in T_pkey2. |
||||||
|
-- |
||||||
|
create function trig_pkey2_after() returns trigger as ' |
||||||
|
# |
||||||
|
# Prepare plans on first call |
||||||
|
# |
||||||
|
if {![info exists GD]} { |
||||||
|
# |
||||||
|
# Plan to update references from T_dta2 |
||||||
|
# |
||||||
|
set GD(plan_dta2_upd) [spi_prepare \\ |
||||||
|
"update T_dta2 set ref1 = \\$3, ref2 = \\$4 \\ |
||||||
|
where ref1 = \\$1 and ref2 = \\$2" \\ |
||||||
|
{int4 bpchar int4 bpchar}] |
||||||
|
# |
||||||
|
# Plan to delete references from T_dta2 |
||||||
|
# |
||||||
|
set GD(plan_dta2_del) [spi_prepare \\ |
||||||
|
"delete from T_dta2 \\ |
||||||
|
where ref1 = \\$1 and ref2 = \\$2" \\ |
||||||
|
{int4 bpchar}] |
||||||
|
} |
||||||
|
|
||||||
|
# |
||||||
|
# Initialize flags |
||||||
|
# |
||||||
|
set old_ref_follow 0 |
||||||
|
set old_ref_delete 0 |
||||||
|
|
||||||
|
switch $TG_op { |
||||||
|
UPDATE { |
||||||
|
# |
||||||
|
# On update we must let old references follow |
||||||
|
# |
||||||
|
set NEW(key2) [string toupper $NEW(key2)] |
||||||
|
|
||||||
|
if {[string compare $NEW(key1) $OLD(key1)] != 0} { |
||||||
|
set old_ref_follow 1 |
||||||
|
} |
||||||
|
if {[string compare $NEW(key2) $OLD(key2)] != 0} { |
||||||
|
set old_ref_follow 1 |
||||||
|
} |
||||||
|
} |
||||||
|
DELETE { |
||||||
|
# |
||||||
|
# On delete we must delete references too |
||||||
|
# |
||||||
|
set old_ref_delete 1 |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if {$old_ref_follow} { |
||||||
|
# |
||||||
|
# Let old references follow and fire NOTICE message if |
||||||
|
# there where some |
||||||
|
# |
||||||
|
set n [spi_execp $GD(plan_dta2_upd) \\ |
||||||
|
[list $OLD(key1) $OLD(key2) $NEW(key1) $NEW(key2)]] |
||||||
|
if {$n > 0} { |
||||||
|
elog NOTICE \\ |
||||||
|
"updated $n entries in T_dta2 for new key in T_pkey2" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if {$old_ref_delete} { |
||||||
|
# |
||||||
|
# delete references and fire NOTICE message if |
||||||
|
# there where some |
||||||
|
# |
||||||
|
set n [spi_execp $GD(plan_dta2_del) \\ |
||||||
|
[list $OLD(key1) $OLD(key2)]] |
||||||
|
if {$n > 0} { |
||||||
|
elog NOTICE \\ |
||||||
|
"deleted $n entries from T_dta2" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return OK |
||||||
|
' language 'pltcl'; |
||||||
|
create trigger pkey2_after after update or delete on T_pkey2 |
||||||
|
for each row execute procedure |
||||||
|
trig_pkey2_after(); |
||||||
|
-- |
||||||
|
-- Generic trigger function to check references in T_dta1 and T_dta2 |
||||||
|
-- |
||||||
|
create function check_primkey() returns trigger as ' |
||||||
|
# |
||||||
|
# For every trigger/relation pair we create |
||||||
|
# a saved plan and hold them in GD |
||||||
|
# |
||||||
|
set plankey [list "plan" $TG_name $TG_relid] |
||||||
|
set planrel [list "relname" $TG_relid] |
||||||
|
|
||||||
|
# |
||||||
|
# Extract the pkey relation name |
||||||
|
# |
||||||
|
set keyidx [expr [llength $args] / 2] |
||||||
|
set keyrel [string tolower [lindex $args $keyidx]] |
||||||
|
|
||||||
|
if {![info exists GD($plankey)]} { |
||||||
|
# |
||||||
|
# We must prepare a new plan. Build up a query string |
||||||
|
# for the primary key check. |
||||||
|
# |
||||||
|
set keylist [lrange $args [expr $keyidx + 1] end] |
||||||
|
|
||||||
|
set query "select 1 from $keyrel" |
||||||
|
set qual " where" |
||||||
|
set typlist "" |
||||||
|
set idx 1 |
||||||
|
foreach key $keylist { |
||||||
|
set key [string tolower $key] |
||||||
|
# |
||||||
|
# Add the qual part to the query string |
||||||
|
# |
||||||
|
append query "$qual $key = \\$$idx" |
||||||
|
set qual " and" |
||||||
|
|
||||||
|
# |
||||||
|
# Lookup the fields type in pg_attribute |
||||||
|
# |
||||||
|
set n [spi_exec "select T.typname \\ |
||||||
|
from pg_catalog.pg_type T, pg_catalog.pg_attribute A, pg_catalog.pg_class C \\ |
||||||
|
where C.relname = ''[quote $keyrel]'' \\ |
||||||
|
and C.oid = A.attrelid \\ |
||||||
|
and A.attname = ''[quote $key]'' \\ |
||||||
|
and A.atttypid = T.oid"] |
||||||
|
if {$n != 1} { |
||||||
|
elog ERROR "table $keyrel doesn''t have a field named $key" |
||||||
|
} |
||||||
|
|
||||||
|
# |
||||||
|
# Append the fields type to the argument type list |
||||||
|
# |
||||||
|
lappend typlist $typname |
||||||
|
incr idx |
||||||
|
} |
||||||
|
|
||||||
|
# |
||||||
|
# Prepare the plan |
||||||
|
# |
||||||
|
set GD($plankey) [spi_prepare $query $typlist] |
||||||
|
|
||||||
|
# |
||||||
|
# Lookup and remember the table name for later error messages |
||||||
|
# |
||||||
|
spi_exec "select relname from pg_catalog.pg_class \\ |
||||||
|
where oid = ''$TG_relid''::oid" |
||||||
|
set GD($planrel) $relname |
||||||
|
} |
||||||
|
|
||||||
|
# |
||||||
|
# Build the argument list from the NEW row |
||||||
|
# |
||||||
|
incr keyidx -1 |
||||||
|
set arglist "" |
||||||
|
foreach arg [lrange $args 0 $keyidx] { |
||||||
|
lappend arglist $NEW($arg) |
||||||
|
} |
||||||
|
|
||||||
|
# |
||||||
|
# Check for the primary key |
||||||
|
# |
||||||
|
set n [spi_execp -count 1 $GD($plankey) $arglist] |
||||||
|
if {$n <= 0} { |
||||||
|
elog ERROR "key for $GD($planrel) not in $keyrel" |
||||||
|
} |
||||||
|
|
||||||
|
# |
||||||
|
# Anything is fine |
||||||
|
# |
||||||
|
return OK |
||||||
|
' language 'pltcl'; |
||||||
|
create trigger dta1_before before insert or update on T_dta1 |
||||||
|
for each row execute procedure |
||||||
|
check_primkey('ref1', 'ref2', 'T_pkey1', 'key1', 'key2'); |
||||||
|
create trigger dta2_before before insert or update on T_dta2 |
||||||
|
for each row execute procedure |
||||||
|
check_primkey('ref1', 'ref2', 'T_pkey2', 'key1', 'key2'); |
||||||
|
create function tcl_int4add(int4,int4) returns int4 as ' |
||||||
|
return [expr $1 + $2] |
||||||
|
' language 'pltcl'; |
||||||
|
-- We use split(n) as a quick-and-dirty way of parsing the input array |
||||||
|
-- value, which comes in as a string like '{1,2}'. There are better ways... |
||||||
|
create function tcl_int4_accum(int4[], int4) returns int4[] as ' |
||||||
|
set state [split $1 "{,}"] |
||||||
|
set newsum [expr {[lindex $state 1] + $2}] |
||||||
|
set newcnt [expr {[lindex $state 2] + 1}] |
||||||
|
return "{$newsum,$newcnt}" |
||||||
|
' language 'pltcl'; |
||||||
|
create function tcl_int4_avg(int4[]) returns int4 as ' |
||||||
|
set state [split $1 "{,}"] |
||||||
|
if {[lindex $state 2] == 0} { return_null } |
||||||
|
return [expr {[lindex $state 1] / [lindex $state 2]}] |
||||||
|
' language 'pltcl'; |
||||||
|
create aggregate tcl_avg ( |
||||||
|
sfunc = tcl_int4_accum, |
||||||
|
basetype = int4, |
||||||
|
stype = int4[], |
||||||
|
finalfunc = tcl_int4_avg, |
||||||
|
initcond = '{0,0}' |
||||||
|
); |
||||||
|
create aggregate tcl_sum ( |
||||||
|
sfunc = tcl_int4add, |
||||||
|
basetype = int4, |
||||||
|
stype = int4, |
||||||
|
initcond1 = 0 |
||||||
|
); |
||||||
|
create function tcl_int4lt(int4,int4) returns bool as ' |
||||||
|
if {$1 < $2} { |
||||||
|
return t |
||||||
|
} |
||||||
|
return f |
||||||
|
' language 'pltcl'; |
||||||
|
create operator @< ( |
||||||
|
leftarg = int4, |
||||||
|
rightarg = int4, |
||||||
|
procedure = tcl_int4lt |
||||||
|
); |
@ -1,8 +0,0 @@ |
|||||||
|
|
||||||
This is a small test suite for PL/Tcl. |
|
||||||
|
|
||||||
Just run the script runtest and compare the files |
|
||||||
test.expected against test.out after. |
|
||||||
|
|
||||||
|
|
||||||
Jan |
|
@ -1,30 +0,0 @@ |
|||||||
#!/bin/sh |
|
||||||
|
|
||||||
DBNAME=pltcl_test |
|
||||||
export DBNAME |
|
||||||
|
|
||||||
echo "**** Destroy old database $DBNAME ****" |
|
||||||
dropdb $DBNAME |
|
||||||
|
|
||||||
sleep 1 |
|
||||||
|
|
||||||
echo "**** Create test database $DBNAME ****" |
|
||||||
createdb $DBNAME || exit 1 |
|
||||||
|
|
||||||
echo "**** Create procedural language pltcl ****" |
|
||||||
createlang pltcl $DBNAME || exit 1 |
|
||||||
|
|
||||||
echo "**** Create tables, functions and triggers ****" |
|
||||||
psql -q -n $DBNAME <test_setup.sql |
|
||||||
|
|
||||||
echo "**** Running test queries ****" |
|
||||||
psql -q -n -e $DBNAME <test_queries.sql > test.out 2>&1 |
|
||||||
|
|
||||||
if diff test.expected test.out >/dev/null 2>&1 ; then |
|
||||||
echo " Tests passed O.K." |
|
||||||
rm test.out |
|
||||||
else |
|
||||||
echo " Tests failed - look at diffs between" |
|
||||||
echo " test.expected and test.out" |
|
||||||
fi |
|
||||||
|
|
Loading…
Reference in new issue