├── src ├── conversions │ ├── basic │ │ ├── extconf.rb │ │ ├── conversions.h │ │ └── plruby_basic.c │ ├── datetime │ │ ├── extconf.rb │ │ ├── conversions.h │ │ └── plruby_datetime.c │ ├── geometry │ │ ├── extconf.rb │ │ ├── conversions.h │ │ └── geometry.sql │ ├── network │ │ ├── extconf.rb │ │ ├── conversions.h │ │ └── network.sql │ ├── bitstring │ │ ├── extconf.rb │ │ ├── conversions.h │ │ └── bitstring.sql │ └── convcommon.h ├── conversions.h ├── package.h ├── plruby.h └── pltrans.c ├── .gitignore ├── test ├── range │ ├── runtest │ ├── b.rb │ ├── test_queries.sql.in │ └── test.expected.73.in ├── conv_geometry │ ├── runtest │ ├── b.rb │ └── test_queries.sql.in ├── conv_network │ ├── runtest │ ├── b.rb │ ├── test_queries.sql.in │ └── test.expected.73 ├── conv_bitstring │ ├── runtest │ ├── b.rb │ ├── test_queries.sql.in │ ├── test.expected.73 │ ├── test.expected.74 │ ├── test.expected.80 │ ├── test.expected.81 │ ├── test.expected.82 │ ├── test.expected.83 │ └── test.expected.84 ├── plp │ ├── runtest │ └── b.rb └── plt │ ├── runtest │ ├── b.rb │ ├── test_queries.sql │ ├── test.expected.83 │ ├── test.expected.73 │ ├── test.expected.74 │ ├── test.expected.75 │ ├── test.expected.80 │ ├── test.expected.81 │ ├── test.expected.82 │ ├── test.expected.84 │ └── test_setup.sql.in ├── ex_trans.sql ├── Changes ├── README.markdown └── extconf.rb /src/conversions/basic/extconf.rb: -------------------------------------------------------------------------------- 1 | require 'mkmf' 2 | 3 | if CONFIG["LIBRUBYARG"] == "$(LIBRUBYARG_SHARED)" && 4 | !enable_config("plruby-shared") 5 | $LIBRUBYARG = "" 6 | end 7 | create_makefile('plruby/plruby_basic') 8 | -------------------------------------------------------------------------------- /src/conversions/datetime/extconf.rb: -------------------------------------------------------------------------------- 1 | require 'mkmf' 2 | 3 | if CONFIG["LIBRUBYARG"] == "$(LIBRUBYARG_SHARED)" && 4 | !enable_config("plruby-shared") 5 | $LIBRUBYARG = "" 6 | end 7 | create_makefile('plruby/plruby_datetime') 8 | -------------------------------------------------------------------------------- /src/conversions/geometry/extconf.rb: -------------------------------------------------------------------------------- 1 | require 'mkmf' 2 | 3 | if CONFIG["LIBRUBYARG"] == "$(LIBRUBYARG_SHARED)" && 4 | !enable_config("plruby-shared") 5 | $LIBRUBYARG = "" 6 | end 7 | create_makefile('plruby/plruby_geometry') 8 | -------------------------------------------------------------------------------- /src/conversions/network/extconf.rb: -------------------------------------------------------------------------------- 1 | require 'mkmf' 2 | 3 | if CONFIG["LIBRUBYARG"] == "$(LIBRUBYARG_SHARED)" && 4 | !enable_config("plruby-shared") 5 | $LIBRUBYARG = "" 6 | end 7 | create_makefile('plruby/plruby_network') 8 | -------------------------------------------------------------------------------- /src/conversions/bitstring/extconf.rb: -------------------------------------------------------------------------------- 1 | require 'mkmf' 2 | 3 | if CONFIG["LIBRUBYARG"] == "$(LIBRUBYARG_SHARED)" && 4 | !enable_config("plruby-shared") 5 | $LIBRUBYARG = "" 6 | end 7 | create_makefile('plruby/plruby_bitstring') 8 | -------------------------------------------------------------------------------- /src/conversions.h: -------------------------------------------------------------------------------- 1 | #include "conversions/basic/conversions.h" 2 | #include "conversions/bitstring/conversions.h" 3 | #include "conversions/datetime/conversions.h" 4 | #include "conversions/geometry/conversions.h" 5 | #include "conversions/network/conversions.h" 6 | -------------------------------------------------------------------------------- /src/package.h: -------------------------------------------------------------------------------- 1 | #ifdef PACKAGE_NAME 2 | #undef PACKAGE_NAME 3 | #endif 4 | 5 | #ifdef PACKAGE_TARNAME 6 | #undef PACKAGE_TARNAME 7 | #endif 8 | 9 | #ifdef PACKAGE_VERSION 10 | #undef PACKAGE_VERSION 11 | #endif 12 | 13 | #ifdef PACKAGE_STRING 14 | #undef PACKAGE_STRING 15 | #endif 16 | 17 | #ifdef PACKAGE_BUGREPORT 18 | #undef PACKAGE_BUGREPORT 19 | #endif 20 | 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Makefile 2 | mkmf.log 3 | src/Makefile 4 | src/*.so 5 | src/conversions/*/plruby_*.so 6 | test/*/test.out 7 | test/*/test_mklang.sql 8 | test/conv_bitstring/test_queries.sql 9 | test/conv_geometry/test_queries.sql 10 | test/conv_network/test_queries.sql 11 | test/plp/test_setup.sql 12 | test/plt/test_setup.sql 13 | test/range/test.expected.73 14 | test/range/test.expected.74 15 | test/range/test.expected.75 16 | test/range/test_queries.sql 17 | -------------------------------------------------------------------------------- /src/conversions/datetime/conversions.h: -------------------------------------------------------------------------------- 1 | { 2 | VALUE tmp; 3 | 4 | #if RUBY_CAN_USE_AUTOLOAD 5 | rb_funcall(rb_mKernel, rb_intern("autoload"), 2, rb_str_new2("Tinterval"), 6 | rb_str_new2("plruby/plruby_datetime")); 7 | tmp = INT2NUM(rb_intern("Tinterval")); 8 | rb_hash_aset(plruby_conversions, INT2NUM(TINTERVALOID), tmp); 9 | #else 10 | tmp = plruby_define_void_class("Tinterval", "plruby/plruby_datetime"); 11 | rb_hash_aset(plruby_classes, INT2NUM(TINTERVALOID), tmp); 12 | #endif 13 | } 14 | -------------------------------------------------------------------------------- /src/conversions/bitstring/conversions.h: -------------------------------------------------------------------------------- 1 | { 2 | VALUE tmp; 3 | 4 | #if RUBY_CAN_USE_AUTOLOAD 5 | rb_funcall(rb_mKernel, rb_intern("autoload"), 2, rb_str_new2("BitString"), 6 | rb_str_new2("plruby/plruby_bitstring")); 7 | tmp = INT2NUM(rb_intern("BitString")); 8 | rb_hash_aset(plruby_conversions, INT2NUM(BITOID), tmp); 9 | rb_hash_aset(plruby_conversions, INT2NUM(VARBITOID), tmp); 10 | #else 11 | tmp = plruby_define_void_class("BitString", "plruby/plruby_bitstring"); 12 | rb_hash_aset(plruby_classes, INT2NUM(BITOID), tmp); 13 | rb_hash_aset(plruby_classes, INT2NUM(VARBITOID), tmp); 14 | #endif 15 | } 16 | -------------------------------------------------------------------------------- /test/range/runtest: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | DBNAME=plruby_test 4 | export DBNAME 5 | 6 | sleep 1 7 | echo "**** Destroy old database $DBNAME ****" 8 | dropdb $DBNAME 9 | 10 | echo "**** Create test database $DBNAME ****" 11 | createdb $DBNAME 12 | 13 | echo "**** Create procedural language plruby$2 ****" 14 | "${RUBY-ruby}" b.rb $* 15 | psql -q -n -X $DBNAME < test_mklang.sql 16 | 17 | echo "**** Running test queries ****" 18 | psql -q -n -X -e $DBNAME < test_queries.sql > test.out 2>&1 19 | 20 | if cmp -s test.expected.$1 test.out; then 21 | echo " Tests passed O.K." 22 | else 23 | echo " Tests failed - look at diffs between" 24 | echo " test.expected.$1 and test.out" 25 | fi 26 | 27 | -------------------------------------------------------------------------------- /test/conv_geometry/runtest: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | DBNAME=plruby_test 4 | export DBNAME 5 | 6 | sleep 1 7 | echo "**** Destroy old database $DBNAME ****" 8 | dropdb $DBNAME 9 | 10 | echo "**** Create test database $DBNAME ****" 11 | createdb $DBNAME 12 | 13 | echo "**** Create procedural language plruby$2 ****" 14 | "${RUBY-ruby}" b.rb $* 15 | psql -q -n -X $DBNAME < test_mklang.sql 16 | 17 | echo "**** Running test queries ****" 18 | psql -q -n -X -e $DBNAME < test_queries.sql > test.out 2>&1 19 | 20 | if cmp -s test.expected.$1 test.out; then 21 | echo " Tests passed O.K." 22 | else 23 | echo " Tests failed - look at diffs between" 24 | echo " test.expected.$1 and test.out" 25 | fi 26 | 27 | -------------------------------------------------------------------------------- /test/conv_network/runtest: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | DBNAME=plruby_test 4 | export DBNAME 5 | 6 | sleep 1 7 | echo "**** Destroy old database $DBNAME ****" 8 | dropdb $DBNAME 9 | 10 | echo "**** Create test database $DBNAME ****" 11 | createdb $DBNAME 12 | 13 | echo "**** Create procedural language plruby$2 ****" 14 | "${RUBY-ruby}" b.rb $* 15 | psql -q -n -X $DBNAME < test_mklang.sql 16 | 17 | echo "**** Running test queries ****" 18 | psql -q -n -X -e $DBNAME < test_queries.sql > test.out 2>&1 19 | 20 | if cmp -s test.expected.$1 test.out; then 21 | echo " Tests passed O.K." 22 | else 23 | echo " Tests failed - look at diffs between" 24 | echo " test.expected.$1 and test.out" 25 | fi 26 | 27 | -------------------------------------------------------------------------------- /test/conv_bitstring/runtest: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | DBNAME=plruby_test 4 | export DBNAME 5 | 6 | sleep 1 7 | echo "**** Destroy old database $DBNAME ****" 8 | dropdb $DBNAME 9 | 10 | echo "**** Create test database $DBNAME ****" 11 | createdb $DBNAME 12 | 13 | echo "**** Create procedural language plruby$2 ****" 14 | "${RUBY-ruby}" b.rb $* 15 | psql -q -n -X $DBNAME < test_mklang.sql 16 | 17 | echo "**** Running test queries ****" 18 | psql -q -n -X -e $DBNAME < test_queries.sql > test.out 2>&1 19 | 20 | if cmp -s test.expected.$1 test.out; then 21 | echo " Tests passed O.K." 22 | else 23 | echo " Tests failed - look at diffs between" 24 | echo " test.expected.$1 and test.out" 25 | fi 26 | 27 | -------------------------------------------------------------------------------- /test/plp/runtest: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | DBNAME=plruby_test 4 | export DBNAME 5 | 6 | sleep 1 7 | echo "**** Destroy old database $DBNAME ****" 8 | dropdb $DBNAME 9 | 10 | echo "**** Create test database $DBNAME ****" 11 | createdb $DBNAME 12 | 13 | echo "**** Create procedural language plruby$2 ****" 14 | "${RUBY-ruby}" b.rb $* 15 | psql -q -n -X $DBNAME < test_mklang.sql 16 | 17 | echo "**** Create tables, functions and triggers ****" 18 | psql -q -n -X $DBNAME < test_setup.sql 19 | 20 | echo "**** Running test queries ****" 21 | psql -q -n -X -e $DBNAME < test_queries.sql > test.out 2>&1 22 | 23 | if cmp -s test.expected.$1 test.out; then 24 | echo " Tests passed O.K." 25 | else 26 | echo " Tests failed - look at diffs between" 27 | echo " test.expected.$1 and test.out" 28 | fi 29 | 30 | -------------------------------------------------------------------------------- /test/plt/runtest: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | DBNAME=plruby_test 4 | export DBNAME 5 | 6 | sleep 1 7 | echo "**** Destroy old database $DBNAME ****" 8 | dropdb $DBNAME 9 | 10 | echo "**** Create test database $DBNAME ****" 11 | createdb $DBNAME 12 | 13 | echo "**** Create procedural language plruby$2 ****" 14 | "${RUBY-ruby}" b.rb $* 15 | psql -q -n -X $DBNAME < test_mklang.sql 16 | 17 | echo "**** Create tables, functions and triggers ****" 18 | psql -q -n -X $DBNAME < test_setup.sql 19 | 20 | echo "**** Running test queries ****" 21 | psql -q -n -X -e $DBNAME < test_queries.sql > test.out 2>&1 22 | 23 | if cmp -s test.expected.$1 test.out; then 24 | echo " Tests passed O.K." 25 | else 26 | echo " Tests failed - look at diffs between" 27 | echo " test.expected.$1 and test.out" 28 | fi 29 | 30 | -------------------------------------------------------------------------------- /test/plp/b.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | require 'rbconfig' 3 | include Config 4 | pwd = Dir.pwd 5 | pwd.sub!(%r{[^/]+/[^/]+$}, "") 6 | 7 | language, extension = 'C', '_new_trigger' 8 | opaque = 'language_handler' 9 | 10 | version = ARGV[0].to_i 11 | suffix = ARGV[1].to_s 12 | 13 | begin 14 | f = File.new("test_setup.sql", "w") 15 | IO.foreach("test_setup.sql.in") do |x| 16 | x.gsub!(/language\s+'plruby'/i, "language 'plruby#{suffix}'") 17 | f.print x 18 | end 19 | f.close 20 | f = File.new("test_mklang.sql", "w") 21 | f.print <> args[1], b0.to_s, b0.to_i, 39 | b0.size, b0.octet_size] 40 | ' language 'plruby'; 41 | 42 | select * from bs(12, 2); 43 | select * from bs(277, -3); 44 | 45 | create function ext(text, integer) returns integer as ' 46 | b0 = BitString.new(args[0]) 47 | b0[args[1]] 48 | ' language 'plruby'; 49 | 50 | select ext('011110', 0); 51 | select ext('011110', -1); 52 | select ext('011110', 1); 53 | select ext('011110', 4); 54 | 55 | create function ext2(text, integer, integer) returns bit varying(8) as ' 56 | b0 = BitString.new(args[0]) 57 | b0[args[1], args[2]] 58 | ' language 'plruby'; 59 | 60 | select ext2('0111101', 0, 2); 61 | select ext2('0111101', -1, 3); 62 | select ext2('0111101', 1, 2); 63 | select ext2('0111101', 4, 1); 64 | -------------------------------------------------------------------------------- /src/conversions/bitstring/bitstring.sql: -------------------------------------------------------------------------------- 1 | drop table bit_op cascade; 2 | 3 | create table bit_op ( 4 | b0 bit(8), b1 bit(8), band bit(8), bor bit(8), 5 | bxor bit(8), bnot0 bit(8), bnot1 bit(8) 6 | ); 7 | 8 | create function bt(integer, integer) returns bit_op as ' 9 | b0 = BitString.new(args[0], 8) 10 | b1 = BitString.new(args[1], 8) 11 | [b0, b1, b0 & b1, b0 | b1, b0 ^ b1, ~b0, ~b1] 12 | ' language 'plruby'; 13 | 14 | select * from bt(12, 24); 15 | select * from bt(12, 32); 16 | select * from bt(15, 278); 17 | 18 | 19 | drop function be(integer); 20 | 21 | create function be(integer) returns setof integer as ' 22 | BitString.new(args[0], 8).each {|i| yield i} 23 | ' language 'plruby'; 24 | 25 | select * from be(12); 26 | select * from be(257); 27 | 28 | drop function bx(integer, integer); 29 | 30 | create function bx(integer, integer) returns bit varying as ' 31 | BitString.new(*args) 32 | ' language 'plruby'; 33 | 34 | select bx(12, 6); 35 | select bx(12, 8); 36 | 37 | drop table bit_sht cascade; 38 | 39 | create table bit_sht ( 40 | b0 bit(8), shft int, bl bit(8), br bit(8), bs text, bi integer, 41 | sz integer, osz integer 42 | ); 43 | 44 | create function bs(integer, integer) returns bit_sht as ' 45 | b0 = BitString.new(args[0], 8) 46 | [b0, args[1], b0 << args[1], b0 >> args[1], b0.to_s, b0.to_i, 47 | b0.size, b0.octet_size] 48 | ' language 'plruby'; 49 | 50 | select * from bs(12, 2); 51 | select * from bs(277, -3); 52 | 53 | drop function ext(text, integer); 54 | 55 | create function ext(text, integer) returns integer as ' 56 | b0 = BitString.new(args[0]) 57 | b0[args[1]] 58 | ' language 'plruby'; 59 | 60 | select ext('011110', 0); 61 | select ext('011110', -1); 62 | select ext('011110', 1); 63 | select ext('011110', 4); 64 | 65 | drop function ext2(text, integer, integer); 66 | 67 | create function ext2(text, integer, integer) returns bit varying(8) as ' 68 | b0 = BitString.new(args[0]) 69 | b0[args[1], args[2]] 70 | ' language 'plruby'; 71 | 72 | select ext2('0111101', 0, 2); 73 | select ext2('0111101', -1, 3); 74 | select ext2('0111101', 1, 2); 75 | select ext2('0111101', 4, 1); 76 | -------------------------------------------------------------------------------- /src/conversions/geometry/conversions.h: -------------------------------------------------------------------------------- 1 | { 2 | #if RUBY_CAN_USE_AUTOLOAD 3 | rb_funcall(rb_mKernel, rb_intern("autoload"), 2, rb_str_new2("Point"), 4 | rb_str_new2("plruby/plruby_geometry")); 5 | rb_funcall(rb_mKernel, rb_intern("autoload"), 2, rb_str_new2("Segment"), 6 | rb_str_new2("plruby/plruby_geometry")); 7 | rb_funcall(rb_mKernel, rb_intern("autoload"), 2, rb_str_new2("Box"), 8 | rb_str_new2("plruby/plruby_geometry")); 9 | rb_funcall(rb_mKernel, rb_intern("autoload"), 2, rb_str_new2("Path"), 10 | rb_str_new2("plruby/plruby_geometry")); 11 | rb_funcall(rb_mKernel, rb_intern("autoload"), 2, rb_str_new2("Polygon"), 12 | rb_str_new2("plruby/plruby_geometry")); 13 | rb_funcall(rb_mKernel, rb_intern("autoload"), 2, rb_str_new2("Circle"), 14 | rb_str_new2("plruby/plruby_geometry")); 15 | rb_hash_aset(plruby_conversions, INT2NUM(POINTOID), INT2NUM(rb_intern("Point"))); 16 | rb_hash_aset(plruby_conversions, INT2NUM(LSEGOID), INT2NUM(rb_intern("Segment"))); 17 | rb_hash_aset(plruby_conversions, INT2NUM(BOXOID), INT2NUM(rb_intern("Box"))); 18 | rb_hash_aset(plruby_conversions, INT2NUM(PATHOID), INT2NUM(rb_intern("Path"))); 19 | rb_hash_aset(plruby_conversions, INT2NUM(POLYGONOID), INT2NUM(rb_intern("Polygon"))); 20 | rb_hash_aset(plruby_conversions, INT2NUM(CIRCLEOID), INT2NUM(rb_intern("Circle"))); 21 | #else 22 | VALUE tmp; 23 | 24 | tmp = plruby_define_void_class("Point", "plruby/plruby_geometry"); 25 | rb_hash_aset(plruby_classes, INT2NUM(POINTOID), tmp); 26 | tmp = plruby_define_void_class("Segment", "plruby/plruby_geometry"); 27 | rb_hash_aset(plruby_classes, INT2NUM(LSEGOID), tmp); 28 | tmp = plruby_define_void_class("Box", "plruby/plruby_geometry"); 29 | rb_hash_aset(plruby_classes, INT2NUM(BOXOID), tmp); 30 | tmp = plruby_define_void_class("Path", "plruby/plruby_geometry"); 31 | rb_hash_aset(plruby_classes, INT2NUM(PATHOID), tmp); 32 | tmp = plruby_define_void_class("Polygon", "plruby/plruby_geometry"); 33 | rb_hash_aset(plruby_classes, INT2NUM(POLYGONOID), tmp); 34 | tmp = plruby_define_void_class("Circle", "plruby/plruby_geometry"); 35 | rb_hash_aset(plruby_classes, INT2NUM(CIRCLEOID), tmp); 36 | #endif 37 | } 38 | -------------------------------------------------------------------------------- /test/conv_network/test_queries.sql.in: -------------------------------------------------------------------------------- 1 | create table pl_inet ( 2 | host text, abbrev text, masklen int, 3 | network inet, netmask inet, first inet, last inet 4 | ); 5 | 6 | 7 | create or replace function inet_val(inet) returns pl_inet as ' 8 | a = args[0] 9 | [a.host, a.abbrev, a.masklen, a.network, a.netmask, 10 | a.first, a.last] 11 | ' language 'plruby'; 12 | 13 | 14 | select * from inet_val('192.168.1'::cidr); 15 | select * from inet_val('192.168.1.226/24'::inet); 16 | select * from inet_val('192.168.1.0/24'::cidr); 17 | select * from inet_val('192.168.1.226'::inet); 18 | select * from inet_val('192.168.1'::cidr); 19 | select * from inet_val('192.168.1.0/24'::inet); 20 | select * from inet_val('192.168.1'::cidr); 21 | select * from inet_val('192.168.1.0/25'::inet); 22 | select * from inet_val('192.168.1'::cidr); 23 | select * from inet_val('192.168.1.255/24'::inet); 24 | select * from inet_val('192.168.1'::cidr); 25 | select * from inet_val('192.168.1.255/25'::inet); 26 | select * from inet_val('10'::cidr); 27 | select * from inet_val('10.1.2.3/8'::inet); 28 | select * from inet_val('10.0.0.0'::cidr); 29 | select * from inet_val('10.1.2.3/8'::inet); 30 | select * from inet_val('10.1.2.3'::cidr); 31 | select * from inet_val('10.1.2.3/32'::inet); 32 | select * from inet_val('10.1.2'::cidr); 33 | select * from inet_val('10.1.2.3/24'::inet); 34 | select * from inet_val('10.1'::cidr); 35 | select * from inet_val('10.1.2.3/16'::inet); 36 | select * from inet_val('10'::cidr); 37 | select * from inet_val('10.1.2.3/8'::inet); 38 | select * from inet_val('10'::cidr); 39 | select * from inet_val('11.1.2.3/8'::inet); 40 | select * from inet_val('10'::cidr); 41 | select * from inet_val('9.1.2.3/8'::inet); 42 | select * from inet_val('10:23::f1'::cidr); 43 | select * from inet_val('10:23::f1/64'::inet); 44 | select * from inet_val('10:23::8000/113'::cidr); 45 | select * from inet_val('10:23::ffff'::inet); 46 | select * from inet_val('::ffff:1.2.3.4'::cidr); 47 | select * from inet_val('::4.3.2.1/24'::inet); 48 | 49 | create or replace function mac_cmp(macaddr, macaddr) returns int as ' 50 | args[0] <=> args[1] 51 | ' language 'plruby'; 52 | 53 | select mac_cmp('00:07:E9:85:3E:C5'::macaddr, '00:E0:29:3E:E7:25'::macaddr); 54 | 55 | create or replace function mac_trunc(macaddr) returns macaddr as ' 56 | args[0].truncate 57 | ' language 'plruby'; 58 | 59 | select mac_trunc('00:07:E9:85:3E:C5'::macaddr); 60 | select mac_trunc('00:E0:29:3E:E7:25'::macaddr); 61 | -------------------------------------------------------------------------------- /src/conversions/network/network.sql: -------------------------------------------------------------------------------- 1 | drop function inet_val(inet); 2 | drop table pl_inet; 3 | 4 | create table pl_inet ( 5 | host text, abbrev text, masklen int, 6 | network inet, netmask inet, first inet, last inet 7 | ); 8 | 9 | 10 | create or replace function inet_val(inet) returns pl_inet as ' 11 | a = args[0] 12 | [a.host, a.abbrev, a.masklen, a.network, a.netmask, 13 | a.first, a.last] 14 | ' language 'plruby'; 15 | 16 | 17 | select * from inet_val('192.168.1'::cidr); 18 | select * from inet_val('192.168.1.226/24'::inet); 19 | select * from inet_val('192.168.1.0/24'::cidr); 20 | select * from inet_val('192.168.1.226'::inet); 21 | select * from inet_val('192.168.1'::cidr); 22 | select * from inet_val('192.168.1.0/24'::inet); 23 | select * from inet_val('192.168.1'::cidr); 24 | select * from inet_val('192.168.1.0/25'::inet); 25 | select * from inet_val('192.168.1'::cidr); 26 | select * from inet_val('192.168.1.255/24'::inet); 27 | select * from inet_val('192.168.1'::cidr); 28 | select * from inet_val('192.168.1.255/25'::inet); 29 | select * from inet_val('10'::cidr); 30 | select * from inet_val('10.1.2.3/8'::inet); 31 | select * from inet_val('10.0.0.0'::cidr); 32 | select * from inet_val('10.1.2.3/8'::inet); 33 | select * from inet_val('10.1.2.3'::cidr); 34 | select * from inet_val('10.1.2.3/32'::inet); 35 | select * from inet_val('10.1.2'::cidr); 36 | select * from inet_val('10.1.2.3/24'::inet); 37 | select * from inet_val('10.1'::cidr); 38 | select * from inet_val('10.1.2.3/16'::inet); 39 | select * from inet_val('10'::cidr); 40 | select * from inet_val('10.1.2.3/8'::inet); 41 | select * from inet_val('10'::cidr); 42 | select * from inet_val('11.1.2.3/8'::inet); 43 | select * from inet_val('10'::cidr); 44 | select * from inet_val('9.1.2.3/8'::inet); 45 | select * from inet_val('10:23::f1'::cidr); 46 | select * from inet_val('10:23::f1/64'::inet); 47 | select * from inet_val('10:23::8000/113'::cidr); 48 | select * from inet_val('10:23::ffff'::inet); 49 | select * from inet_val('::ffff:1.2.3.4'::cidr); 50 | select * from inet_val('::4.3.2.1/24'::inet); 51 | 52 | create or replace function mac_cmp(macaddr, macaddr) returns int as ' 53 | args[0] <=> args[1] 54 | ' language 'plruby'; 55 | 56 | select mac_cmp('00:07:E9:85:3E:C5'::macaddr, '00:E0:29:3E:E7:25'::macaddr); 57 | 58 | create or replace function mac_trunc(macaddr) returns macaddr as ' 59 | args[0].truncate 60 | ' language 'plruby'; 61 | 62 | select mac_trunc('00:07:E9:85:3E:C5'::macaddr); 63 | select mac_trunc('00:E0:29:3E:E7:25'::macaddr); 64 | -------------------------------------------------------------------------------- /test/plt/test_queries.sql: -------------------------------------------------------------------------------- 1 | 2 | insert into T_pkey1 values (1, 'key1-1', 'test key'); 3 | insert into T_pkey1 values (1, 'key1-2', 'test key'); 4 | insert into T_pkey1 values (1, 'key1-3', 'test key'); 5 | insert into T_pkey1 values (2, 'key2-1', 'test key'); 6 | insert into T_pkey1 values (2, 'key2-2', 'test key'); 7 | insert into T_pkey1 values (2, 'key2-3', 'test key'); 8 | 9 | insert into T_pkey2 values (1, 'key1-1', 'test key'); 10 | insert into T_pkey2 values (1, 'key1-2', 'test key'); 11 | insert into T_pkey2 values (1, 'key1-3', 'test key'); 12 | insert into T_pkey2 values (2, 'key2-1', 'test key'); 13 | insert into T_pkey2 values (2, 'key2-2', 'test key'); 14 | insert into T_pkey2 values (2, 'key2-3', 'test key'); 15 | 16 | select * from T_pkey1; 17 | 18 | -- key2 in T_pkey2 should have upper case only 19 | select * from T_pkey2; 20 | 21 | insert into T_pkey1 values (1, 'KEY1-3', 'should work'); 22 | 23 | -- Due to the upper case translation in trigger this must fail 24 | insert into T_pkey2 values (1, 'KEY1-3', 'should fail'); 25 | 26 | insert into T_dta1 values ('trec 1', 1, 'key1-1'); 27 | insert into T_dta1 values ('trec 2', 1, 'key1-2'); 28 | insert into T_dta1 values ('trec 3', 1, 'key1-3'); 29 | 30 | -- Must fail due to unknown key in T_pkey1 31 | insert into T_dta1 values ('trec 4', 1, 'key1-4'); 32 | 33 | insert into T_dta2 values ('trec 1', 1, 'KEY1-1'); 34 | insert into T_dta2 values ('trec 2', 1, 'KEY1-2'); 35 | insert into T_dta2 values ('trec 3', 1, 'KEY1-3'); 36 | 37 | -- Must fail due to unknown key in T_pkey2 38 | insert into T_dta2 values ('trec 4', 1, 'KEY1-4'); 39 | 40 | select * from T_dta1; 41 | 42 | select * from T_dta2; 43 | 44 | update T_pkey1 set key2 = 'key2-9' where key1 = 2 and key2 = 'key2-1'; 45 | update T_pkey1 set key2 = 'key1-9' where key1 = 1 and key2 = 'key1-1'; 46 | delete from T_pkey1 where key1 = 2 and key2 = 'key2-2'; 47 | delete from T_pkey1 where key1 = 1 and key2 = 'key1-2'; 48 | 49 | update T_pkey2 set key2 = 'KEY2-9' where key1 = 2 and key2 = 'KEY2-1'; 50 | update T_pkey2 set key2 = 'KEY1-9' where key1 = 1 and key2 = 'KEY1-1'; 51 | delete from T_pkey2 where key1 = 2 and key2 = 'KEY2-2'; 52 | delete from T_pkey2 where key1 = 1 and key2 = 'KEY1-2'; 53 | 54 | select * from T_pkey1; 55 | select * from T_pkey2; 56 | select * from T_dta1; 57 | select * from T_dta2; 58 | 59 | select ruby_avg(key1) from T_pkey1; 60 | select ruby_sum(key1) from T_pkey1; 61 | select ruby_avg(key1) from T_pkey2; 62 | select ruby_sum(key1) from T_pkey2; 63 | 64 | -- The following should return NULL instead of 0 65 | select ruby_avg(key1) from T_pkey1 where key1 = 99; 66 | select ruby_sum(key1) from T_pkey1 where key1 = 99; 67 | 68 | select 1 @< 2; 69 | select 100 @< 4; 70 | 71 | select * from T_pkey1 order by key1 using @<; 72 | select * from T_pkey2 order by key1 using @<; 73 | -------------------------------------------------------------------------------- /Changes: -------------------------------------------------------------------------------- 1 | -- 0.1.0 - 0.2.5 2 | 3 | adaptation for new version of PostgreSQL 4 | 5 | -- 0.2.6 6 | 7 | #exec can have an optional output of type "array", "hash" 8 | (see the example in plruby.html) 9 | 10 | -- 0.2.7 11 | 12 | PLrubyplan#each 13 | 14 | --- 0.2.8 15 | 16 | adapted for 7.3b3 17 | 18 | --- 0.2.9 19 | 20 | adapted for 1.8.0 21 | corrected stupid bug with GC 22 | 23 | --- 0.3.0 24 | 25 | *experimental* timeout (--with-timeout) 26 | fixed SPI_execp() 27 | 28 | --- 0.3.1 29 | 30 | corrected for_numvals() (Thanks Brad Hilton ) 31 | 32 | --- 0.3.2 33 | 34 | functions returning set (PostgreSQL >= 7.3) 35 | 36 | --- 0.3.3 37 | 38 | corrected timeout 39 | corrected bug with GC 40 | new classes PL::Plan, PL::Cursor 41 | 42 | --- 0.3.4 43 | 44 | adapted for 7.4 45 | Warning : array as input and output (7.4) 46 | anyarray/anyelement in input/output for 7.4 47 | 48 | --- 0.3.6 49 | 50 | corrected "EACH STATEMENT" 51 | added ExprMultiResult 52 | 53 | --- 0.3.7 54 | 55 | corrected full qualified type names for SPI_prepare 56 | #each return nil for PL::Plan 57 | protected PL::PLan::new against parse error 58 | protected thread value when there is a timeout 59 | 60 | --- 0.3.8 61 | 62 | stupid bug portalActive (only 7.4) 63 | 64 | --- 0.3.9 65 | 66 | corrected rb_hash_delete for 1.6 (Thanks Dennis Vshivkov ) 67 | decode arrays in tuples (Thanks Dennis Vshivkov ) 68 | corrected pl_yield (Thanks Dennis Vshivkov ) 69 | convert arguments (--enable-conversion at compile time : experimental) 70 | conversion plfunction ==> method (only with --enable-conversion) 71 | add network, geometry (experimental) 72 | corrected function returning table 73 | 74 | --- 0.4.0 75 | 76 | --enable-shared 77 | array for trigger 78 | 79 | --- 0.4.1 80 | 81 | add bitstring 82 | 83 | --- 0.4.2 84 | 85 | by default convert arguments ( --disable-conversion to remove it) 86 | 87 | --- 0.4.3 88 | 89 | IMPORTANT : work only with postgres >= 7.3 90 | transaction and named arguments for 8.0 91 | 92 | --- 0.4.4 93 | 94 | * patch written by Dennis Vshivkov 95 | - Time-conversion-fix 96 | - typtypmod-fix 97 | - Integer-conversion-fix 98 | - extconf.rb-PG_TRY-test-fix 99 | - redundant-ReleaseSysCache-fix 100 | - BYTEA-conv 101 | - MONEY-conv-fix 102 | - Float-conv-back-fix 103 | 104 | --- 0.4.5 105 | 106 | * corrected ReleaseSysCache() (Thanks to Peter Eisentraut ) 107 | * conversion function String#to_datum (Thanks to Dennis Vshivkov ) 108 | 109 | --- 0.4.5 - 0.5.3 (2008-03-03) 110 | 111 | * adapted for 8.1, 8.2, and 8.3 112 | 113 | The original author Guy Decoux passed away in the beginning of the 114 | month of July 2008 at the age of 53. The development is inherited by 115 | Akinori MUSHA. 116 | 117 | --- 0.5.4 (2010-01-02) 118 | 119 | * adapted for 8.4 120 | * bug-fix 121 | * extconf.rb changed to make use of pg_config(1) 122 | -------------------------------------------------------------------------------- /test/conv_bitstring/test.expected.73: -------------------------------------------------------------------------------- 1 | create table bit_op ( 2 | b0 bit(8), b1 bit(8), band bit(8), bor bit(8), 3 | bxor bit(8), bnot0 bit(8), bnot1 bit(8) 4 | ); 5 | create function bt(integer, integer) returns bit_op as ' 6 | b0 = BitString.new(args[0], 8) 7 | b1 = BitString.new(args[1], 8) 8 | [b0, b1, b0 & b1, b0 | b1, b0 ^ b1, ~b0, ~b1] 9 | ' language 'plruby'; 10 | select * from bt(12, 24); 11 | b0 | b1 | band | bor | bxor | bnot0 | bnot1 12 | ----------+----------+----------+----------+----------+----------+---------- 13 | 00001100 | 00011000 | 00001000 | 00011100 | 00010100 | 11110011 | 11100111 14 | (1 row) 15 | 16 | select * from bt(12, 32); 17 | b0 | b1 | band | bor | bxor | bnot0 | bnot1 18 | ----------+----------+----------+----------+----------+----------+---------- 19 | 00001100 | 00100000 | 00000000 | 00101100 | 00101100 | 11110011 | 11011111 20 | (1 row) 21 | 22 | select * from bt(15, 278); 23 | b0 | b1 | band | bor | bxor | bnot0 | bnot1 24 | ----------+----------+----------+----------+----------+----------+---------- 25 | 00001111 | 00010110 | 00000110 | 00011111 | 00011001 | 11110000 | 11101001 26 | (1 row) 27 | 28 | create function be(integer) returns setof integer as ' 29 | BitString.new(args[0], 8).each {|i| yield i} 30 | ' language 'plruby'; 31 | select * from be(12); 32 | be 33 | ---- 34 | 0 35 | 0 36 | 0 37 | 0 38 | 1 39 | 1 40 | 0 41 | 0 42 | (8 rows) 43 | 44 | select * from be(257); 45 | be 46 | ---- 47 | 0 48 | 0 49 | 0 50 | 0 51 | 0 52 | 0 53 | 0 54 | 1 55 | (8 rows) 56 | 57 | create function bx(integer, integer) returns bit varying as ' 58 | BitString.new(*args) 59 | ' language 'plruby'; 60 | select bx(12, 6); 61 | bx 62 | -------- 63 | 001100 64 | (1 row) 65 | 66 | select bx(12, 8); 67 | bx 68 | ---------- 69 | 00001100 70 | (1 row) 71 | 72 | create table bit_sht ( 73 | b0 bit(8), shft int, bl bit(8), br bit(8), bs text, bi integer, 74 | sz integer, osz integer 75 | ); 76 | create function bs(integer, integer) returns bit_sht as ' 77 | b0 = BitString.new(args[0], 8) 78 | [b0, args[1], b0 << args[1], b0 >> args[1], b0.to_s, b0.to_i, 79 | b0.size, b0.octet_size] 80 | ' language 'plruby'; 81 | select * from bs(12, 2); 82 | b0 | shft | bl | br | bs | bi | sz | osz 83 | ----------+------+----------+----------+----------+----+----+----- 84 | 00001100 | 2 | 00110000 | 00000011 | 00001100 | 12 | 8 | 1 85 | (1 row) 86 | 87 | select * from bs(277, -3); 88 | b0 | shft | bl | br | bs | bi | sz | osz 89 | ----------+------+----------+----------+----------+----+----+----- 90 | 00010101 | -3 | 00000010 | 10101000 | 00010101 | 21 | 8 | 1 91 | (1 row) 92 | 93 | create function ext(text, integer) returns integer as ' 94 | b0 = BitString.new(args[0]) 95 | b0[args[1]] 96 | ' language 'plruby'; 97 | select ext('011110', 0); 98 | ext 99 | ----- 100 | 0 101 | (1 row) 102 | 103 | select ext('011110', -1); 104 | ext 105 | ----- 106 | 0 107 | (1 row) 108 | 109 | select ext('011110', 1); 110 | ext 111 | ----- 112 | 1 113 | (1 row) 114 | 115 | select ext('011110', 4); 116 | ext 117 | ----- 118 | 1 119 | (1 row) 120 | 121 | create function ext2(text, integer, integer) returns bit varying(8) as ' 122 | b0 = BitString.new(args[0]) 123 | b0[args[1], args[2]] 124 | ' language 'plruby'; 125 | select ext2('0111101', 0, 2); 126 | ext2 127 | ------ 128 | 01 129 | (1 row) 130 | 131 | select ext2('0111101', -1, 3); 132 | ext2 133 | ------ 134 | 1 135 | (1 row) 136 | 137 | select ext2('0111101', 1, 2); 138 | ext2 139 | ------ 140 | 11 141 | (1 row) 142 | 143 | select ext2('0111101', 4, 1); 144 | ext2 145 | ------ 146 | 1 147 | (1 row) 148 | 149 | -------------------------------------------------------------------------------- /test/conv_bitstring/test.expected.74: -------------------------------------------------------------------------------- 1 | create table bit_op ( 2 | b0 bit(8), b1 bit(8), band bit(8), bor bit(8), 3 | bxor bit(8), bnot0 bit(8), bnot1 bit(8) 4 | ); 5 | create function bt(integer, integer) returns bit_op as ' 6 | b0 = BitString.new(args[0], 8) 7 | b1 = BitString.new(args[1], 8) 8 | [b0, b1, b0 & b1, b0 | b1, b0 ^ b1, ~b0, ~b1] 9 | ' language 'plruby'; 10 | select * from bt(12, 24); 11 | b0 | b1 | band | bor | bxor | bnot0 | bnot1 12 | ----------+----------+----------+----------+----------+----------+---------- 13 | 00001100 | 00011000 | 00001000 | 00011100 | 00010100 | 11110011 | 11100111 14 | (1 row) 15 | 16 | select * from bt(12, 32); 17 | b0 | b1 | band | bor | bxor | bnot0 | bnot1 18 | ----------+----------+----------+----------+----------+----------+---------- 19 | 00001100 | 00100000 | 00000000 | 00101100 | 00101100 | 11110011 | 11011111 20 | (1 row) 21 | 22 | select * from bt(15, 278); 23 | b0 | b1 | band | bor | bxor | bnot0 | bnot1 24 | ----------+----------+----------+----------+----------+----------+---------- 25 | 00001111 | 00010110 | 00000110 | 00011111 | 00011001 | 11110000 | 11101001 26 | (1 row) 27 | 28 | create function be(integer) returns setof integer as ' 29 | BitString.new(args[0], 8).each {|i| yield i} 30 | ' language 'plruby'; 31 | select * from be(12); 32 | be 33 | ---- 34 | 0 35 | 0 36 | 0 37 | 0 38 | 1 39 | 1 40 | 0 41 | 0 42 | (8 rows) 43 | 44 | select * from be(257); 45 | be 46 | ---- 47 | 0 48 | 0 49 | 0 50 | 0 51 | 0 52 | 0 53 | 0 54 | 1 55 | (8 rows) 56 | 57 | create function bx(integer, integer) returns bit varying as ' 58 | BitString.new(*args) 59 | ' language 'plruby'; 60 | select bx(12, 6); 61 | bx 62 | -------- 63 | 001100 64 | (1 row) 65 | 66 | select bx(12, 8); 67 | bx 68 | ---------- 69 | 00001100 70 | (1 row) 71 | 72 | create table bit_sht ( 73 | b0 bit(8), shft int, bl bit(8), br bit(8), bs text, bi integer, 74 | sz integer, osz integer 75 | ); 76 | create function bs(integer, integer) returns bit_sht as ' 77 | b0 = BitString.new(args[0], 8) 78 | [b0, args[1], b0 << args[1], b0 >> args[1], b0.to_s, b0.to_i, 79 | b0.size, b0.octet_size] 80 | ' language 'plruby'; 81 | select * from bs(12, 2); 82 | b0 | shft | bl | br | bs | bi | sz | osz 83 | ----------+------+----------+----------+----------+----+----+----- 84 | 00001100 | 2 | 00110000 | 00000011 | 00001100 | 12 | 8 | 1 85 | (1 row) 86 | 87 | select * from bs(277, -3); 88 | b0 | shft | bl | br | bs | bi | sz | osz 89 | ----------+------+----------+----------+----------+----+----+----- 90 | 00010101 | -3 | 00000010 | 10101000 | 00010101 | 21 | 8 | 1 91 | (1 row) 92 | 93 | create function ext(text, integer) returns integer as ' 94 | b0 = BitString.new(args[0]) 95 | b0[args[1]] 96 | ' language 'plruby'; 97 | select ext('011110', 0); 98 | ext 99 | ----- 100 | 0 101 | (1 row) 102 | 103 | select ext('011110', -1); 104 | ext 105 | ----- 106 | 0 107 | (1 row) 108 | 109 | select ext('011110', 1); 110 | ext 111 | ----- 112 | 1 113 | (1 row) 114 | 115 | select ext('011110', 4); 116 | ext 117 | ----- 118 | 1 119 | (1 row) 120 | 121 | create function ext2(text, integer, integer) returns bit varying(8) as ' 122 | b0 = BitString.new(args[0]) 123 | b0[args[1], args[2]] 124 | ' language 'plruby'; 125 | select ext2('0111101', 0, 2); 126 | ext2 127 | ------ 128 | 01 129 | (1 row) 130 | 131 | select ext2('0111101', -1, 3); 132 | ext2 133 | ------ 134 | 1 135 | (1 row) 136 | 137 | select ext2('0111101', 1, 2); 138 | ext2 139 | ------ 140 | 11 141 | (1 row) 142 | 143 | select ext2('0111101', 4, 1); 144 | ext2 145 | ------ 146 | 1 147 | (1 row) 148 | 149 | -------------------------------------------------------------------------------- /test/conv_bitstring/test.expected.80: -------------------------------------------------------------------------------- 1 | create table bit_op ( 2 | b0 bit(8), b1 bit(8), band bit(8), bor bit(8), 3 | bxor bit(8), bnot0 bit(8), bnot1 bit(8) 4 | ); 5 | create function bt(integer, integer) returns bit_op as ' 6 | b0 = BitString.new(args[0], 8) 7 | b1 = BitString.new(args[1], 8) 8 | [b0, b1, b0 & b1, b0 | b1, b0 ^ b1, ~b0, ~b1] 9 | ' language 'plruby'; 10 | select * from bt(12, 24); 11 | b0 | b1 | band | bor | bxor | bnot0 | bnot1 12 | ----------+----------+----------+----------+----------+----------+---------- 13 | 00001100 | 00011000 | 00001000 | 00011100 | 00010100 | 11110011 | 11100111 14 | (1 row) 15 | 16 | select * from bt(12, 32); 17 | b0 | b1 | band | bor | bxor | bnot0 | bnot1 18 | ----------+----------+----------+----------+----------+----------+---------- 19 | 00001100 | 00100000 | 00000000 | 00101100 | 00101100 | 11110011 | 11011111 20 | (1 row) 21 | 22 | select * from bt(15, 278); 23 | b0 | b1 | band | bor | bxor | bnot0 | bnot1 24 | ----------+----------+----------+----------+----------+----------+---------- 25 | 00001111 | 00010110 | 00000110 | 00011111 | 00011001 | 11110000 | 11101001 26 | (1 row) 27 | 28 | create function be(integer) returns setof integer as ' 29 | BitString.new(args[0], 8).each {|i| yield i} 30 | ' language 'plruby'; 31 | select * from be(12); 32 | be 33 | ---- 34 | 0 35 | 0 36 | 0 37 | 0 38 | 1 39 | 1 40 | 0 41 | 0 42 | (8 rows) 43 | 44 | select * from be(257); 45 | be 46 | ---- 47 | 0 48 | 0 49 | 0 50 | 0 51 | 0 52 | 0 53 | 0 54 | 1 55 | (8 rows) 56 | 57 | create function bx(integer, integer) returns bit varying as ' 58 | BitString.new(*args) 59 | ' language 'plruby'; 60 | select bx(12, 6); 61 | bx 62 | -------- 63 | 001100 64 | (1 row) 65 | 66 | select bx(12, 8); 67 | bx 68 | ---------- 69 | 00001100 70 | (1 row) 71 | 72 | create table bit_sht ( 73 | b0 bit(8), shft int, bl bit(8), br bit(8), bs text, bi integer, 74 | sz integer, osz integer 75 | ); 76 | create function bs(integer, integer) returns bit_sht as ' 77 | b0 = BitString.new(args[0], 8) 78 | [b0, args[1], b0 << args[1], b0 >> args[1], b0.to_s, b0.to_i, 79 | b0.size, b0.octet_size] 80 | ' language 'plruby'; 81 | select * from bs(12, 2); 82 | b0 | shft | bl | br | bs | bi | sz | osz 83 | ----------+------+----------+----------+----------+----+----+----- 84 | 00001100 | 2 | 00110000 | 00000011 | 00001100 | 12 | 8 | 1 85 | (1 row) 86 | 87 | select * from bs(277, -3); 88 | b0 | shft | bl | br | bs | bi | sz | osz 89 | ----------+------+----------+----------+----------+----+----+----- 90 | 00010101 | -3 | 00000010 | 10101000 | 00010101 | 21 | 8 | 1 91 | (1 row) 92 | 93 | create function ext(text, integer) returns integer as ' 94 | b0 = BitString.new(args[0]) 95 | b0[args[1]] 96 | ' language 'plruby'; 97 | select ext('011110', 0); 98 | ext 99 | ----- 100 | 0 101 | (1 row) 102 | 103 | select ext('011110', -1); 104 | ext 105 | ----- 106 | 0 107 | (1 row) 108 | 109 | select ext('011110', 1); 110 | ext 111 | ----- 112 | 1 113 | (1 row) 114 | 115 | select ext('011110', 4); 116 | ext 117 | ----- 118 | 1 119 | (1 row) 120 | 121 | create function ext2(text, integer, integer) returns bit varying(8) as ' 122 | b0 = BitString.new(args[0]) 123 | b0[args[1], args[2]] 124 | ' language 'plruby'; 125 | select ext2('0111101', 0, 2); 126 | ext2 127 | ------ 128 | 01 129 | (1 row) 130 | 131 | select ext2('0111101', -1, 3); 132 | ext2 133 | ------ 134 | 1 135 | (1 row) 136 | 137 | select ext2('0111101', 1, 2); 138 | ext2 139 | ------ 140 | 11 141 | (1 row) 142 | 143 | select ext2('0111101', 4, 1); 144 | ext2 145 | ------ 146 | 1 147 | (1 row) 148 | 149 | -------------------------------------------------------------------------------- /test/conv_bitstring/test.expected.81: -------------------------------------------------------------------------------- 1 | create table bit_op ( 2 | b0 bit(8), b1 bit(8), band bit(8), bor bit(8), 3 | bxor bit(8), bnot0 bit(8), bnot1 bit(8) 4 | ); 5 | create function bt(integer, integer) returns bit_op as ' 6 | b0 = BitString.new(args[0], 8) 7 | b1 = BitString.new(args[1], 8) 8 | [b0, b1, b0 & b1, b0 | b1, b0 ^ b1, ~b0, ~b1] 9 | ' language 'plruby'; 10 | select * from bt(12, 24); 11 | b0 | b1 | band | bor | bxor | bnot0 | bnot1 12 | ----------+----------+----------+----------+----------+----------+---------- 13 | 00001100 | 00011000 | 00001000 | 00011100 | 00010100 | 11110011 | 11100111 14 | (1 row) 15 | 16 | select * from bt(12, 32); 17 | b0 | b1 | band | bor | bxor | bnot0 | bnot1 18 | ----------+----------+----------+----------+----------+----------+---------- 19 | 00001100 | 00100000 | 00000000 | 00101100 | 00101100 | 11110011 | 11011111 20 | (1 row) 21 | 22 | select * from bt(15, 278); 23 | b0 | b1 | band | bor | bxor | bnot0 | bnot1 24 | ----------+----------+----------+----------+----------+----------+---------- 25 | 00001111 | 00010110 | 00000110 | 00011111 | 00011001 | 11110000 | 11101001 26 | (1 row) 27 | 28 | create function be(integer) returns setof integer as ' 29 | BitString.new(args[0], 8).each {|i| yield i} 30 | ' language 'plruby'; 31 | select * from be(12); 32 | be 33 | ---- 34 | 0 35 | 0 36 | 0 37 | 0 38 | 1 39 | 1 40 | 0 41 | 0 42 | (8 rows) 43 | 44 | select * from be(257); 45 | be 46 | ---- 47 | 0 48 | 0 49 | 0 50 | 0 51 | 0 52 | 0 53 | 0 54 | 1 55 | (8 rows) 56 | 57 | create function bx(integer, integer) returns bit varying as ' 58 | BitString.new(*args) 59 | ' language 'plruby'; 60 | select bx(12, 6); 61 | bx 62 | -------- 63 | 001100 64 | (1 row) 65 | 66 | select bx(12, 8); 67 | bx 68 | ---------- 69 | 00001100 70 | (1 row) 71 | 72 | create table bit_sht ( 73 | b0 bit(8), shft int, bl bit(8), br bit(8), bs text, bi integer, 74 | sz integer, osz integer 75 | ); 76 | create function bs(integer, integer) returns bit_sht as ' 77 | b0 = BitString.new(args[0], 8) 78 | [b0, args[1], b0 << args[1], b0 >> args[1], b0.to_s, b0.to_i, 79 | b0.size, b0.octet_size] 80 | ' language 'plruby'; 81 | select * from bs(12, 2); 82 | b0 | shft | bl | br | bs | bi | sz | osz 83 | ----------+------+----------+----------+----------+----+----+----- 84 | 00001100 | 2 | 00110000 | 00000011 | 00001100 | 12 | 8 | 1 85 | (1 row) 86 | 87 | select * from bs(277, -3); 88 | b0 | shft | bl | br | bs | bi | sz | osz 89 | ----------+------+----------+----------+----------+----+----+----- 90 | 00010101 | -3 | 00000010 | 10101000 | 00010101 | 21 | 8 | 1 91 | (1 row) 92 | 93 | create function ext(text, integer) returns integer as ' 94 | b0 = BitString.new(args[0]) 95 | b0[args[1]] 96 | ' language 'plruby'; 97 | select ext('011110', 0); 98 | ext 99 | ----- 100 | 0 101 | (1 row) 102 | 103 | select ext('011110', -1); 104 | ext 105 | ----- 106 | 0 107 | (1 row) 108 | 109 | select ext('011110', 1); 110 | ext 111 | ----- 112 | 1 113 | (1 row) 114 | 115 | select ext('011110', 4); 116 | ext 117 | ----- 118 | 1 119 | (1 row) 120 | 121 | create function ext2(text, integer, integer) returns bit varying(8) as ' 122 | b0 = BitString.new(args[0]) 123 | b0[args[1], args[2]] 124 | ' language 'plruby'; 125 | select ext2('0111101', 0, 2); 126 | ext2 127 | ------ 128 | 01 129 | (1 row) 130 | 131 | select ext2('0111101', -1, 3); 132 | ext2 133 | ------ 134 | 1 135 | (1 row) 136 | 137 | select ext2('0111101', 1, 2); 138 | ext2 139 | ------ 140 | 11 141 | (1 row) 142 | 143 | select ext2('0111101', 4, 1); 144 | ext2 145 | ------ 146 | 1 147 | (1 row) 148 | 149 | -------------------------------------------------------------------------------- /test/conv_bitstring/test.expected.82: -------------------------------------------------------------------------------- 1 | create table bit_op ( 2 | b0 bit(8), b1 bit(8), band bit(8), bor bit(8), 3 | bxor bit(8), bnot0 bit(8), bnot1 bit(8) 4 | ); 5 | create function bt(integer, integer) returns bit_op as ' 6 | b0 = BitString.new(args[0], 8) 7 | b1 = BitString.new(args[1], 8) 8 | [b0, b1, b0 & b1, b0 | b1, b0 ^ b1, ~b0, ~b1] 9 | ' language 'plruby'; 10 | select * from bt(12, 24); 11 | b0 | b1 | band | bor | bxor | bnot0 | bnot1 12 | ----------+----------+----------+----------+----------+----------+---------- 13 | 00001100 | 00011000 | 00001000 | 00011100 | 00010100 | 11110011 | 11100111 14 | (1 row) 15 | 16 | select * from bt(12, 32); 17 | b0 | b1 | band | bor | bxor | bnot0 | bnot1 18 | ----------+----------+----------+----------+----------+----------+---------- 19 | 00001100 | 00100000 | 00000000 | 00101100 | 00101100 | 11110011 | 11011111 20 | (1 row) 21 | 22 | select * from bt(15, 278); 23 | b0 | b1 | band | bor | bxor | bnot0 | bnot1 24 | ----------+----------+----------+----------+----------+----------+---------- 25 | 00001111 | 00010110 | 00000110 | 00011111 | 00011001 | 11110000 | 11101001 26 | (1 row) 27 | 28 | create function be(integer) returns setof integer as ' 29 | BitString.new(args[0], 8).each {|i| yield i} 30 | ' language 'plruby'; 31 | select * from be(12); 32 | be 33 | ---- 34 | 0 35 | 0 36 | 0 37 | 0 38 | 1 39 | 1 40 | 0 41 | 0 42 | (8 rows) 43 | 44 | select * from be(257); 45 | be 46 | ---- 47 | 0 48 | 0 49 | 0 50 | 0 51 | 0 52 | 0 53 | 0 54 | 1 55 | (8 rows) 56 | 57 | create function bx(integer, integer) returns bit varying as ' 58 | BitString.new(*args) 59 | ' language 'plruby'; 60 | select bx(12, 6); 61 | bx 62 | -------- 63 | 001100 64 | (1 row) 65 | 66 | select bx(12, 8); 67 | bx 68 | ---------- 69 | 00001100 70 | (1 row) 71 | 72 | create table bit_sht ( 73 | b0 bit(8), shft int, bl bit(8), br bit(8), bs text, bi integer, 74 | sz integer, osz integer 75 | ); 76 | create function bs(integer, integer) returns bit_sht as ' 77 | b0 = BitString.new(args[0], 8) 78 | [b0, args[1], b0 << args[1], b0 >> args[1], b0.to_s, b0.to_i, 79 | b0.size, b0.octet_size] 80 | ' language 'plruby'; 81 | select * from bs(12, 2); 82 | b0 | shft | bl | br | bs | bi | sz | osz 83 | ----------+------+----------+----------+----------+----+----+----- 84 | 00001100 | 2 | 00110000 | 00000011 | 00001100 | 12 | 8 | 1 85 | (1 row) 86 | 87 | select * from bs(277, -3); 88 | b0 | shft | bl | br | bs | bi | sz | osz 89 | ----------+------+----------+----------+----------+----+----+----- 90 | 00010101 | -3 | 00000010 | 10101000 | 00010101 | 21 | 8 | 1 91 | (1 row) 92 | 93 | create function ext(text, integer) returns integer as ' 94 | b0 = BitString.new(args[0]) 95 | b0[args[1]] 96 | ' language 'plruby'; 97 | select ext('011110', 0); 98 | ext 99 | ----- 100 | 0 101 | (1 row) 102 | 103 | select ext('011110', -1); 104 | ext 105 | ----- 106 | 0 107 | (1 row) 108 | 109 | select ext('011110', 1); 110 | ext 111 | ----- 112 | 1 113 | (1 row) 114 | 115 | select ext('011110', 4); 116 | ext 117 | ----- 118 | 1 119 | (1 row) 120 | 121 | create function ext2(text, integer, integer) returns bit varying(8) as ' 122 | b0 = BitString.new(args[0]) 123 | b0[args[1], args[2]] 124 | ' language 'plruby'; 125 | select ext2('0111101', 0, 2); 126 | ext2 127 | ------ 128 | 01 129 | (1 row) 130 | 131 | select ext2('0111101', -1, 3); 132 | ext2 133 | ------ 134 | 1 135 | (1 row) 136 | 137 | select ext2('0111101', 1, 2); 138 | ext2 139 | ------ 140 | 11 141 | (1 row) 142 | 143 | select ext2('0111101', 4, 1); 144 | ext2 145 | ------ 146 | 1 147 | (1 row) 148 | 149 | -------------------------------------------------------------------------------- /test/conv_bitstring/test.expected.83: -------------------------------------------------------------------------------- 1 | create table bit_op ( 2 | b0 bit(8), b1 bit(8), band bit(8), bor bit(8), 3 | bxor bit(8), bnot0 bit(8), bnot1 bit(8) 4 | ); 5 | create function bt(integer, integer) returns bit_op as ' 6 | b0 = BitString.new(args[0], 8) 7 | b1 = BitString.new(args[1], 8) 8 | [b0, b1, b0 & b1, b0 | b1, b0 ^ b1, ~b0, ~b1] 9 | ' language 'plruby'; 10 | select * from bt(12, 24); 11 | b0 | b1 | band | bor | bxor | bnot0 | bnot1 12 | ----------+----------+----------+----------+----------+----------+---------- 13 | 00001100 | 00011000 | 00001000 | 00011100 | 00010100 | 11110011 | 11100111 14 | (1 row) 15 | 16 | select * from bt(12, 32); 17 | b0 | b1 | band | bor | bxor | bnot0 | bnot1 18 | ----------+----------+----------+----------+----------+----------+---------- 19 | 00001100 | 00100000 | 00000000 | 00101100 | 00101100 | 11110011 | 11011111 20 | (1 row) 21 | 22 | select * from bt(15, 278); 23 | b0 | b1 | band | bor | bxor | bnot0 | bnot1 24 | ----------+----------+----------+----------+----------+----------+---------- 25 | 00001111 | 00010110 | 00000110 | 00011111 | 00011001 | 11110000 | 11101001 26 | (1 row) 27 | 28 | create function be(integer) returns setof integer as ' 29 | BitString.new(args[0], 8).each {|i| yield i} 30 | ' language 'plruby'; 31 | select * from be(12); 32 | be 33 | ---- 34 | 0 35 | 0 36 | 0 37 | 0 38 | 1 39 | 1 40 | 0 41 | 0 42 | (8 rows) 43 | 44 | select * from be(257); 45 | be 46 | ---- 47 | 0 48 | 0 49 | 0 50 | 0 51 | 0 52 | 0 53 | 0 54 | 1 55 | (8 rows) 56 | 57 | create function bx(integer, integer) returns bit varying as ' 58 | BitString.new(*args) 59 | ' language 'plruby'; 60 | select bx(12, 6); 61 | bx 62 | -------- 63 | 001100 64 | (1 row) 65 | 66 | select bx(12, 8); 67 | bx 68 | ---------- 69 | 00001100 70 | (1 row) 71 | 72 | create table bit_sht ( 73 | b0 bit(8), shft int, bl bit(8), br bit(8), bs text, bi integer, 74 | sz integer, osz integer 75 | ); 76 | create function bs(integer, integer) returns bit_sht as ' 77 | b0 = BitString.new(args[0], 8) 78 | [b0, args[1], b0 << args[1], b0 >> args[1], b0.to_s, b0.to_i, 79 | b0.size, b0.octet_size] 80 | ' language 'plruby'; 81 | select * from bs(12, 2); 82 | b0 | shft | bl | br | bs | bi | sz | osz 83 | ----------+------+----------+----------+----------+----+----+----- 84 | 00001100 | 2 | 00110000 | 00000011 | 00001100 | 12 | 8 | 1 85 | (1 row) 86 | 87 | select * from bs(277, -3); 88 | b0 | shft | bl | br | bs | bi | sz | osz 89 | ----------+------+----------+----------+----------+----+----+----- 90 | 00010101 | -3 | 00000010 | 10101000 | 00010101 | 21 | 8 | 1 91 | (1 row) 92 | 93 | create function ext(text, integer) returns integer as ' 94 | b0 = BitString.new(args[0]) 95 | b0[args[1]] 96 | ' language 'plruby'; 97 | select ext('011110', 0); 98 | ext 99 | ----- 100 | 0 101 | (1 row) 102 | 103 | select ext('011110', -1); 104 | ext 105 | ----- 106 | 0 107 | (1 row) 108 | 109 | select ext('011110', 1); 110 | ext 111 | ----- 112 | 1 113 | (1 row) 114 | 115 | select ext('011110', 4); 116 | ext 117 | ----- 118 | 1 119 | (1 row) 120 | 121 | create function ext2(text, integer, integer) returns bit varying(8) as ' 122 | b0 = BitString.new(args[0]) 123 | b0[args[1], args[2]] 124 | ' language 'plruby'; 125 | select ext2('0111101', 0, 2); 126 | ext2 127 | ------ 128 | 01 129 | (1 row) 130 | 131 | select ext2('0111101', -1, 3); 132 | ext2 133 | ------ 134 | 1 135 | (1 row) 136 | 137 | select ext2('0111101', 1, 2); 138 | ext2 139 | ------ 140 | 11 141 | (1 row) 142 | 143 | select ext2('0111101', 4, 1); 144 | ext2 145 | ------ 146 | 1 147 | (1 row) 148 | 149 | -------------------------------------------------------------------------------- /test/conv_bitstring/test.expected.84: -------------------------------------------------------------------------------- 1 | create table bit_op ( 2 | b0 bit(8), b1 bit(8), band bit(8), bor bit(8), 3 | bxor bit(8), bnot0 bit(8), bnot1 bit(8) 4 | ); 5 | create function bt(integer, integer) returns bit_op as ' 6 | b0 = BitString.new(args[0], 8) 7 | b1 = BitString.new(args[1], 8) 8 | [b0, b1, b0 & b1, b0 | b1, b0 ^ b1, ~b0, ~b1] 9 | ' language 'plruby'; 10 | select * from bt(12, 24); 11 | b0 | b1 | band | bor | bxor | bnot0 | bnot1 12 | ----------+----------+----------+----------+----------+----------+---------- 13 | 00001100 | 00011000 | 00001000 | 00011100 | 00010100 | 11110011 | 11100111 14 | (1 row) 15 | 16 | select * from bt(12, 32); 17 | b0 | b1 | band | bor | bxor | bnot0 | bnot1 18 | ----------+----------+----------+----------+----------+----------+---------- 19 | 00001100 | 00100000 | 00000000 | 00101100 | 00101100 | 11110011 | 11011111 20 | (1 row) 21 | 22 | select * from bt(15, 278); 23 | b0 | b1 | band | bor | bxor | bnot0 | bnot1 24 | ----------+----------+----------+----------+----------+----------+---------- 25 | 00001111 | 00010110 | 00000110 | 00011111 | 00011001 | 11110000 | 11101001 26 | (1 row) 27 | 28 | create function be(integer) returns setof integer as ' 29 | BitString.new(args[0], 8).each {|i| yield i} 30 | ' language 'plruby'; 31 | select * from be(12); 32 | be 33 | ---- 34 | 0 35 | 0 36 | 0 37 | 0 38 | 1 39 | 1 40 | 0 41 | 0 42 | (8 rows) 43 | 44 | select * from be(257); 45 | be 46 | ---- 47 | 0 48 | 0 49 | 0 50 | 0 51 | 0 52 | 0 53 | 0 54 | 1 55 | (8 rows) 56 | 57 | create function bx(integer, integer) returns bit varying as ' 58 | BitString.new(*args) 59 | ' language 'plruby'; 60 | select bx(12, 6); 61 | bx 62 | -------- 63 | 001100 64 | (1 row) 65 | 66 | select bx(12, 8); 67 | bx 68 | ---------- 69 | 00001100 70 | (1 row) 71 | 72 | create table bit_sht ( 73 | b0 bit(8), shft int, bl bit(8), br bit(8), bs text, bi integer, 74 | sz integer, osz integer 75 | ); 76 | create function bs(integer, integer) returns bit_sht as ' 77 | b0 = BitString.new(args[0], 8) 78 | [b0, args[1], b0 << args[1], b0 >> args[1], b0.to_s, b0.to_i, 79 | b0.size, b0.octet_size] 80 | ' language 'plruby'; 81 | select * from bs(12, 2); 82 | b0 | shft | bl | br | bs | bi | sz | osz 83 | ----------+------+----------+----------+----------+----+----+----- 84 | 00001100 | 2 | 00110000 | 00000011 | 00001100 | 12 | 8 | 1 85 | (1 row) 86 | 87 | select * from bs(277, -3); 88 | b0 | shft | bl | br | bs | bi | sz | osz 89 | ----------+------+----------+----------+----------+----+----+----- 90 | 00010101 | -3 | 00000010 | 10101000 | 00010101 | 21 | 8 | 1 91 | (1 row) 92 | 93 | create function ext(text, integer) returns integer as ' 94 | b0 = BitString.new(args[0]) 95 | b0[args[1]] 96 | ' language 'plruby'; 97 | select ext('011110', 0); 98 | ext 99 | ----- 100 | 0 101 | (1 row) 102 | 103 | select ext('011110', -1); 104 | ext 105 | ----- 106 | 0 107 | (1 row) 108 | 109 | select ext('011110', 1); 110 | ext 111 | ----- 112 | 1 113 | (1 row) 114 | 115 | select ext('011110', 4); 116 | ext 117 | ----- 118 | 1 119 | (1 row) 120 | 121 | create function ext2(text, integer, integer) returns bit varying(8) as ' 122 | b0 = BitString.new(args[0]) 123 | b0[args[1], args[2]] 124 | ' language 'plruby'; 125 | select ext2('0111101', 0, 2); 126 | ext2 127 | ------ 128 | 01 129 | (1 row) 130 | 131 | select ext2('0111101', -1, 3); 132 | ext2 133 | ------ 134 | 1 135 | (1 row) 136 | 137 | select ext2('0111101', 1, 2); 138 | ext2 139 | ------ 140 | 11 141 | (1 row) 142 | 143 | select ext2('0111101', 4, 1); 144 | ext2 145 | ------ 146 | 1 147 | (1 row) 148 | 149 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | PL/ruby 2 | ======= 3 | 4 | PL/Ruby is a loadable procedural language for the PostgreSQL database 5 | system that enables the Ruby language to create functions and trigger 6 | procedures. 7 | 8 | 9 | Prerequisite 10 | ------------ 11 | 12 | > * ruby 1.8.7 or later (maybe 1.8.6 too) 13 | > * postgresql >= 7.3 14 | 15 | All PostgreSQL headers need to be installed. Command (see `INSTALL` in the 16 | directory postgresql-7.x.y) 17 | 18 | make install-all-headers 19 | 20 | Installation 21 | ------------ 22 | 23 | ruby extconf.rb 24 | make 25 | make install 26 | 27 | You may need to specify some of the following extconf.rb options: 28 | 29 | --with-pg-config= 30 | 31 | Specifies the location of pg_config. 32 | e.g. --with-pg-config=/usr/local/bin/pg_config 33 | 34 | --with-greenplum 35 | 36 | Builds plruby for Greenplum instead of PostgreSQL. 37 | 38 | --disable-conversion 39 | 40 | By default plruby tries to convert a postgres type to a ruby class. 41 | This option gives the possibility to disable all conversions. 42 | 43 | --with-suffix= 44 | 45 | Specifies a suffix to add to the extension module file. 46 | e.g. `ruby extconf.rb --with-suffix=_geo` will create 47 | `plruby_geo.so`. 48 | 49 | --with-safe-level 50 | 51 | Lowers the safe level which the plruby functions are run under. 52 | (default: 12; meaning the maximum) 53 | 54 | --with-timeout= 55 | 56 | Sets the timeout for each function call. (default: none) 57 | 58 | --with-main-safe-level 59 | 60 | Lowers the safe level which the main thread waiting for timeouts is 61 | run under. (default: 3) This option is read only when --with-timeout 62 | is given. 63 | 64 | 65 | Test (and examples) 66 | ------------------- 67 | 68 | WARNING: if plruby was compiled without --disable-conversion you 69 | must **FIRST** run `make install` before `make test`. 70 | 71 | make test 72 | 73 | This will run the following two commands: 74 | 75 | ( cd test/plt; ./runtest ) 76 | ( cd test/plp; ./runtest ) 77 | 78 | The database `plruby_test` is created and then destroyed. Don't use 79 | it if you have such a database. 80 | 81 | Now you are ready to create the PL/Ruby language in PostgreSQL. 82 | 83 | Since the `pg_language` system catalog is private to each database, 84 | the new language can be created only for individual databases, or in 85 | the template1 database. In the latter case, it is automatically 86 | available in all newly created databases. 87 | 88 | The commands to create the new language are: 89 | 90 | create function plruby_call_handler () returns language_handler 91 | as 'path-to-plruby-shared-lib' 92 | language 'C'; 93 | 94 | create trusted language 'plruby' 95 | handler plruby_call_handler 96 | lancompiler 'PL/Ruby'; 97 | 98 | 99 | The `trusted` keyword on `create language` tells PostgreSQL, 100 | that all users (not only those with superuser privilege) are 101 | permitted to create functions with `LANGUAGE 'plruby'`. This is 102 | absolutely safe, because there is nothing a normal user can do 103 | with PL/Ruby, to get around access restrictions he/she has. 104 | 105 | Documentation 106 | ------------- 107 | 108 | see `plruby.rd` and `plruby.html` 109 | 110 | Development 111 | ----------- 112 | 113 | New releases and sources can be obtained from 114 | 115 | Copying 116 | ------- 117 | 118 | This extension module is copyrighted free software by Guy Decoux. 119 | 120 | You can redistribute it and/or modify it under the same term as Ruby. 121 | 122 | * * * 123 | 124 | Guy Decoux (original author, deceased in July 2008) 125 | Akinori MUSHA (current maintainer) 126 | -------------------------------------------------------------------------------- /test/plt/test.expected.83: -------------------------------------------------------------------------------- 1 | insert into T_pkey1 values (1, 'key1-1', 'test key'); 2 | insert into T_pkey1 values (1, 'key1-2', 'test key'); 3 | insert into T_pkey1 values (1, 'key1-3', 'test key'); 4 | insert into T_pkey1 values (2, 'key2-1', 'test key'); 5 | insert into T_pkey1 values (2, 'key2-2', 'test key'); 6 | insert into T_pkey1 values (2, 'key2-3', 'test key'); 7 | insert into T_pkey2 values (1, 'key1-1', 'test key'); 8 | insert into T_pkey2 values (1, 'key1-2', 'test key'); 9 | insert into T_pkey2 values (1, 'key1-3', 'test key'); 10 | insert into T_pkey2 values (2, 'key2-1', 'test key'); 11 | insert into T_pkey2 values (2, 'key2-2', 'test key'); 12 | insert into T_pkey2 values (2, 'key2-3', 'test key'); 13 | select * from T_pkey1; 14 | key1 | key2 | txt 15 | ------+--------+---------- 16 | 1 | key1-1 | test key 17 | 1 | key1-2 | test key 18 | 1 | key1-3 | test key 19 | 2 | key2-1 | test key 20 | 2 | key2-2 | test key 21 | 2 | key2-3 | test key 22 | (6 rows) 23 | 24 | select * from T_pkey2; 25 | key1 | key2 | txt 26 | ------+--------+---------- 27 | 1 | KEY1-1 | test key 28 | 1 | KEY1-2 | test key 29 | 1 | KEY1-3 | test key 30 | 2 | KEY2-1 | test key 31 | 2 | KEY2-2 | test key 32 | 2 | KEY2-3 | test key 33 | (6 rows) 34 | 35 | insert into T_pkey1 values (1, 'KEY1-3', 'should work'); 36 | insert into T_pkey2 values (1, 'KEY1-3', 'should fail'); 37 | ERROR: duplicate key '1', 'KEY1-3' for T_pkey2 38 | insert into T_dta1 values ('trec 1', 1, 'key1-1'); 39 | insert into T_dta1 values ('trec 2', 1, 'key1-2'); 40 | insert into T_dta1 values ('trec 3', 1, 'key1-3'); 41 | insert into T_dta1 values ('trec 4', 1, 'key1-4'); 42 | ERROR: key for t_dta1 not in t_pkey1 43 | insert into T_dta2 values ('trec 1', 1, 'KEY1-1'); 44 | insert into T_dta2 values ('trec 2', 1, 'KEY1-2'); 45 | insert into T_dta2 values ('trec 3', 1, 'KEY1-3'); 46 | insert into T_dta2 values ('trec 4', 1, 'KEY1-4'); 47 | ERROR: key for t_dta2 not in t_pkey2 48 | select * from T_dta1; 49 | tkey | ref1 | ref2 50 | --------+------+-------- 51 | trec 1 | 1 | key1-1 52 | trec 2 | 1 | key1-2 53 | trec 3 | 1 | key1-3 54 | (3 rows) 55 | 56 | select * from T_dta2; 57 | tkey | ref1 | ref2 58 | --------+------+-------- 59 | trec 1 | 1 | KEY1-1 60 | trec 2 | 1 | KEY1-2 61 | trec 3 | 1 | KEY1-3 62 | (3 rows) 63 | 64 | update T_pkey1 set key2 = 'key2-9' where key1 = 2 and key2 = 'key2-1'; 65 | update T_pkey1 set key2 = 'key1-9' where key1 = 1 and key2 = 'key1-1'; 66 | ERROR: key '1', 'key1-1' referenced by T_dta1 67 | delete from T_pkey1 where key1 = 2 and key2 = 'key2-2'; 68 | delete from T_pkey1 where key1 = 1 and key2 = 'key1-2'; 69 | ERROR: key '1', 'key1-2' referenced by T_dta1 70 | update T_pkey2 set key2 = 'KEY2-9' where key1 = 2 and key2 = 'KEY2-1'; 71 | update T_pkey2 set key2 = 'KEY1-9' where key1 = 1 and key2 = 'KEY1-1'; 72 | NOTICE: updated 1 entries in T_dta2 for new key in T_pkey2 73 | delete from T_pkey2 where key1 = 2 and key2 = 'KEY2-2'; 74 | delete from T_pkey2 where key1 = 1 and key2 = 'KEY1-2'; 75 | NOTICE: deleted 1 entries from T_dta2 76 | select * from T_pkey1; 77 | key1 | key2 | txt 78 | ------+--------+------------- 79 | 1 | key1-1 | test key 80 | 1 | key1-2 | test key 81 | 1 | key1-3 | test key 82 | 2 | key2-3 | test key 83 | 1 | KEY1-3 | should work 84 | 2 | key2-9 | test key 85 | (6 rows) 86 | 87 | select * from T_pkey2; 88 | key1 | key2 | txt 89 | ------+--------+---------- 90 | 1 | KEY1-3 | test key 91 | 2 | KEY2-3 | test key 92 | 2 | KEY2-9 | test key 93 | 1 | KEY1-9 | test key 94 | (4 rows) 95 | 96 | select * from T_dta1; 97 | tkey | ref1 | ref2 98 | --------+------+-------- 99 | trec 1 | 1 | key1-1 100 | trec 2 | 1 | key1-2 101 | trec 3 | 1 | key1-3 102 | (3 rows) 103 | 104 | select * from T_dta2; 105 | tkey | ref1 | ref2 106 | --------+------+-------- 107 | trec 3 | 1 | KEY1-3 108 | trec 1 | 1 | KEY1-9 109 | (2 rows) 110 | 111 | select ruby_avg(key1) from T_pkey1; 112 | ruby_avg 113 | ---------- 114 | 1 115 | (1 row) 116 | 117 | select ruby_sum(key1) from T_pkey1; 118 | ruby_sum 119 | ---------- 120 | 8 121 | (1 row) 122 | 123 | select ruby_avg(key1) from T_pkey2; 124 | ruby_avg 125 | ---------- 126 | 1 127 | (1 row) 128 | 129 | select ruby_sum(key1) from T_pkey2; 130 | ruby_sum 131 | ---------- 132 | 6 133 | (1 row) 134 | 135 | select ruby_avg(key1) from T_pkey1 where key1 = 99; 136 | ruby_avg 137 | ---------- 138 | 139 | (1 row) 140 | 141 | select ruby_sum(key1) from T_pkey1 where key1 = 99; 142 | ruby_sum 143 | ---------- 144 | 0 145 | (1 row) 146 | 147 | select 1 @< 2; 148 | ?column? 149 | ---------- 150 | t 151 | (1 row) 152 | 153 | select 100 @< 4; 154 | ?column? 155 | ---------- 156 | f 157 | (1 row) 158 | 159 | select * from T_pkey1 order by key1 using @<; 160 | ERROR: operator @< is not a valid ordering operator 161 | HINT: Ordering operators must be "<" or ">" members of btree operator families. 162 | select * from T_pkey2 order by key1 using @<; 163 | ERROR: operator @< is not a valid ordering operator 164 | HINT: Ordering operators must be "<" or ">" members of btree operator families. 165 | -------------------------------------------------------------------------------- /test/plt/test.expected.73: -------------------------------------------------------------------------------- 1 | insert into T_pkey1 values (1, 'key1-1', 'test key'); 2 | insert into T_pkey1 values (1, 'key1-2', 'test key'); 3 | insert into T_pkey1 values (1, 'key1-3', 'test key'); 4 | insert into T_pkey1 values (2, 'key2-1', 'test key'); 5 | insert into T_pkey1 values (2, 'key2-2', 'test key'); 6 | insert into T_pkey1 values (2, 'key2-3', 'test key'); 7 | insert into T_pkey2 values (1, 'key1-1', 'test key'); 8 | insert into T_pkey2 values (1, 'key1-2', 'test key'); 9 | insert into T_pkey2 values (1, 'key1-3', 'test key'); 10 | insert into T_pkey2 values (2, 'key2-1', 'test key'); 11 | insert into T_pkey2 values (2, 'key2-2', 'test key'); 12 | insert into T_pkey2 values (2, 'key2-3', 'test key'); 13 | select * from T_pkey1; 14 | key1 | key2 | txt 15 | ------+--------+---------- 16 | 1 | key1-1 | test key 17 | 1 | key1-2 | test key 18 | 1 | key1-3 | test key 19 | 2 | key2-1 | test key 20 | 2 | key2-2 | test key 21 | 2 | key2-3 | test key 22 | (6 rows) 23 | 24 | select * from T_pkey2; 25 | key1 | key2 | txt 26 | ------+--------+---------- 27 | 1 | KEY1-1 | test key 28 | 1 | KEY1-2 | test key 29 | 1 | KEY1-3 | test key 30 | 2 | KEY2-1 | test key 31 | 2 | KEY2-2 | test key 32 | 2 | KEY2-3 | test key 33 | (6 rows) 34 | 35 | insert into T_pkey1 values (1, 'KEY1-3', 'should work'); 36 | insert into T_pkey2 values (1, 'KEY1-3', 'should fail'); 37 | ERROR: duplicate key '1', 'KEY1-3' for T_pkey2 38 | insert into T_dta1 values ('trec 1', 1, 'key1-1'); 39 | insert into T_dta1 values ('trec 2', 1, 'key1-2'); 40 | insert into T_dta1 values ('trec 3', 1, 'key1-3'); 41 | insert into T_dta1 values ('trec 4', 1, 'key1-4'); 42 | ERROR: key for t_dta1 not in t_pkey1 43 | insert into T_dta2 values ('trec 1', 1, 'KEY1-1'); 44 | insert into T_dta2 values ('trec 2', 1, 'KEY1-2'); 45 | insert into T_dta2 values ('trec 3', 1, 'KEY1-3'); 46 | insert into T_dta2 values ('trec 4', 1, 'KEY1-4'); 47 | ERROR: key for t_dta2 not in t_pkey2 48 | select * from T_dta1; 49 | tkey | ref1 | ref2 50 | --------+------+-------- 51 | trec 1 | 1 | key1-1 52 | trec 2 | 1 | key1-2 53 | trec 3 | 1 | key1-3 54 | (3 rows) 55 | 56 | select * from T_dta2; 57 | tkey | ref1 | ref2 58 | --------+------+-------- 59 | trec 1 | 1 | KEY1-1 60 | trec 2 | 1 | KEY1-2 61 | trec 3 | 1 | KEY1-3 62 | (3 rows) 63 | 64 | update T_pkey1 set key2 = 'key2-9' where key1 = 2 and key2 = 'key2-1'; 65 | update T_pkey1 set key2 = 'key1-9' where key1 = 1 and key2 = 'key1-1'; 66 | ERROR: key '1', 'key1-1' referenced by T_dta1 67 | delete from T_pkey1 where key1 = 2 and key2 = 'key2-2'; 68 | delete from T_pkey1 where key1 = 1 and key2 = 'key1-2'; 69 | ERROR: key '1', 'key1-2' referenced by T_dta1 70 | update T_pkey2 set key2 = 'KEY2-9' where key1 = 2 and key2 = 'KEY2-1'; 71 | update T_pkey2 set key2 = 'KEY1-9' where key1 = 1 and key2 = 'KEY1-1'; 72 | NOTICE: updated 1 entries in T_dta2 for new key in T_pkey2 73 | delete from T_pkey2 where key1 = 2 and key2 = 'KEY2-2'; 74 | delete from T_pkey2 where key1 = 1 and key2 = 'KEY1-2'; 75 | NOTICE: deleted 1 entries from T_dta2 76 | select * from T_pkey1; 77 | key1 | key2 | txt 78 | ------+--------+------------- 79 | 1 | key1-1 | test key 80 | 1 | key1-2 | test key 81 | 1 | key1-3 | test key 82 | 2 | key2-3 | test key 83 | 1 | KEY1-3 | should work 84 | 2 | key2-9 | test key 85 | (6 rows) 86 | 87 | select * from T_pkey2; 88 | key1 | key2 | txt 89 | ------+--------+---------- 90 | 1 | KEY1-3 | test key 91 | 2 | KEY2-3 | test key 92 | 2 | KEY2-9 | test key 93 | 1 | KEY1-9 | test key 94 | (4 rows) 95 | 96 | select * from T_dta1; 97 | tkey | ref1 | ref2 98 | --------+------+-------- 99 | trec 1 | 1 | key1-1 100 | trec 2 | 1 | key1-2 101 | trec 3 | 1 | key1-3 102 | (3 rows) 103 | 104 | select * from T_dta2; 105 | tkey | ref1 | ref2 106 | --------+------+-------- 107 | trec 3 | 1 | KEY1-3 108 | trec 1 | 1 | KEY1-9 109 | (2 rows) 110 | 111 | select ruby_avg(key1) from T_pkey1; 112 | ruby_avg 113 | ---------- 114 | 1 115 | (1 row) 116 | 117 | select ruby_sum(key1) from T_pkey1; 118 | ruby_sum 119 | ---------- 120 | 8 121 | (1 row) 122 | 123 | select ruby_avg(key1) from T_pkey2; 124 | ruby_avg 125 | ---------- 126 | 1 127 | (1 row) 128 | 129 | select ruby_sum(key1) from T_pkey2; 130 | ruby_sum 131 | ---------- 132 | 6 133 | (1 row) 134 | 135 | select ruby_avg(key1) from T_pkey1 where key1 = 99; 136 | ruby_avg 137 | ---------- 138 | 139 | (1 row) 140 | 141 | select ruby_sum(key1) from T_pkey1 where key1 = 99; 142 | ruby_sum 143 | ---------- 144 | 0 145 | (1 row) 146 | 147 | select 1 @< 2; 148 | ?column? 149 | ---------- 150 | t 151 | (1 row) 152 | 153 | select 100 @< 4; 154 | ?column? 155 | ---------- 156 | f 157 | (1 row) 158 | 159 | select * from T_pkey1 order by key1 using @<; 160 | key1 | key2 | txt 161 | ------+--------+------------- 162 | 1 | key1-1 | test key 163 | 1 | key1-2 | test key 164 | 1 | key1-3 | test key 165 | 1 | KEY1-3 | should work 166 | 2 | key2-3 | test key 167 | 2 | key2-9 | test key 168 | (6 rows) 169 | 170 | select * from T_pkey2 order by key1 using @<; 171 | key1 | key2 | txt 172 | ------+--------+---------- 173 | 1 | KEY1-3 | test key 174 | 1 | KEY1-9 | test key 175 | 2 | KEY2-3 | test key 176 | 2 | KEY2-9 | test key 177 | (4 rows) 178 | 179 | -------------------------------------------------------------------------------- /test/plt/test.expected.74: -------------------------------------------------------------------------------- 1 | insert into T_pkey1 values (1, 'key1-1', 'test key'); 2 | insert into T_pkey1 values (1, 'key1-2', 'test key'); 3 | insert into T_pkey1 values (1, 'key1-3', 'test key'); 4 | insert into T_pkey1 values (2, 'key2-1', 'test key'); 5 | insert into T_pkey1 values (2, 'key2-2', 'test key'); 6 | insert into T_pkey1 values (2, 'key2-3', 'test key'); 7 | insert into T_pkey2 values (1, 'key1-1', 'test key'); 8 | insert into T_pkey2 values (1, 'key1-2', 'test key'); 9 | insert into T_pkey2 values (1, 'key1-3', 'test key'); 10 | insert into T_pkey2 values (2, 'key2-1', 'test key'); 11 | insert into T_pkey2 values (2, 'key2-2', 'test key'); 12 | insert into T_pkey2 values (2, 'key2-3', 'test key'); 13 | select * from T_pkey1; 14 | key1 | key2 | txt 15 | ------+--------+---------- 16 | 1 | key1-1 | test key 17 | 1 | key1-2 | test key 18 | 1 | key1-3 | test key 19 | 2 | key2-1 | test key 20 | 2 | key2-2 | test key 21 | 2 | key2-3 | test key 22 | (6 rows) 23 | 24 | select * from T_pkey2; 25 | key1 | key2 | txt 26 | ------+--------+---------- 27 | 1 | KEY1-1 | test key 28 | 1 | KEY1-2 | test key 29 | 1 | KEY1-3 | test key 30 | 2 | KEY2-1 | test key 31 | 2 | KEY2-2 | test key 32 | 2 | KEY2-3 | test key 33 | (6 rows) 34 | 35 | insert into T_pkey1 values (1, 'KEY1-3', 'should work'); 36 | insert into T_pkey2 values (1, 'KEY1-3', 'should fail'); 37 | ERROR: duplicate key '1', 'KEY1-3' for T_pkey2 38 | insert into T_dta1 values ('trec 1', 1, 'key1-1'); 39 | insert into T_dta1 values ('trec 2', 1, 'key1-2'); 40 | insert into T_dta1 values ('trec 3', 1, 'key1-3'); 41 | insert into T_dta1 values ('trec 4', 1, 'key1-4'); 42 | ERROR: key for t_dta1 not in t_pkey1 43 | insert into T_dta2 values ('trec 1', 1, 'KEY1-1'); 44 | insert into T_dta2 values ('trec 2', 1, 'KEY1-2'); 45 | insert into T_dta2 values ('trec 3', 1, 'KEY1-3'); 46 | insert into T_dta2 values ('trec 4', 1, 'KEY1-4'); 47 | ERROR: key for t_dta2 not in t_pkey2 48 | select * from T_dta1; 49 | tkey | ref1 | ref2 50 | --------+------+-------- 51 | trec 1 | 1 | key1-1 52 | trec 2 | 1 | key1-2 53 | trec 3 | 1 | key1-3 54 | (3 rows) 55 | 56 | select * from T_dta2; 57 | tkey | ref1 | ref2 58 | --------+------+-------- 59 | trec 1 | 1 | KEY1-1 60 | trec 2 | 1 | KEY1-2 61 | trec 3 | 1 | KEY1-3 62 | (3 rows) 63 | 64 | update T_pkey1 set key2 = 'key2-9' where key1 = 2 and key2 = 'key2-1'; 65 | update T_pkey1 set key2 = 'key1-9' where key1 = 1 and key2 = 'key1-1'; 66 | ERROR: key '1', 'key1-1' referenced by T_dta1 67 | delete from T_pkey1 where key1 = 2 and key2 = 'key2-2'; 68 | delete from T_pkey1 where key1 = 1 and key2 = 'key1-2'; 69 | ERROR: key '1', 'key1-2' referenced by T_dta1 70 | update T_pkey2 set key2 = 'KEY2-9' where key1 = 2 and key2 = 'KEY2-1'; 71 | update T_pkey2 set key2 = 'KEY1-9' where key1 = 1 and key2 = 'KEY1-1'; 72 | NOTICE: updated 1 entries in T_dta2 for new key in T_pkey2 73 | delete from T_pkey2 where key1 = 2 and key2 = 'KEY2-2'; 74 | delete from T_pkey2 where key1 = 1 and key2 = 'KEY1-2'; 75 | NOTICE: deleted 1 entries from T_dta2 76 | select * from T_pkey1; 77 | key1 | key2 | txt 78 | ------+--------+------------- 79 | 1 | key1-1 | test key 80 | 1 | key1-2 | test key 81 | 1 | key1-3 | test key 82 | 2 | key2-3 | test key 83 | 1 | KEY1-3 | should work 84 | 2 | key2-9 | test key 85 | (6 rows) 86 | 87 | select * from T_pkey2; 88 | key1 | key2 | txt 89 | ------+--------+---------- 90 | 1 | KEY1-3 | test key 91 | 2 | KEY2-3 | test key 92 | 2 | KEY2-9 | test key 93 | 1 | KEY1-9 | test key 94 | (4 rows) 95 | 96 | select * from T_dta1; 97 | tkey | ref1 | ref2 98 | --------+------+-------- 99 | trec 1 | 1 | key1-1 100 | trec 2 | 1 | key1-2 101 | trec 3 | 1 | key1-3 102 | (3 rows) 103 | 104 | select * from T_dta2; 105 | tkey | ref1 | ref2 106 | --------+------+-------- 107 | trec 3 | 1 | KEY1-3 108 | trec 1 | 1 | KEY1-9 109 | (2 rows) 110 | 111 | select ruby_avg(key1) from T_pkey1; 112 | ruby_avg 113 | ---------- 114 | 1 115 | (1 row) 116 | 117 | select ruby_sum(key1) from T_pkey1; 118 | ruby_sum 119 | ---------- 120 | 8 121 | (1 row) 122 | 123 | select ruby_avg(key1) from T_pkey2; 124 | ruby_avg 125 | ---------- 126 | 1 127 | (1 row) 128 | 129 | select ruby_sum(key1) from T_pkey2; 130 | ruby_sum 131 | ---------- 132 | 6 133 | (1 row) 134 | 135 | select ruby_avg(key1) from T_pkey1 where key1 = 99; 136 | ruby_avg 137 | ---------- 138 | 139 | (1 row) 140 | 141 | select ruby_sum(key1) from T_pkey1 where key1 = 99; 142 | ruby_sum 143 | ---------- 144 | 0 145 | (1 row) 146 | 147 | select 1 @< 2; 148 | ?column? 149 | ---------- 150 | t 151 | (1 row) 152 | 153 | select 100 @< 4; 154 | ?column? 155 | ---------- 156 | f 157 | (1 row) 158 | 159 | select * from T_pkey1 order by key1 using @<; 160 | key1 | key2 | txt 161 | ------+--------+------------- 162 | 1 | key1-1 | test key 163 | 1 | key1-2 | test key 164 | 1 | key1-3 | test key 165 | 1 | KEY1-3 | should work 166 | 2 | key2-3 | test key 167 | 2 | key2-9 | test key 168 | (6 rows) 169 | 170 | select * from T_pkey2 order by key1 using @<; 171 | key1 | key2 | txt 172 | ------+--------+---------- 173 | 1 | KEY1-3 | test key 174 | 1 | KEY1-9 | test key 175 | 2 | KEY2-3 | test key 176 | 2 | KEY2-9 | test key 177 | (4 rows) 178 | 179 | -------------------------------------------------------------------------------- /test/plt/test.expected.75: -------------------------------------------------------------------------------- 1 | insert into T_pkey1 values (1, 'key1-1', 'test key'); 2 | insert into T_pkey1 values (1, 'key1-2', 'test key'); 3 | insert into T_pkey1 values (1, 'key1-3', 'test key'); 4 | insert into T_pkey1 values (2, 'key2-1', 'test key'); 5 | insert into T_pkey1 values (2, 'key2-2', 'test key'); 6 | insert into T_pkey1 values (2, 'key2-3', 'test key'); 7 | insert into T_pkey2 values (1, 'key1-1', 'test key'); 8 | insert into T_pkey2 values (1, 'key1-2', 'test key'); 9 | insert into T_pkey2 values (1, 'key1-3', 'test key'); 10 | insert into T_pkey2 values (2, 'key2-1', 'test key'); 11 | insert into T_pkey2 values (2, 'key2-2', 'test key'); 12 | insert into T_pkey2 values (2, 'key2-3', 'test key'); 13 | select * from T_pkey1; 14 | key1 | key2 | txt 15 | ------+--------+---------- 16 | 1 | key1-1 | test key 17 | 1 | key1-2 | test key 18 | 1 | key1-3 | test key 19 | 2 | key2-1 | test key 20 | 2 | key2-2 | test key 21 | 2 | key2-3 | test key 22 | (6 rows) 23 | 24 | select * from T_pkey2; 25 | key1 | key2 | txt 26 | ------+--------+---------- 27 | 1 | KEY1-1 | test key 28 | 1 | KEY1-2 | test key 29 | 1 | KEY1-3 | test key 30 | 2 | KEY2-1 | test key 31 | 2 | KEY2-2 | test key 32 | 2 | KEY2-3 | test key 33 | (6 rows) 34 | 35 | insert into T_pkey1 values (1, 'KEY1-3', 'should work'); 36 | insert into T_pkey2 values (1, 'KEY1-3', 'should fail'); 37 | ERROR: duplicate key '1', 'KEY1-3' for T_pkey2 38 | insert into T_dta1 values ('trec 1', 1, 'key1-1'); 39 | insert into T_dta1 values ('trec 2', 1, 'key1-2'); 40 | insert into T_dta1 values ('trec 3', 1, 'key1-3'); 41 | insert into T_dta1 values ('trec 4', 1, 'key1-4'); 42 | ERROR: key for t_dta1 not in t_pkey1 43 | insert into T_dta2 values ('trec 1', 1, 'KEY1-1'); 44 | insert into T_dta2 values ('trec 2', 1, 'KEY1-2'); 45 | insert into T_dta2 values ('trec 3', 1, 'KEY1-3'); 46 | insert into T_dta2 values ('trec 4', 1, 'KEY1-4'); 47 | ERROR: key for t_dta2 not in t_pkey2 48 | select * from T_dta1; 49 | tkey | ref1 | ref2 50 | --------+------+-------- 51 | trec 1 | 1 | key1-1 52 | trec 2 | 1 | key1-2 53 | trec 3 | 1 | key1-3 54 | (3 rows) 55 | 56 | select * from T_dta2; 57 | tkey | ref1 | ref2 58 | --------+------+-------- 59 | trec 1 | 1 | KEY1-1 60 | trec 2 | 1 | KEY1-2 61 | trec 3 | 1 | KEY1-3 62 | (3 rows) 63 | 64 | update T_pkey1 set key2 = 'key2-9' where key1 = 2 and key2 = 'key2-1'; 65 | update T_pkey1 set key2 = 'key1-9' where key1 = 1 and key2 = 'key1-1'; 66 | ERROR: key '1', 'key1-1' referenced by T_dta1 67 | delete from T_pkey1 where key1 = 2 and key2 = 'key2-2'; 68 | delete from T_pkey1 where key1 = 1 and key2 = 'key1-2'; 69 | ERROR: key '1', 'key1-2' referenced by T_dta1 70 | update T_pkey2 set key2 = 'KEY2-9' where key1 = 2 and key2 = 'KEY2-1'; 71 | update T_pkey2 set key2 = 'KEY1-9' where key1 = 1 and key2 = 'KEY1-1'; 72 | NOTICE: updated 1 entries in T_dta2 for new key in T_pkey2 73 | delete from T_pkey2 where key1 = 2 and key2 = 'KEY2-2'; 74 | delete from T_pkey2 where key1 = 1 and key2 = 'KEY1-2'; 75 | NOTICE: deleted 1 entries from T_dta2 76 | select * from T_pkey1; 77 | key1 | key2 | txt 78 | ------+--------+------------- 79 | 1 | key1-1 | test key 80 | 1 | key1-2 | test key 81 | 1 | key1-3 | test key 82 | 2 | key2-3 | test key 83 | 1 | KEY1-3 | should work 84 | 2 | key2-9 | test key 85 | (6 rows) 86 | 87 | select * from T_pkey2; 88 | key1 | key2 | txt 89 | ------+--------+---------- 90 | 1 | KEY1-3 | test key 91 | 2 | KEY2-3 | test key 92 | 2 | KEY2-9 | test key 93 | 1 | KEY1-9 | test key 94 | (4 rows) 95 | 96 | select * from T_dta1; 97 | tkey | ref1 | ref2 98 | --------+------+-------- 99 | trec 1 | 1 | key1-1 100 | trec 2 | 1 | key1-2 101 | trec 3 | 1 | key1-3 102 | (3 rows) 103 | 104 | select * from T_dta2; 105 | tkey | ref1 | ref2 106 | --------+------+-------- 107 | trec 3 | 1 | KEY1-3 108 | trec 1 | 1 | KEY1-9 109 | (2 rows) 110 | 111 | select ruby_avg(key1) from T_pkey1; 112 | ruby_avg 113 | ---------- 114 | 1 115 | (1 row) 116 | 117 | select ruby_sum(key1) from T_pkey1; 118 | ruby_sum 119 | ---------- 120 | 8 121 | (1 row) 122 | 123 | select ruby_avg(key1) from T_pkey2; 124 | ruby_avg 125 | ---------- 126 | 1 127 | (1 row) 128 | 129 | select ruby_sum(key1) from T_pkey2; 130 | ruby_sum 131 | ---------- 132 | 6 133 | (1 row) 134 | 135 | select ruby_avg(key1) from T_pkey1 where key1 = 99; 136 | ruby_avg 137 | ---------- 138 | 139 | (1 row) 140 | 141 | select ruby_sum(key1) from T_pkey1 where key1 = 99; 142 | ruby_sum 143 | ---------- 144 | 0 145 | (1 row) 146 | 147 | select 1 @< 2; 148 | ?column? 149 | ---------- 150 | t 151 | (1 row) 152 | 153 | select 100 @< 4; 154 | ?column? 155 | ---------- 156 | f 157 | (1 row) 158 | 159 | select * from T_pkey1 order by key1 using @<; 160 | key1 | key2 | txt 161 | ------+--------+------------- 162 | 1 | key1-1 | test key 163 | 1 | key1-2 | test key 164 | 1 | key1-3 | test key 165 | 1 | KEY1-3 | should work 166 | 2 | key2-3 | test key 167 | 2 | key2-9 | test key 168 | (6 rows) 169 | 170 | select * from T_pkey2 order by key1 using @<; 171 | key1 | key2 | txt 172 | ------+--------+---------- 173 | 1 | KEY1-3 | test key 174 | 1 | KEY1-9 | test key 175 | 2 | KEY2-3 | test key 176 | 2 | KEY2-9 | test key 177 | (4 rows) 178 | 179 | -------------------------------------------------------------------------------- /test/plt/test.expected.80: -------------------------------------------------------------------------------- 1 | insert into T_pkey1 values (1, 'key1-1', 'test key'); 2 | insert into T_pkey1 values (1, 'key1-2', 'test key'); 3 | insert into T_pkey1 values (1, 'key1-3', 'test key'); 4 | insert into T_pkey1 values (2, 'key2-1', 'test key'); 5 | insert into T_pkey1 values (2, 'key2-2', 'test key'); 6 | insert into T_pkey1 values (2, 'key2-3', 'test key'); 7 | insert into T_pkey2 values (1, 'key1-1', 'test key'); 8 | insert into T_pkey2 values (1, 'key1-2', 'test key'); 9 | insert into T_pkey2 values (1, 'key1-3', 'test key'); 10 | insert into T_pkey2 values (2, 'key2-1', 'test key'); 11 | insert into T_pkey2 values (2, 'key2-2', 'test key'); 12 | insert into T_pkey2 values (2, 'key2-3', 'test key'); 13 | select * from T_pkey1; 14 | key1 | key2 | txt 15 | ------+--------+---------- 16 | 1 | key1-1 | test key 17 | 1 | key1-2 | test key 18 | 1 | key1-3 | test key 19 | 2 | key2-1 | test key 20 | 2 | key2-2 | test key 21 | 2 | key2-3 | test key 22 | (6 rows) 23 | 24 | select * from T_pkey2; 25 | key1 | key2 | txt 26 | ------+--------+---------- 27 | 1 | KEY1-1 | test key 28 | 1 | KEY1-2 | test key 29 | 1 | KEY1-3 | test key 30 | 2 | KEY2-1 | test key 31 | 2 | KEY2-2 | test key 32 | 2 | KEY2-3 | test key 33 | (6 rows) 34 | 35 | insert into T_pkey1 values (1, 'KEY1-3', 'should work'); 36 | insert into T_pkey2 values (1, 'KEY1-3', 'should fail'); 37 | ERROR: duplicate key '1', 'KEY1-3' for T_pkey2 38 | insert into T_dta1 values ('trec 1', 1, 'key1-1'); 39 | insert into T_dta1 values ('trec 2', 1, 'key1-2'); 40 | insert into T_dta1 values ('trec 3', 1, 'key1-3'); 41 | insert into T_dta1 values ('trec 4', 1, 'key1-4'); 42 | ERROR: key for t_dta1 not in t_pkey1 43 | insert into T_dta2 values ('trec 1', 1, 'KEY1-1'); 44 | insert into T_dta2 values ('trec 2', 1, 'KEY1-2'); 45 | insert into T_dta2 values ('trec 3', 1, 'KEY1-3'); 46 | insert into T_dta2 values ('trec 4', 1, 'KEY1-4'); 47 | ERROR: key for t_dta2 not in t_pkey2 48 | select * from T_dta1; 49 | tkey | ref1 | ref2 50 | --------+------+-------- 51 | trec 1 | 1 | key1-1 52 | trec 2 | 1 | key1-2 53 | trec 3 | 1 | key1-3 54 | (3 rows) 55 | 56 | select * from T_dta2; 57 | tkey | ref1 | ref2 58 | --------+------+-------- 59 | trec 1 | 1 | KEY1-1 60 | trec 2 | 1 | KEY1-2 61 | trec 3 | 1 | KEY1-3 62 | (3 rows) 63 | 64 | update T_pkey1 set key2 = 'key2-9' where key1 = 2 and key2 = 'key2-1'; 65 | update T_pkey1 set key2 = 'key1-9' where key1 = 1 and key2 = 'key1-1'; 66 | ERROR: key '1', 'key1-1' referenced by T_dta1 67 | delete from T_pkey1 where key1 = 2 and key2 = 'key2-2'; 68 | delete from T_pkey1 where key1 = 1 and key2 = 'key1-2'; 69 | ERROR: key '1', 'key1-2' referenced by T_dta1 70 | update T_pkey2 set key2 = 'KEY2-9' where key1 = 2 and key2 = 'KEY2-1'; 71 | update T_pkey2 set key2 = 'KEY1-9' where key1 = 1 and key2 = 'KEY1-1'; 72 | NOTICE: updated 1 entries in T_dta2 for new key in T_pkey2 73 | delete from T_pkey2 where key1 = 2 and key2 = 'KEY2-2'; 74 | delete from T_pkey2 where key1 = 1 and key2 = 'KEY1-2'; 75 | NOTICE: deleted 1 entries from T_dta2 76 | select * from T_pkey1; 77 | key1 | key2 | txt 78 | ------+--------+------------- 79 | 1 | key1-1 | test key 80 | 1 | key1-2 | test key 81 | 1 | key1-3 | test key 82 | 2 | key2-3 | test key 83 | 1 | KEY1-3 | should work 84 | 2 | key2-9 | test key 85 | (6 rows) 86 | 87 | select * from T_pkey2; 88 | key1 | key2 | txt 89 | ------+--------+---------- 90 | 1 | KEY1-3 | test key 91 | 2 | KEY2-3 | test key 92 | 2 | KEY2-9 | test key 93 | 1 | KEY1-9 | test key 94 | (4 rows) 95 | 96 | select * from T_dta1; 97 | tkey | ref1 | ref2 98 | --------+------+-------- 99 | trec 1 | 1 | key1-1 100 | trec 2 | 1 | key1-2 101 | trec 3 | 1 | key1-3 102 | (3 rows) 103 | 104 | select * from T_dta2; 105 | tkey | ref1 | ref2 106 | --------+------+-------- 107 | trec 3 | 1 | KEY1-3 108 | trec 1 | 1 | KEY1-9 109 | (2 rows) 110 | 111 | select ruby_avg(key1) from T_pkey1; 112 | ruby_avg 113 | ---------- 114 | 1 115 | (1 row) 116 | 117 | select ruby_sum(key1) from T_pkey1; 118 | ruby_sum 119 | ---------- 120 | 8 121 | (1 row) 122 | 123 | select ruby_avg(key1) from T_pkey2; 124 | ruby_avg 125 | ---------- 126 | 1 127 | (1 row) 128 | 129 | select ruby_sum(key1) from T_pkey2; 130 | ruby_sum 131 | ---------- 132 | 6 133 | (1 row) 134 | 135 | select ruby_avg(key1) from T_pkey1 where key1 = 99; 136 | ruby_avg 137 | ---------- 138 | 139 | (1 row) 140 | 141 | select ruby_sum(key1) from T_pkey1 where key1 = 99; 142 | ruby_sum 143 | ---------- 144 | 0 145 | (1 row) 146 | 147 | select 1 @< 2; 148 | ?column? 149 | ---------- 150 | t 151 | (1 row) 152 | 153 | select 100 @< 4; 154 | ?column? 155 | ---------- 156 | f 157 | (1 row) 158 | 159 | select * from T_pkey1 order by key1 using @<; 160 | key1 | key2 | txt 161 | ------+--------+------------- 162 | 1 | key1-1 | test key 163 | 1 | key1-2 | test key 164 | 1 | key1-3 | test key 165 | 1 | KEY1-3 | should work 166 | 2 | key2-3 | test key 167 | 2 | key2-9 | test key 168 | (6 rows) 169 | 170 | select * from T_pkey2 order by key1 using @<; 171 | key1 | key2 | txt 172 | ------+--------+---------- 173 | 1 | KEY1-3 | test key 174 | 1 | KEY1-9 | test key 175 | 2 | KEY2-3 | test key 176 | 2 | KEY2-9 | test key 177 | (4 rows) 178 | 179 | -------------------------------------------------------------------------------- /test/plt/test.expected.81: -------------------------------------------------------------------------------- 1 | insert into T_pkey1 values (1, 'key1-1', 'test key'); 2 | insert into T_pkey1 values (1, 'key1-2', 'test key'); 3 | insert into T_pkey1 values (1, 'key1-3', 'test key'); 4 | insert into T_pkey1 values (2, 'key2-1', 'test key'); 5 | insert into T_pkey1 values (2, 'key2-2', 'test key'); 6 | insert into T_pkey1 values (2, 'key2-3', 'test key'); 7 | insert into T_pkey2 values (1, 'key1-1', 'test key'); 8 | insert into T_pkey2 values (1, 'key1-2', 'test key'); 9 | insert into T_pkey2 values (1, 'key1-3', 'test key'); 10 | insert into T_pkey2 values (2, 'key2-1', 'test key'); 11 | insert into T_pkey2 values (2, 'key2-2', 'test key'); 12 | insert into T_pkey2 values (2, 'key2-3', 'test key'); 13 | select * from T_pkey1; 14 | key1 | key2 | txt 15 | ------+--------+---------- 16 | 1 | key1-1 | test key 17 | 1 | key1-2 | test key 18 | 1 | key1-3 | test key 19 | 2 | key2-1 | test key 20 | 2 | key2-2 | test key 21 | 2 | key2-3 | test key 22 | (6 rows) 23 | 24 | select * from T_pkey2; 25 | key1 | key2 | txt 26 | ------+--------+---------- 27 | 1 | KEY1-1 | test key 28 | 1 | KEY1-2 | test key 29 | 1 | KEY1-3 | test key 30 | 2 | KEY2-1 | test key 31 | 2 | KEY2-2 | test key 32 | 2 | KEY2-3 | test key 33 | (6 rows) 34 | 35 | insert into T_pkey1 values (1, 'KEY1-3', 'should work'); 36 | insert into T_pkey2 values (1, 'KEY1-3', 'should fail'); 37 | ERROR: duplicate key '1', 'KEY1-3' for T_pkey2 38 | insert into T_dta1 values ('trec 1', 1, 'key1-1'); 39 | insert into T_dta1 values ('trec 2', 1, 'key1-2'); 40 | insert into T_dta1 values ('trec 3', 1, 'key1-3'); 41 | insert into T_dta1 values ('trec 4', 1, 'key1-4'); 42 | ERROR: key for t_dta1 not in t_pkey1 43 | insert into T_dta2 values ('trec 1', 1, 'KEY1-1'); 44 | insert into T_dta2 values ('trec 2', 1, 'KEY1-2'); 45 | insert into T_dta2 values ('trec 3', 1, 'KEY1-3'); 46 | insert into T_dta2 values ('trec 4', 1, 'KEY1-4'); 47 | ERROR: key for t_dta2 not in t_pkey2 48 | select * from T_dta1; 49 | tkey | ref1 | ref2 50 | --------+------+-------- 51 | trec 1 | 1 | key1-1 52 | trec 2 | 1 | key1-2 53 | trec 3 | 1 | key1-3 54 | (3 rows) 55 | 56 | select * from T_dta2; 57 | tkey | ref1 | ref2 58 | --------+------+-------- 59 | trec 1 | 1 | KEY1-1 60 | trec 2 | 1 | KEY1-2 61 | trec 3 | 1 | KEY1-3 62 | (3 rows) 63 | 64 | update T_pkey1 set key2 = 'key2-9' where key1 = 2 and key2 = 'key2-1'; 65 | update T_pkey1 set key2 = 'key1-9' where key1 = 1 and key2 = 'key1-1'; 66 | ERROR: key '1', 'key1-1' referenced by T_dta1 67 | delete from T_pkey1 where key1 = 2 and key2 = 'key2-2'; 68 | delete from T_pkey1 where key1 = 1 and key2 = 'key1-2'; 69 | ERROR: key '1', 'key1-2' referenced by T_dta1 70 | update T_pkey2 set key2 = 'KEY2-9' where key1 = 2 and key2 = 'KEY2-1'; 71 | update T_pkey2 set key2 = 'KEY1-9' where key1 = 1 and key2 = 'KEY1-1'; 72 | NOTICE: updated 1 entries in T_dta2 for new key in T_pkey2 73 | delete from T_pkey2 where key1 = 2 and key2 = 'KEY2-2'; 74 | delete from T_pkey2 where key1 = 1 and key2 = 'KEY1-2'; 75 | NOTICE: deleted 1 entries from T_dta2 76 | select * from T_pkey1; 77 | key1 | key2 | txt 78 | ------+--------+------------- 79 | 1 | key1-1 | test key 80 | 1 | key1-2 | test key 81 | 1 | key1-3 | test key 82 | 2 | key2-3 | test key 83 | 1 | KEY1-3 | should work 84 | 2 | key2-9 | test key 85 | (6 rows) 86 | 87 | select * from T_pkey2; 88 | key1 | key2 | txt 89 | ------+--------+---------- 90 | 1 | KEY1-3 | test key 91 | 2 | KEY2-3 | test key 92 | 2 | KEY2-9 | test key 93 | 1 | KEY1-9 | test key 94 | (4 rows) 95 | 96 | select * from T_dta1; 97 | tkey | ref1 | ref2 98 | --------+------+-------- 99 | trec 1 | 1 | key1-1 100 | trec 2 | 1 | key1-2 101 | trec 3 | 1 | key1-3 102 | (3 rows) 103 | 104 | select * from T_dta2; 105 | tkey | ref1 | ref2 106 | --------+------+-------- 107 | trec 3 | 1 | KEY1-3 108 | trec 1 | 1 | KEY1-9 109 | (2 rows) 110 | 111 | select ruby_avg(key1) from T_pkey1; 112 | ruby_avg 113 | ---------- 114 | 1 115 | (1 row) 116 | 117 | select ruby_sum(key1) from T_pkey1; 118 | ruby_sum 119 | ---------- 120 | 8 121 | (1 row) 122 | 123 | select ruby_avg(key1) from T_pkey2; 124 | ruby_avg 125 | ---------- 126 | 1 127 | (1 row) 128 | 129 | select ruby_sum(key1) from T_pkey2; 130 | ruby_sum 131 | ---------- 132 | 6 133 | (1 row) 134 | 135 | select ruby_avg(key1) from T_pkey1 where key1 = 99; 136 | ruby_avg 137 | ---------- 138 | 139 | (1 row) 140 | 141 | select ruby_sum(key1) from T_pkey1 where key1 = 99; 142 | ruby_sum 143 | ---------- 144 | 0 145 | (1 row) 146 | 147 | select 1 @< 2; 148 | ?column? 149 | ---------- 150 | t 151 | (1 row) 152 | 153 | select 100 @< 4; 154 | ?column? 155 | ---------- 156 | f 157 | (1 row) 158 | 159 | select * from T_pkey1 order by key1 using @<; 160 | key1 | key2 | txt 161 | ------+--------+------------- 162 | 1 | key1-1 | test key 163 | 1 | key1-2 | test key 164 | 1 | key1-3 | test key 165 | 1 | KEY1-3 | should work 166 | 2 | key2-3 | test key 167 | 2 | key2-9 | test key 168 | (6 rows) 169 | 170 | select * from T_pkey2 order by key1 using @<; 171 | key1 | key2 | txt 172 | ------+--------+---------- 173 | 1 | KEY1-3 | test key 174 | 1 | KEY1-9 | test key 175 | 2 | KEY2-3 | test key 176 | 2 | KEY2-9 | test key 177 | (4 rows) 178 | 179 | -------------------------------------------------------------------------------- /test/plt/test.expected.82: -------------------------------------------------------------------------------- 1 | insert into T_pkey1 values (1, 'key1-1', 'test key'); 2 | insert into T_pkey1 values (1, 'key1-2', 'test key'); 3 | insert into T_pkey1 values (1, 'key1-3', 'test key'); 4 | insert into T_pkey1 values (2, 'key2-1', 'test key'); 5 | insert into T_pkey1 values (2, 'key2-2', 'test key'); 6 | insert into T_pkey1 values (2, 'key2-3', 'test key'); 7 | insert into T_pkey2 values (1, 'key1-1', 'test key'); 8 | insert into T_pkey2 values (1, 'key1-2', 'test key'); 9 | insert into T_pkey2 values (1, 'key1-3', 'test key'); 10 | insert into T_pkey2 values (2, 'key2-1', 'test key'); 11 | insert into T_pkey2 values (2, 'key2-2', 'test key'); 12 | insert into T_pkey2 values (2, 'key2-3', 'test key'); 13 | select * from T_pkey1; 14 | key1 | key2 | txt 15 | ------+--------+---------- 16 | 1 | key1-1 | test key 17 | 1 | key1-2 | test key 18 | 1 | key1-3 | test key 19 | 2 | key2-1 | test key 20 | 2 | key2-2 | test key 21 | 2 | key2-3 | test key 22 | (6 rows) 23 | 24 | select * from T_pkey2; 25 | key1 | key2 | txt 26 | ------+--------+---------- 27 | 1 | KEY1-1 | test key 28 | 1 | KEY1-2 | test key 29 | 1 | KEY1-3 | test key 30 | 2 | KEY2-1 | test key 31 | 2 | KEY2-2 | test key 32 | 2 | KEY2-3 | test key 33 | (6 rows) 34 | 35 | insert into T_pkey1 values (1, 'KEY1-3', 'should work'); 36 | insert into T_pkey2 values (1, 'KEY1-3', 'should fail'); 37 | ERROR: duplicate key '1', 'KEY1-3' for T_pkey2 38 | insert into T_dta1 values ('trec 1', 1, 'key1-1'); 39 | insert into T_dta1 values ('trec 2', 1, 'key1-2'); 40 | insert into T_dta1 values ('trec 3', 1, 'key1-3'); 41 | insert into T_dta1 values ('trec 4', 1, 'key1-4'); 42 | ERROR: key for t_dta1 not in t_pkey1 43 | insert into T_dta2 values ('trec 1', 1, 'KEY1-1'); 44 | insert into T_dta2 values ('trec 2', 1, 'KEY1-2'); 45 | insert into T_dta2 values ('trec 3', 1, 'KEY1-3'); 46 | insert into T_dta2 values ('trec 4', 1, 'KEY1-4'); 47 | ERROR: key for t_dta2 not in t_pkey2 48 | select * from T_dta1; 49 | tkey | ref1 | ref2 50 | --------+------+-------- 51 | trec 1 | 1 | key1-1 52 | trec 2 | 1 | key1-2 53 | trec 3 | 1 | key1-3 54 | (3 rows) 55 | 56 | select * from T_dta2; 57 | tkey | ref1 | ref2 58 | --------+------+-------- 59 | trec 1 | 1 | KEY1-1 60 | trec 2 | 1 | KEY1-2 61 | trec 3 | 1 | KEY1-3 62 | (3 rows) 63 | 64 | update T_pkey1 set key2 = 'key2-9' where key1 = 2 and key2 = 'key2-1'; 65 | update T_pkey1 set key2 = 'key1-9' where key1 = 1 and key2 = 'key1-1'; 66 | ERROR: key '1', 'key1-1' referenced by T_dta1 67 | delete from T_pkey1 where key1 = 2 and key2 = 'key2-2'; 68 | delete from T_pkey1 where key1 = 1 and key2 = 'key1-2'; 69 | ERROR: key '1', 'key1-2' referenced by T_dta1 70 | update T_pkey2 set key2 = 'KEY2-9' where key1 = 2 and key2 = 'KEY2-1'; 71 | update T_pkey2 set key2 = 'KEY1-9' where key1 = 1 and key2 = 'KEY1-1'; 72 | NOTICE: updated 1 entries in T_dta2 for new key in T_pkey2 73 | delete from T_pkey2 where key1 = 2 and key2 = 'KEY2-2'; 74 | delete from T_pkey2 where key1 = 1 and key2 = 'KEY1-2'; 75 | NOTICE: deleted 1 entries from T_dta2 76 | select * from T_pkey1; 77 | key1 | key2 | txt 78 | ------+--------+------------- 79 | 1 | key1-1 | test key 80 | 1 | key1-2 | test key 81 | 1 | key1-3 | test key 82 | 2 | key2-3 | test key 83 | 1 | KEY1-3 | should work 84 | 2 | key2-9 | test key 85 | (6 rows) 86 | 87 | select * from T_pkey2; 88 | key1 | key2 | txt 89 | ------+--------+---------- 90 | 1 | KEY1-3 | test key 91 | 2 | KEY2-3 | test key 92 | 2 | KEY2-9 | test key 93 | 1 | KEY1-9 | test key 94 | (4 rows) 95 | 96 | select * from T_dta1; 97 | tkey | ref1 | ref2 98 | --------+------+-------- 99 | trec 1 | 1 | key1-1 100 | trec 2 | 1 | key1-2 101 | trec 3 | 1 | key1-3 102 | (3 rows) 103 | 104 | select * from T_dta2; 105 | tkey | ref1 | ref2 106 | --------+------+-------- 107 | trec 3 | 1 | KEY1-3 108 | trec 1 | 1 | KEY1-9 109 | (2 rows) 110 | 111 | select ruby_avg(key1) from T_pkey1; 112 | ruby_avg 113 | ---------- 114 | 1 115 | (1 row) 116 | 117 | select ruby_sum(key1) from T_pkey1; 118 | ruby_sum 119 | ---------- 120 | 8 121 | (1 row) 122 | 123 | select ruby_avg(key1) from T_pkey2; 124 | ruby_avg 125 | ---------- 126 | 1 127 | (1 row) 128 | 129 | select ruby_sum(key1) from T_pkey2; 130 | ruby_sum 131 | ---------- 132 | 6 133 | (1 row) 134 | 135 | select ruby_avg(key1) from T_pkey1 where key1 = 99; 136 | ruby_avg 137 | ---------- 138 | 139 | (1 row) 140 | 141 | select ruby_sum(key1) from T_pkey1 where key1 = 99; 142 | ruby_sum 143 | ---------- 144 | 0 145 | (1 row) 146 | 147 | select 1 @< 2; 148 | ?column? 149 | ---------- 150 | t 151 | (1 row) 152 | 153 | select 100 @< 4; 154 | ?column? 155 | ---------- 156 | f 157 | (1 row) 158 | 159 | select * from T_pkey1 order by key1 using @<; 160 | key1 | key2 | txt 161 | ------+--------+------------- 162 | 1 | key1-1 | test key 163 | 1 | key1-2 | test key 164 | 1 | key1-3 | test key 165 | 1 | KEY1-3 | should work 166 | 2 | key2-3 | test key 167 | 2 | key2-9 | test key 168 | (6 rows) 169 | 170 | select * from T_pkey2 order by key1 using @<; 171 | key1 | key2 | txt 172 | ------+--------+---------- 173 | 1 | KEY1-3 | test key 174 | 1 | KEY1-9 | test key 175 | 2 | KEY2-3 | test key 176 | 2 | KEY2-9 | test key 177 | (4 rows) 178 | 179 | -------------------------------------------------------------------------------- /test/plt/test.expected.84: -------------------------------------------------------------------------------- 1 | insert into T_pkey1 values (1, 'key1-1', 'test key'); 2 | insert into T_pkey1 values (1, 'key1-2', 'test key'); 3 | insert into T_pkey1 values (1, 'key1-3', 'test key'); 4 | insert into T_pkey1 values (2, 'key2-1', 'test key'); 5 | insert into T_pkey1 values (2, 'key2-2', 'test key'); 6 | insert into T_pkey1 values (2, 'key2-3', 'test key'); 7 | insert into T_pkey2 values (1, 'key1-1', 'test key'); 8 | insert into T_pkey2 values (1, 'key1-2', 'test key'); 9 | insert into T_pkey2 values (1, 'key1-3', 'test key'); 10 | insert into T_pkey2 values (2, 'key2-1', 'test key'); 11 | insert into T_pkey2 values (2, 'key2-2', 'test key'); 12 | insert into T_pkey2 values (2, 'key2-3', 'test key'); 13 | select * from T_pkey1; 14 | key1 | key2 | txt 15 | ------+--------+---------- 16 | 1 | key1-1 | test key 17 | 1 | key1-2 | test key 18 | 1 | key1-3 | test key 19 | 2 | key2-1 | test key 20 | 2 | key2-2 | test key 21 | 2 | key2-3 | test key 22 | (6 rows) 23 | 24 | select * from T_pkey2; 25 | key1 | key2 | txt 26 | ------+--------+---------- 27 | 1 | KEY1-1 | test key 28 | 1 | KEY1-2 | test key 29 | 1 | KEY1-3 | test key 30 | 2 | KEY2-1 | test key 31 | 2 | KEY2-2 | test key 32 | 2 | KEY2-3 | test key 33 | (6 rows) 34 | 35 | insert into T_pkey1 values (1, 'KEY1-3', 'should work'); 36 | insert into T_pkey2 values (1, 'KEY1-3', 'should fail'); 37 | ERROR: duplicate key '1', 'KEY1-3' for T_pkey2 38 | insert into T_dta1 values ('trec 1', 1, 'key1-1'); 39 | insert into T_dta1 values ('trec 2', 1, 'key1-2'); 40 | insert into T_dta1 values ('trec 3', 1, 'key1-3'); 41 | insert into T_dta1 values ('trec 4', 1, 'key1-4'); 42 | ERROR: key for t_dta1 not in t_pkey1 43 | insert into T_dta2 values ('trec 1', 1, 'KEY1-1'); 44 | insert into T_dta2 values ('trec 2', 1, 'KEY1-2'); 45 | insert into T_dta2 values ('trec 3', 1, 'KEY1-3'); 46 | insert into T_dta2 values ('trec 4', 1, 'KEY1-4'); 47 | ERROR: key for t_dta2 not in t_pkey2 48 | select * from T_dta1; 49 | tkey | ref1 | ref2 50 | --------+------+-------- 51 | trec 1 | 1 | key1-1 52 | trec 2 | 1 | key1-2 53 | trec 3 | 1 | key1-3 54 | (3 rows) 55 | 56 | select * from T_dta2; 57 | tkey | ref1 | ref2 58 | --------+------+-------- 59 | trec 1 | 1 | KEY1-1 60 | trec 2 | 1 | KEY1-2 61 | trec 3 | 1 | KEY1-3 62 | (3 rows) 63 | 64 | update T_pkey1 set key2 = 'key2-9' where key1 = 2 and key2 = 'key2-1'; 65 | update T_pkey1 set key2 = 'key1-9' where key1 = 1 and key2 = 'key1-1'; 66 | ERROR: key '1', 'key1-1' referenced by T_dta1 67 | delete from T_pkey1 where key1 = 2 and key2 = 'key2-2'; 68 | delete from T_pkey1 where key1 = 1 and key2 = 'key1-2'; 69 | ERROR: key '1', 'key1-2' referenced by T_dta1 70 | update T_pkey2 set key2 = 'KEY2-9' where key1 = 2 and key2 = 'KEY2-1'; 71 | update T_pkey2 set key2 = 'KEY1-9' where key1 = 1 and key2 = 'KEY1-1'; 72 | NOTICE: updated 1 entries in T_dta2 for new key in T_pkey2 73 | delete from T_pkey2 where key1 = 2 and key2 = 'KEY2-2'; 74 | delete from T_pkey2 where key1 = 1 and key2 = 'KEY1-2'; 75 | NOTICE: deleted 1 entries from T_dta2 76 | select * from T_pkey1; 77 | key1 | key2 | txt 78 | ------+--------+------------- 79 | 1 | key1-1 | test key 80 | 1 | key1-2 | test key 81 | 1 | key1-3 | test key 82 | 2 | key2-3 | test key 83 | 1 | KEY1-3 | should work 84 | 2 | key2-9 | test key 85 | (6 rows) 86 | 87 | select * from T_pkey2; 88 | key1 | key2 | txt 89 | ------+--------+---------- 90 | 1 | KEY1-3 | test key 91 | 2 | KEY2-3 | test key 92 | 2 | KEY2-9 | test key 93 | 1 | KEY1-9 | test key 94 | (4 rows) 95 | 96 | select * from T_dta1; 97 | tkey | ref1 | ref2 98 | --------+------+-------- 99 | trec 1 | 1 | key1-1 100 | trec 2 | 1 | key1-2 101 | trec 3 | 1 | key1-3 102 | (3 rows) 103 | 104 | select * from T_dta2; 105 | tkey | ref1 | ref2 106 | --------+------+-------- 107 | trec 3 | 1 | KEY1-3 108 | trec 1 | 1 | KEY1-9 109 | (2 rows) 110 | 111 | select ruby_avg(key1) from T_pkey1; 112 | ruby_avg 113 | ---------- 114 | 1 115 | (1 row) 116 | 117 | select ruby_sum(key1) from T_pkey1; 118 | ruby_sum 119 | ---------- 120 | 8 121 | (1 row) 122 | 123 | select ruby_avg(key1) from T_pkey2; 124 | ruby_avg 125 | ---------- 126 | 1 127 | (1 row) 128 | 129 | select ruby_sum(key1) from T_pkey2; 130 | ruby_sum 131 | ---------- 132 | 6 133 | (1 row) 134 | 135 | select ruby_avg(key1) from T_pkey1 where key1 = 99; 136 | ruby_avg 137 | ---------- 138 | 139 | (1 row) 140 | 141 | select ruby_sum(key1) from T_pkey1 where key1 = 99; 142 | ruby_sum 143 | ---------- 144 | 0 145 | (1 row) 146 | 147 | select 1 @< 2; 148 | ?column? 149 | ---------- 150 | t 151 | (1 row) 152 | 153 | select 100 @< 4; 154 | ?column? 155 | ---------- 156 | f 157 | (1 row) 158 | 159 | select * from T_pkey1 order by key1 using @<; 160 | ERROR: operator @< is not a valid ordering operator 161 | LINE 1: select * from T_pkey1 order by key1 using @<; 162 | ^ 163 | HINT: Ordering operators must be "<" or ">" members of btree operator families. 164 | select * from T_pkey2 order by key1 using @<; 165 | ERROR: operator @< is not a valid ordering operator 166 | LINE 1: select * from T_pkey2 order by key1 using @<; 167 | ^ 168 | HINT: Ordering operators must be "<" or ">" members of btree operator families. 169 | -------------------------------------------------------------------------------- /test/conv_geometry/test_queries.sql.in: -------------------------------------------------------------------------------- 1 | set client_min_messages = 'WARNING'; 2 | 3 | create table pl_box ( 4 | data box, barea float, boverlaps bool, boverleft bool, boverright bool, 5 | bleft bool, bright bool 6 | ); 7 | 8 | create or replace function box_val(box[]) returns setof pl_box as ' 9 | b1 = Box.new(2.5,2.5,1.0,1.0) 10 | b2 = Box.new(2.0,2.0,2.5,2.5) 11 | b3 = Box.new(3.0,3.0,5.0,5.0) 12 | args[0].each do |b| 13 | yield [b, b.area, b.overlap?(b1), b.overleft?(b2), 14 | b.overright?(b2), b.left?(b3), b3.right?(b)] 15 | end 16 | ' language 'plruby'; 17 | 18 | select * from 19 | box_val('{(2.0,2.0,0.0,0.0);(1.0,1.0,3.0,3.0);(2.5,2.5,2.5,3.5);(3.0,3.0,3.0,3.0)}'::box[]); 20 | 21 | drop table pl_box cascade; 22 | 23 | create table pl_box ( 24 | b box, bcmp1 int, bcmp2 int, bin bool, bcontain bool, bcenter point 25 | ); 26 | 27 | create or replace function box_val(box[]) returns setof pl_box as ' 28 | b1 = Box.new(3.0,3.0,5.0,5.0) 29 | b2 = Box.new(3.5,3.0,4.5,3.0) 30 | b3 = Box.new(0,0,3,3) 31 | args[0].each do |b| 32 | yield [b, b <=> b1, b <=> b2, b.in?(b3), b.contain?(b3), b.center] 33 | end 34 | ' language 'plruby'; 35 | 36 | select * from 37 | box_val('{(2.0,2.0,0.0,0.0);(1.0,1.0,3.0,3.0);(2.5,2.5,2.5,3.5);(3.0,3.0,3.0,3.0)}'::box[]); 38 | 39 | drop table pl_box cascade; 40 | 41 | create table pl_box ( 42 | b0 box, b1 box, b2 box, b3 box 43 | ); 44 | 45 | create or replace function box_val(box[]) returns setof pl_box as ' 46 | p0 = [Point.new(-10.0,0.0), Point.new(-3.0,4.0), 47 | Point.new(5.1, 34.5), Point.new(-5.0,-12.0)] 48 | args[0].each do |b| 49 | p0.each do |p| 50 | yield [b + p, b - p, b * p, b / p] 51 | end 52 | p0.each do |p| 53 | yield [p + b, p - b, p * b, p / b] 54 | end 55 | end 56 | ' language 'plruby'; 57 | 58 | select * from 59 | box_val('{(2.0,2.0,0.0,0.0);(1.0,1.0,3.0,3.0);(2.5,2.5,2.5,3.5);(3.0,3.0,3.0,3.0)}'::box[]); 60 | 61 | drop table pl_box cascade; 62 | 63 | create table pl_box ( 64 | p point, dp float, d0 float, c circle, w float, h float 65 | ); 66 | 67 | create or replace function box_val(box[]) returns setof pl_box as ' 68 | p = Point.new(6,4) 69 | args[0].each do |b| 70 | p0 = b.center 71 | yield [p0, Geometry::distance(p, b),Geometry::distance(p, p0), 72 | b.to_circle, b.height, b.width] 73 | end 74 | ' language 'plruby'; 75 | 76 | select * from 77 | box_val('{(2.0,2.0,0.0,0.0);(1.0,1.0,3.0,3.0);(2.5,2.5,2.5,3.5);(3.0,3.0,3.0,3.0)}'::box[]); 78 | 79 | drop table pl_box cascade; 80 | 81 | create table pl_box ( 82 | nb int, ll float, pc bool 83 | ); 84 | 85 | create or replace function path_val(path[]) returns setof pl_box as ' 86 | args[0].each do |b| 87 | yield [b.npoints, b.length, b.closed?] 88 | end 89 | ' language 'plruby'; 90 | 91 | select * from 92 | path_val('{"[1,2,3,4]","(0,0),(3,0),(4,5),(1,6)","11,12,13,14,25,12"}'::path[]); 93 | 94 | drop table pl_box cascade; 95 | 96 | create table pl_box ( 97 | p path, p0 path, p1 path 98 | ); 99 | 100 | create or replace function path_val(path[]) returns setof pl_box as ' 101 | p0 = Point.new(6,7) 102 | args[0].each do |b| 103 | yield [b, b + p0, b - p0] 104 | end 105 | ' language 'plruby'; 106 | 107 | select * from 108 | path_val('{"[1,2,3,4]","(0,0),(3,0),(4,5),(1,6)","11,12,13,14,25,12"}'::path[]); 109 | 110 | drop table pl_box cascade; 111 | 112 | create table pl_box ( 113 | p path, p0 path, p1 path 114 | ); 115 | 116 | create or replace function path_val(path[]) returns setof pl_box as ' 117 | p0 = Point.new(6,7) 118 | args[0].each do |b| 119 | yield [b, b * p0, b / p0] 120 | end 121 | ' language 'plruby'; 122 | 123 | select * from 124 | path_val('{"[1,2,3,4]","(0,0),(3,0),(4,5),(1,6)","11,12,13,14,25,12"}'::path[]); 125 | 126 | drop table pl_box cascade; 127 | 128 | create table pl_box ( 129 | center point, area float, radius float, diameter float 130 | ); 131 | 132 | create or replace function circle_val(circle[]) returns setof pl_box as ' 133 | args[0].each do |b| 134 | yield b.center, b.area, b.radius, b.diameter 135 | end 136 | ' language 'plruby'; 137 | 138 | select * from 139 | circle_val('{"<(5,1),3>","<(1,2),100>","<(100,200),10>","<(100,1),115>","1,3,5"}'::circle[]); 140 | 141 | 142 | drop table pl_box cascade; 143 | 144 | create table pl_box ( 145 | p circle, p0 circle, p1 circle 146 | ); 147 | 148 | create or replace function circle_val(circle[]) returns setof pl_box as ' 149 | p0 = Point.new(6,7) 150 | args[0].each do |b| 151 | yield [b, b + p0, b - p0] 152 | end 153 | ' language 'plruby'; 154 | 155 | select * from 156 | circle_val('{"<(5,1),3>","<(1,2),100>","<(100,200),10>","<(100,1),115>","1,3,5"}'::circle[]); 157 | 158 | drop table pl_box cascade; 159 | 160 | create table pl_box ( 161 | p circle, p0 circle, p1 circle 162 | ); 163 | 164 | create or replace function circle_val(circle[]) returns setof pl_box as ' 165 | p0 = Point.new(6,7) 166 | args[0].each do |b| 167 | yield [b, p0 * b, p0 / b ] 168 | end 169 | ' language 'plruby'; 170 | 171 | select * from 172 | circle_val('{"<(5,1),3>","<(1,2),100>","<(100,200),10>","<(100,1),115>","1,3,5"}'::circle[]); 173 | 174 | drop table pl_box cascade; 175 | 176 | create table pl_box ( 177 | overlap bool, overleft bool, overright bool, 178 | bleft bool, bright bool, below bool, above bool 179 | ); 180 | 181 | create or replace function circle_val(circle, circle) returns setof pl_box as ' 182 | b0 = args[0] 183 | b1 = args[1] 184 | yield b0.overlap?(b1), b0.overleft?(b1), b0.overright?(b1), 185 | b0.left?(b1), b0.right?(b1), b0.below?(b1), b0.above?(b1) 186 | ' language 'plruby'; 187 | 188 | 189 | select * from 190 | circle_val('<(5,1),3>'::circle, '<(1,2),100>'::circle); 191 | 192 | select * from 193 | circle_val('<(100,200),10>'::circle, '<(100,1),115>'::circle); 194 | 195 | -------------------------------------------------------------------------------- /src/conversions/geometry/geometry.sql: -------------------------------------------------------------------------------- 1 | set client_min_messages = 'WARNING'; 2 | 3 | drop table pl_box cascade; 4 | 5 | create table pl_box ( 6 | data box, barea float, boverlaps bool, boverleft bool, boverright bool, 7 | bleft bool, bright bool 8 | ); 9 | 10 | create or replace function box_val(box[]) returns setof pl_box as ' 11 | b1 = Box.new(2.5,2.5,1.0,1.0) 12 | b2 = Box.new(2.0,2.0,2.5,2.5) 13 | b3 = Box.new(3.0,3.0,5.0,5.0) 14 | args[0].each do |b| 15 | yield [b, b.area, b.overlap?(b1), b.overleft?(b2), 16 | b.overright?(b2), b.left?(b3), b3.right?(b)] 17 | end 18 | ' language 'plruby'; 19 | 20 | select * from 21 | box_val('{(2.0,2.0,0.0,0.0);(1.0,1.0,3.0,3.0);(2.5,2.5,2.5,3.5);(3.0,3.0,3.0,3.0)'::box[]); 22 | 23 | drop table pl_box cascade; 24 | 25 | create table pl_box ( 26 | b box, bcmp1 int, bcmp2 int, bin bool, bcontain bool, bcenter point 27 | ); 28 | 29 | create or replace function box_val(box[]) returns setof pl_box as ' 30 | b1 = Box.new(3.0,3.0,5.0,5.0) 31 | b2 = Box.new(3.5,3.0,4.5,3.0) 32 | b3 = Box.new(0,0,3,3) 33 | args[0].each do |b| 34 | yield [b, b <=> b1, b <=> b2, b.in?(b3), b.contain?(b3), b.center] 35 | end 36 | ' language 'plruby'; 37 | 38 | select * from 39 | box_val('{(2.0,2.0,0.0,0.0);(1.0,1.0,3.0,3.0);(2.5,2.5,2.5,3.5);(3.0,3.0,3.0,3.0)}'::box[]); 40 | 41 | drop table pl_box cascade; 42 | 43 | create table pl_box ( 44 | b0 box, b1 box, b2 box, b3 box 45 | ); 46 | 47 | create or replace function box_val(box[]) returns setof pl_box as ' 48 | p0 = [Point.new(-10.0,0.0), Point.new(-3.0,4.0), 49 | Point.new(5.1, 34.5), Point.new(-5.0,-12.0)] 50 | args[0].each do |b| 51 | p0.each do |p| 52 | yield [b + p, b - p, b * p, b / p] 53 | end 54 | p0.each do |p| 55 | yield [p + b, p - b, p * b, p / b] 56 | end 57 | end 58 | ' language 'plruby'; 59 | 60 | select * from 61 | box_val('{(2.0,2.0,0.0,0.0);(1.0,1.0,3.0,3.0);(2.5,2.5,2.5,3.5);(3.0,3.0,3.0,3.0)}'::box[]); 62 | 63 | drop table pl_box cascade; 64 | 65 | create table pl_box ( 66 | p point, dp float, d0 float, c circle, w float, h float 67 | ); 68 | 69 | create or replace function box_val(box[]) returns setof pl_box as ' 70 | p = Point.new(6,4) 71 | args[0].each do |b| 72 | p0 = b.center 73 | yield [p0, Geometry::distance(p, b),Geometry::distance(p, p0), 74 | b.to_circle, b.height, b.width] 75 | end 76 | ' language 'plruby'; 77 | 78 | select * from 79 | box_val('{(2.0,2.0,0.0,0.0);(1.0,1.0,3.0,3.0);(2.5,2.5,2.5,3.5);(3.0,3.0,3.0,3.0)}'::box[]); 80 | 81 | drop table pl_box cascade; 82 | 83 | create table pl_box ( 84 | nb int, ll float, pc bool 85 | ); 86 | 87 | create or replace function path_val(path[]) returns setof pl_box as ' 88 | args[0].each do |b| 89 | yield [b.npoints, b.length, b.closed?] 90 | end 91 | ' language 'plruby'; 92 | 93 | select * from 94 | path_val('{[1,2,3,4],(0,0),(3,0),(4,5),(1,6),11,12,13,14,25,12}'::path[]); 95 | 96 | drop table pl_box cascade; 97 | 98 | create table pl_box ( 99 | p path, p0 path, p1 path 100 | ); 101 | 102 | create or replace function path_val(path[]) returns setof pl_box as ' 103 | p0 = Point.new(6,7) 104 | args[0].each do |b| 105 | yield [b, b + p0, b - p0] 106 | end 107 | ' language 'plruby'; 108 | 109 | select * from 110 | path_val('{"[1,2,3,4]","(0,0),(3,0),(4,5),(1,6)","11,12,13,14,25,12"}'::path[]); 111 | 112 | drop table pl_box cascade; 113 | 114 | create table pl_box ( 115 | p path, p0 path, p1 path 116 | ); 117 | 118 | create or replace function path_val(path[]) returns setof pl_box as ' 119 | p0 = Point.new(6,7) 120 | args[0].each do |b| 121 | yield [b, b * p0, b / p0] 122 | end 123 | ' language 'plruby'; 124 | 125 | select * from 126 | path_val('{"[1,2,3,4]","(0,0),(3,0),(4,5),(1,6)","11,12,13,14,25,12"}'::path[]); 127 | 128 | drop table pl_box cascade; 129 | 130 | create table pl_box ( 131 | center point, area float, radius float, diameter float 132 | ); 133 | 134 | create or replace function circle_val(circle[]) returns setof pl_box as ' 135 | args[0].each do |b| 136 | yield b.center, b.area, b.radius, b.diameter 137 | end 138 | ' language 'plruby'; 139 | 140 | select * from 141 | circle_val('{"<(5,1),3>","<(1,2),100>","<(100,200),10>","<(100,1),115>","1,3,5"}'::circle[]); 142 | 143 | 144 | drop table pl_box cascade; 145 | 146 | create table pl_box ( 147 | p circle, p0 circle, p1 circle 148 | ); 149 | 150 | create or replace function circle_val(circle[]) returns setof pl_box as ' 151 | p0 = Point.new(6,7) 152 | args[0].each do |b| 153 | yield [b, b + p0, b - p0] 154 | end 155 | ' language 'plruby'; 156 | 157 | select * from 158 | circle_val('{"<(5,1),3>","<(1,2),100>","<(100,200),10>","<(100,1),115>","1,3,5"}'::circle[]); 159 | 160 | drop table pl_box cascade; 161 | 162 | create table pl_box ( 163 | p circle, p0 circle, p1 circle 164 | ); 165 | 166 | create or replace function circle_val(circle[]) returns setof pl_box as ' 167 | p0 = Point.new(6,7) 168 | args[0].each do |b| 169 | yield [b, p0 * b, p0 / b ] 170 | end 171 | ' language 'plruby'; 172 | 173 | select * from 174 | circle_val('{"<(5,1),3>","<(1,2),100>","<(100,200),10>","<(100,1),115>","1,3,5"}'::circle[]); 175 | 176 | drop table pl_box cascade; 177 | 178 | create table pl_box ( 179 | overlap bool, overleft bool, overright bool, 180 | bleft bool, bright bool, below bool, above bool 181 | ); 182 | 183 | create or replace function circle_val(circle, circle) returns setof pl_box as ' 184 | b0 = args[0] 185 | b1 = args[1] 186 | yield b0.overlap?(b1), b0.overleft?(b1), b0.overright?(b1), 187 | b0.left?(b1), b0.right?(b1), b0.below?(b1), b0.above?(b1) 188 | ' language 'plruby'; 189 | 190 | 191 | select * from 192 | circle_val('<(5,1),3>'::circle, '<(1,2),100>'::circle); 193 | 194 | select * from 195 | circle_val('<(100,200),10>'::circle, '<(100,1),115>'::circle); 196 | 197 | -------------------------------------------------------------------------------- /src/conversions/convcommon.h: -------------------------------------------------------------------------------- 1 | #include "package.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "package.h" 8 | #include 9 | #include "package.h" 10 | 11 | #define CPY_FREE(p0_, p1_, size_) do { \ 12 | void *p2_ = (void *)p1_; \ 13 | memcpy((p0_), (p2_), (size_)); \ 14 | pfree(p2_); \ 15 | } while (0) 16 | 17 | #if PG_PL_VERSION >= 74 18 | 19 | #define PL_MDUMP(name_,func_) \ 20 | static VALUE \ 21 | name_(int argc, VALUE *argv, VALUE obj) \ 22 | { \ 23 | void *mac; \ 24 | char *res; \ 25 | VALUE result; \ 26 | \ 27 | Data_Get_Struct(obj, void, mac); \ 28 | res = (char *)PLRUBY_DFC1(func_, mac); \ 29 | result = rb_tainted_str_new(VARDATA(res), VARSIZE(res)); \ 30 | pfree(res); \ 31 | return result; \ 32 | } 33 | 34 | #define PL_MLOAD(name_,func_,type_) \ 35 | static VALUE \ 36 | name_(VALUE obj, VALUE a) \ 37 | { \ 38 | StringInfoData si; \ 39 | type_ *mac0, *mac1; \ 40 | \ 41 | if (TYPE(a) != T_STRING || !RSTRING_LEN(a)) { \ 42 | rb_raise(rb_eArgError, "expected a String object"); \ 43 | } \ 44 | initStringInfo(&si); \ 45 | appendBinaryStringInfo(&si, RSTRING_PTR(a), RSTRING_LEN(a)); \ 46 | mac1 = (type_ *)PLRUBY_DFC1(func_, &si); \ 47 | pfree(si.data); \ 48 | Data_Get_Struct(obj, type_, mac0); \ 49 | CPY_FREE(mac0, mac1, sizeof(type_)); \ 50 | return obj; \ 51 | } 52 | 53 | #define PL_MLOADVAR(name_,func_,type_,size_) \ 54 | static VALUE \ 55 | name_(VALUE obj, VALUE a) \ 56 | { \ 57 | StringInfoData si; \ 58 | type_ *mac0, *mac1; \ 59 | int szl; \ 60 | \ 61 | if (TYPE(a) != T_STRING || !RSTRING_LEN(a)) { \ 62 | rb_raise(rb_eArgError, "expected a String object"); \ 63 | } \ 64 | initStringInfo(&si); \ 65 | appendBinaryStringInfo(&si, RSTRING_PTR(a), RSTRING_LEN(a)); \ 66 | mac1 = (type_ *)PLRUBY_DFC1(func_, &si); \ 67 | pfree(si.data); \ 68 | Data_Get_Struct(obj, type_, mac0); \ 69 | free(mac0); \ 70 | szl = size_(mac1); \ 71 | mac0 = (type_ *)ALLOC_N(char, szl); \ 72 | CPY_FREE(mac0, mac1, szl); \ 73 | RDATA(obj)->data = mac0; \ 74 | return obj; \ 75 | } 76 | 77 | #ifndef RUBY_CAN_USE_MARSHAL_LOAD 78 | extern VALUE plruby_s_load _((VALUE, VALUE)); 79 | #endif 80 | 81 | #else 82 | 83 | #define PL_MDUMP(name_,func_) 84 | #define PL_MLOAD(name_,func_,type_) 85 | #define PL_MLOADVAR(name_,func_,type_,size_) 86 | 87 | #endif 88 | 89 | extern VALUE plruby_to_s _((VALUE)); 90 | extern VALUE plruby_s_new _((int, VALUE *, VALUE)); 91 | extern VALUE plruby_define_void_class _((char *, char *)); 92 | #ifndef HAVE_RB_INITIALIZE_COPY 93 | extern VALUE plruby_clone _((VALUE)); 94 | #endif 95 | extern Oid plruby_datum_oid _((VALUE, int *)); 96 | extern VALUE plruby_datum_set _((VALUE, Datum)); 97 | extern VALUE plruby_datum_get _((VALUE, Oid *)); 98 | 99 | #ifndef StringValuePtr 100 | #define StringValuePtr(x) STR2CSTR(x) 101 | #endif 102 | 103 | #ifndef RSTRING_PTR 104 | # define RSTRING_PTR(x_) RSTRING(x_)->ptr 105 | # define RSTRING_LEN(x_) RSTRING(x_)->len 106 | #endif 107 | 108 | #ifndef RARRAY_PTR 109 | # define RARRAY_PTR(x_) RARRAY(x_)->ptr 110 | # define RARRAY_LEN(x_) RARRAY(x_)->len 111 | #endif 112 | 113 | #ifndef RHASH_TBL 114 | #define RHASH_TBL(x_) (RHASH(x_)->tbl) 115 | #endif 116 | 117 | #ifndef RFLOAT_VALUE 118 | #define RFLOAT_VALUE(x_) (RFLOAT(x_)->value) 119 | #endif 120 | 121 | extern Datum plruby_dfc0 _((PGFunction)); 122 | extern Datum plruby_dfc1 _((PGFunction, Datum)); 123 | extern Datum plruby_dfc2 _((PGFunction, Datum, Datum)); 124 | extern Datum plruby_dfc3 _((PGFunction, Datum, Datum, Datum)); 125 | 126 | #define PLRUBY_DFC0(a_) plruby_dfc0(a_) 127 | #define PLRUBY_DFC1(a_,b_) plruby_dfc1(a_,PointerGetDatum(b_)) 128 | #define PLRUBY_DFC2(a_,b_,c_) plruby_dfc2(a_,PointerGetDatum(b_),PointerGetDatum(c_)) 129 | #define PLRUBY_DFC3(a_,b_,c_,d_) plruby_dfc3(a_,PointerGetDatum(b_),PointerGetDatum(c_),PointerGetDatum(d_)) 130 | -------------------------------------------------------------------------------- /extconf.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | ARGV.collect! {|x| 3 | x = x.sub(/\A--((?:en|dis)able)-shared\z/) { "--#$1-plruby-shared" } 4 | } 5 | 6 | orig_argv = ARGV.dup 7 | 8 | require 'mkmf' 9 | 10 | class AX 11 | def marshal_dump 12 | "abc" 13 | end 14 | end 15 | 16 | def check_autoload(safe = 12) 17 | File.open("a.rb", "w") {|f| f.puts "class A; end" } 18 | autoload :A, "a.rb" 19 | Thread.new do 20 | begin 21 | $SAFE = safe 22 | A.new 23 | rescue Exception 24 | false 25 | end 26 | end.value 27 | ensure 28 | File.unlink("a.rb") 29 | end 30 | 31 | def check_md 32 | Marshal.load(Marshal.dump(AX.new)) 33 | false 34 | rescue 35 | true 36 | end 37 | 38 | def create_lang(version = 74, suffix = '', safe = 0) 39 | language, opaque = 'C', 'language_handler' 40 | opaque = "opaque" if version == 72 41 | safe = safe.to_i 42 | trusted = if safe >= 4 43 | "trusted" 44 | else 45 | "" 46 | end 47 | puts <<-EOT 48 | 49 | ======================================================================== 50 | After the installation use *something like this* to create the language 51 | plruby#{suffix} 52 | 53 | 54 | create function plruby#{suffix}_call_handler() returns #{opaque} 55 | as '#{Config::CONFIG["sitearchdir"]}/plruby#{suffix}.#{CONFIG["DLEXT"]}' 56 | language '#{language}'; 57 | 58 | create #{trusted} language 'plruby#{suffix}' 59 | handler plruby#{suffix}_call_handler 60 | lancompiler 'PL/Ruby#{suffix}'; 61 | 62 | ======================================================================== 63 | EOT 64 | end 65 | 66 | def rule(target, clean = nil) 67 | wr = "#{target}: 68 | \t@for subdir in $(SUBDIRS); do \\ 69 | \t\t(cd $${subdir} && $(MAKE) #{target}); \\ 70 | \tdone; 71 | " 72 | if clean != nil 73 | wr << "\t@-rm tmp/* tests/tmp/* 2> /dev/null\n" 74 | wr << "\t@rm Makefile\n" if clean 75 | end 76 | wr 77 | end 78 | 79 | 80 | include_dir, = dir_config("pgsql") 81 | if include_dir 82 | raise "--with-pgsql-include/--with-pgsql-dir is obsolete. Use --with-pg-config instead only if necessary." 83 | end 84 | 85 | pg_config = with_config('pg-config', 'pg_config') 86 | include_dir = `#{pg_config} --includedir`.strip 87 | $CFLAGS << " -I" << include_dir 88 | $CFLAGS << " -I" << `#{pg_config} --includedir-server`.strip 89 | 90 | if safe = with_config("safe-level") 91 | safe = Integer(safe) 92 | if safe < 0 93 | raise "invalid value for safe #{safe}" 94 | end 95 | $CFLAGS += " -DSAFE_LEVEL=#{safe}" 96 | else 97 | safe = 12 98 | end 99 | 100 | if with_config("greenplum") 101 | $CFLAGS += " -I" << File.join( include_dir, "postgresql", "internal" ) 102 | $CFLAGS += " -DWITH_GREENPLUM=1" 103 | end 104 | 105 | if timeout = with_config("timeout") 106 | timeout = Integer(timeout) 107 | if timeout < 2 108 | raise "Invalid value for timeout #{timeout}" 109 | end 110 | $CFLAGS += " -DPLRUBY_TIMEOUT=#{timeout}" 111 | if mainsafe = with_config("main-safe-level") 112 | $CFLAGS += " -DMAIN_SAFE_LEVEL=#{mainsafe}" 113 | end 114 | end 115 | 116 | if ! have_header("catalog/pg_proc.h") 117 | raise "Some include file are missing (see README for the installation)" 118 | end 119 | 120 | if have_func("rb_hash_delete", "ruby.h") 121 | $CFLAGS += " -DHAVE_RB_HASH_DELETE" 122 | end 123 | 124 | case version_str = `#{pg_config} --version` 125 | when /^PostgreSQL ([7-9])\.([0-9])(\.[0-9]+)?$/ 126 | version = 10 * $1.to_i + $2.to_i 127 | else 128 | version = 0 129 | end 130 | if version < 73 131 | raise <<-EOT 132 | 133 | ============================================================ 134 | #{version_str} is unsupported. Try plruby-0.4.2. 135 | ============================================================ 136 | EOT 137 | end 138 | 139 | if "aa".respond_to?(:initialize_copy, true) 140 | $CFLAGS += " -DHAVE_RB_INITIALIZE_COPY" 141 | end 142 | 143 | have_func("rb_block_call") 144 | have_header("st.h") 145 | 146 | if version >= 74 147 | if !have_header("server/utils/array.h") 148 | if !have_header("utils/array.h") 149 | raise "I cant't find server/utils/array.h" 150 | end 151 | $CFLAGS += " -DPG_UTILS_ARRAY" 152 | end 153 | end 154 | 155 | if macro_defined?("PG_TRY", %Q{#include "c.h"\n#include "utils/elog.h"}) 156 | $CFLAGS += " -DPG_PL_TRYCATCH" 157 | end 158 | 159 | enable_conversion = false 160 | if enable_conversion = enable_config("conversion", true) 161 | $CFLAGS += " -DPLRUBY_ENABLE_CONVERSION" 162 | if check_autoload(safe.to_i) 163 | $CFLAGS += " -DRUBY_CAN_USE_AUTOLOAD" 164 | end 165 | if check_md 166 | $CFLAGS += " -DRUBY_CAN_USE_MARSHAL_DUMP" 167 | end 168 | end 169 | 170 | conversions = {} 171 | subdirs = [] 172 | 173 | if enable_conversion 174 | Dir.foreach("src/conversions") do |dir| 175 | next if dir[0] == ?. || !File.directory?("src/conversions/" + dir) 176 | conversions[dir] = true 177 | end 178 | if !conversions.empty? 179 | File.open("src/conversions.h", "w") do |conv| 180 | conversions.sort.each do |key, val| 181 | conv.puts "#include \"conversions/#{key}/conversions.h\"" 182 | subdirs << "src/conversions/#{key}" 183 | end 184 | end 185 | end 186 | end 187 | 188 | $CFLAGS += " -DPG_PL_VERSION=#{version}" 189 | 190 | suffix = with_config('suffix').to_s 191 | $CFLAGS += " -DPLRUBY_CALL_HANDLER=plruby#{suffix}_call_handler" 192 | $CFLAGS += " -DPLRUBY_VALIDATOR=plruby#{suffix}_validator" 193 | 194 | subdirs.each do |key| 195 | orig_argv << "--with-cflags='#$CFLAGS -I.. -I ../..'" 196 | orig_argv << "--with-ldflags='#$LDFLAGS'" 197 | orig_argv << "--with-cppflags='#$CPPFLAGS'" 198 | cmd = "#{CONFIG['RUBY_INSTALL_NAME']} extconf.rb #{orig_argv.join(' ')}" 199 | system("cd #{key}; #{cmd}") 200 | end 201 | 202 | subdirs.unshift("src") 203 | 204 | begin 205 | Dir.chdir("src") 206 | if CONFIG["ENABLE_SHARED"] == "no" 207 | libs = if CONFIG.key?("LIBRUBYARG_STATIC") 208 | Config::expand(CONFIG["LIBRUBYARG_STATIC"].dup).sub(/^-l/, '') 209 | else 210 | Config::expand(CONFIG["LIBRUBYARG"].dup).sub(/lib([^.]*).*/, '\\1') 211 | end 212 | find_library(libs, "ruby_init", Config::expand(CONFIG["archdir"].dup)) 213 | end 214 | $objs = ["plruby.o", "plplan.o", "plpl.o", "pltrans.o"] unless $objs 215 | create_makefile("plruby#{suffix}") 216 | ensure 217 | Dir.chdir("..") 218 | end 219 | 220 | make = open("Makefile", "w") 221 | make.print <<-EOF 222 | SUBDIRS = #{subdirs.join(' ')} 223 | 224 | #{rule('all')} 225 | #{rule('clean', false)} 226 | #{rule('distclean', true)} 227 | #{rule('realclean', true)} 228 | #{rule('install')} 229 | #{rule('site-install')} 230 | 231 | %.html: %.rd 232 | \trd2 $< > ${<:%.rd=%.html} 233 | 234 | plruby.html: plruby.rd 235 | 236 | rd2: html 237 | 238 | html: plruby.html 239 | 240 | rdoc: docs/doc/index.html 241 | 242 | docs/doc/index.html: $(RDOC) 243 | \t@-(cd docs; rdoc plruby.rb) 244 | 245 | ri: 246 | \t@-(cd docs; rdoc -r plruby.rb) 247 | 248 | ri-site: 249 | \t@-(cd docs; rdoc -R plruby.rb) 250 | 251 | test: src/$(DLLIB) 252 | EOF 253 | regexp = %r{\Atest/conv_(.*)} 254 | Dir["test/*"].each do |dir| 255 | if regexp =~ dir 256 | next unless subdirs.include?("src/conversions/#{$1}") 257 | end 258 | make.puts "\t-(cd #{dir} ; RUBY='#{$ruby}' sh ./runtest #{version} #{suffix})" 259 | end 260 | 261 | make.close 262 | 263 | create_lang(version, suffix, safe) 264 | 265 | -------------------------------------------------------------------------------- /test/plt/test_setup.sql.in: -------------------------------------------------------------------------------- 1 | create table T_pkey1 ( 2 | key1 int4, 3 | key2 varchar(20), 4 | txt varchar(40) 5 | ); 6 | 7 | create table T_pkey2 ( 8 | key1 int4, 9 | key2 varchar(20), 10 | txt varchar(40) 11 | ); 12 | 13 | create table T_dta1 ( 14 | tkey varchar(20), 15 | ref1 int4, 16 | ref2 varchar(20) 17 | ); 18 | 19 | create table T_dta2 ( 20 | tkey varchar(20), 21 | ref1 int4, 22 | ref2 varchar(20) 23 | ); 24 | 25 | -- 26 | -- Function to check key existance in T_pkey1 27 | -- 28 | create function check_pkey1_exists(int4, varchar) returns bool as ' 29 | if ! $Plans.key?("plan") 30 | $Plans["plan"] = PL::Plan.new("select 1 from T_pkey1 31 | where key1 = $1 and key2 = $2", 32 | ["int4", "varchar"]).save 33 | end 34 | if $Plans["plan"].exec(args, 1) 35 | return true 36 | else 37 | return false 38 | end 39 | ' language 'plruby'; 40 | 41 | 42 | -- 43 | -- Trigger function on every change to T_pkey1 44 | -- 45 | create function trig_pkey1_before() returns trigger as ' 46 | if ! $Plans.key?("plan_pkey1") 47 | $Plans["plan_pkey1"] = PL::Plan.new("select check_pkey1_exists($1, $2) as ret", 48 | ["int4", "varchar"]).save 49 | $Plans["plan_dta1"] = PL::Plan.new("select 1 from T_dta1 50 | where ref1 = $1 and ref2 = $2", 51 | ["int4", "varchar"]).save 52 | end 53 | check_old_ref = false 54 | check_new_dup = false 55 | 56 | case tg["op"] 57 | when PL::INSERT 58 | check_new_dup = true 59 | when PL::UPDATE 60 | check_old_ref = new["key1"] != old["key1"] || new["key2"] != old["key2"] 61 | check_new_dup = true 62 | when PL::DELETE 63 | check_old_ref = true 64 | end 65 | if check_new_dup 66 | n = $Plans["plan_pkey1"].exec([new["key1"], new["key2"]], 1) 67 | if n["ret"] == "t" 68 | raise "duplicate key ''#{new[''key1'']}'', ''#{new[''key2'']}'' for T_pkey1" 69 | end 70 | end 71 | 72 | if check_old_ref 73 | if $Plans["plan_dta1"].exec([old["key1"], old["key2"]], 1) 74 | raise "key ''#{old[''key1'']}'', ''#{old[''key2'']}'' referenced by T_dta1" 75 | end 76 | end 77 | PL::OK 78 | ' language 'plruby'; 79 | 80 | create trigger pkey1_before before insert or update or delete on T_pkey1 81 | for each row execute procedure 82 | trig_pkey1_before(); 83 | 84 | 85 | -- 86 | -- Trigger function to check for duplicate keys in T_pkey2 87 | -- and to force key2 to be upper case only without leading whitespaces 88 | -- 89 | create function trig_pkey2_before() returns trigger as ' 90 | if ! $Plans.key?("plan_pkey2") 91 | $Plans["plan_pkey2"] = PL::Plan.new("select 1 from T_pkey2 92 | where key1 = $1 and key2 = $2", 93 | ["int4", "varchar"]).save 94 | end 95 | new["key2"] = new["key2"].sub(/^\\s*/, "").sub(/\\s*$/, "").upcase 96 | if $Plans["plan_pkey2"].exec([new["key1"], new["key2"]], 1) 97 | raise "duplicate key ''#{new[''key1'']}'', ''#{new[''key2'']}'' for T_pkey2" 98 | end 99 | new 100 | ' language 'plruby'; 101 | 102 | create trigger pkey2_before before insert or update on T_pkey2 103 | for each row execute procedure 104 | trig_pkey2_before(); 105 | 106 | 107 | -- 108 | -- Trigger function to force references from T_dta2 follow changes 109 | -- in T_pkey2 or be deleted too. This must be done AFTER the changes 110 | -- in T_pkey2 are done so the trigger for primkey check on T_dta2 111 | -- fired on our updates will see the new key values in T_pkey2. 112 | -- 113 | create function trig_pkey2_after() returns trigger as ' 114 | if ! $Plans["plan_dta2_upd"] 115 | $Plans["plan_dta2_upd"] = 116 | PL::Plan.new("update T_dta2 117 | set ref1 = $3, ref2 = $4 118 | where ref1 = $1 and ref2 = $2", 119 | ["int4", "varchar", "int4", "varchar" ]).save 120 | $Plans["plan_dta2_del"] = 121 | PL::Plan.new("delete from T_dta2 122 | where ref1 = $1 and ref2 = $2", 123 | ["int4", "varchar"]).save 124 | end 125 | 126 | old_ref_follow = false 127 | old_ref_delete = false 128 | 129 | case tg["op"] 130 | when PL::UPDATE 131 | new["key2"] = new["key2"].upcase 132 | old_ref_follow = (new["key1"] != old["key1"]) || 133 | (new["key2"] != old["key2"]) 134 | when PL::DELETE 135 | old_ref_delete = true 136 | end 137 | 138 | if old_ref_follow 139 | n = $Plans["plan_dta2_upd"].exec([old["key1"], old["key2"], new["key1"], new["key2"]]) 140 | warn "updated #{n} entries in T_dta2 for new key in T_pkey2" if n != 0 141 | end 142 | 143 | if old_ref_delete 144 | n = $Plans["plan_dta2_del"].exec([old["key1"], old["key2"]]) 145 | warn "deleted #{n} entries from T_dta2" if n != 0 146 | end 147 | 148 | PL::OK 149 | ' language 'plruby'; 150 | 151 | 152 | create trigger pkey2_after after update or delete on T_pkey2 153 | for each row execute procedure 154 | trig_pkey2_after(); 155 | 156 | 157 | -- 158 | -- Generic trigger function to check references in T_dta1 and T_dta2 159 | -- 160 | create function check_primkey() returns trigger as ' 161 | plankey = ["plan", tg["name"], tg["relid"]] 162 | planrel = ["relname", tg["relid"]] 163 | keyidx = args.size / 2 164 | keyrel = args[keyidx].downcase 165 | if ! $Plans[plankey] 166 | keylist = args[keyidx + 1 .. -1] 167 | query = "select 1 from #{keyrel}" 168 | qual = " where" 169 | typlist = [] 170 | idx = 1 171 | keylist.each do |key| 172 | key = key.downcase 173 | query << "#{qual} #{key} = $#{idx}" 174 | qual = " and" 175 | n = PL.exec("select T.typname as typname 176 | from pg_type T, pg_attribute A, pg_class C 177 | where C.relname = ''#{PL.quote(keyrel)}'' 178 | and C.oid = A.attrelid 179 | and A.attname = ''#{PL.quote(key)}'' 180 | and A.atttypid = T.oid", 1) 181 | if ! n 182 | raise "table #{keyrel} doesn''t have a field named #{key}" 183 | end 184 | typlist.push(n["typname"]) 185 | idx += 1 186 | end 187 | $Plans[plankey] = PL::Plan.new(query, typlist).save 188 | $Plans[planrel] = PL.exec("select relname from pg_class 189 | where oid = ''#{tg[''relid'']}''::oid", 1) 190 | end 191 | values = [] 192 | keyidx.times {|x| values.push(new[args[x]]) } 193 | n = $Plans[plankey].exec(values, 1) 194 | if ! n 195 | raise "key for #{$Plans[planrel][''relname'']} not in #{keyrel}" 196 | end 197 | PL::OK 198 | ' language 'plruby'; 199 | 200 | 201 | create trigger dta1_before before insert or update on T_dta1 202 | for each row execute procedure 203 | check_primkey('ref1', 'ref2', 'T_pkey1', 'key1', 'key2'); 204 | 205 | 206 | create trigger dta2_before before insert or update on T_dta2 207 | for each row execute procedure 208 | check_primkey('ref1', 'ref2', 'T_pkey2', 'key1', 'key2'); 209 | 210 | 211 | create function ruby_int4add(int4,int4) returns int4 as ' 212 | args[0].to_i + args[1].to_i 213 | ' language 'plruby'; 214 | 215 | create function ruby_int4_accum(_int4, int4) returns _int4 as ' 216 | a = args[0] 217 | [a[0].to_i + args[1].to_i, a[1].to_i + 1] 218 | ' language 'plruby'; 219 | 220 | create function ruby_int4_avg(_int4) returns int4 as ' 221 | a = args[0] 222 | if a[1].to_i == 0 223 | nil 224 | else 225 | a[0].to_i / a[1].to_i 226 | end 227 | ' language 'plruby'; 228 | 229 | create aggregate ruby_avg ( 230 | sfunc = ruby_int4_accum, 231 | basetype = int4, 232 | stype = _int4, 233 | finalfunc = ruby_int4_avg, 234 | initcond = '{0,0}' 235 | ); 236 | 237 | create aggregate ruby_sum ( 238 | sfunc = ruby_int4add, 239 | basetype = int4, 240 | stype = int4, 241 | initcond = '0' 242 | ); 243 | 244 | create function ruby_int4lt(int4,int4) returns bool as ' 245 | args[0].to_i < args[1].to_i 246 | ' language 'plruby'; 247 | 248 | create operator @< ( 249 | leftarg = int4, 250 | rightarg = int4, 251 | procedure = ruby_int4lt 252 | ); 253 | -------------------------------------------------------------------------------- /src/conversions/datetime/plruby_datetime.c: -------------------------------------------------------------------------------- 1 | #include "convcommon.h" 2 | 3 | #include 4 | #include 5 | 6 | static VALUE pl_cTinter, pl_mMarshal; 7 | 8 | static char * 9 | pl_dequote(char *src) 10 | { 11 | char *origin; 12 | 13 | while (*src && *src != '"') ++src; 14 | if (*src != '"') { 15 | rb_raise(rb_eArgError, "Invalid Tinterval"); 16 | } 17 | ++src; 18 | origin = src; 19 | while (*src && *src != '"') ++src; 20 | if (*src != '"') { 21 | rb_raise(rb_eArgError, "Invalid Tinterval"); 22 | } 23 | *src = 0; 24 | return origin; 25 | } 26 | 27 | struct pl_tint { 28 | VALUE low, high; 29 | }; 30 | 31 | static void 32 | pl_tint_mark(struct pl_tint *tint) 33 | { 34 | rb_gc_mark(tint->low); 35 | rb_gc_mark(tint->high); 36 | } 37 | 38 | static VALUE 39 | pl_tint_s_alloc(VALUE obj) 40 | { 41 | struct pl_tint *tint; 42 | return Data_Make_Struct(obj, struct pl_tint, pl_tint_mark, free, tint); 43 | } 44 | 45 | static VALUE 46 | pl_tint_s_from_string(VALUE obj, VALUE str) 47 | { 48 | char *first, *second, *tmp; 49 | VALUE d0, d1; 50 | struct pl_tint *tint; 51 | VALUE res; 52 | 53 | tmp = StringValuePtr(str); 54 | first = pl_dequote(tmp); 55 | second = pl_dequote(first + strlen(first) + 1); 56 | d0 = rb_dbl2big(DatumGetTimestamp(PLRUBY_DFC1(date_timestamp, 57 | PLRUBY_DFC1(date_in, first)))); 58 | d1 = rb_dbl2big(DatumGetTimestamp(PLRUBY_DFC1(date_timestamp, 59 | PLRUBY_DFC1(date_in, second)))); 60 | res = Data_Make_Struct(obj, struct pl_tint, pl_tint_mark, free, tint); 61 | tint->low = rb_funcall(rb_cTime, rb_intern("at"), 1, d0); 62 | tint->high = rb_funcall(rb_cTime, rb_intern("at"), 1, d1); 63 | if (OBJ_TAINTED(str)) OBJ_TAINT(res); 64 | return res; 65 | } 66 | 67 | static VALUE 68 | pl_tint_s_datum(VALUE obj, VALUE a) 69 | { 70 | TimeIntervalData *interval; 71 | Oid typoid; 72 | VALUE tmp, res; 73 | 74 | interval = (TimeIntervalData *)plruby_datum_get(a, &typoid); 75 | if (typoid != TINTERVALOID) { 76 | rb_raise(rb_eArgError, "invalid argument"); 77 | } 78 | res = rb_ary_new2(2); 79 | tmp = rb_dbl2big(DatumGetTimestamp(PLRUBY_DFC1(abstime_timestamp, 80 | interval->data[0]))); 81 | tmp = rb_funcall(rb_cTime, rb_intern("at"), 1, tmp); 82 | OBJ_TAINT(tmp); 83 | rb_ary_push(res, tmp); 84 | tmp = rb_dbl2big(DatumGetTimestamp(PLRUBY_DFC1(abstime_timestamp, 85 | interval->data[1]))); 86 | tmp = rb_funcall(rb_cTime, rb_intern("at"), 1, tmp); 87 | OBJ_TAINT(tmp); 88 | rb_ary_push(res, tmp); 89 | OBJ_TAINT(res); 90 | return res; 91 | } 92 | 93 | #if PG_PL_VERSION >= 74 94 | 95 | static VALUE 96 | pl_tint_mdump(int argc, VALUE *argv, VALUE obj) 97 | { 98 | struct pl_tint *tint; 99 | VALUE ary; 100 | 101 | Data_Get_Struct(obj, struct pl_tint, tint); 102 | ary = rb_ary_new2(2); 103 | rb_ary_push(ary, tint->low); 104 | rb_ary_push(ary, tint->high); 105 | return rb_funcall(pl_mMarshal, rb_intern("dump"), 1, ary); 106 | } 107 | 108 | static VALUE 109 | pl_tint_mload(VALUE obj, VALUE a) 110 | { 111 | struct pl_tint *tint; 112 | 113 | if (TYPE(a) != T_STRING || !RSTRING_LEN(a)) { 114 | rb_raise(rb_eArgError, "expected a String object"); 115 | } 116 | a = rb_funcall(pl_mMarshal, rb_intern("load"), 1, a); 117 | if (TYPE(a) != T_ARRAY || RARRAY_LEN(a) != 2) { 118 | rb_raise(rb_eArgError, "expected an Array with 2 elements"); 119 | } 120 | if (!rb_obj_is_kind_of(RARRAY_PTR(a)[0], rb_cTime) || 121 | !rb_obj_is_kind_of(RARRAY_PTR(a)[1], rb_cTime)) { 122 | rb_raise(rb_eArgError, "need 2 Times objects"); 123 | } 124 | Data_Get_Struct(obj, struct pl_tint, tint); 125 | tint->low = RARRAY_PTR(a)[0]; 126 | tint->high = RARRAY_PTR(a)[1]; 127 | return obj; 128 | } 129 | 130 | #endif 131 | 132 | static VALUE 133 | pl_tint_init(VALUE obj, VALUE a, VALUE b) 134 | { 135 | struct pl_tint *tint; 136 | 137 | if (!rb_obj_is_kind_of(a, rb_cTime) || !rb_obj_is_kind_of(b, rb_cTime)) { 138 | rb_raise(rb_eArgError, "need 2 Times objects"); 139 | } 140 | Data_Get_Struct(obj, struct pl_tint, tint); 141 | tint->low = a; 142 | tint->high = b; 143 | if (OBJ_TAINTED(a) || OBJ_TAINTED(b)) OBJ_TAINT(obj); 144 | return obj; 145 | } 146 | 147 | static VALUE 148 | pl_tint_low(VALUE obj) 149 | { 150 | struct pl_tint *tint; 151 | VALUE res; 152 | 153 | Data_Get_Struct(obj, struct pl_tint, tint); 154 | res = rb_obj_dup(tint->low); 155 | if (OBJ_TAINTED(obj)) OBJ_TAINT(res); 156 | return res; 157 | } 158 | 159 | static VALUE 160 | pl_tint_lowset(VALUE obj, VALUE a) 161 | { 162 | struct pl_tint *tint; 163 | 164 | Data_Get_Struct(obj, struct pl_tint, tint); 165 | if (!rb_obj_is_kind_of(a, rb_cTime)) { 166 | rb_raise(rb_eArgError, "need a Time object"); 167 | } 168 | tint->low = a; 169 | if (OBJ_TAINTED(a)) OBJ_TAINT(obj); 170 | return a; 171 | } 172 | 173 | static VALUE 174 | pl_tint_high(VALUE obj) 175 | { 176 | struct pl_tint *tint; 177 | VALUE res; 178 | 179 | Data_Get_Struct(obj, struct pl_tint, tint); 180 | res = rb_obj_dup(tint->high); 181 | if (OBJ_TAINTED(obj)) OBJ_TAINT(res); 182 | return res; 183 | } 184 | 185 | static VALUE 186 | pl_tint_highset(VALUE obj, VALUE a) 187 | { 188 | struct pl_tint *tint; 189 | 190 | Data_Get_Struct(obj, struct pl_tint, tint); 191 | if (!rb_obj_is_kind_of(a, rb_cTime)) { 192 | rb_raise(rb_eArgError, "need a Time object"); 193 | } 194 | tint->high = a; 195 | if (OBJ_TAINTED(a)) OBJ_TAINT(obj); 196 | return a; 197 | } 198 | 199 | #define tinterval_str "[\"%s\" \"%s\"]" 200 | 201 | static VALUE 202 | pl_tint_to_s(VALUE obj) 203 | { 204 | char *tmp, *t0, *t1; 205 | VALUE v0, v1; 206 | struct pl_tint *tint; 207 | 208 | Data_Get_Struct(obj, struct pl_tint, tint); 209 | v0 = plruby_to_s(tint->low); 210 | t0 = StringValuePtr(v0); 211 | v1 = plruby_to_s(tint->high); 212 | t1 = StringValuePtr(v1); 213 | tmp = ALLOCA_N(char, strlen(tinterval_str) + strlen(t0) + strlen(t1) + 1); 214 | sprintf(tmp, tinterval_str, t0, t1); 215 | if (OBJ_TAINTED(obj)) { 216 | return rb_tainted_str_new2(tmp); 217 | } 218 | return rb_str_new2(tmp); 219 | } 220 | 221 | static VALUE 222 | pl_tint_init_copy(VALUE copy, VALUE orig) 223 | { 224 | struct pl_tint *t0, *t1; 225 | 226 | if (copy == orig) return copy; 227 | if (TYPE(orig) != T_DATA || 228 | RDATA(orig)->dmark != (RUBY_DATA_FUNC)pl_tint_mark) { 229 | rb_raise(rb_eTypeError, "wrong argument type to clone"); 230 | } 231 | Data_Get_Struct(orig, struct pl_tint, t0); 232 | Data_Get_Struct(copy, struct pl_tint, t1); 233 | t1->low = rb_obj_dup(t0->low); 234 | t1->high = rb_obj_dup(t0->high); 235 | return copy; 236 | } 237 | 238 | void Init_plruby_datetime() 239 | { 240 | pl_mMarshal = rb_const_get(rb_cObject, rb_intern("Marshal")); 241 | pl_cTinter = rb_define_class("Tinterval", rb_cObject); 242 | rb_undef_method(CLASS_OF(pl_cTinter), "method_missing"); 243 | #if HAVE_RB_DEFINE_ALLOC_FUNC 244 | rb_define_alloc_func(pl_cTinter, pl_tint_s_alloc); 245 | #else 246 | rb_define_singleton_method(pl_cTinter, "allocate", pl_tint_s_alloc, 0); 247 | #endif 248 | rb_define_singleton_method(pl_cTinter, "new", plruby_s_new, -1); 249 | rb_define_singleton_method(pl_cTinter, "from_string", pl_tint_s_from_string, 1); 250 | rb_define_singleton_method(pl_cTinter, "from_datum", pl_tint_s_datum, 1); 251 | rb_define_method(pl_cTinter, "initialize", pl_tint_init, 2); 252 | #ifndef HAVE_RB_INITIALIZE_COPY 253 | rb_define_method(pl_cTinter, "clone", plruby_clone, 0); 254 | #endif 255 | rb_define_method(pl_cTinter, "initialize_copy", pl_tint_init_copy, 1); 256 | #if PG_PL_VERSION >= 74 257 | rb_define_method(pl_cTinter, "marshal_load", pl_tint_mload, 1); 258 | rb_define_method(pl_cTinter, "marshal_dump", pl_tint_mdump, -1); 259 | #ifndef RUBY_CAN_USE_MARSHAL_LOAD 260 | rb_define_singleton_method(pl_cTinter, "_load", plruby_s_load, 1); 261 | rb_define_alias(pl_cTinter, "_dump", "marshal_dump"); 262 | #endif 263 | #endif 264 | rb_define_method(pl_cTinter, "low", pl_tint_low, 0); 265 | rb_define_method(pl_cTinter, "low=", pl_tint_lowset, 1); 266 | rb_define_method(pl_cTinter, "high", pl_tint_high, 0); 267 | rb_define_method(pl_cTinter, "high=", pl_tint_highset, 1); 268 | rb_define_method(pl_cTinter, "to_s", pl_tint_to_s, 0); 269 | } 270 | -------------------------------------------------------------------------------- /test/range/test_queries.sql.in: -------------------------------------------------------------------------------- 1 | --- 2 | --- test from postgreSQL 3 | --- 4 | SELECT * FROM pg_settings WHERE name LIKE 'enable%'; 5 | 6 | CREATE TABLE foo2(fooid int, f2 int); 7 | INSERT INTO foo2 VALUES(1, 11); 8 | INSERT INTO foo2 VALUES(2, 22); 9 | INSERT INTO foo2 VALUES(1, 111); 10 | 11 | CREATE FUNCTION foot(int) returns setof foo2 as ' 12 | "SELECT * FROM foo2 WHERE fooid = #{args[0]}"' 13 | language 'plruby'; 14 | 15 | -- supposed to fail with ERROR 16 | select * from foo2, foot(foo2.fooid) z where foo2.f2 = z.f2; 17 | 18 | -- function in subselect 19 | select * from foo2 where f2 in ( 20 | select f2 from foot(foo2.fooid) z where z.fooid = foo2.fooid 21 | ) ORDER BY 1,2; 22 | 23 | -- function in subselect 24 | select * from foo2 where f2 in ( 25 | select f2 from foot(1) z where z.fooid = foo2.fooid 26 | ) ORDER BY 1,2; 27 | 28 | -- function in subselect 29 | select * from foo2 where f2 in ( 30 | select f2 from foot(foo2.fooid) z where z.fooid = 1 31 | ) ORDER BY 1,2; 32 | 33 | -- nested functions 34 | select foot.fooid, foot.f2 from foot(sin(pi()/2)::int) ORDER BY 1,2; 35 | 36 | CREATE TABLE foo (fooid int, foosubid int, fooname text, primary key(fooid,foosubid)); 37 | INSERT INTO foo VALUES(1,1,'Joe'); 38 | INSERT INTO foo VALUES(1,2,'Ed'); 39 | INSERT INTO foo VALUES(2,1,'Mary'); 40 | 41 | -- sql, proretset = f, prorettype = b 42 | CREATE FUNCTION getfoo(int) RETURNS int AS ' 43 | args[0] 44 | ' language 'plruby'; 45 | 46 | SELECT * FROM getfoo(1) AS t1; 47 | CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1); 48 | SELECT * FROM vw_getfoo; 49 | 50 | -- sql, proretset = t, prorettype = b 51 | DROP VIEW vw_getfoo; 52 | DROP FUNCTION getfoo(int); 53 | 54 | CREATE FUNCTION getfoo(int) RETURNS setof int AS ' 55 | "SELECT fooid FROM foo WHERE fooid = #{args[0]}" 56 | ' language 'plruby'; 57 | 58 | SELECT * FROM getfoo(1) AS t1; 59 | CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1); 60 | SELECT * FROM vw_getfoo; 61 | 62 | -- sql, proretset = t, prorettype = b 63 | DROP VIEW vw_getfoo; 64 | DROP FUNCTION getfoo(int); 65 | 66 | CREATE FUNCTION getfoo(int) RETURNS setof text AS ' 67 | "SELECT fooname FROM foo WHERE fooid = #{args[0]}" 68 | ' language 'plruby'; 69 | 70 | SELECT * FROM getfoo(1) AS t1; 71 | CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1); 72 | SELECT * FROM vw_getfoo; 73 | 74 | -- sql, proretset = t, prorettype = c 75 | DROP VIEW vw_getfoo; 76 | DROP FUNCTION getfoo(int); 77 | 78 | CREATE FUNCTION getfoo(int) RETURNS setof foo AS ' 79 | "SELECT * FROM foo WHERE fooid = #{args[0]}" 80 | ' language 'plruby'; 81 | 82 | SELECT * FROM getfoo(1) AS t1; 83 | CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1); 84 | SELECT * FROM vw_getfoo; 85 | 86 | -- sql, proretset = t, prorettype = record 87 | DROP VIEW vw_getfoo; 88 | DROP FUNCTION getfoo(int); 89 | 90 | CREATE FUNCTION getfoo(int) RETURNS setof record AS ' 91 | "SELECT * FROM foo WHERE fooid = #{args[0]}" 92 | ' language 'plruby'; 93 | 94 | SELECT * FROM getfoo(1) AS t1(fooid int, foosubid int, fooname text); 95 | CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1) AS (fooid int, foosubid int, fooname text); 96 | SELECT * FROM vw_getfoo; 97 | 98 | -- plpgsql, proretset = f, prorettype = b 99 | DROP VIEW vw_getfoo; 100 | DROP FUNCTION getfoo(int); 101 | 102 | CREATE FUNCTION getfoo(int) RETURNS setof int AS ' 103 | PL.exec("SELECT fooid FROM foo WHERE fooid = #{args[0]}") do |row| 104 | yield row.values 105 | end 106 | ' language 'plruby'; 107 | 108 | SELECT * FROM getfoo(1) AS t1; 109 | CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1); 110 | SELECT * FROM vw_getfoo; 111 | 112 | -- plpgsql, proretset = f, prorettype = c 113 | DROP VIEW vw_getfoo; 114 | DROP FUNCTION getfoo(int); 115 | 116 | CREATE FUNCTION getfoo(int) RETURNS setof foo AS ' 117 | PL.exec("SELECT * FROM foo WHERE fooid = #{args[0]}", nil, "value") do |r| 118 | yield r 119 | end 120 | ' language 'plruby'; 121 | 122 | SELECT * FROM getfoo(1) AS t1; 123 | CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1); 124 | SELECT * FROM vw_getfoo; 125 | 126 | DROP VIEW vw_getfoo; 127 | DROP FUNCTION getfoo(int); 128 | DROP FUNCTION foot(int); 129 | DROP TABLE foo2; 130 | DROP TABLE foo; 131 | 132 | -- Rescan tests -- 133 | CREATE TABLE foorescan (fooid int, foosubid int, fooname text, primary key(fooid,foosubid)); 134 | INSERT INTO foorescan values(5000,1,'abc.5000.1'); 135 | INSERT INTO foorescan values(5001,1,'abc.5001.1'); 136 | INSERT INTO foorescan values(5002,1,'abc.5002.1'); 137 | INSERT INTO foorescan values(5003,1,'abc.5003.1'); 138 | INSERT INTO foorescan values(5004,1,'abc.5004.1'); 139 | INSERT INTO foorescan values(5005,1,'abc.5005.1'); 140 | INSERT INTO foorescan values(5006,1,'abc.5006.1'); 141 | INSERT INTO foorescan values(5007,1,'abc.5007.1'); 142 | INSERT INTO foorescan values(5008,1,'abc.5008.1'); 143 | INSERT INTO foorescan values(5009,1,'abc.5009.1'); 144 | 145 | INSERT INTO foorescan values(5000,2,'abc.5000.2'); 146 | INSERT INTO foorescan values(5001,2,'abc.5001.2'); 147 | INSERT INTO foorescan values(5002,2,'abc.5002.2'); 148 | INSERT INTO foorescan values(5003,2,'abc.5003.2'); 149 | INSERT INTO foorescan values(5004,2,'abc.5004.2'); 150 | INSERT INTO foorescan values(5005,2,'abc.5005.2'); 151 | INSERT INTO foorescan values(5006,2,'abc.5006.2'); 152 | INSERT INTO foorescan values(5007,2,'abc.5007.2'); 153 | INSERT INTO foorescan values(5008,2,'abc.5008.2'); 154 | INSERT INTO foorescan values(5009,2,'abc.5009.2'); 155 | 156 | INSERT INTO foorescan values(5000,3,'abc.5000.3'); 157 | INSERT INTO foorescan values(5001,3,'abc.5001.3'); 158 | INSERT INTO foorescan values(5002,3,'abc.5002.3'); 159 | INSERT INTO foorescan values(5003,3,'abc.5003.3'); 160 | INSERT INTO foorescan values(5004,3,'abc.5004.3'); 161 | INSERT INTO foorescan values(5005,3,'abc.5005.3'); 162 | INSERT INTO foorescan values(5006,3,'abc.5006.3'); 163 | INSERT INTO foorescan values(5007,3,'abc.5007.3'); 164 | INSERT INTO foorescan values(5008,3,'abc.5008.3'); 165 | INSERT INTO foorescan values(5009,3,'abc.5009.3'); 166 | 167 | INSERT INTO foorescan values(5000,4,'abc.5000.4'); 168 | INSERT INTO foorescan values(5001,4,'abc.5001.4'); 169 | INSERT INTO foorescan values(5002,4,'abc.5002.4'); 170 | INSERT INTO foorescan values(5003,4,'abc.5003.4'); 171 | INSERT INTO foorescan values(5004,4,'abc.5004.4'); 172 | INSERT INTO foorescan values(5005,4,'abc.5005.4'); 173 | INSERT INTO foorescan values(5006,4,'abc.5006.4'); 174 | INSERT INTO foorescan values(5007,4,'abc.5007.4'); 175 | INSERT INTO foorescan values(5008,4,'abc.5008.4'); 176 | INSERT INTO foorescan values(5009,4,'abc.5009.4'); 177 | 178 | INSERT INTO foorescan values(5000,5,'abc.5000.5'); 179 | INSERT INTO foorescan values(5001,5,'abc.5001.5'); 180 | INSERT INTO foorescan values(5002,5,'abc.5002.5'); 181 | INSERT INTO foorescan values(5003,5,'abc.5003.5'); 182 | INSERT INTO foorescan values(5004,5,'abc.5004.5'); 183 | INSERT INTO foorescan values(5005,5,'abc.5005.5'); 184 | INSERT INTO foorescan values(5006,5,'abc.5006.5'); 185 | INSERT INTO foorescan values(5007,5,'abc.5007.5'); 186 | INSERT INTO foorescan values(5008,5,'abc.5008.5'); 187 | INSERT INTO foorescan values(5009,5,'abc.5009.5'); 188 | 189 | CREATE FUNCTION foorescan(int,int) RETURNS setof foorescan AS ' 190 | "SELECT * FROM foorescan WHERE fooid >= #{args[0]} and fooid < #{args[1]}" 191 | ' language 'plruby'; 192 | 193 | --invokes ExecFunctionReScan 194 | SELECT * FROM foorescan f WHERE f.fooid IN ( 195 | SELECT fooid FROM foorescan(5002,5004) 196 | ) ORDER BY 1,2; 197 | 198 | CREATE VIEW vw_foorescan AS SELECT * FROM foorescan(5002,5004); 199 | 200 | --invokes ExecFunctionReScan 201 | 202 | SELECT * FROM foorescan f WHERE f.fooid IN ( 203 | SELECT fooid FROM vw_foorescan 204 | ) ORDER BY 1,2; 205 | 206 | CREATE TABLE barrescan (fooid int primary key); 207 | INSERT INTO barrescan values(5003); 208 | INSERT INTO barrescan values(5004); 209 | INSERT INTO barrescan values(5005); 210 | INSERT INTO barrescan values(5006); 211 | INSERT INTO barrescan values(5007); 212 | INSERT INTO barrescan values(5008); 213 | 214 | CREATE FUNCTION foorescan(int) RETURNS setof foorescan AS ' 215 | "SELECT * FROM foorescan WHERE fooid = #{args[0]}" 216 | ' language 'plruby'; 217 | 218 | --invokes ExecFunctionReScan with chgParam != NULL 219 | SELECT f.* FROM barrescan b, foorescan f 220 | WHERE f.fooid = b.fooid AND b.fooid IN ( 221 | SELECT fooid FROM foorescan(b.fooid) 222 | ) ORDER BY 1,2; 223 | 224 | SELECT b.fooid, max(f.foosubid) FROM barrescan b, foorescan f 225 | WHERE f.fooid = b.fooid AND b.fooid IN ( 226 | SELECT fooid FROM foorescan(b.fooid) 227 | ) GROUP BY b.fooid ORDER BY 1,2; 228 | 229 | CREATE VIEW fooview1 AS SELECT f.* FROM barrescan b, foorescan f 230 | WHERE f.fooid = b.fooid AND b.fooid IN ( 231 | SELECT fooid FROM foorescan(b.fooid) 232 | ) ORDER BY 1,2; 233 | 234 | SELECT * FROM fooview1 AS fv WHERE fv.fooid = 5004; 235 | 236 | CREATE VIEW fooview2 AS SELECT b.fooid, max(f.foosubid) AS maxsubid 237 | FROM barrescan b, foorescan f WHERE f.fooid = b.fooid AND b.fooid IN ( 238 | SELECT fooid FROM foorescan(b.fooid) 239 | ) GROUP BY b.fooid ORDER BY 1,2; 240 | 241 | SELECT * FROM fooview2 AS fv WHERE fv.maxsubid = 5; 242 | 243 | DROP VIEW vw_foorescan; 244 | DROP VIEW fooview1; 245 | DROP VIEW fooview2; 246 | DROP FUNCTION foorescan(int,int); 247 | DROP FUNCTION foorescan(int); 248 | DROP TABLE foorescan; 249 | DROP TABLE barrescan; 250 | -------------------------------------------------------------------------------- /src/conversions/basic/plruby_basic.c: -------------------------------------------------------------------------------- 1 | #include "convcommon.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | static double cash_divisor; 11 | static Timestamp epoch; 12 | static ID id_at, id_to_f, id_to_i, id_usec; 13 | 14 | static VALUE 15 | pl_fixnum_s_datum(VALUE obj, VALUE a) 16 | { 17 | Oid typoid; 18 | Datum value; 19 | 20 | value = plruby_datum_get(a, &typoid); 21 | switch (typoid) { 22 | case OIDOID: 23 | return UINT2NUM(DatumGetObjectId(value)); 24 | 25 | case INT2OID: 26 | return INT2NUM(DatumGetInt16(value)); 27 | 28 | case INT4OID: 29 | return INT2NUM(DatumGetInt32(value)); 30 | 31 | case INT8OID: 32 | return LL2NUM(DatumGetInt64(value)); 33 | 34 | default: 35 | rb_raise(rb_eArgError, "unknown OID type %d", typoid); 36 | } 37 | } 38 | 39 | static VALUE 40 | pl_fixnum_to_datum(VALUE obj, VALUE a) 41 | { 42 | Datum d; 43 | 44 | switch (plruby_datum_oid(a, NULL)) { 45 | case OIDOID: 46 | d = ObjectIdGetDatum(NUM2UINT(obj)); 47 | break; 48 | 49 | case INT2OID: 50 | d = Int16GetDatum(NUM2INT(obj)); 51 | break; 52 | 53 | case INT4OID: 54 | d = Int32GetDatum(NUM2INT(obj)); 55 | break; 56 | 57 | case INT8OID: 58 | d = Int64GetDatum(NUM2LL(obj)); 59 | break; 60 | 61 | default: 62 | return Qnil; 63 | } 64 | return plruby_datum_set(a, d); 65 | } 66 | 67 | static VALUE 68 | pl_float_s_datum(VALUE obj, VALUE a) 69 | { 70 | Oid typoid; 71 | Datum value; 72 | double result; 73 | 74 | value = plruby_datum_get(a, &typoid); 75 | switch (typoid) { 76 | case FLOAT4OID: 77 | result = DatumGetFloat4(value); 78 | break; 79 | 80 | case FLOAT8OID: 81 | result = DatumGetFloat8(value); 82 | break; 83 | 84 | case CASHOID: 85 | result = (double) *(Cash *) DatumGetPointer(value) / cash_divisor; 86 | break; 87 | 88 | case NUMERICOID: 89 | result = DatumGetFloat8(plruby_dfc1(numeric_float8, value)); 90 | break; 91 | 92 | default: 93 | rb_raise(rb_eArgError, "unknown OID type %d", typoid); 94 | } 95 | return rb_float_new(result); 96 | } 97 | 98 | extern double round(); 99 | 100 | static VALUE 101 | pl_float_to_datum(VALUE obj, VALUE a) 102 | { 103 | double value; 104 | Datum d; 105 | 106 | value = RFLOAT_VALUE(obj); 107 | switch (plruby_datum_oid(a, NULL)) { 108 | case FLOAT4OID: 109 | d = Float4GetDatum((float4)value); 110 | break; 111 | 112 | case FLOAT8OID: 113 | d = Float8GetDatum((float8)value); 114 | break; 115 | 116 | case CASHOID: 117 | { 118 | Cash *cash = (Cash *) palloc(sizeof(Cash)); 119 | *cash = (Cash) round(value * cash_divisor); 120 | d = PointerGetDatum(cash); 121 | break; 122 | } 123 | 124 | case NUMERICOID: 125 | d = plruby_dfc1(float8_numeric, Float8GetDatum((float8)value)); 126 | break; 127 | 128 | default: 129 | return Qnil; 130 | } 131 | return plruby_datum_set(a, d); 132 | } 133 | 134 | static VALUE 135 | pl_str_s_datum(VALUE klass, VALUE a) 136 | { 137 | bytea *data; 138 | Oid typoid; 139 | Datum value; 140 | 141 | value = plruby_datum_get(a, &typoid); 142 | if (typoid != BYTEAOID) { 143 | return Qnil; 144 | } 145 | data = DatumGetByteaP(value); 146 | return rb_str_new(VARDATA(data), VARSIZE(data) - VARHDRSZ); 147 | } 148 | 149 | static VALUE 150 | pl_str_to_datum(VALUE obj, VALUE a) 151 | { 152 | bytea *data; 153 | size_t len; 154 | 155 | /* Converts BYTEA only. */ 156 | if (plruby_datum_oid(a, NULL) != BYTEAOID) 157 | return Qnil; 158 | 159 | len = RSTRING_LEN(obj); 160 | data = palloc(VARHDRSZ + len); 161 | memcpy(VARDATA(data), RSTRING_PTR(obj), len); 162 | #ifdef SET_VARSIZE 163 | SET_VARSIZE(data, VARHDRSZ + len); 164 | #else 165 | VARATT_SIZEP(data) = VARHDRSZ + len; 166 | #endif 167 | return plruby_datum_set(a, PointerGetDatum(data)); 168 | } 169 | 170 | static VALUE 171 | pl_time_s_datum(VALUE klass, VALUE a) 172 | { 173 | Timestamp ts; 174 | Oid typoid; 175 | Datum value; 176 | 177 | /* 178 | * INTERVAL and RELTIME are converted to Float (number of seconds). 179 | * For INTERVALs containing nonzero month/year component, duration of one 180 | * month is assumed to be 30*24*60*60 seconds. A special type has to be 181 | * created for this, because of the months/year components and also because 182 | * long or short enough numbers do not convert back right (exponential 183 | * notation of INTERVALs is not accepted by Postgres). 184 | * 185 | * TIMESTAMP, TIMESTAMP WITH TIME ZONE, ABSTIME, DATE are converted to klass 186 | * (Time), naturally. 187 | * 188 | * TIME and TIME WITH TIME ZONE are also converted to klass (Time), as in 189 | * the (totally broken anyway) 0.4.3 implementation. The result is that 190 | * specific time since Unix epoch. That makes little sense (the reverse 191 | * conversion of the result breaks anyway), but some at least. A special 192 | * type has to be created for this. 193 | */ 194 | value = plruby_datum_get(a, &typoid); 195 | switch (typoid) { 196 | /* Time interval types. */ 197 | 198 | case RELTIMEOID: 199 | value = plruby_dfc1(reltime_interval, value); 200 | /* ... */ 201 | case INTERVALOID: 202 | { 203 | Interval *iv = DatumGetIntervalP(value); 204 | 205 | return rb_float_new((double) iv->month * 30*24*60*60 + 206 | iv->time 207 | #ifdef HAVE_INT64_TIMESTAMP 208 | / 1E6 209 | #endif 210 | ); 211 | } 212 | 213 | /* 214 | * Time of day types. 215 | * 216 | * No separate conversion code is written, abusing the coincidence of C 217 | * types used for TimeADT and Timestamp (int64 or double, depending on 218 | * HAVE_INT64_TIMESTAMP). The proper implementation would use a special 219 | * type anyway, see above. 220 | */ 221 | 222 | case TIMETZOID: 223 | { 224 | TimeTzADT *timetz = DatumGetTimeTzADTP(value); 225 | 226 | /* Shift according to the timezone. */ 227 | ts = timetz->time + (Timestamp) timetz->zone 228 | #ifdef HAVE_INT64_TIMESTAMP 229 | * 1000000 230 | #endif 231 | ; 232 | } 233 | goto convert; 234 | 235 | case TIMEOID: 236 | ts = (Timestamp) DatumGetTimeADT(value); 237 | goto convert; 238 | 239 | /* The rest of types end up as a Timestamp in `value'. */ 240 | 241 | case ABSTIMEOID: 242 | value = plruby_dfc1(abstime_timestamptz, value); 243 | break; 244 | 245 | case DATEOID: 246 | value = plruby_dfc1(date_timestamptz, value); 247 | break; 248 | 249 | case TIMESTAMPOID: 250 | case TIMESTAMPTZOID: 251 | break; 252 | 253 | default: 254 | rb_raise(rb_eTypeError, "%s: incompatible type OID %u", 255 | rb_class2name(klass), typoid); 256 | } 257 | 258 | ts = DatumGetTimestamp(value) - epoch; 259 | 260 | convert: 261 | return rb_funcall(klass, id_at, 262 | #ifndef HAVE_INT64_TIMESTAMP 263 | 1, rb_float_new(ts) 264 | #else 265 | 2, LONG2NUM(ts / 1000000), ULONG2NUM(ts % 1000000) 266 | #endif 267 | ); 268 | } 269 | 270 | static VALUE 271 | pl_time_to_datum(VALUE obj, VALUE a) 272 | { 273 | PGFunction conv; 274 | Datum d; 275 | int typoid; 276 | 277 | typoid = plruby_datum_oid(a, NULL); 278 | switch (typoid) { 279 | case ABSTIMEOID: 280 | case DATEOID: 281 | case TIMEOID: 282 | case TIMESTAMPOID: 283 | case TIMESTAMPTZOID: 284 | case TIMETZOID: 285 | break; 286 | 287 | default: 288 | return Qnil; 289 | } 290 | 291 | /* Convert Time to TimestampTz first. */ 292 | #ifndef HAVE_INT64_TIMESTAMP 293 | d = TimestampTzGetDatum(epoch + NUM2DBL(rb_funcall(obj, id_to_f, 0))); 294 | #else 295 | d = TimestampTzGetDatum(epoch + (TimestampTz) 296 | NUM2LONG(rb_funcall(obj, id_to_i, 0)) * 1000000 + 297 | NUM2ULONG(rb_funcall(obj, id_usec, 0))); 298 | #endif 299 | 300 | conv = NULL; 301 | switch (typoid) { 302 | case ABSTIMEOID: 303 | conv = timestamptz_abstime; 304 | break; 305 | 306 | case DATEOID: 307 | conv = timestamptz_date; 308 | break; 309 | 310 | case TIMEOID: 311 | conv = timestamptz_time; 312 | break; 313 | 314 | case TIMESTAMPOID: 315 | conv = timestamptz_timestamp; 316 | break; 317 | 318 | case TIMESTAMPTZOID: 319 | break; 320 | 321 | case TIMETZOID: 322 | conv = timestamptz_timetz; 323 | break; 324 | } 325 | 326 | if (conv == NULL) { 327 | return Qnil; 328 | } 329 | d = plruby_dfc1(conv, d); 330 | return plruby_datum_set(a, d); 331 | } 332 | 333 | void Init_plruby_basic() 334 | { 335 | int fpoint; 336 | struct lconv *lconvert = PGLC_localeconv(); 337 | 338 | fpoint = lconvert->frac_digits; 339 | if (fpoint < 0 || fpoint > 10) { 340 | fpoint = 2; 341 | } 342 | cash_divisor = pow(10.0, fpoint); 343 | epoch = SetEpochTimestamp(); 344 | id_at = rb_intern("at"); 345 | id_to_f = rb_intern("to_f"); 346 | id_to_i = rb_intern("to_i"); 347 | id_usec = rb_intern("usec"); 348 | 349 | rb_define_singleton_method(rb_cFixnum, "from_datum", pl_fixnum_s_datum, 1); 350 | rb_define_method(rb_cFixnum, "to_datum", pl_fixnum_to_datum, 1); 351 | rb_define_singleton_method(rb_cFloat, "from_datum", pl_float_s_datum, 1); 352 | rb_define_method(rb_cFloat, "to_datum", pl_float_to_datum, 1); 353 | rb_define_singleton_method(rb_cString, "from_datum", pl_str_s_datum, 1); 354 | rb_define_method(rb_cString, "to_datum", pl_str_to_datum, 1); 355 | rb_define_singleton_method(rb_cTime, "from_datum", pl_time_s_datum, 1); 356 | rb_define_method(rb_cTime, "to_datum", pl_time_to_datum, 1); 357 | } 358 | -------------------------------------------------------------------------------- /src/plruby.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "postgres.h" 6 | #include "executor/spi.h" 7 | #include "commands/trigger.h" 8 | #include "utils/elog.h" 9 | #include "utils/builtins.h" 10 | #include "fmgr.h" 11 | #include "access/heapam.h" 12 | 13 | #include "tcop/tcopprot.h" 14 | #include "utils/syscache.h" 15 | #include "catalog/pg_proc.h" 16 | #include "catalog/pg_language.h" 17 | #include "catalog/pg_type.h" 18 | 19 | #include "nodes/makefuncs.h" 20 | #include "nodes/nodes.h" 21 | #include "parser/parse_type.h" 22 | #include "utils/lsyscache.h" 23 | #include "funcapi.h" 24 | 25 | #include "utils/array.h" 26 | 27 | #if PG_PL_VERSION >= 75 28 | #include "nodes/pg_list.h" 29 | #include "utils/typcache.h" 30 | #include "access/xact.h" 31 | #endif 32 | 33 | #if PG_PL_VERSION >= 81 34 | #include "utils/memutils.h" 35 | #endif 36 | 37 | #include "package.h" 38 | 39 | #include 40 | #if HAVE_ST_H 41 | #include 42 | #endif 43 | 44 | #ifndef StringValuePtr 45 | #define StringValuePtr(x) STR2CSTR(x) 46 | #endif 47 | 48 | #ifndef RSTRING_PTR 49 | # define RSTRING_PTR(x_) RSTRING(x_)->ptr 50 | # define RSTRING_LEN(x_) RSTRING(x_)->len 51 | #endif 52 | 53 | #ifndef RARRAY_PTR 54 | # define RARRAY_PTR(x_) RARRAY(x_)->ptr 55 | # define RARRAY_LEN(x_) RARRAY(x_)->len 56 | #endif 57 | 58 | #ifndef RHASH_TBL 59 | #define RHASH_TBL(x_) (RHASH(x_)->tbl) 60 | #endif 61 | 62 | #ifndef RFLOAT_VALUE 63 | #define RFLOAT_VALUE(x_) (RFLOAT(x_)->value) 64 | #endif 65 | 66 | extern VALUE rb_thread_list(); 67 | 68 | #ifndef SAFE_LEVEL 69 | #define SAFE_LEVEL 12 70 | #endif 71 | 72 | #ifndef MAIN_SAFE_LEVEL 73 | #ifdef PLRUBY_TIMEOUT 74 | #define MAIN_SAFE_LEVEL 3 75 | #else 76 | #define MAIN_SAFE_LEVEL SAFE_LEVEL 77 | #endif 78 | #endif 79 | 80 | #if SAFE_LEVEL <= MAIN_SAFE_LEVEL 81 | #define MAIN_SAFE_LEVEL SAFE_LEVEL 82 | #endif 83 | 84 | #ifdef PLRUBY_TIMEOUT 85 | 86 | extern int plruby_in_progress; 87 | extern int plruby_interrupted; 88 | 89 | #define PLRUBY_BEGIN(lvl_) do { \ 90 | int in_progress = plruby_in_progress; \ 91 | if (plruby_interrupted) { \ 92 | rb_raise(pl_ePLruby, "timeout"); \ 93 | } \ 94 | plruby_in_progress = lvl_; 95 | 96 | #define PLRUBY_END \ 97 | plruby_in_progress = in_progress; \ 98 | if (plruby_interrupted) { \ 99 | rb_raise(pl_ePLruby, "timeout"); \ 100 | } \ 101 | } while (0) 102 | 103 | #ifdef PG_PL_TRYCATCH 104 | 105 | #define PLRUBY_BEGIN_PROTECT(lvl_) do { \ 106 | int in_progress = plruby_in_progress; \ 107 | if (plruby_interrupted) { \ 108 | rb_raise(pl_ePLruby, "timeout"); \ 109 | } \ 110 | plruby_in_progress = lvl_; \ 111 | PG_TRY(); \ 112 | { 113 | 114 | extern int errorcode; 115 | 116 | #define PLRUBY_END_PROTECT \ 117 | } \ 118 | PG_CATCH(); \ 119 | { \ 120 | plruby_in_progress = in_progress; \ 121 | rb_raise(pl_eCatch, "propagate"); \ 122 | } \ 123 | PG_END_TRY(); \ 124 | plruby_in_progress = in_progress; \ 125 | if (plruby_interrupted) { \ 126 | rb_raise(pl_ePLruby, "timeout"); \ 127 | } \ 128 | } while (0) 129 | 130 | #else 131 | 132 | #define PLRUBY_BEGIN_PROTECT(lvl_) do { \ 133 | sigjmp_buf save_restart; \ 134 | int in_progress = plruby_in_progress; \ 135 | if (plruby_interrupted) { \ 136 | rb_raise(pl_ePLruby, "timeout"); \ 137 | } \ 138 | memcpy(&save_restart, &Warn_restart, sizeof(save_restart)); \ 139 | if (sigsetjmp(Warn_restart, 1) != 0) { \ 140 | plruby_in_progress = in_progress; \ 141 | memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); \ 142 | rb_raise(pl_eCatch, "propagate"); \ 143 | } \ 144 | plruby_in_progress = lvl_; 145 | 146 | #define PLRUBY_END_PROTECT \ 147 | plruby_in_progress = in_progress; \ 148 | memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); \ 149 | if (plruby_interrupted) { \ 150 | rb_raise(pl_ePLruby, "timeout"); \ 151 | } \ 152 | } while (0) 153 | 154 | #endif 155 | 156 | #else 157 | 158 | #define PLRUBY_BEGIN(lvl_) 159 | #define PLRUBY_END 160 | 161 | #ifdef PG_PL_TRYCATCH 162 | 163 | #define PLRUBY_BEGIN_PROTECT(lvl_) do { \ 164 | PG_TRY(); \ 165 | { 166 | 167 | #define PLRUBY_END_PROTECT \ 168 | } \ 169 | PG_CATCH(); \ 170 | { \ 171 | rb_raise(pl_eCatch, "propagate"); \ 172 | } \ 173 | PG_END_TRY(); \ 174 | } while (0) 175 | 176 | #else 177 | 178 | #define PLRUBY_BEGIN_PROTECT(lvl_) do { \ 179 | sigjmp_buf save_restart; \ 180 | memcpy(&save_restart, &Warn_restart, sizeof(save_restart)); \ 181 | if (sigsetjmp(Warn_restart, 1) != 0) { \ 182 | memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); \ 183 | rb_raise(pl_eCatch, "propagate"); \ 184 | } 185 | 186 | #define PLRUBY_END_PROTECT \ 187 | memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); \ 188 | } while (0) 189 | 190 | #endif 191 | 192 | #endif 193 | 194 | 195 | 196 | enum { TG_OK, TG_SKIP }; 197 | enum { TG_BEFORE, TG_AFTER, TG_ROW, TG_STATEMENT, TG_INSERT, 198 | TG_DELETE, TG_UPDATE, TG_UNKNOWN }; 199 | 200 | struct pl_thread_st { 201 | PG_FUNCTION_ARGS; 202 | int timeout; 203 | Oid validator; 204 | }; 205 | 206 | typedef struct pl_proc_desc 207 | { 208 | char *proname; 209 | TransactionId fn_xmin; 210 | CommandId fn_cmin; 211 | FmgrInfo result_func; 212 | Oid result_elem; 213 | Oid result_oid; 214 | int result_len; 215 | bool result_is_array; 216 | bool result_val; 217 | char result_align; 218 | int nargs; 219 | #if PG_PL_VERSION >= 75 220 | int named_args; 221 | #endif 222 | FmgrInfo arg_func[FUNC_MAX_ARGS]; 223 | Oid arg_elem[FUNC_MAX_ARGS]; 224 | Oid arg_type[FUNC_MAX_ARGS]; 225 | int arg_len[FUNC_MAX_ARGS]; 226 | bool arg_is_array[FUNC_MAX_ARGS]; 227 | bool arg_val[FUNC_MAX_ARGS]; 228 | char arg_align[FUNC_MAX_ARGS]; 229 | int arg_is_rel[FUNC_MAX_ARGS]; 230 | char result_type; 231 | } pl_proc_desc; 232 | 233 | struct portal_options { 234 | VALUE argsv; 235 | int count, output; 236 | int block, save; 237 | }; 238 | 239 | typedef struct pl_query_desc 240 | { 241 | char qname[20]; 242 | void *plan; 243 | int nargs; 244 | Oid *argtypes; 245 | FmgrInfo *arginfuncs; 246 | Oid *argtypelems; 247 | int *arglen; 248 | bool *arg_is_array; 249 | bool *arg_val; 250 | char *arg_align; 251 | int cursor; 252 | struct portal_options po; 253 | } pl_query_desc; 254 | 255 | struct PLportal { 256 | Portal portal; 257 | char *nulls; 258 | Datum *argvalues; 259 | int *arglen; 260 | int nargs; 261 | struct portal_options po; 262 | }; 263 | 264 | #define GetPortal(obj, portal) do { \ 265 | Data_Get_Struct(obj, struct PLportal, portal); \ 266 | if (!portal->portal) { \ 267 | rb_raise(pl_ePLruby, "cursor closed"); \ 268 | } \ 269 | } while (0) 270 | 271 | #define GetPlan(obj, qdesc) do { \ 272 | Data_Get_Struct(obj, pl_query_desc, qdesc); \ 273 | if (!qdesc->plan) { \ 274 | rb_raise(pl_ePLruby, "plan was dropped during the session"); \ 275 | } \ 276 | } while (0) 277 | 278 | #define RET_HASH 1 279 | #define RET_ARRAY 2 280 | #define RET_DESC 4 281 | #define RET_DESC_ARR 12 282 | #define RET_BASIC 16 283 | 284 | extern VALUE plruby_s_new _((int, VALUE *, VALUE)); 285 | extern VALUE plruby_build_tuple _((HeapTuple, TupleDesc, int)); 286 | extern Datum plruby_to_datum _((VALUE, FmgrInfo *, Oid, Oid, int)); 287 | extern Datum plruby_return_value _((struct pl_thread_st *, pl_proc_desc *, 288 | VALUE, VALUE)); 289 | extern VALUE plruby_create_args _((struct pl_thread_st *, pl_proc_desc *)); 290 | extern VALUE plruby_i_each _((VALUE, struct portal_options *)); 291 | extern void plruby_exec_output _((VALUE, int, int *)); 292 | extern VALUE plruby_to_s _((VALUE)); 293 | 294 | extern Datum plruby_return_array _((VALUE, pl_proc_desc *)); 295 | extern MemoryContext plruby_spi_context; 296 | 297 | extern Datum plruby_dfc0 _((PGFunction)); 298 | extern Datum plruby_dfc1 _((PGFunction, Datum)); 299 | extern Datum plruby_dfc2 _((PGFunction, Datum, Datum)); 300 | extern Datum plruby_dfc3 _((PGFunction, Datum, Datum, Datum)); 301 | 302 | #ifdef PLRUBY_ENABLE_CONVERSION 303 | extern VALUE plruby_classes, plruby_conversions; 304 | extern Oid plruby_datum_oid _((VALUE, int *)); 305 | extern VALUE plruby_datum_set _((VALUE, Datum)); 306 | extern Datum plruby_datum_get _((VALUE, Oid *)); 307 | extern VALUE plruby_define_void_class _((char *, char *)); 308 | #endif 309 | 310 | #define DFC1(a_, b_) DirectFunctionCall1((a_), (b_)) 311 | 312 | #define OidGD(a_) ObjectIdGetDatum(a_) 313 | #define PointerGD(a_) PointerGetDatum(a_) 314 | #define NameGD(a_) NameGetDatum(a_) 315 | #define BoolGD(a_) BoolGetDatum(a_) 316 | #define IntGD(a_) Int32GetDatum(a_) 317 | #define CStringGD(a_) CStringGetDatum(a_) 318 | #define TupleGD(a_,b_) TupleGetDatum((a_),(b_)) 319 | 320 | 321 | -------------------------------------------------------------------------------- /test/conv_network/test.expected.73: -------------------------------------------------------------------------------- 1 | create table pl_inet ( 2 | host text, abbrev text, masklen int, 3 | network inet, netmask inet, first inet, last inet 4 | ); 5 | create or replace function inet_val(inet) returns pl_inet as ' 6 | a = args[0] 7 | [a.host, a.abbrev, a.masklen, a.network, a.netmask, 8 | a.first, a.last] 9 | ' language 'plruby'; 10 | select * from inet_val('192.168.1'::cidr); 11 | host | abbrev | masklen | network | netmask | first | last 12 | -------------+--------------+---------+----------------+---------------+----------------+--------------- 13 | 192.168.1.0 | 192.168.1/24 | 24 | 192.168.1.0/24 | 255.255.255.0 | 192.168.1.0/24 | 192.168.1.255 14 | (1 row) 15 | 16 | select * from inet_val('192.168.1.226/24'::inet); 17 | host | abbrev | masklen | network | netmask | first | last 18 | ---------------+------------------+---------+----------------+---------------+----------------+--------------- 19 | 192.168.1.226 | 192.168.1.226/24 | 24 | 192.168.1.0/24 | 255.255.255.0 | 192.168.1.0/24 | 192.168.1.255 20 | (1 row) 21 | 22 | select * from inet_val('192.168.1.0/24'::cidr); 23 | host | abbrev | masklen | network | netmask | first | last 24 | -------------+--------------+---------+----------------+---------------+----------------+--------------- 25 | 192.168.1.0 | 192.168.1/24 | 24 | 192.168.1.0/24 | 255.255.255.0 | 192.168.1.0/24 | 192.168.1.255 26 | (1 row) 27 | 28 | select * from inet_val('192.168.1.226'::inet); 29 | host | abbrev | masklen | network | netmask | first | last 30 | ---------------+---------------+---------+------------------+-----------------+------------------+--------------- 31 | 192.168.1.226 | 192.168.1.226 | 32 | 192.168.1.226/32 | 255.255.255.255 | 192.168.1.226/32 | 192.168.1.226 32 | (1 row) 33 | 34 | select * from inet_val('192.168.1'::cidr); 35 | host | abbrev | masklen | network | netmask | first | last 36 | -------------+--------------+---------+----------------+---------------+----------------+--------------- 37 | 192.168.1.0 | 192.168.1/24 | 24 | 192.168.1.0/24 | 255.255.255.0 | 192.168.1.0/24 | 192.168.1.255 38 | (1 row) 39 | 40 | select * from inet_val('192.168.1.0/24'::inet); 41 | host | abbrev | masklen | network | netmask | first | last 42 | -------------+----------------+---------+----------------+---------------+----------------+--------------- 43 | 192.168.1.0 | 192.168.1.0/24 | 24 | 192.168.1.0/24 | 255.255.255.0 | 192.168.1.0/24 | 192.168.1.255 44 | (1 row) 45 | 46 | select * from inet_val('192.168.1'::cidr); 47 | host | abbrev | masklen | network | netmask | first | last 48 | -------------+--------------+---------+----------------+---------------+----------------+--------------- 49 | 192.168.1.0 | 192.168.1/24 | 24 | 192.168.1.0/24 | 255.255.255.0 | 192.168.1.0/24 | 192.168.1.255 50 | (1 row) 51 | 52 | select * from inet_val('192.168.1.0/25'::inet); 53 | host | abbrev | masklen | network | netmask | first | last 54 | -------------+----------------+---------+----------------+-----------------+----------------+--------------- 55 | 192.168.1.0 | 192.168.1.0/25 | 25 | 192.168.1.0/25 | 255.255.255.128 | 192.168.1.0/25 | 192.168.1.127 56 | (1 row) 57 | 58 | select * from inet_val('192.168.1'::cidr); 59 | host | abbrev | masklen | network | netmask | first | last 60 | -------------+--------------+---------+----------------+---------------+----------------+--------------- 61 | 192.168.1.0 | 192.168.1/24 | 24 | 192.168.1.0/24 | 255.255.255.0 | 192.168.1.0/24 | 192.168.1.255 62 | (1 row) 63 | 64 | select * from inet_val('192.168.1.255/24'::inet); 65 | host | abbrev | masklen | network | netmask | first | last 66 | ---------------+------------------+---------+----------------+---------------+----------------+--------------- 67 | 192.168.1.255 | 192.168.1.255/24 | 24 | 192.168.1.0/24 | 255.255.255.0 | 192.168.1.0/24 | 192.168.1.255 68 | (1 row) 69 | 70 | select * from inet_val('192.168.1'::cidr); 71 | host | abbrev | masklen | network | netmask | first | last 72 | -------------+--------------+---------+----------------+---------------+----------------+--------------- 73 | 192.168.1.0 | 192.168.1/24 | 24 | 192.168.1.0/24 | 255.255.255.0 | 192.168.1.0/24 | 192.168.1.255 74 | (1 row) 75 | 76 | select * from inet_val('192.168.1.255/25'::inet); 77 | host | abbrev | masklen | network | netmask | first | last 78 | ---------------+------------------+---------+------------------+-----------------+------------------+--------------- 79 | 192.168.1.255 | 192.168.1.255/25 | 25 | 192.168.1.128/25 | 255.255.255.128 | 192.168.1.128/25 | 192.168.1.255 80 | (1 row) 81 | 82 | select * from inet_val('10'::cidr); 83 | host | abbrev | masklen | network | netmask | first | last 84 | ----------+--------+---------+------------+-----------+------------+---------------- 85 | 10.0.0.0 | 10/8 | 8 | 10.0.0.0/8 | 255.0.0.0 | 10.0.0.0/8 | 10.255.255.255 86 | (1 row) 87 | 88 | select * from inet_val('10.1.2.3/8'::inet); 89 | host | abbrev | masklen | network | netmask | first | last 90 | ----------+------------+---------+------------+-----------+------------+---------------- 91 | 10.1.2.3 | 10.1.2.3/8 | 8 | 10.0.0.0/8 | 255.0.0.0 | 10.0.0.0/8 | 10.255.255.255 92 | (1 row) 93 | 94 | select * from inet_val('10.0.0.0'::cidr); 95 | host | abbrev | masklen | network | netmask | first | last 96 | ----------+-------------+---------+-------------+-----------------+-------------+---------- 97 | 10.0.0.0 | 10.0.0.0/32 | 32 | 10.0.0.0/32 | 255.255.255.255 | 10.0.0.0/32 | 10.0.0.0 98 | (1 row) 99 | 100 | select * from inet_val('10.1.2.3/8'::inet); 101 | host | abbrev | masklen | network | netmask | first | last 102 | ----------+------------+---------+------------+-----------+------------+---------------- 103 | 10.1.2.3 | 10.1.2.3/8 | 8 | 10.0.0.0/8 | 255.0.0.0 | 10.0.0.0/8 | 10.255.255.255 104 | (1 row) 105 | 106 | select * from inet_val('10.1.2.3'::cidr); 107 | host | abbrev | masklen | network | netmask | first | last 108 | ----------+-------------+---------+-------------+-----------------+-------------+---------- 109 | 10.1.2.3 | 10.1.2.3/32 | 32 | 10.1.2.3/32 | 255.255.255.255 | 10.1.2.3/32 | 10.1.2.3 110 | (1 row) 111 | 112 | select * from inet_val('10.1.2.3/32'::inet); 113 | host | abbrev | masklen | network | netmask | first | last 114 | ----------+----------+---------+-------------+-----------------+-------------+---------- 115 | 10.1.2.3 | 10.1.2.3 | 32 | 10.1.2.3/32 | 255.255.255.255 | 10.1.2.3/32 | 10.1.2.3 116 | (1 row) 117 | 118 | select * from inet_val('10.1.2'::cidr); 119 | host | abbrev | masklen | network | netmask | first | last 120 | ----------+-----------+---------+-------------+---------------+-------------+------------ 121 | 10.1.2.0 | 10.1.2/24 | 24 | 10.1.2.0/24 | 255.255.255.0 | 10.1.2.0/24 | 10.1.2.255 122 | (1 row) 123 | 124 | select * from inet_val('10.1.2.3/24'::inet); 125 | host | abbrev | masklen | network | netmask | first | last 126 | ----------+-------------+---------+-------------+---------------+-------------+------------ 127 | 10.1.2.3 | 10.1.2.3/24 | 24 | 10.1.2.0/24 | 255.255.255.0 | 10.1.2.0/24 | 10.1.2.255 128 | (1 row) 129 | 130 | select * from inet_val('10.1'::cidr); 131 | host | abbrev | masklen | network | netmask | first | last 132 | ----------+---------+---------+-------------+-------------+-------------+-------------- 133 | 10.1.0.0 | 10.1/16 | 16 | 10.1.0.0/16 | 255.255.0.0 | 10.1.0.0/16 | 10.1.255.255 134 | (1 row) 135 | 136 | select * from inet_val('10.1.2.3/16'::inet); 137 | host | abbrev | masklen | network | netmask | first | last 138 | ----------+-------------+---------+-------------+-------------+-------------+-------------- 139 | 10.1.2.3 | 10.1.2.3/16 | 16 | 10.1.0.0/16 | 255.255.0.0 | 10.1.0.0/16 | 10.1.255.255 140 | (1 row) 141 | 142 | select * from inet_val('10'::cidr); 143 | host | abbrev | masklen | network | netmask | first | last 144 | ----------+--------+---------+------------+-----------+------------+---------------- 145 | 10.0.0.0 | 10/8 | 8 | 10.0.0.0/8 | 255.0.0.0 | 10.0.0.0/8 | 10.255.255.255 146 | (1 row) 147 | 148 | select * from inet_val('10.1.2.3/8'::inet); 149 | host | abbrev | masklen | network | netmask | first | last 150 | ----------+------------+---------+------------+-----------+------------+---------------- 151 | 10.1.2.3 | 10.1.2.3/8 | 8 | 10.0.0.0/8 | 255.0.0.0 | 10.0.0.0/8 | 10.255.255.255 152 | (1 row) 153 | 154 | select * from inet_val('10'::cidr); 155 | host | abbrev | masklen | network | netmask | first | last 156 | ----------+--------+---------+------------+-----------+------------+---------------- 157 | 10.0.0.0 | 10/8 | 8 | 10.0.0.0/8 | 255.0.0.0 | 10.0.0.0/8 | 10.255.255.255 158 | (1 row) 159 | 160 | select * from inet_val('11.1.2.3/8'::inet); 161 | host | abbrev | masklen | network | netmask | first | last 162 | ----------+------------+---------+------------+-----------+------------+---------------- 163 | 11.1.2.3 | 11.1.2.3/8 | 8 | 11.0.0.0/8 | 255.0.0.0 | 11.0.0.0/8 | 11.255.255.255 164 | (1 row) 165 | 166 | select * from inet_val('10'::cidr); 167 | host | abbrev | masklen | network | netmask | first | last 168 | ----------+--------+---------+------------+-----------+------------+---------------- 169 | 10.0.0.0 | 10/8 | 8 | 10.0.0.0/8 | 255.0.0.0 | 10.0.0.0/8 | 10.255.255.255 170 | (1 row) 171 | 172 | select * from inet_val('9.1.2.3/8'::inet); 173 | host | abbrev | masklen | network | netmask | first | last 174 | ---------+-----------+---------+-----------+-----------+-----------+--------------- 175 | 9.1.2.3 | 9.1.2.3/8 | 8 | 9.0.0.0/8 | 255.0.0.0 | 9.0.0.0/8 | 9.255.255.255 176 | (1 row) 177 | 178 | select * from inet_val('10:23::f1'::cidr); 179 | ERROR: invalid CIDR value '10:23::f1' 180 | select * from inet_val('10:23::f1/64'::inet); 181 | ERROR: invalid INET value '10:23::f1/64' 182 | select * from inet_val('10:23::8000/113'::cidr); 183 | ERROR: invalid CIDR value '10:23::8000/113' 184 | select * from inet_val('10:23::ffff'::inet); 185 | ERROR: invalid INET value '10:23::ffff' 186 | select * from inet_val('::ffff:1.2.3.4'::cidr); 187 | ERROR: invalid CIDR value '::ffff:1.2.3.4' 188 | select * from inet_val('::4.3.2.1/24'::inet); 189 | ERROR: invalid INET value '::4.3.2.1/24' 190 | create or replace function mac_cmp(macaddr, macaddr) returns int as ' 191 | args[0] <=> args[1] 192 | ' language 'plruby'; 193 | select mac_cmp('00:07:E9:85:3E:C5'::macaddr, '00:E0:29:3E:E7:25'::macaddr); 194 | mac_cmp 195 | --------- 196 | -1 197 | (1 row) 198 | 199 | create or replace function mac_trunc(macaddr) returns macaddr as ' 200 | args[0].truncate 201 | ' language 'plruby'; 202 | select mac_trunc('00:07:E9:85:3E:C5'::macaddr); 203 | mac_trunc 204 | ------------------- 205 | 00:07:e9:00:00:00 206 | (1 row) 207 | 208 | select mac_trunc('00:E0:29:3E:E7:25'::macaddr); 209 | mac_trunc 210 | ------------------- 211 | 00:e0:29:00:00:00 212 | (1 row) 213 | 214 | -------------------------------------------------------------------------------- /src/pltrans.c: -------------------------------------------------------------------------------- 1 | #include "plruby.h" 2 | 3 | #if PG_PL_VERSION >= 75 4 | 5 | #define PG_PL_READ_UNCOMMITTED 0 6 | #define PG_PL_READ_COMMITTED 1 7 | #define PG_PL_REPETABLE_READ 2 8 | #define PG_PL_SERIALIZABLE 3 9 | 10 | #define PL_ELOG_DEBUG 0 11 | 12 | #if PL_ELOG_DEBUG 13 | #define pl_elog(a,b) elog(a,b) 14 | #else 15 | #define pl_elog(a,b) 16 | #endif 17 | 18 | static int pl_in_transaction = 0; 19 | static VALUE pl_eCatch, pl_ePLruby, pl_cTrans; 20 | 21 | struct pl_trans { 22 | VALUE name; 23 | int commit; 24 | }; 25 | 26 | static void 27 | pl_trans_mark(void *trans) 28 | { 29 | } 30 | 31 | #define GetTrans(obj_, trans_) do { \ 32 | if (TYPE(obj_) != T_DATA || \ 33 | RDATA(obj_)->dmark != (RUBY_DATA_FUNC)pl_trans_mark) { \ 34 | rb_raise(rb_eArgError, \ 35 | "transaction method called with a wrong object"); \ 36 | } \ 37 | Data_Get_Struct(obj_, struct pl_trans, trans_); \ 38 | } while (0) 39 | 40 | static char *savename = "savepoint_name"; 41 | 42 | static DefElem * 43 | make_defelem(char *name, VALUE arg) 44 | { 45 | DefElem *f = makeNode(DefElem); 46 | f->defname = name; 47 | f->arg = (Node *)makeString(RSTRING_PTR(arg)); 48 | return f; 49 | } 50 | 51 | 52 | static VALUE 53 | pl_intern_commit(VALUE obj) 54 | { 55 | struct pl_trans *trans; 56 | int rc; 57 | 58 | pl_elog(NOTICE, "==> pl_intern_commit"); 59 | GetTrans(obj, trans); 60 | PLRUBY_BEGIN_PROTECT(1); 61 | if (NIL_P(trans->name)) { 62 | if (!trans->commit) { 63 | pl_elog(NOTICE, "ReleaseCurrentSubTransaction"); 64 | trans->commit = Qtrue; 65 | if ((rc = SPI_finish()) != SPI_OK_FINISH) { 66 | elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc)); 67 | } 68 | ReleaseCurrentSubTransaction(); 69 | } 70 | } 71 | else { 72 | List *list; 73 | 74 | pl_elog(NOTICE, "ReleaseSavepoint"); 75 | list = list_make1(make_defelem(savename, trans->name)); 76 | trans->name = Qnil; 77 | trans->commit = Qtrue; 78 | ReleaseSavepoint(list); 79 | CommitTransactionCommand(); 80 | StartTransactionCommand(); 81 | } 82 | PLRUBY_END_PROTECT; 83 | pl_elog(NOTICE, "<== pl_intern_commit"); 84 | return Qnil; 85 | } 86 | 87 | struct pl_throw { 88 | VALUE txn; 89 | int commit; 90 | }; 91 | 92 | static void 93 | pl_throw_mark(struct pl_throw *plt) 94 | { 95 | rb_gc_mark(plt->txn); 96 | } 97 | 98 | static VALUE 99 | pl_commit(VALUE obj) 100 | { 101 | VALUE res; 102 | struct pl_throw *plt; 103 | 104 | pl_elog(NOTICE, "pl_commit"); 105 | if (!IsSubTransaction()) { 106 | rb_raise(pl_ePLruby, "outside a transaction"); 107 | } 108 | res = Data_Make_Struct(pl_cTrans, struct pl_throw, pl_throw_mark, free, plt); 109 | plt->commit = Qtrue; 110 | plt->txn = obj; 111 | rb_throw("__plruby__transaction__", res); 112 | return Qnil; 113 | } 114 | 115 | static VALUE 116 | pl_intern_abort(VALUE obj) 117 | { 118 | struct pl_trans *trans; 119 | int rc; 120 | 121 | pl_elog(NOTICE, "==> pl_intern_abort"); 122 | if (!IsSubTransaction()) { 123 | rb_raise(pl_ePLruby, "outside a transaction"); 124 | } 125 | GetTrans(obj, trans); 126 | PLRUBY_BEGIN_PROTECT(1); 127 | if (NIL_P(trans->name)) { 128 | if (!trans->commit) { 129 | pl_elog(NOTICE, "RollbackAndReleaseCurrentSubTransaction"); 130 | trans->commit = Qtrue; 131 | if ((rc = SPI_finish()) != SPI_OK_FINISH) { 132 | elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc)); 133 | } 134 | RollbackAndReleaseCurrentSubTransaction(); 135 | } 136 | } 137 | else { 138 | List *list; 139 | 140 | pl_elog(NOTICE, "RollbackToSavepoint"); 141 | list = list_make1(make_defelem(savename, trans->name)); 142 | trans->name = Qnil; 143 | trans->commit = Qtrue; 144 | RollbackToSavepoint(list); 145 | CommitTransactionCommand(); 146 | RollbackAndReleaseCurrentSubTransaction(); 147 | } 148 | PLRUBY_END_PROTECT; 149 | pl_elog(NOTICE, "<== pl_intern_abort"); 150 | return Qnil; 151 | } 152 | 153 | static VALUE 154 | pl_intern_error(VALUE obj) 155 | { 156 | struct pl_trans *trans; 157 | 158 | pl_elog(NOTICE, "==> pl_intern_error"); 159 | if (!IsSubTransaction()) { 160 | rb_raise(pl_ePLruby, "outside a transaction"); 161 | } 162 | GetTrans(obj, trans); 163 | PLRUBY_BEGIN_PROTECT(1); 164 | pl_elog(NOTICE, "RollbackAndReleaseCurrentSubTransaction"); 165 | trans->commit = Qtrue; 166 | RollbackAndReleaseCurrentSubTransaction(); 167 | PLRUBY_END_PROTECT; 168 | pl_elog(NOTICE, "<== pl_intern_error"); 169 | return Qnil; 170 | } 171 | 172 | static VALUE 173 | pl_abort(VALUE obj) 174 | { 175 | VALUE res; 176 | struct pl_throw *plt; 177 | 178 | pl_elog(NOTICE, "pl_abort"); 179 | if (!IsSubTransaction()) { 180 | rb_raise(pl_ePLruby, "outside a transaction"); 181 | } 182 | res = Data_Make_Struct(pl_cTrans, struct pl_throw, pl_throw_mark, free, plt); 183 | plt->commit = Qfalse; 184 | plt->txn = obj; 185 | rb_throw("__plruby__transaction__", res); 186 | return Qnil; 187 | } 188 | 189 | static VALUE 190 | pl_exec(VALUE val, VALUE args, VALUE self) 191 | { 192 | rb_yield(args); 193 | return Qnil; 194 | } 195 | 196 | static VALUE 197 | pl_catch(VALUE obj) 198 | { 199 | VALUE res; 200 | struct pl_throw *plt; 201 | 202 | pl_elog(NOTICE, "pl_catch"); 203 | res = rb_catch("__plruby__transaction__", pl_exec, obj); 204 | if (TYPE(res) == T_DATA && 205 | RDATA(res)->dmark == (RUBY_DATA_FUNC)pl_throw_mark) { 206 | Data_Get_Struct(res, struct pl_throw, plt); 207 | if (plt->commit) { 208 | pl_intern_commit(obj); 209 | } 210 | else { 211 | pl_intern_abort(obj); 212 | } 213 | if (obj != plt->txn) { 214 | rb_throw("__plruby__transaction__", res); 215 | } 216 | } 217 | else { 218 | pl_intern_commit(obj); 219 | } 220 | return Qnil; 221 | } 222 | 223 | 224 | static VALUE 225 | pl_transaction(VALUE obj) 226 | { 227 | struct pl_trans *trans; 228 | VALUE res; 229 | int state, rc, begin_sub; 230 | MemoryContext orig_context = 0; 231 | 232 | 233 | pl_elog(NOTICE, "==> pl_transaction"); 234 | if (!rb_block_given_p()) { 235 | rb_raise(rb_eArgError, "no block given"); 236 | } 237 | res = Data_Make_Struct(pl_cTrans, struct pl_trans, pl_trans_mark, 0, trans); 238 | trans->name = Qnil; 239 | PLRUBY_BEGIN_PROTECT(1); 240 | if (IsSubTransaction()) { 241 | char name[1024]; 242 | 243 | pl_elog(NOTICE, "DefineSavepoint"); 244 | sprintf(name, "__plruby__%d__", pl_in_transaction); 245 | DefineSavepoint(name); 246 | CommitTransactionCommand(); 247 | StartTransactionCommand(); 248 | pl_in_transaction++; 249 | begin_sub = Qfalse; 250 | trans->name = rb_str_new2(name); 251 | } 252 | else { 253 | pl_in_transaction = 0; 254 | pl_elog(NOTICE, "BeginTransactionBlock"); 255 | orig_context = CurrentMemoryContext; 256 | SPI_push(); 257 | BeginInternalSubTransaction(NULL); 258 | MemoryContextSwitchTo(orig_context); 259 | if ((rc = SPI_connect()) != SPI_OK_CONNECT) { 260 | elog(ERROR, "SPI_connect in transaction failed : %s", 261 | SPI_result_code_string(rc)); 262 | } 263 | begin_sub = Qtrue; 264 | } 265 | PLRUBY_END_PROTECT; 266 | pl_elog(NOTICE, "==> rb_protect"); 267 | rb_protect(pl_catch, res, &state); 268 | pl_elog(NOTICE, "<== rb_protect"); 269 | if (state) { 270 | VALUE error = rb_gv_get("$!"); 271 | if (begin_sub && CLASS_OF(error) == pl_eCatch) { 272 | if (!trans->commit) { 273 | rb_protect(pl_intern_error, res, 0); 274 | } 275 | } 276 | else { 277 | if (!trans->commit) { 278 | rb_protect(pl_intern_abort, res, 0); 279 | } 280 | if (begin_sub) { 281 | MemoryContextSwitchTo(orig_context); 282 | SPI_pop(); 283 | } 284 | } 285 | rb_jump_tag(state); 286 | } 287 | Data_Get_Struct(res, struct pl_trans, trans); 288 | if (begin_sub) { 289 | pl_elog(NOTICE, "commit"); 290 | if (!trans->commit) { 291 | rb_protect(pl_intern_commit, res, 0); 292 | } 293 | MemoryContextSwitchTo(orig_context); 294 | SPI_pop(); 295 | } 296 | pl_elog(NOTICE, "<== pl_transaction"); 297 | return Qnil; 298 | } 299 | 300 | #endif 301 | 302 | #if PG_PL_VERSION >= 81 303 | 304 | static VALUE 305 | pl_savepoint(VALUE obj, VALUE a) 306 | { 307 | if (!IsTransactionBlock() || !IsSubTransaction()) { 308 | rb_raise(pl_ePLruby, "savepoint called outside a transaction"); 309 | } 310 | a = plruby_to_s(a); 311 | pl_elog(NOTICE, "====> definesavepoint"); 312 | PLRUBY_BEGIN_PROTECT(1); 313 | DefineSavepoint(RSTRING_PTR(a)); 314 | CommitTransactionCommand(); 315 | StartTransactionCommand(); 316 | PLRUBY_END_PROTECT; 317 | pl_elog(NOTICE, "<==== definesavepoint"); 318 | return Qnil; 319 | } 320 | 321 | static VALUE 322 | pl_release(VALUE obj, VALUE a) 323 | { 324 | if (!IsTransactionBlock() || !IsSubTransaction()) { 325 | rb_raise(pl_ePLruby, "release called outside a transaction"); 326 | } 327 | a = plruby_to_s(a); 328 | pl_elog(NOTICE, "====> releasesavepoint"); 329 | PLRUBY_BEGIN_PROTECT(1); 330 | ReleaseSavepoint(list_make1(make_defelem("savepoint_name", a))); 331 | CommitTransactionCommand(); 332 | StartTransactionCommand(); 333 | PLRUBY_END_PROTECT; 334 | pl_elog(NOTICE, "<==== releasesavepoint"); 335 | return Qnil; 336 | } 337 | 338 | static VALUE 339 | pl_rollback(VALUE obj, VALUE a) 340 | { 341 | if (!IsTransactionBlock() || !IsSubTransaction()) { 342 | rb_raise(pl_ePLruby, "rollback called outside a transaction"); 343 | } 344 | a = plruby_to_s(a); 345 | pl_elog(NOTICE, "====> rollbacksavepoint"); 346 | PLRUBY_BEGIN_PROTECT(1); 347 | RollbackToSavepoint(list_make1(make_defelem("savepoint_name", a))); 348 | CommitTransactionCommand(); 349 | RollbackAndReleaseCurrentSubTransaction(); 350 | PLRUBY_END_PROTECT; 351 | pl_elog(NOTICE, "<==== rollbacksavepoint"); 352 | return Qnil; 353 | } 354 | 355 | #endif 356 | 357 | void 358 | Init_plruby_trans() 359 | { 360 | #if PG_PL_VERSION >= 75 361 | VALUE pl_mPL; 362 | 363 | pl_mPL = rb_const_get(rb_cObject, rb_intern("PL")); 364 | pl_ePLruby = rb_const_get(pl_mPL, rb_intern("Error")); 365 | pl_eCatch = rb_const_get(pl_mPL, rb_intern("Catch")); 366 | 367 | rb_define_global_const("READ_UNCOMMITED", INT2FIX(PG_PL_READ_UNCOMMITTED)); 368 | rb_define_global_const("READ_COMMITED", INT2FIX(PG_PL_READ_COMMITTED)); 369 | rb_define_global_const("REPETABLE_READ", INT2FIX(PG_PL_REPETABLE_READ)); 370 | rb_define_global_const("SERIALIZABLE", INT2FIX(PG_PL_SERIALIZABLE)); 371 | rb_define_global_function("transaction", pl_transaction, 0); 372 | #if PG_PL_VERSION >= 81 373 | rb_define_global_function("savepoint", pl_savepoint, 1); 374 | rb_define_global_function("release_savepoint", pl_release, 1); 375 | rb_define_global_function("rollback_to_savepoint", pl_rollback, 1); 376 | #endif 377 | pl_cTrans = rb_define_class_under(pl_mPL, "Transaction", rb_cObject); 378 | #if HAVE_RB_DEFINE_ALLOC_FUNC 379 | rb_undef_alloc_func(pl_cTrans); 380 | #else 381 | rb_undef_method(CLASS_OF(pl_cTrans), "allocate"); 382 | #endif 383 | rb_undef_method(CLASS_OF(pl_cTrans), "new"); 384 | rb_define_method(pl_cTrans, "commit", pl_commit, 0); 385 | rb_define_method(pl_cTrans, "abort", pl_abort, 0); 386 | rb_define_method(pl_cTrans, "rollback", pl_abort, 0); 387 | #endif 388 | } 389 | -------------------------------------------------------------------------------- /test/range/test.expected.73.in: -------------------------------------------------------------------------------- 1 | SELECT * FROM pg_settings WHERE name LIKE 'enable%'; 2 | name | setting 3 | ------------------+--------- 4 | enable_hashjoin | on 5 | enable_indexscan | on 6 | enable_mergejoin | on 7 | enable_nestloop | on 8 | enable_seqscan | on 9 | enable_sort | on 10 | enable_tidscan | on 11 | (7 rows) 12 | 13 | CREATE TABLE foo2(fooid int, f2 int); 14 | INSERT INTO foo2 VALUES(1, 11); 15 | INSERT INTO foo2 VALUES(2, 22); 16 | INSERT INTO foo2 VALUES(1, 111); 17 | CREATE FUNCTION foot(int) returns setof foo2 as ' 18 | "SELECT * FROM foo2 WHERE fooid = #{args[0]}"' 19 | language 'plruby'; 20 | select * from foo2, foot(foo2.fooid) z where foo2.f2 = z.f2; 21 | NOTICE: Adding missing FROM-clause entry for table "foo2" 22 | ERROR: FROM function expression may not refer to other relations of same query level 23 | select * from foo2 where f2 in ( 24 | select f2 from foot(foo2.fooid) z where z.fooid = foo2.fooid 25 | ) ORDER BY 1,2; 26 | fooid | f2 27 | -------+----- 28 | 1 | 11 29 | 1 | 111 30 | 2 | 22 31 | (3 rows) 32 | 33 | select * from foo2 where f2 in ( 34 | select f2 from foot(1) z where z.fooid = foo2.fooid 35 | ) ORDER BY 1,2; 36 | fooid | f2 37 | -------+----- 38 | 1 | 11 39 | 1 | 111 40 | (2 rows) 41 | 42 | select * from foo2 where f2 in ( 43 | select f2 from foot(foo2.fooid) z where z.fooid = 1 44 | ) ORDER BY 1,2; 45 | fooid | f2 46 | -------+----- 47 | 1 | 11 48 | 1 | 111 49 | (2 rows) 50 | 51 | select foot.fooid, foot.f2 from foot(sin(pi()/2)::int) ORDER BY 1,2; 52 | fooid | f2 53 | -------+----- 54 | 1 | 11 55 | 1 | 111 56 | (2 rows) 57 | 58 | CREATE TABLE foo (fooid int, foosubid int, fooname text, primary key(fooid,foosubid)); 59 | NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index 'foo_pkey' for table 'foo' 60 | INSERT INTO foo VALUES(1,1,'Joe'); 61 | INSERT INTO foo VALUES(1,2,'Ed'); 62 | INSERT INTO foo VALUES(2,1,'Mary'); 63 | CREATE FUNCTION getfoo(int) RETURNS int AS ' 64 | args[0] 65 | ' language 'plruby'; 66 | SELECT * FROM getfoo(1) AS t1; 67 | t1 68 | ---- 69 | 1 70 | (1 row) 71 | 72 | CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1); 73 | SELECT * FROM vw_getfoo; 74 | getfoo 75 | -------- 76 | 1 77 | (1 row) 78 | 79 | DROP VIEW vw_getfoo; 80 | DROP FUNCTION getfoo(int); 81 | CREATE FUNCTION getfoo(int) RETURNS setof int AS ' 82 | "SELECT fooid FROM foo WHERE fooid = #{args[0]}" 83 | ' language 'plruby'; 84 | SELECT * FROM getfoo(1) AS t1; 85 | t1 86 | ---- 87 | 1 88 | 1 89 | (2 rows) 90 | 91 | CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1); 92 | SELECT * FROM vw_getfoo; 93 | getfoo 94 | -------- 95 | 1 96 | 1 97 | (2 rows) 98 | 99 | DROP VIEW vw_getfoo; 100 | DROP FUNCTION getfoo(int); 101 | CREATE FUNCTION getfoo(int) RETURNS setof text AS ' 102 | "SELECT fooname FROM foo WHERE fooid = #{args[0]}" 103 | ' language 'plruby'; 104 | SELECT * FROM getfoo(1) AS t1; 105 | t1 106 | ----- 107 | Joe 108 | Ed 109 | (2 rows) 110 | 111 | CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1); 112 | SELECT * FROM vw_getfoo; 113 | getfoo 114 | -------- 115 | Joe 116 | Ed 117 | (2 rows) 118 | 119 | DROP VIEW vw_getfoo; 120 | DROP FUNCTION getfoo(int); 121 | CREATE FUNCTION getfoo(int) RETURNS setof foo AS ' 122 | "SELECT * FROM foo WHERE fooid = #{args[0]}" 123 | ' language 'plruby'; 124 | SELECT * FROM getfoo(1) AS t1; 125 | fooid | foosubid | fooname 126 | -------+----------+--------- 127 | 1 | 1 | Joe 128 | 1 | 2 | Ed 129 | (2 rows) 130 | 131 | CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1); 132 | SELECT * FROM vw_getfoo; 133 | fooid | foosubid | fooname 134 | -------+----------+--------- 135 | 1 | 1 | Joe 136 | 1 | 2 | Ed 137 | (2 rows) 138 | 139 | DROP VIEW vw_getfoo; 140 | DROP FUNCTION getfoo(int); 141 | CREATE FUNCTION getfoo(int) RETURNS setof record AS ' 142 | "SELECT * FROM foo WHERE fooid = #{args[0]}" 143 | ' language 'plruby'; 144 | SELECT * FROM getfoo(1) AS t1(fooid int, foosubid int, fooname text); 145 | fooid | foosubid | fooname 146 | -------+----------+--------- 147 | 1 | 1 | Joe 148 | 1 | 2 | Ed 149 | (2 rows) 150 | 151 | CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1) AS (fooid int, foosubid int, fooname text); 152 | SELECT * FROM vw_getfoo; 153 | fooid | foosubid | fooname 154 | -------+----------+--------- 155 | 1 | 1 | Joe 156 | 1 | 2 | Ed 157 | (2 rows) 158 | 159 | DROP VIEW vw_getfoo; 160 | DROP FUNCTION getfoo(int); 161 | CREATE FUNCTION getfoo(int) RETURNS setof int AS ' 162 | PL.exec("SELECT fooid FROM foo WHERE fooid = #{args[0]}") do |row| 163 | yield row.values 164 | end 165 | ' language 'plruby'; 166 | SELECT * FROM getfoo(1) AS t1; 167 | t1 168 | ---- 169 | 1 170 | 1 171 | (2 rows) 172 | 173 | CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1); 174 | SELECT * FROM vw_getfoo; 175 | getfoo 176 | -------- 177 | 1 178 | 1 179 | (2 rows) 180 | 181 | DROP VIEW vw_getfoo; 182 | DROP FUNCTION getfoo(int); 183 | CREATE FUNCTION getfoo(int) RETURNS setof foo AS ' 184 | PL.exec("SELECT * FROM foo WHERE fooid = #{args[0]}", nil, "value") do |r| 185 | yield r 186 | end 187 | ' language 'plruby'; 188 | SELECT * FROM getfoo(1) AS t1; 189 | fooid | foosubid | fooname 190 | -------+----------+--------- 191 | 1 | 1 | Joe 192 | 1 | 2 | Ed 193 | (2 rows) 194 | 195 | CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1); 196 | SELECT * FROM vw_getfoo; 197 | fooid | foosubid | fooname 198 | -------+----------+--------- 199 | 1 | 1 | Joe 200 | 1 | 2 | Ed 201 | (2 rows) 202 | 203 | DROP VIEW vw_getfoo; 204 | DROP FUNCTION getfoo(int); 205 | DROP FUNCTION foot(int); 206 | DROP TABLE foo2; 207 | DROP TABLE foo; 208 | CREATE TABLE foorescan (fooid int, foosubid int, fooname text, primary key(fooid,foosubid)); 209 | NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index 'foorescan_pkey' for table 'foorescan' 210 | INSERT INTO foorescan values(5000,1,'abc.5000.1'); 211 | INSERT INTO foorescan values(5001,1,'abc.5001.1'); 212 | INSERT INTO foorescan values(5002,1,'abc.5002.1'); 213 | INSERT INTO foorescan values(5003,1,'abc.5003.1'); 214 | INSERT INTO foorescan values(5004,1,'abc.5004.1'); 215 | INSERT INTO foorescan values(5005,1,'abc.5005.1'); 216 | INSERT INTO foorescan values(5006,1,'abc.5006.1'); 217 | INSERT INTO foorescan values(5007,1,'abc.5007.1'); 218 | INSERT INTO foorescan values(5008,1,'abc.5008.1'); 219 | INSERT INTO foorescan values(5009,1,'abc.5009.1'); 220 | INSERT INTO foorescan values(5000,2,'abc.5000.2'); 221 | INSERT INTO foorescan values(5001,2,'abc.5001.2'); 222 | INSERT INTO foorescan values(5002,2,'abc.5002.2'); 223 | INSERT INTO foorescan values(5003,2,'abc.5003.2'); 224 | INSERT INTO foorescan values(5004,2,'abc.5004.2'); 225 | INSERT INTO foorescan values(5005,2,'abc.5005.2'); 226 | INSERT INTO foorescan values(5006,2,'abc.5006.2'); 227 | INSERT INTO foorescan values(5007,2,'abc.5007.2'); 228 | INSERT INTO foorescan values(5008,2,'abc.5008.2'); 229 | INSERT INTO foorescan values(5009,2,'abc.5009.2'); 230 | INSERT INTO foorescan values(5000,3,'abc.5000.3'); 231 | INSERT INTO foorescan values(5001,3,'abc.5001.3'); 232 | INSERT INTO foorescan values(5002,3,'abc.5002.3'); 233 | INSERT INTO foorescan values(5003,3,'abc.5003.3'); 234 | INSERT INTO foorescan values(5004,3,'abc.5004.3'); 235 | INSERT INTO foorescan values(5005,3,'abc.5005.3'); 236 | INSERT INTO foorescan values(5006,3,'abc.5006.3'); 237 | INSERT INTO foorescan values(5007,3,'abc.5007.3'); 238 | INSERT INTO foorescan values(5008,3,'abc.5008.3'); 239 | INSERT INTO foorescan values(5009,3,'abc.5009.3'); 240 | INSERT INTO foorescan values(5000,4,'abc.5000.4'); 241 | INSERT INTO foorescan values(5001,4,'abc.5001.4'); 242 | INSERT INTO foorescan values(5002,4,'abc.5002.4'); 243 | INSERT INTO foorescan values(5003,4,'abc.5003.4'); 244 | INSERT INTO foorescan values(5004,4,'abc.5004.4'); 245 | INSERT INTO foorescan values(5005,4,'abc.5005.4'); 246 | INSERT INTO foorescan values(5006,4,'abc.5006.4'); 247 | INSERT INTO foorescan values(5007,4,'abc.5007.4'); 248 | INSERT INTO foorescan values(5008,4,'abc.5008.4'); 249 | INSERT INTO foorescan values(5009,4,'abc.5009.4'); 250 | INSERT INTO foorescan values(5000,5,'abc.5000.5'); 251 | INSERT INTO foorescan values(5001,5,'abc.5001.5'); 252 | INSERT INTO foorescan values(5002,5,'abc.5002.5'); 253 | INSERT INTO foorescan values(5003,5,'abc.5003.5'); 254 | INSERT INTO foorescan values(5004,5,'abc.5004.5'); 255 | INSERT INTO foorescan values(5005,5,'abc.5005.5'); 256 | INSERT INTO foorescan values(5006,5,'abc.5006.5'); 257 | INSERT INTO foorescan values(5007,5,'abc.5007.5'); 258 | INSERT INTO foorescan values(5008,5,'abc.5008.5'); 259 | INSERT INTO foorescan values(5009,5,'abc.5009.5'); 260 | CREATE FUNCTION foorescan(int,int) RETURNS setof foorescan AS ' 261 | "SELECT * FROM foorescan WHERE fooid >= #{args[0]} and fooid < #{args[1]}" 262 | ' language 'plruby'; 263 | SELECT * FROM foorescan f WHERE f.fooid IN ( 264 | SELECT fooid FROM foorescan(5002,5004) 265 | ) ORDER BY 1,2; 266 | fooid | foosubid | fooname 267 | -------+----------+------------ 268 | 5002 | 1 | abc.5002.1 269 | 5002 | 2 | abc.5002.2 270 | 5002 | 3 | abc.5002.3 271 | 5002 | 4 | abc.5002.4 272 | 5002 | 5 | abc.5002.5 273 | 5003 | 1 | abc.5003.1 274 | 5003 | 2 | abc.5003.2 275 | 5003 | 3 | abc.5003.3 276 | 5003 | 4 | abc.5003.4 277 | 5003 | 5 | abc.5003.5 278 | (10 rows) 279 | 280 | CREATE VIEW vw_foorescan AS SELECT * FROM foorescan(5002,5004); 281 | SELECT * FROM foorescan f WHERE f.fooid IN ( 282 | SELECT fooid FROM vw_foorescan 283 | ) ORDER BY 1,2; 284 | fooid | foosubid | fooname 285 | -------+----------+------------ 286 | 5002 | 1 | abc.5002.1 287 | 5002 | 2 | abc.5002.2 288 | 5002 | 3 | abc.5002.3 289 | 5002 | 4 | abc.5002.4 290 | 5002 | 5 | abc.5002.5 291 | 5003 | 1 | abc.5003.1 292 | 5003 | 2 | abc.5003.2 293 | 5003 | 3 | abc.5003.3 294 | 5003 | 4 | abc.5003.4 295 | 5003 | 5 | abc.5003.5 296 | (10 rows) 297 | 298 | CREATE TABLE barrescan (fooid int primary key); 299 | NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index 'barrescan_pkey' for table 'barrescan' 300 | INSERT INTO barrescan values(5003); 301 | INSERT INTO barrescan values(5004); 302 | INSERT INTO barrescan values(5005); 303 | INSERT INTO barrescan values(5006); 304 | INSERT INTO barrescan values(5007); 305 | INSERT INTO barrescan values(5008); 306 | CREATE FUNCTION foorescan(int) RETURNS setof foorescan AS ' 307 | "SELECT * FROM foorescan WHERE fooid = #{args[0]}" 308 | ' language 'plruby'; 309 | SELECT f.* FROM barrescan b, foorescan f 310 | WHERE f.fooid = b.fooid AND b.fooid IN ( 311 | SELECT fooid FROM foorescan(b.fooid) 312 | ) ORDER BY 1,2; 313 | fooid | foosubid | fooname 314 | -------+----------+------------ 315 | 5003 | 1 | abc.5003.1 316 | 5003 | 2 | abc.5003.2 317 | 5003 | 3 | abc.5003.3 318 | 5003 | 4 | abc.5003.4 319 | 5003 | 5 | abc.5003.5 320 | 5004 | 1 | abc.5004.1 321 | 5004 | 2 | abc.5004.2 322 | 5004 | 3 | abc.5004.3 323 | 5004 | 4 | abc.5004.4 324 | 5004 | 5 | abc.5004.5 325 | 5005 | 1 | abc.5005.1 326 | 5005 | 2 | abc.5005.2 327 | 5005 | 3 | abc.5005.3 328 | 5005 | 4 | abc.5005.4 329 | 5005 | 5 | abc.5005.5 330 | 5006 | 1 | abc.5006.1 331 | 5006 | 2 | abc.5006.2 332 | 5006 | 3 | abc.5006.3 333 | 5006 | 4 | abc.5006.4 334 | 5006 | 5 | abc.5006.5 335 | 5007 | 1 | abc.5007.1 336 | 5007 | 2 | abc.5007.2 337 | 5007 | 3 | abc.5007.3 338 | 5007 | 4 | abc.5007.4 339 | 5007 | 5 | abc.5007.5 340 | 5008 | 1 | abc.5008.1 341 | 5008 | 2 | abc.5008.2 342 | 5008 | 3 | abc.5008.3 343 | 5008 | 4 | abc.5008.4 344 | 5008 | 5 | abc.5008.5 345 | (30 rows) 346 | 347 | SELECT b.fooid, max(f.foosubid) FROM barrescan b, foorescan f 348 | WHERE f.fooid = b.fooid AND b.fooid IN ( 349 | SELECT fooid FROM foorescan(b.fooid) 350 | ) GROUP BY b.fooid ORDER BY 1,2; 351 | fooid | max 352 | -------+----- 353 | 5003 | 5 354 | 5004 | 5 355 | 5005 | 5 356 | 5006 | 5 357 | 5007 | 5 358 | 5008 | 5 359 | (6 rows) 360 | 361 | CREATE VIEW fooview1 AS SELECT f.* FROM barrescan b, foorescan f 362 | WHERE f.fooid = b.fooid AND b.fooid IN ( 363 | SELECT fooid FROM foorescan(b.fooid) 364 | ) ORDER BY 1,2; 365 | SELECT * FROM fooview1 AS fv WHERE fv.fooid = 5004; 366 | fooid | foosubid | fooname 367 | -------+----------+------------ 368 | 5004 | 1 | abc.5004.1 369 | 5004 | 2 | abc.5004.2 370 | 5004 | 3 | abc.5004.3 371 | 5004 | 4 | abc.5004.4 372 | 5004 | 5 | abc.5004.5 373 | (5 rows) 374 | 375 | CREATE VIEW fooview2 AS SELECT b.fooid, max(f.foosubid) AS maxsubid 376 | FROM barrescan b, foorescan f WHERE f.fooid = b.fooid AND b.fooid IN ( 377 | SELECT fooid FROM foorescan(b.fooid) 378 | ) GROUP BY b.fooid ORDER BY 1,2; 379 | SELECT * FROM fooview2 AS fv WHERE fv.maxsubid = 5; 380 | fooid | maxsubid 381 | -------+---------- 382 | 5003 | 5 383 | 5004 | 5 384 | 5005 | 5 385 | 5006 | 5 386 | 5007 | 5 387 | 5008 | 5 388 | (6 rows) 389 | 390 | DROP VIEW vw_foorescan; 391 | DROP VIEW fooview1; 392 | DROP VIEW fooview2; 393 | DROP FUNCTION foorescan(int,int); 394 | DROP FUNCTION foorescan(int); 395 | DROP TABLE foorescan; 396 | DROP TABLE barrescan; 397 | --------------------------------------------------------------------------------