commit 67630f1d8c74c171495131f33a52a16f179cdd6d Author: Sergey Zhemoitel Date: Thu Sep 18 14:59:27 2014 +0400 Imported from SRPM diff --git a/.abf.yml b/.abf.yml new file mode 100644 index 0000000..2b7ddf1 --- /dev/null +++ b/.abf.yml @@ -0,0 +1,3 @@ +sources: + postgresql-9.3-A4.pdf: 3465825cc5e89429314fcdccfc26a788d57a53e2 + postgresql-9.3.4.tar.bz2: a5b4a63339c26849e8e9b7645436c976e4b40f50 diff --git a/1c_FULL_93-0.23.2 b/1c_FULL_93-0.23.2 new file mode 100644 index 0000000..de6048c --- /dev/null +++ b/1c_FULL_93-0.23.2 @@ -0,0 +1,9120 @@ +diff --git a/contrib/fasttrun/Makefile b/contrib/fasttrun/Makefile +new file mode 100644 +index 0000000..06c47a1 +--- /dev/null ++++ b/contrib/fasttrun/Makefile +@@ -0,0 +1,15 @@ ++MODULE_big = fasttrun ++OBJS = fasttrun.o ++DATA_built = fasttrun.sql ++DOCS = README.fasttrun ++REGRESS = fasttrun ++ ++ifdef USE_PGXS ++PGXS := $(shell pg_config --pgxs) ++include $(PGXS) ++else ++subdir = contrib/fasttrun ++top_builddir = ../.. ++include $(top_builddir)/src/Makefile.global ++include $(top_srcdir)/contrib/contrib-global.mk ++endif +diff --git a/contrib/fasttrun/README.fasttrun b/contrib/fasttrun/README.fasttrun +new file mode 100644 +index 0000000..4b1dfdc +--- /dev/null ++++ b/contrib/fasttrun/README.fasttrun +@@ -0,0 +1,17 @@ ++select fasttruncate('TABLE_NAME'); ++ ++Function truncates the temporary table and doesn't grow ++pg_class size. ++ ++Warning: function isn't transaction safe! ++ ++For tests: ++create or replace function f() returns void as $$ ++begin ++for i in 1..1000 ++loop ++ PERFORM fasttruncate('tt1'); ++end loop; ++end; ++$$ language plpgsql; ++ +diff --git a/contrib/fasttrun/expected/fasttrun.out b/contrib/fasttrun/expected/fasttrun.out +new file mode 100644 +index 0000000..9914e77 +--- /dev/null ++++ b/contrib/fasttrun/expected/fasttrun.out +@@ -0,0 +1,115 @@ ++\set ECHO none ++create table persist ( a int ); ++insert into persist values (1); ++select fasttruncate('persist'); ++ERROR: Relation isn't a temporary table ++insert into persist values (2); ++select * from persist order by a; ++ a ++--- ++ 1 ++ 2 ++(2 rows) ++ ++create temp table temp1 (a int); ++insert into temp1 values (1); ++BEGIN; ++create temp table temp2 (a int); ++insert into temp2 values (1); ++select * from temp1 order by a; ++ a ++--- ++ 1 ++(1 row) ++ ++select * from temp2 order by a; ++ a ++--- ++ 1 ++(1 row) ++ ++insert into temp1 (select * from generate_series(1,10000)); ++insert into temp2 (select * from generate_series(1,11000)); ++analyze temp2; ++select relname, relpages>0, reltuples>0 from pg_class where relname in ('temp1', 'temp2') order by relname; ++ relname | ?column? | ?column? ++---------+----------+---------- ++ temp1 | f | f ++ temp2 | t | t ++(2 rows) ++ ++select fasttruncate('temp1'); ++ fasttruncate ++-------------- ++ ++(1 row) ++ ++select fasttruncate('temp2'); ++ fasttruncate ++-------------- ++ ++(1 row) ++ ++insert into temp1 values (-2); ++insert into temp2 values (-2); ++select * from temp1 order by a; ++ a ++---- ++ -2 ++(1 row) ++ ++select * from temp2 order by a; ++ a ++---- ++ -2 ++(1 row) ++ ++COMMIT; ++select * from temp1 order by a; ++ a ++---- ++ -2 ++(1 row) ++ ++select * from temp2 order by a; ++ a ++---- ++ -2 ++(1 row) ++ ++select relname, relpages>0, reltuples>0 from pg_class where relname in ('temp1', 'temp2') order by relname; ++ relname | ?column? | ?column? ++---------+----------+---------- ++ temp1 | f | f ++ temp2 | f | f ++(2 rows) ++ ++select fasttruncate('temp1'); ++ fasttruncate ++-------------- ++ ++(1 row) ++ ++select fasttruncate('temp2'); ++ fasttruncate ++-------------- ++ ++(1 row) ++ ++select * from temp1 order by a; ++ a ++--- ++(0 rows) ++ ++select * from temp2 order by a; ++ a ++--- ++(0 rows) ++ ++select relname, relpages>0, reltuples>0 from pg_class where relname in ('temp1', 'temp2') order by relname; ++ relname | ?column? | ?column? ++---------+----------+---------- ++ temp1 | f | f ++ temp2 | f | f ++(2 rows) ++ +diff --git a/contrib/fasttrun/fasttrun.c b/contrib/fasttrun/fasttrun.c +new file mode 100644 +index 0000000..e9407e3 +--- /dev/null ++++ b/contrib/fasttrun/fasttrun.c +@@ -0,0 +1,73 @@ ++#include "postgres.h" ++ ++#include "access/genam.h" ++#include "access/heapam.h" ++#include "miscadmin.h" ++#include "storage/lmgr.h" ++#include "storage/bufmgr.h" ++#include "catalog/namespace.h" ++#include "utils/lsyscache.h" ++#include "utils/builtins.h" ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef PG_MODULE_MAGIC ++PG_MODULE_MAGIC; ++#endif ++ ++PG_FUNCTION_INFO_V1(fasttruncate); ++Datum fasttruncate(PG_FUNCTION_ARGS); ++Datum ++fasttruncate(PG_FUNCTION_ARGS) { ++ text *name=PG_GETARG_TEXT_P(0); ++ char *relname; ++ List *relname_list; ++ RangeVar *relvar; ++ Oid relOid; ++ Relation rel; ++ bool makeanalyze = false; ++ ++ relname = palloc( VARSIZE(name) + 1); ++ memcpy(relname, VARDATA(name), VARSIZE(name)-VARHDRSZ); ++ relname[ VARSIZE(name)-VARHDRSZ ] = '\0'; ++ ++ relname_list = stringToQualifiedNameList(relname); ++ relvar = makeRangeVarFromNameList(relname_list); ++ relOid = RangeVarGetRelid(relvar, AccessExclusiveLock, false); ++ ++ if ( get_rel_relkind(relOid) != RELKIND_RELATION ) ++ elog(ERROR,"Relation isn't a ordinary table"); ++ ++ rel = heap_open(relOid, NoLock); ++ ++ if ( !isTempNamespace(get_rel_namespace(relOid)) ) ++ elog(ERROR,"Relation isn't a temporary table"); ++ ++ heap_truncate(list_make1_oid(relOid)); ++ ++ if ( rel->rd_rel->relpages > 0 || rel->rd_rel->reltuples > 0 ) ++ makeanalyze = true; ++ ++ /* ++ * heap_truncate doesn't unlock the table, ++ * so we should unlock it. ++ */ ++ ++ heap_close(rel, AccessExclusiveLock); ++ ++ if ( makeanalyze ) { ++ VacuumStmt *vac = makeNode(VacuumStmt); ++ ++ vac->options = VACOPT_ANALYZE; ++ vac->relation = relvar; ++ ++ vacuum(vac, relOid, false, ++ GetAccessStrategy(BAS_VACUUM), false, false); ++ } ++ ++ PG_RETURN_VOID(); ++} +diff --git a/contrib/fasttrun/fasttrun.sql.in b/contrib/fasttrun/fasttrun.sql.in +new file mode 100644 +index 0000000..0895c77 +--- /dev/null ++++ b/contrib/fasttrun/fasttrun.sql.in +@@ -0,0 +1,8 @@ ++BEGIN; ++ ++ ++CREATE OR REPLACE FUNCTION fasttruncate(text) ++RETURNS void AS 'MODULE_PATHNAME' ++LANGUAGE C RETURNS NULL ON NULL INPUT VOLATILE; ++ ++COMMIT; +diff --git a/contrib/fasttrun/sql/fasttrun.sql b/contrib/fasttrun/sql/fasttrun.sql +new file mode 100644 +index 0000000..73beaf4 +--- /dev/null ++++ b/contrib/fasttrun/sql/fasttrun.sql +@@ -0,0 +1,50 @@ ++\set ECHO none ++\i fasttrun.sql ++\set ECHO all ++ ++create table persist ( a int ); ++insert into persist values (1); ++select fasttruncate('persist'); ++insert into persist values (2); ++select * from persist order by a; ++ ++create temp table temp1 (a int); ++insert into temp1 values (1); ++ ++BEGIN; ++ ++create temp table temp2 (a int); ++insert into temp2 values (1); ++ ++select * from temp1 order by a; ++select * from temp2 order by a; ++ ++insert into temp1 (select * from generate_series(1,10000)); ++insert into temp2 (select * from generate_series(1,11000)); ++ ++analyze temp2; ++select relname, relpages>0, reltuples>0 from pg_class where relname in ('temp1', 'temp2') order by relname; ++ ++select fasttruncate('temp1'); ++select fasttruncate('temp2'); ++ ++insert into temp1 values (-2); ++insert into temp2 values (-2); ++ ++select * from temp1 order by a; ++select * from temp2 order by a; ++ ++COMMIT; ++ ++select * from temp1 order by a; ++select * from temp2 order by a; ++ ++select relname, relpages>0, reltuples>0 from pg_class where relname in ('temp1', 'temp2') order by relname; ++ ++select fasttruncate('temp1'); ++select fasttruncate('temp2'); ++ ++select * from temp1 order by a; ++select * from temp2 order by a; ++ ++select relname, relpages>0, reltuples>0 from pg_class where relname in ('temp1', 'temp2') order by relname; +diff --git a/contrib/fulleq/Makefile b/contrib/fulleq/Makefile +new file mode 100644 +index 0000000..bc8bdd2 +--- /dev/null ++++ b/contrib/fulleq/Makefile +@@ -0,0 +1,32 @@ ++MODULE_big = fulleq ++OBJS = fulleq.o ++DATA_built = fulleq.sql ++DOCS = README.fulleq ++REGRESS = fulleq ++ ++ARGTYPE = bool bytea char name int8 int2 int2vector int4 text \ ++ oid xid cid oidvector float4 float8 abstime reltime macaddr \ ++ inet cidr varchar date time timestamp timestamptz \ ++ interval timetz ++ ++EXTRA_CLEAN = fulleq.sql.in ++ ++ifdef USE_PGXS ++PGXS := $(shell pg_config --pgxs) ++include $(PGXS) ++else ++subdir = contrib/fulleq ++top_builddir = ../.. ++include $(top_builddir)/src/Makefile.global ++include $(top_srcdir)/contrib/contrib-global.mk ++endif ++ ++fulleq.sql.in: fulleq.sql.in.in ++ echo 'BEGIN;' > $@ ++ echo 'SET search_path = public;' >> $@ ++ for type in $(ARGTYPE); \ ++ do \ ++ sed -e "s/ARGTYPE/$$type/g" < $< >> $@; \ ++ done ++ echo 'COMMIT;' >> $@ ++ +diff --git a/contrib/fulleq/README.fulleq b/contrib/fulleq/README.fulleq +new file mode 100644 +index 0000000..a677c49 +--- /dev/null ++++ b/contrib/fulleq/README.fulleq +@@ -0,0 +1,3 @@ ++Introduce operator == which returns true when ++operands are equal or both are nulls. ++ +diff --git a/contrib/fulleq/expected/fulleq.out b/contrib/fulleq/expected/fulleq.out +new file mode 100644 +index 0000000..4842d8c +--- /dev/null ++++ b/contrib/fulleq/expected/fulleq.out +@@ -0,0 +1,61 @@ ++\set ECHO none ++select 4::int == 4; ++ ?column? ++---------- ++ t ++(1 row) ++ ++select 4::int == 5; ++ ?column? ++---------- ++ f ++(1 row) ++ ++select 4::int == NULL; ++ ?column? ++---------- ++ f ++(1 row) ++ ++select NULL::int == 5; ++ ?column? ++---------- ++ f ++(1 row) ++ ++select NULL::int == NULL; ++ ?column? ++---------- ++ t ++(1 row) ++ ++select '4'::text == '4'; ++ ?column? ++---------- ++ t ++(1 row) ++ ++select '4'::text == '5'; ++ ?column? ++---------- ++ f ++(1 row) ++ ++select '4'::text == NULL; ++ ?column? ++---------- ++ f ++(1 row) ++ ++select NULL::text == '5'; ++ ?column? ++---------- ++ f ++(1 row) ++ ++select NULL::text == NULL; ++ ?column? ++---------- ++ t ++(1 row) ++ +diff --git a/contrib/fulleq/fulleq.c b/contrib/fulleq/fulleq.c +new file mode 100644 +index 0000000..8e8e17b +--- /dev/null ++++ b/contrib/fulleq/fulleq.c +@@ -0,0 +1,72 @@ ++#include "postgres.h" ++#include "fmgr.h" ++#include "access/hash.h" ++#include "utils/builtins.h" ++#include "utils/bytea.h" ++#include "utils/int8.h" ++#include "utils/nabstime.h" ++#include "utils/timestamp.h" ++#include "utils/date.h" ++ ++#ifdef PG_MODULE_MAGIC ++PG_MODULE_MAGIC; ++#endif ++ ++#define NULLHASHVALUE (-2147483647) ++ ++#define FULLEQ_FUNC(type, cmpfunc, hashfunc) \ ++PG_FUNCTION_INFO_V1( isfulleq_##type ); \ ++Datum isfulleq_##type(PG_FUNCTION_ARGS); \ ++Datum \ ++isfulleq_##type(PG_FUNCTION_ARGS) { \ ++ if ( PG_ARGISNULL(0) && PG_ARGISNULL(1) ) \ ++ PG_RETURN_BOOL(true); \ ++ else if ( PG_ARGISNULL(0) || PG_ARGISNULL(1) ) \ ++ PG_RETURN_BOOL(false); \ ++ \ ++ PG_RETURN_DATUM( DirectFunctionCall2( cmpfunc, \ ++ PG_GETARG_DATUM(0), \ ++ PG_GETARG_DATUM(1) \ ++ ) ); \ ++} \ ++ \ ++PG_FUNCTION_INFO_V1( fullhash_##type ); \ ++Datum fullhash_##type(PG_FUNCTION_ARGS); \ ++Datum \ ++fullhash_##type(PG_FUNCTION_ARGS) { \ ++ if ( PG_ARGISNULL(0) ) \ ++ PG_RETURN_INT32(NULLHASHVALUE); \ ++ \ ++ PG_RETURN_DATUM( DirectFunctionCall1( hashfunc, \ ++ PG_GETARG_DATUM(0) \ ++ ) ); \ ++} ++ ++ ++FULLEQ_FUNC( bool , booleq , hashchar ); ++FULLEQ_FUNC( bytea , byteaeq , hashvarlena ); ++FULLEQ_FUNC( char , chareq , hashchar ); ++FULLEQ_FUNC( name , nameeq , hashname ); ++FULLEQ_FUNC( int8 , int8eq , hashint8 ); ++FULLEQ_FUNC( int2 , int2eq , hashint2 ); ++FULLEQ_FUNC( int2vector , int2vectoreq , hashint2vector ); ++FULLEQ_FUNC( int4 , int4eq , hashint4 ); ++FULLEQ_FUNC( text , texteq , hashtext ); ++FULLEQ_FUNC( oid , oideq , hashoid ); ++FULLEQ_FUNC( xid , xideq , hashint4 ); ++FULLEQ_FUNC( cid , cideq , hashint4 ); ++FULLEQ_FUNC( oidvector , oidvectoreq , hashoidvector ); ++FULLEQ_FUNC( float4 , float4eq , hashfloat4 ); ++FULLEQ_FUNC( float8 , float8eq , hashfloat8 ); ++FULLEQ_FUNC( abstime , abstimeeq , hashint4 ); ++FULLEQ_FUNC( reltime , reltimeeq , hashint4 ); ++FULLEQ_FUNC( macaddr , macaddr_eq , hashmacaddr ); ++FULLEQ_FUNC( inet , network_eq , hashinet ); ++FULLEQ_FUNC( cidr , network_eq , hashinet ); ++FULLEQ_FUNC( varchar , texteq , hashtext ); ++FULLEQ_FUNC( date , date_eq , hashint4 ); ++FULLEQ_FUNC( time , time_eq , hashfloat8 ); ++FULLEQ_FUNC( timestamp , timestamp_eq , hashfloat8 ); ++FULLEQ_FUNC( timestamptz , timestamp_eq , hashfloat8 ); ++FULLEQ_FUNC( interval , interval_eq , interval_hash ); ++FULLEQ_FUNC( timetz , timetz_eq , timetz_hash ); +diff --git a/contrib/fulleq/fulleq.sql.in.in b/contrib/fulleq/fulleq.sql.in.in +new file mode 100644 +index 0000000..55e980e +--- /dev/null ++++ b/contrib/fulleq/fulleq.sql.in.in +@@ -0,0 +1,26 @@ ++-- For ARGTYPE ++ ++CREATE OR REPLACE FUNCTION isfulleq_ARGTYPE(ARGTYPE, ARGTYPE) ++RETURNS bool AS 'MODULE_PATHNAME' ++LANGUAGE C CALLED ON NULL INPUT IMMUTABLE; ++ ++CREATE OR REPLACE FUNCTION fullhash_ARGTYPE(ARGTYPE) ++RETURNS int4 AS 'MODULE_PATHNAME' ++LANGUAGE C CALLED ON NULL INPUT IMMUTABLE; ++ ++ ++CREATE OPERATOR == ( ++ LEFTARG = ARGTYPE, ++ RIGHTARG = ARGTYPE, ++ PROCEDURE = isfulleq_ARGTYPE, ++ COMMUTATOR = '==', ++ RESTRICT = eqsel, ++ JOIN = eqjoinsel, ++ HASHES ++); ++ ++CREATE OPERATOR CLASS ARGTYPE_fill_ops ++ FOR TYPE ARGTYPE USING hash AS ++ OPERATOR 1 ==, ++ FUNCTION 1 fullhash_ARGTYPE(ARGTYPE); ++ +diff --git a/contrib/fulleq/sql/fulleq.sql b/contrib/fulleq/sql/fulleq.sql +new file mode 100644 +index 0000000..02b192c +--- /dev/null ++++ b/contrib/fulleq/sql/fulleq.sql +@@ -0,0 +1,16 @@ ++\set ECHO none ++\i fulleq.sql ++\set ECHO all ++ ++select 4::int == 4; ++select 4::int == 5; ++select 4::int == NULL; ++select NULL::int == 5; ++select NULL::int == NULL; ++ ++select '4'::text == '4'; ++select '4'::text == '5'; ++select '4'::text == NULL; ++select NULL::text == '5'; ++select NULL::text == NULL; ++ +diff --git a/contrib/mchar/Changes b/contrib/mchar/Changes +new file mode 100644 +index 0000000..b597ee7 +--- /dev/null ++++ b/contrib/mchar/Changes +@@ -0,0 +1,19 @@ ++0.17 add == operation: ++ a == b => ( a = b or a is null and b is null ) ++0.16 fix pg_dump - now mchar in pg_catalog scheme, not public ++ fix bug in mvarchar_substr() ++0.15 add upper()/lower() ++0.14 Add ESCAPE for LIKE, SIMILAR TO [ESCAPE], POSIX regexp ++0.13 Outer binary format is now different from ++ inner: it's just a UTF-16 string ++0.12 Fix copy binary ++0.11 Force UTF-8 convertor if server_encoding='UTF8' ++0.10 add (mchar|mvarchar)_(send|recv) functions to ++ allow binary copying. Note: that functions ++ don't recode values. ++0.9 index support for like, improve recoding functions ++0.8 initial suport for like optimizioation with index: ++ still thres no algo to find the nearest greater string ++0.7 hash indexes and enable a hash joins ++0.6 implicit casting mchar-mvarchar ++ cross type comparison operations +diff --git a/contrib/mchar/Makefile b/contrib/mchar/Makefile +new file mode 100644 +index 0000000..27302df +--- /dev/null ++++ b/contrib/mchar/Makefile +@@ -0,0 +1,27 @@ ++MODULE_big = mchar ++OBJS = mchar_io.o mchar_proc.o mchar_op.o mchar_recode.o \ ++ mchar_like.o ++DATA_built = mchar.sql ++DATA = uninstall_mchar.sql ++DOCS = README.mchar ++REGRESS = init mchar mvarchar mm like compat ++ ++PG_CPPFLAGS=-I/usr/local/include ++ ++ifdef USE_PGXS ++PGXS := $(shell pg_config --pgxs) ++include $(PGXS) ++else ++subdir = contrib/mchar ++top_builddir = ../.. ++include $(top_builddir)/src/Makefile.global ++include $(top_srcdir)/contrib/contrib-global.mk ++endif ++ ++ifeq ($(PORTNAME),win32) ++ICUNAME=icuin ++else ++ICUNAME=icui18n ++endif ++ ++SHLIB_LINK += -L/usr/local/lib -licuuc -l$(ICUNAME) -Wl,-rpath,'$$ORIGIN' +diff --git a/contrib/mchar/README.mchar b/contrib/mchar/README.mchar +new file mode 100644 +index 0000000..479a7d1 +--- /dev/null ++++ b/contrib/mchar/README.mchar +@@ -0,0 +1,20 @@ ++MCHAR & VARCHAR ++ type modifier ++ length() ++ substr(str, pos[, length]) ++ || - concatenation with any (mchar,mvarchar) arguments ++ < <= = >= > - case-insensitive comparisons (libICU) ++ &< &<= &= &>= &> - case-sensitive comparisons (libICU) ++ implicit casting mchar<->mvarchar ++ B-tree and hash index ++ LIKE [ESCAPE] ++ SIMILAR TO [ESCAPE] ++ ~ (POSIX regexp) ++ index support for LIKE ++ ++ ++Authors: ++ Oleg Bartunov ++ Teodor Sigaev ++ ++ +diff --git a/contrib/mchar/expected/compat.out b/contrib/mchar/expected/compat.out +new file mode 100644 +index 0000000..480a286 +--- /dev/null ++++ b/contrib/mchar/expected/compat.out +@@ -0,0 +1,66 @@ ++--- table based checks ++select '<' || ch || '>', '<' || vch || '>' from chvch; ++ ?column? | ?column? ++----------------+-------------- ++ | ++ | ++ <1 space > | <1 space > ++(3 rows) ++ ++select * from chvch where vch = 'One space'; ++ ch | vch ++--------------+------------ ++ One space | One space ++(1 row) ++ ++select * from chvch where vch = 'One space '; ++ ch | vch ++--------------+------------ ++ One space | One space ++(1 row) ++ ++select * from ch where chcol = 'abcd' order by chcol; ++ chcol ++---------------------------------- ++ abcd ++ AbcD ++(2 rows) ++ ++select * from ch t1 join ch t2 on t1.chcol = t2.chcol order by t1.chcol, t2.chcol; ++ chcol | chcol ++----------------------------------+---------------------------------- ++ abcd | AbcD ++ abcd | abcd ++ AbcD | AbcD ++ AbcD | abcd ++ abcz | abcz ++ defg | dEfg ++ defg | defg ++ dEfg | dEfg ++ dEfg | defg ++ ee | Ee ++ ee | ee ++ Ee | Ee ++ Ee | ee ++(13 rows) ++ ++select * from ch where chcol > 'abcd' and chcol<'ee'; ++ chcol ++---------------------------------- ++ abcz ++ defg ++ dEfg ++(3 rows) ++ ++select * from ch order by chcol; ++ chcol ++---------------------------------- ++ abcd ++ AbcD ++ abcz ++ defg ++ dEfg ++ ee ++ Ee ++(7 rows) ++ +diff --git a/contrib/mchar/expected/init.out b/contrib/mchar/expected/init.out +new file mode 100644 +index 0000000..19aa916 +--- /dev/null ++++ b/contrib/mchar/expected/init.out +@@ -0,0 +1,32 @@ ++-- ++-- first, define the datatype. Turn off echoing so that expected file ++-- does not depend on contents of mchar.sql. ++-- ++\set ECHO none ++psql:mchar.sql:20: NOTICE: type "mchar" is not yet defined ++DETAIL: Creating a shell type definition. ++psql:mchar.sql:25: NOTICE: argument type mchar is only a shell ++psql:mchar.sql:30: NOTICE: argument type mchar is only a shell ++psql:mchar.sql:35: NOTICE: return type mchar is only a shell ++psql:mchar.sql:59: NOTICE: type "mvarchar" is not yet defined ++DETAIL: Creating a shell type definition. ++psql:mchar.sql:64: NOTICE: argument type mvarchar is only a shell ++psql:mchar.sql:69: NOTICE: argument type mvarchar is only a shell ++psql:mchar.sql:74: NOTICE: return type mvarchar is only a shell ++create table ch ( ++ chcol mchar(32) ++) without oids; ++insert into ch values('abcd'); ++insert into ch values('AbcD'); ++insert into ch values('abcz'); ++insert into ch values('defg'); ++insert into ch values('dEfg'); ++insert into ch values('ee'); ++insert into ch values('Ee'); ++create table chvch ( ++ ch mchar(12), ++ vch mvarchar(12) ++) without oids; ++insert into chvch values('No spaces', 'No spaces'); ++insert into chvch values('One space ', 'One space '); ++insert into chvch values('1 space', '1 space '); +diff --git a/contrib/mchar/expected/like.out b/contrib/mchar/expected/like.out +new file mode 100644 +index 0000000..3a57082 +--- /dev/null ++++ b/contrib/mchar/expected/like.out +@@ -0,0 +1,791 @@ ++-- simplest examples ++-- E061-04 like predicate ++SELECT 'hawkeye'::mchar LIKE 'h%' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'hawkeye'::mchar NOT LIKE 'h%' AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'hawkeye'::mchar LIKE 'H%' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'hawkeye'::mchar NOT LIKE 'H%' AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'hawkeye'::mchar LIKE 'indio%' AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'hawkeye'::mchar NOT LIKE 'indio%' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'hawkeye'::mchar LIKE 'h%eye' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'hawkeye'::mchar NOT LIKE 'h%eye' AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'indio'::mchar LIKE '_ndio' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'indio'::mchar NOT LIKE '_ndio' AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'indio'::mchar LIKE 'in__o' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'indio'::mchar NOT LIKE 'in__o' AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'indio'::mchar LIKE 'in_o' AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'indio'::mchar NOT LIKE 'in_o' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'hawkeye'::mvarchar LIKE 'h%' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'hawkeye'::mvarchar NOT LIKE 'h%' AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'hawkeye'::mvarchar LIKE 'H%' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'hawkeye'::mvarchar NOT LIKE 'H%' AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'hawkeye'::mvarchar LIKE 'indio%' AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'hawkeye'::mvarchar NOT LIKE 'indio%' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'hawkeye'::mvarchar LIKE 'h%eye' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'hawkeye'::mvarchar NOT LIKE 'h%eye' AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'indio'::mvarchar LIKE '_ndio' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'indio'::mvarchar NOT LIKE '_ndio' AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'indio'::mvarchar LIKE 'in__o' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'indio'::mvarchar NOT LIKE 'in__o' AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'indio'::mvarchar LIKE 'in_o' AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'indio'::mvarchar NOT LIKE 'in_o' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++-- unused escape character ++SELECT 'hawkeye'::mchar LIKE 'h%'::mchar ESCAPE '#' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'hawkeye'::mchar NOT LIKE 'h%'::mchar ESCAPE '#' AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'indio'::mchar LIKE 'ind_o'::mchar ESCAPE '$' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'indio'::mchar NOT LIKE 'ind_o'::mchar ESCAPE '$' AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++-- escape character ++-- E061-05 like predicate with escape clause ++SELECT 'h%'::mchar LIKE 'h#%'::mchar ESCAPE '#' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'h%'::mchar NOT LIKE 'h#%'::mchar ESCAPE '#' AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'h%wkeye'::mchar LIKE 'h#%'::mchar ESCAPE '#' AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'h%wkeye'::mchar NOT LIKE 'h#%'::mchar ESCAPE '#' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'h%wkeye'::mchar LIKE 'h#%%'::mchar ESCAPE '#' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'h%wkeye'::mchar NOT LIKE 'h#%%'::mchar ESCAPE '#' AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'h%awkeye'::mchar LIKE 'h#%a%k%e'::mchar ESCAPE '#' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'h%awkeye'::mchar NOT LIKE 'h#%a%k%e'::mchar ESCAPE '#' AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'indio'::mchar LIKE '_ndio'::mchar ESCAPE '$' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'indio'::mchar NOT LIKE '_ndio'::mchar ESCAPE '$' AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'i_dio'::mchar LIKE 'i$_d_o'::mchar ESCAPE '$' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'i_dio'::mchar NOT LIKE 'i$_d_o'::mchar ESCAPE '$' AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'i_dio'::mchar LIKE 'i$_nd_o'::mchar ESCAPE '$' AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'i_dio'::mchar NOT LIKE 'i$_nd_o'::mchar ESCAPE '$' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'i_dio'::mchar LIKE 'i$_d%o'::mchar ESCAPE '$' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'i_dio'::mchar NOT LIKE 'i$_d%o'::mchar ESCAPE '$' AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++-- escape character same as pattern character ++SELECT 'maca'::mchar LIKE 'm%aca' ESCAPE '%'::mchar AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'maca'::mchar NOT LIKE 'm%aca' ESCAPE '%'::mchar AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'ma%a'::mchar LIKE 'm%a%%a' ESCAPE '%'::mchar AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'ma%a'::mchar NOT LIKE 'm%a%%a' ESCAPE '%'::mchar AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'bear'::mchar LIKE 'b_ear' ESCAPE '_'::mchar AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'bear'::mchar NOT LIKE 'b_ear'::mchar ESCAPE '_' AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'be_r'::mchar LIKE 'b_e__r' ESCAPE '_'::mchar AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'be_r'::mchar NOT LIKE 'b_e__r' ESCAPE '_'::mchar AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'be_r'::mchar LIKE '__e__r' ESCAPE '_'::mchar AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'be_r'::mchar NOT LIKE '__e__r'::mchar ESCAPE '_' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++-- unused escape character ++SELECT 'hawkeye'::mvarchar LIKE 'h%'::mvarchar ESCAPE '#' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'hawkeye'::mvarchar NOT LIKE 'h%'::mvarchar ESCAPE '#' AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'indio'::mvarchar LIKE 'ind_o'::mvarchar ESCAPE '$' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'indio'::mvarchar NOT LIKE 'ind_o'::mvarchar ESCAPE '$' AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++-- escape character ++-- E061-05 like predicate with escape clause ++SELECT 'h%'::mvarchar LIKE 'h#%'::mvarchar ESCAPE '#' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'h%'::mvarchar NOT LIKE 'h#%'::mvarchar ESCAPE '#' AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'h%wkeye'::mvarchar LIKE 'h#%'::mvarchar ESCAPE '#' AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'h%wkeye'::mvarchar NOT LIKE 'h#%'::mvarchar ESCAPE '#' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'h%wkeye'::mvarchar LIKE 'h#%%'::mvarchar ESCAPE '#' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'h%wkeye'::mvarchar NOT LIKE 'h#%%'::mvarchar ESCAPE '#' AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'h%awkeye'::mvarchar LIKE 'h#%a%k%e'::mvarchar ESCAPE '#' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'h%awkeye'::mvarchar NOT LIKE 'h#%a%k%e'::mvarchar ESCAPE '#' AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'indio'::mvarchar LIKE '_ndio'::mvarchar ESCAPE '$' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'indio'::mvarchar NOT LIKE '_ndio'::mvarchar ESCAPE '$' AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'i_dio'::mvarchar LIKE 'i$_d_o'::mvarchar ESCAPE '$' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'i_dio'::mvarchar NOT LIKE 'i$_d_o'::mvarchar ESCAPE '$' AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'i_dio'::mvarchar LIKE 'i$_nd_o'::mvarchar ESCAPE '$' AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'i_dio'::mvarchar NOT LIKE 'i$_nd_o'::mvarchar ESCAPE '$' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'i_dio'::mvarchar LIKE 'i$_d%o'::mvarchar ESCAPE '$' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'i_dio'::mvarchar NOT LIKE 'i$_d%o'::mvarchar ESCAPE '$' AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++-- escape character same as pattern character ++SELECT 'maca'::mvarchar LIKE 'm%aca' ESCAPE '%'::mvarchar AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'maca'::mvarchar NOT LIKE 'm%aca' ESCAPE '%'::mvarchar AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'ma%a'::mvarchar LIKE 'm%a%%a' ESCAPE '%'::mvarchar AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'ma%a'::mvarchar NOT LIKE 'm%a%%a' ESCAPE '%'::mvarchar AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'bear'::mvarchar LIKE 'b_ear' ESCAPE '_'::mvarchar AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'bear'::mvarchar NOT LIKE 'b_ear'::mvarchar ESCAPE '_' AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'be_r'::mvarchar LIKE 'b_e__r' ESCAPE '_'::mvarchar AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'be_r'::mvarchar NOT LIKE 'b_e__r' ESCAPE '_'::mvarchar AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'be_r'::mvarchar LIKE '__e__r' ESCAPE '_'::mvarchar AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'be_r'::mvarchar NOT LIKE '__e__r'::mvarchar ESCAPE '_' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++-- similar to ++SELECT 'abc'::mchar SIMILAR TO 'abc'::mchar AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'abc'::mchar SIMILAR TO 'a'::mchar AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'abc'::mchar SIMILAR TO '%(b|d)%'::mchar AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'abc'::mchar SIMILAR TO '(b|c)%'::mchar AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'h%'::mchar SIMILAR TO 'h#%'::mchar AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'h%'::mchar SIMILAR TO 'h#%'::mchar ESCAPE '#' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'abc'::mvarchar SIMILAR TO 'abc'::mvarchar AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'abc'::mvarchar SIMILAR TO 'a'::mvarchar AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'abc'::mvarchar SIMILAR TO '%(b|d)%'::mvarchar AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++SELECT 'abc'::mvarchar SIMILAR TO '(b|c)%'::mvarchar AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'h%'::mvarchar SIMILAR TO 'h#%'::mvarchar AS "false"; ++ false ++------- ++ f ++(1 row) ++ ++SELECT 'h%'::mvarchar SIMILAR TO 'h#%'::mvarchar ESCAPE '#' AS "true"; ++ true ++------ ++ t ++(1 row) ++ ++-- index support ++SELECT * from ch where chcol like 'aB_d' order by chcol using &<; ++ chcol ++---------------------------------- ++ AbcD ++ abcd ++(2 rows) ++ ++SELECT * from ch where chcol like 'aB%d' order by chcol using &<; ++ chcol ++---------------------------------- ++ AbcD ++ abcd ++(2 rows) ++ ++SELECT * from ch where chcol like 'aB%' order by chcol using &<; ++ chcol ++---------------------------------- ++ AbcD ++ abcd ++ abcz ++(3 rows) ++ ++SELECT * from ch where chcol like '%BC%' order by chcol using &<; ++ chcol ++---------------------------------- ++ AbcD ++ abcd ++ abcz ++(3 rows) ++ ++set enable_seqscan = off; ++SELECT * from ch where chcol like 'aB_d' order by chcol using &<; ++ chcol ++---------------------------------- ++ AbcD ++ abcd ++(2 rows) ++ ++SELECT * from ch where chcol like 'aB%d' order by chcol using &<; ++ chcol ++---------------------------------- ++ AbcD ++ abcd ++(2 rows) ++ ++SELECT * from ch where chcol like 'aB%' order by chcol using &<; ++ chcol ++---------------------------------- ++ AbcD ++ abcd ++ abcz ++(3 rows) ++ ++SELECT * from ch where chcol like '%BC%' order by chcol using &<; ++ chcol ++---------------------------------- ++ AbcD ++ abcd ++ abcz ++(3 rows) ++ ++set enable_seqscan = on; ++create table testt (f1 mchar(10)); ++insert into testt values ('Abc-000001'); ++insert into testt values ('Abc-000002'); ++insert into testt values ('0000000001'); ++insert into testt values ('0000000002'); ++select f1 from testt where f1::mvarchar like E'Abc\\-%'::mvarchar; ++ f1 ++------------ ++ Abc-000001 ++ Abc-000002 ++(2 rows) ++ ++select * from testt where f1::mchar like E'Abc\\-%'::mchar; ++ f1 ++------------ ++ Abc-000001 ++ Abc-000002 ++(2 rows) ++ ++create index testindex on testt(f1); ++set enable_seqscan=off; ++select f1 from testt where f1::mvarchar like E'Abc\\-%'::mvarchar; ++ f1 ++------------ ++ Abc-000001 ++ Abc-000002 ++(2 rows) ++ ++select * from testt where f1::mchar like E'Abc\\-%'::mchar; ++ f1 ++------------ ++ Abc-000001 ++ Abc-000002 ++(2 rows) ++ ++set enable_seqscan = on; ++drop table testt; ++create table testt (f1 mvarchar(10)); ++insert into testt values ('Abc-000001'); ++insert into testt values ('Abc-000002'); ++insert into testt values ('0000000001'); ++insert into testt values ('0000000002'); ++select f1 from testt where f1::mvarchar like E'Abc\\-%'::mvarchar; ++ f1 ++------------ ++ Abc-000001 ++ Abc-000002 ++(2 rows) ++ ++select * from testt where f1::mchar like E'Abc\\-%'::mchar; ++ f1 ++------------ ++ Abc-000001 ++ Abc-000002 ++(2 rows) ++ ++select * from testt where f1::mchar like E'Abc\\- %'::mchar; ++ f1 ++------------ ++ Abc-000001 ++ Abc-000002 ++(2 rows) ++ ++select * from testt where f1::mchar like E' %'::mchar; ++ f1 ++------------ ++ Abc-000001 ++ Abc-000002 ++ 0000000001 ++ 0000000002 ++(4 rows) ++ ++create index testindex on testt(f1); ++set enable_seqscan=off; ++select f1 from testt where f1::mvarchar like E'Abc\\-%'::mvarchar; ++ f1 ++------------ ++ Abc-000001 ++ Abc-000002 ++(2 rows) ++ ++select * from testt where f1::mchar like E'Abc\\-%'::mchar; ++ f1 ++------------ ++ Abc-000001 ++ Abc-000002 ++(2 rows) ++ ++select * from testt where f1::mchar like E'Abc\\- %'::mchar; ++ f1 ++------------ ++ Abc-000001 ++ Abc-000002 ++(2 rows) ++ ++select * from testt where f1::mchar like E' %'::mchar; ++ f1 ++------------ ++ 0000000001 ++ 0000000002 ++ Abc-000001 ++ Abc-000002 ++(4 rows) ++ ++set enable_seqscan = on; ++drop table testt; ++CREATE TABLE test ( code mchar(5) NOT NULL ); ++insert into test values('1111 '); ++insert into test values('111 '); ++insert into test values('11 '); ++insert into test values('1 '); ++SELECT * FROM test WHERE code LIKE ('% '); ++ code ++------- ++ 1 ++(1 row) ++ +diff --git a/contrib/mchar/expected/mchar.out b/contrib/mchar/expected/mchar.out +new file mode 100644 +index 0000000..c587424 +--- /dev/null ++++ b/contrib/mchar/expected/mchar.out +@@ -0,0 +1,363 @@ ++-- I/O tests ++select '1'::mchar; ++ mchar ++------- ++ 1 ++(1 row) ++ ++select '2 '::mchar; ++ mchar ++------- ++ 2 ++(1 row) ++ ++select '10 '::mchar; ++ mchar ++------- ++ 10 ++(1 row) ++ ++select '1'::mchar(2); ++ mchar ++------- ++ 1 ++(1 row) ++ ++select '2 '::mchar(2); ++ mchar ++------- ++ 2 ++(1 row) ++ ++select '3 '::mchar(2); ++ mchar ++------- ++ 3 ++(1 row) ++ ++select '10 '::mchar(2); ++ mchar ++------- ++ 10 ++(1 row) ++ ++select ' '::mchar(10); ++ mchar ++------------ ++ ++(1 row) ++ ++select ' '::mchar; ++ mchar ++------- ++ ++(1 row) ++ ++-- operations & functions ++select length('1'::mchar); ++ length ++-------- ++ 1 ++(1 row) ++ ++select length('2 '::mchar); ++ length ++-------- ++ 1 ++(1 row) ++ ++select length('10 '::mchar); ++ length ++-------- ++ 2 ++(1 row) ++ ++select length('1'::mchar(2)); ++ length ++-------- ++ 1 ++(1 row) ++ ++select length('2 '::mchar(2)); ++ length ++-------- ++ 1 ++(1 row) ++ ++select length('3 '::mchar(2)); ++ length ++-------- ++ 1 ++(1 row) ++ ++select length('10 '::mchar(2)); ++ length ++-------- ++ 2 ++(1 row) ++ ++select length(' '::mchar(10)); ++ length ++-------- ++ 0 ++(1 row) ++ ++select length(' '::mchar); ++ length ++-------- ++ 0 ++(1 row) ++ ++select 'asd'::mchar(10) || '>'::mchar(10); ++ ?column? ++---------------------- ++ asd > ++(1 row) ++ ++select length('asd'::mchar(10) || '>'::mchar(10)); ++ length ++-------- ++ 11 ++(1 row) ++ ++select 'asd'::mchar(2) || '>'::mchar(10); ++ ?column? ++-------------- ++ as> ++(1 row) ++ ++select length('asd'::mchar(2) || '>'::mchar(10)); ++ length ++-------- ++ 3 ++(1 row) ++ ++-- Comparisons ++select 'asdf'::mchar = 'aSdf'::mchar; ++ ?column? ++---------- ++ t ++(1 row) ++ ++select 'asdf'::mchar = 'aSdf '::mchar; ++ ?column? ++---------- ++ t ++(1 row) ++ ++select 'asdf'::mchar = 'aSdf 1'::mchar(4); ++ ?column? ++---------- ++ t ++(1 row) ++ ++select 'asdf'::mchar = 'aSdf 1'::mchar(5); ++ ?column? ++---------- ++ t ++(1 row) ++ ++select 'asdf'::mchar = 'aSdf 1'::mchar(6); ++ ?column? ++---------- ++ f ++(1 row) ++ ++select 'asdf'::mchar(3) = 'aSdf 1'::mchar(5); ++ ?column? ++---------- ++ f ++(1 row) ++ ++select 'asdf'::mchar(3) = 'aSdf 1'::mchar(3); ++ ?column? ++---------- ++ t ++(1 row) ++ ++select 'asdf'::mchar < 'aSdf'::mchar; ++ ?column? ++---------- ++ f ++(1 row) ++ ++select 'asdf'::mchar < 'aSdf '::mchar; ++ ?column? ++---------- ++ f ++(1 row) ++ ++select 'asdf'::mchar < 'aSdf 1'::mchar(4); ++ ?column? ++---------- ++ f ++(1 row) ++ ++select 'asdf'::mchar < 'aSdf 1'::mchar(5); ++ ?column? ++---------- ++ f ++(1 row) ++ ++select 'asdf'::mchar < 'aSdf 1'::mchar(6); ++ ?column? ++---------- ++ t ++(1 row) ++ ++select 'asdf'::mchar <= 'aSdf'::mchar; ++ ?column? ++---------- ++ t ++(1 row) ++ ++select 'asdf'::mchar <= 'aSdf '::mchar; ++ ?column? ++---------- ++ t ++(1 row) ++ ++select 'asdf'::mchar <= 'aSdf 1'::mchar(4); ++ ?column? ++---------- ++ t ++(1 row) ++ ++select 'asdf'::mchar <= 'aSdf 1'::mchar(5); ++ ?column? ++---------- ++ t ++(1 row) ++ ++select 'asdf'::mchar <= 'aSdf 1'::mchar(6); ++ ?column? ++---------- ++ t ++(1 row) ++ ++select 'asdf'::mchar >= 'aSdf'::mchar; ++ ?column? ++---------- ++ t ++(1 row) ++ ++select 'asdf'::mchar >= 'aSdf '::mchar; ++ ?column? ++---------- ++ t ++(1 row) ++ ++select 'asdf'::mchar >= 'aSdf 1'::mchar(4); ++ ?column? ++---------- ++ t ++(1 row) ++ ++select 'asdf'::mchar >= 'aSdf 1'::mchar(5); ++ ?column? ++---------- ++ t ++(1 row) ++ ++select 'asdf'::mchar >= 'aSdf 1'::mchar(6); ++ ?column? ++---------- ++ f ++(1 row) ++ ++select 'asdf'::mchar > 'aSdf'::mchar; ++ ?column? ++---------- ++ f ++(1 row) ++ ++select 'asdf'::mchar > 'aSdf '::mchar; ++ ?column? ++---------- ++ f ++(1 row) ++ ++select 'asdf'::mchar > 'aSdf 1'::mchar(4); ++ ?column? ++---------- ++ f ++(1 row) ++ ++select 'asdf'::mchar > 'aSdf 1'::mchar(5); ++ ?column? ++---------- ++ f ++(1 row) ++ ++select 'asdf'::mchar > 'aSdf 1'::mchar(6); ++ ?column? ++---------- ++ f ++(1 row) ++ ++select max(ch) from chvch; ++ max ++-------------- ++ One space ++(1 row) ++ ++select min(ch) from chvch; ++ min ++-------------- ++ 1 space ++(1 row) ++ ++select substr('1234567890'::mchar, 3) = '34567890' as "34567890"; ++ 34567890 ++---------- ++ f ++(1 row) ++ ++select substr('1234567890'::mchar, 4, 3) = '456' as "456"; ++ 456 ++----- ++ t ++(1 row) ++ ++select lower('asdfASDF'::mchar); ++ lower ++---------- ++ asdfasdf ++(1 row) ++ ++select upper('asdfASDF'::mchar); ++ upper ++---------- ++ ASDFASDF ++(1 row) ++ ++select 'asd'::mchar == 'aSd'::mchar; ++ ?column? ++---------- ++ t ++(1 row) ++ ++select 'asd'::mchar == 'aCd'::mchar; ++ ?column? ++---------- ++ f ++(1 row) ++ ++select 'asd'::mchar == NULL; ++ ?column? ++---------- ++ f ++(1 row) ++ ++select NULL == 'aCd'::mchar; ++ ?column? ++---------- ++ f ++(1 row) ++ ++select NULL::mchar == NULL; ++ ?column? ++---------- ++ t ++(1 row) ++ +diff --git a/contrib/mchar/expected/mm.out b/contrib/mchar/expected/mm.out +new file mode 100644 +index 0000000..fa2e924 +--- /dev/null ++++ b/contrib/mchar/expected/mm.out +@@ -0,0 +1,805 @@ ++select 'asd'::mchar::mvarchar; ++ mvarchar ++---------- ++ asd ++(1 row) ++ ++select 'asd '::mchar::mvarchar; ++ mvarchar ++---------- ++ asd ++(1 row) ++ ++select 'asd'::mchar(2)::mvarchar; ++ mvarchar ++---------- ++ as ++(1 row) ++ ++select 'asd '::mchar(2)::mvarchar; ++ mvarchar ++---------- ++ as ++(1 row) ++ ++select 'asd'::mchar(5)::mvarchar; ++ mvarchar ++---------- ++ asd ++(1 row) ++ ++select 'asd '::mchar(5)::mvarchar; ++ mvarchar ++---------- ++ asd ++(1 row) ++ ++select 'asd'::mchar::mvarchar(2); ++ mvarchar ++---------- ++ as ++(1 row) ++ ++select 'asd '::mchar::mvarchar(2); ++ mvarchar ++---------- ++ as ++(1 row) ++ ++select 'asd'::mchar(2)::mvarchar(2); ++ mvarchar ++---------- ++ as ++(1 row) ++ ++select 'asd '::mchar(2)::mvarchar(2); ++ mvarchar ++---------- ++ as ++(1 row) ++ ++select 'asd'::mchar(5)::mvarchar(2); ++ mvarchar ++---------- ++ as ++(1 row) ++ ++select 'asd '::mchar(5)::mvarchar(2); ++ mvarchar ++---------- ++ as ++(1 row) ++ ++select 'asd'::mchar::mvarchar(5); ++ mvarchar ++---------- ++ asd ++(1 row) ++ ++select 'asd '::mchar::mvarchar(5); ++ mvarchar ++---------- ++ asd ++(1 row) ++ ++select 'asd'::mchar(2)::mvarchar(5); ++ mvarchar ++---------- ++ as ++(1 row) ++ ++select 'asd '::mchar(2)::mvarchar(5); ++ mvarchar ++---------- ++ as ++(1 row) ++ ++select 'asd'::mchar(5)::mvarchar(5); ++ mvarchar ++---------- ++ asd ++(1 row) ++ ++select 'asd '::mchar(5)::mvarchar(5); ++ mvarchar ++---------- ++ asd ++(1 row) ++ ++select 'asd'::mvarchar::mchar; ++ mchar ++------- ++ asd ++(1 row) ++ ++select 'asd '::mvarchar::mchar; ++ mchar ++------- ++ asd ++(1 row) ++ ++select 'asd'::mvarchar(2)::mchar; ++ mchar ++------- ++ as ++(1 row) ++ ++select 'asd '::mvarchar(2)::mchar; ++ mchar ++------- ++ as ++(1 row) ++ ++select 'asd'::mvarchar(5)::mchar; ++ mchar ++------- ++ asd ++(1 row) ++ ++select 'asd '::mvarchar(5)::mchar; ++ mchar ++------- ++ asd ++(1 row) ++ ++select 'asd'::mvarchar::mchar(2); ++ mchar ++------- ++ as ++(1 row) ++ ++select 'asd '::mvarchar::mchar(2); ++ mchar ++------- ++ as ++(1 row) ++ ++select 'asd'::mvarchar(2)::mchar(2); ++ mchar ++------- ++ as ++(1 row) ++ ++select 'asd '::mvarchar(2)::mchar(2); ++ mchar ++------- ++ as ++(1 row) ++ ++select 'asd'::mvarchar(5)::mchar(2); ++ mchar ++------- ++ as ++(1 row) ++ ++select 'asd '::mvarchar(5)::mchar(2); ++ mchar ++------- ++ as ++(1 row) ++ ++select 'asd'::mvarchar::mchar(5); ++ mchar ++------- ++ asd ++(1 row) ++ ++select 'asd '::mvarchar::mchar(5); ++ mchar ++------- ++ asd ++(1 row) ++ ++select 'asd'::mvarchar(2)::mchar(5); ++ mchar ++------- ++ as ++(1 row) ++ ++select 'asd '::mvarchar(2)::mchar(5); ++ mchar ++------- ++ as ++(1 row) ++ ++select 'asd'::mvarchar(5)::mchar(5); ++ mchar ++------- ++ asd ++(1 row) ++ ++select 'asd '::mvarchar(5)::mchar(5); ++ mchar ++------- ++ asd ++(1 row) ++ ++select 'asd'::mchar || '123'; ++ ?column? ++---------- ++ asd123 ++(1 row) ++ ++select 'asd'::mchar || '123'::mchar; ++ ?column? ++---------- ++ asd123 ++(1 row) ++ ++select 'asd'::mchar || '123'::mvarchar; ++ ?column? ++---------- ++ asd123 ++(1 row) ++ ++select 'asd '::mchar || '123'; ++ ?column? ++---------- ++ asd123 ++(1 row) ++ ++select 'asd '::mchar || '123'::mchar; ++ ?column? ++---------- ++ asd123 ++(1 row) ++ ++select 'asd '::mchar || '123'::mvarchar; ++ ?column? ++---------- ++ asd123 ++(1 row) ++ ++select 'asd '::mchar || '123 '; ++ ?column? ++---------- ++ asd123 ++(1 row) ++ ++select 'asd '::mchar || '123 '::mchar; ++ ?column? ++---------- ++ asd123 ++(1 row) ++ ++select 'asd '::mchar || '123 '::mvarchar; ++ ?column? ++---------- ++ asd123 ++(1 row) ++ ++select 'asd'::mvarchar || '123'; ++ ?column? ++---------- ++ asd123 ++(1 row) ++ ++select 'asd'::mvarchar || '123'::mchar; ++ ?column? ++---------- ++ asd123 ++(1 row) ++ ++select 'asd'::mvarchar || '123'::mvarchar; ++ ?column? ++---------- ++ asd123 ++(1 row) ++ ++select 'asd '::mvarchar || '123'; ++ ?column? ++---------- ++ asd 123 ++(1 row) ++ ++select 'asd '::mvarchar || '123'::mchar; ++ ?column? ++---------- ++ asd 123 ++(1 row) ++ ++select 'asd '::mvarchar || '123'::mvarchar; ++ ?column? ++---------- ++ asd 123 ++(1 row) ++ ++select 'asd '::mvarchar || '123 '; ++ ?column? ++---------- ++ asd 123 ++(1 row) ++ ++select 'asd '::mvarchar || '123 '::mchar; ++ ?column? ++---------- ++ asd 123 ++(1 row) ++ ++select 'asd '::mvarchar || '123 '::mvarchar; ++ ?column? ++---------- ++ asd 123 ++(1 row) ++ ++select 'asd'::mchar(2) || '123'; ++ ?column? ++---------- ++ as123 ++(1 row) ++ ++select 'asd'::mchar(2) || '123'::mchar; ++ ?column? ++---------- ++ as123 ++(1 row) ++ ++select 'asd'::mchar(2) || '123'::mvarchar; ++ ?column? ++---------- ++ as123 ++(1 row) ++ ++select 'asd '::mchar(2) || '123'; ++ ?column? ++---------- ++ as123 ++(1 row) ++ ++select 'asd '::mchar(2) || '123'::mchar; ++ ?column? ++---------- ++ as123 ++(1 row) ++ ++select 'asd '::mchar(2) || '123'::mvarchar; ++ ?column? ++---------- ++ as123 ++(1 row) ++ ++select 'asd '::mchar(2) || '123 '; ++ ?column? ++---------- ++ as123 ++(1 row) ++ ++select 'asd '::mchar(2) || '123 '::mchar; ++ ?column? ++---------- ++ as123 ++(1 row) ++ ++select 'asd '::mchar(2) || '123 '::mvarchar; ++ ?column? ++---------- ++ as123 ++(1 row) ++ ++select 'asd'::mvarchar(2) || '123'; ++ ?column? ++---------- ++ as123 ++(1 row) ++ ++select 'asd'::mvarchar(2) || '123'::mchar; ++ ?column? ++---------- ++ as123 ++(1 row) ++ ++select 'asd'::mvarchar(2) || '123'::mvarchar; ++ ?column? ++---------- ++ as123 ++(1 row) ++ ++select 'asd '::mvarchar(2) || '123'; ++ ?column? ++---------- ++ as123 ++(1 row) ++ ++select 'asd '::mvarchar(2) || '123'::mchar; ++ ?column? ++---------- ++ as123 ++(1 row) ++ ++select 'asd '::mvarchar(2) || '123'::mvarchar; ++ ?column? ++---------- ++ as123 ++(1 row) ++ ++select 'asd '::mvarchar(2) || '123 '; ++ ?column? ++---------- ++ as123 ++(1 row) ++ ++select 'asd '::mvarchar(2) || '123 '::mchar; ++ ?column? ++---------- ++ as123 ++(1 row) ++ ++select 'asd '::mvarchar(2) || '123 '::mvarchar; ++ ?column? ++---------- ++ as123 ++(1 row) ++ ++select 'asd'::mchar(4) || '143'; ++ ?column? ++---------- ++ asd 143 ++(1 row) ++ ++select 'asd'::mchar(4) || '123'::mchar; ++ ?column? ++---------- ++ asd 123 ++(1 row) ++ ++select 'asd'::mchar(4) || '123'::mvarchar; ++ ?column? ++---------- ++ asd 123 ++(1 row) ++ ++select 'asd '::mchar(4) || '123'; ++ ?column? ++---------- ++ asd 123 ++(1 row) ++ ++select 'asd '::mchar(4) || '123'::mchar; ++ ?column? ++---------- ++ asd 123 ++(1 row) ++ ++select 'asd '::mchar(4) || '123'::mvarchar; ++ ?column? ++---------- ++ asd 123 ++(1 row) ++ ++select 'asd '::mchar(4) || '123 '; ++ ?column? ++---------- ++ asd 123 ++(1 row) ++ ++select 'asd '::mchar(4) || '123 '::mchar; ++ ?column? ++---------- ++ asd 123 ++(1 row) ++ ++select 'asd '::mchar(4) || '123 '::mvarchar; ++ ?column? ++---------- ++ asd 123 ++(1 row) ++ ++select 'asd'::mvarchar(4) || '123'; ++ ?column? ++---------- ++ asd123 ++(1 row) ++ ++select 'asd'::mvarchar(4) || '123'::mchar; ++ ?column? ++---------- ++ asd123 ++(1 row) ++ ++select 'asd'::mvarchar(4) || '123'::mvarchar; ++ ?column? ++---------- ++ asd123 ++(1 row) ++ ++select 'asd '::mvarchar(4) || '123'; ++ ?column? ++---------- ++ asd 123 ++(1 row) ++ ++select 'asd '::mvarchar(4) || '123'::mchar; ++ ?column? ++---------- ++ asd 123 ++(1 row) ++ ++select 'asd '::mvarchar(4) || '123'::mvarchar; ++ ?column? ++---------- ++ asd 123 ++(1 row) ++ ++select 'asd '::mvarchar(4) || '123 '; ++ ?column? ++---------- ++ asd 123 ++(1 row) ++ ++select 'asd '::mvarchar(4) || '123 '::mchar; ++ ?column? ++---------- ++ asd 123 ++(1 row) ++ ++select 'asd '::mvarchar(4) || '123 '::mvarchar; ++ ?column? ++---------- ++ asd 123 ++(1 row) ++ ++select 'asd '::mvarchar(4) || '123 '::mchar(4); ++ ?column? ++---------- ++ asd 123 ++(1 row) ++ ++select 'asd '::mvarchar(4) || '123 '::mvarchar(4); ++ ?column? ++---------- ++ asd 123 ++(1 row) ++ ++select 'asd '::mvarchar(4) || '123'::mchar(4); ++ ?column? ++---------- ++ asd 123 ++(1 row) ++ ++select 'asd '::mvarchar(4) || '123'::mvarchar(4); ++ ?column? ++---------- ++ asd 123 ++(1 row) ++ ++select 1 where 'f'::mchar='F'::mvarchar; ++ ?column? ++---------- ++ 1 ++(1 row) ++ ++select 1 where 'f'::mchar='F '::mvarchar; ++ ?column? ++---------- ++ 1 ++(1 row) ++ ++select 1 where 'f '::mchar='F'::mvarchar; ++ ?column? ++---------- ++ 1 ++(1 row) ++ ++select 1 where 'f '::mchar='F '::mvarchar; ++ ?column? ++---------- ++ 1 ++(1 row) ++ ++select 1 where 'f'::mchar='F'::mvarchar(2); ++ ?column? ++---------- ++ 1 ++(1 row) ++ ++select 1 where 'f'::mchar='F '::mvarchar(2); ++ ?column? ++---------- ++ 1 ++(1 row) ++ ++select 1 where 'f '::mchar='F'::mvarchar(2); ++ ?column? ++---------- ++ 1 ++(1 row) ++ ++select 1 where 'f '::mchar='F '::mvarchar(2); ++ ?column? ++---------- ++ 1 ++(1 row) ++ ++select 1 where 'f'::mchar(2)='F'::mvarchar; ++ ?column? ++---------- ++ 1 ++(1 row) ++ ++select 1 where 'f'::mchar(2)='F '::mvarchar; ++ ?column? ++---------- ++ 1 ++(1 row) ++ ++select 1 where 'f '::mchar(2)='F'::mvarchar; ++ ?column? ++---------- ++ 1 ++(1 row) ++ ++select 1 where 'f '::mchar(2)='F '::mvarchar; ++ ?column? ++---------- ++ 1 ++(1 row) ++ ++select 1 where 'f'::mchar(2)='F'::mvarchar(2); ++ ?column? ++---------- ++ 1 ++(1 row) ++ ++select 1 where 'f'::mchar(2)='F '::mvarchar(2); ++ ?column? ++---------- ++ 1 ++(1 row) ++ ++select 1 where 'f '::mchar(2)='F'::mvarchar(2); ++ ?column? ++---------- ++ 1 ++(1 row) ++ ++select 1 where 'f '::mchar(2)='F '::mvarchar(2); ++ ?column? ++---------- ++ 1 ++(1 row) ++ ++select 1 where 'foo'::mchar='FOO'::mvarchar; ++ ?column? ++---------- ++ 1 ++(1 row) ++ ++select 1 where 'foo'::mchar='FOO '::mvarchar; ++ ?column? ++---------- ++ 1 ++(1 row) ++ ++select 1 where 'foo '::mchar='FOO'::mvarchar; ++ ?column? ++---------- ++ 1 ++(1 row) ++ ++select 1 where 'foo '::mchar='FOO '::mvarchar; ++ ?column? ++---------- ++ 1 ++(1 row) ++ ++select 1 where 'foo'::mchar='FOO'::mvarchar(2); ++ ?column? ++---------- ++(0 rows) ++ ++select 1 where 'foo'::mchar='FOO '::mvarchar(2); ++ ?column? ++---------- ++(0 rows) ++ ++select 1 where 'foo '::mchar='FOO'::mvarchar(2); ++ ?column? ++---------- ++(0 rows) ++ ++select 1 where 'foo '::mchar='FOO '::mvarchar(2); ++ ?column? ++---------- ++(0 rows) ++ ++select 1 where 'foo'::mchar(2)='FOO'::mvarchar; ++ ?column? ++---------- ++(0 rows) ++ ++select 1 where 'foo'::mchar(2)='FOO '::mvarchar; ++ ?column? ++---------- ++(0 rows) ++ ++select 1 where 'foo '::mchar(2)='FOO'::mvarchar; ++ ?column? ++---------- ++(0 rows) ++ ++select 1 where 'foo '::mchar(2)='FOO '::mvarchar; ++ ?column? ++---------- ++(0 rows) ++ ++select 1 where 'foo'::mchar(2)='FOO'::mvarchar(2); ++ ?column? ++---------- ++ 1 ++(1 row) ++ ++select 1 where 'foo'::mchar(2)='FOO '::mvarchar(2); ++ ?column? ++---------- ++ 1 ++(1 row) ++ ++select 1 where 'foo '::mchar(2)='FOO'::mvarchar(2); ++ ?column? ++---------- ++ 1 ++(1 row) ++ ++select 1 where 'foo '::mchar(2)='FOO '::mvarchar(2); ++ ?column? ++---------- ++ 1 ++(1 row) ++ ++Select 'f'::mchar(1) Union Select 'o'::mvarchar(1); ++ mchar ++------- ++ f ++ o ++(2 rows) ++ ++Select 'f'::mvarchar(1) Union Select 'o'::mchar(1); ++ mvarchar ++---------- ++ f ++ o ++(2 rows) ++ ++select * from chvch where ch=vch; ++ ch | vch ++--------------+------------ ++ No spaces | No spaces ++ One space | One space ++ 1 space | 1 space ++(3 rows) ++ ++select ch.* from ch, (select 'dEfg'::mvarchar as q) as p where chcol > p.q; ++ chcol ++---------------------------------- ++ ee ++ Ee ++(2 rows) ++ ++create index qq on ch (chcol); ++set enable_seqscan=off; ++select ch.* from ch, (select 'dEfg'::mvarchar as q) as p where chcol > p.q; ++ chcol ++---------------------------------- ++ ee ++ Ee ++(2 rows) ++ ++set enable_seqscan=on; ++--\copy chvch to 'results/chvch.dump' binary ++--truncate table chvch; ++--\copy chvch from 'results/chvch.dump' binary ++--test joins ++CREATE TABLE a (mchar2 MCHAR(2) NOT NULL); ++CREATE TABLE c (mvarchar255 mvarchar NOT NULL); ++SELECT * FROM a, c WHERE mchar2 = mvarchar255; ++ mchar2 | mvarchar255 ++--------+------------- ++(0 rows) ++ ++SELECT * FROM a, c WHERE mvarchar255 = mchar2; ++ mchar2 | mvarchar255 ++--------+------------- ++(0 rows) ++ ++DROP TABLE a; ++DROP TABLE c; +diff --git a/contrib/mchar/expected/mvarchar.out b/contrib/mchar/expected/mvarchar.out +new file mode 100644 +index 0000000..5c866b4 +--- /dev/null ++++ b/contrib/mchar/expected/mvarchar.out +@@ -0,0 +1,363 @@ ++-- I/O tests ++select '1'::mvarchar; ++ mvarchar ++---------- ++ 1 ++(1 row) ++ ++select '2 '::mvarchar; ++ mvarchar ++---------- ++ 2 ++(1 row) ++ ++select '10 '::mvarchar; ++ mvarchar ++-------------- ++ 10 ++(1 row) ++ ++select '1'::mvarchar(2); ++ mvarchar ++---------- ++ 1 ++(1 row) ++ ++select '2 '::mvarchar(2); ++ mvarchar ++---------- ++ 2 ++(1 row) ++ ++select '3 '::mvarchar(2); ++ mvarchar ++---------- ++ 3 ++(1 row) ++ ++select '10 '::mvarchar(2); ++ mvarchar ++---------- ++ 10 ++(1 row) ++ ++select ' '::mvarchar(10); ++ mvarchar ++------------ ++ ++(1 row) ++ ++select ' '::mvarchar; ++ mvarchar ++-------------------- ++ ++(1 row) ++ ++-- operations & functions ++select length('1'::mvarchar); ++ length ++-------- ++ 1 ++(1 row) ++ ++select length('2 '::mvarchar); ++ length ++-------- ++ 1 ++(1 row) ++ ++select length('10 '::mvarchar); ++ length ++-------- ++ 2 ++(1 row) ++ ++select length('1'::mvarchar(2)); ++ length ++-------- ++ 1 ++(1 row) ++ ++select length('2 '::mvarchar(2)); ++ length ++-------- ++ 1 ++(1 row) ++ ++select length('3 '::mvarchar(2)); ++ length ++-------- ++ 1 ++(1 row) ++ ++select length('10 '::mvarchar(2)); ++ length ++-------- ++ 2 ++(1 row) ++ ++select length(' '::mvarchar(10)); ++ length ++-------- ++ 0 ++(1 row) ++ ++select length(' '::mvarchar); ++ length ++-------- ++ 0 ++(1 row) ++ ++select 'asd'::mvarchar(10) || '>'::mvarchar(10); ++ ?column? ++---------- ++ asd> ++(1 row) ++ ++select length('asd'::mvarchar(10) || '>'::mvarchar(10)); ++ length ++-------- ++ 4 ++(1 row) ++ ++select 'asd'::mvarchar(2) || '>'::mvarchar(10); ++ ?column? ++---------- ++ as> ++(1 row) ++ ++select length('asd'::mvarchar(2) || '>'::mvarchar(10)); ++ length ++-------- ++ 3 ++(1 row) ++ ++-- Comparisons ++select 'asdf'::mvarchar = 'aSdf'::mvarchar; ++ ?column? ++---------- ++ t ++(1 row) ++ ++select 'asdf'::mvarchar = 'aSdf '::mvarchar; ++ ?column? ++---------- ++ t ++(1 row) ++ ++select 'asdf'::mvarchar = 'aSdf 1'::mvarchar(4); ++ ?column? ++---------- ++ t ++(1 row) ++ ++select 'asdf'::mvarchar = 'aSdf 1'::mvarchar(5); ++ ?column? ++---------- ++ t ++(1 row) ++ ++select 'asdf'::mvarchar = 'aSdf 1'::mvarchar(6); ++ ?column? ++---------- ++ f ++(1 row) ++ ++select 'asdf'::mvarchar(3) = 'aSdf 1'::mvarchar(5); ++ ?column? ++---------- ++ f ++(1 row) ++ ++select 'asdf'::mvarchar(3) = 'aSdf 1'::mvarchar(3); ++ ?column? ++---------- ++ t ++(1 row) ++ ++select 'asdf'::mvarchar < 'aSdf'::mvarchar; ++ ?column? ++---------- ++ f ++(1 row) ++ ++select 'asdf'::mvarchar < 'aSdf '::mvarchar; ++ ?column? ++---------- ++ f ++(1 row) ++ ++select 'asdf'::mvarchar < 'aSdf 1'::mvarchar(4); ++ ?column? ++---------- ++ f ++(1 row) ++ ++select 'asdf'::mvarchar < 'aSdf 1'::mvarchar(5); ++ ?column? ++---------- ++ f ++(1 row) ++ ++select 'asdf'::mvarchar < 'aSdf 1'::mvarchar(6); ++ ?column? ++---------- ++ t ++(1 row) ++ ++select 'asdf'::mvarchar <= 'aSdf'::mvarchar; ++ ?column? ++---------- ++ t ++(1 row) ++ ++select 'asdf'::mvarchar <= 'aSdf '::mvarchar; ++ ?column? ++---------- ++ t ++(1 row) ++ ++select 'asdf'::mvarchar <= 'aSdf 1'::mvarchar(4); ++ ?column? ++---------- ++ t ++(1 row) ++ ++select 'asdf'::mvarchar <= 'aSdf 1'::mvarchar(5); ++ ?column? ++---------- ++ t ++(1 row) ++ ++select 'asdf'::mvarchar <= 'aSdf 1'::mvarchar(6); ++ ?column? ++---------- ++ t ++(1 row) ++ ++select 'asdf'::mvarchar >= 'aSdf'::mvarchar; ++ ?column? ++---------- ++ t ++(1 row) ++ ++select 'asdf'::mvarchar >= 'aSdf '::mvarchar; ++ ?column? ++---------- ++ t ++(1 row) ++ ++select 'asdf'::mvarchar >= 'aSdf 1'::mvarchar(4); ++ ?column? ++---------- ++ t ++(1 row) ++ ++select 'asdf'::mvarchar >= 'aSdf 1'::mvarchar(5); ++ ?column? ++---------- ++ t ++(1 row) ++ ++select 'asdf'::mvarchar >= 'aSdf 1'::mvarchar(6); ++ ?column? ++---------- ++ f ++(1 row) ++ ++select 'asdf'::mvarchar > 'aSdf'::mvarchar; ++ ?column? ++---------- ++ f ++(1 row) ++ ++select 'asdf'::mvarchar > 'aSdf '::mvarchar; ++ ?column? ++---------- ++ f ++(1 row) ++ ++select 'asdf'::mvarchar > 'aSdf 1'::mvarchar(4); ++ ?column? ++---------- ++ f ++(1 row) ++ ++select 'asdf'::mvarchar > 'aSdf 1'::mvarchar(5); ++ ?column? ++---------- ++ f ++(1 row) ++ ++select 'asdf'::mvarchar > 'aSdf 1'::mvarchar(6); ++ ?column? ++---------- ++ f ++(1 row) ++ ++select max(vch) from chvch; ++ max ++------------ ++ One space ++(1 row) ++ ++select min(vch) from chvch; ++ min ++---------- ++ 1 space ++(1 row) ++ ++select substr('1234567890'::mvarchar, 3) = '34567890' as "34567890"; ++ 34567890 ++---------- ++ f ++(1 row) ++ ++select substr('1234567890'::mvarchar, 4, 3) = '456' as "456"; ++ 456 ++----- ++ t ++(1 row) ++ ++select lower('asdfASDF'::mvarchar); ++ lower ++---------- ++ asdfasdf ++(1 row) ++ ++select upper('asdfASDF'::mvarchar); ++ upper ++---------- ++ ASDFASDF ++(1 row) ++ ++select 'asd'::mvarchar == 'aSd'::mvarchar; ++ ?column? ++---------- ++ t ++(1 row) ++ ++select 'asd'::mvarchar == 'aCd'::mvarchar; ++ ?column? ++---------- ++ f ++(1 row) ++ ++select 'asd'::mvarchar == NULL; ++ ?column? ++---------- ++ f ++(1 row) ++ ++select NULL == 'aCd'::mvarchar; ++ ?column? ++---------- ++ f ++(1 row) ++ ++select NULL::mvarchar == NULL; ++ ?column? ++---------- ++ t ++(1 row) ++ +diff --git a/contrib/mchar/mchar.h b/contrib/mchar/mchar.h +new file mode 100644 +index 0000000..8823169 +--- /dev/null ++++ b/contrib/mchar/mchar.h +@@ -0,0 +1,62 @@ ++#ifndef __MCHAR_H__ ++#define __MCHAR_H__ ++ ++#include "postgres.h" ++#include "mb/pg_wchar.h" ++#include "utils/builtins.h" ++#include "unicode/uchar.h" ++#include "unicode/ustring.h" ++ ++typedef struct { ++ int32 len; ++ int32 typmod; ++ UChar data[1]; ++} MChar; ++ ++#define MCHARHDRSZ offsetof(MChar, data) ++#define MCHARLENGTH(m) ( VARSIZE(m)-MCHARHDRSZ ) ++#define UCHARLENGTH(m) ( MCHARLENGTH(m)/sizeof(UChar) ) ++ ++#define DatumGetMChar(m) ((MChar*)DatumGetPointer(m)) ++#define MCharGetDatum(m) PointerGetDatum(m) ++ ++#define PG_GETARG_MCHAR(n) DatumGetMChar(PG_DETOAST_DATUM(PG_GETARG_DATUM(n))) ++#define PG_GETARG_MCHAR_COPY(n) DatumGetMChar(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(n))) ++ ++#define PG_RETURN_MCHAR(m) PG_RETURN_POINTER(m) ++ ++typedef struct { ++ int32 len; ++ UChar data[1]; ++} MVarChar; ++ ++#define MVARCHARHDRSZ offsetof(MVarChar, data) ++#define MVARCHARLENGTH(m) ( VARSIZE(m)-MVARCHARHDRSZ ) ++#define UVARCHARLENGTH(m) ( MVARCHARLENGTH(m)/sizeof(UChar) ) ++ ++#define DatumGetMVarChar(m) ((MVarChar*)DatumGetPointer(m)) ++#define MVarCharGetDatum(m) PointerGetDatum(m) ++ ++#define PG_GETARG_MVARCHAR(n) DatumGetMVarChar(PG_DETOAST_DATUM(PG_GETARG_DATUM(n))) ++#define PG_GETARG_MVARCHAR_COPY(n) DatumGetMVarChar(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(n))) ++ ++#define PG_RETURN_MVARCHAR(m) PG_RETURN_POINTER(m) ++ ++ ++int Char2UChar(const char * src, int srclen, UChar *dst); ++int UChar2Char(const UChar * src, int srclen, char *dst); ++int UChar2Wchar(UChar * src, int srclen, pg_wchar *dst); ++int UCharCompare(UChar * a, int alen, UChar *b, int blen); ++int UCharCaseCompare(UChar * a, int alen, UChar *b, int blen); ++ ++void FillWhiteSpace( UChar *dst, int n ); ++ ++int lengthWithoutSpaceVarChar(MVarChar *m); ++int lengthWithoutSpaceChar(MChar *m); ++ ++extern Datum mchar_hash(PG_FUNCTION_ARGS); ++extern Datum mvarchar_hash(PG_FUNCTION_ARGS); ++ ++int m_isspace(UChar c); /* is == ' ' */ ++ ++#endif +diff --git a/contrib/mchar/mchar.sql.in b/contrib/mchar/mchar.sql.in +new file mode 100644 +index 0000000..b5ef4e6 +--- /dev/null ++++ b/contrib/mchar/mchar.sql.in +@@ -0,0 +1,1328 @@ ++SET search_path = public; ++ ++BEGIN; ++ ++-- I/O functions ++ ++CREATE FUNCTION mchartypmod_in(cstring[]) ++RETURNS int4 ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mchartypmod_out(int4) ++RETURNS cstring ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mchar_in(cstring) ++RETURNS mchar ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mchar_out(mchar) ++RETURNS cstring ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mchar_send(mchar) ++RETURNS bytea ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mchar_recv(internal) ++RETURNS mchar ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE TYPE mchar ( ++ INTERNALLENGTH = -1, ++ INPUT = mchar_in, ++ OUTPUT = mchar_out, ++ TYPMOD_IN = mchartypmod_in, ++ TYPMOD_OUT = mchartypmod_out, ++ RECEIVE = mchar_recv, ++ SEND = mchar_send, ++ STORAGE = extended ++); ++ ++CREATE FUNCTION mchar(mchar, integer, boolean) ++RETURNS mchar ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE CAST (mchar as mchar) ++WITH FUNCTION mchar(mchar, integer, boolean) as IMPLICIT; ++ ++CREATE FUNCTION mvarchar_in(cstring) ++RETURNS mvarchar ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mvarchar_out(mvarchar) ++RETURNS cstring ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mvarchar_send(mvarchar) ++RETURNS bytea ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mvarchar_recv(internal) ++RETURNS mvarchar ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE TYPE mvarchar ( ++ INTERNALLENGTH = -1, ++ INPUT = mvarchar_in, ++ OUTPUT = mvarchar_out, ++ TYPMOD_IN = mchartypmod_in, ++ TYPMOD_OUT = mchartypmod_out, ++ RECEIVE = mvarchar_recv, ++ SEND = mvarchar_send, ++ STORAGE = extended ++); ++ ++CREATE FUNCTION mvarchar(mvarchar, integer, boolean) ++RETURNS mvarchar ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE CAST (mvarchar as mvarchar) ++WITH FUNCTION mvarchar(mvarchar, integer, boolean) as IMPLICIT; ++ ++--Operations and functions ++ ++CREATE FUNCTION length(mchar) ++RETURNS int4 ++AS 'MODULE_PATHNAME', 'mchar_length' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION upper(mchar) ++RETURNS mchar ++AS 'MODULE_PATHNAME', 'mchar_upper' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION lower(mchar) ++RETURNS mchar ++AS 'MODULE_PATHNAME', 'mchar_lower' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mchar_hash(mchar) ++RETURNS int4 ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mchar_concat(mchar, mchar) ++RETURNS mchar ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE OPERATOR || ( ++ LEFTARG = mchar, ++ RIGHTARG = mchar, ++ PROCEDURE = mchar_concat ++); ++ ++CREATE FUNCTION mchar_like(mchar, mvarchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mchar_notlike(mchar, mvarchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE OPERATOR ~~ ( ++ LEFTARG = mchar, ++ RIGHTARG = mvarchar, ++ PROCEDURE = mchar_like, ++ RESTRICT = likesel, ++ JOIN = likejoinsel, ++ NEGATOR = '!~~' ++); ++ ++CREATE OPERATOR !~~ ( ++ LEFTARG = mchar, ++ RIGHTARG = mvarchar, ++ PROCEDURE = mchar_notlike, ++ RESTRICT = nlikesel, ++ JOIN = nlikejoinsel, ++ NEGATOR = '~~' ++); ++ ++CREATE FUNCTION mchar_regexeq(mchar, mchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mchar_regexne(mchar, mchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE OPERATOR ~ ( ++ LEFTARG = mchar, ++ RIGHTARG = mchar, ++ PROCEDURE = mchar_regexeq, ++ RESTRICT = regexeqsel, ++ JOIN = regexeqjoinsel, ++ NEGATOR = '!~' ++); ++ ++CREATE OPERATOR !~ ( ++ LEFTARG = mchar, ++ RIGHTARG = mchar, ++ PROCEDURE = mchar_regexne, ++ RESTRICT = regexnesel, ++ JOIN = regexnejoinsel, ++ NEGATOR = '~' ++); ++ ++CREATE FUNCTION similar_escape(mchar, mchar) ++RETURNS mchar ++AS 'MODULE_PATHNAME', 'mchar_similar_escape' ++LANGUAGE C IMMUTABLE; ++ ++CREATE FUNCTION length(mvarchar) ++RETURNS int4 ++AS 'MODULE_PATHNAME', 'mvarchar_length' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION upper(mvarchar) ++RETURNS mvarchar ++AS 'MODULE_PATHNAME', 'mvarchar_upper' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION lower(mvarchar) ++RETURNS mvarchar ++AS 'MODULE_PATHNAME', 'mvarchar_lower' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mvarchar_hash(mvarchar) ++RETURNS int4 ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mvarchar_concat(mvarchar, mvarchar) ++RETURNS mvarchar ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE OPERATOR || ( ++ LEFTARG = mvarchar, ++ RIGHTARG = mvarchar, ++ PROCEDURE = mvarchar_concat ++); ++ ++CREATE FUNCTION mvarchar_like(mvarchar, mvarchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION like_escape(mvarchar, mvarchar) ++RETURNS mvarchar ++AS 'MODULE_PATHNAME', 'mvarchar_like_escape' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mvarchar_notlike(mvarchar, mvarchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE OPERATOR ~~ ( ++ LEFTARG = mvarchar, ++ RIGHTARG = mvarchar, ++ PROCEDURE = mvarchar_like, ++ RESTRICT = likesel, ++ JOIN = likejoinsel, ++ NEGATOR = '!~~' ++); ++ ++CREATE OPERATOR !~~ ( ++ LEFTARG = mvarchar, ++ RIGHTARG = mvarchar, ++ PROCEDURE = mvarchar_notlike, ++ RESTRICT = nlikesel, ++ JOIN = nlikejoinsel, ++ NEGATOR = '~~' ++); ++ ++CREATE FUNCTION mvarchar_regexeq(mvarchar, mvarchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mvarchar_regexne(mvarchar, mvarchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE OPERATOR ~ ( ++ LEFTARG = mvarchar, ++ RIGHTARG = mvarchar, ++ PROCEDURE = mvarchar_regexeq, ++ RESTRICT = regexeqsel, ++ JOIN = regexeqjoinsel, ++ NEGATOR = '!~' ++); ++ ++CREATE OPERATOR !~ ( ++ LEFTARG = mvarchar, ++ RIGHTARG = mvarchar, ++ PROCEDURE = mvarchar_regexne, ++ RESTRICT = regexnesel, ++ JOIN = regexnejoinsel, ++ NEGATOR = '~' ++); ++ ++CREATE FUNCTION similar_escape(mvarchar, mvarchar) ++RETURNS mvarchar ++AS 'MODULE_PATHNAME', 'mvarchar_similar_escape' ++LANGUAGE C IMMUTABLE; ++ ++CREATE FUNCTION substr (mchar, int4) ++RETURNS mchar ++AS 'MODULE_PATHNAME', 'mchar_substring_no_len' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION substr (mchar, int4, int4) ++RETURNS mchar ++AS 'MODULE_PATHNAME', 'mchar_substring' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION substr (mvarchar, int4) ++RETURNS mvarchar ++AS 'MODULE_PATHNAME', 'mvarchar_substring_no_len' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION substr (mvarchar, int4, int4) ++RETURNS mvarchar ++AS 'MODULE_PATHNAME', 'mvarchar_substring' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++-- Comparing ++-- MCHAR ++ ++CREATE FUNCTION mchar_icase_cmp(mchar, mchar) ++RETURNS int4 ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mchar_icase_eq(mchar, mchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mchar_icase_ne(mchar, mchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mchar_icase_lt(mchar, mchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mchar_icase_le(mchar, mchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mchar_icase_gt(mchar, mchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mchar_icase_ge(mchar, mchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++ ++CREATE OPERATOR < ( ++ LEFTARG = mchar, ++ RIGHTARG = mchar, ++ PROCEDURE = mchar_icase_lt, ++ COMMUTATOR = '>', ++ NEGATOR = '>=', ++ RESTRICT = scalarltsel, ++ JOIN = scalarltjoinsel ++); ++ ++CREATE OPERATOR > ( ++ LEFTARG = mchar, ++ RIGHTARG = mchar, ++ PROCEDURE = mchar_icase_gt, ++ COMMUTATOR = '<', ++ NEGATOR = '<=', ++ RESTRICT = scalargtsel, ++ JOIN = scalargtjoinsel ++); ++ ++CREATE OPERATOR <= ( ++ LEFTARG = mchar, ++ RIGHTARG = mchar, ++ PROCEDURE = mchar_icase_le, ++ COMMUTATOR = '>=', ++ NEGATOR = '>', ++ RESTRICT = scalarltsel, ++ JOIN = scalarltjoinsel ++); ++ ++CREATE OPERATOR >= ( ++ LEFTARG = mchar, ++ RIGHTARG = mchar, ++ PROCEDURE = mchar_icase_ge, ++ COMMUTATOR = '<=', ++ NEGATOR = '<', ++ RESTRICT = scalargtsel, ++ JOIN = scalargtjoinsel ++); ++ ++CREATE OPERATOR = ( ++ LEFTARG = mchar, ++ RIGHTARG = mchar, ++ PROCEDURE = mchar_icase_eq, ++ COMMUTATOR = '=', ++ NEGATOR = '<>', ++ RESTRICT = eqsel, ++ JOIN = eqjoinsel, ++ SORT1 = '<', ++ SORT2 = '<', ++ HASHES ++); ++ ++CREATE OPERATOR <> ( ++ LEFTARG = mchar, ++ RIGHTARG = mchar, ++ PROCEDURE = mchar_icase_ne, ++ COMMUTATOR = '<>', ++ NEGATOR = '=', ++ RESTRICT = neqsel, ++ JOIN = neqjoinsel ++); ++ ++CREATE FUNCTION mchar_case_cmp(mchar, mchar) ++RETURNS int4 ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mchar_case_eq(mchar, mchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mchar_case_ne(mchar, mchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mchar_case_lt(mchar, mchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mchar_case_le(mchar, mchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mchar_case_gt(mchar, mchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mchar_case_ge(mchar, mchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++ ++CREATE OPERATOR &< ( ++ LEFTARG = mchar, ++ RIGHTARG = mchar, ++ PROCEDURE = mchar_case_lt, ++ COMMUTATOR = '&>', ++ NEGATOR = '&>=', ++ RESTRICT = scalarltsel, ++ JOIN = scalarltjoinsel ++); ++ ++CREATE OPERATOR &> ( ++ LEFTARG = mchar, ++ RIGHTARG = mchar, ++ PROCEDURE = mchar_case_gt, ++ COMMUTATOR = '&<', ++ NEGATOR = '&<=', ++ RESTRICT = scalargtsel, ++ JOIN = scalargtjoinsel ++); ++ ++CREATE OPERATOR &<= ( ++ LEFTARG = mchar, ++ RIGHTARG = mchar, ++ PROCEDURE = mchar_case_le, ++ COMMUTATOR = '&>=', ++ NEGATOR = '&>', ++ RESTRICT = scalarltsel, ++ JOIN = scalarltjoinsel ++); ++ ++CREATE OPERATOR &>= ( ++ LEFTARG = mchar, ++ RIGHTARG = mchar, ++ PROCEDURE = mchar_case_ge, ++ COMMUTATOR = '&<=', ++ NEGATOR = '&<', ++ RESTRICT = scalargtsel, ++ JOIN = scalargtjoinsel ++); ++ ++CREATE OPERATOR &= ( ++ LEFTARG = mchar, ++ RIGHTARG = mchar, ++ PROCEDURE = mchar_case_eq, ++ COMMUTATOR = '&=', ++ NEGATOR = '&<>', ++ RESTRICT = eqsel, ++ JOIN = eqjoinsel, ++ SORT1 = '&<', ++ SORT2 = '&<' ++); ++ ++CREATE OPERATOR &<> ( ++ LEFTARG = mchar, ++ RIGHTARG = mchar, ++ PROCEDURE = mchar_case_ne, ++ COMMUTATOR = '&<>', ++ NEGATOR = '&=', ++ RESTRICT = neqsel, ++ JOIN = neqjoinsel ++); ++ ++--MVARCHAR ++ ++CREATE FUNCTION mvarchar_icase_cmp(mvarchar, mvarchar) ++RETURNS int4 ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mvarchar_icase_eq(mvarchar, mvarchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mvarchar_icase_ne(mvarchar, mvarchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mvarchar_icase_lt(mvarchar, mvarchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mvarchar_icase_le(mvarchar, mvarchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mvarchar_icase_gt(mvarchar, mvarchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mvarchar_icase_ge(mvarchar, mvarchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++ ++CREATE OPERATOR < ( ++ LEFTARG = mvarchar, ++ RIGHTARG = mvarchar, ++ PROCEDURE = mvarchar_icase_lt, ++ COMMUTATOR = '>', ++ NEGATOR = '>=', ++ RESTRICT = scalarltsel, ++ JOIN = scalarltjoinsel ++); ++ ++CREATE OPERATOR > ( ++ LEFTARG = mvarchar, ++ RIGHTARG = mvarchar, ++ PROCEDURE = mvarchar_icase_gt, ++ COMMUTATOR = '<', ++ NEGATOR = '<=', ++ RESTRICT = scalargtsel, ++ JOIN = scalargtjoinsel ++); ++ ++CREATE OPERATOR <= ( ++ LEFTARG = mvarchar, ++ RIGHTARG = mvarchar, ++ PROCEDURE = mvarchar_icase_le, ++ COMMUTATOR = '>=', ++ NEGATOR = '>', ++ RESTRICT = scalarltsel, ++ JOIN = scalarltjoinsel ++); ++ ++CREATE OPERATOR >= ( ++ LEFTARG = mvarchar, ++ RIGHTARG = mvarchar, ++ PROCEDURE = mvarchar_icase_ge, ++ COMMUTATOR = '<=', ++ NEGATOR = '<', ++ RESTRICT = scalargtsel, ++ JOIN = scalargtjoinsel ++); ++ ++CREATE OPERATOR = ( ++ LEFTARG = mvarchar, ++ RIGHTARG = mvarchar, ++ PROCEDURE = mvarchar_icase_eq, ++ COMMUTATOR = '=', ++ NEGATOR = '<>', ++ RESTRICT = eqsel, ++ JOIN = eqjoinsel, ++ SORT1 = '<', ++ SORT2 = '<', ++ HASHES ++); ++ ++CREATE OPERATOR <> ( ++ LEFTARG = mvarchar, ++ RIGHTARG = mvarchar, ++ PROCEDURE = mvarchar_icase_ne, ++ COMMUTATOR = '<>', ++ NEGATOR = '=', ++ RESTRICT = neqsel, ++ JOIN = neqjoinsel ++); ++ ++CREATE FUNCTION mvarchar_case_cmp(mvarchar, mvarchar) ++RETURNS int4 ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mvarchar_case_eq(mvarchar, mvarchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mvarchar_case_ne(mvarchar, mvarchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mvarchar_case_lt(mvarchar, mvarchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mvarchar_case_le(mvarchar, mvarchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mvarchar_case_gt(mvarchar, mvarchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mvarchar_case_ge(mvarchar, mvarchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++ ++CREATE OPERATOR &< ( ++ LEFTARG = mvarchar, ++ RIGHTARG = mvarchar, ++ PROCEDURE = mvarchar_case_lt, ++ COMMUTATOR = '&>', ++ NEGATOR = '&>=', ++ RESTRICT = scalarltsel, ++ JOIN = scalarltjoinsel ++); ++ ++CREATE OPERATOR &> ( ++ LEFTARG = mvarchar, ++ RIGHTARG = mvarchar, ++ PROCEDURE = mvarchar_case_gt, ++ COMMUTATOR = '&<', ++ NEGATOR = '&<=', ++ RESTRICT = scalargtsel, ++ JOIN = scalargtjoinsel ++); ++ ++CREATE OPERATOR &<= ( ++ LEFTARG = mvarchar, ++ RIGHTARG = mvarchar, ++ PROCEDURE = mvarchar_case_le, ++ COMMUTATOR = '&>=', ++ NEGATOR = '&>', ++ RESTRICT = scalarltsel, ++ JOIN = scalarltjoinsel ++); ++ ++CREATE OPERATOR &>= ( ++ LEFTARG = mvarchar, ++ RIGHTARG = mvarchar, ++ PROCEDURE = mvarchar_case_ge, ++ COMMUTATOR = '&<=', ++ NEGATOR = '&<', ++ RESTRICT = scalargtsel, ++ JOIN = scalargtjoinsel ++); ++ ++CREATE OPERATOR &= ( ++ LEFTARG = mvarchar, ++ RIGHTARG = mvarchar, ++ PROCEDURE = mvarchar_case_eq, ++ COMMUTATOR = '&=', ++ NEGATOR = '&<>', ++ RESTRICT = eqsel, ++ JOIN = eqjoinsel, ++ SORT1 = '&<', ++ SORT2 = '&<' ++); ++ ++CREATE OPERATOR &<> ( ++ LEFTARG = mvarchar, ++ RIGHTARG = mvarchar, ++ PROCEDURE = mvarchar_case_ne, ++ COMMUTATOR = '&<>', ++ NEGATOR = '&=', ++ RESTRICT = neqsel, ++ JOIN = neqjoinsel ++); ++ ++-- MCHAR <> MVARCHAR ++ ++CREATE FUNCTION mc_mv_icase_cmp(mchar, mvarchar) ++RETURNS int4 ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mc_mv_icase_eq(mchar, mvarchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mc_mv_icase_ne(mchar, mvarchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mc_mv_icase_lt(mchar, mvarchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mc_mv_icase_le(mchar, mvarchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mc_mv_icase_gt(mchar, mvarchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mc_mv_icase_ge(mchar, mvarchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++ ++CREATE OPERATOR < ( ++ LEFTARG = mchar, ++ RIGHTARG = mvarchar, ++ PROCEDURE = mc_mv_icase_lt, ++ COMMUTATOR = '>', ++ NEGATOR = '>=', ++ RESTRICT = scalarltsel, ++ JOIN = scalarltjoinsel ++); ++ ++CREATE OPERATOR > ( ++ LEFTARG = mchar, ++ RIGHTARG = mvarchar, ++ PROCEDURE = mc_mv_icase_gt, ++ COMMUTATOR = '<', ++ NEGATOR = '<=', ++ RESTRICT = scalargtsel, ++ JOIN = scalargtjoinsel ++); ++ ++CREATE OPERATOR <= ( ++ LEFTARG = mchar, ++ RIGHTARG = mvarchar, ++ PROCEDURE = mc_mv_icase_le, ++ COMMUTATOR = '>=', ++ NEGATOR = '>', ++ RESTRICT = scalarltsel, ++ JOIN = scalarltjoinsel ++); ++ ++CREATE OPERATOR >= ( ++ LEFTARG = mchar, ++ RIGHTARG = mvarchar, ++ PROCEDURE = mc_mv_icase_ge, ++ COMMUTATOR = '<=', ++ NEGATOR = '<', ++ RESTRICT = scalargtsel, ++ JOIN = scalargtjoinsel ++); ++ ++CREATE OPERATOR = ( ++ LEFTARG = mchar, ++ RIGHTARG = mvarchar, ++ PROCEDURE = mc_mv_icase_eq, ++ COMMUTATOR = '=', ++ NEGATOR = '<>', ++ RESTRICT = eqsel, ++ JOIN = eqjoinsel, ++ SORT1 = '<', ++ SORT2 = '<' ++); ++ ++CREATE OPERATOR <> ( ++ LEFTARG = mchar, ++ RIGHTARG = mvarchar, ++ PROCEDURE = mc_mv_icase_ne, ++ COMMUTATOR = '<>', ++ NEGATOR = '=', ++ RESTRICT = neqsel, ++ JOIN = neqjoinsel ++); ++ ++CREATE FUNCTION mc_mv_case_cmp(mchar, mvarchar) ++RETURNS int4 ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mc_mv_case_eq(mchar, mvarchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mc_mv_case_ne(mchar, mvarchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mc_mv_case_lt(mchar, mvarchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mc_mv_case_le(mchar, mvarchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mc_mv_case_gt(mchar, mvarchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mc_mv_case_ge(mchar, mvarchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++ ++CREATE OPERATOR &< ( ++ LEFTARG = mchar, ++ RIGHTARG = mvarchar, ++ PROCEDURE = mc_mv_case_lt, ++ COMMUTATOR = '&>', ++ NEGATOR = '&>=', ++ RESTRICT = scalarltsel, ++ JOIN = scalarltjoinsel ++); ++ ++CREATE OPERATOR &> ( ++ LEFTARG = mchar, ++ RIGHTARG = mvarchar, ++ PROCEDURE = mc_mv_case_gt, ++ COMMUTATOR = '&<', ++ NEGATOR = '&<=', ++ RESTRICT = scalargtsel, ++ JOIN = scalargtjoinsel ++); ++ ++CREATE OPERATOR &<= ( ++ LEFTARG = mchar, ++ RIGHTARG = mvarchar, ++ PROCEDURE = mc_mv_case_le, ++ COMMUTATOR = '&>=', ++ NEGATOR = '&>', ++ RESTRICT = scalarltsel, ++ JOIN = scalarltjoinsel ++); ++ ++CREATE OPERATOR &>= ( ++ LEFTARG = mchar, ++ RIGHTARG = mvarchar, ++ PROCEDURE = mc_mv_case_ge, ++ COMMUTATOR = '&<=', ++ NEGATOR = '&<', ++ RESTRICT = scalargtsel, ++ JOIN = scalargtjoinsel ++); ++ ++CREATE OPERATOR &= ( ++ LEFTARG = mchar, ++ RIGHTARG = mvarchar, ++ PROCEDURE = mc_mv_case_eq, ++ COMMUTATOR = '&=', ++ NEGATOR = '&<>', ++ RESTRICT = eqsel, ++ JOIN = eqjoinsel, ++ SORT1 = '&<', ++ SORT2 = '&<' ++); ++ ++CREATE OPERATOR &<> ( ++ LEFTARG = mchar, ++ RIGHTARG = mvarchar, ++ PROCEDURE = mc_mv_case_ne, ++ COMMUTATOR = '&<>', ++ NEGATOR = '&=', ++ RESTRICT = neqsel, ++ JOIN = neqjoinsel ++); ++ ++-- MVARCHAR <> MCHAR ++ ++CREATE FUNCTION mv_mc_icase_cmp(mvarchar, mchar) ++RETURNS int4 ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mv_mc_icase_eq(mvarchar, mchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mv_mc_icase_ne(mvarchar, mchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mv_mc_icase_lt(mvarchar, mchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mv_mc_icase_le(mvarchar, mchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mv_mc_icase_gt(mvarchar, mchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mv_mc_icase_ge(mvarchar, mchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++ ++CREATE OPERATOR < ( ++ LEFTARG = mvarchar, ++ RIGHTARG = mchar, ++ PROCEDURE = mv_mc_icase_lt, ++ COMMUTATOR = '>', ++ NEGATOR = '>=', ++ RESTRICT = scalarltsel, ++ JOIN = scalarltjoinsel ++); ++ ++CREATE OPERATOR > ( ++ LEFTARG = mvarchar, ++ RIGHTARG = mchar, ++ PROCEDURE = mv_mc_icase_gt, ++ COMMUTATOR = '<', ++ NEGATOR = '<=', ++ RESTRICT = scalargtsel, ++ JOIN = scalargtjoinsel ++); ++ ++CREATE OPERATOR <= ( ++ LEFTARG = mvarchar, ++ RIGHTARG = mchar, ++ PROCEDURE = mv_mc_icase_le, ++ COMMUTATOR = '>=', ++ NEGATOR = '>', ++ RESTRICT = scalarltsel, ++ JOIN = scalarltjoinsel ++); ++ ++CREATE OPERATOR >= ( ++ LEFTARG = mvarchar, ++ RIGHTARG = mchar, ++ PROCEDURE = mv_mc_icase_ge, ++ COMMUTATOR = '<=', ++ NEGATOR = '<', ++ RESTRICT = scalargtsel, ++ JOIN = scalargtjoinsel ++); ++ ++CREATE OPERATOR = ( ++ LEFTARG = mvarchar, ++ RIGHTARG = mchar, ++ PROCEDURE = mv_mc_icase_eq, ++ COMMUTATOR = '=', ++ NEGATOR = '<>', ++ RESTRICT = eqsel, ++ JOIN = eqjoinsel, ++ SORT1 = '<', ++ SORT2 = '<' ++); ++ ++CREATE OPERATOR <> ( ++ LEFTARG = mvarchar, ++ RIGHTARG = mchar, ++ PROCEDURE = mv_mc_icase_ne, ++ COMMUTATOR = '<>', ++ NEGATOR = '=', ++ RESTRICT = neqsel, ++ JOIN = neqjoinsel ++); ++ ++CREATE FUNCTION mv_mc_case_cmp(mvarchar, mchar) ++RETURNS int4 ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mv_mc_case_eq(mvarchar, mchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mv_mc_case_ne(mvarchar, mchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mv_mc_case_lt(mvarchar, mchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mv_mc_case_le(mvarchar, mchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mv_mc_case_gt(mvarchar, mchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mv_mc_case_ge(mvarchar, mchar) ++RETURNS bool ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++ ++CREATE OPERATOR &< ( ++ LEFTARG = mvarchar, ++ RIGHTARG = mchar, ++ PROCEDURE = mv_mc_case_lt, ++ COMMUTATOR = '&>', ++ NEGATOR = '&>=', ++ RESTRICT = scalarltsel, ++ JOIN = scalarltjoinsel ++); ++ ++CREATE OPERATOR &> ( ++ LEFTARG = mvarchar, ++ RIGHTARG = mchar, ++ PROCEDURE = mv_mc_case_gt, ++ COMMUTATOR = '&<', ++ NEGATOR = '&<=', ++ RESTRICT = scalargtsel, ++ JOIN = scalargtjoinsel ++); ++ ++CREATE OPERATOR &<= ( ++ LEFTARG = mvarchar, ++ RIGHTARG = mchar, ++ PROCEDURE = mv_mc_case_le, ++ COMMUTATOR = '&>=', ++ NEGATOR = '&>', ++ RESTRICT = scalarltsel, ++ JOIN = scalarltjoinsel ++); ++ ++CREATE OPERATOR &>= ( ++ LEFTARG = mvarchar, ++ RIGHTARG = mchar, ++ PROCEDURE = mv_mc_case_ge, ++ COMMUTATOR = '&<=', ++ NEGATOR = '&<', ++ RESTRICT = scalargtsel, ++ JOIN = scalargtjoinsel ++); ++ ++CREATE OPERATOR &= ( ++ LEFTARG = mvarchar, ++ RIGHTARG = mchar, ++ PROCEDURE = mv_mc_case_eq, ++ COMMUTATOR = '&=', ++ NEGATOR = '&<>', ++ RESTRICT = eqsel, ++ JOIN = eqjoinsel, ++ SORT1 = '&<', ++ SORT2 = '&<' ++); ++ ++CREATE OPERATOR &<> ( ++ LEFTARG = mvarchar, ++ RIGHTARG = mchar, ++ PROCEDURE = mv_mc_case_ne, ++ COMMUTATOR = '&<>', ++ NEGATOR = '&=', ++ RESTRICT = neqsel, ++ JOIN = neqjoinsel ++); ++ ++-- MCHAR - VARCHAR operations ++ ++CREATE FUNCTION mchar_mvarchar_concat(mchar, mvarchar) ++RETURNS mvarchar ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE OPERATOR || ( ++ LEFTARG = mchar, ++ RIGHTARG = mvarchar, ++ PROCEDURE = mchar_mvarchar_concat ++); ++ ++CREATE FUNCTION mvarchar_mchar_concat(mvarchar, mchar) ++RETURNS mvarchar ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE OPERATOR || ( ++ LEFTARG = mvarchar, ++ RIGHTARG = mchar, ++ PROCEDURE = mvarchar_mchar_concat ++); ++ ++CREATE FUNCTION mvarchar_mchar(mvarchar, integer, boolean) ++RETURNS mchar ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE CAST (mvarchar as mchar) ++WITH FUNCTION mvarchar_mchar(mvarchar, integer, boolean) as IMPLICIT; ++ ++CREATE FUNCTION mchar_mvarchar(mchar, integer, boolean) ++RETURNS mvarchar ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE CAST (mchar as mvarchar) ++WITH FUNCTION mchar_mvarchar(mchar, integer, boolean) as IMPLICIT; ++ ++-- Aggregates ++ ++CREATE FUNCTION mchar_larger(mchar, mchar) ++RETURNS mchar ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE AGGREGATE max ( ++ BASETYPE = mchar, ++ SFUNC = mchar_larger, ++ STYPE = mchar, ++ SORTOP = '>' ++); ++ ++CREATE FUNCTION mchar_smaller(mchar, mchar) ++RETURNS mchar ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE AGGREGATE min ( ++ BASETYPE = mchar, ++ SFUNC = mchar_smaller, ++ STYPE = mchar, ++ SORTOP = '<' ++); ++ ++CREATE FUNCTION mvarchar_larger(mvarchar, mvarchar) ++RETURNS mvarchar ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE AGGREGATE max ( ++ BASETYPE = mvarchar, ++ SFUNC = mvarchar_larger, ++ STYPE = mvarchar, ++ SORTOP = '>' ++); ++ ++CREATE FUNCTION mvarchar_smaller(mvarchar, mvarchar) ++RETURNS mvarchar ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE AGGREGATE min ( ++ BASETYPE = mvarchar, ++ SFUNC = mvarchar_smaller, ++ STYPE = mvarchar, ++ SORTOP = '<' ++); ++ ++-- B-tree support ++CREATE OPERATOR FAMILY icase_ops USING btree; ++CREATE OPERATOR FAMILY case_ops USING btree; ++ ++CREATE OPERATOR CLASS mchar_icase_ops ++DEFAULT FOR TYPE mchar USING btree FAMILY icase_ops AS ++ OPERATOR 1 < , ++ OPERATOR 2 <= , ++ OPERATOR 3 = , ++ OPERATOR 4 >= , ++ OPERATOR 5 > , ++ FUNCTION 1 mchar_icase_cmp(mchar, mchar), ++ OPERATOR 1 < (mchar, mvarchar), ++ OPERATOR 2 <= (mchar, mvarchar), ++ OPERATOR 3 = (mchar, mvarchar), ++ OPERATOR 4 >= (mchar, mvarchar), ++ OPERATOR 5 > (mchar, mvarchar), ++ FUNCTION 1 mc_mv_icase_cmp(mchar, mvarchar); ++ ++CREATE OPERATOR CLASS mchar_case_ops ++FOR TYPE mchar USING btree FAMILY case_ops AS ++ OPERATOR 1 &< , ++ OPERATOR 2 &<= , ++ OPERATOR 3 &= , ++ OPERATOR 4 &>= , ++ OPERATOR 5 &> , ++ FUNCTION 1 mchar_case_cmp(mchar, mchar), ++ OPERATOR 1 &< (mchar, mvarchar), ++ OPERATOR 2 &<= (mchar, mvarchar), ++ OPERATOR 3 &= (mchar, mvarchar), ++ OPERATOR 4 &>= (mchar, mvarchar), ++ OPERATOR 5 &> (mchar, mvarchar), ++ FUNCTION 1 mc_mv_case_cmp(mchar, mvarchar); ++ ++CREATE OPERATOR CLASS mchar_icase_ops ++DEFAULT FOR TYPE mchar USING hash AS ++ OPERATOR 1 = , ++ FUNCTION 1 mchar_hash(mchar); ++ ++CREATE OPERATOR CLASS mvarchar_icase_ops ++DEFAULT FOR TYPE mvarchar USING btree FAMILY icase_ops AS ++ OPERATOR 1 < , ++ OPERATOR 2 <= , ++ OPERATOR 3 = , ++ OPERATOR 4 >= , ++ OPERATOR 5 > , ++ FUNCTION 1 mvarchar_icase_cmp(mvarchar, mvarchar), ++ OPERATOR 1 < (mvarchar, mchar), ++ OPERATOR 2 <= (mvarchar, mchar), ++ OPERATOR 3 = (mvarchar, mchar), ++ OPERATOR 4 >= (mvarchar, mchar), ++ OPERATOR 5 > (mvarchar, mchar), ++ FUNCTION 1 mv_mc_icase_cmp(mvarchar, mchar); ++ ++CREATE OPERATOR CLASS mvarchar_case_ops ++FOR TYPE mvarchar USING btree FAMILY case_ops AS ++ OPERATOR 1 &< , ++ OPERATOR 2 &<= , ++ OPERATOR 3 &= , ++ OPERATOR 4 &>= , ++ OPERATOR 5 &> , ++ FUNCTION 1 mvarchar_case_cmp(mvarchar, mvarchar), ++ OPERATOR 1 &< (mvarchar, mchar), ++ OPERATOR 2 &<= (mvarchar, mchar), ++ OPERATOR 3 &= (mvarchar, mchar), ++ OPERATOR 4 &>= (mvarchar, mchar), ++ OPERATOR 5 &> (mvarchar, mchar), ++ FUNCTION 1 mv_mc_case_cmp(mvarchar, mchar); ++ ++CREATE OPERATOR CLASS mvarchar_icase_ops ++DEFAULT FOR TYPE mvarchar USING hash AS ++ OPERATOR 1 = , ++ FUNCTION 1 mvarchar_hash(mvarchar); ++ ++ ++-- Index support for LIKE ++ ++CREATE FUNCTION mchar_pattern_fixed_prefix(internal, internal, internal) ++RETURNS int4 ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE FUNCTION mchar_greaterstring(internal) ++RETURNS internal ++AS 'MODULE_PATHNAME' ++LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT; ++ ++CREATE OR REPLACE FUNCTION isfulleq_mchar(mchar, mchar) ++RETURNS bool AS 'MODULE_PATHNAME' ++LANGUAGE C CALLED ON NULL INPUT IMMUTABLE; ++ ++CREATE OR REPLACE FUNCTION fullhash_mchar(mchar) ++RETURNS int4 AS 'MODULE_PATHNAME' ++LANGUAGE C CALLED ON NULL INPUT IMMUTABLE; ++ ++ ++CREATE OPERATOR == ( ++ LEFTARG = mchar, ++ RIGHTARG = mchar, ++ PROCEDURE = isfulleq_mchar, ++ COMMUTATOR = '==', ++ RESTRICT = eqsel, ++ JOIN = eqjoinsel, ++ HASHES ++); ++ ++CREATE OPERATOR CLASS mchar_fill_ops ++ FOR TYPE mchar USING hash AS ++ OPERATOR 1 ==, ++ FUNCTION 1 fullhash_mchar(mchar); ++ ++CREATE OR REPLACE FUNCTION isfulleq_mvarchar(mvarchar, mvarchar) ++RETURNS bool AS 'MODULE_PATHNAME' ++LANGUAGE C CALLED ON NULL INPUT IMMUTABLE; ++ ++CREATE OR REPLACE FUNCTION fullhash_mvarchar(mvarchar) ++RETURNS int4 AS 'MODULE_PATHNAME' ++LANGUAGE C CALLED ON NULL INPUT IMMUTABLE; ++ ++ ++CREATE OPERATOR == ( ++ LEFTARG = mvarchar, ++ RIGHTARG = mvarchar, ++ PROCEDURE = isfulleq_mvarchar, ++ COMMUTATOR = '==', ++ RESTRICT = eqsel, ++ JOIN = eqjoinsel, ++ HASHES ++); ++ ++CREATE OPERATOR CLASS mvarchar_fill_ops ++ FOR TYPE mvarchar USING hash AS ++ OPERATOR 1 ==, ++ FUNCTION 1 fullhash_mvarchar(mvarchar); ++ ++COMMIT; ++SET search_path = public; ++ +diff --git a/contrib/mchar/mchar_io.c b/contrib/mchar/mchar_io.c +new file mode 100644 +index 0000000..9179412 +--- /dev/null ++++ b/contrib/mchar/mchar_io.c +@@ -0,0 +1,372 @@ ++#include "mchar.h" ++#include "mb/pg_wchar.h" ++#include "fmgr.h" ++#include "libpq/pqformat.h" ++#include ++ ++#ifdef PG_MODULE_MAGIC ++PG_MODULE_MAGIC; ++#endif ++ ++PG_FUNCTION_INFO_V1(mchar_in); ++Datum mchar_in(PG_FUNCTION_ARGS); ++PG_FUNCTION_INFO_V1(mchar_out); ++Datum mchar_out(PG_FUNCTION_ARGS); ++PG_FUNCTION_INFO_V1(mchar); ++Datum mchar(PG_FUNCTION_ARGS); ++ ++PG_FUNCTION_INFO_V1(mvarchar_in); ++Datum mvarchar_in(PG_FUNCTION_ARGS); ++PG_FUNCTION_INFO_V1(mvarchar_out); ++Datum mvarchar_out(PG_FUNCTION_ARGS); ++PG_FUNCTION_INFO_V1(mvarchar); ++Datum mvarchar(PG_FUNCTION_ARGS); ++ ++PG_FUNCTION_INFO_V1(mchartypmod_in); ++Datum mchartypmod_in(PG_FUNCTION_ARGS); ++Datum ++mchartypmod_in(PG_FUNCTION_ARGS) { ++ ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0); ++ int32 *tl; ++ int n; ++ ++ tl = ArrayGetIntegerTypmods(ta, &n); ++ ++ if (n != 1) ++ ereport(ERROR, ++ (errcode(ERRCODE_INVALID_PARAMETER_VALUE), ++ errmsg("invalid type modifier"))); ++ if (*tl < 1) ++ ereport(ERROR, ++ (errcode(ERRCODE_INVALID_PARAMETER_VALUE), ++ errmsg("length for type mchar/mvarchar must be at least 1"))); ++ ++ return *tl; ++} ++ ++PG_FUNCTION_INFO_V1(mchartypmod_out); ++Datum mchartypmod_out(PG_FUNCTION_ARGS); ++Datum ++mchartypmod_out(PG_FUNCTION_ARGS) { ++ int32 typmod = PG_GETARG_INT32(0); ++ char *res = (char *) palloc(64); ++ ++ if (typmod >0) ++ snprintf(res, 64, "(%d)", (int) (typmod)); ++ else ++ *res = '\0'; ++ ++ PG_RETURN_CSTRING( res ); ++} ++ ++static void ++mchar_strip( MChar * m, int atttypmod ) { ++ int maxlen; ++ ++ if ( atttypmod<=0 ) { ++ atttypmod =-1; ++ } else { ++ int charlen = u_countChar32( m->data, UCHARLENGTH(m) ); ++ ++ if ( charlen > atttypmod ) { ++ int i=0; ++ U16_FWD_N( m->data, i, UCHARLENGTH(m), atttypmod); ++ SET_VARSIZE( m, sizeof(UChar) * i + MCHARHDRSZ ); ++ } ++ } ++ ++ m->typmod = atttypmod; ++ ++ maxlen = UCHARLENGTH(m); ++ while( maxlen>0 && m_isspace( m->data[ maxlen-1 ] ) ) ++ maxlen--; ++ ++ SET_VARSIZE(m, sizeof(UChar) * maxlen + MCHARHDRSZ); ++} ++ ++ ++Datum ++mchar_in(PG_FUNCTION_ARGS) { ++ char *s = PG_GETARG_CSTRING(0); ++#ifdef NOT_USED ++ Oid typelem = PG_GETARG_OID(1); ++#endif ++ int32 atttypmod = PG_GETARG_INT32(2); ++ MChar *result; ++ int32 slen = strlen(s), rlen; ++ ++ pg_verifymbstr(s, slen, false); ++ ++ result = (MChar*)palloc( MCHARHDRSZ + slen * sizeof(UChar) * 4 /* upper limit of length */ ); ++ rlen = Char2UChar( s, slen, result->data ); ++ SET_VARSIZE(result, sizeof(UChar) * rlen + MCHARHDRSZ); ++ ++ mchar_strip(result, atttypmod); ++ ++ PG_RETURN_MCHAR(result); ++} ++ ++Datum ++mchar_out(PG_FUNCTION_ARGS) { ++ MChar *in = PG_GETARG_MCHAR(0); ++ char *out; ++ size_t size, inlen = UCHARLENGTH(in); ++ size_t charlen = u_countChar32(in->data, inlen); ++ ++ Assert( in->typmod < 0 || charlen<=in->typmod ); ++ size = ( in->typmod < 0 ) ? inlen : in->typmod; ++ size *= pg_database_encoding_max_length(); ++ ++ out = (char*)palloc( size+1 ); ++ size = UChar2Char( in->data, inlen, out ); ++ ++ if ( in->typmod>0 && charlen < in->typmod ) { ++ memset( out+size, ' ', in->typmod - charlen); ++ size += in->typmod - charlen; ++ } ++ ++ out[size] = '\0'; ++ ++ PG_FREE_IF_COPY(in,0); ++ ++ PG_RETURN_CSTRING(out); ++} ++ ++Datum ++mchar(PG_FUNCTION_ARGS) { ++ MChar *source = PG_GETARG_MCHAR(0); ++ MChar *result; ++ int32 typmod = PG_GETARG_INT32(1); ++#ifdef NOT_USED ++ bool isExplicit = PG_GETARG_BOOL(2); ++#endif ++ ++ result = palloc( VARSIZE(source) ); ++ memcpy( result, source, VARSIZE(source) ); ++ PG_FREE_IF_COPY(source,0); ++ ++ mchar_strip(result, typmod); ++ ++ PG_RETURN_MCHAR(result); ++} ++ ++Datum ++mvarchar_in(PG_FUNCTION_ARGS) { ++ char *s = PG_GETARG_CSTRING(0); ++#ifdef NOT_USED ++ Oid typelem = PG_GETARG_OID(1); ++#endif ++ int32 atttypmod = PG_GETARG_INT32(2); ++ MVarChar *result; ++ int32 slen = strlen(s), rlen; ++ ++ pg_verifymbstr(s, slen, false); ++ ++ result = (MVarChar*)palloc( MVARCHARHDRSZ + slen * sizeof(UChar) * 2 /* upper limit of length */ ); ++ rlen = Char2UChar( s, slen, result->data ); ++ SET_VARSIZE(result, sizeof(UChar) * rlen + MVARCHARHDRSZ); ++ ++ if ( atttypmod > 0 && atttypmod < u_countChar32(result->data, UVARCHARLENGTH(result)) ) ++ elog(ERROR,"value too long for type mvarchar(%d)", atttypmod); ++ ++ PG_RETURN_MVARCHAR(result); ++} ++ ++Datum ++mvarchar_out(PG_FUNCTION_ARGS) { ++ MVarChar *in = PG_GETARG_MVARCHAR(0); ++ char *out; ++ size_t size = UVARCHARLENGTH(in); ++ ++ size *= pg_database_encoding_max_length(); ++ ++ out = (char*)palloc( size+1 ); ++ size = UChar2Char( in->data, UVARCHARLENGTH(in), out ); ++ ++ out[size] = '\0'; ++ ++ PG_FREE_IF_COPY(in,0); ++ ++ PG_RETURN_CSTRING(out); ++} ++ ++static void ++mvarchar_strip(MVarChar *m, int atttypmod) { ++ int charlen = u_countChar32(m->data, UVARCHARLENGTH(m)); ++ ++ if ( atttypmod>=0 && atttypmod < charlen ) { ++ int i=0; ++ U16_FWD_N( m->data, i, charlen, atttypmod); ++ SET_VARSIZE(m, sizeof(UChar) * i + MVARCHARHDRSZ); ++ } ++} ++ ++Datum ++mvarchar(PG_FUNCTION_ARGS) { ++ MVarChar *source = PG_GETARG_MVARCHAR(0); ++ MVarChar *result; ++ int32 typmod = PG_GETARG_INT32(1); ++ bool isExplicit = PG_GETARG_BOOL(2); ++ int charlen = u_countChar32(source->data, UVARCHARLENGTH(source)); ++ ++ result = palloc( VARSIZE(source) ); ++ memcpy( result, source, VARSIZE(source) ); ++ PG_FREE_IF_COPY(source,0); ++ ++ if ( typmod>=0 && typmod < charlen ) { ++ if ( isExplicit ) ++ mvarchar_strip(result, typmod); ++ else ++ elog(ERROR,"value too long for type mvarchar(%d)", typmod); ++ } ++ ++ PG_RETURN_MVARCHAR(result); ++} ++ ++PG_FUNCTION_INFO_V1(mvarchar_mchar); ++Datum mvarchar_mchar(PG_FUNCTION_ARGS); ++Datum ++mvarchar_mchar(PG_FUNCTION_ARGS) { ++ MVarChar *source = PG_GETARG_MVARCHAR(0); ++ MChar *result; ++ int32 typmod = PG_GETARG_INT32(1); ++#ifdef NOT_USED ++ bool isExplicit = PG_GETARG_BOOL(2); ++#endif ++ ++ result = palloc( MVARCHARLENGTH(source) + MCHARHDRSZ ); ++ SET_VARSIZE(result, MVARCHARLENGTH(source) + MCHARHDRSZ); ++ memcpy( result->data, source->data, MVARCHARLENGTH(source)); ++ ++ PG_FREE_IF_COPY(source,0); ++ ++ mchar_strip( result, typmod ); ++ ++ PG_RETURN_MCHAR(result); ++} ++ ++PG_FUNCTION_INFO_V1(mchar_mvarchar); ++Datum mchar_mvarchar(PG_FUNCTION_ARGS); ++Datum ++mchar_mvarchar(PG_FUNCTION_ARGS) { ++ MChar *source = PG_GETARG_MCHAR(0); ++ MVarChar *result; ++ int32 typmod = PG_GETARG_INT32(1); ++ int32 scharlen = u_countChar32(source->data, UCHARLENGTH(source)); ++ int32 curlen = 0, maxcharlen; ++#ifdef NOT_USED ++ bool isExplicit = PG_GETARG_BOOL(2); ++#endif ++ ++ maxcharlen = (source->typmod > 0) ? source->typmod : scharlen; ++ ++ result = palloc( MVARCHARHDRSZ + sizeof(UChar) * 2 * maxcharlen ); ++ ++ curlen = UCHARLENGTH( source ); ++ if ( curlen > 0 ) ++ memcpy( result->data, source->data, MCHARLENGTH(source) ); ++ if ( source->typmod > 0 && scharlen < source->typmod ) { ++ FillWhiteSpace( result->data + curlen, source->typmod-scharlen ); ++ curlen += source->typmod-scharlen; ++ } ++ SET_VARSIZE(result, MVARCHARHDRSZ + curlen *sizeof(UChar)); ++ ++ PG_FREE_IF_COPY(source,0); ++ ++ mvarchar_strip( result, typmod ); ++ ++ PG_RETURN_MCHAR(result); ++} ++ ++PG_FUNCTION_INFO_V1(mchar_send); ++Datum mchar_send(PG_FUNCTION_ARGS); ++Datum ++mchar_send(PG_FUNCTION_ARGS) { ++ MChar *in = PG_GETARG_MCHAR(0); ++ size_t inlen = UCHARLENGTH(in); ++ size_t charlen = u_countChar32(in->data, inlen); ++ StringInfoData buf; ++ ++ Assert( in->typmod < 0 || charlen<=in->typmod ); ++ ++ pq_begintypsend(&buf); ++ pq_sendbytes(&buf, (char*)in->data, inlen * sizeof(UChar) ); ++ ++ if ( in->typmod>0 && charlen < in->typmod ) { ++ int nw = in->typmod - charlen; ++ UChar *white = palloc( sizeof(UChar) * nw ); ++ ++ FillWhiteSpace( white, nw ); ++ pq_sendbytes(&buf, (char*)white, sizeof(UChar) * nw); ++ pfree(white); ++ } ++ ++ PG_FREE_IF_COPY(in,0); ++ ++ PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); ++} ++ ++PG_FUNCTION_INFO_V1(mchar_recv); ++Datum mchar_recv(PG_FUNCTION_ARGS); ++Datum ++mchar_recv(PG_FUNCTION_ARGS) { ++ StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); ++ MChar *res; ++ int nbytes; ++#ifdef NOT_USED ++ Oid typelem = PG_GETARG_OID(1); ++#endif ++ int32 atttypmod = PG_GETARG_INT32(2); ++ ++ nbytes = buf->len - buf->cursor; ++ res = (MChar*)palloc( nbytes + MCHARHDRSZ ); ++ res->len = nbytes + MCHARHDRSZ; ++ res->typmod = -1; ++ SET_VARSIZE(res, res->len); ++ pq_copymsgbytes(buf, (char*)res->data, nbytes); ++ ++ mchar_strip( res, atttypmod ); ++ ++ PG_RETURN_MCHAR(res); ++} ++ ++PG_FUNCTION_INFO_V1(mvarchar_send); ++Datum mvarchar_send(PG_FUNCTION_ARGS); ++Datum ++mvarchar_send(PG_FUNCTION_ARGS) { ++ MVarChar *in = PG_GETARG_MVARCHAR(0); ++ size_t inlen = UVARCHARLENGTH(in); ++ StringInfoData buf; ++ ++ pq_begintypsend(&buf); ++ pq_sendbytes(&buf, (char*)in->data, inlen * sizeof(UChar) ); ++ ++ PG_FREE_IF_COPY(in,0); ++ ++ PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); ++} ++ ++PG_FUNCTION_INFO_V1(mvarchar_recv); ++Datum mvarchar_recv(PG_FUNCTION_ARGS); ++Datum ++mvarchar_recv(PG_FUNCTION_ARGS) { ++ StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); ++ MVarChar *res; ++ int nbytes; ++#ifdef NOT_USED ++ Oid typelem = PG_GETARG_OID(1); ++#endif ++ int32 atttypmod = PG_GETARG_INT32(2); ++ ++ nbytes = buf->len - buf->cursor; ++ res = (MVarChar*)palloc( nbytes + MVARCHARHDRSZ ); ++ res->len = nbytes + MVARCHARHDRSZ; ++ SET_VARSIZE(res, res->len); ++ pq_copymsgbytes(buf, (char*)res->data, nbytes); ++ ++ mvarchar_strip( res, atttypmod ); ++ ++ PG_RETURN_MVARCHAR(res); ++} ++ ++ +diff --git a/contrib/mchar/mchar_like.c b/contrib/mchar/mchar_like.c +new file mode 100644 +index 0000000..3af13d8 +--- /dev/null ++++ b/contrib/mchar/mchar_like.c +@@ -0,0 +1,862 @@ ++#include "mchar.h" ++#include "mb/pg_wchar.h" ++ ++#include "catalog/pg_collation.h" ++#include "utils/selfuncs.h" ++#include "nodes/primnodes.h" ++#include "nodes/makefuncs.h" ++#include "regex/regex.h" ++ ++/* ++** Originally written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986. ++** Rich $alz is now . ++** Special thanks to Lars Mathiesen for the LABORT code. ++** ++** This code was shamelessly stolen from the "pql" code by myself and ++** slightly modified :) ++** ++** All references to the word "star" were replaced by "percent" ++** All references to the word "wild" were replaced by "like" ++** ++** All the nice shell RE matching stuff was replaced by just "_" and "%" ++** ++** As I don't have a copy of the SQL standard handy I wasn't sure whether ++** to leave in the '\' escape character handling. ++** ++** Keith Parks. ++** ++** SQL92 lets you specify the escape character by saying ++** LIKE ESCAPE . We are a small operation ++** so we force you to use '\'. - ay 7/95 ++** ++** Now we have the like_escape() function that converts patterns with ++** any specified escape character (or none at all) to the internal ++** default escape character, which is still '\'. - tgl 9/2000 ++** ++** The code is rewritten to avoid requiring null-terminated strings, ++** which in turn allows us to leave out some memcpy() operations. ++** This code should be faster and take less memory, but no promises... ++** - thomas 2000-08-06 ++** ++** Adopted for UTF-16 by teodor ++*/ ++ ++#define LIKE_TRUE 1 ++#define LIKE_FALSE 0 ++#define LIKE_ABORT (-1) ++ ++ ++static int ++uchareq(UChar *p1, UChar *p2) { ++ int l1=0, l2=0; ++ /* ++ * Count length of char: ++ * We suppose that string is correct!! ++ */ ++ U16_FWD_1(p1, l1, 2); ++ U16_FWD_1(p2, l2, 2); ++ ++ return (UCharCaseCompare(p1, l1, p2, l2)==0) ? 1 : 0; ++} ++ ++#define NextChar(p, plen) \ ++ do { \ ++ int __l = 0; \ ++ U16_FWD_1((p), __l, (plen));\ ++ (p) +=__l; \ ++ (plen) -=__l; \ ++ } while(0) ++ ++#define CopyAdvChar(dst, src, srclen) \ ++ do { \ ++ int __l = 0; \ ++ U16_FWD_1((src), __l, (srclen));\ ++ (srclen) -= __l; \ ++ while (__l-- > 0) \ ++ *(dst)++ = *(src)++; \ ++ } while (0) ++ ++ ++static UChar UCharPercent = 0; ++static UChar UCharBackSlesh = 0; ++static UChar UCharUnderLine = 0; ++static UChar UCharStar = 0; ++static UChar UCharDotDot = 0; ++static UChar UCharUp = 0; ++static UChar UCharLBracket = 0; ++static UChar UCharQ = 0; ++static UChar UCharRBracket = 0; ++static UChar UCharDollar = 0; ++static UChar UCharDot = 0; ++static UChar UCharLFBracket = 0; ++static UChar UCharRFBracket = 0; ++static UChar UCharQuote = 0; ++static UChar UCharSpace = 0; ++ ++#define MkUChar(uc, c) do { \ ++ char __c = (c); \ ++ u_charsToUChars( &__c, &(uc), 1 ); \ ++} while(0) ++ ++#define SET_UCHAR if ( UCharPercent == 0 ) { \ ++ MkUChar( UCharPercent, '%' ); \ ++ MkUChar( UCharBackSlesh, '\\' ); \ ++ MkUChar( UCharUnderLine, '_' ); \ ++ MkUChar( UCharStar, '*' ); \ ++ MkUChar( UCharDotDot, ':' ); \ ++ MkUChar( UCharUp, '^' ); \ ++ MkUChar( UCharLBracket, '(' ); \ ++ MkUChar( UCharQ, '?' ); \ ++ MkUChar( UCharRBracket, ')' ); \ ++ MkUChar( UCharDollar, '$' ); \ ++ MkUChar( UCharDot, '.' ); \ ++ MkUChar( UCharLFBracket, '{' ); \ ++ MkUChar( UCharRFBracket, '}' ); \ ++ MkUChar( UCharQuote, '"' ); \ ++ MkUChar( UCharSpace, ' ' ); \ ++ } ++ ++int ++m_isspace(UChar c) { ++ SET_UCHAR; ++ ++ return (c == UCharSpace); ++} ++ ++static int ++MatchUChar(UChar *t, int tlen, UChar *p, int plen) { ++ SET_UCHAR; ++ ++ /* Fast path for match-everything pattern */ ++ if ((plen == 1) && (*p == UCharPercent)) ++ return LIKE_TRUE; ++ ++ while ((tlen > 0) && (plen > 0)) { ++ if (*p == UCharBackSlesh) { ++ /* Next pattern char must match literally, whatever it is */ ++ NextChar(p, plen); ++ if ((plen <= 0) || !uchareq(t, p)) ++ return LIKE_FALSE; ++ } else if (*p == UCharPercent) { ++ /* %% is the same as % according to the SQL standard */ ++ /* Advance past all %'s */ ++ while ((plen > 0) && (*p == UCharPercent)) ++ NextChar(p, plen); ++ /* Trailing percent matches everything. */ ++ if (plen <= 0) ++ return LIKE_TRUE; ++ ++ /* ++ * Otherwise, scan for a text position at which we can match the ++ * rest of the pattern. ++ */ ++ while (tlen > 0) { ++ /* ++ * Optimization to prevent most recursion: don't recurse ++ * unless first pattern char might match this text char. ++ */ ++ if (uchareq(t, p) || (*p == UCharBackSlesh) || (*p == UCharUnderLine)) { ++ int matched = MatchUChar(t, tlen, p, plen); ++ ++ if (matched != LIKE_FALSE) ++ return matched; /* TRUE or ABORT */ ++ } ++ ++ NextChar(t, tlen); ++ } ++ ++ /* ++ * End of text with no match, so no point in trying later places ++ * to start matching this pattern. ++ */ ++ return LIKE_ABORT; ++ } if ((*p != UCharUnderLine) && !uchareq(t, p)) { ++ /* ++ * Not the single-character wildcard and no explicit match? Then ++ * time to quit... ++ */ ++ return LIKE_FALSE; ++ } ++ ++ NextChar(t, tlen); ++ NextChar(p, plen); ++ } ++ ++ if (tlen > 0) ++ return LIKE_FALSE; /* end of pattern, but not of text */ ++ ++ /* End of input string. Do we have matching pattern remaining? */ ++ while ((plen > 0) && (*p == UCharPercent)) /* allow multiple %'s at end of ++ * pattern */ ++ NextChar(p, plen); ++ if (plen <= 0) ++ return LIKE_TRUE; ++ ++ /* ++ * End of text with no match, so no point in trying later places to start ++ * matching this pattern. ++ */ ++ ++ return LIKE_ABORT; ++} ++ ++PG_FUNCTION_INFO_V1( mvarchar_like ); ++Datum mvarchar_like( PG_FUNCTION_ARGS ); ++Datum ++mvarchar_like( PG_FUNCTION_ARGS ) { ++ MVarChar *str = PG_GETARG_MVARCHAR(0); ++ MVarChar *pat = PG_GETARG_MVARCHAR(1); ++ bool result; ++ ++ result = MatchUChar( str->data, UVARCHARLENGTH(str), pat->data, UVARCHARLENGTH(pat) ); ++ ++ PG_FREE_IF_COPY(str,0); ++ PG_FREE_IF_COPY(pat,1); ++ ++ PG_RETURN_BOOL(result == LIKE_TRUE); ++} ++ ++PG_FUNCTION_INFO_V1( mvarchar_notlike ); ++Datum mvarchar_notlike( PG_FUNCTION_ARGS ); ++Datum ++mvarchar_notlike( PG_FUNCTION_ARGS ) { ++ bool res = DatumGetBool( DirectFunctionCall2( ++ mvarchar_like, ++ PG_GETARG_DATUM(0), ++ PG_GETARG_DATUM(1) ++ )); ++ PG_RETURN_BOOL( !res ); ++} ++ ++/* ++ * Removes trailing spaces in '111 %' pattern ++ */ ++static UChar * ++removeTrailingSpaces( UChar *src, int srclen, int *dstlen, bool *isSpecialLast) { ++ UChar* dst = src; ++ UChar *ptr, *dptr, *markptr; ++ ++ *dstlen = srclen; ++ ptr = src + srclen-1; ++ SET_UCHAR; ++ ++ *isSpecialLast = ( srclen > 0 && (u_isspace(*ptr) || *ptr == UCharPercent || *ptr == UCharUnderLine ) ) ? true : false; ++ while( ptr>=src ) { ++ if ( *ptr == UCharPercent || *ptr == UCharUnderLine ) { ++ if ( ptr==src ) ++ return dst; /* first character */ ++ ++ if ( *(ptr-1) == UCharBackSlesh ) ++ return dst; /* use src as is */ ++ ++ if ( u_isspace( *(ptr-1) ) ) { ++ ptr--; ++ break; /* % or _ is after space which should be removed */ ++ } ++ } else { ++ return dst; ++ } ++ ptr--; ++ } ++ ++ markptr = ptr+1; ++ dst = (UChar*)palloc( sizeof(UChar) * srclen ); ++ ++ /* find last non-space character */ ++ while( ptr>=src && u_isspace(*ptr) ) ++ ptr--; ++ ++ dptr = dst + (ptr-src+1); ++ ++ if ( ptr>=src ) ++ memcpy( dst, src, sizeof(UChar) * (ptr-src+1) ); ++ ++ while( markptr - src < srclen ) { ++ *dptr = *markptr; ++ dptr++; ++ markptr++; ++ } ++ ++ *dstlen = dptr - dst; ++ return dst; ++} ++ ++static UChar* ++addTrailingSpace( MChar *src, int *newlen ) { ++ int scharlen = u_countChar32(src->data, UCHARLENGTH(src)); ++ ++ if ( src->typmod > scharlen ) { ++ UChar *res = (UChar*) palloc( sizeof(UChar) * (UCHARLENGTH(src) + src->typmod) ); ++ ++ memcpy( res, src->data, sizeof(UChar) * UCHARLENGTH(src)); ++ FillWhiteSpace( res+UCHARLENGTH(src), src->typmod - scharlen ); ++ ++ *newlen = src->typmod; ++ ++ return res; ++ } else { ++ *newlen = UCHARLENGTH(src); ++ return src->data; ++ } ++} ++ ++PG_FUNCTION_INFO_V1( mchar_like ); ++Datum mchar_like( PG_FUNCTION_ARGS ); ++Datum ++mchar_like( PG_FUNCTION_ARGS ) { ++ MChar *str = PG_GETARG_MCHAR(0); ++ MVarChar *pat = PG_GETARG_MVARCHAR(1); ++ bool result, isNeedAdd = false; ++ UChar *cleaned, *filled; ++ int clen=0, flen=0; ++ ++ cleaned = removeTrailingSpaces(pat->data, UVARCHARLENGTH(pat), &clen, &isNeedAdd); ++ if ( isNeedAdd ) ++ filled = addTrailingSpace(str, &flen); ++ else { ++ filled = str->data; ++ flen = UCHARLENGTH(str); ++ } ++ ++ result = MatchUChar( filled, flen, cleaned, clen ); ++ ++ if ( pat->data != cleaned ) ++ pfree( cleaned ); ++ if ( str->data != filled ) ++ pfree( filled ); ++ ++ PG_FREE_IF_COPY(str,0); ++ PG_FREE_IF_COPY(pat,1); ++ ++ ++ PG_RETURN_BOOL(result == LIKE_TRUE); ++} ++ ++PG_FUNCTION_INFO_V1( mchar_notlike ); ++Datum mchar_notlike( PG_FUNCTION_ARGS ); ++Datum ++mchar_notlike( PG_FUNCTION_ARGS ) { ++ bool res = DatumGetBool( DirectFunctionCall2( ++ mchar_like, ++ PG_GETARG_DATUM(0), ++ PG_GETARG_DATUM(1) ++ )); ++ ++ PG_RETURN_BOOL( !res ); ++} ++ ++ ++ ++PG_FUNCTION_INFO_V1( mchar_pattern_fixed_prefix ); ++Datum mchar_pattern_fixed_prefix( PG_FUNCTION_ARGS ); ++Datum ++mchar_pattern_fixed_prefix( PG_FUNCTION_ARGS ) { ++ Const *patt = (Const*)PG_GETARG_POINTER(0); ++ Pattern_Type ptype = (Pattern_Type)PG_GETARG_INT32(1); ++ Const **prefix = (Const**)PG_GETARG_POINTER(2); ++ UChar *spatt; ++ int32 slen, prefixlen=0, restlen=0, i=0; ++ MVarChar *sprefix; ++ MVarChar *srest; ++ Pattern_Prefix_Status status = Pattern_Prefix_None; ++ ++ *prefix = NULL; ++ ++ if ( ptype != Pattern_Type_Like ) ++ PG_RETURN_INT32(Pattern_Prefix_None); ++ ++ SET_UCHAR; ++ ++ spatt = ((MVarChar*)DatumGetPointer(patt->constvalue))->data; ++ slen = UVARCHARLENGTH( DatumGetPointer(patt->constvalue) ); ++ ++ sprefix = (MVarChar*)palloc( MCHARHDRSZ /*The biggest hdr!! */ + sizeof(UChar) * slen ); ++ srest = (MVarChar*)palloc( MCHARHDRSZ /*The biggest hdr!! */ + sizeof(UChar) * slen ); ++ ++ while( prefixlen < slen && i < slen ) { ++ if ( spatt[i] == UCharPercent || spatt[i] == UCharUnderLine ) ++ break; ++ else if ( spatt[i] == UCharBackSlesh ) { ++ i++; ++ if ( i>= slen ) ++ break; ++ } ++ sprefix->data[ prefixlen++ ] = spatt[i++]; ++ } ++ ++ while( prefixlen > 0 ) { ++ if ( ! u_isspace( sprefix->data[ prefixlen-1 ] ) ) ++ break; ++ prefixlen--; ++ } ++ ++ if ( prefixlen == 0 ) ++ PG_RETURN_INT32(Pattern_Prefix_None); ++ ++ for(;idata[ restlen++ ] = spatt[i]; ++ ++ SET_VARSIZE(sprefix, sizeof(UChar) * prefixlen + MVARCHARHDRSZ); ++ SET_VARSIZE(srest, sizeof(UChar) * restlen + MVARCHARHDRSZ); ++ ++ *prefix = makeConst( patt->consttype, -1, DEFAULT_COLLATION_OID, VARSIZE(sprefix), PointerGetDatum(sprefix), false, false ); ++ ++ if ( prefixlen == slen ) /* in LIKE, an empty pattern is an exact match! */ ++ status = Pattern_Prefix_Exact; ++ else if ( prefixlen > 0 ) ++ status = Pattern_Prefix_Partial; ++ ++ PG_RETURN_INT32( status ); ++} ++ ++static bool ++checkCmp( UChar *left, int32 leftlen, UChar *right, int32 rightlen ) { ++ ++ return (UCharCaseCompare( left, leftlen, right, rightlen) < 0 ) ? true : false; ++} ++ ++ ++PG_FUNCTION_INFO_V1( mchar_greaterstring ); ++Datum mchar_greaterstring( PG_FUNCTION_ARGS ); ++Datum ++mchar_greaterstring( PG_FUNCTION_ARGS ) { ++ Const *patt = (Const*)PG_GETARG_POINTER(0); ++ char *src = (char*)DatumGetPointer( patt->constvalue ); ++ int dstlen, srclen = VARSIZE(src); ++ char *dst = palloc( srclen ); ++ UChar *ptr, *srcptr; ++ ++ memcpy( dst, src, srclen ); ++ ++ srclen = dstlen = UVARCHARLENGTH( dst ); ++ ptr = ((MVarChar*)dst)->data; ++ srcptr = ((MVarChar*)src)->data; ++ ++ while( dstlen > 0 ) { ++ UChar *lastchar = ptr + dstlen - 1; ++ ++ if ( !U16_IS_LEAD( *lastchar ) ) { ++ while( *lastchar<0xffff ) { ++ ++ (*lastchar)++; ++ ++ if ( ublock_getCode(*lastchar) == UBLOCK_INVALID_CODE || !checkCmp( srcptr, srclen, ptr, dstlen ) ) ++ continue; ++ else { ++ SET_VARSIZE(dst, sizeof(UChar) * dstlen + MVARCHARHDRSZ); ++ ++ PG_RETURN_POINTER( makeConst( patt->consttype, -1, DEFAULT_COLLATION_OID, VARSIZE(dst), PointerGetDatum(dst), false, false ) ); ++ } ++ } ++ } ++ ++ dstlen--; ++ } ++ ++ PG_RETURN_POINTER(NULL); ++} ++ ++static int ++do_like_escape( UChar *pat, int plen, UChar *esc, int elen, UChar *result) { ++ UChar *p = pat,*e =esc ,*r; ++ bool afterescape; ++ ++ r = result; ++ SET_UCHAR; ++ ++ if ( elen == 0 ) { ++ /* ++ * No escape character is wanted. Double any backslashes in the ++ * pattern to make them act like ordinary characters. ++ */ ++ while (plen > 0) { ++ if (*p == UCharBackSlesh ) ++ *r++ = UCharBackSlesh; ++ CopyAdvChar(r, p, plen); ++ } ++ } else { ++ /* ++ * The specified escape must be only a single character. ++ */ ++ NextChar(e, elen); ++ ++ if (elen != 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_INVALID_ESCAPE_SEQUENCE), ++ errmsg("invalid escape string"), ++ errhint("Escape string must be empty or one character."))); ++ ++ e = esc; ++ ++ /* ++ * If specified escape is '\', just copy the pattern as-is. ++ */ ++ if ( *e == UCharBackSlesh ) { ++ memcpy(result, pat, plen * sizeof(UChar)); ++ return plen; ++ } ++ ++ /* ++ * Otherwise, convert occurrences of the specified escape character to ++ * '\', and double occurrences of '\' --- unless they immediately ++ * follow an escape character! ++ */ ++ afterescape = false; ++ ++ while (plen > 0) { ++ if ( uchareq(p,e) && !afterescape) { ++ *r++ = UCharBackSlesh; ++ NextChar(p, plen); ++ afterescape = true; ++ } else if ( *p == UCharBackSlesh ) { ++ *r++ = UCharBackSlesh; ++ if (!afterescape) ++ *r++ = UCharBackSlesh; ++ NextChar(p, plen); ++ afterescape = false; ++ } else { ++ CopyAdvChar(r, p, plen); ++ afterescape = false; ++ } ++ } ++ } ++ ++ return ( r - result ); ++} ++ ++PG_FUNCTION_INFO_V1( mvarchar_like_escape ); ++Datum mvarchar_like_escape( PG_FUNCTION_ARGS ); ++Datum ++mvarchar_like_escape( PG_FUNCTION_ARGS ) { ++ MVarChar *pat = PG_GETARG_MVARCHAR(0); ++ MVarChar *esc = PG_GETARG_MVARCHAR(1); ++ MVarChar *result; ++ ++ result = (MVarChar*)palloc( MVARCHARHDRSZ + sizeof(UChar)*2*UVARCHARLENGTH(pat) ); ++ result->len = MVARCHARHDRSZ + do_like_escape( pat->data, UVARCHARLENGTH(pat), ++ esc->data, UVARCHARLENGTH(esc), ++ result->data ) * sizeof(UChar); ++ ++ SET_VARSIZE(result, result->len); ++ PG_FREE_IF_COPY(pat,0); ++ PG_FREE_IF_COPY(esc,1); ++ ++ PG_RETURN_MVARCHAR(result); ++} ++ ++#define RE_CACHE_SIZE 32 ++typedef struct ReCache { ++ UChar *pattern; ++ int length; ++ int flags; ++ regex_t re; ++} ReCache; ++ ++static int num_res = 0; ++static ReCache re_array[RE_CACHE_SIZE]; /* cached re's */ ++static const int mchar_regex_flavor = REG_ADVANCED | REG_ICASE; ++ ++static regex_t * ++RE_compile_and_cache(UChar *text_re, int text_re_len, int cflags) { ++ pg_wchar *pattern; ++ size_t pattern_len; ++ int i; ++ int regcomp_result; ++ ReCache re_temp; ++ char errMsg[128]; ++ ++ ++ for (i = 0; i < num_res; i++) { ++ if ( re_array[i].length == text_re_len && ++ re_array[i].flags == cflags && ++ memcmp(re_array[i].pattern, text_re, sizeof(UChar)*text_re_len) == 0 ) { ++ ++ /* Found, move it to front */ ++ if ( i>0 ) { ++ re_temp = re_array[i]; ++ memmove(&re_array[1], &re_array[0], i * sizeof(ReCache)); ++ re_array[0] = re_temp; ++ } ++ ++ return &re_array[0].re; ++ } ++ } ++ ++ pattern = (pg_wchar *) palloc((1 + text_re_len) * sizeof(pg_wchar)); ++ pattern_len = UChar2Wchar(text_re, text_re_len, pattern); ++ ++ regcomp_result = pg_regcomp(&re_temp.re, ++ pattern, ++ pattern_len, ++ cflags, ++ DEFAULT_COLLATION_OID); ++ pfree( pattern ); ++ ++ if (regcomp_result != REG_OKAY) { ++ pg_regerror(regcomp_result, &re_temp.re, errMsg, sizeof(errMsg)); ++ ereport(ERROR, ++ (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION), ++ errmsg("invalid regular expression: %s", errMsg))); ++ } ++ ++ re_temp.pattern = malloc( text_re_len*sizeof(UChar) ); ++ if ( re_temp.pattern == NULL ) ++ elog(ERROR,"Out of memory"); ++ ++ memcpy(re_temp.pattern, text_re, text_re_len*sizeof(UChar) ); ++ re_temp.length = text_re_len; ++ re_temp.flags = cflags; ++ ++ if (num_res >= RE_CACHE_SIZE) { ++ --num_res; ++ Assert(num_res < RE_CACHE_SIZE); ++ pg_regfree(&re_array[num_res].re); ++ free(re_array[num_res].pattern); ++ } ++ ++ if (num_res > 0) ++ memmove(&re_array[1], &re_array[0], num_res * sizeof(ReCache)); ++ ++ re_array[0] = re_temp; ++ num_res++; ++ ++ return &re_array[0].re; ++} ++ ++static bool ++RE_compile_and_execute(UChar *pat, int pat_len, UChar *dat, int dat_len, ++ int cflags, int nmatch, regmatch_t *pmatch) { ++ pg_wchar *data; ++ size_t data_len; ++ int regexec_result; ++ regex_t *re; ++ char errMsg[128]; ++ ++ data = (pg_wchar *) palloc((1+dat_len) * sizeof(pg_wchar)); ++ data_len = UChar2Wchar(dat, dat_len, data); ++ ++ re = RE_compile_and_cache(pat, pat_len, cflags); ++ ++ regexec_result = pg_regexec(re, ++ data, ++ data_len, ++ 0, ++ NULL, ++ nmatch, ++ pmatch, ++ 0); ++ pfree(data); ++ ++ if (regexec_result != REG_OKAY && regexec_result != REG_NOMATCH) { ++ /* re failed??? */ ++ pg_regerror(regexec_result, re, errMsg, sizeof(errMsg)); ++ ereport(ERROR, ++ (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION), ++ errmsg("regular expression failed: %s", errMsg))); ++ } ++ ++ return (regexec_result == REG_OKAY); ++} ++ ++PG_FUNCTION_INFO_V1( mchar_regexeq ); ++Datum mchar_regexeq( PG_FUNCTION_ARGS ); ++Datum ++mchar_regexeq( PG_FUNCTION_ARGS ) { ++ MChar *t = PG_GETARG_MCHAR(0); ++ MChar *p = PG_GETARG_MCHAR(1); ++ bool res; ++ ++ res = RE_compile_and_execute(p->data, UCHARLENGTH(p), ++ t->data, UCHARLENGTH(t), ++ mchar_regex_flavor, ++ 0, NULL); ++ PG_FREE_IF_COPY(t, 0); ++ PG_FREE_IF_COPY(p, 1); ++ ++ PG_RETURN_BOOL(res); ++} ++ ++PG_FUNCTION_INFO_V1( mchar_regexne ); ++Datum mchar_regexne( PG_FUNCTION_ARGS ); ++Datum ++mchar_regexne( PG_FUNCTION_ARGS ) { ++ MChar *t = PG_GETARG_MCHAR(0); ++ MChar *p = PG_GETARG_MCHAR(1); ++ bool res; ++ ++ res = RE_compile_and_execute(p->data, UCHARLENGTH(p), ++ t->data, UCHARLENGTH(t), ++ mchar_regex_flavor, ++ 0, NULL); ++ PG_FREE_IF_COPY(t, 0); ++ PG_FREE_IF_COPY(p, 1); ++ ++ PG_RETURN_BOOL(!res); ++} ++ ++PG_FUNCTION_INFO_V1( mvarchar_regexeq ); ++Datum mvarchar_regexeq( PG_FUNCTION_ARGS ); ++Datum ++mvarchar_regexeq( PG_FUNCTION_ARGS ) { ++ MVarChar *t = PG_GETARG_MVARCHAR(0); ++ MVarChar *p = PG_GETARG_MVARCHAR(1); ++ bool res; ++ ++ res = RE_compile_and_execute(p->data, UVARCHARLENGTH(p), ++ t->data, UVARCHARLENGTH(t), ++ mchar_regex_flavor, ++ 0, NULL); ++ PG_FREE_IF_COPY(t, 0); ++ PG_FREE_IF_COPY(p, 1); ++ ++ PG_RETURN_BOOL(res); ++} ++ ++PG_FUNCTION_INFO_V1( mvarchar_regexne ); ++Datum mvarchar_regexne( PG_FUNCTION_ARGS ); ++Datum ++mvarchar_regexne( PG_FUNCTION_ARGS ) { ++ MVarChar *t = PG_GETARG_MVARCHAR(0); ++ MVarChar *p = PG_GETARG_MVARCHAR(1); ++ bool res; ++ ++ res = RE_compile_and_execute(p->data, UVARCHARLENGTH(p), ++ t->data, UVARCHARLENGTH(t), ++ mchar_regex_flavor, ++ 0, NULL); ++ PG_FREE_IF_COPY(t, 0); ++ PG_FREE_IF_COPY(p, 1); ++ ++ PG_RETURN_BOOL(!res); ++} ++ ++static int ++do_similar_escape(UChar *p, int plen, UChar *e, int elen, UChar *result) { ++ UChar *r; ++ bool afterescape = false; ++ int nquotes = 0; ++ ++ SET_UCHAR; ++ ++ if (e==NULL || elen <0 ) { ++ e = &UCharBackSlesh; ++ elen = 1; ++ } else { ++ if ( elen == 0 ) ++ e = NULL; ++ else if ( elen != 1) ++ ereport(ERROR, ++ (errcode(ERRCODE_INVALID_ESCAPE_SEQUENCE), ++ errmsg("invalid escape string"), ++ errhint("Escape string must be empty or one character."))); ++ } ++ ++ /* ++ * Look explanation of following in ./utils/adt/regexp.c ++ */ ++ r = result; ++ ++ *r++ = UCharStar; ++ *r++ = UCharStar; ++ *r++ = UCharStar; ++ *r++ = UCharDotDot; ++ *r++ = UCharUp; ++ *r++ = UCharLBracket; ++ *r++ = UCharQ; ++ *r++ = UCharDotDot; ++ ++ while( plen>0 ) { ++ if (afterescape) { ++ if ( *p == UCharQuote ) { ++ *r++ = ((nquotes++ % 2) == 0) ? UCharLBracket : UCharRBracket; ++ } else { ++ *r++ = UCharBackSlesh; ++ *r++ = *p; ++ } ++ afterescape = false; ++ } else if ( e && *p == *e ) { ++ afterescape = true; ++ } else if ( *p == UCharPercent ) { ++ *r++ = UCharDot; ++ *r++ = UCharStar; ++ } else if ( *p == UCharUnderLine ) { ++ *r++ = UCharDot; ++ } else if ( *p == UCharBackSlesh || *p == UCharDot || *p == UCharQ || *p == UCharLFBracket ) { ++ *r++ = UCharBackSlesh; ++ *r++ = *p; ++ } else ++ *r++ = *p; ++ ++ p++, plen--; ++ } ++ ++ *r++ = UCharRBracket; ++ *r++ = UCharDollar; ++ ++ return r-result; ++} ++ ++PG_FUNCTION_INFO_V1( mchar_similar_escape ); ++Datum mchar_similar_escape( PG_FUNCTION_ARGS ); ++Datum ++mchar_similar_escape( PG_FUNCTION_ARGS ) { ++ MChar *pat; ++ MChar *esc; ++ MChar *result; ++ ++ if (PG_ARGISNULL(0)) ++ PG_RETURN_NULL(); ++ pat = PG_GETARG_MCHAR(0); ++ ++ if (PG_ARGISNULL(1)) { ++ esc = NULL; ++ } else { ++ esc = PG_GETARG_MCHAR(1); ++ } ++ ++ result = (MChar*)palloc( MCHARHDRSZ + sizeof(UChar)*(10 + 2*UCHARLENGTH(pat)) ); ++ result->len = MCHARHDRSZ + do_similar_escape( pat->data, UCHARLENGTH(pat), ++ (esc) ? esc->data : NULL, (esc) ? UCHARLENGTH(esc) : -1, ++ result->data ) * sizeof(UChar); ++ result->typmod=-1; ++ ++ SET_VARSIZE(result, result->len); ++ PG_FREE_IF_COPY(pat,0); ++ if ( esc ) ++ PG_FREE_IF_COPY(esc,1); ++ ++ PG_RETURN_MCHAR(result); ++} ++ ++PG_FUNCTION_INFO_V1( mvarchar_similar_escape ); ++Datum mvarchar_similar_escape( PG_FUNCTION_ARGS ); ++Datum ++mvarchar_similar_escape( PG_FUNCTION_ARGS ) { ++ MVarChar *pat; ++ MVarChar *esc; ++ MVarChar *result; ++ ++ if (PG_ARGISNULL(0)) ++ PG_RETURN_NULL(); ++ pat = PG_GETARG_MVARCHAR(0); ++ ++ if (PG_ARGISNULL(1)) { ++ esc = NULL; ++ } else { ++ esc = PG_GETARG_MVARCHAR(1); ++ } ++ ++ result = (MVarChar*)palloc( MVARCHARHDRSZ + sizeof(UChar)*(10 + 2*UVARCHARLENGTH(pat)) ); ++ result->len = MVARCHARHDRSZ + do_similar_escape( pat->data, UVARCHARLENGTH(pat), ++ (esc) ? esc->data : NULL, (esc) ? UVARCHARLENGTH(esc) : -1, ++ result->data ) * sizeof(UChar); ++ ++ SET_VARSIZE(result, result->len); ++ PG_FREE_IF_COPY(pat,0); ++ if ( esc ) ++ PG_FREE_IF_COPY(esc,1); ++ ++ PG_RETURN_MVARCHAR(result); ++} ++ ++#define RE_CACHE_SIZE 32 +diff --git a/contrib/mchar/mchar_op.c b/contrib/mchar/mchar_op.c +new file mode 100644 +index 0000000..4694d9c +--- /dev/null ++++ b/contrib/mchar/mchar_op.c +@@ -0,0 +1,449 @@ ++#include "mchar.h" ++ ++int ++lengthWithoutSpaceVarChar(MVarChar *m) { ++ int l = UVARCHARLENGTH(m); ++ ++ while( l>0 && m_isspace( m->data[ l-1 ] ) ) ++ l--; ++ ++ return l; ++} ++ ++int ++lengthWithoutSpaceChar(MChar *m) { ++ int l = UCHARLENGTH(m); ++ ++ while( l>0 && m_isspace( m->data[ l-1 ] ) ) ++ l--; ++ ++ return l; ++} ++ ++static inline int ++mchar_icase_compare( MChar *a, MChar *b ) { ++ return UCharCaseCompare( ++ a->data, lengthWithoutSpaceChar(a), ++ b->data, lengthWithoutSpaceChar(b) ++ ); ++} ++ ++static inline int ++mchar_case_compare( MChar *a, MChar *b ) { ++ return UCharCompare( ++ a->data, lengthWithoutSpaceChar(a), ++ b->data, lengthWithoutSpaceChar(b) ++ ); ++} ++ ++#define MCHARCMPFUNC( c, type, action, ret ) \ ++PG_FUNCTION_INFO_V1( mchar_##c##_##type ); \ ++Datum mchar_##c##_##type(PG_FUNCTION_ARGS);\ ++Datum \ ++mchar_##c##_##type(PG_FUNCTION_ARGS) { \ ++ MChar *a = PG_GETARG_MCHAR(0); \ ++ MChar *b = PG_GETARG_MCHAR(1); \ ++ int res = mchar_##c##_compare(a,b); \ ++ \ ++ PG_FREE_IF_COPY(a,0); \ ++ PG_FREE_IF_COPY(b,1); \ ++ PG_RETURN_##ret( res action 0 ); \ ++} ++ ++ ++MCHARCMPFUNC( case, eq, ==, BOOL ) ++MCHARCMPFUNC( case, ne, !=, BOOL ) ++MCHARCMPFUNC( case, lt, <, BOOL ) ++MCHARCMPFUNC( case, le, <=, BOOL ) ++MCHARCMPFUNC( case, ge, >=, BOOL ) ++MCHARCMPFUNC( case, gt, >, BOOL ) ++MCHARCMPFUNC( case, cmp, +, INT32 ) ++ ++MCHARCMPFUNC( icase, eq, ==, BOOL ) ++MCHARCMPFUNC( icase, ne, !=, BOOL ) ++MCHARCMPFUNC( icase, lt, <, BOOL ) ++MCHARCMPFUNC( icase, le, <=, BOOL ) ++MCHARCMPFUNC( icase, ge, >=, BOOL ) ++MCHARCMPFUNC( icase, gt, >, BOOL ) ++MCHARCMPFUNC( icase, cmp, +, INT32 ) ++ ++PG_FUNCTION_INFO_V1( mchar_larger ); ++Datum mchar_larger( PG_FUNCTION_ARGS ); ++Datum ++mchar_larger( PG_FUNCTION_ARGS ) { ++ MChar *a = PG_GETARG_MCHAR(0); ++ MChar *b = PG_GETARG_MCHAR(1); ++ MChar *r; ++ ++ r = ( mchar_icase_compare(a,b) > 0 ) ? a : b; ++ ++ PG_RETURN_MCHAR(r); ++} ++ ++PG_FUNCTION_INFO_V1( mchar_smaller ); ++Datum mchar_smaller( PG_FUNCTION_ARGS ); ++Datum ++mchar_smaller( PG_FUNCTION_ARGS ) { ++ MChar *a = PG_GETARG_MCHAR(0); ++ MChar *b = PG_GETARG_MCHAR(1); ++ MChar *r; ++ ++ r = ( mchar_icase_compare(a,b) < 0 ) ? a : b; ++ ++ PG_RETURN_MCHAR(r); ++} ++ ++ ++PG_FUNCTION_INFO_V1( mchar_concat ); ++Datum mchar_concat( PG_FUNCTION_ARGS ); ++Datum ++mchar_concat( PG_FUNCTION_ARGS ) { ++ MChar *a = PG_GETARG_MCHAR(0); ++ MChar *b = PG_GETARG_MCHAR(1); ++ MChar *result; ++ int maxcharlen, curlen; ++ int acharlen = u_countChar32(a->data, UCHARLENGTH(a)), ++ bcharlen = u_countChar32(b->data, UCHARLENGTH(b)); ++ ++ ++ maxcharlen = ((a->typmod<=0) ? acharlen : a->typmod) + ++ ((b->typmod<=0) ? bcharlen : b->typmod); ++ ++ result = (MChar*)palloc( MCHARHDRSZ + sizeof(UChar) * 2 * maxcharlen ); ++ ++ curlen = UCHARLENGTH( a ); ++ if ( curlen > 0 ) ++ memcpy( result->data, a->data, MCHARLENGTH(a) ); ++ if ( a->typmod > 0 && acharlen < a->typmod ) { ++ FillWhiteSpace( result->data + curlen, a->typmod-acharlen ); ++ curlen += a->typmod-acharlen; ++ } ++ ++ if ( UCHARLENGTH(b) > 0 ) { ++ memcpy( result->data + curlen, b->data, MCHARLENGTH( b ) ); ++ curlen += UCHARLENGTH( b ); ++ } ++ if ( b->typmod > 0 && bcharlen < b->typmod ) { ++ FillWhiteSpace( result->data + curlen, b->typmod-bcharlen ); ++ curlen += b->typmod-bcharlen; ++ } ++ ++ ++ result->typmod = -1; ++ SET_VARSIZE(result, sizeof(UChar) * curlen + MCHARHDRSZ); ++ ++ PG_FREE_IF_COPY(a,0); ++ PG_FREE_IF_COPY(b,1); ++ ++ PG_RETURN_MCHAR(result); ++} ++ ++static inline int ++mvarchar_icase_compare( MVarChar *a, MVarChar *b ) { ++ ++ return UCharCaseCompare( ++ a->data, lengthWithoutSpaceVarChar(a), ++ b->data, lengthWithoutSpaceVarChar(b) ++ ); ++} ++ ++static inline int ++mvarchar_case_compare( MVarChar *a, MVarChar *b ) { ++ return UCharCompare( ++ a->data, lengthWithoutSpaceVarChar(a), ++ b->data, lengthWithoutSpaceVarChar(b) ++ ); ++} ++ ++#define MVARCHARCMPFUNC( c, type, action, ret ) \ ++PG_FUNCTION_INFO_V1( mvarchar_##c##_##type ); \ ++Datum mvarchar_##c##_##type(PG_FUNCTION_ARGS); \ ++Datum \ ++mvarchar_##c##_##type(PG_FUNCTION_ARGS) { \ ++ MVarChar *a = PG_GETARG_MVARCHAR(0); \ ++ MVarChar *b = PG_GETARG_MVARCHAR(1); \ ++ int res = mvarchar_##c##_compare(a,b); \ ++ \ ++ PG_FREE_IF_COPY(a,0); \ ++ PG_FREE_IF_COPY(b,1); \ ++ PG_RETURN_##ret( res action 0 ); \ ++} ++ ++ ++MVARCHARCMPFUNC( case, eq, ==, BOOL ) ++MVARCHARCMPFUNC( case, ne, !=, BOOL ) ++MVARCHARCMPFUNC( case, lt, <, BOOL ) ++MVARCHARCMPFUNC( case, le, <=, BOOL ) ++MVARCHARCMPFUNC( case, ge, >=, BOOL ) ++MVARCHARCMPFUNC( case, gt, >, BOOL ) ++MVARCHARCMPFUNC( case, cmp, +, INT32 ) ++ ++MVARCHARCMPFUNC( icase, eq, ==, BOOL ) ++MVARCHARCMPFUNC( icase, ne, !=, BOOL ) ++MVARCHARCMPFUNC( icase, lt, <, BOOL ) ++MVARCHARCMPFUNC( icase, le, <=, BOOL ) ++MVARCHARCMPFUNC( icase, ge, >=, BOOL ) ++MVARCHARCMPFUNC( icase, gt, >, BOOL ) ++MVARCHARCMPFUNC( icase, cmp, +, INT32 ) ++ ++PG_FUNCTION_INFO_V1( mvarchar_larger ); ++Datum mvarchar_larger( PG_FUNCTION_ARGS ); ++Datum ++mvarchar_larger( PG_FUNCTION_ARGS ) { ++ MVarChar *a = PG_GETARG_MVARCHAR(0); ++ MVarChar *b = PG_GETARG_MVARCHAR(1); ++ MVarChar *r; ++ ++ r = ( mvarchar_icase_compare(a,b) > 0 ) ? a : b; ++ ++ PG_RETURN_MVARCHAR(r); ++} ++ ++PG_FUNCTION_INFO_V1( mvarchar_smaller ); ++Datum mvarchar_smaller( PG_FUNCTION_ARGS ); ++Datum ++mvarchar_smaller( PG_FUNCTION_ARGS ) { ++ MVarChar *a = PG_GETARG_MVARCHAR(0); ++ MVarChar *b = PG_GETARG_MVARCHAR(1); ++ MVarChar *r; ++ ++ r = ( mvarchar_icase_compare(a,b) < 0 ) ? a : b; ++ ++ PG_RETURN_MVARCHAR(r); ++} ++ ++PG_FUNCTION_INFO_V1( mvarchar_concat ); ++Datum mvarchar_concat( PG_FUNCTION_ARGS ); ++Datum ++mvarchar_concat( PG_FUNCTION_ARGS ) { ++ MVarChar *a = PG_GETARG_MVARCHAR(0); ++ MVarChar *b = PG_GETARG_MVARCHAR(1); ++ MVarChar *result; ++ int curlen; ++ int acharlen = u_countChar32(a->data, UVARCHARLENGTH(a)), ++ bcharlen = u_countChar32(b->data, UVARCHARLENGTH(b)); ++ ++ result = (MVarChar*)palloc( MVARCHARHDRSZ + sizeof(UChar) * 2 * (acharlen + bcharlen) ); ++ ++ curlen = UVARCHARLENGTH( a ); ++ if ( curlen > 0 ) ++ memcpy( result->data, a->data, MVARCHARLENGTH(a) ); ++ ++ if ( UVARCHARLENGTH(b) > 0 ) { ++ memcpy( result->data + curlen, b->data, MVARCHARLENGTH( b ) ); ++ curlen += UVARCHARLENGTH( b ); ++ } ++ ++ SET_VARSIZE(result, sizeof(UChar) * curlen + MVARCHARHDRSZ); ++ ++ PG_FREE_IF_COPY(a,0); ++ PG_FREE_IF_COPY(b,1); ++ ++ PG_RETURN_MVARCHAR(result); ++} ++ ++PG_FUNCTION_INFO_V1( mchar_mvarchar_concat ); ++Datum mchar_mvarchar_concat( PG_FUNCTION_ARGS ); ++Datum ++mchar_mvarchar_concat( PG_FUNCTION_ARGS ) { ++ MChar *a = PG_GETARG_MCHAR(0); ++ MVarChar *b = PG_GETARG_MVARCHAR(1); ++ MVarChar *result; ++ int curlen, maxcharlen; ++ int acharlen = u_countChar32(a->data, UCHARLENGTH(a)), ++ bcharlen = u_countChar32(b->data, UVARCHARLENGTH(b)); ++ ++ maxcharlen = ((a->typmod<=0) ? acharlen : a->typmod) + bcharlen; ++ ++ result = (MVarChar*)palloc( MVARCHARHDRSZ + sizeof(UChar) * 2 * maxcharlen ); ++ ++ curlen = UCHARLENGTH( a ); ++ if ( curlen > 0 ) ++ memcpy( result->data, a->data, MCHARLENGTH(a) ); ++ if ( a->typmod > 0 && acharlen < a->typmod ) { ++ FillWhiteSpace( result->data + curlen, a->typmod-acharlen ); ++ curlen += a->typmod-acharlen; ++ } ++ ++ if ( UVARCHARLENGTH(b) > 0 ) { ++ memcpy( result->data + curlen, b->data, MVARCHARLENGTH( b ) ); ++ curlen += UVARCHARLENGTH( b ); ++ } ++ ++ SET_VARSIZE(result, sizeof(UChar) * curlen + MVARCHARHDRSZ); ++ ++ PG_FREE_IF_COPY(a,0); ++ PG_FREE_IF_COPY(b,1); ++ ++ PG_RETURN_MVARCHAR(result); ++} ++ ++PG_FUNCTION_INFO_V1( mvarchar_mchar_concat ); ++Datum mvarchar_mchar_concat( PG_FUNCTION_ARGS ); ++Datum ++mvarchar_mchar_concat( PG_FUNCTION_ARGS ) { ++ MVarChar *a = PG_GETARG_MVARCHAR(0); ++ MChar *b = PG_GETARG_MCHAR(1); ++ MVarChar *result; ++ int curlen, maxcharlen; ++ int acharlen = u_countChar32(a->data, UVARCHARLENGTH(a)), ++ bcharlen = u_countChar32(b->data, UCHARLENGTH(b)); ++ ++ maxcharlen = acharlen + ((b->typmod<=0) ? bcharlen : b->typmod); ++ ++ result = (MVarChar*)palloc( MVARCHARHDRSZ + sizeof(UChar) * 2 * maxcharlen ); ++ ++ curlen = UVARCHARLENGTH( a ); ++ if ( curlen > 0 ) ++ memcpy( result->data, a->data, MVARCHARLENGTH(a) ); ++ ++ if ( UCHARLENGTH(b) > 0 ) { ++ memcpy( result->data + curlen, b->data, MCHARLENGTH( b ) ); ++ curlen += UCHARLENGTH( b ); ++ } ++ if ( b->typmod > 0 && bcharlen < b->typmod ) { ++ FillWhiteSpace( result->data + curlen, b->typmod-bcharlen ); ++ curlen += b->typmod-bcharlen; ++ } ++ ++ SET_VARSIZE(result, sizeof(UChar) * curlen + MVARCHARHDRSZ); ++ ++ PG_FREE_IF_COPY(a,0); ++ PG_FREE_IF_COPY(b,1); ++ ++ PG_RETURN_MVARCHAR(result); ++} ++ ++/* ++ * mchar <> mvarchar ++ */ ++static inline int ++mc_mv_icase_compare( MChar *a, MVarChar *b ) { ++ return UCharCaseCompare( ++ a->data, lengthWithoutSpaceChar(a), ++ b->data, lengthWithoutSpaceVarChar(b) ++ ); ++} ++ ++static inline int ++mc_mv_case_compare( MChar *a, MVarChar *b ) { ++ return UCharCompare( ++ a->data, lengthWithoutSpaceChar(a), ++ b->data, lengthWithoutSpaceVarChar(b) ++ ); ++} ++ ++#define MC_MV_CHARCMPFUNC( c, type, action, ret ) \ ++PG_FUNCTION_INFO_V1( mc_mv_##c##_##type ); \ ++Datum mc_mv_##c##_##type(PG_FUNCTION_ARGS);\ ++Datum \ ++mc_mv_##c##_##type(PG_FUNCTION_ARGS) { \ ++ MChar *a = PG_GETARG_MCHAR(0); \ ++ MVarChar *b = PG_GETARG_MVARCHAR(1); \ ++ int res = mc_mv_##c##_compare(a,b); \ ++ \ ++ PG_FREE_IF_COPY(a,0); \ ++ PG_FREE_IF_COPY(b,1); \ ++ PG_RETURN_##ret( res action 0 ); \ ++} ++ ++ ++MC_MV_CHARCMPFUNC( case, eq, ==, BOOL ) ++MC_MV_CHARCMPFUNC( case, ne, !=, BOOL ) ++MC_MV_CHARCMPFUNC( case, lt, <, BOOL ) ++MC_MV_CHARCMPFUNC( case, le, <=, BOOL ) ++MC_MV_CHARCMPFUNC( case, ge, >=, BOOL ) ++MC_MV_CHARCMPFUNC( case, gt, >, BOOL ) ++MC_MV_CHARCMPFUNC( case, cmp, +, INT32 ) ++ ++MC_MV_CHARCMPFUNC( icase, eq, ==, BOOL ) ++MC_MV_CHARCMPFUNC( icase, ne, !=, BOOL ) ++MC_MV_CHARCMPFUNC( icase, lt, <, BOOL ) ++MC_MV_CHARCMPFUNC( icase, le, <=, BOOL ) ++MC_MV_CHARCMPFUNC( icase, ge, >=, BOOL ) ++MC_MV_CHARCMPFUNC( icase, gt, >, BOOL ) ++MC_MV_CHARCMPFUNC( icase, cmp, +, INT32 ) ++ ++/* ++ * mvarchar <> mchar ++ */ ++static inline int ++mv_mc_icase_compare( MVarChar *a, MChar *b ) { ++ return UCharCaseCompare( ++ a->data, lengthWithoutSpaceVarChar(a), ++ b->data, lengthWithoutSpaceChar(b) ++ ); ++} ++ ++static inline int ++mv_mc_case_compare( MVarChar *a, MChar *b ) { ++ return UCharCompare( ++ a->data, lengthWithoutSpaceVarChar(a), ++ b->data, lengthWithoutSpaceChar(b) ++ ); ++} ++ ++#define MV_MC_CHARCMPFUNC( c, type, action, ret ) \ ++PG_FUNCTION_INFO_V1( mv_mc_##c##_##type ); \ ++Datum mv_mc_##c##_##type(PG_FUNCTION_ARGS);\ ++Datum \ ++mv_mc_##c##_##type(PG_FUNCTION_ARGS) { \ ++ MVarChar *a = PG_GETARG_MVARCHAR(0); \ ++ MChar *b = PG_GETARG_MCHAR(1); \ ++ int res = mv_mc_##c##_compare(a,b); \ ++ \ ++ PG_FREE_IF_COPY(a,0); \ ++ PG_FREE_IF_COPY(b,1); \ ++ PG_RETURN_##ret( res action 0 ); \ ++} ++ ++ ++MV_MC_CHARCMPFUNC( case, eq, ==, BOOL ) ++MV_MC_CHARCMPFUNC( case, ne, !=, BOOL ) ++MV_MC_CHARCMPFUNC( case, lt, <, BOOL ) ++MV_MC_CHARCMPFUNC( case, le, <=, BOOL ) ++MV_MC_CHARCMPFUNC( case, ge, >=, BOOL ) ++MV_MC_CHARCMPFUNC( case, gt, >, BOOL ) ++MV_MC_CHARCMPFUNC( case, cmp, +, INT32 ) ++ ++MV_MC_CHARCMPFUNC( icase, eq, ==, BOOL ) ++MV_MC_CHARCMPFUNC( icase, ne, !=, BOOL ) ++MV_MC_CHARCMPFUNC( icase, lt, <, BOOL ) ++MV_MC_CHARCMPFUNC( icase, le, <=, BOOL ) ++MV_MC_CHARCMPFUNC( icase, ge, >=, BOOL ) ++MV_MC_CHARCMPFUNC( icase, gt, >, BOOL ) ++MV_MC_CHARCMPFUNC( icase, cmp, +, INT32 ) ++ ++#define NULLHASHVALUE (-2147483647) ++ ++#define FULLEQ_FUNC(type, cmpfunc, hashfunc) \ ++PG_FUNCTION_INFO_V1( isfulleq_##type ); \ ++Datum isfulleq_##type(PG_FUNCTION_ARGS); \ ++Datum \ ++isfulleq_##type(PG_FUNCTION_ARGS) { \ ++ if ( PG_ARGISNULL(0) && PG_ARGISNULL(1) ) \ ++ PG_RETURN_BOOL(true); \ ++ else if ( PG_ARGISNULL(0) || PG_ARGISNULL(1) ) \ ++ PG_RETURN_BOOL(false); \ ++ \ ++ PG_RETURN_DATUM( DirectFunctionCall2( cmpfunc, \ ++ PG_GETARG_DATUM(0), \ ++ PG_GETARG_DATUM(1) \ ++ ) ); \ ++} \ ++ \ ++PG_FUNCTION_INFO_V1( fullhash_##type ); \ ++Datum fullhash_##type(PG_FUNCTION_ARGS); \ ++Datum \ ++fullhash_##type(PG_FUNCTION_ARGS) { \ ++ if ( PG_ARGISNULL(0) ) \ ++ PG_RETURN_INT32(NULLHASHVALUE); \ ++ \ ++ PG_RETURN_DATUM( DirectFunctionCall1( hashfunc, \ ++ PG_GETARG_DATUM(0) \ ++ ) ); \ ++} ++ ++FULLEQ_FUNC( mchar, mchar_icase_eq, mchar_hash ); ++FULLEQ_FUNC( mvarchar, mvarchar_icase_eq, mvarchar_hash ); ++ +diff --git a/contrib/mchar/mchar_proc.c b/contrib/mchar/mchar_proc.c +new file mode 100644 +index 0000000..165d44c +--- /dev/null ++++ b/contrib/mchar/mchar_proc.c +@@ -0,0 +1,339 @@ ++#include "mchar.h" ++#include "mb/pg_wchar.h" ++#include "access/hash.h" ++ ++PG_FUNCTION_INFO_V1(mchar_length); ++Datum mchar_length(PG_FUNCTION_ARGS); ++ ++Datum ++mchar_length(PG_FUNCTION_ARGS) { ++ MChar *m = PG_GETARG_MCHAR(0); ++ int32 l = UCHARLENGTH(m); ++ ++ while( l>0 && m_isspace( m->data[ l-1 ] ) ) ++ l--; ++ ++ l = u_countChar32(m->data, l); ++ ++ PG_FREE_IF_COPY(m,0); ++ ++ PG_RETURN_INT32(l); ++} ++ ++PG_FUNCTION_INFO_V1(mvarchar_length); ++Datum mvarchar_length(PG_FUNCTION_ARGS); ++ ++Datum ++mvarchar_length(PG_FUNCTION_ARGS) { ++ MVarChar *m = PG_GETARG_MVARCHAR(0); ++ int32 l = UVARCHARLENGTH(m); ++ ++ while( l>0 && m_isspace( m->data[ l-1 ] ) ) ++ l--; ++ ++ l = u_countChar32(m->data, l); ++ ++ PG_FREE_IF_COPY(m,0); ++ ++ PG_RETURN_INT32(l); ++} ++ ++static int32 ++uchar_substring( ++ UChar *str, int32 strl, ++ int32 start, int32 length, bool length_not_specified, ++ UChar *dst) { ++ int32 S = start-1; /* start position */ ++ int32 S1; /* adjusted start position */ ++ int32 L1; /* adjusted substring length */ ++ int32 subbegin=0, subend=0; ++ ++ S1 = Max(S, 0); ++ if (length_not_specified) ++ L1 = -1; ++ else { ++ /* end position */ ++ int32 E = S + length; ++ ++ /* ++ * A negative value for L is the only way for the end position to ++ * be before the start. SQL99 says to throw an error. ++ */ ++ ++ if (E < S) ++ ereport(ERROR, ++ (errcode(ERRCODE_SUBSTRING_ERROR), ++ errmsg("negative substring length not allowed"))); ++ ++ /* ++ * A zero or negative value for the end position can happen if the ++ * start was negative or one. SQL99 says to return a zero-length ++ * string. ++ */ ++ if (E < 0) ++ return 0; ++ ++ L1 = E - S1; ++ } ++ ++ U16_FWD_N( str, subbegin, strl, S1 ); ++ if ( subbegin >= strl ) ++ return 0; ++ subend = subbegin; ++ U16_FWD_N( str, subend, strl, L1 ); ++ ++ memcpy( dst, str+subbegin, sizeof(UChar)*(subend-subbegin) ); ++ ++ return subend-subbegin; ++} ++ ++PG_FUNCTION_INFO_V1(mchar_substring); ++Datum mchar_substring(PG_FUNCTION_ARGS); ++Datum ++mchar_substring(PG_FUNCTION_ARGS) { ++ MChar *src = PG_GETARG_MCHAR(0); ++ MChar *dst; ++ int32 length; ++ ++ dst = (MChar*)palloc( VARSIZE(src) ); ++ length = uchar_substring( ++ src->data, UCHARLENGTH(src), ++ PG_GETARG_INT32(1), PG_GETARG_INT32(2), false, ++ dst->data); ++ ++ dst->typmod = src->typmod; ++ SET_VARSIZE(dst, MCHARHDRSZ + length *sizeof(UChar)); ++ ++ PG_FREE_IF_COPY(src, 0); ++ PG_RETURN_MCHAR(dst); ++} ++ ++PG_FUNCTION_INFO_V1(mchar_substring_no_len); ++Datum mchar_substring_no_len(PG_FUNCTION_ARGS); ++Datum ++mchar_substring_no_len(PG_FUNCTION_ARGS) { ++ MChar *src = PG_GETARG_MCHAR(0); ++ MChar *dst; ++ int32 length; ++ ++ dst = (MChar*)palloc( VARSIZE(src) ); ++ length = uchar_substring( ++ src->data, UCHARLENGTH(src), ++ PG_GETARG_INT32(1), -1, true, ++ dst->data); ++ ++ dst->typmod = src->typmod; ++ SET_VARSIZE(dst, MCHARHDRSZ + length *sizeof(UChar)); ++ ++ PG_FREE_IF_COPY(src, 0); ++ PG_RETURN_MCHAR(dst); ++} ++ ++PG_FUNCTION_INFO_V1(mvarchar_substring); ++Datum mvarchar_substring(PG_FUNCTION_ARGS); ++Datum ++mvarchar_substring(PG_FUNCTION_ARGS) { ++ MVarChar *src = PG_GETARG_MVARCHAR(0); ++ MVarChar *dst; ++ int32 length; ++ ++ dst = (MVarChar*)palloc( VARSIZE(src) ); ++ length = uchar_substring( ++ src->data, UVARCHARLENGTH(src), ++ PG_GETARG_INT32(1), PG_GETARG_INT32(2), false, ++ dst->data); ++ ++ SET_VARSIZE(dst, MVARCHARHDRSZ + length *sizeof(UChar)); ++ ++ PG_FREE_IF_COPY(src, 0); ++ PG_RETURN_MVARCHAR(dst); ++} ++ ++PG_FUNCTION_INFO_V1(mvarchar_substring_no_len); ++Datum mvarchar_substring_no_len(PG_FUNCTION_ARGS); ++Datum ++mvarchar_substring_no_len(PG_FUNCTION_ARGS) { ++ MVarChar *src = PG_GETARG_MVARCHAR(0); ++ MVarChar *dst; ++ int32 length; ++ ++ dst = (MVarChar*)palloc( VARSIZE(src) ); ++ length = uchar_substring( ++ src->data, UVARCHARLENGTH(src), ++ PG_GETARG_INT32(1), -1, true, ++ dst->data); ++ ++ SET_VARSIZE(dst, MVARCHARHDRSZ + length *sizeof(UChar)); ++ ++ PG_FREE_IF_COPY(src, 0); ++ PG_RETURN_MVARCHAR(dst); ++} ++ ++static Datum ++hash_uchar( UChar *s, int len ) { ++ int32 length; ++ UErrorCode err = 0; ++ UChar *d; ++ Datum res; ++ ++ if ( len == 0 ) ++ return hash_any( NULL, 0 ); ++ ++ err = 0; ++ d = (UChar*) palloc( sizeof(UChar) * len * 2 ); ++ length = u_strFoldCase(d, len*2, s, len, U_FOLD_CASE_DEFAULT, &err); ++ ++ if ( U_FAILURE(err) ) ++ elog(ERROR,"ICU u_strFoldCase fails and returns %d (%s)", err, u_errorName(err)); ++ ++ res = hash_any( (unsigned char*) d, length * sizeof(UChar) ); ++ ++ pfree(d); ++ return res; ++} ++ ++PG_FUNCTION_INFO_V1(mvarchar_hash); ++Datum ++mvarchar_hash(PG_FUNCTION_ARGS) { ++ MVarChar *src = PG_GETARG_MVARCHAR(0); ++ Datum res; ++ ++ res = hash_uchar( src->data, lengthWithoutSpaceVarChar(src) ); ++ ++ PG_FREE_IF_COPY(src, 0); ++ PG_RETURN_DATUM( res ); ++} ++ ++PG_FUNCTION_INFO_V1(mchar_hash); ++Datum ++mchar_hash(PG_FUNCTION_ARGS) { ++ MChar *src = PG_GETARG_MCHAR(0); ++ Datum res; ++ ++ res = hash_uchar( src->data, lengthWithoutSpaceChar(src) ); ++ ++ PG_FREE_IF_COPY(src, 0); ++ PG_RETURN_DATUM( res ); ++} ++ ++PG_FUNCTION_INFO_V1(mchar_upper); ++Datum mchar_upper(PG_FUNCTION_ARGS); ++Datum ++mchar_upper(PG_FUNCTION_ARGS) { ++ MChar *src = PG_GETARG_MCHAR(0); ++ MChar *dst = (MChar*)palloc( VARSIZE(src) * 2 ); ++ ++ dst->len = MCHARHDRSZ; ++ dst->typmod = src->typmod; ++ if ( UCHARLENGTH(src) != 0 ) { ++ int length; ++ UErrorCode err=0; ++ ++ length = u_strToUpper( dst->data, VARSIZE(src) * 2 - MCHARHDRSZ, ++ src->data, UCHARLENGTH(src), ++ NULL, &err ); ++ ++ Assert( length <= VARSIZE(src) * 2 - MCHARHDRSZ ); ++ ++ if ( U_FAILURE(err) ) ++ elog(ERROR,"ICU u_strToUpper fails and returns %d (%s)", err, u_errorName(err)); ++ ++ dst->len += sizeof(UChar) * length; ++ } ++ ++ SET_VARSIZE( dst, dst->len ); ++ PG_FREE_IF_COPY(src, 0); ++ PG_RETURN_MCHAR( dst ); ++} ++ ++PG_FUNCTION_INFO_V1(mchar_lower); ++Datum mchar_lower(PG_FUNCTION_ARGS); ++Datum ++mchar_lower(PG_FUNCTION_ARGS) { ++ MChar *src = PG_GETARG_MCHAR(0); ++ MChar *dst = (MChar*)palloc( VARSIZE(src) * 2 ); ++ ++ dst->len = MCHARHDRSZ; ++ dst->typmod = src->typmod; ++ if ( UCHARLENGTH(src) != 0 ) { ++ int length; ++ UErrorCode err=0; ++ ++ length = u_strToLower( dst->data, VARSIZE(src) * 2 - MCHARHDRSZ, ++ src->data, UCHARLENGTH(src), ++ NULL, &err ); ++ ++ Assert( length <= VARSIZE(src) * 2 - MCHARHDRSZ ); ++ ++ if ( U_FAILURE(err) ) ++ elog(ERROR,"ICU u_strToLower fails and returns %d (%s)", err, u_errorName(err)); ++ ++ dst->len += sizeof(UChar) * length; ++ } ++ ++ SET_VARSIZE( dst, dst->len ); ++ PG_FREE_IF_COPY(src, 0); ++ PG_RETURN_MCHAR( dst ); ++} ++ ++PG_FUNCTION_INFO_V1(mvarchar_upper); ++Datum mvarchar_upper(PG_FUNCTION_ARGS); ++Datum ++mvarchar_upper(PG_FUNCTION_ARGS) { ++ MVarChar *src = PG_GETARG_MVARCHAR(0); ++ MVarChar *dst = (MVarChar*)palloc( VARSIZE(src) * 2 ); ++ ++ dst->len = MVARCHARHDRSZ; ++ ++ if ( UVARCHARLENGTH(src) != 0 ) { ++ int length; ++ UErrorCode err=0; ++ ++ length = u_strToUpper( dst->data, VARSIZE(src) * 2 - MVARCHARHDRSZ, ++ src->data, UVARCHARLENGTH(src), ++ NULL, &err ); ++ ++ Assert( length <= VARSIZE(src) * 2 - MVARCHARHDRSZ ); ++ ++ if ( U_FAILURE(err) ) ++ elog(ERROR,"ICU u_strToUpper fails and returns %d (%s)", err, u_errorName(err)); ++ ++ dst->len += sizeof(UChar) * length; ++ } ++ ++ SET_VARSIZE( dst, dst->len ); ++ PG_FREE_IF_COPY(src, 0); ++ PG_RETURN_MVARCHAR( dst ); ++} ++ ++PG_FUNCTION_INFO_V1(mvarchar_lower); ++Datum mvarchar_lower(PG_FUNCTION_ARGS); ++Datum ++mvarchar_lower(PG_FUNCTION_ARGS) { ++ MVarChar *src = PG_GETARG_MVARCHAR(0); ++ MVarChar *dst = (MVarChar*)palloc( VARSIZE(src) * 2 ); ++ ++ dst->len = MVARCHARHDRSZ; ++ ++ if ( UVARCHARLENGTH(src) != 0 ) { ++ int length; ++ UErrorCode err=0; ++ ++ length = u_strToLower( dst->data, VARSIZE(src) * 2 - MVARCHARHDRSZ, ++ src->data, UVARCHARLENGTH(src), ++ NULL, &err ); ++ ++ Assert( length <= VARSIZE(src) * 2 - MVARCHARHDRSZ ); ++ ++ if ( U_FAILURE(err) ) ++ elog(ERROR,"ICU u_strToLower fails and returns %d (%s)", err, u_errorName(err)); ++ ++ dst->len += sizeof(UChar) * length; ++ } ++ ++ SET_VARSIZE( dst, dst->len ); ++ PG_FREE_IF_COPY(src, 0); ++ PG_RETURN_MVARCHAR( dst ); ++} ++ ++ +diff --git a/contrib/mchar/mchar_recode.c b/contrib/mchar/mchar_recode.c +new file mode 100644 +index 0000000..d4f3659 +--- /dev/null ++++ b/contrib/mchar/mchar_recode.c +@@ -0,0 +1,142 @@ ++#include "mchar.h" ++ ++#include "unicode/ucol.h" ++#include "unicode/ucnv.h" ++ ++static UConverter *cnvDB = NULL; ++static UCollator *colCaseInsensitive = NULL; ++static UCollator *colCaseSensitive = NULL; ++ ++static void ++createUObjs() { ++ if ( !cnvDB ) { ++ UErrorCode err = 0; ++ ++ if ( GetDatabaseEncoding() == PG_UTF8 ) ++ cnvDB = ucnv_open("UTF8", &err); ++ else ++ cnvDB = ucnv_open(NULL, &err); ++ if ( U_FAILURE(err) || cnvDB == NULL ) ++ elog(ERROR,"ICU ucnv_open returns %d (%s)", err, u_errorName(err)); ++ } ++ ++ if ( !colCaseInsensitive ) { ++ UErrorCode err = 0; ++ ++ colCaseInsensitive = ucol_open("", &err); ++ if ( U_FAILURE(err) || cnvDB == NULL ) { ++ if ( colCaseSensitive ) ++ ucol_close( colCaseSensitive ); ++ colCaseSensitive = NULL; ++ elog(ERROR,"ICU ucol_open returns %d (%s)", err, u_errorName(err)); ++ } ++ ++ ucol_setStrength( colCaseInsensitive, UCOL_SECONDARY ); ++ } ++ ++ if ( !colCaseSensitive ) { ++ UErrorCode err = 0; ++ ++ colCaseSensitive = ucol_open("", &err); ++ if ( U_FAILURE(err) || cnvDB == NULL ) { ++ if ( colCaseSensitive ) ++ ucol_close( colCaseSensitive ); ++ colCaseSensitive = NULL; ++ elog(ERROR,"ICU ucol_open returns %d (%s)", err, u_errorName(err)); ++ } ++ ++ ucol_setAttribute(colCaseSensitive, UCOL_CASE_FIRST, UCOL_UPPER_FIRST, &err); ++ if (U_FAILURE(err)) { ++ if ( colCaseSensitive ) ++ ucol_close( colCaseSensitive ); ++ colCaseSensitive = NULL; ++ elog(ERROR,"ICU ucol_setAttribute returns %d (%s)", err, u_errorName(err)); ++ } ++ } ++} ++ ++int ++Char2UChar(const char * src, int srclen, UChar *dst) { ++ int dstlen=0; ++ UErrorCode err = 0; ++ ++ createUObjs(); ++ dstlen = ucnv_toUChars( cnvDB, dst, srclen*4, src, srclen, &err ); ++ if ( U_FAILURE(err)) ++ elog(ERROR,"ICU ucnv_toUChars returns %d (%s)", err, u_errorName(err)); ++ ++ return dstlen; ++} ++ ++int ++UChar2Char(const UChar * src, int srclen, char *dst) { ++ int dstlen=0; ++ UErrorCode err = 0; ++ ++ createUObjs(); ++ dstlen = ucnv_fromUChars( cnvDB, dst, srclen*4, src, srclen, &err ); ++ if ( U_FAILURE(err) ) ++ elog(ERROR,"ICU ucnv_fromUChars returns %d (%s)", err, u_errorName(err)); ++ ++ return dstlen; ++} ++ ++int ++UChar2Wchar(UChar * src, int srclen, pg_wchar *dst) { ++ int dstlen=0; ++ char *utf = palloc(sizeof(char)*srclen*4); ++ ++ dstlen = UChar2Char(src, srclen, utf); ++ dstlen = pg_mb2wchar_with_len( utf, dst, dstlen ); ++ pfree(utf); ++ ++ return dstlen; ++} ++ ++static UChar UCharWhiteSpace = 0; ++ ++void ++FillWhiteSpace( UChar *dst, int n ) { ++ if ( UCharWhiteSpace == 0 ) { ++ int len; ++ UErrorCode err = 0; ++ ++ u_strFromUTF8( &UCharWhiteSpace, 1, &len, " ", 1, &err); ++ ++ Assert( len==1 ); ++ Assert( !U_FAILURE(err) ); ++ } ++ ++ while( n-- > 0 ) ++ *dst++ = UCharWhiteSpace; ++} ++ ++int ++UCharCaseCompare(UChar * a, int alen, UChar *b, int blen) { ++ int len = Min(alen, blen); ++ int res; ++ ++ createUObjs(); ++ ++ res = (int)ucol_strcoll( colCaseInsensitive, ++ a, len, ++ b, len); ++ if ( res == 0 && alen != blen ) ++ return (alen > blen) ? 1 : - 1; ++ return res; ++} ++ ++int ++UCharCompare(UChar * a, int alen, UChar *b, int blen) { ++ int len = Min(alen, blen); ++ int res; ++ ++ createUObjs(); ++ ++ res = (int)ucol_strcoll( colCaseSensitive, ++ a, len, ++ b, len); ++ if ( res == 0 && alen != blen ) ++ return (alen > blen) ? 1 : - 1; ++ return res; ++} +diff --git a/contrib/mchar/sql/compat.sql b/contrib/mchar/sql/compat.sql +new file mode 100644 +index 0000000..d5b6a98 +--- /dev/null ++++ b/contrib/mchar/sql/compat.sql +@@ -0,0 +1,11 @@ ++--- table based checks ++ ++select '<' || ch || '>', '<' || vch || '>' from chvch; ++select * from chvch where vch = 'One space'; ++select * from chvch where vch = 'One space '; ++ ++select * from ch where chcol = 'abcd' order by chcol; ++select * from ch t1 join ch t2 on t1.chcol = t2.chcol order by t1.chcol, t2.chcol; ++select * from ch where chcol > 'abcd' and chcol<'ee'; ++select * from ch order by chcol; ++ +diff --git a/contrib/mchar/sql/init.sql b/contrib/mchar/sql/init.sql +new file mode 100644 +index 0000000..a303feb +--- /dev/null ++++ b/contrib/mchar/sql/init.sql +@@ -0,0 +1,33 @@ ++ ++-- ++-- first, define the datatype. Turn off echoing so that expected file ++-- does not depend on contents of mchar.sql. ++-- ++ ++\set ECHO none ++\i mchar.sql ++--- load for table based checks ++SET search_path = public; ++\set ECHO all ++ ++create table ch ( ++ chcol mchar(32) ++) without oids; ++ ++insert into ch values('abcd'); ++insert into ch values('AbcD'); ++insert into ch values('abcz'); ++insert into ch values('defg'); ++insert into ch values('dEfg'); ++insert into ch values('ee'); ++insert into ch values('Ee'); ++ ++create table chvch ( ++ ch mchar(12), ++ vch mvarchar(12) ++) without oids; ++ ++insert into chvch values('No spaces', 'No spaces'); ++insert into chvch values('One space ', 'One space '); ++insert into chvch values('1 space', '1 space '); ++ +diff --git a/contrib/mchar/sql/like.sql b/contrib/mchar/sql/like.sql +new file mode 100644 +index 0000000..aebf924 +--- /dev/null ++++ b/contrib/mchar/sql/like.sql +@@ -0,0 +1,216 @@ ++-- simplest examples ++-- E061-04 like predicate ++SELECT 'hawkeye'::mchar LIKE 'h%' AS "true"; ++SELECT 'hawkeye'::mchar NOT LIKE 'h%' AS "false"; ++ ++SELECT 'hawkeye'::mchar LIKE 'H%' AS "true"; ++SELECT 'hawkeye'::mchar NOT LIKE 'H%' AS "false"; ++ ++SELECT 'hawkeye'::mchar LIKE 'indio%' AS "false"; ++SELECT 'hawkeye'::mchar NOT LIKE 'indio%' AS "true"; ++ ++SELECT 'hawkeye'::mchar LIKE 'h%eye' AS "true"; ++SELECT 'hawkeye'::mchar NOT LIKE 'h%eye' AS "false"; ++ ++SELECT 'indio'::mchar LIKE '_ndio' AS "true"; ++SELECT 'indio'::mchar NOT LIKE '_ndio' AS "false"; ++ ++SELECT 'indio'::mchar LIKE 'in__o' AS "true"; ++SELECT 'indio'::mchar NOT LIKE 'in__o' AS "false"; ++ ++SELECT 'indio'::mchar LIKE 'in_o' AS "false"; ++SELECT 'indio'::mchar NOT LIKE 'in_o' AS "true"; ++ ++SELECT 'hawkeye'::mvarchar LIKE 'h%' AS "true"; ++SELECT 'hawkeye'::mvarchar NOT LIKE 'h%' AS "false"; ++ ++SELECT 'hawkeye'::mvarchar LIKE 'H%' AS "true"; ++SELECT 'hawkeye'::mvarchar NOT LIKE 'H%' AS "false"; ++ ++SELECT 'hawkeye'::mvarchar LIKE 'indio%' AS "false"; ++SELECT 'hawkeye'::mvarchar NOT LIKE 'indio%' AS "true"; ++ ++SELECT 'hawkeye'::mvarchar LIKE 'h%eye' AS "true"; ++SELECT 'hawkeye'::mvarchar NOT LIKE 'h%eye' AS "false"; ++ ++SELECT 'indio'::mvarchar LIKE '_ndio' AS "true"; ++SELECT 'indio'::mvarchar NOT LIKE '_ndio' AS "false"; ++ ++SELECT 'indio'::mvarchar LIKE 'in__o' AS "true"; ++SELECT 'indio'::mvarchar NOT LIKE 'in__o' AS "false"; ++ ++SELECT 'indio'::mvarchar LIKE 'in_o' AS "false"; ++SELECT 'indio'::mvarchar NOT LIKE 'in_o' AS "true"; ++ ++-- unused escape character ++SELECT 'hawkeye'::mchar LIKE 'h%'::mchar ESCAPE '#' AS "true"; ++SELECT 'hawkeye'::mchar NOT LIKE 'h%'::mchar ESCAPE '#' AS "false"; ++ ++SELECT 'indio'::mchar LIKE 'ind_o'::mchar ESCAPE '$' AS "true"; ++SELECT 'indio'::mchar NOT LIKE 'ind_o'::mchar ESCAPE '$' AS "false"; ++ ++-- escape character ++-- E061-05 like predicate with escape clause ++SELECT 'h%'::mchar LIKE 'h#%'::mchar ESCAPE '#' AS "true"; ++SELECT 'h%'::mchar NOT LIKE 'h#%'::mchar ESCAPE '#' AS "false"; ++ ++SELECT 'h%wkeye'::mchar LIKE 'h#%'::mchar ESCAPE '#' AS "false"; ++SELECT 'h%wkeye'::mchar NOT LIKE 'h#%'::mchar ESCAPE '#' AS "true"; ++ ++SELECT 'h%wkeye'::mchar LIKE 'h#%%'::mchar ESCAPE '#' AS "true"; ++SELECT 'h%wkeye'::mchar NOT LIKE 'h#%%'::mchar ESCAPE '#' AS "false"; ++ ++SELECT 'h%awkeye'::mchar LIKE 'h#%a%k%e'::mchar ESCAPE '#' AS "true"; ++SELECT 'h%awkeye'::mchar NOT LIKE 'h#%a%k%e'::mchar ESCAPE '#' AS "false"; ++ ++SELECT 'indio'::mchar LIKE '_ndio'::mchar ESCAPE '$' AS "true"; ++SELECT 'indio'::mchar NOT LIKE '_ndio'::mchar ESCAPE '$' AS "false"; ++ ++SELECT 'i_dio'::mchar LIKE 'i$_d_o'::mchar ESCAPE '$' AS "true"; ++SELECT 'i_dio'::mchar NOT LIKE 'i$_d_o'::mchar ESCAPE '$' AS "false"; ++ ++SELECT 'i_dio'::mchar LIKE 'i$_nd_o'::mchar ESCAPE '$' AS "false"; ++SELECT 'i_dio'::mchar NOT LIKE 'i$_nd_o'::mchar ESCAPE '$' AS "true"; ++ ++SELECT 'i_dio'::mchar LIKE 'i$_d%o'::mchar ESCAPE '$' AS "true"; ++SELECT 'i_dio'::mchar NOT LIKE 'i$_d%o'::mchar ESCAPE '$' AS "false"; ++ ++-- escape character same as pattern character ++SELECT 'maca'::mchar LIKE 'm%aca' ESCAPE '%'::mchar AS "true"; ++SELECT 'maca'::mchar NOT LIKE 'm%aca' ESCAPE '%'::mchar AS "false"; ++ ++SELECT 'ma%a'::mchar LIKE 'm%a%%a' ESCAPE '%'::mchar AS "true"; ++SELECT 'ma%a'::mchar NOT LIKE 'm%a%%a' ESCAPE '%'::mchar AS "false"; ++ ++SELECT 'bear'::mchar LIKE 'b_ear' ESCAPE '_'::mchar AS "true"; ++SELECT 'bear'::mchar NOT LIKE 'b_ear'::mchar ESCAPE '_' AS "false"; ++ ++SELECT 'be_r'::mchar LIKE 'b_e__r' ESCAPE '_'::mchar AS "true"; ++SELECT 'be_r'::mchar NOT LIKE 'b_e__r' ESCAPE '_'::mchar AS "false"; ++ ++SELECT 'be_r'::mchar LIKE '__e__r' ESCAPE '_'::mchar AS "false"; ++SELECT 'be_r'::mchar NOT LIKE '__e__r'::mchar ESCAPE '_' AS "true"; ++ ++-- unused escape character ++SELECT 'hawkeye'::mvarchar LIKE 'h%'::mvarchar ESCAPE '#' AS "true"; ++SELECT 'hawkeye'::mvarchar NOT LIKE 'h%'::mvarchar ESCAPE '#' AS "false"; ++ ++SELECT 'indio'::mvarchar LIKE 'ind_o'::mvarchar ESCAPE '$' AS "true"; ++SELECT 'indio'::mvarchar NOT LIKE 'ind_o'::mvarchar ESCAPE '$' AS "false"; ++ ++-- escape character ++-- E061-05 like predicate with escape clause ++SELECT 'h%'::mvarchar LIKE 'h#%'::mvarchar ESCAPE '#' AS "true"; ++SELECT 'h%'::mvarchar NOT LIKE 'h#%'::mvarchar ESCAPE '#' AS "false"; ++ ++SELECT 'h%wkeye'::mvarchar LIKE 'h#%'::mvarchar ESCAPE '#' AS "false"; ++SELECT 'h%wkeye'::mvarchar NOT LIKE 'h#%'::mvarchar ESCAPE '#' AS "true"; ++ ++SELECT 'h%wkeye'::mvarchar LIKE 'h#%%'::mvarchar ESCAPE '#' AS "true"; ++SELECT 'h%wkeye'::mvarchar NOT LIKE 'h#%%'::mvarchar ESCAPE '#' AS "false"; ++ ++SELECT 'h%awkeye'::mvarchar LIKE 'h#%a%k%e'::mvarchar ESCAPE '#' AS "true"; ++SELECT 'h%awkeye'::mvarchar NOT LIKE 'h#%a%k%e'::mvarchar ESCAPE '#' AS "false"; ++ ++SELECT 'indio'::mvarchar LIKE '_ndio'::mvarchar ESCAPE '$' AS "true"; ++SELECT 'indio'::mvarchar NOT LIKE '_ndio'::mvarchar ESCAPE '$' AS "false"; ++ ++SELECT 'i_dio'::mvarchar LIKE 'i$_d_o'::mvarchar ESCAPE '$' AS "true"; ++SELECT 'i_dio'::mvarchar NOT LIKE 'i$_d_o'::mvarchar ESCAPE '$' AS "false"; ++ ++SELECT 'i_dio'::mvarchar LIKE 'i$_nd_o'::mvarchar ESCAPE '$' AS "false"; ++SELECT 'i_dio'::mvarchar NOT LIKE 'i$_nd_o'::mvarchar ESCAPE '$' AS "true"; ++ ++SELECT 'i_dio'::mvarchar LIKE 'i$_d%o'::mvarchar ESCAPE '$' AS "true"; ++SELECT 'i_dio'::mvarchar NOT LIKE 'i$_d%o'::mvarchar ESCAPE '$' AS "false"; ++ ++-- escape character same as pattern character ++SELECT 'maca'::mvarchar LIKE 'm%aca' ESCAPE '%'::mvarchar AS "true"; ++SELECT 'maca'::mvarchar NOT LIKE 'm%aca' ESCAPE '%'::mvarchar AS "false"; ++ ++SELECT 'ma%a'::mvarchar LIKE 'm%a%%a' ESCAPE '%'::mvarchar AS "true"; ++SELECT 'ma%a'::mvarchar NOT LIKE 'm%a%%a' ESCAPE '%'::mvarchar AS "false"; ++ ++SELECT 'bear'::mvarchar LIKE 'b_ear' ESCAPE '_'::mvarchar AS "true"; ++SELECT 'bear'::mvarchar NOT LIKE 'b_ear'::mvarchar ESCAPE '_' AS "false"; ++ ++SELECT 'be_r'::mvarchar LIKE 'b_e__r' ESCAPE '_'::mvarchar AS "true"; ++SELECT 'be_r'::mvarchar NOT LIKE 'b_e__r' ESCAPE '_'::mvarchar AS "false"; ++ ++SELECT 'be_r'::mvarchar LIKE '__e__r' ESCAPE '_'::mvarchar AS "false"; ++SELECT 'be_r'::mvarchar NOT LIKE '__e__r'::mvarchar ESCAPE '_' AS "true"; ++ ++-- similar to ++ ++SELECT 'abc'::mchar SIMILAR TO 'abc'::mchar AS "true"; ++SELECT 'abc'::mchar SIMILAR TO 'a'::mchar AS "false"; ++SELECT 'abc'::mchar SIMILAR TO '%(b|d)%'::mchar AS "true"; ++SELECT 'abc'::mchar SIMILAR TO '(b|c)%'::mchar AS "false"; ++SELECT 'h%'::mchar SIMILAR TO 'h#%'::mchar AS "false"; ++SELECT 'h%'::mchar SIMILAR TO 'h#%'::mchar ESCAPE '#' AS "true"; ++ ++SELECT 'abc'::mvarchar SIMILAR TO 'abc'::mvarchar AS "true"; ++SELECT 'abc'::mvarchar SIMILAR TO 'a'::mvarchar AS "false"; ++SELECT 'abc'::mvarchar SIMILAR TO '%(b|d)%'::mvarchar AS "true"; ++SELECT 'abc'::mvarchar SIMILAR TO '(b|c)%'::mvarchar AS "false"; ++SELECT 'h%'::mvarchar SIMILAR TO 'h#%'::mvarchar AS "false"; ++SELECT 'h%'::mvarchar SIMILAR TO 'h#%'::mvarchar ESCAPE '#' AS "true"; ++ ++-- index support ++ ++SELECT * from ch where chcol like 'aB_d' order by chcol using &<; ++SELECT * from ch where chcol like 'aB%d' order by chcol using &<; ++SELECT * from ch where chcol like 'aB%' order by chcol using &<; ++SELECT * from ch where chcol like '%BC%' order by chcol using &<; ++set enable_seqscan = off; ++SELECT * from ch where chcol like 'aB_d' order by chcol using &<; ++SELECT * from ch where chcol like 'aB%d' order by chcol using &<; ++SELECT * from ch where chcol like 'aB%' order by chcol using &<; ++SELECT * from ch where chcol like '%BC%' order by chcol using &<; ++set enable_seqscan = on; ++ ++ ++create table testt (f1 mchar(10)); ++insert into testt values ('Abc-000001'); ++insert into testt values ('Abc-000002'); ++insert into testt values ('0000000001'); ++insert into testt values ('0000000002'); ++ ++select f1 from testt where f1::mvarchar like E'Abc\\-%'::mvarchar; ++select * from testt where f1::mchar like E'Abc\\-%'::mchar; ++create index testindex on testt(f1); ++set enable_seqscan=off; ++select f1 from testt where f1::mvarchar like E'Abc\\-%'::mvarchar; ++select * from testt where f1::mchar like E'Abc\\-%'::mchar; ++set enable_seqscan = on; ++drop table testt; ++ ++create table testt (f1 mvarchar(10)); ++insert into testt values ('Abc-000001'); ++insert into testt values ('Abc-000002'); ++insert into testt values ('0000000001'); ++insert into testt values ('0000000002'); ++ ++select f1 from testt where f1::mvarchar like E'Abc\\-%'::mvarchar; ++select * from testt where f1::mchar like E'Abc\\-%'::mchar; ++select * from testt where f1::mchar like E'Abc\\- %'::mchar; ++select * from testt where f1::mchar like E' %'::mchar; ++create index testindex on testt(f1); ++set enable_seqscan=off; ++select f1 from testt where f1::mvarchar like E'Abc\\-%'::mvarchar; ++select * from testt where f1::mchar like E'Abc\\-%'::mchar; ++select * from testt where f1::mchar like E'Abc\\- %'::mchar; ++select * from testt where f1::mchar like E' %'::mchar; ++set enable_seqscan = on; ++drop table testt; ++ ++ ++CREATE TABLE test ( code mchar(5) NOT NULL ); ++insert into test values('1111 '); ++insert into test values('111 '); ++insert into test values('11 '); ++insert into test values('1 '); ++ ++SELECT * FROM test WHERE code LIKE ('% '); ++ ++ +diff --git a/contrib/mchar/sql/mchar.sql b/contrib/mchar/sql/mchar.sql +new file mode 100644 +index 0000000..640f166 +--- /dev/null ++++ b/contrib/mchar/sql/mchar.sql +@@ -0,0 +1,81 @@ ++-- I/O tests ++ ++select '1'::mchar; ++select '2 '::mchar; ++select '10 '::mchar; ++ ++select '1'::mchar(2); ++select '2 '::mchar(2); ++select '3 '::mchar(2); ++select '10 '::mchar(2); ++ ++select ' '::mchar(10); ++select ' '::mchar; ++ ++-- operations & functions ++ ++select length('1'::mchar); ++select length('2 '::mchar); ++select length('10 '::mchar); ++ ++select length('1'::mchar(2)); ++select length('2 '::mchar(2)); ++select length('3 '::mchar(2)); ++select length('10 '::mchar(2)); ++ ++select length(' '::mchar(10)); ++select length(' '::mchar); ++ ++select 'asd'::mchar(10) || '>'::mchar(10); ++select length('asd'::mchar(10) || '>'::mchar(10)); ++select 'asd'::mchar(2) || '>'::mchar(10); ++select length('asd'::mchar(2) || '>'::mchar(10)); ++ ++-- Comparisons ++ ++select 'asdf'::mchar = 'aSdf'::mchar; ++select 'asdf'::mchar = 'aSdf '::mchar; ++select 'asdf'::mchar = 'aSdf 1'::mchar(4); ++select 'asdf'::mchar = 'aSdf 1'::mchar(5); ++select 'asdf'::mchar = 'aSdf 1'::mchar(6); ++select 'asdf'::mchar(3) = 'aSdf 1'::mchar(5); ++select 'asdf'::mchar(3) = 'aSdf 1'::mchar(3); ++ ++select 'asdf'::mchar < 'aSdf'::mchar; ++select 'asdf'::mchar < 'aSdf '::mchar; ++select 'asdf'::mchar < 'aSdf 1'::mchar(4); ++select 'asdf'::mchar < 'aSdf 1'::mchar(5); ++select 'asdf'::mchar < 'aSdf 1'::mchar(6); ++ ++select 'asdf'::mchar <= 'aSdf'::mchar; ++select 'asdf'::mchar <= 'aSdf '::mchar; ++select 'asdf'::mchar <= 'aSdf 1'::mchar(4); ++select 'asdf'::mchar <= 'aSdf 1'::mchar(5); ++select 'asdf'::mchar <= 'aSdf 1'::mchar(6); ++ ++select 'asdf'::mchar >= 'aSdf'::mchar; ++select 'asdf'::mchar >= 'aSdf '::mchar; ++select 'asdf'::mchar >= 'aSdf 1'::mchar(4); ++select 'asdf'::mchar >= 'aSdf 1'::mchar(5); ++select 'asdf'::mchar >= 'aSdf 1'::mchar(6); ++ ++select 'asdf'::mchar > 'aSdf'::mchar; ++select 'asdf'::mchar > 'aSdf '::mchar; ++select 'asdf'::mchar > 'aSdf 1'::mchar(4); ++select 'asdf'::mchar > 'aSdf 1'::mchar(5); ++select 'asdf'::mchar > 'aSdf 1'::mchar(6); ++ ++select max(ch) from chvch; ++select min(ch) from chvch; ++ ++select substr('1234567890'::mchar, 3) = '34567890' as "34567890"; ++select substr('1234567890'::mchar, 4, 3) = '456' as "456"; ++ ++select lower('asdfASDF'::mchar); ++select upper('asdfASDF'::mchar); ++ ++select 'asd'::mchar == 'aSd'::mchar; ++select 'asd'::mchar == 'aCd'::mchar; ++select 'asd'::mchar == NULL; ++select NULL == 'aCd'::mchar; ++select NULL::mchar == NULL; +diff --git a/contrib/mchar/sql/mm.sql b/contrib/mchar/sql/mm.sql +new file mode 100644 +index 0000000..c16aaa1 +--- /dev/null ++++ b/contrib/mchar/sql/mm.sql +@@ -0,0 +1,185 @@ ++select 'asd'::mchar::mvarchar; ++select 'asd '::mchar::mvarchar; ++select 'asd'::mchar(2)::mvarchar; ++select 'asd '::mchar(2)::mvarchar; ++select 'asd'::mchar(5)::mvarchar; ++select 'asd '::mchar(5)::mvarchar; ++select 'asd'::mchar::mvarchar(2); ++select 'asd '::mchar::mvarchar(2); ++select 'asd'::mchar(2)::mvarchar(2); ++select 'asd '::mchar(2)::mvarchar(2); ++select 'asd'::mchar(5)::mvarchar(2); ++select 'asd '::mchar(5)::mvarchar(2); ++select 'asd'::mchar::mvarchar(5); ++select 'asd '::mchar::mvarchar(5); ++select 'asd'::mchar(2)::mvarchar(5); ++select 'asd '::mchar(2)::mvarchar(5); ++select 'asd'::mchar(5)::mvarchar(5); ++select 'asd '::mchar(5)::mvarchar(5); ++ ++select 'asd'::mvarchar::mchar; ++select 'asd '::mvarchar::mchar; ++select 'asd'::mvarchar(2)::mchar; ++select 'asd '::mvarchar(2)::mchar; ++select 'asd'::mvarchar(5)::mchar; ++select 'asd '::mvarchar(5)::mchar; ++select 'asd'::mvarchar::mchar(2); ++select 'asd '::mvarchar::mchar(2); ++select 'asd'::mvarchar(2)::mchar(2); ++select 'asd '::mvarchar(2)::mchar(2); ++select 'asd'::mvarchar(5)::mchar(2); ++select 'asd '::mvarchar(5)::mchar(2); ++select 'asd'::mvarchar::mchar(5); ++select 'asd '::mvarchar::mchar(5); ++select 'asd'::mvarchar(2)::mchar(5); ++select 'asd '::mvarchar(2)::mchar(5); ++select 'asd'::mvarchar(5)::mchar(5); ++select 'asd '::mvarchar(5)::mchar(5); ++ ++select 'asd'::mchar || '123'; ++select 'asd'::mchar || '123'::mchar; ++select 'asd'::mchar || '123'::mvarchar; ++ ++select 'asd '::mchar || '123'; ++select 'asd '::mchar || '123'::mchar; ++select 'asd '::mchar || '123'::mvarchar; ++ ++select 'asd '::mchar || '123 '; ++select 'asd '::mchar || '123 '::mchar; ++select 'asd '::mchar || '123 '::mvarchar; ++ ++ ++select 'asd'::mvarchar || '123'; ++select 'asd'::mvarchar || '123'::mchar; ++select 'asd'::mvarchar || '123'::mvarchar; ++ ++select 'asd '::mvarchar || '123'; ++select 'asd '::mvarchar || '123'::mchar; ++select 'asd '::mvarchar || '123'::mvarchar; ++ ++select 'asd '::mvarchar || '123 '; ++select 'asd '::mvarchar || '123 '::mchar; ++select 'asd '::mvarchar || '123 '::mvarchar; ++ ++ ++select 'asd'::mchar(2) || '123'; ++select 'asd'::mchar(2) || '123'::mchar; ++select 'asd'::mchar(2) || '123'::mvarchar; ++ ++ ++select 'asd '::mchar(2) || '123'; ++select 'asd '::mchar(2) || '123'::mchar; ++select 'asd '::mchar(2) || '123'::mvarchar; ++ ++ ++select 'asd '::mchar(2) || '123 '; ++select 'asd '::mchar(2) || '123 '::mchar; ++select 'asd '::mchar(2) || '123 '::mvarchar; ++ ++select 'asd'::mvarchar(2) || '123'; ++select 'asd'::mvarchar(2) || '123'::mchar; ++select 'asd'::mvarchar(2) || '123'::mvarchar; ++ ++select 'asd '::mvarchar(2) || '123'; ++select 'asd '::mvarchar(2) || '123'::mchar; ++select 'asd '::mvarchar(2) || '123'::mvarchar; ++ ++select 'asd '::mvarchar(2) || '123 '; ++select 'asd '::mvarchar(2) || '123 '::mchar; ++select 'asd '::mvarchar(2) || '123 '::mvarchar; ++ ++select 'asd'::mchar(4) || '143'; ++select 'asd'::mchar(4) || '123'::mchar; ++select 'asd'::mchar(4) || '123'::mvarchar; ++ ++select 'asd '::mchar(4) || '123'; ++select 'asd '::mchar(4) || '123'::mchar; ++select 'asd '::mchar(4) || '123'::mvarchar; ++ ++select 'asd '::mchar(4) || '123 '; ++select 'asd '::mchar(4) || '123 '::mchar; ++select 'asd '::mchar(4) || '123 '::mvarchar; ++ ++select 'asd'::mvarchar(4) || '123'; ++select 'asd'::mvarchar(4) || '123'::mchar; ++select 'asd'::mvarchar(4) || '123'::mvarchar; ++ ++select 'asd '::mvarchar(4) || '123'; ++select 'asd '::mvarchar(4) || '123'::mchar; ++select 'asd '::mvarchar(4) || '123'::mvarchar; ++ ++select 'asd '::mvarchar(4) || '123 '; ++select 'asd '::mvarchar(4) || '123 '::mchar; ++select 'asd '::mvarchar(4) || '123 '::mvarchar; ++ ++ ++select 'asd '::mvarchar(4) || '123 '::mchar(4); ++select 'asd '::mvarchar(4) || '123 '::mvarchar(4); ++select 'asd '::mvarchar(4) || '123'::mchar(4); ++select 'asd '::mvarchar(4) || '123'::mvarchar(4); ++ ++ ++select 1 where 'f'::mchar='F'::mvarchar; ++select 1 where 'f'::mchar='F '::mvarchar; ++select 1 where 'f '::mchar='F'::mvarchar; ++select 1 where 'f '::mchar='F '::mvarchar; ++ ++select 1 where 'f'::mchar='F'::mvarchar(2); ++select 1 where 'f'::mchar='F '::mvarchar(2); ++select 1 where 'f '::mchar='F'::mvarchar(2); ++select 1 where 'f '::mchar='F '::mvarchar(2); ++ ++select 1 where 'f'::mchar(2)='F'::mvarchar; ++select 1 where 'f'::mchar(2)='F '::mvarchar; ++select 1 where 'f '::mchar(2)='F'::mvarchar; ++select 1 where 'f '::mchar(2)='F '::mvarchar; ++ ++select 1 where 'f'::mchar(2)='F'::mvarchar(2); ++select 1 where 'f'::mchar(2)='F '::mvarchar(2); ++select 1 where 'f '::mchar(2)='F'::mvarchar(2); ++select 1 where 'f '::mchar(2)='F '::mvarchar(2); ++ ++select 1 where 'foo'::mchar='FOO'::mvarchar; ++select 1 where 'foo'::mchar='FOO '::mvarchar; ++select 1 where 'foo '::mchar='FOO'::mvarchar; ++select 1 where 'foo '::mchar='FOO '::mvarchar; ++ ++select 1 where 'foo'::mchar='FOO'::mvarchar(2); ++select 1 where 'foo'::mchar='FOO '::mvarchar(2); ++select 1 where 'foo '::mchar='FOO'::mvarchar(2); ++select 1 where 'foo '::mchar='FOO '::mvarchar(2); ++ ++select 1 where 'foo'::mchar(2)='FOO'::mvarchar; ++select 1 where 'foo'::mchar(2)='FOO '::mvarchar; ++select 1 where 'foo '::mchar(2)='FOO'::mvarchar; ++select 1 where 'foo '::mchar(2)='FOO '::mvarchar; ++ ++select 1 where 'foo'::mchar(2)='FOO'::mvarchar(2); ++select 1 where 'foo'::mchar(2)='FOO '::mvarchar(2); ++select 1 where 'foo '::mchar(2)='FOO'::mvarchar(2); ++select 1 where 'foo '::mchar(2)='FOO '::mvarchar(2); ++ ++Select 'f'::mchar(1) Union Select 'o'::mvarchar(1); ++Select 'f'::mvarchar(1) Union Select 'o'::mchar(1); ++ ++select * from chvch where ch=vch; ++ ++select ch.* from ch, (select 'dEfg'::mvarchar as q) as p where chcol > p.q; ++create index qq on ch (chcol); ++set enable_seqscan=off; ++select ch.* from ch, (select 'dEfg'::mvarchar as q) as p where chcol > p.q; ++set enable_seqscan=on; ++ ++ ++--\copy chvch to 'results/chvch.dump' binary ++--truncate table chvch; ++--\copy chvch from 'results/chvch.dump' binary ++ ++--test joins ++CREATE TABLE a (mchar2 MCHAR(2) NOT NULL); ++CREATE TABLE c (mvarchar255 mvarchar NOT NULL); ++SELECT * FROM a, c WHERE mchar2 = mvarchar255; ++SELECT * FROM a, c WHERE mvarchar255 = mchar2; ++DROP TABLE a; ++DROP TABLE c; ++ +diff --git a/contrib/mchar/sql/mvarchar.sql b/contrib/mchar/sql/mvarchar.sql +new file mode 100644 +index 0000000..91b0981 +--- /dev/null ++++ b/contrib/mchar/sql/mvarchar.sql +@@ -0,0 +1,82 @@ ++-- I/O tests ++ ++select '1'::mvarchar; ++select '2 '::mvarchar; ++select '10 '::mvarchar; ++ ++select '1'::mvarchar(2); ++select '2 '::mvarchar(2); ++select '3 '::mvarchar(2); ++select '10 '::mvarchar(2); ++ ++select ' '::mvarchar(10); ++select ' '::mvarchar; ++ ++-- operations & functions ++ ++select length('1'::mvarchar); ++select length('2 '::mvarchar); ++select length('10 '::mvarchar); ++ ++select length('1'::mvarchar(2)); ++select length('2 '::mvarchar(2)); ++select length('3 '::mvarchar(2)); ++select length('10 '::mvarchar(2)); ++ ++select length(' '::mvarchar(10)); ++select length(' '::mvarchar); ++ ++select 'asd'::mvarchar(10) || '>'::mvarchar(10); ++select length('asd'::mvarchar(10) || '>'::mvarchar(10)); ++select 'asd'::mvarchar(2) || '>'::mvarchar(10); ++select length('asd'::mvarchar(2) || '>'::mvarchar(10)); ++ ++-- Comparisons ++ ++select 'asdf'::mvarchar = 'aSdf'::mvarchar; ++select 'asdf'::mvarchar = 'aSdf '::mvarchar; ++select 'asdf'::mvarchar = 'aSdf 1'::mvarchar(4); ++select 'asdf'::mvarchar = 'aSdf 1'::mvarchar(5); ++select 'asdf'::mvarchar = 'aSdf 1'::mvarchar(6); ++select 'asdf'::mvarchar(3) = 'aSdf 1'::mvarchar(5); ++select 'asdf'::mvarchar(3) = 'aSdf 1'::mvarchar(3); ++ ++select 'asdf'::mvarchar < 'aSdf'::mvarchar; ++select 'asdf'::mvarchar < 'aSdf '::mvarchar; ++select 'asdf'::mvarchar < 'aSdf 1'::mvarchar(4); ++select 'asdf'::mvarchar < 'aSdf 1'::mvarchar(5); ++select 'asdf'::mvarchar < 'aSdf 1'::mvarchar(6); ++ ++select 'asdf'::mvarchar <= 'aSdf'::mvarchar; ++select 'asdf'::mvarchar <= 'aSdf '::mvarchar; ++select 'asdf'::mvarchar <= 'aSdf 1'::mvarchar(4); ++select 'asdf'::mvarchar <= 'aSdf 1'::mvarchar(5); ++select 'asdf'::mvarchar <= 'aSdf 1'::mvarchar(6); ++ ++select 'asdf'::mvarchar >= 'aSdf'::mvarchar; ++select 'asdf'::mvarchar >= 'aSdf '::mvarchar; ++select 'asdf'::mvarchar >= 'aSdf 1'::mvarchar(4); ++select 'asdf'::mvarchar >= 'aSdf 1'::mvarchar(5); ++select 'asdf'::mvarchar >= 'aSdf 1'::mvarchar(6); ++ ++select 'asdf'::mvarchar > 'aSdf'::mvarchar; ++select 'asdf'::mvarchar > 'aSdf '::mvarchar; ++select 'asdf'::mvarchar > 'aSdf 1'::mvarchar(4); ++select 'asdf'::mvarchar > 'aSdf 1'::mvarchar(5); ++select 'asdf'::mvarchar > 'aSdf 1'::mvarchar(6); ++ ++select max(vch) from chvch; ++select min(vch) from chvch; ++ ++select substr('1234567890'::mvarchar, 3) = '34567890' as "34567890"; ++select substr('1234567890'::mvarchar, 4, 3) = '456' as "456"; ++ ++select lower('asdfASDF'::mvarchar); ++select upper('asdfASDF'::mvarchar); ++ ++select 'asd'::mvarchar == 'aSd'::mvarchar; ++select 'asd'::mvarchar == 'aCd'::mvarchar; ++select 'asd'::mvarchar == NULL; ++select NULL == 'aCd'::mvarchar; ++select NULL::mvarchar == NULL; ++ +diff --git a/contrib/mchar/uninstall_mchar.sql b/contrib/mchar/uninstall_mchar.sql +new file mode 100644 +index 0000000..59f61e2 +--- /dev/null ++++ b/contrib/mchar/uninstall_mchar.sql +@@ -0,0 +1,9 @@ ++SET search_path = public; ++BEGIN; ++ ++DROP FUNCTION mchar_pattern_fixed_prefix(internal, internal, internal); ++DROP FUNCTION mchar_greaterstring(internal); ++DROP TYPE MCHAR CASCADE; ++DROP TYPE MVARCHAR CASCADE; ++ ++COMMIT; +diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c +index f6e2114..19ad0eb 100644 +--- a/src/backend/nodes/outfuncs.c ++++ b/src/backend/nodes/outfuncs.c +@@ -1585,6 +1585,7 @@ _outAppendPath(StringInfo str, const AppendPath *node) + _outPathInfo(str, (const Path *) node); + + WRITE_NODE_FIELD(subpaths); ++ WRITE_BOOL_FIELD(pull_tlist); + } + + static void +diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c +index bfd3809..a7cab3b 100644 +--- a/src/backend/optimizer/path/allpaths.c ++++ b/src/backend/optimizer/path/allpaths.c +@@ -396,6 +396,9 @@ set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) + /* Consider index scans */ + create_index_paths(root, rel); + ++ /* Consider index scans with rewrited quals */ ++ keybased_rewrite_index_paths(root, rel); ++ + /* Consider TID scans */ + create_tidscan_paths(root, rel); + +@@ -799,7 +802,7 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, + * if we have zero or one live subpath due to constraint exclusion.) + */ + if (subpaths_valid) +- add_path(rel, (Path *) create_append_path(rel, subpaths, NULL)); ++ add_path(rel, (Path *) create_append_path(rel, subpaths, NULL, false, NIL)); + + /* + * Also build unparameterized MergeAppend paths based on the collected +@@ -849,7 +852,7 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, + + if (subpaths_valid) + add_path(rel, (Path *) +- create_append_path(rel, subpaths, required_outer)); ++ create_append_path(rel, subpaths, required_outer, false, NIL)); + } + + /* Select cheapest paths */ +@@ -1067,7 +1070,7 @@ set_dummy_rel_pathlist(RelOptInfo *rel) + /* Discard any pre-existing paths; no further need for them */ + rel->pathlist = NIL; + +- add_path(rel, (Path *) create_append_path(rel, NIL, NULL)); ++ add_path(rel, (Path *) create_append_path(rel, NIL, NULL, false, NIL)); + + /* Select cheapest path (pretty easy in this case...) */ + set_cheapest(rel); +diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c +index 606734a..56d240c 100644 +--- a/src/backend/optimizer/path/indxpath.c ++++ b/src/backend/optimizer/path/indxpath.c +@@ -38,6 +38,14 @@ + #include "utils/pg_locale.h" + #include "utils/selfuncs.h" + ++/* ++ * index support for LIKE mchar ++ */ ++#include "fmgr.h" ++#include "access/htup_details.h" ++#include "utils/catcache.h" ++#include "utils/syscache.h" ++#include "parser/parse_type.h" + + #define IsBooleanOpfamily(opfamily) \ + ((opfamily) == BOOL_BTREE_FAM_OID || (opfamily) == BOOL_HASH_FAM_OID) +@@ -2968,6 +2976,208 @@ match_index_to_operand(Node *operand, + } + + /**************************************************************************** ++ * ---- ROUTINES FOR "SPECIAL" INDEXABLE OPERATORS FOR ++ * SPECIAL USER_DEFINED TYPES ---- ++ * -- teodor ++ ****************************************************************************/ ++ ++static Oid mmPFPOid = InvalidOid; ++static Oid mmGTOid = InvalidOid; ++static Oid mcharOid = InvalidOid; ++static Oid mvarcharOid = InvalidOid; ++ ++static bool ++fillMCharOIDS() { ++ CatCList *catlist; ++ HeapTuple tup; ++ char *funcname = "mchar_pattern_fixed_prefix"; ++ int n_members; ++ ++ catlist = SearchSysCacheList(PROCNAMEARGSNSP, 1, ++ CStringGetDatum(funcname), ++ 0, 0, 0); ++ n_members = catlist->n_members; ++ ++ if ( n_members != 1 ) { ++ ReleaseSysCacheList(catlist); ++ if ( n_members > 1 ) ++ elog(ERROR,"There are %d candidates for '%s' function'", n_members, funcname); ++ return false; ++ } ++ ++ tup = &catlist->members[0]->tuple; ++ ++ if ( HeapTupleGetOid(tup) != mmPFPOid ) { ++ TypeName *typename; ++ Type typtup; ++ char *quals_funcname = "mchar_greaterstring"; ++ Oid tmp_mmPFPOid = HeapTupleGetOid(tup); ++ ++ ReleaseSysCacheList(catlist); ++ ++ typename = makeTypeName("mchar"); ++ typtup = LookupTypeName(NULL, typename, NULL); ++ if ( typtup ) { ++ mcharOid = typeTypeId(typtup); ++ ReleaseSysCache(typtup); ++ } ++ ++ typename = makeTypeName("mvarchar"); ++ typtup = LookupTypeName(NULL, typename, NULL); ++ if ( typtup ) { ++ mvarcharOid = typeTypeId(typtup); ++ ReleaseSysCache(typtup); ++ } ++ ++ ++ if ( mcharOid == InvalidOid || mvarcharOid == InvalidOid ) { ++ elog(LOG,"Can't find mchar/mvarvarchar types: mchar=%d mvarchar=%d", ++ mcharOid, mvarcharOid); ++ return false; ++ } ++ ++ catlist = SearchSysCacheList(PROCNAMEARGSNSP, 1, ++ CStringGetDatum(quals_funcname), ++ 0, 0, 0); ++ n_members = catlist->n_members; ++ ++ if ( n_members != 1 ) { ++ ReleaseSysCacheList(catlist); ++ if ( n_members > 1 ) ++ elog(ERROR,"There are %d candidates for '%s' function'", n_members, quals_funcname); ++ return false; ++ } ++ ++ tup = &catlist->members[0]->tuple; ++ mmGTOid = HeapTupleGetOid(tup); ++ mmPFPOid = tmp_mmPFPOid; ++ } ++ ++ ReleaseSysCacheList(catlist); ++ ++ return true; ++} ++ ++static Pattern_Prefix_Status ++mchar_pattern_fixed_prefix(Oid opOid, Oid opfamilyOid, Const *patt, Pattern_Type ptype, ++ Const **prefix, Oid *leftTypeOid) { ++ HeapTuple tup; ++ Form_pg_operator oprForm; ++ bool isMCharLike = true; ++ ++ if ( !fillMCharOIDS() ) ++ return Pattern_Prefix_None; ++ ++ tup = SearchSysCache(OPEROID, opOid, 0, 0, 0); ++ oprForm = (Form_pg_operator) GETSTRUCT(tup); ++ ++ if ( strncmp(oprForm->oprname.data, "~~", 2) != 0 ) ++ isMCharLike = false; ++ ++ if ( oprForm->oprright != mvarcharOid ) ++ isMCharLike = false; ++ ++ if ( !( oprForm->oprleft == mcharOid || oprForm->oprleft == mvarcharOid ) ) ++ isMCharLike = false; ++ ++ if ( patt->consttype != mvarcharOid ) ++ isMCharLike = false; ++ ++ if (leftTypeOid) ++ *leftTypeOid = oprForm->oprleft; ++ ++ ReleaseSysCache(tup); ++ ++ if ( !isMCharLike ) ++ return Pattern_Prefix_None; ++ ++ if ( opfamilyOid != InvalidOid ) { ++ Form_pg_opfamily claForm; ++ ++ tup = SearchSysCache(OPFAMILYOID, opfamilyOid, 0, 0, 0); ++ claForm = (Form_pg_opfamily) GETSTRUCT(tup); ++ ++ if ( claForm->opfmethod != BTREE_AM_OID ) ++ isMCharLike = false; ++ ++ if ( mcharOid && strncmp(claForm->opfname.data, "icase_ops", 9 /* strlen(icase_ops) */ ) != 0 ) ++ isMCharLike = false; ++ ++ ReleaseSysCache(tup); ++ } ++ ++ if ( !isMCharLike ) ++ return Pattern_Prefix_None; ++ ++ return (Pattern_Prefix_Status)DatumGetInt32( OidFunctionCall3( ++ mmPFPOid, ++ PointerGetDatum( patt ), ++ Int32GetDatum( ptype ), ++ PointerGetDatum( prefix ) ++ ) ); ++} ++ ++static Oid ++get_opclass_member_mchar(Oid opclass, Oid leftTypeOid, int strategy) { ++ Oid oproid; ++ ++ oproid = get_opfamily_member(opclass, leftTypeOid, mvarcharOid, strategy); ++ ++ if ( oproid == InvalidOid ) ++ elog(ERROR, "no operator for opclass %u for strategy %u for left type %u", opclass, strategy, leftTypeOid); ++ ++ return oproid; ++} ++ ++static List * ++mchar_prefix_quals(Node *leftop, Oid leftTypeOid, Oid opclass, ++ Const *prefix_const, Pattern_Prefix_Status pstatus) { ++ Oid oproid; ++ Expr *expr; ++ List *result; ++ Const *greaterstr; ++ ++ Assert(pstatus != Pattern_Prefix_None); ++ if ( pstatus == Pattern_Prefix_Exact ) { ++ oproid = get_opclass_member_mchar(opclass, leftTypeOid, BTEqualStrategyNumber); ++ ++ expr = make_opclause(oproid, BOOLOID, false, ++ (Expr *) leftop, (Expr *) prefix_const, ++ InvalidOid, InvalidOid); ++ result = list_make1(make_simple_restrictinfo(expr)); ++ return result; ++ } ++ ++ /* We can always say "x >= prefix". */ ++ oproid = get_opclass_member_mchar(opclass, leftTypeOid, BTGreaterEqualStrategyNumber); ++ ++ expr = make_opclause(oproid, BOOLOID, false, ++ (Expr *) leftop, (Expr *) prefix_const, ++ InvalidOid, InvalidOid); ++ result = list_make1(make_simple_restrictinfo(expr)); ++ ++ /* If we can create a string larger than the prefix, we can say ++ * "x < greaterstr". */ ++ ++ greaterstr = (Const*)DatumGetPointer( OidFunctionCall1( ++ mmGTOid, ++ PointerGetDatum( prefix_const ) ++ ) ); ++ ++ if (greaterstr) { ++ oproid = get_opclass_member_mchar(opclass, leftTypeOid, BTLessStrategyNumber); ++ ++ expr = make_opclause(oproid, BOOLOID, false, ++ (Expr *) leftop, (Expr *) greaterstr, ++ InvalidOid, InvalidOid); ++ result = lappend(result, make_simple_restrictinfo(expr)); ++ } ++ ++ return result; ++} ++ ++ ++/**************************************************************************** + * ---- ROUTINES FOR "SPECIAL" INDEXABLE OPERATORS ---- + ****************************************************************************/ + +@@ -3159,9 +3369,16 @@ match_special_index_operator(Expr *clause, Oid opfamily, Oid idxcollation, + pfree(prefix); + } + +- /* done if the expression doesn't look indexable */ +- if (!isIndexable) ++ if ( !isIndexable ) { ++ /* done if the expression doesn't look indexable, ++ but we should previously check it for mchar/mvarchar types */ ++ if ( mchar_pattern_fixed_prefix(expr_op, InvalidOid, ++ patt, Pattern_Type_Like, ++ &prefix, NULL) != Pattern_Prefix_None ) { ++ return true; ++ } + return false; ++ } + + /* + * Must also check that index's opfamily supports the operators we will +@@ -3412,6 +3629,14 @@ expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily, Oid idxcollation) + Const *patt = (Const *) rightop; + Const *prefix = NULL; + Pattern_Prefix_Status pstatus; ++ Oid leftTypeOid; ++ ++ pstatus = mchar_pattern_fixed_prefix(expr_op, opfamily, ++ patt, Pattern_Type_Like, ++ &prefix, &leftTypeOid); ++ ++ if ( pstatus != Pattern_Prefix_None ) ++ return mchar_prefix_quals(leftop, leftTypeOid, opfamily, prefix, pstatus); + + /* + * LIKE and regex operators are not members of any btree index opfamily, +diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c +index d627f9e..589b728 100644 +--- a/src/backend/optimizer/path/joinrels.c ++++ b/src/backend/optimizer/path/joinrels.c +@@ -1053,7 +1053,7 @@ mark_dummy_rel(RelOptInfo *rel) + rel->pathlist = NIL; + + /* Set up the dummy path */ +- add_path(rel, (Path *) create_append_path(rel, NIL, NULL)); ++ add_path(rel, (Path *) create_append_path(rel, NIL, NULL, false, NIL)); + + /* Set or update cheapest_total_path and related fields */ + set_cheapest(rel); +diff --git a/src/backend/optimizer/path/orindxpath.c b/src/backend/optimizer/path/orindxpath.c +index 16f29d3..a0b6c53 100644 +--- a/src/backend/optimizer/path/orindxpath.c ++++ b/src/backend/optimizer/path/orindxpath.c +@@ -15,10 +15,58 @@ + + #include "postgres.h" + ++#include "access/skey.h" ++#include "catalog/pg_am.h" + #include "optimizer/cost.h" ++#include "optimizer/clauses.h" + #include "optimizer/paths.h" ++#include "optimizer/pathnode.h" ++#include "optimizer/planmain.h" ++#include "optimizer/predtest.h" + #include "optimizer/restrictinfo.h" ++#include "utils/lsyscache.h" + ++typedef struct CKey { ++ RestrictInfo *rinfo; /* original rinfo */ ++ int n; /* IndexPath's number in bitmapquals */ ++ OpExpr *normalizedexpr; /* expression with Var on left */ ++ Var *var; ++ Node *value; ++ Oid opfamily; ++ int strategy; ++ uint8 strategyMask; ++} CKey; ++#define BTMASK(x) ( 1<<(x) ) ++ ++static List* find_common_quals( BitmapOrPath *path ); ++static RestrictInfo* unionOperation(CKey *key); ++static BitmapOrPath* cleanup_nested_quals( PlannerInfo *root, RelOptInfo *rel, BitmapOrPath *path ); ++static List* sortIndexScans( List* ipaths ); ++static List* reverseScanDirIdxPaths(List *indexPaths); ++static IndexPath* reverseScanDirIdxPath(IndexPath *ipath); ++ ++#define IS_LESS(a) ( (a) == BTLessStrategyNumber || (a)== BTLessEqualStrategyNumber ) ++#define IS_GREATER(a) ( (a) == BTGreaterStrategyNumber || (a) == BTGreaterEqualStrategyNumber ) ++#define IS_ONE_DIRECTION(a,b) ( \ ++ ( IS_LESS(a) && IS_LESS(b) ) \ ++ || \ ++ ( IS_GREATER(a) && IS_GREATER(b) ) \ ++) ++ ++typedef struct ExExpr { ++ OpExpr *expr; ++ Oid opfamily; ++ Oid lefttype; ++ Oid righttype; ++ int strategy; ++ int attno; ++} ExExpr; ++ ++ ++typedef struct IndexPathEx { ++ IndexPath *path; ++ List *preparedquals; /* list of ExExpr */ ++} IndexPathEx; + + /*---------- + * create_or_index_quals +@@ -185,3 +233,912 @@ create_or_index_quals(PlannerInfo *root, RelOptInfo *rel) + /* Tell caller to recompute partial index status and rowcount estimate */ + return true; + } ++ ++ ++/*---------- ++ * keybased_rewrite_or_index_quals ++ * Examine join OR-of-AND quals to see if any useful common restriction ++ * clauses can be extracted. If so, try to use for creating new index paths. ++ * ++ * For example consider ++ * WHERE ( a.x=5 and a.y>10 ) OR a.x>5 ++ * and there is an index on a.x or (a.x, a.y). So, plan ++ * will be seqscan or BitmapOr(IndexPath,IndexPath) ++ * So, we can add some restriction: ++ * WHERE (( a.x=5 and a.y>10 ) OR a.x>5) AND a.x>=5 ++ * and plan may be so ++ * Index Scan (a.x>=5) ++ * Filter( (( a.x=5 and a.y>10 ) OR a.x>5) ) ++ * ++ * We don't want to add new clauses to baserestrictinfo, just ++ * use it as index quals. ++ * ++ * Next thing which it possible to test is use append of ++ * searches instead of OR. ++ * For example consider ++ * WHERE ( a.x=5 and a.y>10 ) OR a.x>6 ++ * and there is an index on (a.x) (a.x, a.y) ++ * So, we can suggest follow plan: ++ * Append ++ * Filter ( a.x=5 and a.y>10 ) OR (a.x>6) ++ * Index Scan (a.x=5) --in case of index on (a.x) ++ * Index Scan (a.x>6) ++ * For that we should proof that index quals isn't overlapped, ++ * also, some index quals may be containedi in other, so it can be eliminated ++ */ ++ ++void ++keybased_rewrite_index_paths(PlannerInfo *root, RelOptInfo *rel) ++{ ++ BitmapOrPath *bestpath = NULL; ++ ListCell *i; ++ List *commonquals; ++ AppendPath *appendidxpath; ++ List *indexPaths; ++ IndexOptInfo *index; ++ ++ foreach(i, rel->baserestrictinfo) ++ { ++ RestrictInfo *rinfo = (RestrictInfo *) lfirst(i); ++ ++ if (restriction_is_or_clause(rinfo) && ++ !rinfo->outerjoin_delayed) ++ { ++ /* ++ * Use the generate_bitmap_or_paths() machinery to estimate the ++ * value of each OR clause. We can use regular restriction ++ * clauses along with the OR clause contents to generate ++ * indexquals. We pass outer_rel = NULL so that sub-clauses ++ * that are actually joins will be ignored. ++ */ ++ List *orpaths; ++ ListCell *k; ++ ++ orpaths = generate_bitmap_or_paths(root, rel, ++ list_make1(rinfo), ++ rel->baserestrictinfo, ++ true); ++ ++ /* Locate the cheapest OR path */ ++ foreach(k, orpaths) ++ { ++ BitmapOrPath *path = (BitmapOrPath *) lfirst(k); ++ ++ Assert(IsA(path, BitmapOrPath)); ++ if (bestpath == NULL || ++ path->path.total_cost < bestpath->path.total_cost) ++ { ++ bestpath = path; ++ } ++ } ++ } ++ } ++ ++ /* Fail if no suitable clauses found */ ++ if (bestpath == NULL) ++ return; ++ ++ commonquals = find_common_quals(bestpath); ++ /* Found quals with the same args, but with, may be, different ++ operations */ ++ if ( commonquals != NULL ) { ++ List *addon=NIL; ++ ++ foreach(i, commonquals) { ++ CKey *key = (CKey*)lfirst(i); ++ RestrictInfo *rinfo; ++ ++ /* ++ * get 'union' of operation for key ++ */ ++ rinfo = unionOperation(key); ++ if ( rinfo ) ++ addon = lappend(addon, rinfo); ++ } ++ ++ /* ++ * Ok, we found common quals and union it, so we will try to ++ * create new possible index paths ++ */ ++ if ( addon ) { ++ List *origbaserestrictinfo = list_copy(rel->baserestrictinfo); ++ ++ rel->baserestrictinfo = list_concat(rel->baserestrictinfo, addon); ++ ++ create_index_paths(root, rel); ++ ++ rel->baserestrictinfo = origbaserestrictinfo; ++ } ++ } ++ ++ /* ++ * Check if indexquals isn't overlapped and all index scan ++ * are on the same index. ++ */ ++ if ( (bestpath = cleanup_nested_quals( root, rel, bestpath )) == NULL ) ++ return; ++ ++ if (IsA(bestpath, IndexPath)) { ++ IndexPath *ipath = (IndexPath*)bestpath; ++ ++ Assert(list_length(ipath->indexquals) == list_length(ipath->indexqualcols)); ++ /* ++ * It's possible to do only one index scan :) ++ */ ++ index = ipath->indexinfo; ++ ++ if ( root->query_pathkeys != NIL && index->sortopfamily && OidIsValid(index->sortopfamily[0]) ) ++ { ++ List *pathkeys; ++ ++ pathkeys = build_index_pathkeys(root, index, ++ ForwardScanDirection); ++ pathkeys = truncate_useless_pathkeys(root, rel, ++ pathkeys); ++ ++ ipath->path.pathkeys = pathkeys; ++ add_path(rel, (Path *) ipath); ++ ++ /* ++ * add path ordered in backward direction if our pathkeys ++ * is still unusable... ++ */ ++ if ( pathkeys == NULL || pathkeys_useful_for_ordering(root, pathkeys) == 0 ) ++ { ++ pathkeys = build_index_pathkeys(root, index, ++ BackwardScanDirection); ++ pathkeys = truncate_useless_pathkeys(root, rel, ++ pathkeys); ++ ++ ipath = reverseScanDirIdxPath( ipath ); ++ ++ ipath->path.pathkeys = pathkeys; ++ add_path(rel, (Path *) ipath); ++ } ++ } else ++ add_path(rel, (Path *) ipath); ++ return; ++ } ++ ++ /* recount costs */ ++ foreach(i, bestpath->bitmapquals ) { ++ IndexPath *ipath = (IndexPath*)lfirst(i); ++ ++ Assert( IsA(ipath, IndexPath) ); ++ Assert(list_length(ipath->indexquals) == list_length(ipath->indexqualcols)); ++ ipath->path.rows = rel->tuples * clauselist_selectivity(root, ++ ipath->indexquals, ++ rel->relid, ++ JOIN_INNER, ++ NULL); ++ ipath->path.rows = clamp_row_est(ipath->path.rows); ++ cost_index(ipath, root, 1); ++ } ++ ++ /* ++ * Check if append index can suggest ordering of result ++ * ++ * Also, we should say to AppendPath about targetlist: ++ * target list will be taked from indexscan ++ */ ++ index = ((IndexPath*)linitial(bestpath->bitmapquals))->indexinfo; ++ if ( root->query_pathkeys != NIL && index->sortopfamily && OidIsValid(index->sortopfamily[0]) && ++ (indexPaths = sortIndexScans( bestpath->bitmapquals )) !=NULL ) { ++ List *pathkeys; ++ ++ pathkeys = build_index_pathkeys(root, index, ++ ForwardScanDirection); ++ pathkeys = truncate_useless_pathkeys(root, rel, ++ pathkeys); ++ ++ appendidxpath = create_append_path(rel, indexPaths, NULL, true, pathkeys); ++ add_path(rel, (Path *) appendidxpath); ++ ++ /* ++ * add path ordered in backward direction if our pathkeys ++ * is still unusable... ++ */ ++ if ( pathkeys == NULL || pathkeys_useful_for_ordering(root, pathkeys) == 0 ) { ++ ++ pathkeys = build_index_pathkeys(root, index, ++ BackwardScanDirection); ++ pathkeys = truncate_useless_pathkeys(root, rel, ++ pathkeys); ++ ++ indexPaths = reverseScanDirIdxPaths(indexPaths); ++ appendidxpath = create_append_path(rel, indexPaths, NULL, true, pathkeys); ++ add_path(rel, (Path *) appendidxpath); ++ } ++ } else { ++ appendidxpath = create_append_path(rel, bestpath->bitmapquals, NULL, true, NIL); ++ add_path(rel, (Path *) appendidxpath); ++ } ++} ++ ++/* ++ * transformToCkey - transform RestrictionInfo ++ * to CKey struct. Fucntion checks possibility and correctness of ++ * RestrictionInfo to use it as common key, normalizes ++ * expression and "caches" some information. Note, ++ * original RestrictInfo isn't touched ++ */ ++ ++static CKey* ++transformToCkey( IndexOptInfo *index, RestrictInfo* rinfo, int indexcol) { ++ CKey *key; ++ OpExpr *expr = (OpExpr*)rinfo->clause; ++ ++ if ( rinfo->outerjoin_delayed ) ++ return NULL; ++ ++ if ( !IsA(expr, OpExpr) ) ++ return NULL; ++ ++ if ( contain_mutable_functions((Node*)expr) ) ++ return NULL; ++ ++ if ( list_length( expr->args ) != 2 ) ++ return NULL; ++ ++ key = (CKey*)palloc(sizeof(CKey)); ++ key->rinfo = rinfo; ++ ++ key->normalizedexpr = (OpExpr*)copyObject( expr ); ++ if (!bms_equal(rinfo->left_relids, index->rel->relids)) ++ CommuteOpExpr(key->normalizedexpr); ++ ++ /* ++ * fix_indexqual_operand returns copy of object ++ */ ++ key->var = (Var*)fix_indexqual_operand(linitial(key->normalizedexpr->args), index, indexcol); ++ Assert( IsA(key->var, Var) ); ++ ++ key->opfamily = index->opfamily[ key->var->varattno - 1 ]; ++ ++ /* restore varattno, because it may be different in different index */ ++ key->var->varattno = key->var->varoattno; ++ ++ key->value = (Node*)lsecond(key->normalizedexpr->args); ++ ++ key->strategy = get_op_opfamily_strategy( key->normalizedexpr->opno, key->opfamily); ++ Assert( key->strategy != InvalidStrategy ); ++ ++ key->strategyMask = BTMASK(key->strategy); ++ ++ return key; ++} ++ ++/* ++ * get_index_quals - get list of quals in ++ * CKeys form ++ */ ++ ++static List* ++get_index_quals(IndexPath *path, int cnt) { ++ ListCell *i, *c; ++ List *quals = NIL; ++ ++ Assert(list_length(path->indexquals) == list_length(path->indexqualcols)); ++ forboth(i, path->indexquals, c, path->indexqualcols) { ++ CKey *k = transformToCkey( path->indexinfo, (RestrictInfo*)lfirst(i), lfirst_int(c) ); ++ if ( k ) { ++ k->n = cnt; ++ quals = lappend(quals, k); ++ } ++ } ++ return quals; ++} ++ ++/* ++ * extract all quals from bitmapquals->indexquals for ++ */ ++static List* ++find_all_quals( BitmapOrPath *path, int *counter ) { ++ ListCell *i,*j; ++ List *allquals = NIL; ++ ++ *counter = 0; ++ ++ foreach(i, path->bitmapquals ) ++ { ++ Path *subpath = (Path *) lfirst(i); ++ ++ if ( IsA(subpath, BitmapAndPath) ) { ++ foreach(j, ((BitmapAndPath*)subpath)->bitmapquals) { ++ Path *subsubpath = (Path *) lfirst(i); ++ ++ if ( IsA(subsubpath, IndexPath) ) { ++ if ( ((IndexPath*)subsubpath)->indexinfo->relam != BTREE_AM_OID ) ++ return NIL; ++ allquals = list_concat(allquals, get_index_quals( (IndexPath*)subsubpath, *counter )); ++ } else ++ return NIL; ++ } ++ } else if ( IsA(subpath, IndexPath) ) { ++ if ( ((IndexPath*)subpath)->indexinfo->relam != BTREE_AM_OID ) ++ return NIL; ++ allquals = list_concat(allquals, get_index_quals( (IndexPath*)subpath, *counter )); ++ } else ++ return NIL; ++ ++ (*counter)++; ++ } ++ ++ return allquals; ++} ++ ++/* ++ * Compares aruments of operation ++ */ ++static bool ++iseqCKeyArgs( CKey *a, CKey *b ) { ++ if ( a->opfamily != b->opfamily ) ++ return false; ++ ++ if ( !equal( a->value, b->value ) ) ++ return false; ++ ++ if ( !equal( a->var, b->var ) ) ++ return false; ++ ++ return true; ++} ++ ++/* ++ * Count entries of CKey with the same arguments ++ */ ++static int ++count_entry( List *allquals, CKey *tocmp ) { ++ ListCell *i; ++ int curcnt=0; ++ ++ foreach(i, allquals) { ++ CKey *key = lfirst(i); ++ ++ if ( key->n == curcnt ) { ++ continue; ++ } else if ( key->n == curcnt+1 ) { ++ if ( iseqCKeyArgs( key, tocmp ) ) { ++ tocmp->strategyMask |= key->strategyMask; ++ curcnt++; ++ } ++ } else ++ return -1; ++ } ++ ++ return curcnt+1; ++} ++ ++/* ++ * Finds all CKey with the same arguments ++ */ ++static List* ++find_common_quals( BitmapOrPath *path ) { ++ List *allquals; ++ List *commonquals = NIL; ++ ListCell *i; ++ int counter; ++ ++ if ( (allquals = find_all_quals( path, &counter ))==NIL ) ++ return NIL; ++ ++ foreach(i, allquals) { ++ CKey *key = lfirst(i); ++ ++ if ( key->n != 0 ) ++ break; ++ ++ if ( counter == count_entry(allquals, key) ) ++ commonquals = lappend( commonquals, key ); ++ } ++ ++ return commonquals; ++} ++ ++/* ++ * unionOperation - make RestrictInfo with combined operation ++ */ ++ ++static RestrictInfo* ++unionOperation(CKey *key) { ++ RestrictInfo *rinfo; ++ Oid lefttype, righttype; ++ int strategy; ++ ++ switch( key->strategyMask ) { ++ case BTMASK(BTLessStrategyNumber): ++ case BTMASK(BTLessEqualStrategyNumber): ++ case BTMASK(BTEqualStrategyNumber): ++ case BTMASK(BTGreaterEqualStrategyNumber): ++ case BTMASK(BTGreaterStrategyNumber): ++ /* trivial case */ ++ break; ++ case BTMASK(BTLessStrategyNumber) | BTMASK(BTLessEqualStrategyNumber): ++ case BTMASK(BTLessStrategyNumber) | BTMASK(BTLessEqualStrategyNumber) | BTMASK(BTEqualStrategyNumber): ++ case BTMASK(BTLessStrategyNumber) | BTMASK(BTEqualStrategyNumber): ++ case BTMASK(BTLessEqualStrategyNumber) | BTMASK(BTEqualStrategyNumber): ++ /* any subset of <, <=, = can be unioned with <= */ ++ key->strategy = BTLessEqualStrategyNumber; ++ break; ++ case BTMASK(BTGreaterEqualStrategyNumber) | BTMASK(BTGreaterStrategyNumber): ++ case BTMASK(BTEqualStrategyNumber) | BTMASK(BTGreaterEqualStrategyNumber) | BTMASK(BTGreaterStrategyNumber): ++ case BTMASK(BTEqualStrategyNumber) | BTMASK(BTGreaterStrategyNumber): ++ case BTMASK(BTEqualStrategyNumber) | BTMASK(BTGreaterEqualStrategyNumber): ++ /* any subset of >, >=, = can be unioned with >= */ ++ key->strategy = BTGreaterEqualStrategyNumber; ++ break; ++ default: ++ /* ++ * Can't make common restrict qual ++ */ ++ return NULL; ++ } ++ ++ get_op_opfamily_properties(key->normalizedexpr->opno, key->opfamily, false, ++ &strategy, &lefttype, &righttype); ++ ++ if ( strategy != key->strategy ) { ++ /* ++ * We should check because it's possible to have "strange" ++ * opfamilies - without some strategies... ++ */ ++ key->normalizedexpr->opno = get_opfamily_member(key->opfamily, lefttype, righttype, key->strategy); ++ ++ if ( key->normalizedexpr->opno == InvalidOid ) ++ return NULL; ++ ++ key->normalizedexpr->opfuncid = get_opcode( key->normalizedexpr->opno ); ++ Assert ( key->normalizedexpr->opfuncid != InvalidOid ); ++ } ++ ++ rinfo = make_simple_restrictinfo((Expr*)key->normalizedexpr); ++ ++ return rinfo; ++} ++ ++/* ++ * Remove unneeded RestrioctionInfo nodes as it ++ * needed by predicate_*_by() ++ */ ++static void ++make_predicate(List *indexquals, List *indexqualcols, List **preds, List **predcols) { ++ ListCell *i, *c; ++ ++ *preds = NIL; ++ *predcols = NIL; ++ ++ forboth(i, indexquals, c, indexqualcols) ++ { ++ RestrictInfo *rinfo = lfirst(i); ++ OpExpr *expr = (OpExpr*)rinfo->clause; ++ ++ if ( rinfo->outerjoin_delayed ) ++ continue; ++ ++ if ( !IsA(expr, OpExpr) ) ++ continue; ++ ++ if ( list_length( expr->args ) != 2 ) ++ continue; ++ ++ *preds = lappend(*preds, rinfo); ++ *predcols = lappend(*predcols, lfirst(c)); ++ } ++} ++ ++#define CELL_GET_QUALS(x) ( ((IndexPath*)lfirst(x))->indexquals ) ++#define CELL_GET_CLAUSES(x) ( ((IndexPath*)lfirst(x))->indexclauses ) ++ ++static List* ++listRInfo2OpExpr(List *listRInfo) { ++ ListCell *i; ++ List *listOpExpr=NULL; ++ ++ foreach(i, listRInfo) ++ { ++ RestrictInfo *rinfo = lfirst(i); ++ OpExpr *expr = (OpExpr*)rinfo->clause; ++ ++ listOpExpr = lappend(listOpExpr, expr); ++ } ++ ++ return listOpExpr; ++} ++ ++/* ++ * returns list of all nested quals ++ */ ++static List* ++contained_quals(List *nested, List* quals, ListCell *check) { ++ ListCell *i; ++ List *checkpred; ++ ++ if ( list_member_ptr( nested, lfirst(check) ) ) ++ return nested; ++ ++ if (equal(CELL_GET_QUALS(check), CELL_GET_CLAUSES(check)) == false) ++ return nested; ++ ++ checkpred = listRInfo2OpExpr(CELL_GET_QUALS(check)); ++ ++ if ( contain_mutable_functions((Node*)checkpred) ) ++ return nested; ++ ++ foreach(i, quals ) ++ { ++ if ( check == i ) ++ continue; ++ ++ if ( list_member_ptr( nested, lfirst(i) ) ) ++ continue; ++ ++ if ( equal(CELL_GET_QUALS(i), CELL_GET_CLAUSES(i)) && ++ predicate_implied_by( checkpred, CELL_GET_QUALS(i) ) ) ++ nested = lappend( nested, lfirst(i) ); ++ } ++ return nested; ++} ++ ++/* ++ * Checks that one row can be in several quals. ++ * It's guaranteed by predicate_refuted_by() ++ */ ++static bool ++is_intersect(ListCell *check) { ++ ListCell *i; ++ List *checkpred=NULL; ++ ++ checkpred=listRInfo2OpExpr(CELL_GET_QUALS(check)); ++ Assert( checkpred != NULL ); ++ ++ for_each_cell(i, check) { ++ if ( i==check ) ++ continue; ++ ++ if ( predicate_refuted_by( checkpred, CELL_GET_QUALS(i) ) == false ) ++ return true; ++ } ++ ++ return false; ++} ++ ++/* ++ * Removes nested quals and gurantees that quals are not intersected, ++ * ie one row can't satisfy to several quals. It's open a possibility of ++ * Append node using instead of BitmapOr ++ */ ++static BitmapOrPath* ++cleanup_nested_quals( PlannerInfo *root, RelOptInfo *rel, BitmapOrPath *path ) { ++ ListCell *i; ++ IndexOptInfo *index=NULL; ++ List *nested = NULL; ++ ++ /* ++ * check all path to use only one index ++ */ ++ foreach(i, path->bitmapquals ) ++ { ++ ++ if ( IsA(lfirst(i), IndexPath) ) { ++ List *preds, *predcols; ++ IndexPath *subpath = (IndexPath *) lfirst(i); ++ ++ if ( subpath->indexinfo->relam != BTREE_AM_OID ) ++ return NULL; ++ ++ if ( index == NULL ) ++ index = subpath->indexinfo; ++ else if ( index->indexoid != subpath->indexinfo->indexoid ) ++ return NULL; ++ ++ /* ++ * work only with optimizable quals ++ */ ++ Assert(list_length(subpath->indexquals) == list_length(subpath->indexqualcols)); ++ make_predicate(subpath->indexquals, subpath->indexqualcols, &preds, &predcols); ++ if (preds == NIL) ++ return NULL; ++ subpath->indexquals = preds; ++ subpath->indexqualcols = predcols; ++ Assert(list_length(subpath->indexquals) == list_length(subpath->indexqualcols)); ++ } else ++ return NULL; ++ } ++ ++ /* ++ * eliminate nested quals ++ */ ++ foreach(i, path->bitmapquals ) { ++ nested = contained_quals(nested, path->bitmapquals, i); ++ } ++ ++ if ( nested != NIL ) { ++ path->bitmapquals = list_difference_ptr( path->bitmapquals, nested ); ++ ++ Assert( list_length( path->bitmapquals )>0 ); ++ ++ /* ++ * All quals becomes only one after eliminating nested quals ++ */ ++ if (list_length( path->bitmapquals ) == 1) ++ return (BitmapOrPath*)linitial(path->bitmapquals); ++ } ++ ++ /* ++ * Checks for intersection ++ */ ++ foreach(i, path->bitmapquals ) { ++ if ( is_intersect( i ) ) ++ return NULL; ++ } ++ ++ return path; ++} ++ ++/* ++ * Checks if whole result of one simple operation is contained ++ * in another ++ */ ++static int ++simpleCmpExpr( ExExpr *a, ExExpr *b ) { ++ if ( predicate_implied_by((List*)a->expr, (List*)b->expr) ) ++ /* ++ * a:( Var < 15 ) > b:( Var <= 10 ) ++ */ ++ return 1; ++ else if ( predicate_implied_by((List*)b->expr, (List*)a->expr) ) ++ /* ++ * a:( Var <= 10 ) < b:( Var < 15 ) ++ */ ++ return -1; ++ else ++ return 0; ++} ++ ++/* ++ * Trys to define where is equation - on left or right side ++ * a(< 10) b(=11) - on right ++ * a(> 10) b(=9) - on left ++ * a(= 10) b(=11) - on right ++ * a(= 10) b(=9) - on left ++ * Any other - result is 0; ++ */ ++static int ++cmpEqExpr( ExExpr *a, ExExpr *b ) { ++ Oid oldop = b->expr->opno; ++ int res=0; ++ ++ b->expr->opno = get_opfamily_member(b->opfamily, b->lefttype, b->righttype, BTLessStrategyNumber); ++ if ( b->expr->opno != InvalidOid ) { ++ b->expr->opfuncid = get_opcode( b->expr->opno ); ++ res = simpleCmpExpr(a,b); ++ } ++ ++ if ( res == 0 ) { ++ b->expr->opno = get_opfamily_member(b->opfamily, b->lefttype, b->righttype, BTGreaterStrategyNumber); ++ if ( b->expr->opno != InvalidOid ) { ++ b->expr->opfuncid = get_opcode( b->expr->opno ); ++ res = -simpleCmpExpr(a,b); ++ } ++ } ++ ++ b->expr->opno = oldop; ++ b->expr->opfuncid = get_opcode( b->expr->opno ); ++ ++ return res; ++} ++ ++/* ++ * Is result of a contained in result of b or on the contrary? ++ */ ++static int ++cmpNegCmp( ExExpr *a, ExExpr *b ) { ++ Oid oldop = b->expr->opno; ++ int res = 0; ++ ++ b->expr->opno = get_negator( b->expr->opno ); ++ if ( b->expr->opno != InvalidOid ) { ++ b->expr->opfuncid = get_opcode( b->expr->opno ); ++ res = simpleCmpExpr(a,b); ++ } ++ ++ b->expr->opno = oldop; ++ b->expr->opfuncid = get_opcode( b->expr->opno ); ++ ++ return ( IS_LESS(a->strategy) ) ? res : -res; ++} ++ ++/* ++ * Returns 1 if whole result of a is on left comparing with result of b ++ * Returns -1 if whole result of a is on right comparing with result of b ++ * Return 0 if it's impossible to define or results is overlapped ++ * Expressions should use the same attribute of index and should be ++ * a simple: just one operation with index. ++ */ ++static int ++cmpExpr( ExExpr *a, ExExpr *b ) { ++ int res; ++ ++ /* ++ * If a and b are overlapped, we can't decide which one is ++ * lefter or righter ++ */ ++ if ( IS_ONE_DIRECTION(a->strategy, b->strategy) || predicate_refuted_by((List*)a->expr, (List*)b->expr) == false ) ++ return 0; ++ ++ /* ++ * In this place it's impossible to have a row which satisfies ++ * a and b expressions, so we will try to find relatiove position of that results ++ */ ++ if ( b->strategy == BTEqualStrategyNumber ) { ++ return -cmpEqExpr(a, b); /* Covers cases with any operations in a */ ++ } else if ( a->strategy == BTEqualStrategyNumber ) { ++ return cmpEqExpr(b, a); ++ } else if ( (res = cmpNegCmp(a, b)) == 0 ) { /* so, a(<10) b(>20) */ ++ res = -cmpNegCmp(b, a); ++ } ++ ++ return res; ++} ++ ++/* ++ * Try to define positions of result which satisfy indexquals a and b per ++ * one index's attribute. ++ */ ++static int ++cmpColumnQuals( List *a, List *b, int attno ) { ++ int res = 0; ++ ListCell *ai, *bi; ++ ++ foreach(ai, a) { ++ ExExpr *ae = (ExExpr*)lfirst(ai); ++ ++ if ( attno != ae->attno ) ++ continue; ++ ++ foreach(bi, b) { ++ ExExpr *be = (ExExpr*)lfirst(bi); ++ ++ if ( attno != be->attno ) ++ continue; ++ ++ if ((res=cmpExpr(ae, be))!=0) ++ return res; ++ } ++ } ++ ++ return 0; ++} ++ ++static IndexOptInfo *sortingIndex = NULL; ++static bool volatile unableToDefine = false; ++ ++/* ++ * Compare result of two indexquals. ++ * Warinig: it use PG_RE_THROW(), so any call should be wrapped with ++ * PG_TRY(). Try/catch construction is used here for minimize unneeded ++ * actions when sorting is impossible ++ */ ++static int ++cmpIndexPathEx(const void *a, const void *b) { ++ IndexPathEx *aipe = (IndexPathEx*)a; ++ IndexPathEx *bipe = (IndexPathEx*)b; ++ int attno, res = 0; ++ ++ for(attno=1; res==0 && attno<=sortingIndex->ncolumns; attno++) ++ res=cmpColumnQuals(aipe->preparedquals, bipe->preparedquals, attno); ++ ++ if ( res==0 ) { ++ unableToDefine = true; ++ PG_RE_THROW(); /* it should be PG_THROW(), but it's the same */ ++ } ++ ++ return res; ++} ++ ++/* ++ * Initialize lists of operation in useful form ++ */ ++static List* ++prepareQuals(IndexOptInfo *index, List *indexquals, List *indexqualcols) { ++ ListCell *i, *c; ++ List *res=NULL; ++ ExExpr *ex; ++ ++ Assert(list_length(indexquals) == list_length(indexqualcols)); ++ forboth(i, indexquals, c, indexqualcols) ++ { ++ RestrictInfo *rinfo = lfirst(i); ++ OpExpr *expr = (OpExpr*)rinfo->clause; ++ ++ if ( rinfo->outerjoin_delayed ) ++ return NULL; ++ ++ if ( !IsA(expr, OpExpr) ) ++ return NULL; ++ ++ if ( list_length( expr->args ) != 2 ) ++ return NULL; ++ ++ if ( contain_mutable_functions((Node*)expr) ) ++ return NULL; ++ ++ ex = (ExExpr*)palloc(sizeof(ExExpr)); ++ ex->expr = (OpExpr*)copyObject( expr ); ++ if (!bms_equal(rinfo->left_relids, index->rel->relids)) ++ CommuteOpExpr(ex->expr); ++ linitial(ex->expr->args) = fix_indexqual_operand(linitial(ex->expr->args), index, lfirst_int(c)); ++ ex->attno = ((Var*)linitial(ex->expr->args))->varattno; ++ ex->opfamily = index->opfamily[ ex->attno - 1 ]; ++ get_op_opfamily_properties( ex->expr->opno, ex->opfamily, false, ++ &ex->strategy, &ex->lefttype, &ex->righttype); ++ ++ ++ res = lappend(res, ex); ++ } ++ ++ return res; ++} ++ ++/* ++ * sortIndexScans - sorts index scans to get sorted results. ++ * Function supposed that index is the same for all ++ * index scans ++ */ ++static List* ++sortIndexScans( List* ipaths ) { ++ ListCell *i; ++ int j=0; ++ IndexPathEx *ipe = (IndexPathEx*)palloc( sizeof(IndexPathEx)*list_length(ipaths) ); ++ List *orderedPaths = NIL; ++ IndexOptInfo *index = ((IndexPath*)linitial(ipaths))->indexinfo; ++ ++ foreach(i, ipaths) { ++ ipe[j].path = (IndexPath*)lfirst(i); ++ ipe[j].preparedquals = prepareQuals( index, ipe[j].path->indexquals, ipe[j].path->indexqualcols ); ++ ++ if (ipe[j].preparedquals == NULL) ++ return NULL; ++ j++; ++ } ++ ++ sortingIndex = index; ++ unableToDefine = false; ++ PG_TRY(); { ++ qsort(ipe, list_length(ipaths), sizeof(IndexPathEx), cmpIndexPathEx); ++ } PG_CATCH(); { ++ if ( unableToDefine == false ) ++ PG_RE_THROW(); /* not our problem */ ++ } PG_END_TRY(); ++ ++ if ( unableToDefine == true ) ++ return NULL; ++ ++ for(j=0;jindexscandir = BackwardScanDirection; ++ ++ return n; ++} ++ ++static List* ++reverseScanDirIdxPaths(List *indexPaths) { ++ List *idxpath = NIL; ++ ListCell *i; ++ ++ foreach(i, indexPaths) { ++ idxpath = lcons(reverseScanDirIdxPath( (IndexPath*)lfirst(i) ), idxpath); ++ } ++ ++ return idxpath; ++} +diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c +index 6724996..8bb74cc 100644 +--- a/src/backend/optimizer/path/pathkeys.c ++++ b/src/backend/optimizer/path/pathkeys.c +@@ -1379,7 +1379,7 @@ right_merge_direction(PlannerInfo *root, PathKey *pathkey) + * no good to order by just the first key(s) of the requested ordering. + * So the result is always either 0 or list_length(root->query_pathkeys). + */ +-static int ++int + pathkeys_useful_for_ordering(PlannerInfo *root, List *pathkeys) + { + if (root->query_pathkeys == NIL) +diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c +index c501737..3745e10 100644 +--- a/src/backend/optimizer/plan/createplan.c ++++ b/src/backend/optimizer/plan/createplan.c +@@ -89,7 +89,6 @@ static void process_subquery_nestloop_params(PlannerInfo *root, + List *subplan_params); + static List *fix_indexqual_references(PlannerInfo *root, IndexPath *index_path); + static List *fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path); +-static Node *fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol); + static List *get_switched_clauses(List *clauses, Relids outerrelids); + static List *order_qual_clauses(PlannerInfo *root, List *clauses); + static void copy_path_costsize(Plan *dest, Path *src); +@@ -679,7 +678,7 @@ static Plan * + create_append_plan(PlannerInfo *root, AppendPath *best_path) + { + Append *plan; +- List *tlist = build_path_tlist(root, &best_path->path); ++ List *tlist; + List *subplans = NIL; + ListCell *subpaths; + +@@ -695,6 +694,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path) + if (best_path->subpaths == NIL) + { + /* Generate a Result plan with constant-FALSE gating qual */ ++ tlist = build_path_tlist(root, &best_path->path); + return (Plan *) make_result(root, + tlist, + (Node *) list_make1(makeBoolConst(false, +@@ -717,6 +717,11 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path) + * parent-rel Vars it'll be asked to emit. + */ + ++ if ( best_path->pull_tlist ) ++ tlist = copyObject( ((Plan*)linitial(subplans))->targetlist ); ++ else ++ tlist = build_path_tlist(root, &best_path->path); ++ + plan = make_append(subplans, tlist); + + return (Plan *) plan; +@@ -2957,7 +2962,7 @@ fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path) + * Most of the code here is just for sanity cross-checking that the given + * expression actually matches the index column it's claimed to. + */ +-static Node * ++Node * + fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol) + { + Var *result; +diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c +index b78d727..3a6b826 100644 +--- a/src/backend/optimizer/plan/setrefs.c ++++ b/src/backend/optimizer/plan/setrefs.c +@@ -1692,6 +1692,10 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context) + { + Var *var = (Var *) node; + ++ /* join_references_mutator already checks this node */ ++ if ( var->varno == OUTER_VAR ) ++ return (Node*)copyObject(var); ++ + /* First look for the var in the input tlists */ + newvar = search_indexed_tlist_for_var(var, + context->outer_itlist, +@@ -1701,6 +1705,9 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context) + return (Node *) newvar; + if (context->inner_itlist) + { ++ if ( var->varno == INNER_VAR ) ++ return (Node*)copyObject(var); ++ + newvar = search_indexed_tlist_for_var(var, + context->inner_itlist, + INNER_VAR, +diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c +index 64b17051..8ed8f50 100644 +--- a/src/backend/optimizer/util/pathnode.c ++++ b/src/backend/optimizer/util/pathnode.c +@@ -888,7 +888,7 @@ create_tidscan_path(PlannerInfo *root, RelOptInfo *rel, List *tidquals, + * Note that we must handle subpaths = NIL, representing a dummy access path. + */ + AppendPath * +-create_append_path(RelOptInfo *rel, List *subpaths, Relids required_outer) ++create_append_path(RelOptInfo *rel, List *subpaths, Relids required_outer, bool pull_tlist, List *pathkeys) + { + AppendPath *pathnode = makeNode(AppendPath); + ListCell *l; +@@ -897,9 +897,10 @@ create_append_path(RelOptInfo *rel, List *subpaths, Relids required_outer) + pathnode->path.parent = rel; + pathnode->path.param_info = get_appendrel_parampathinfo(rel, + required_outer); +- pathnode->path.pathkeys = NIL; /* result is always considered +- * unsorted */ ++ pathnode->path.pathkeys = pathkeys; /* !=NIL in case of append OR index scans */ ++ + pathnode->subpaths = subpaths; ++ pathnode->pull_tlist = pull_tlist; + + /* + * We don't bother with inventing a cost_append(), but just do it here. +diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y +index 5094226..048a790 100644 +--- a/src/backend/parser/gram.y ++++ b/src/backend/parser/gram.y +@@ -10549,7 +10549,7 @@ a_expr: c_expr { $$ = $1; } + | a_expr LIKE a_expr ESCAPE a_expr + { + FuncCall *n = makeNode(FuncCall); +- n->funcname = SystemFuncName("like_escape"); ++ n->funcname = list_make1(makeString("like_escape")); + n->args = list_make2($3, $5); + n->agg_order = NIL; + n->agg_star = FALSE; +@@ -10564,7 +10564,7 @@ a_expr: c_expr { $$ = $1; } + | a_expr NOT LIKE a_expr ESCAPE a_expr + { + FuncCall *n = makeNode(FuncCall); +- n->funcname = SystemFuncName("like_escape"); ++ n->funcname = list_make1(makeString("like_escape")); + n->args = list_make2($4, $6); + n->agg_order = NIL; + n->agg_star = FALSE; +@@ -10579,7 +10579,7 @@ a_expr: c_expr { $$ = $1; } + | a_expr ILIKE a_expr ESCAPE a_expr + { + FuncCall *n = makeNode(FuncCall); +- n->funcname = SystemFuncName("like_escape"); ++ n->funcname = list_make1(makeString("like_escape")); + n->args = list_make2($3, $5); + n->agg_order = NIL; + n->agg_star = FALSE; +@@ -10594,7 +10594,7 @@ a_expr: c_expr { $$ = $1; } + | a_expr NOT ILIKE a_expr ESCAPE a_expr + { + FuncCall *n = makeNode(FuncCall); +- n->funcname = SystemFuncName("like_escape"); ++ n->funcname = list_make1(makeString("like_escape")); + n->args = list_make2($4, $6); + n->agg_order = NIL; + n->agg_star = FALSE; +@@ -10608,7 +10608,7 @@ a_expr: c_expr { $$ = $1; } + | a_expr SIMILAR TO a_expr %prec SIMILAR + { + FuncCall *n = makeNode(FuncCall); +- n->funcname = SystemFuncName("similar_escape"); ++ n->funcname = list_make1(makeString("similar_escape")); + n->args = list_make2($4, makeNullAConst(-1)); + n->agg_order = NIL; + n->agg_star = FALSE; +@@ -10621,7 +10621,7 @@ a_expr: c_expr { $$ = $1; } + | a_expr SIMILAR TO a_expr ESCAPE a_expr + { + FuncCall *n = makeNode(FuncCall); +- n->funcname = SystemFuncName("similar_escape"); ++ n->funcname = list_make1(makeString("similar_escape")); + n->args = list_make2($4, $6); + n->agg_order = NIL; + n->agg_star = FALSE; +@@ -10634,7 +10634,7 @@ a_expr: c_expr { $$ = $1; } + | a_expr NOT SIMILAR TO a_expr %prec SIMILAR + { + FuncCall *n = makeNode(FuncCall); +- n->funcname = SystemFuncName("similar_escape"); ++ n->funcname = list_make1(makeString("similar_escape")); + n->args = list_make2($5, makeNullAConst(-1)); + n->agg_order = NIL; + n->agg_star = FALSE; +@@ -10647,7 +10647,7 @@ a_expr: c_expr { $$ = $1; } + | a_expr NOT SIMILAR TO a_expr ESCAPE a_expr + { + FuncCall *n = makeNode(FuncCall); +- n->funcname = SystemFuncName("similar_escape"); ++ n->funcname = list_make1(makeString("similar_escape")); + n->args = list_make2($5, $7); + n->agg_order = NIL; + n->agg_star = FALSE; +diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h +index a2853fb..e67ee63 100644 +--- a/src/include/nodes/relation.h ++++ b/src/include/nodes/relation.h +@@ -879,6 +879,11 @@ typedef struct AppendPath + { + Path path; + List *subpaths; /* list of component Paths */ ++ bool pull_tlist; /* if = true, create_append_plan() ++ should get targetlist from any ++ subpath - they are the same, ++ because the only place - append ++ index scan for range OR */ + } AppendPath; + + #define IS_DUMMY_PATH(p) \ +diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h +index bc68789..5108fa1 100644 +--- a/src/include/optimizer/pathnode.h ++++ b/src/include/optimizer/pathnode.h +@@ -57,7 +57,7 @@ extern BitmapOrPath *create_bitmap_or_path(PlannerInfo *root, + extern TidPath *create_tidscan_path(PlannerInfo *root, RelOptInfo *rel, + List *tidquals, Relids required_outer); + extern AppendPath *create_append_path(RelOptInfo *rel, List *subpaths, +- Relids required_outer); ++ Relids required_outer, bool pull_tlist, List *pathkeys); + extern MergeAppendPath *create_merge_append_path(PlannerInfo *root, + RelOptInfo *rel, + List *subpaths, +diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h +index 9ef93c7..eda1ae1 100644 +--- a/src/include/optimizer/paths.h ++++ b/src/include/optimizer/paths.h +@@ -66,6 +66,7 @@ extern Expr *adjust_rowcompare_for_index(RowCompareExpr *clause, + * additional routines for indexable OR clauses + */ + extern bool create_or_index_quals(PlannerInfo *root, RelOptInfo *rel); ++extern void keybased_rewrite_index_paths(PlannerInfo *root, RelOptInfo *rel); + + /* + * tidpath.h +@@ -188,6 +189,7 @@ extern List *select_outer_pathkeys_for_merge(PlannerInfo *root, + extern List *make_inner_pathkeys_for_merge(PlannerInfo *root, + List *mergeclauses, + List *outer_pathkeys); ++extern int pathkeys_useful_for_ordering(PlannerInfo *root, List *pathkeys); + extern List *truncate_useless_pathkeys(PlannerInfo *root, + RelOptInfo *rel, + List *pathkeys); +diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h +index 33eaf32..0379141 100644 +--- a/src/include/optimizer/planmain.h ++++ b/src/include/optimizer/planmain.h +@@ -88,7 +88,7 @@ extern ModifyTable *make_modifytable(PlannerInfo *root, + List *resultRelations, List *subplans, List *returningLists, + List *rowMarks, int epqParam); + extern bool is_projection_capable_plan(Plan *plan); +- ++extern Node * fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol); + /* + * prototypes for plan/initsplan.c + */ +diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out +index 81c64e5..43f0340 100644 +--- a/src/test/regress/expected/create_index.out ++++ b/src/test/regress/expected/create_index.out +@@ -2625,18 +2625,12 @@ DROP TABLE onek_with_null; + EXPLAIN (COSTS OFF) + SELECT * FROM tenk1 + WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3 OR tenthous = 42); +- QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------ +- Bitmap Heap Scan on tenk1 +- Recheck Cond: (((thousand = 42) AND (tenthous = 1)) OR ((thousand = 42) AND (tenthous = 3)) OR ((thousand = 42) AND (tenthous = 42))) +- -> BitmapOr +- -> Bitmap Index Scan on tenk1_thous_tenthous +- Index Cond: ((thousand = 42) AND (tenthous = 1)) +- -> Bitmap Index Scan on tenk1_thous_tenthous +- Index Cond: ((thousand = 42) AND (tenthous = 3)) +- -> Bitmap Index Scan on tenk1_thous_tenthous +- Index Cond: ((thousand = 42) AND (tenthous = 42)) +-(9 rows) ++ QUERY PLAN ++----------------------------------------------------------------- ++ Index Scan using tenk1_thous_tenthous on tenk1 ++ Index Cond: ((thousand = 42) AND (thousand = 42)) ++ Filter: ((tenthous = 1) OR (tenthous = 3) OR (tenthous = 42)) ++(3 rows) + + SELECT * FROM tenk1 + WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3 OR tenthous = 42); +diff --git a/src/test/regress/expected/select.out b/src/test/regress/expected/select.out +index c376523..185d177 100644 +--- a/src/test/regress/expected/select.out ++++ b/src/test/regress/expected/select.out +@@ -518,6 +518,124 @@ TABLE int8_tbl; + (9 rows) + + -- ++-- test order by NULLS (FIRST|LAST) ++-- ++select unique1, unique2 into onek_with_null from onek; ++insert into onek_with_null (unique1,unique2) values (NULL, -1), (NULL, NULL); ++select * from onek_with_null order by unique1 nulls first , unique2 limit 3; ++ unique1 | unique2 ++---------+--------- ++ | -1 ++ | ++ 0 | 998 ++(3 rows) ++ ++select * from onek_with_null order by unique1 nulls last , unique2 limit 3; ++ unique1 | unique2 ++---------+--------- ++ 0 | 998 ++ 1 | 214 ++ 2 | 326 ++(3 rows) ++ ++select * from onek_with_null order by unique1 nulls first , unique2 nulls first limit 3; ++ unique1 | unique2 ++---------+--------- ++ | ++ | -1 ++ 0 | 998 ++(3 rows) ++ ++select * from onek_with_null order by unique1 nulls last , unique2 nulls first limit 3; ++ unique1 | unique2 ++---------+--------- ++ 0 | 998 ++ 1 | 214 ++ 2 | 326 ++(3 rows) ++ ++select * from onek_with_null order by unique1 nulls first , unique2 nulls last limit 3; ++ unique1 | unique2 ++---------+--------- ++ | -1 ++ | ++ 0 | 998 ++(3 rows) ++ ++select * from onek_with_null order by unique1 nulls last , unique2 nulls last limit 3; ++ unique1 | unique2 ++---------+--------- ++ 0 | 998 ++ 1 | 214 ++ 2 | 326 ++(3 rows) ++ ++select * from onek_with_null order by unique1 desc nulls first , unique2 desc limit 3; ++ unique1 | unique2 ++---------+--------- ++ | ++ | -1 ++ 999 | 152 ++(3 rows) ++ ++select * from onek_with_null order by unique1 desc nulls last , unique2 desc limit 3; ++ unique1 | unique2 ++---------+--------- ++ 999 | 152 ++ 998 | 549 ++ 997 | 21 ++(3 rows) ++ ++select * from onek_with_null order by unique1 desc nulls first , unique2 desc nulls first limit 3; ++ unique1 | unique2 ++---------+--------- ++ | ++ | -1 ++ 999 | 152 ++(3 rows) ++ ++select * from onek_with_null order by unique1 desc nulls last , unique2 desc nulls first limit 3; ++ unique1 | unique2 ++---------+--------- ++ 999 | 152 ++ 998 | 549 ++ 997 | 21 ++(3 rows) ++ ++select * from onek_with_null order by unique1 desc nulls first , unique2 desc nulls last limit 3; ++ unique1 | unique2 ++---------+--------- ++ | -1 ++ | ++ 999 | 152 ++(3 rows) ++ ++select * from onek_with_null order by unique1 desc nulls last , unique2 desc nulls last limit 3; ++ unique1 | unique2 ++---------+--------- ++ 999 | 152 ++ 998 | 549 ++ 997 | 21 ++(3 rows) ++ ++select unique1 as u1, unique2 as u2 from onek_with_null order by u1 nulls first , u2 nulls first limit 3; ++ u1 | u2 ++----+----- ++ | ++ | -1 ++ 0 | 998 ++(3 rows) ++ ++select unique1 as u1, unique2 as u2 from onek_with_null order by u1 asc nulls first , u2 desc nulls first limit 3; ++ u1 | u2 ++----+----- ++ | ++ | -1 ++ 0 | 998 ++(3 rows) ++ ++drop table onek_with_null; ++-- + -- Test ORDER BY options + -- + CREATE TEMP TABLE foo (f1 int); +diff --git a/src/test/regress/sql/select.sql b/src/test/regress/sql/select.sql +index b99fb13..582e5d6 100644 +--- a/src/test/regress/sql/select.sql ++++ b/src/test/regress/sql/select.sql +@@ -149,6 +149,33 @@ UNION ALL + TABLE int8_tbl; + + -- ++-- test order by NULLS (FIRST|LAST) ++-- ++ ++select unique1, unique2 into onek_with_null from onek; ++insert into onek_with_null (unique1,unique2) values (NULL, -1), (NULL, NULL); ++ ++ ++select * from onek_with_null order by unique1 nulls first , unique2 limit 3; ++select * from onek_with_null order by unique1 nulls last , unique2 limit 3; ++select * from onek_with_null order by unique1 nulls first , unique2 nulls first limit 3; ++select * from onek_with_null order by unique1 nulls last , unique2 nulls first limit 3; ++select * from onek_with_null order by unique1 nulls first , unique2 nulls last limit 3; ++select * from onek_with_null order by unique1 nulls last , unique2 nulls last limit 3; ++ ++select * from onek_with_null order by unique1 desc nulls first , unique2 desc limit 3; ++select * from onek_with_null order by unique1 desc nulls last , unique2 desc limit 3; ++select * from onek_with_null order by unique1 desc nulls first , unique2 desc nulls first limit 3; ++select * from onek_with_null order by unique1 desc nulls last , unique2 desc nulls first limit 3; ++select * from onek_with_null order by unique1 desc nulls first , unique2 desc nulls last limit 3; ++select * from onek_with_null order by unique1 desc nulls last , unique2 desc nulls last limit 3; ++ ++select unique1 as u1, unique2 as u2 from onek_with_null order by u1 nulls first , u2 nulls first limit 3; ++select unique1 as u1, unique2 as u2 from onek_with_null order by u1 asc nulls first , u2 desc nulls first limit 3; ++ ++drop table onek_with_null; ++ ++-- + -- Test ORDER BY options + -- + +diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm +index 7964c01..dc38388 100644 +--- a/src/tools/msvc/Mkvcbuild.pm ++++ b/src/tools/msvc/Mkvcbuild.pm +@@ -44,7 +44,7 @@ my @contrib_uselibpgcommon = ( + 'pg_test_fsync', 'pg_test_timing', + 'pg_upgrade', 'pg_xlogdump', + 'vacuumlo'); +-my $contrib_extralibs = { 'pgbench' => ['wsock32.lib'] }; ++my $contrib_extralibs = { 'pgbench' => ['wsock32.lib'], 'mchar' => ['icuin.lib', 'icuuc.lib'] }; + my $contrib_extraincludes = + { 'tsearch2' => ['contrib/tsearch2'], 'dblink' => ['src/backend'] }; + my $contrib_extrasource = { diff --git a/Makefile.regress b/Makefile.regress new file mode 100644 index 0000000..43c7ba4 --- /dev/null +++ b/Makefile.regress @@ -0,0 +1,67 @@ +# +# Simplified makefile for running the PostgreSQL regression tests +# in an RPM installation +# + +# maximum simultaneous connections for parallel tests +MAXCONNOPT = +ifdef MAX_CONNECTIONS +MAXCONNOPT += --max-connections=$(MAX_CONNECTIONS) +endif + +# locale +NOLOCALE = +ifdef NO_LOCALE +NOLOCALE += --no-locale +endif + +srcdir := . + +REGRESS_OPTS += --dlpath=. + +pg_regress_locale_flags = $(if $(ENCODING),--encoding=$(ENCODING)) $(NOLOCALE) + +pg_regress_installcheck = ./pg_regress --inputdir=$(srcdir) --psqldir='/usr/bin' $(pg_regress_locale_flags) + +# Test input and expected files. These are created by pg_regress itself, so we +# don't have a rule to create them. We do need rules to clean them however. +ifile_list := $(subst .source,, $(notdir $(wildcard $(srcdir)/input/*.source))) +input_files := $(foreach file, $(ifile_list), sql/$(file).sql) +ofile_list := $(subst .source,, $(notdir $(wildcard $(srcdir)/output/*.source))) +output_files := $(foreach file, $(ofile_list), expected/$(file).out) + +abs_srcdir := $(shell pwd) +abs_builddir := $(shell pwd) + +check: installcheck-parallel + +installcheck: cleandirs + $(pg_regress_installcheck) $(REGRESS_OPTS) --schedule=$(srcdir)/serial_schedule $(EXTRA_TESTS) + +installcheck-parallel: cleandirs + $(pg_regress_installcheck) $(REGRESS_OPTS) --schedule=$(srcdir)/parallel_schedule $(MAXCONNOPT) $(EXTRA_TESTS) + +# The tests command the server to write into testtablespace and results. +# On a SELinux-enabled system this will fail unless we mark those directories +# as writable by the server. +cleandirs: + -rm -rf testtablespace results + mkdir testtablespace results + [ -x /usr/bin/chcon ] && /usr/bin/chcon -u system_u -r object_r -t postgresql_db_t testtablespace results + +# old interfaces follow... + +runcheck: check +runtest: installcheck +runtest-parallel: installcheck-parallel + + +## +## Clean up +## + +clean distclean maintainer-clean: + rm -f $(output_files) $(input_files) + rm -rf testtablespace + rm -rf results tmp_check log + rm -f regression.diffs regression.out regress.out run_check.out diff --git a/README.rpm-dist b/README.rpm-dist new file mode 100644 index 0000000..64e7444 --- /dev/null +++ b/README.rpm-dist @@ -0,0 +1,351 @@ +README.rpm-dist +----------------------------------------------------------------------------- +Version 9.3, for the PostgreSQL 9.3 RPM set. +Devrim Gündüz +----------------------------------------------------------------------------- + +Contents: + 1.) Introduction and QuickStart + 2.) Upgrading an installation + 3.) PostgreSQL RPM packages and rationale + 4.) Starting multiple postmasters + 5.) Regression Testing + 6.) Starting postmaster automatically on startup + 7.) Grand Unified Configuration(GUC) File + 8.) Logging set up + 9.) Rebuilding from the source RPM +10.) Contrib files +11.) Further Information Resource + +INTRODUCTION +----------------------------------------------------------------------------- +This document exists to explain the layout of the RPMs for PostgreSQL, to +describe various RPM specifics, and to document special features found +in the RPMset. + +This document is written to be applicable to version 9.3 of PostgreSQL, +which is the current version of the RPMs as of this writing. More to the +point, versions prior to 9.3 are not documented here. + +Official PostgreSQL Global Development Group RPMs carry a 'PGDG after the +release number. Other RPMsets as distributed with Linux distributions may +have a different release number and initials. + +If you want to stay up-to-date on the PostgreSQL core itself, you may +want to use PGDG set, instead of the binaries supplied by distribution. + +These RPMs do not support any sort of major version upgrading process +other than that documented in the regular documentation. That is, you +must dump, upgrade,initdb, and restore your data if you are +performing a major version update. This is not needed for minor version +updates. + +QUICKSTART (note that this requires postgresql93-server installed) +----------------------------------------------------------------------------- +For a fresh installation, you will need to initialize the cluster first. Run: + + /usr/pgsql-9.3/bin/postgresql93-setup initdb + +as root, and it will prepare a new database cluster for you. Then you will +need to start PostgreSQL. Again as root, run: + + systemctl start postgresql-9.3.service + +This command will start a postmaster that willl listen on localhost and Unix +socket 5432 only. Edit /var/lib/pgsql/9.3/data/postgresql.conf and pg_hba.conf +if you want to allow remote access -- see the section on Grand Unified +Configuration. + +You will probably also want to do + + systemctl enable postgresql-9.3.service + +so that the postmaster is automatically started during future reboots. + +The file /var/lib/pgsql/9.3/.bash_profile is packaged to help with the +setting of environment variables. You may edit this file, and it won't be +overwritten during an upgrade. However, enhancements and bugfixes may +be added to this file, so be sure to check .bash_profile.rpmnew after +upgrading. + +The user 'postgres' is created during installation of the server subpackage. +This user by default is UID and GID 26. The user has the default shell set to +bash, and the home directory set to /var/lib/pgsql. This user also has no +default password. If you want to be able to su to it from a non-root account +or login as 'postgres' you will need to set a password using passwd. + +In the default installation, only postgres user is allowed to access to +the database server, because initdb in the RPMs pick up "peer" auth +method in the RPMs. + +All the binaries are installed under /usr/pgsql-9.3/bin. However, the +tools that are compatible with the previous releases are symlinked under +/usr/bin directory. Please note that RPMs are using alternatives method +in here, so whenever a newer major version is installed, symlinks will +point to the new version. Here is the current list of the binaries that +are under $PATH: + +- clusterdb +- createdb +- createlang +- createuser +- dropdb +- droplang +- dropuser +- pg_dump +- pg_dumpall +- pg_restore +- psql +- reindexdb + +You may want to add /usr/pgsql-9.3/bin to your $PATH variable in +/etc/profile, if you want to use them easily. However, please note that +this may lead to some unintentional side effects, so be careful. + + +UPGRADING AN INSTALLATION +----------------------------------------------------------------------------- +For a minor-version upgrade (such as 9.3.1 to 9.3.2), just install the +new RPMs; there's usually nothing more to it than that. Upgrading +across a major release of PostgreSQL (for example, from 9.2.x to 9.3.x) +requires more effort. + +If you are upgrading across more than one major release of PostgreSQL +(for example, from 9.1.x to 9.3.x), you will need to follow the "traditional" +dump and reload process to bring your data into the new version. That is: +*before* upgrading, run pg_dumpall to extract all your data into a SQL file. +Shut down the old postmaster, upgrade to the new version RPMs, initdb, +and run the dump file through psql to restore your data. + +In 9.1+, the RPMs also support in-place upgrade from th immediately +previous major release. Currently, you can upgrade in-place +from 9.2.x to 9.3.x. Just run: +$ /usr/pgsql-9.3/bin/postgresql93-setup upgrade + +Please note that 9.2 and 9.3 contrib RPMs needs to be installed for this +feature to work. + +POSTGRESQL RPM PACKAGES AND RATIONALE. +----------------------------------------------------------------------------- +PostgreSQL is split up into multiple packages so that users can 'pick and +choose' what pieces are needed, and what dependencies are required. + +The RPMset is packaged in the following subpackages: + +postgresql93: Key clients and libraries, and documentation +postgresql93-server: Server executables and data files +postgresql93-devel: Development libraries and include files +postgresql93-test: The regression tests and associated files +postgresql93-libs: Client shared libraries +postgresql93-docs: Extra documentation, such as the tutorial files +postgresql93-contrib: The contrib source tree, as well as selected binaries +postgresql93-plperl: PL/Perl procedural language +postgresql93-plpython: PL/Python procedural language +postgresql93-pltcl: PL/Tcl procedural language + +postgresql93-jdbc, postgresql93-python, postgresql93-tcl and postgresql93-odbc have +been splitted into seperate (s)rpms. + +You have to install postgresql and postgresql93-libs to do anything. +postgresql93-server is needed unless you only plan to use the clients to work +with a remote PostgreSQL server. The others are optional. + +postgresql93-python package includes PyGreSQL, and Pgtcl is distributed +via postgresql93-tcl package. + +RPM FILE LOCATIONS. +----------------------------------------------------------------------------- +To be in compliance with the Linux FHS, the PostgreSQL RPMs install files in +a manner not consistent with most of the PostgreSQL documentation. According +to the standard PostgreSQL documentation, PostgreSQL is installed under the +directory /usr/local/pgsql, with executables, source, and data existing in +various subdirectories. + +Different distributions have different ideas of some of these file locations. +In particular, the documentation directory can be /usr/doc, /usr/doc/packages, +/usr/share/doc, /usr/share/doc/packages, or some other similar path. + +However, the Red Hat / Scientific Linux ( CentOS / Fedora RPM's install +the files like this: + +Executables: /usr/bin and /usr/pgsql-9.3/bin +Libraries: /usr/pgsql-9.3/lib +Documentation: /usr/pgsql-9.3/doc +Contrib documentation: /usr/pgsql-9.3/doc +Source: not installed +Data: /var/lib/pgsql/9.3/data +Backup area: /var/lib/pgsql/9.3/backups +Templates: /usr/pgsql-9.3/share +Procedural Languages: /usr/pgsql-9.3/lib +Development Headers: /usr/pgsql-9.3/include +Other shared data: /usr/pgsql-9.3/share +Regression tests: /usr/pgsql-9.3/lib/test + +While it may seem gratuitous to place these files in different locations, the +FHS requires it -- distributions should not ever touch /usr/local. It may +also seem like more work to keep track of where everything is -- but, that's +the beauty of RPM -- you don't have to keep track of the files, RPM does it +for you. + +These RPMs are designed to be LSB-compliant -- if you find this not to be the +case, please let us know by way of the pgsql-pkg-yum@postgresql.org +mailing list. + +------------------------------------------------------------------------------- +MULTIPLE POSTMASTERS (For the same PostgreSQL version) +------------------------------------------------------------------------------- +The postgresql-server RPM contains an 'initscript' that is used to start the +postmaster. The current version of this script has logic to be able to start +multiple postmasters, with different data areas, listening on different ports, +etc. To use this functionality requires root access. + +As an example, let us create a secondary postmaster called, creatively enough, +'secondary'. Here are the steps: +1.) create a hard link in /etc/rc.d/init.d (or equivalent location) + to postgresql-9.3 named 'secondary-9.3' : ln postgresql secondary Pick + a name not already used in /etc/rc.d/init.d! +2.) create a file in /etc/sysconfig/pgsql named secondary. This file is + a shell script -- typically you would define PGDATA, PGPORT, and PGOPTS + here. Since $PGDATA/postgresql.conf will override many of these + settings, except PGDATA, you might be surprised on startup. +3.) create the target PGDATA. +4.) Initdb the targe PGDATA as documented in the main documentation. + Automatic initdb may or may not work for you, so a manual one is + preferred. This must be done as user 'postgres' +5.) Edit postgresql.conf to change the port, address, tcpip settings, etc. +6.) Start the postmaster with 'service secondary-9.3 start'. + +Note that there may be problems with the standard symlink -- consider this +support experimental at this point in time. + +When doing a major-version upgrade of a secondary postmaster, mention the +service name in the postgresql-setup command, for example 'postgresql-setup +upgrade secondary'. This will let postgresql-setup find the correct data +directory from the service file. + +REGRESSION TESTING +------------------------------------------------------------------------------- +If you install the postgresql-test RPM then you can run the PostgreSQL +regression tests. These tests stress your database installation and produce +results that give you assurances that the installation is complete, and that +your database machine is up to the task. + +To run the regression tests under the RPM installation, make sure that the +postmaster has been started (if not, su to root and do "systemctl start +postgresql-9.3.service"), cd to /usr/pgsql-9.3/lib/test/regress, su to +postgres, and execute "make check". This command will start the +regression tests and will both show the results to the screen and store +the results in the file regress.out. + +If any tests fail, see the file regression.diffs in that directory for details, +and read the "Regression Tests" section of the PostgreSQL documentation to +find out whether the differences are actually significant. If you need help +interpreting the results, contact the pgsql-general list at +postgresql.org. + +After testing, say "make clean" to remove the files generated by the test +script. + +STARTING POSTMASTER AUTOMATICALLY AT SYSTEM STARTUP +------------------------------------------------------------------------------- +Fedora / Red Hat / CentOS use the systemd package to manage server startup. +A systemd unit file for PostgreSQL is provided in the server package, as +/lib/systemd/system/postgresql-9.3.service. To start the postmaster manually, +as root run + systemctl start postgresql-9.3.service +To shut the postmaster down, + systemctl stop postgresql-9.3.service +These two commands only change the postmaster's current status. If you +want the postmaster to be started automatically during future system startups, +run + systemctl enable postgresql-9.3.service +To undo that again, + systemctl disable postgresql-9.3.service +See "man systemctl" for other possible subcommands. + +GRAND UNIFIED CONFIGURATION (GUC) FILE +------------------------------------------------------------------------------- +The PostgreSQL server has many tunable parameters -- the file +/var/lib/pgsql/9.3/data/postgresql.conf is the master configuration file for the +whole system. + +The RPM ships with a mostly-default file -- you will need to tune the +parameters for your installation. In particular, you might want to allow +nonlocal TCP/IP socket connections -- in order to allow these, you will need +to edit the postgresql.conf file. The line in question contains the string +'listen_addresses' -- you need to both uncomment the line and set the value +to '*' to get the postmaster to accept nonlocal connections. You'll also need +to adjust pg_hba.conf appropriately. + +LOGGING SET UP +------------------------------------------------------------------------------- +By default, the postmaster's stderr log is directed into files placed in a +pg_log subdirectory of the data directory (ie, /var/lib/pgsql/9.3/data/pg_log). +The out-of-the-box configuration rotates among seven files, one for each +day of the week. You can adjust this by changing postgresql.conf settings. + +REBUILDING FROM SOURCE RPM +------------------------------------------------------------------------------- +If your distribution is not supported by the binary RPMs from PostgreSQL.org, +you will need to rebuild from the source RPM. Download the .src.rpm for this +release. You will need to be root to rebuild, unless you have set up +a non-root build environment (which is the recommended method anyway). + +Install the source RPM with rpm -i, then cd to the rpm building area +(which is /usr/src/redhat by default). You will have to have a full +development environment to rebuild the full RPM set. + +This release of the RPMset includes the ability to conditionally build +sets of packages. The parameters, their defaults, and the meanings are: + +beta 0 #build with cassert and do not strip the binaries +python 1 #build the postgresql-python package. +tcl 1 #build the postgresql-tcl package. +test 1 #build the postgresql-test package. +plpython 1 #build the PL/Python procedural language package. +pltcl 1 #build the PL/Tcl procedural language package. +plperl 1 #build the PL/Perl procedural language package. +ssl 1 #use OpenSSL support. +kerberos 1 #use Kerberos 5 support. +nls 1 #build with national language support. +ldap 1 #build with LDAP support. +pam 1 #build with PAM support. +runselftest 1 #do "make check" during the build. +sdt 1 #build with SystemTap support. +xml 1 #build with XML support +pgfts 1 #build with --enable-thread-safety +uuid 1 #build contrib/uuid-ossp + +To use these defines, invoke a rebuild like this: +rpmbuild --rebuild --define 'python 0' --define 'tcl 0' \ + --define 'test 0' --define 'runselftest 0' --define 'kerberos 0' \ + postgresql93-9.3.1-1PGDG.f17.src.rpm +This line would disable the python, tcl, and test subpackages, disable +the regression test run during build, and disable kerberos support. + +You might need to disable runselftest if there is an installed version of +PostgreSQL that is a different major version from what you are trying to +build. The self test tends to pick up the installed libpq.so shared library +in place of the one being built :-(, so if that isn't compatible the test will +fail. Also, you can't use runselftest when doing the build as root. + +More of these conditionals will be added in the future. + +CONTRIB FILES +------------------------------------------------------------------------------- +The contents of the contrib tree are packaged into the -contrib subpackage +and are processed with make and make install. Most of the modules are in +/usr/pgsql-9.3/lib for loadable modules, and binaries are in +/usr/pgsql-9.3/bin. In the future these files may be split out, +depending upon function and dependencies. + + +MORE INFORMATION +------------------------------------------------------------------------------- +You can get more information at http://www.postgresql.org and +http://yum.postgresql.org + +Please help make this packaging better -- let us know if you find problems, or +better ways of doing things. You can reach us by e-mail at +pgsql-pkg-yum@postgresql.org +------------------------------------------------------------------------------- diff --git a/applock-1c-9.3.patch b/applock-1c-9.3.patch new file mode 100644 index 0000000..1aa1780 --- /dev/null +++ b/applock-1c-9.3.patch @@ -0,0 +1,107 @@ +--- ../postgresql-9.2.0.orig/src/backend/parser/gram.y 2011-09-09 01:13:28.000000000 +0400 ++++ ./src/backend/parser/gram.y 2011-09-14 21:38:48.694882400 +0400 +@@ -488,7 +488,7 @@ + + /* ordinary key words in alphabetical order */ + %token ABORT_P ABSOLUTE_P ACCESS ACTION ADD_P ADMIN AFTER +- AGGREGATE ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY ARRAY AS ASC ++ AGGREGATE ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY APPLICATION ARRAY AS ASC + ASSERTION ASSIGNMENT ASYMMETRIC AT ATTRIBUTE AUTHORIZATION + + BACKWARD BEFORE BEGIN_P BETWEEN BIGINT BINARY BIT +@@ -8139,6 +8139,8 @@ + lock_type: ACCESS SHARE { $$ = AccessShareLock; } + | ROW SHARE { $$ = RowShareLock; } + | ROW EXCLUSIVE { $$ = RowExclusiveLock; } ++ | APPLICATION SHARE { $$ = ApplicationShareLock; } ++ | APPLICATION EXCLUSIVE { $$ = ApplicationExclusiveLock; } + | SHARE UPDATE EXCLUSIVE { $$ = ShareUpdateExclusiveLock; } + | SHARE { $$ = ShareLock; } + | SHARE ROW EXCLUSIVE { $$ = ShareRowExclusiveLock; } +@@ -11828,6 +11830,7 @@ + | ALSO + | ALTER + | ALWAYS ++ | APPLICATION + | ASSERTION + | ASSIGNMENT + | AT +--- --- ../postgresql-9.2.0.orig/src/backend/storage/lmgr/lock.c Thu Aug 23 22:06:50 2012 UTC ++++ ./src/backend/storage/lmgr/lock.c Mon Sep 10 07:35:10 2012 UTC +@@ -96,8 +96,14 @@ + (1 << AccessShareLock) | (1 << RowShareLock) | + (1 << RowExclusiveLock) | (1 << ShareUpdateExclusiveLock) | + (1 << ShareLock) | (1 << ShareRowExclusiveLock) | +- (1 << ExclusiveLock) | (1 << AccessExclusiveLock) ++ (1 << ExclusiveLock) | (1 << AccessExclusiveLock), ++ ++ /* ApplicationShareLock*/ ++ (1 << ApplicationExclusiveLock), ++ + ++ /* ApplicationExclusiveLock*/ ++ (1 << ApplicationExclusiveLock | 1 << ApplicationShareLock) + }; + + /* Names of lock modes, for debug printouts */ +@@ -111,7 +117,9 @@ + "ShareLock", + "ShareRowExclusiveLock", + "ExclusiveLock", +- "AccessExclusiveLock" ++ "AccessExclusiveLock", ++ "ApplicationShareLock", ++ "ApplicationExclusiveLock" + }; + + #ifndef LOCK_DEBUG +@@ -119,7 +127,7 @@ + #endif + + static const LockMethodData default_lockmethod = { +- AccessExclusiveLock, /* highest valid lock mode number */ ++ ApplicationExclusiveLock, /* highest valid lock mode number */ + LockConflicts, + lock_mode_names, + #ifdef LOCK_DEBUG +@@ -130,7 +138,7 @@ + }; + + static const LockMethodData user_lockmethod = { +- AccessExclusiveLock, /* highest valid lock mode number */ ++ ApplicationExclusiveLock, /* highest valid lock mode number */ + LockConflicts, + lock_mode_names, + #ifdef LOCK_DEBUG +--- ../postgresql-9.2.0.orig/src/include/parser/kwlist.h 2011-09-09 01:13:28.000000000 +0400 ++++ ./src/include/parser/kwlist.h 2011-09-14 19:40:30.858423500 +0400 +@@ -42,6 +42,7 @@ + PG_KEYWORD("analyze", ANALYZE, RESERVED_KEYWORD) + PG_KEYWORD("and", AND, RESERVED_KEYWORD) + PG_KEYWORD("any", ANY, RESERVED_KEYWORD) ++PG_KEYWORD("application", APPLICATION, UNRESERVED_KEYWORD) + PG_KEYWORD("array", ARRAY, RESERVED_KEYWORD) + PG_KEYWORD("as", AS, RESERVED_KEYWORD) + PG_KEYWORD("asc", ASC, RESERVED_KEYWORD) + +--- ../postgresql-9.2.0_orig/src/include/storage/lock.h 2011-09-09 01:13:28.000000000 +0400 ++++ ./src/include/storage/lock.h 2011-09-14 19:40:30.905223600 +0400 +@@ -87,7 +87,7 @@ + typedef int LOCKMODE; + + /* MAX_LOCKMODES cannot be larger than the # of bits in LOCKMASK */ +-#define MAX_LOCKMODES 10 ++#define MAX_LOCKMODES 12 + + #define LOCKBIT_ON(lockmode) (1 << (lockmode)) + #define LOCKBIT_OFF(lockmode) (~(1 << (lockmode))) +@@ -156,6 +156,9 @@ + #define AccessExclusiveLock 8 /* ALTER TABLE, DROP TABLE, VACUUM + * FULL, and unqualified LOCK TABLE */ + ++#define ApplicationShareLock 9 /* requested explicitly */ ++#define ApplicationExclusiveLock 10 /* requested explicitly */ ++ + + /* + * LOCKTAG is the key information needed to look up a LOCK item in the diff --git a/ecpg_config.h b/ecpg_config.h new file mode 100644 index 0000000..6fecfcb --- /dev/null +++ b/ecpg_config.h @@ -0,0 +1,29 @@ +/* + * Kluge to support multilib installation of both 32- and 64-bit RPMS: + * we need to arrange that header files that appear in both RPMs are + * identical. Hence, this file is architecture-independent and calls + * in an arch-dependent file that will appear in just one RPM. + * + * To avoid breaking arches not explicitly supported by Red Hat, we + * use this indirection file *only* on known multilib arches. + * + * Note: this may well fail if user tries to use gcc's -I- option. + * But that option is deprecated anyway. + */ +#if defined(__x86_64__) +#include "ecpg_config_x86_64.h" +#elif defined(__i386__) +#include "ecpg_config_i386.h" +#elif defined(__ppc64__) || defined(__powerpc64__) +#include "ecpg_config_ppc64.h" +#elif defined(__ppc__) || defined(__powerpc__) +#include "ecpg_config_ppc.h" +#elif defined(__s390x__) +#include "ecpg_config_s390x.h" +#elif defined(__s390__) +#include "ecpg_config_s390.h" +#elif defined(__sparc__) && defined(__arch64__) +#include "ecpg_config_sparc64.h" +#elif defined(__sparc__) +#include "ecpg_config_sparc.h" +#endif diff --git a/filter-requires-perl-Pg.sh b/filter-requires-perl-Pg.sh new file mode 100644 index 0000000..f37557f --- /dev/null +++ b/filter-requires-perl-Pg.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +/usr/lib/rpm/perl.req $* | grep -v 'perl(Pg' diff --git a/online_analyze_93.patch b/online_analyze_93.patch new file mode 100644 index 0000000..c55239a --- /dev/null +++ b/online_analyze_93.patch @@ -0,0 +1,812 @@ +--- ../postgresql-9.3.0.orig/contrib/online_analyze/COPYRIGHT 1970-01-01 03:00:00.000000000 +0300 ++++ ./contrib/online_analyze/COPYRIGHT 2013-09-10 12:33:46.000000000 +0400 +@@ -0,0 +1,29 @@ ++/* ++ * Copyright (c) 2011 Teodor Sigaev ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. Neither the name of the author nor the names of any co-contributors ++ * may be used to endorse or promote products derived from this software ++ * without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY CONTRIBUTORS ``AS IS'' AND ANY EXPRESS ++ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ++ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY ++ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE ++ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER ++ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR ++ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN ++ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ +--- ../postgresql-9.3.0.orig/contrib/online_analyze/Makefile 1970-01-01 03:00:00.000000000 +0300 ++++ ./contrib/online_analyze/Makefile 2013-09-10 12:33:46.000000000 +0400 +@@ -0,0 +1,16 @@ ++MODULE_big = online_analyze ++OBJS = online_analyze.o ++#DATA_built = online_analyze.sql ++DOCS = README.online_analyze ++#REGRESS = online_analyze ++ ++ifdef USE_PGXS ++PGXS := $(shell pg_config --pgxs) ++include $(PGXS) ++else ++subdir = contrib/online_analyze ++top_builddir = ../.. ++include $(top_builddir)/src/Makefile.global ++include $(top_srcdir)/contrib/contrib-global.mk ++endif ++ +--- ../postgresql-9.3.0.orig/contrib/online_analyze/online_analyze.c 1970-01-01 03:00:00.000000000 +0300 ++++ ./contrib/online_analyze/online_analyze.c 2013-09-10 12:33:46.000000000 +0400 +@@ -0,0 +1,719 @@ ++/* ++ * Copyright (c) 2011 Teodor Sigaev ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. Neither the name of the author nor the names of any co-contributors ++ * may be used to endorse or promote products derived from this software ++ * without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY CONTRIBUTORS ``AS IS'' AND ANY EXPRESS ++ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ++ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY ++ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE ++ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER ++ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR ++ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN ++ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include "postgres.h" ++ ++#include "pgstat.h" ++#include "catalog/namespace.h" ++#include "commands/vacuum.h" ++#include "executor/executor.h" ++#include "nodes/nodes.h" ++#include "nodes/parsenodes.h" ++#include "storage/bufmgr.h" ++#include "utils/builtins.h" ++#include "utils/lsyscache.h" ++#include "utils/guc.h" ++#if PG_VERSION_NUM >= 90200 ++#include "catalog/pg_class.h" ++#include "nodes/primnodes.h" ++#include "tcop/utility.h" ++#include "utils/rel.h" ++#include "utils/relcache.h" ++#include "utils/timestamp.h" ++#endif ++ ++#ifdef PG_MODULE_MAGIC ++PG_MODULE_MAGIC; ++#endif ++ ++static bool online_analyze_enable = true; ++static bool online_analyze_verbose = true; ++static double online_analyze_scale_factor = 0.1; ++static int online_analyze_threshold = 50; ++static double online_analyze_min_interval = 10000; ++ ++static ExecutorEnd_hook_type oldExecutorEndHook = NULL; ++#if PG_VERSION_NUM >= 90200 ++static ProcessUtility_hook_type oldProcessUtilityHook = NULL; ++#endif ++ ++typedef enum ++{ ++ OATT_ALL = 0x03, ++ OATT_PERSISTENT = 0x01, ++ OATT_TEMPORARY = 0x02, ++ OATT_NONE = 0x00 ++} OnlineAnalyzeTableType; ++ ++static const struct config_enum_entry online_analyze_table_type_options[] = ++{ ++ {"all", OATT_ALL, false}, ++ {"persistent", OATT_PERSISTENT, false}, ++ {"temporary", OATT_TEMPORARY, false}, ++ {"none", OATT_NONE, false}, ++ {NULL, 0, false}, ++}; ++ ++static int online_analyze_table_type = (int)OATT_ALL; ++ ++typedef struct TableList { ++ int nTables; ++ Oid *tables; ++ char *tableStr; ++} TableList; ++ ++static TableList excludeTables = {0, NULL, NULL}; ++static TableList includeTables = {0, NULL, NULL}; ++ ++static int ++oid_cmp(const void *a, const void *b) ++{ ++ if (*(Oid*)a == *(Oid*)b) ++ return 0; ++ return (*(Oid*)a > *(Oid*)b) ? 1 : -1; ++} ++ ++static const char * ++tableListAssign(const char * newval, bool doit, TableList *tbl) ++{ ++ char *rawname; ++ List *namelist; ++ ListCell *l; ++ Oid *newOids = NULL; ++ int nOids = 0, ++ i = 0; ++ ++ rawname = pstrdup(newval); ++ ++ if (!SplitIdentifierString(rawname, ',', &namelist)) ++ goto cleanup; ++ ++ if (doit) ++ { ++ nOids = list_length(namelist); ++ newOids = malloc(sizeof(Oid) * (nOids+1)); ++ if (!newOids) ++ elog(ERROR,"could not allocate %d bytes", (int)(sizeof(Oid) * (nOids+1))); ++ } ++ ++ foreach(l, namelist) ++ { ++ char *curname = (char *) lfirst(l); ++#if PG_VERSION_NUM >= 90200 ++ Oid relOid = RangeVarGetRelid(makeRangeVarFromNameList(stringToQualifiedNameList(curname)), ++ NoLock, true); ++#else ++ Oid relOid = RangeVarGetRelid(makeRangeVarFromNameList(stringToQualifiedNameList(curname)), ++ true); ++#endif ++ ++ if (relOid == InvalidOid) ++ { ++#if PG_VERSION_NUM >= 90100 ++ if (doit == false) ++#endif ++ elog(WARNING,"'%s' does not exist", curname); ++ continue; ++ } ++ else if ( get_rel_relkind(relOid) != RELKIND_RELATION ) ++ { ++#if PG_VERSION_NUM >= 90100 ++ if (doit == false) ++#endif ++ elog(WARNING,"'%s' is not an table", curname); ++ continue; ++ } ++ else if (doit) ++ { ++ newOids[i++] = relOid; ++ } ++ } ++ ++ if (doit) ++ { ++ tbl->nTables = i; ++ if (tbl->tables) ++ free(tbl->tables); ++ tbl->tables = newOids; ++ if (tbl->nTables > 1) ++ qsort(tbl->tables, tbl->nTables, sizeof(tbl->tables[0]), oid_cmp); ++ } ++ ++ pfree(rawname); ++ list_free(namelist); ++ ++ return newval; ++ ++cleanup: ++ if (newOids) ++ free(newOids); ++ pfree(rawname); ++ list_free(namelist); ++ return NULL; ++} ++ ++#if PG_VERSION_NUM >= 90100 ++static bool ++excludeTablesCheck(char **newval, void **extra, GucSource source) ++{ ++ char *val; ++ ++ val = (char*)tableListAssign(*newval, false, &excludeTables); ++ ++ if (val) ++ { ++ *newval = val; ++ return true; ++ } ++ ++ return false; ++} ++ ++static void ++excludeTablesAssign(const char *newval, void *extra) ++{ ++ tableListAssign(newval, true, &excludeTables); ++} ++ ++static bool ++includeTablesCheck(char **newval, void **extra, GucSource source) ++{ ++ char *val; ++ ++ val = (char*)tableListAssign(*newval, false, &includeTables); ++ ++ if (val) ++ { ++ *newval = val; ++ return true; ++ } ++ ++ return false; ++} ++ ++static void ++includeTablesAssign(const char *newval, void *extra) ++{ ++ tableListAssign(newval, true, &excludeTables); ++} ++ ++#else /* PG_VERSION_NUM < 90100 */ ++ ++static const char * ++excludeTablesAssign(const char * newval, bool doit, GucSource source) ++{ ++ return tableListAssign(newval, doit, &excludeTables); ++} ++ ++static const char * ++includeTablesAssign(const char * newval, bool doit, GucSource source) ++{ ++ return tableListAssign(newval, doit, &includeTables); ++} ++ ++#endif ++ ++static const char* ++tableListShow(TableList *tbl) ++{ ++ char *val, *ptr; ++ int i, ++ len; ++ ++ len = 1 /* \0 */ + tbl->nTables * (2 * NAMEDATALEN + 2 /* ', ' */ + 1 /* . */); ++ ptr = val = palloc(len); ++ *ptr ='\0'; ++ for(i=0; inTables; i++) ++ { ++ char *relname = get_rel_name(tbl->tables[i]); ++ Oid nspOid = get_rel_namespace(tbl->tables[i]); ++ char *nspname = get_namespace_name(nspOid); ++ ++ if ( relname == NULL || nspOid == InvalidOid || nspname == NULL ) ++ continue; ++ ++ ptr += snprintf(ptr, len - (ptr - val), "%s%s.%s", ++ (i==0) ? "" : ", ", ++ nspname, relname); ++ } ++ ++ return val; ++} ++ ++static const char* ++excludeTablesShow(void) ++{ ++ return tableListShow(&excludeTables); ++} ++ ++static const char* ++includeTablesShow(void) ++{ ++ return tableListShow(&includeTables); ++} ++ ++static bool ++matchOid(TableList *tbl, Oid oid) ++{ ++ Oid *StopLow = tbl->tables, ++ *StopHigh = tbl->tables + tbl->nTables, ++ *StopMiddle; ++ ++ /* Loop invariant: StopLow <= val < StopHigh */ ++ while (StopLow < StopHigh) ++ { ++ StopMiddle = StopLow + ((StopHigh - StopLow) >> 1); ++ ++ if (*StopMiddle == oid) ++ return true; ++ else if (*StopMiddle < oid) ++ StopLow = StopMiddle + 1; ++ else ++ StopHigh = StopMiddle; ++ } ++ ++ return false; ++} ++ ++static void ++makeAnalyze(Oid relOid, CmdType operation, uint32 naffected) ++{ ++ PgStat_StatTabEntry *tabentry; ++ TimestampTz now = GetCurrentTimestamp(); ++ ++ if (relOid == InvalidOid) ++ return; ++ ++ if (get_rel_relkind(relOid) != RELKIND_RELATION) ++ return; ++ ++ tabentry = pgstat_fetch_stat_tabentry(relOid); ++ ++#if PG_VERSION_NUM >= 90000 ++#define changes_since_analyze(t) ((t)->changes_since_analyze) ++#else ++#define changes_since_analyze(t) ((t)->n_live_tuples + (t)->n_dead_tuples - (t)->last_anl_tuples) ++#endif ++ ++ if ( ++ tabentry == NULL /* a new table */ || ++ ( ++ /* do not analyze too often, if both stamps are exceeded the go */ ++ TimestampDifferenceExceeds(tabentry->analyze_timestamp, now, online_analyze_min_interval) && ++ TimestampDifferenceExceeds(tabentry->autovac_analyze_timestamp, now, online_analyze_min_interval) && ++ /* be in sync with relation_needs_vacanalyze */ ++ ((double)(changes_since_analyze(tabentry) + naffected)) >= ++ online_analyze_scale_factor * ((double)(tabentry->n_dead_tuples + tabentry->n_live_tuples)) + ++ (double)online_analyze_threshold ++ ) ++ ) ++ { ++ VacuumStmt vacstmt; ++ TimestampTz startStamp, endStamp; ++ ++ memset(&startStamp, 0, sizeof(startStamp)); /* keep compiler quiet */ ++ ++ /* ++ * includeTables overwrites excludeTables ++ */ ++ switch(online_analyze_table_type) ++ { ++ case OATT_ALL: ++ if (matchOid(&excludeTables, relOid) == true && matchOid(&includeTables, relOid) == false) ++ return; ++ break; ++ case OATT_NONE: ++ if (matchOid(&includeTables, relOid) == false) ++ return; ++ break; ++ case OATT_TEMPORARY: ++ case OATT_PERSISTENT: ++ default: ++ { ++ Relation rel; ++ OnlineAnalyzeTableType reltype; ++ ++ rel = RelationIdGetRelation(relOid); ++ reltype = ++#if PG_VERSION_NUM >= 90100 ++ (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP) ++#else ++ (rel->rd_istemp || rel->rd_islocaltemp) ++#endif ++ ? OATT_TEMPORARY : OATT_PERSISTENT; ++ RelationClose(rel); ++ ++ /* ++ * skip analyze if relation's type doesn't not match online_analyze_table_type ++ */ ++ if ((online_analyze_table_type & reltype) == 0 || matchOid(&excludeTables, relOid) == true) ++ { ++ if (matchOid(&includeTables, relOid) == false) ++ return; ++ } ++ } ++ break; ++ } ++ ++ vacstmt.type = T_VacuumStmt; ++ vacstmt.freeze_min_age = -1; ++ vacstmt.freeze_table_age = -1; /* ??? */ ++ vacstmt.relation = NULL; ++ vacstmt.va_cols = NIL; ++ ++#if PG_VERSION_NUM >= 90000 ++ vacstmt.options = VACOPT_ANALYZE; ++ if (online_analyze_verbose) ++ vacstmt.options |= VACOPT_VERBOSE; ++#else ++ vacstmt.vacuum = vacstmt.full = false; ++ vacstmt.analyze = true; ++ vacstmt.verbose = online_analyze_verbose; ++#endif ++ ++ if (online_analyze_verbose) ++ startStamp = GetCurrentTimestamp(); ++ ++ analyze_rel(relOid, &vacstmt, GetAccessStrategy(BAS_VACUUM) ++#if (PG_VERSION_NUM < 90004) && (PG_VERSION_NUM >= 90000) ++ , true ++#endif ++ ); ++ ++ if (online_analyze_verbose) ++ { ++ long secs; ++ int microsecs; ++ ++ endStamp = GetCurrentTimestamp(); ++ TimestampDifference(startStamp, endStamp, &secs, µsecs); ++ elog(INFO, "analyze \"%s\" took %.02f seconds", ++ get_rel_name(relOid), ((double)secs) + ((double)microsecs)/1.0e6); ++ } ++ ++ ++ if (tabentry == NULL) ++ { ++ /* new table */ ++ pgstat_clear_snapshot(); ++ } ++ else ++ { ++ /* update last analyze timestamp in local memory of backend */ ++ tabentry->analyze_timestamp = now; ++ } ++ } ++#if PG_VERSION_NUM >= 90000 ++ else if (tabentry != NULL) ++ { ++ tabentry->changes_since_analyze += naffected; ++ } ++#endif ++} ++ ++extern PGDLLIMPORT void onlineAnalyzeHooker(QueryDesc *queryDesc); ++void ++onlineAnalyzeHooker(QueryDesc *queryDesc) ++{ ++ uint32 naffected = 0; ++ ++ if (queryDesc->estate) ++ naffected = queryDesc->estate->es_processed; ++ ++ if (online_analyze_enable && queryDesc->plannedstmt && ++ (queryDesc->operation == CMD_INSERT || ++ queryDesc->operation == CMD_UPDATE || ++ queryDesc->operation == CMD_DELETE ++#if PG_VERSION_NUM < 90200 ++ || (queryDesc->operation == CMD_SELECT && queryDesc->plannedstmt->intoClause) ++#endif ++ )) ++ { ++#if PG_VERSION_NUM < 90200 ++ if (queryDesc->operation == CMD_SELECT) ++ { ++ Oid relOid = RangeVarGetRelid(queryDesc->plannedstmt->intoClause->rel, true); ++ ++ makeAnalyze(relOid, queryDesc->operation, naffected); ++ } ++ else ++#endif ++ if (queryDesc->plannedstmt->resultRelations && ++ queryDesc->plannedstmt->rtable) ++ { ++ ListCell *l; ++ ++ foreach(l, queryDesc->plannedstmt->resultRelations) ++ { ++ int n = lfirst_int(l); ++ RangeTblEntry *rte = list_nth(queryDesc->plannedstmt->rtable, n-1); ++ ++ if (rte->rtekind == RTE_RELATION) ++ makeAnalyze(rte->relid, queryDesc->operation, naffected); ++ } ++ } ++ } ++ ++ if (oldExecutorEndHook) ++ oldExecutorEndHook(queryDesc); ++ else ++ standard_ExecutorEnd(queryDesc); ++} ++ ++#if PG_VERSION_NUM >= 90200 ++static void ++onlineAnalyzeHookerUtility(Node *parsetree, const char *queryString, ++#if PG_VERSION_NUM >= 90300 ++ ProcessUtilityContext context, ParamListInfo params, ++#else ++ ParamListInfo params, bool isTopLevel, ++#endif ++ DestReceiver *dest, char *completionTag) { ++ RangeVar *tblname = NULL; ++ ++ if (IsA(parsetree, CreateTableAsStmt) && ((CreateTableAsStmt*)parsetree)->into) ++ tblname = (RangeVar*)copyObject(((CreateTableAsStmt*)parsetree)->into->rel); ++ ++ if (oldProcessUtilityHook) ++ oldProcessUtilityHook(parsetree, queryString, ++#if PG_VERSION_NUM >= 90300 ++ context, params, ++#else ++ params, isTopLevel, ++#endif ++ dest, completionTag); ++ else ++ standard_ProcessUtility(parsetree, queryString, ++#if PG_VERSION_NUM >= 90300 ++ context, params, ++#else ++ params, isTopLevel, ++#endif ++ dest, completionTag); ++ ++ if (tblname) { ++ Oid tblOid = RangeVarGetRelid(tblname, NoLock, true); ++ ++ makeAnalyze(tblOid, CMD_INSERT, 0); ++ } ++} ++#endif ++ ++void _PG_init(void); ++void ++_PG_init(void) ++{ ++ oldExecutorEndHook = ExecutorEnd_hook; ++ ++ ExecutorEnd_hook = onlineAnalyzeHooker; ++ ++#if PG_VERSION_NUM >= 90200 ++ oldProcessUtilityHook = ProcessUtility_hook; ++ ++ ProcessUtility_hook = onlineAnalyzeHookerUtility; ++#endif ++ ++ ++ DefineCustomBoolVariable( ++ "online_analyze.enable", ++ "Enable on-line analyze", ++ "Enables analyze of table directly after insert/update/delete/select into", ++ &online_analyze_enable, ++#if PG_VERSION_NUM >= 80400 ++ online_analyze_enable, ++#endif ++ PGC_USERSET, ++#if PG_VERSION_NUM >= 80400 ++ GUC_NOT_IN_SAMPLE, ++#if PG_VERSION_NUM >= 90100 ++ NULL, ++#endif ++#endif ++ NULL, ++ NULL ++ ); ++ ++ DefineCustomBoolVariable( ++ "online_analyze.verbose", ++ "Verbosity of on-line analyze", ++ "Make ANALYZE VERBOSE after table's changes", ++ &online_analyze_verbose, ++#if PG_VERSION_NUM >= 80400 ++ online_analyze_verbose, ++#endif ++ PGC_USERSET, ++#if PG_VERSION_NUM >= 80400 ++ GUC_NOT_IN_SAMPLE, ++#if PG_VERSION_NUM >= 90100 ++ NULL, ++#endif ++#endif ++ NULL, ++ NULL ++ ); ++ ++ DefineCustomRealVariable( ++ "online_analyze.scale_factor", ++ "fraction of table size to start on-line analyze", ++ "fraction of table size to start on-line analyze", ++ &online_analyze_scale_factor, ++#if PG_VERSION_NUM >= 80400 ++ online_analyze_scale_factor, ++#endif ++ 0.0, ++ 1.0, ++ PGC_USERSET, ++#if PG_VERSION_NUM >= 80400 ++ GUC_NOT_IN_SAMPLE, ++#if PG_VERSION_NUM >= 90100 ++ NULL, ++#endif ++#endif ++ NULL, ++ NULL ++ ); ++ ++ DefineCustomIntVariable( ++ "online_analyze.threshold", ++ "min number of row updates before on-line analyze", ++ "min number of row updates before on-line analyze", ++ &online_analyze_threshold, ++#if PG_VERSION_NUM >= 80400 ++ online_analyze_threshold, ++#endif ++ 0, ++ 0x7fffffff, ++ PGC_USERSET, ++#if PG_VERSION_NUM >= 80400 ++ GUC_NOT_IN_SAMPLE, ++#if PG_VERSION_NUM >= 90100 ++ NULL, ++#endif ++#endif ++ NULL, ++ NULL ++ ); ++ ++ DefineCustomRealVariable( ++ "online_analyze.min_interval", ++ "minimum time interval between analyze call (in milliseconds)", ++ "minimum time interval between analyze call (in milliseconds)", ++ &online_analyze_min_interval, ++#if PG_VERSION_NUM >= 80400 ++ online_analyze_min_interval, ++#endif ++ 0.0, ++ 1e30, ++ PGC_USERSET, ++#if PG_VERSION_NUM >= 80400 ++ GUC_NOT_IN_SAMPLE, ++#if PG_VERSION_NUM >= 90100 ++ NULL, ++#endif ++#endif ++ NULL, ++ NULL ++ ); ++ ++ DefineCustomEnumVariable( ++ "online_analyze.table_type", ++ "Type(s) of table for online analyze: all(default), persistent, temporary, none", ++ NULL, ++ &online_analyze_table_type, ++#if PG_VERSION_NUM >= 80400 ++ online_analyze_table_type, ++#endif ++ online_analyze_table_type_options, ++ PGC_USERSET, ++#if PG_VERSION_NUM >= 80400 ++ GUC_NOT_IN_SAMPLE, ++#if PG_VERSION_NUM >= 90100 ++ NULL, ++#endif ++#endif ++ NULL, ++ NULL ++ ); ++ ++ DefineCustomStringVariable( ++ "online_analyze.exclude_tables", ++ "List of tables which will not online analyze", ++ NULL, ++ &excludeTables.tableStr, ++#if PG_VERSION_NUM >= 80400 ++ "", ++#endif ++ PGC_USERSET, ++ 0, ++#if PG_VERSION_NUM >= 90100 ++ excludeTablesCheck, ++ excludeTablesAssign, ++#else ++ excludeTablesAssign, ++#endif ++ excludeTablesShow ++ ); ++ ++ DefineCustomStringVariable( ++ "online_analyze.include_tables", ++ "List of tables which will online analyze", ++ NULL, ++ &includeTables.tableStr, ++#if PG_VERSION_NUM >= 80400 ++ "", ++#endif ++ PGC_USERSET, ++ 0, ++#if PG_VERSION_NUM >= 90100 ++ includeTablesCheck, ++ includeTablesAssign, ++#else ++ includeTablesAssign, ++#endif ++ includeTablesShow ++ ); ++} ++ ++void _PG_fini(void); ++void ++_PG_fini(void) ++{ ++ ExecutorEnd_hook = oldExecutorEndHook; ++#if PG_VERSION_NUM >= 90200 ++ ProcessUtility_hook = oldProcessUtilityHook; ++#endif ++ ++ if (excludeTables.tables) ++ free(excludeTables.tables); ++ if (includeTables.tables) ++ free(includeTables.tables); ++ ++ excludeTables.tables = includeTables.tables = NULL; ++ excludeTables.nTables = includeTables.nTables = 0; ++} +--- ../postgresql-9.3.0.orig/contrib/online_analyze/README.online_analyze 1970-01-01 03:00:00.000000000 +0300 ++++ ./contrib/online_analyze/README.online_analyze 2013-09-10 12:33:46.000000000 +0400 +@@ -0,0 +1,36 @@ ++Module makes an analyze call immediately after INSERT/UPDATE/DELETE/SELECT INTO ++for affected table(s). ++ ++Supported versionsi of PostgreSQL: 8.4.*, 9.0.*, 9.1.*, 9.2.*, 9.3.* ++ ++Usage: LOAD 'online_analyze'; ++ ++Custom variables (defaults values are shown): ++online_analyze.enable = on ++ Enables on-line analyze ++ ++online_analyze.verbose = on ++ Execute ANALYZE VERBOSE ++ ++online_analyze.scale_factor = 0.1 ++ Fraction of table size to start on-line analyze (similar to ++ autovacuum_analyze_scale_factor) ++ ++online_analyze.threshold = 50 ++ Min number of row updates before on-line analyze (similar to ++ autovacuum_analyze_threshold) ++ ++online_analyze.min_interval = 10000 ++ Minimum time interval between analyze call per table (in milliseconds) ++ ++online_analyze.table_type = "all" ++ Type(s) of table for online analyze: all, persistent, temporary, none ++ ++online_analyze.exclude_tables = "" ++ List of tables which will not online analyze ++ ++online_analyze.include_tables = "" ++ List of tables which will online analyze ++ online_analyze.include_tables overwrites online_analyze.exclude_tables. ++ ++Author: Teodor Sigaev diff --git a/pg_config.h b/pg_config.h new file mode 100644 index 0000000..97ef2ba --- /dev/null +++ b/pg_config.h @@ -0,0 +1,29 @@ +/* + * Kluge to support multilib installation of both 32- and 64-bit RPMS: + * we need to arrange that header files that appear in both RPMs are + * identical. Hence, this file is architecture-independent and calls + * in an arch-dependent file that will appear in just one RPM. + * + * To avoid breaking arches not explicitly supported by Red Hat, we + * use this indirection file *only* on known multilib arches. + * + * Note: this may well fail if user tries to use gcc's -I- option. + * But that option is deprecated anyway. + */ +#if defined(__x86_64__) +#include "pg_config_x86_64.h" +#elif defined(__i386__) +#include "pg_config_i386.h" +#elif defined(__ppc64__) || defined(__powerpc64__) +#include "pg_config_ppc64.h" +#elif defined(__ppc__) || defined(__powerpc__) +#include "pg_config_ppc.h" +#elif defined(__s390x__) +#include "pg_config_s390x.h" +#elif defined(__s390__) +#include "pg_config_s390.h" +#elif defined(__sparc__) && defined(__arch64__) +#include "pg_config_sparc64.h" +#elif defined(__sparc__) +#include "pg_config_sparc.h" +#endif diff --git a/plantuner.patch b/plantuner.patch new file mode 100644 index 0000000..c612f60 --- /dev/null +++ b/plantuner.patch @@ -0,0 +1,671 @@ +diff -urN ../postgresql-9.2.0.orig/contrib/plantuner\COPYRIGHT ./contrib/plantuner/COPYRIGHT +--- ../postgresql-9.2.0.orig/contrib/plantuner/COPYRIGHT Thu Jan 01 04:00:00 1970 ++++ ./contrib/plantuner/COPYRIGHT Mon Sep 17 20:00:32 2012 +@@ -0,0 +1,29 @@ ++/* ++ * Copyright (c) 2009 Teodor Sigaev ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. Neither the name of the author nor the names of any co-contributors ++ * may be used to endorse or promote products derived from this software ++ * without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY CONTRIBUTORS ``AS IS'' AND ANY EXPRESS ++ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ++ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY ++ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE ++ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER ++ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR ++ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN ++ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ +diff -urN ../postgresql-9.2.0.orig/contrib/plantuner/expected/plantuner.out ./contrib/plantuner/expected/plantuner.out +--- ../postgresql-9.2.0.orig/contrib/plantuner/expected/plantuner.out Thu Jan 01 04:00:00 1970 ++++ ./contrib/plantuner/expected/plantuner.out Mon Sep 17 20:00:32 2012 +@@ -0,0 +1,49 @@ ++LOAD 'plantuner'; ++SHOW plantuner.disable_index; ++ plantuner.disable_index ++------------------------- ++ ++(1 row) ++ ++CREATE TABLE wow (i int, j int); ++CREATE INDEX i_idx ON wow (i); ++CREATE INDEX j_idx ON wow (j); ++SET enable_seqscan=off; ++SELECT * FROM wow; ++ i | j ++---+--- ++(0 rows) ++ ++SET plantuner.disable_index="i_idx, j_idx"; ++SELECT * FROM wow; ++ i | j ++---+--- ++(0 rows) ++ ++SHOW plantuner.disable_index; ++ plantuner.disable_index ++---------------------------- ++ public.i_idx, public.j_idx ++(1 row) ++ ++SET plantuner.disable_index="i_idx, nonexistent, public.j_idx, wow"; ++WARNING: 'nonexistent' does not exist ++WARNING: 'wow' is not an index ++SHOW plantuner.disable_index; ++ plantuner.disable_index ++---------------------------- ++ public.i_idx, public.j_idx ++(1 row) ++ ++SET plantuner.enable_index="i_idx"; ++SHOW plantuner.enable_index; ++ plantuner.enable_index ++------------------------ ++ public.i_idx ++(1 row) ++ ++SELECT * FROM wow; ++ i | j ++---+--- ++(0 rows) ++ +diff -urN ../postgresql-9.2.0.orig/contrib/plantuner/Makefile ./contrib/plantuner/Makefile +--- ../postgresql-9.2.0.orig/contrib/plantuner/Makefile Thu Jan 01 04:00:00 1970 ++++ ./contrib/plantuner/Makefile Mon Sep 17 20:00:32 2012 +@@ -0,0 +1,15 @@ ++MODULE_big = plantuner ++DOCS = README.plantuner ++REGRESS = plantuner ++OBJS=plantuner.o ++ ++ifdef USE_PGXS ++PGXS = $(shell pg_config --pgxs) ++include $(PGXS) ++else ++subdir = contrib/plantuner ++top_builddir = ../.. ++include $(top_builddir)/src/Makefile.global ++ ++include $(top_srcdir)/contrib/contrib-global.mk ++endif +diff -urN ../postgresql-9.2.0.orig/contrib/plantuner/plantuner.c ./contrib/plantuner/plantuner.c +--- ../postgresql-9.2.0.orig/contrib/plantuner/plantuner.c Thu Jan 01 04:00:00 1970 ++++ ./contrib/plantuner/plantuner.c Mon Sep 17 20:00:32 2012 +@@ -0,0 +1,378 @@ ++/* ++ * Copyright (c) 2009 Teodor Sigaev ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. Neither the name of the author nor the names of any co-contributors ++ * may be used to endorse or promote products derived from this software ++ * without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY CONTRIBUTORS ``AS IS'' AND ANY EXPRESS ++ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ++ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY ++ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE ++ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER ++ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR ++ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN ++ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++PG_MODULE_MAGIC; ++ ++static int nDisabledIndexes = 0; ++static Oid *disabledIndexes = NULL; ++static char *disableIndexesOutStr = ""; ++ ++static int nEnabledIndexes = 0; ++static Oid *enabledIndexes = NULL; ++static char *enableIndexesOutStr = ""; ++ ++get_relation_info_hook_type prevHook = NULL; ++static bool fix_empty_table = false; ++ ++ ++static const char * ++indexesAssign(const char * newval, bool doit, GucSource source, bool isDisable) ++{ ++ char *rawname; ++ List *namelist; ++ ListCell *l; ++ Oid *newOids = NULL; ++ int nOids = 0, ++ i = 0; ++ ++ rawname = pstrdup(newval); ++ ++ if (!SplitIdentifierString(rawname, ',', &namelist)) ++ goto cleanup; ++ ++ if (doit) ++ { ++ nOids = list_length(namelist); ++ newOids = malloc(sizeof(Oid) * (nOids+1)); ++ if (!newOids) ++ elog(ERROR,"could not allocate %d bytes", (int)(sizeof(Oid) * (nOids+1))); ++ } ++ ++ foreach(l, namelist) ++ { ++ char *curname = (char *) lfirst(l); ++#if PG_VERSION_NUM >= 90200 ++ Oid indexOid = RangeVarGetRelid(makeRangeVarFromNameList(stringToQualifiedNameList(curname)), ++ NoLock, true); ++#else ++ Oid indexOid = RangeVarGetRelid(makeRangeVarFromNameList(stringToQualifiedNameList(curname)), ++ true); ++#endif ++ ++ if (indexOid == InvalidOid) ++ { ++#if PG_VERSION_NUM >= 90100 ++ if (doit == false) ++#endif ++ elog(WARNING,"'%s' does not exist", curname); ++ continue; ++ } ++ else if ( get_rel_relkind(indexOid) != RELKIND_INDEX ) ++ { ++#if PG_VERSION_NUM >= 90100 ++ if (doit == false) ++#endif ++ elog(WARNING,"'%s' is not an index", curname); ++ continue; ++ } ++ else if (doit) ++ { ++ newOids[i++] = indexOid; ++ } ++ } ++ ++ if (doit) ++ { ++ if (isDisable) ++ { ++ nDisabledIndexes = nOids; ++ disabledIndexes = newOids; ++ } ++ else ++ { ++ nEnabledIndexes = nOids; ++ enabledIndexes = newOids; ++ } ++ } ++ ++ pfree(rawname); ++ list_free(namelist); ++ ++ return newval; ++ ++cleanup: ++ if (newOids) ++ free(newOids); ++ pfree(rawname); ++ list_free(namelist); ++ return NULL; ++} ++ ++static const char * ++assignDisabledIndexes(const char * newval, bool doit, GucSource source) ++{ ++ return indexesAssign(newval, doit, source, true); ++} ++ ++static const char * ++assignEnabledIndexes(const char * newval, bool doit, GucSource source) ++{ ++ return indexesAssign(newval, doit, source, false); ++} ++ ++#if PG_VERSION_NUM >= 90100 ++ ++static bool ++checkDisabledIndexes(char **newval, void **extra, GucSource source) ++{ ++ char *val; ++ ++ val = (char*)indexesAssign(*newval, false, source, true); ++ ++ if (val) ++ { ++ *newval = val; ++ return true; ++ } ++ ++ return false; ++} ++ ++static bool ++checkEnabledIndexes(char **newval, void **extra, GucSource source) ++{ ++ char *val; ++ ++ val = (char*)indexesAssign(*newval, false, source, false); ++ ++ if (val) ++ { ++ *newval = val; ++ return true; ++ } ++ ++ return false; ++} ++ ++static void ++assignDisabledIndexesNew(const char *newval, void *extra) ++{ ++ assignDisabledIndexes(newval, true, PGC_S_USER /* doesn't matter */); ++} ++ ++static void ++assignEnabledIndexesNew(const char *newval, void *extra) ++{ ++ assignEnabledIndexes(newval, true, PGC_S_USER /* doesn't matter */); ++} ++ ++#endif ++ ++static void ++indexFilter(PlannerInfo *root, Oid relationObjectId, bool inhparent, RelOptInfo *rel) { ++ int i; ++ ++ for(i=0;iindexlist) ++ { ++ IndexOptInfo *info = (IndexOptInfo*)lfirst(l); ++ ++ if (disabledIndexes[i] == info->indexoid) ++ { ++ int j; ++ ++ for(j=0; jindexoid) ++ break; ++ ++ if (j >= nEnabledIndexes) ++ rel->indexlist = list_delete_ptr(rel->indexlist, info); ++ ++ break; ++ } ++ } ++ } ++} ++ ++static void ++execPlantuner(PlannerInfo *root, Oid relationObjectId, bool inhparent, RelOptInfo *rel) { ++ Relation relation; ++ ++ relation = heap_open(relationObjectId, NoLock); ++ if (relation->rd_rel->relkind == RELKIND_RELATION) ++ { ++ if (fix_empty_table && RelationGetNumberOfBlocks(relation) == 0) ++ { ++ /* ++ * estimate_rel_size() could be too pessimistic for particular ++ * workload ++ */ ++ rel->pages = 0.0; ++ rel->tuples = 0.0; ++ } ++ ++ indexFilter(root, relationObjectId, inhparent, rel); ++ } ++ heap_close(relation, NoLock); ++ ++ /* ++ * Call next hook if it exists ++ */ ++ if (prevHook) ++ prevHook(root, relationObjectId, inhparent, rel); ++} ++ ++static const char* ++IndexFilterShow(Oid* indexes, int nIndexes) ++{ ++ char *val, *ptr; ++ int i, ++ len; ++ ++ len = 1 /* \0 */ + nIndexes * (2 * NAMEDATALEN + 2 /* ', ' */ + 1 /* . */); ++ ptr = val = palloc(len); ++ ++ *ptr =(char)'\0'; ++ for(i=0; i= 90100 ++ checkDisabledIndexes, ++ assignDisabledIndexesNew, ++#else ++ assignDisabledIndexes, ++#endif ++ disabledIndexFilterShow ++ ); ++ ++ DefineCustomStringVariable( ++ "plantuner.disable_index", ++ "List of disabled indexes", ++ "Listed indexes will not be used in queries", ++ &disableIndexesOutStr, ++ "", ++ PGC_USERSET, ++ 0, ++#if PG_VERSION_NUM >= 90100 ++ checkDisabledIndexes, ++ assignDisabledIndexesNew, ++#else ++ assignDisabledIndexes, ++#endif ++ disabledIndexFilterShow ++ ); ++ ++ DefineCustomStringVariable( ++ "plantuner.enable_index", ++ "List of enabled indexes (overload plantuner.disable_index)", ++ "Listed indexes which could be used in queries even they are listed in plantuner.disable_index", ++ &enableIndexesOutStr, ++ "", ++ PGC_USERSET, ++ 0, ++#if PG_VERSION_NUM >= 90100 ++ checkEnabledIndexes, ++ assignEnabledIndexesNew, ++#else ++ assignEnabledIndexes, ++#endif ++ enabledIndexFilterShow ++ ); ++ ++ DefineCustomBoolVariable( ++ "plantuner.fix_empty_table", ++ "Sets to zero estimations for empty tables", ++ "Sets to zero estimations for empty or newly created tables", ++ &fix_empty_table, ++#if PG_VERSION_NUM >= 80400 ++ fix_empty_table, ++#endif ++ PGC_USERSET, ++#if PG_VERSION_NUM >= 80400 ++ GUC_NOT_IN_SAMPLE, ++#if PG_VERSION_NUM >= 90100 ++ NULL, ++#endif ++#endif ++ NULL, ++ NULL ++ ); ++ ++ if (get_relation_info_hook != execPlantuner ) ++ { ++ prevHook = get_relation_info_hook; ++ get_relation_info_hook = execPlantuner; ++ } ++} +diff -urN ../postgresql-9.2.0.orig/contrib/plantuner/README.plantuner ./contrib/plantuner/README.plantuner +--- ../postgresql-9.2.0.orig/contrib/plantuner/README.plantuner Thu Jan 01 04:00:00 1970 ++++ ./contrib/plantuner/README.plantuner Mon Sep 17 20:00:32 2012 +@@ -0,0 +1,96 @@ ++Plantuner - enable planner hints ++ ++ contrib/plantuner is a contribution module for PostgreSQL 8.4+, which ++ enable planner hints. ++ ++ All work was done by Teodor Sigaev (teodor@sigaev.ru) and Oleg Bartunov ++ (oleg@sai.msu.su). ++ ++ Sponsor: Nomao project (http://www.nomao.com) ++ ++Motivation ++ ++ Whether somebody think it's bad or not, but sometime it's very ++ interesting to be able to control planner (provide hints, which tells ++ optimizer to ignore its algorithm in part), which is currently ++ impossible in POstgreSQL. Oracle, for example, has over 120 hints, SQL ++ Server also provides hints. ++ ++ This first version of plantuner provides a possibility to hide ++ specified indexes from PostgreSQL planner, so it will not use them. ++ ++ There are many situation, when developer want to temporarily disable ++ specific index(es), without dropping them, or to instruct planner to ++ use specific index. ++ ++ Next, for some workload PostgreSQL could be too pessimistic for ++ newly created tables and assumes much more rows in table than ++ it actually has. If plantuner.fix_empty_table GUC variable is set ++ to true then module will set to zero number of pages/tuples of ++ table which hasn't blocks in file. ++ ++Installation ++ ++ * Get latest source of plantuner from CVS Repository ++ * gmake && gmake install && gmake installcheck ++ ++Syntax ++ plantuner.forbid_index (deprecated) ++ plantuner.disable_index ++ List of indexes invisible to planner ++ plantuner.enable_index ++ List of indexes visible to planner even they are hided ++ by plantuner.disable_index. ++ ++Usage ++ ++ To enable the module you can either load shared library 'plantuner' in ++ psql session or specify 'shared_preload_libraries' option in ++ postgresql.conf. ++=# LOAD 'plantuner'; ++=# create table test(id int); ++=# create index id_idx on test(id); ++=# create index id_idx2 on test(id); ++=# \d test ++ Table "public.test" ++ Column | Type | Modifiers ++--------+---------+----------- ++ id | integer | ++Indexes: ++ "id_idx" btree (id) ++ "id_idx2" btree (id) ++=# explain select id from test where id=1; ++ QUERY PLAN ++----------------------------------------------------------------------- ++ Bitmap Heap Scan on test (cost=4.34..15.03 rows=12 width=4) ++ Recheck Cond: (id = 1) ++ -> Bitmap Index Scan on id_idx2 (cost=0.00..4.34 rows=12 width=0) ++ Index Cond: (id = 1) ++(4 rows) ++=# set enable_seqscan=off; ++=# set plantuner.disable_index='id_idx2'; ++=# explain select id from test where id=1; ++ QUERY PLAN ++---------------------------------------------------------------------- ++ Bitmap Heap Scan on test (cost=4.34..15.03 rows=12 width=4) ++ Recheck Cond: (id = 1) ++ -> Bitmap Index Scan on id_idx (cost=0.00..4.34 rows=12 width=0) ++ Index Cond: (id = 1) ++(4 rows) ++=# set plantuner.disable_index='id_idx2,id_idx'; ++=# explain select id from test where id=1; ++ QUERY PLAN ++------------------------------------------------------------------------- ++ Seq Scan on test (cost=10000000000.00..10000000040.00 rows=12 width=4) ++ Filter: (id = 1) ++(2 rows) ++=# set plantuner.enable_index='id_idx'; ++=# explain select id from test where id=1; ++ QUERY PLAN ++----------------------------------------------------------------------- ++ Bitmap Heap Scan on test (cost=4.34..15.03 rows=12 width=4) ++ Recheck Cond: (id = 1) ++ -> Bitmap Index Scan on id_idx (cost=0.00..4.34 rows=12 width=0) ++ Index Cond: (id = 1) ++(4 rows) ++ +diff -urN ../postgresql-9.2.0.orig/contrib/plantuner/results/plantuner.out ./contrib/plantuner/results/plantuner.out +--- ../postgresql-9.2.0.orig/contrib/plantuner/results/plantuner.out Thu Jan 01 04:00:00 1970 ++++ ./contrib/plantuner/results/plantuner.out Tue Sep 18 11:59:30 2012 +@@ -0,0 +1,49 @@ ++LOAD 'plantuner'; ++SHOW plantuner.disable_index; ++ plantuner.disable_index ++------------------------- ++ ++(1 row) ++ ++CREATE TABLE wow (i int, j int); ++CREATE INDEX i_idx ON wow (i); ++CREATE INDEX j_idx ON wow (j); ++SET enable_seqscan=off; ++SELECT * FROM wow; ++ i | j ++---+--- ++(0 rows) ++ ++SET plantuner.disable_index="i_idx, j_idx"; ++SELECT * FROM wow; ++ i | j ++---+--- ++(0 rows) ++ ++SHOW plantuner.disable_index; ++ plantuner.disable_index ++---------------------------- ++ public.i_idx, public.j_idx ++(1 row) ++ ++SET plantuner.disable_index="i_idx, nonexistent, public.j_idx, wow"; ++WARNING: 'nonexistent' does not exist ++WARNING: 'wow' is not an index ++SHOW plantuner.disable_index; ++ plantuner.disable_index ++---------------------------- ++ public.i_idx, public.j_idx ++(1 row) ++ ++SET plantuner.enable_index="i_idx"; ++SHOW plantuner.enable_index; ++ plantuner.enable_index ++------------------------ ++ public.i_idx ++(1 row) ++ ++SELECT * FROM wow; ++ i | j ++---+--- ++(0 rows) ++ +diff -urN ../postgresql-9.2.0.orig/contrib/plantuner/sql/plantuner.sql ./contrib/plantuner/sql/plantuner.sql +--- ../postgresql-9.2.0.orig/contrib/plantuner/sql/plantuner.sql Thu Jan 01 04:00:00 1970 ++++ ./contrib/plantuner/sql/plantuner.sql Mon Sep 17 20:00:32 2012 +@@ -0,0 +1,27 @@ ++LOAD 'plantuner'; ++ ++SHOW plantuner.disable_index; ++ ++CREATE TABLE wow (i int, j int); ++CREATE INDEX i_idx ON wow (i); ++CREATE INDEX j_idx ON wow (j); ++ ++SET enable_seqscan=off; ++ ++SELECT * FROM wow; ++ ++SET plantuner.disable_index="i_idx, j_idx"; ++ ++SELECT * FROM wow; ++ ++SHOW plantuner.disable_index; ++ ++SET plantuner.disable_index="i_idx, nonexistent, public.j_idx, wow"; ++ ++SHOW plantuner.disable_index; ++ ++SET plantuner.enable_index="i_idx"; ++ ++SHOW plantuner.enable_index; ++ ++SELECT * FROM wow; diff --git a/postgresql-1c-9.3.patch b/postgresql-1c-9.3.patch new file mode 100644 index 0000000..e7b4ff6 --- /dev/null +++ b/postgresql-1c-9.3.patch @@ -0,0 +1,101 @@ +--- ../postgresql-9.3.0.orig/contrib/Makefile 2013-09-03 00:53:18.000000000 +0400 ++++ ./contrib/Makefile 2013-09-16 10:49:00.535579000 +0400 +@@ -53,7 +53,12 @@ + tsearch2 \ + unaccent \ + vacuumlo \ +- worker_spi ++ worker_spi \ ++ mchar \ ++ fulleq \ ++ fasttrun \ ++ online_analyze \ ++ plantuner + + ifeq ($(with_openssl),yes) + SUBDIRS += sslinfo +--- ../postgresql-9.3.0.orig/src/backend/libpq/pg_hba.conf.sample Thu Aug 23 22:06:50 2012 UTC ++++ ./src/backend/libpq/pg_hba.conf.sample Mon Sep 10 07:25:38 2012 UTC +@@ -79,11 +79,12 @@ + @remove-line-for-nolocal@# "local" is for Unix domain socket connections only + @remove-line-for-nolocal@local all all @authmethodlocal@ + # IPv4 local connections: +-host all all 127.0.0.1/32 @authmethodhost@ ++#host all all 127.0.0.1/32 @authmethodhost@ ++host all all 0.0.0.0/0 @authmethodhost@ + # IPv6 local connections: +-host all all ::1/128 @authmethodhost@ ++host all all ::1/128 @authmethodhost@ + # Allow replication connections from localhost, by a user with the + # replication privilege. + @remove-line-for-nolocal@#local replication @default_username@ @authmethodlocal@ +-#host replication @default_username@ 127.0.0.1/32 @authmethodhost@ +-#host replication @default_username@ ::1/128 @authmethodhost@ ++#host replication @default_username@ 127.0.0.1/32 @authmethodhost@ ++#host replication @default_username@ ::1/128 @authmethodhost@ + +--- ../postgresql-9.3.0.orig/src/backend/utils/misc/postgresql.conf.sample Thu Sep 6 21:26:18 2012 UTC ++++ ./src/backend/utils/misc/postgresql.conf.sample Tue Sep 11 13:37:37 2012 UTC +@@ -56,7 +56,7 @@ + + # - Connection Settings - + +-#listen_addresses = 'localhost' # what IP address(es) to listen on; ++listen_addresses = '*' # what IP address(es) to listen on; + # comma-separated list of addresses; + # defaults to 'localhost'; use '*' for all + # (change requires restart) +@@ -132,7 +132,7 @@ + + #max_files_per_process = 1000 # min 25 + # (change requires restart) +-#shared_preload_libraries = '' # (change requires restart) ++shared_preload_libraries = 'online_analyze, plantuner' # (change requires restart) + + # - Cost-Based Vacuum Delay - + +@@ -263,7 +263,7 @@ + #cpu_tuple_cost = 0.01 # same scale as above + #cpu_index_tuple_cost = 0.005 # same scale as above + #cpu_operator_cost = 0.0025 # same scale as above +-#effective_cache_size = 128MB ++effective_cache_size = 512MB + + # - Genetic Query Optimizer - + +@@ -528,7 +528,7 @@ + #------------------------------------------------------------------------------ + + #deadlock_timeout = 1s +-#max_locks_per_transaction = 64 # min 10 ++max_locks_per_transaction = 150 # min 10 + # (change requires restart) + # Note: Each lock table slot uses ~270 bytes of shared memory, and there are + # max_locks_per_transaction * (max_connections + max_prepared_transactions) +@@ -546,11 +546,11 @@ + #array_nulls = on + #backslash_quote = safe_encoding # on, off, or safe_encoding + #default_with_oids = off +-#escape_string_warning = on ++#escape_string_warning = on + #lo_compat_privileges = off + #quote_all_identifiers = off + #sql_inheritance = on +-#standard_conforming_strings = on ++#standard_conforming_strings = on + #synchronize_seqscans = on + + # - Other Platforms and Clients - +@@ -570,4 +570,10 @@ + # CUSTOMIZED OPTIONS + #------------------------------------------------------------------------------ + +-# Add settings for extensions here ++online_analyze.threshold = 50 ++online_analyze.scale_factor = 0.1 ++online_analyze.enable = off ++online_analyze.verbose = off ++online_analyze.min_interval = 10000 ++online_analyze.table_type = 'temporary' ++plantuner.fix_empty_table = true + diff --git a/postgresql-9.3-libs.conf b/postgresql-9.3-libs.conf new file mode 100644 index 0000000..9637212 --- /dev/null +++ b/postgresql-9.3-libs.conf @@ -0,0 +1 @@ +/usr/pgsql-9.3/lib/ diff --git a/postgresql-9.3.spec b/postgresql-9.3.spec new file mode 100644 index 0000000..6525ea3 --- /dev/null +++ b/postgresql-9.3.spec @@ -0,0 +1,1081 @@ +# Conventions for PostgreSQL Global Development Group RPM releases: + +# Official PostgreSQL Development Group RPMS have a PGDG after the release number. +# Integer releases are stable -- 0.1.x releases are Pre-releases, and x.y are +# test releases. + +# Pre-releases are those that are built from CVS snapshots or pre-release +# tarballs from postgresql.org. Official beta releases are not +# considered pre-releases, nor are release candidates, as their beta or +# release candidate status is reflected in the version of the tarball. Pre- +# releases' versions do not change -- the pre-release tarball of 7.0.3, for +# example, has the same tarball version as the final official release of 7.0.3: +# but the tarball is different. + +# Test releases are where PostgreSQL itself is not in beta, but certain parts of +# the RPM packaging (such as the spec file, the initscript, etc) are in beta. + +# Pre-release RPM's should not be put up on the public ftp.postgresql.org server +# -- only test releases or full releases should be. +# This is the PostgreSQL Global Development Group Official RPMset spec file, +# or a derivative thereof. +# Copyright 2003-2013 Devrim GÜNDÜZ +# and others listed. + +# Major Contributors: +# --------------- +# Lamar Owen +# Tom Lane +# Peter Eisentraut +# Alvaro Herrera +# David Fetter +# Greg Smith +# and others in the Changelog.... + +# This spec file and ancilliary files are licensed in accordance with +# The PostgreSQL license. + +# In this file you can find the default build package list macros. These can be overridden by defining +# on the rpm command line: +# rpm --define 'packagename 1' .... to force the package to build. +# rpm --define 'packagename 0' .... to force the package NOT to build. +# The base package, the lib package, the devel package, and the server package always get built. + +%define _unpackaged_files_terminate_build 0 + +%define beta 0 +%{?beta:%define __os_install_post /usr/lib/rpm/brp-compress} + +%{!?kerbdir:%define kerbdir "/usr"} + +# This is a macro to be used with find_lang and other stuff +%define majorversion 9.3 +%define packageversion 93 +%define oname postgresql +%define pgbaseinstdir /usr/pgsql-%{majorversion} + +%{!?test:%define test 1} +%{!?plpython:%define plpython 1} +%{!?pltcl:%define pltcl 0} +%{!?plperl:%define plperl 1} +%{!?ssl:%define ssl 1} +%{!?intdatetimes:%define intdatetimes 1} +%{!?kerberos:%define kerberos 1} +%{!?nls:%define nls 1} +%{!?xml:%define xml 1} +%{!?pam:%define pam 1} +%{!?disablepgfts:%define disablepgfts 0} +%{!?runselftest:%define runselftest 0} +%{!?uuid:%define uuid 0} +%{!?ldap:%define ldap 0} +%{!?icu:%define icu 1} + +%ifarch x86_64 + %define local_libdir /usr/lib64 +%else + %define local_libdir /usr/lib +%endif + + +Summary: PostgreSQL client programs and libraries +Name: %{oname}%{packageversion} +Version: 9.3.4 +Release: 1.1C +License: PostgreSQL +Group: Applications/Databases +Url: http://www.postgresql.org/ + +Source0: ftp://ftp.postgresql.org/pub/source/v%{version}/postgresql-%{version}.tar.bz2 +Source3: postgresql.init +Source4: Makefile.regress +Source5: pg_config.h +Source6: README.rpm-dist +Source7: ecpg_config.h +Source9: postgresql-9.3-libs.conf +Source12: http://www.postgresql.org/files/documentation/pdf/%{majorversion}/%{oname}-%{majorversion}-A4.pdf +Source14: postgresql.pam +Source16: filter-requires-perl-Pg.sh + +Patch1: rpm-pgsql.patch +Patch3: postgresql-logging.patch +Patch6: postgresql-perl-rpath.patch +Patch8: postgresql-prefer-ncurses.patch +Patch9: 1c_FULL_93-0.23.2 +Patch10: postgresql-1c-9.3.patch +Patch11: applock-1c-9.3.patch +Patch12: online_analyze_93.patch +Patch13: plantuner.patch + +Buildrequires: perl +BuildRequires: glibc-devel +BuildRequires: bison +BuildRequires: flex + +Requires: /sbin/ldconfig initscripts + +#if %plpython +BuildRequires: python-devel +#endif + +#if %pltcl +BuildRequires: tcl-devel +#endif + +BuildRequires: readline-devel +BuildRequires: zlib-devel >= 1.0.4 + +#if %ssl +BuildRequires: openssl-devel +#endif + +#if %kerberos +BuildRequires: krb5-devel +BuildRequires: e2fsprogs-devel +#endif + +#if %nls +BuildRequires: gettext >= 0.10.35 +#endif + +#if %xml +BuildRequires: libxml2-devel +BuildRequires: libxslt-devel +#endif + +#if %pam +BuildRequires: pam-devel +#endif + +#if %uuid +BuildRequires: uuid-devel +#endif + +#if %ldap +BuildRequires: openldap-devel +#endif + +#if %icu +BuildRequires: libicu-devel +#endif + + +Requires: %{name}-libs = %{version}-%{release} +Requires(post): %{_sbindir}/update-alternatives +Requires(postun): %{_sbindir}/update-alternatives + +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) +Provides: postgresql + +%description +PostgreSQL is an advanced Object-Relational database management system +(DBMS) that supports almost all SQL constructs (including +transactions, subselects and user-defined types and functions). The +postgresql package includes the client programs and libraries that +you'll need to access a PostgreSQL DBMS server. These PostgreSQL +client programs are programs that directly manipulate the internal +structure of PostgreSQL databases on a PostgreSQL server. These client +programs can be located on the same machine with the PostgreSQL +server, or may be on a remote machine which accesses a PostgreSQL +server over a network connection. This package contains the command-line +utilities for managing PostgreSQL databases on a PostgreSQL server. + +If you want to manipulate a PostgreSQL database on a local or remote PostgreSQL +server, you need this package. You also need to install this package +if you're installing the postgresql93-server package. + +%package libs +Summary: The shared libraries required for any PostgreSQL clients +Group: Applications/Databases +Provides: libpq.so +Provides: postgresql-libs + +%description libs +The postgresql93-libs package provides the essential shared libraries for any +PostgreSQL client program or interface. You will need to install this package +to use any other PostgreSQL package or any clients that need to connect to a +PostgreSQL server. + +%package server +Summary: The programs needed to create and run a PostgreSQL server +Group: Applications/Databases +Requires: /usr/sbin/useradd /sbin/chkconfig +Requires: %{name} = %{version}-%{release} +Provides: postgresql-server + +%description server +The postgresql93-server package includes the programs needed to create +and run a PostgreSQL server, which will in turn allow you to create +and maintain PostgreSQL databases. PostgreSQL is an advanced +Object-Relational database management system (DBMS) that supports +almost all SQL constructs (including transactions, subselects and +user-defined types and functions). You should install +postgresql93-server if you want to create and maintain your own +PostgreSQL databases and/or your own PostgreSQL server. You also need +to install the postgresql package. + +%package docs +Summary: Extra documentation for PostgreSQL +Group: Applications/Databases +Provides: postgresql-docs + +%description docs +The postgresql93-docs package includes the SGML source for the documentation +as well as the documentation in PDF format and some extra documentation. +Install this package if you want to help with the PostgreSQL documentation +project, or if you want to generate printed documentation. This package also +includes HTML version of the documentation. + +%package contrib +Summary: Contributed source and binaries distributed with PostgreSQL +Group: Applications/Databases +Requires: %{name} = %{version} +Provides: postgresql-contrib + +%description contrib +The postgresql93-contrib package contains contributed packages that are +included in the PostgreSQL distribution. + +%package devel +Summary: PostgreSQL development header files and libraries +Group: Development/Libraries +Requires: %{name} = %{version}-%{release} +Provides: postgresql-devel + +%description devel +The postgresql93-devel package contains the header files and libraries +needed to compile C or C++ applications which will directly interact +with a PostgreSQL database management server and the ecpg Embedded C +Postgres preprocessor. You need to install this package if you want to +develop applications which will interact with a PostgreSQL server. + +%if %plperl +%package plperl +Summary: The Perl procedural language for PostgreSQL +Group: Applications/Databases +Requires: %{name}-server = %{version}-%{release} +%ifarch ppc ppc64 +BuildRequires: perl-devel +%endif +Obsoletes: postgresql-pl +Provides: postgresql-plperl + +%description plperl +PostgreSQL is an advanced Object-Relational database management +system. The postgresql93-plperl package contains the PL/Perl language +for the backend. +%endif + +%if %plpython +%package plpython +Summary: The Python procedural language for PostgreSQL +Group: Applications/Databases +Requires: %{name} = %{version} +Requires: %{name}-server = %{version} +Obsoletes: postgresql-pl +Provides: postgresql-plpython + +%description plpython +PostgreSQL is an advanced Object-Relational database management +system. The postgresql93-plpython package contains the PL/Python language +for the backend. +%endif + +%if %pltcl +%package pltcl +Summary: The Tcl procedural language for PostgreSQL +Group: Applications/Databases +Requires: %{name} = %{version} +Requires: %{name}-server = %{version} +Obsoletes: postgresql-pl +Provides: postgresql-pltcl + +%description pltcl +PostgreSQL is an advanced Object-Relational database management +system. The postgresql93-pltcl package contains the PL/Tcl language +for the backend. +%endif + +%if %test +%package test +Summary: The test suite distributed with PostgreSQL +Group: Applications/Databases +Requires: %{name}-server = %{version}-%{release} +Provides: postgresql-test + +%description test +PostgreSQL is an advanced Object-Relational database management +system. The postgresql-test package includes the sources and pre-built +binaries of various tests for the PostgreSQL database management +system, including regression tests and benchmarks. +%endif + +%define __perl_requires %{SOURCE16} + +%prep +%setup -q -n %{oname}-%{version} +#patch1 -p1 +%patch3 -p1 +# patch5 is applied later +%patch6 -p1 +%patch8 -p1 +%patch9 -p1 +%patch10 -p0 +%patch11 -p0 +%patch12 -p0 +%patch13 -p0 + +cp -p %{SOURCE12} . + +%build + +CFLAGS="${CFLAGS:-%optflags}" ; export CFLAGS +CXXFLAGS="${CXXFLAGS:-%optflags}" ; export CXXFLAGS +%if %kerberos +CPPFLAGS="${CPPFLAGS} -I%{_includedir}/et" ; export CPPFLAGS +CFLAGS="${CFLAGS} -I%{_includedir}/et" ; export CFLAGS +%endif + +# Strip out -ffast-math from CFLAGS.... + +CFLAGS=`echo $CFLAGS|xargs -n 1|grep -v ffast-math|xargs -n 100` + +export LIBNAME=%{_lib} +./configure --disable-rpath \ + --prefix=%{pgbaseinstdir} \ + --includedir=%{pgbaseinstdir}/include \ + --mandir=%{pgbaseinstdir}/share/man \ + --datadir=%{pgbaseinstdir}/share \ +%if %beta + --enable-debug \ + --enable-cassert \ +%endif +%if %plperl + --with-perl \ +%endif +%if %plpython + --with-python \ +%endif +%if %pltcl + --with-tcl \ + --with-tclconfig=%{_libdir} \ +%endif +%if %ssl + --with-openssl \ +%endif +%if %pam + --with-pam \ +%endif +%if %kerberos + --with-krb5 \ + --with-gssapi \ + --with-includes=%{kerbdir}/include \ + --with-libraries=%{kerbdir}/%{_lib} \ +%endif +%if %nls + --enable-nls \ +%endif +%if !%intdatetimes + --disable-integer-datetimes \ +%endif +%if %disablepgfts + --disable-thread-safety \ +%endif +%if %uuid + --with-ossp-uuid \ +%endif +%if %xml + --with-libxml \ + --with-libxslt \ +%endif +%if %ldap + --with-ldap \ +%endif + --with-system-tzdata=%{_datadir}/zoneinfo \ + --sysconfdir=/etc/sysconfig/pgsql \ + --docdir=%{_docdir} + +make %{?_smp_mflags} all +make %{?_smp_mflags} -C contrib all +%if %uuid +make %{?_smp_mflags} -C contrib/uuid-ossp all +%endif + +# Have to hack makefile to put correct path into tutorial scripts +sed "s|C=\`pwd\`;|C=%{pgbaseinstdir}/lib/tutorial;|" < src/tutorial/Makefile > src/tutorial/GNUmakefile +make %{?_smp_mflags} -C src/tutorial NO_PGXS=1 all +rm -f src/tutorial/GNUmakefile + +%if %runselftest + pushd src/test/regress + make all + cp ../../../contrib/spi/refint.so . + cp ../../../contrib/spi/autoinc.so . + make MAX_CONNECTIONS=5 check + make clean + popd + pushd src/pl + make MAX_CONNECTIONS=5 check + popd + pushd contrib + make MAX_CONNECTIONS=5 check + popd +%endif + +%if %test + pushd src/test/regress + make all + popd +%endif + +%install +rm -rf %{buildroot} + +make DESTDIR=%{buildroot} install +mkdir -p %{buildroot}%{pgbaseinstdir}/share/extensions/ +make -C contrib DESTDIR=%{buildroot} install +%if %uuid +make -C contrib/uuid-ossp DESTDIR=%{buildroot} install +%endif + +# multilib header hack; note pg_config.h is installed in two places! +# we only apply this to known Red Hat multilib arches, per bug #177564 +case `uname -i` in + i386 | x86_64 | ppc | ppc64 | s390 | s390x) + mv %{buildroot}%{pgbaseinstdir}/include/pg_config.h %{buildroot}%{pgbaseinstdir}/include/pg_config_`uname -i`.h + install -m 644 %{SOURCE5} %{buildroot}%{pgbaseinstdir}/include/ + mv %{buildroot}%{pgbaseinstdir}/include/server/pg_config.h %{buildroot}%{pgbaseinstdir}/include/server/pg_config_`uname -i`.h + install -m 644 %{SOURCE5} %{buildroot}%{pgbaseinstdir}/include/server/ + mv %{buildroot}%{pgbaseinstdir}/include/ecpg_config.h %{buildroot}%{pgbaseinstdir}/include/ecpg_config_`uname -i`.h + install -m 644 %{SOURCE7} %{buildroot}%{pgbaseinstdir}/include/ + ;; + *) + ;; +esac + +install -d %{buildroot}/etc/rc.d/init.d +sed 's/^PGVERSION=.*$/PGVERSION=%{version}/' <%{SOURCE3} > postgresql.init +install -m 755 postgresql.init %{buildroot}/etc/rc.d/init.d/postgresql-%{majorversion} + +%if %pam +install -d %{buildroot}/etc/pam.d +install -m 644 %{SOURCE14} %{buildroot}/etc/pam.d/postgresql%{packageversion} +%endif + +# PGDATA needs removal of group and world permissions due to pg_pwd hole. +install -d -m 700 %{buildroot}/var/lib/pgsql/%{majorversion}/data + +# backups of data go here... +install -d -m 700 %{buildroot}/var/lib/pgsql/%{majorversion}/backups + +# Create the multiple postmaster startup directory +install -d -m 700 %{buildroot}/etc/sysconfig/pgsql/%{majorversion} + +# Install linker conf file under postgresql installation directory. +# We will install the latest version via alternatives. +install -d -m 755 %{buildroot}%{pgbaseinstdir}/share/ +install -m 700 %{SOURCE9} %{buildroot}%{pgbaseinstdir}/share/ + +%if %test + # tests. There are many files included here that are unnecessary, + # but include them anyway for completeness. We replace the original + # Makefiles, however. + mkdir -p %{buildroot}%{pgbaseinstdir}/lib/test + cp -a src/test/regress %{buildroot}%{pgbaseinstdir}/lib/test + install -m 0755 contrib/spi/refint.so %{buildroot}%{pgbaseinstdir}/lib/test/regress + install -m 0755 contrib/spi/autoinc.so %{buildroot}%{pgbaseinstdir}/lib/test/regress + pushd %{buildroot}%{pgbaseinstdir}/lib/test/regress + strip *.so + rm -f GNUmakefile Makefile *.o + chmod 0755 pg_regress regress.so + popd + cp %{SOURCE4} %{buildroot}%{pgbaseinstdir}/lib/test/regress/Makefile + chmod 0644 %{buildroot}%{pgbaseinstdir}/lib/test/regress/Makefile +%endif + +# Fix some more documentation +# gzip doc/internals.ps +cp %{SOURCE6} README.rpm-dist +mkdir -p %{buildroot}%{pgbaseinstdir}/share/doc/html +mv doc/src/sgml/html doc +mkdir -p %{buildroot}%{pgbaseinstdir}/share/man/ +mv doc/src/sgml/man1 doc/src/sgml/man3 doc/src/sgml/man7 %{buildroot}%{pgbaseinstdir}/share/man/ +rm -rf %{buildroot}%{_docdir}/pgsql +%if %icu + install -m 0755 %{local_libdir}/libicuuc.so.*.* %{buildroot}%{pgbaseinstdir}/lib/ + install -m 0755 %{local_libdir}/libicui18n.so.*.* %{buildroot}%{pgbaseinstdir}/lib/ + install -m 0755 %{local_libdir}/libicudata.so.*.* %{buildroot}%{pgbaseinstdir}/lib/ +%endif +# initialize file lists +cp /dev/null main.lst +cp /dev/null libs.lst +cp /dev/null server.lst +cp /dev/null devel.lst +cp /dev/null plperl.lst +cp /dev/null pltcl.lst +cp /dev/null plpython.lst + +%if %nls +%find_lang ecpg-%{majorversion} +%find_lang ecpglib6-%{majorversion} +%find_lang initdb-%{majorversion} +%find_lang libpq5-%{majorversion} +%find_lang pg_basebackup-%{majorversion} +%find_lang pg_config-%{majorversion} +%find_lang pg_controldata-%{majorversion} +%find_lang pg_ctl-%{majorversion} +%find_lang pg_dump-%{majorversion} +%find_lang pg_resetxlog-%{majorversion} +%find_lang pgscripts-%{majorversion} +%if %plperl +%find_lang plperl-%{majorversion} +cat plperl-%{majorversion}.lang > pg_plperl.lst +%endif +%find_lang plpgsql-%{majorversion} +%if %plpython +%find_lang plpython-%{majorversion} +cat plpython-%{majorversion}.lang > pg_plpython.lst +%endif +%if %pltcl +%find_lang pltcl-%{majorversion} +cat pltcl-%{majorversion}.lang > pg_pltcl.lst +%endif +%find_lang postgres-%{majorversion} +%find_lang psql-%{majorversion} +%endif + +cat libpq5-%{majorversion}.lang > pg_libpq5.lst +cat pg_config-%{majorversion}.lang ecpg-%{majorversion}.lang ecpglib6-%{majorversion}.lang > pg_devel.lst +cat initdb-%{majorversion}.lang pg_ctl-%{majorversion}.lang psql-%{majorversion}.lang pg_dump-%{majorversion}.lang pg_basebackup-%{majorversion}.lang pgscripts-%{majorversion}.lang > pg_main.lst +cat postgres-%{majorversion}.lang pg_resetxlog-%{majorversion}.lang pg_controldata-%{majorversion}.lang plpgsql-%{majorversion}.lang > pg_server.lst + +%pre server +groupadd -g 26 -o -r postgres >/dev/null 2>&1 || : +useradd -M -n -g postgres -o -r -d /var/lib/pgsql -s /bin/bash \ + -c "PostgreSQL Server" -u 26 postgres >/dev/null 2>&1 || : +touch /var/log/pgsql +chown postgres:postgres /var/log/pgsql +chmod 0700 /var/log/pgsql + +%post server +chkconfig --add postgresql-9.3 +/sbin/ldconfig +# postgres' .bash_profile. +# We now don't install .bash_profile as we used to in pre 9.0. Instead, use cat, +# so that package manager will be happy during upgrade to new major version. +echo "[ -f /etc/profile ] && source /etc/profile +PGDATA=/var/lib/pgsql/9.3/data +export PGDATA" > /var/lib/pgsql/.bash_profile +chown postgres: /var/lib/pgsql/.bash_profile + +%preun server +if [ $1 = 0 ] ; then + /sbin/service postgresql-9.3 condstop >/dev/null 2>&1 + chkconfig --del postgresql-9.3 +fi + +%postun server +/sbin/ldconfig +if [ $1 -ge 1 ]; then + /sbin/service postgresql-9.3 condrestart >/dev/null 2>&1 +fi + +%if %plperl +%post -p /sbin/ldconfig plperl +%postun -p /sbin/ldconfig plperl +%endif + +%if %plpython +%post -p /sbin/ldconfig plpython +%postun -p /sbin/ldconfig plpython +%endif + +%if %pltcl +%post -p /sbin/ldconfig pltcl +%postun -p /sbin/ldconfig pltcl +%endif + +%if %test +%post test +chown -R postgres:postgres /usr/share/pgsql/test >/dev/null 2>&1 || : +%endif + +# Create alternatives entries for common binaries and man files +%post +%{_sbindir}/update-alternatives --install /usr/bin/psql pgsql-psql %{pgbaseinstdir}/bin/psql 930 +%{_sbindir}/update-alternatives --install /usr/bin/clusterdb pgsql-clusterdb %{pgbaseinstdir}/bin/clusterdb 930 +%{_sbindir}/update-alternatives --install /usr/bin/createdb pgsql-createdb %{pgbaseinstdir}/bin/createdb 930 +%{_sbindir}/update-alternatives --install /usr/bin/createlang pgsql-createlang %{pgbaseinstdir}/bin/createlang 930 +%{_sbindir}/update-alternatives --install /usr/bin/createuser pgsql-createuser %{pgbaseinstdir}/bin/createuser 930 +%{_sbindir}/update-alternatives --install /usr/bin/dropdb pgsql-dropdb %{pgbaseinstdir}/bin/dropdb 930 +%{_sbindir}/update-alternatives --install /usr/bin/droplang pgsql-droplang %{pgbaseinstdir}/bin/droplang 930 +%{_sbindir}/update-alternatives --install /usr/bin/dropuser pgsql-dropuser %{pgbaseinstdir}/bin/dropuser 930 +%{_sbindir}/update-alternatives --install /usr/bin/pg_basebackup pgsql-pg_basebackup %{pgbaseinstdir}/bin/pg_basebackup 930 +%{_sbindir}/update-alternatives --install /usr/bin/pg_dump pgsql-pg_dump %{pgbaseinstdir}/bin/pg_dump 930 +%{_sbindir}/update-alternatives --install /usr/bin/pg_dumpall pgsql-pg_dumpall %{pgbaseinstdir}/bin/pg_dumpall 930 +%{_sbindir}/update-alternatives --install /usr/bin/pg_restore pgsql-pg_restore %{pgbaseinstdir}/bin/pg_restore 930 +%{_sbindir}/update-alternatives --install /usr/bin/reindexdb pgsql-reindexdb %{pgbaseinstdir}/bin/reindexdb 930 +%{_sbindir}/update-alternatives --install /usr/bin/vacuumdb pgsql-vacuumdb %{pgbaseinstdir}/bin/vacuumdb 930 +%{_sbindir}/update-alternatives --install /usr/share/man/man1/clusterdb.1 pgsql-clusterdbman %{pgbaseinstdir}/share/man/man1/clusterdb.1 930 +%{_sbindir}/update-alternatives --install /usr/share/man/man1/createdb.1 pgsql-createdbman %{pgbaseinstdir}/share/man/man1/createdb.1 930 +%{_sbindir}/update-alternatives --install /usr/share/man/man1/createlang.1 pgsql-createlangman %{pgbaseinstdir}/share/man/man1/createlang.1 930 +%{_sbindir}/update-alternatives --install /usr/share/man/man1/createuser.1 pgsql-createuserman %{pgbaseinstdir}/share/man/man1/createuser.1 930 +%{_sbindir}/update-alternatives --install /usr/share/man/man1/dropdb.1 pgsql-dropdbman %{pgbaseinstdir}/share/man/man1/dropdb.1 930 +%{_sbindir}/update-alternatives --install /usr/share/man/man1/droplang.1 pgsql-droplangman %{pgbaseinstdir}/share/man/man1/droplang.1 930 +%{_sbindir}/update-alternatives --install /usr/share/man/man1/dropuser.1 pgsql-dropuserman %{pgbaseinstdir}/share/man/man1/dropuser.1 930 +%{_sbindir}/update-alternatives --install /usr/share/man/man1/pg_basebackup.1 pgsql-pg_basebackupman %{pgbaseinstdir}/share/man/man1/pg_basebackup.1 930 +%{_sbindir}/update-alternatives --install /usr/share/man/man1/pg_dump.1 pgsql-pg_dumpman %{pgbaseinstdir}/share/man/man1/pg_dump.1 930 +%{_sbindir}/update-alternatives --install /usr/share/man/man1/pg_dumpall.1 pgsql-pg_dumpallman %{pgbaseinstdir}/share/man/man1/pg_dumpall.1 930 +%{_sbindir}/update-alternatives --install /usr/share/man/man1/pg_restore.1 pgsql-pg_restoreman %{pgbaseinstdir}/share/man/man1/pg_restore.1 930 +%{_sbindir}/update-alternatives --install /usr/share/man/man1/psql.1 pgsql-psqlman %{pgbaseinstdir}/share/man/man1/psql.1 930 +%{_sbindir}/update-alternatives --install /usr/share/man/man1/reindexdb.1 pgsql-reindexdbman %{pgbaseinstdir}/share/man/man1/reindexdb.1 930 +%{_sbindir}/update-alternatives --install /usr/share/man/man1/vacuumdb.1 pgsql-vacuumdbman %{pgbaseinstdir}/share/man/man1/vacuumdb.1 930 + +%post libs +%{_sbindir}/update-alternatives --install /etc/ld.so.conf.d/postgresql-pgdg-libs.conf pgsql-ld-conf %{pgbaseinstdir}/share/postgresql-9.3-libs.conf 930 +/sbin/ldconfig + +# Drop alternatives entries for common binaries and man files +%postun + if [ "$1" -eq 0 ] + then + # Only remove these links if the package is completely removed from the system (vs.just being upgraded) + %{_sbindir}/update-alternatives --remove pgsql-psql %{pgbaseinstdir}/bin/psql + %{_sbindir}/update-alternatives --remove pgsql-clusterdb %{pgbaseinstdir}/bin/clusterdb + %{_sbindir}/update-alternatives --remove pgsql-clusterdbman %{pgbaseinstdir}/share/man/man1/clusterdb.1 + %{_sbindir}/update-alternatives --remove pgsql-createdb %{pgbaseinstdir}/bin/createdb + %{_sbindir}/update-alternatives --remove pgsql-createdbman %{pgbaseinstdir}/share/man/man1/createdb.1 + %{_sbindir}/update-alternatives --remove pgsql-createlang %{pgbaseinstdir}/bin/createlang + %{_sbindir}/update-alternatives --remove pgsql-createlangman %{pgbaseinstdir}/share/man/man1/createlang.1 + %{_sbindir}/update-alternatives --remove pgsql-createuser %{pgbaseinstdir}/bin/createuser + %{_sbindir}/update-alternatives --remove pgsql-createuserman %{pgbaseinstdir}/share/man/man1/createuser.1 + %{_sbindir}/update-alternatives --remove pgsql-dropdb %{pgbaseinstdir}/bin/dropdb + %{_sbindir}/update-alternatives --remove pgsql-dropdbman %{pgbaseinstdir}/share/man/man1/dropdb.1 + %{_sbindir}/update-alternatives --remove pgsql-droplang %{pgbaseinstdir}/bin/droplang + %{_sbindir}/update-alternatives --remove pgsql-droplangman %{pgbaseinstdir}/share/man/man1/droplang.1 + %{_sbindir}/update-alternatives --remove pgsql-dropuser %{pgbaseinstdir}/bin/dropuser + %{_sbindir}/update-alternatives --remove pgsql-dropuserman %{pgbaseinstdir}/share/man/man1/dropuser.1 + %{_sbindir}/update-alternatives --remove pgsql-pg_basebackup %{pgbaseinstdir}/bin/pg_basebackup + %{_sbindir}/update-alternatives --remove pgsql-pg_dump %{pgbaseinstdir}/bin/pg_dump + %{_sbindir}/update-alternatives --remove pgsql-pg_dumpall %{pgbaseinstdir}/bin/pg_dumpall + %{_sbindir}/update-alternatives --remove pgsql-pg_dumpallman %{pgbaseinstdir}/share/man/man1/pg_dumpall.1 + %{_sbindir}/update-alternatives --remove pgsql-pg_basebackupman %{pgbaseinstdir}/share/man/man1/pg_basebackup.1 + %{_sbindir}/update-alternatives --remove pgsql-pg_dumpman %{pgbaseinstdir}/share/man/man1/pg_dump.1 + %{_sbindir}/update-alternatives --remove pgsql-pg_restore %{pgbaseinstdir}/bin/pg_restore + %{_sbindir}/update-alternatives --remove pgsql-pg_restoreman %{pgbaseinstdir}/share/man/man1/pg_restore.1 + %{_sbindir}/update-alternatives --remove pgsql-psqlman %{pgbaseinstdir}/share/man/man1/psql.1 + %{_sbindir}/update-alternatives --remove pgsql-reindexdb %{pgbaseinstdir}/bin/reindexdb + %{_sbindir}/update-alternatives --remove pgsql-reindexdbman %{pgbaseinstdir}/share/man/man1/reindexdb.1 + %{_sbindir}/update-alternatives --remove pgsql-vacuumdb %{pgbaseinstdir}/bin/vacuumdb + %{_sbindir}/update-alternatives --remove pgsql-vacuumdbman %{pgbaseinstdir}/share/man/man1/vacuumdb.1 + fi + +%postun libs +if [ "$1" -eq 0 ] + then + %{_sbindir}/update-alternatives --remove pgsql-ld-conf %{pgbaseinstdir}/share/postgresql-9.3-libs.conf + /sbin/ldconfig +fi + +%clean +rm -rf %{buildroot} + +# FILES section. + +%files -f pg_main.lst +%defattr(-,root,root) +%doc doc/KNOWN_BUGS doc/MISSING_FEATURES +%doc COPYRIGHT doc/bug.template +%doc README.rpm-dist +%{pgbaseinstdir}/bin/clusterdb +%{pgbaseinstdir}/bin/createdb +%{pgbaseinstdir}/bin/createlang +%{pgbaseinstdir}/bin/createuser +%{pgbaseinstdir}/bin/dropdb +%{pgbaseinstdir}/bin/droplang +%{pgbaseinstdir}/bin/dropuser +%{pgbaseinstdir}/bin/pg_basebackup +%{pgbaseinstdir}/bin/pg_config +%{pgbaseinstdir}/bin/pg_dump +%{pgbaseinstdir}/bin/pg_dumpall +%{pgbaseinstdir}/bin/pg_isready +%{pgbaseinstdir}/bin/pg_restore +%{pgbaseinstdir}/bin/pg_test_fsync +%{pgbaseinstdir}/bin/pg_receivexlog +%{pgbaseinstdir}/bin/psql +%{pgbaseinstdir}/bin/reindexdb +%{pgbaseinstdir}/bin/vacuumdb +%{pgbaseinstdir}/share/man/man1/clusterdb.* +%{pgbaseinstdir}/share/man/man1/createdb.* +%{pgbaseinstdir}/share/man/man1/createlang.* +%{pgbaseinstdir}/share/man/man1/createuser.* +%{pgbaseinstdir}/share/man/man1/dropdb.* +%{pgbaseinstdir}/share/man/man1/droplang.* +%{pgbaseinstdir}/share/man/man1/dropuser.* +%{pgbaseinstdir}/share/man/man1/pg_basebackup.* +%{pgbaseinstdir}/share/man/man1/pg_config.* +%{pgbaseinstdir}/share/man/man1/pg_dump.* +%{pgbaseinstdir}/share/man/man1/pg_dumpall.* +%{pgbaseinstdir}/share/man/man1/pg_isready.* +%{pgbaseinstdir}/share/man/man1/pg_receivexlog.* +%{pgbaseinstdir}/share/man/man1/pg_restore.* +%{pgbaseinstdir}/share/man/man1/psql.* +%{pgbaseinstdir}/share/man/man1/reindexdb.* +%{pgbaseinstdir}/share/man/man1/vacuumdb.* +%{pgbaseinstdir}/share/man/man3/* +%{pgbaseinstdir}/share/man/man7/* + +%files docs +%defattr(-,root,root) +%doc doc/src/* +%doc *-A4.pdf +%doc src/tutorial +%doc doc/html + +%files contrib +%defattr(-,root,root) +%{pgbaseinstdir}/lib/_int.so +%{pgbaseinstdir}/lib/adminpack.so +%{pgbaseinstdir}/lib/auth_delay.so +%{pgbaseinstdir}/lib/autoinc.so +%{pgbaseinstdir}/lib/auto_explain.so +%{pgbaseinstdir}/lib/btree_gin.so +%{pgbaseinstdir}/lib/btree_gist.so +%{pgbaseinstdir}/lib/chkpass.so +%{pgbaseinstdir}/lib/citext.so +%{pgbaseinstdir}/lib/cube.so +%{pgbaseinstdir}/lib/dblink.so +%{pgbaseinstdir}/lib/dummy_seclabel.so +%{pgbaseinstdir}/lib/earthdistance.so +%{pgbaseinstdir}/lib/file_fdw.so* +%{pgbaseinstdir}/lib/fuzzystrmatch.so +%{pgbaseinstdir}/lib/insert_username.so +%{pgbaseinstdir}/lib/isn.so +%{pgbaseinstdir}/lib/hstore.so +%{pgbaseinstdir}/lib/fasttrun.so +%{pgbaseinstdir}/lib/fulleq.so +%{pgbaseinstdir}/lib/online_analyze.so +%{pgbaseinstdir}/lib/plantuner.so +%{pgbaseinstdir}/lib/mchar.so +%{pgbaseinstdir}/lib/passwordcheck.so +%{pgbaseinstdir}/lib/pg_freespacemap.so +%{pgbaseinstdir}/lib/pg_stat_statements.so +%{pgbaseinstdir}/lib/pgrowlocks.so +%{pgbaseinstdir}/lib/postgres_fdw.so +%{pgbaseinstdir}/lib/sslinfo.so +%{pgbaseinstdir}/lib/lo.so +%{pgbaseinstdir}/lib/ltree.so +%{pgbaseinstdir}/lib/moddatetime.so +%{pgbaseinstdir}/lib/pageinspect.so +%{pgbaseinstdir}/lib/pgcrypto.so +%{pgbaseinstdir}/lib/pgstattuple.so +%{pgbaseinstdir}/lib/pg_buffercache.so +%{pgbaseinstdir}/lib/pg_trgm.so +%{pgbaseinstdir}/lib/pg_upgrade_support.so +%{pgbaseinstdir}/lib/refint.so +%{pgbaseinstdir}/lib/seg.so +%{pgbaseinstdir}/lib/tablefunc.so +%{pgbaseinstdir}/lib/tcn.so +%{pgbaseinstdir}/lib/timetravel.so +%{pgbaseinstdir}/lib/unaccent.so +%{pgbaseinstdir}/lib/worker_spi.so +%if %xml +%{pgbaseinstdir}/lib/pgxml.so +%endif +%if %uuid +%{pgbaseinstdir}/lib/uuid-ossp.so +%endif +%{pgbaseinstdir}/share/extension/adminpack* +%{pgbaseinstdir}/share/extension/autoinc* +%{pgbaseinstdir}/share/extension/btree_gin* +%{pgbaseinstdir}/share/extension/btree_gist* +%{pgbaseinstdir}/share/extension/chkpass* +%{pgbaseinstdir}/share/extension/citext* +%{pgbaseinstdir}/share/extension/cube* +%{pgbaseinstdir}/share/extension/dblink* +%{pgbaseinstdir}/share/extension/dict_int* +%{pgbaseinstdir}/share/extension/dict_xsyn* +%{pgbaseinstdir}/share/extension/earthdistance* +%{pgbaseinstdir}/share/extension/file_fdw* +%{pgbaseinstdir}/share/extension/fuzzystrmatch* +%{pgbaseinstdir}/share/extension/hstore* +%{pgbaseinstdir}/share/extension/insert_username* +%{pgbaseinstdir}/share/extension/intagg* +%{pgbaseinstdir}/share/extension/intarray* +%{pgbaseinstdir}/share/extension/isn* +%{pgbaseinstdir}/share/extension/lo* +%{pgbaseinstdir}/share/extension/ltree* +%{pgbaseinstdir}/share/extension/moddatetime* +%{pgbaseinstdir}/share/extension/pageinspect* +%{pgbaseinstdir}/share/extension/pg_buffercache* +%{pgbaseinstdir}/share/extension/pg_freespacemap* +%{pgbaseinstdir}/share/extension/pg_stat_statements* +%{pgbaseinstdir}/share/extension/pg_trgm* +%{pgbaseinstdir}/share/extension/pgcrypto* +%{pgbaseinstdir}/share/extension/pgrowlocks* +%{pgbaseinstdir}/share/extension/pgstattuple* +%{pgbaseinstdir}/share/extension/postgres_fdw* +%{pgbaseinstdir}/share/extension/refint* +%{pgbaseinstdir}/share/extension/seg* +%{pgbaseinstdir}/share/extension/sslinfo* +%{pgbaseinstdir}/share/extension/tablefunc* +%{pgbaseinstdir}/share/extension/tcn* +%{pgbaseinstdir}/share/extension/test_parser* +%{pgbaseinstdir}/share/extension/timetravel* +%{pgbaseinstdir}/share/extension/tsearch2* +%{pgbaseinstdir}/share/extension/unaccent* +%if %uuid +%{pgbaseinstdir}/share/extension/uuid-ossp* +%endif +%{pgbaseinstdir}/share/extension/xml2* +%{pgbaseinstdir}/bin/oid2name +%{pgbaseinstdir}/bin/pgbench +%{pgbaseinstdir}/bin/vacuumlo +%{pgbaseinstdir}/bin/pg_archivecleanup +%{pgbaseinstdir}/bin/pg_standby +%{pgbaseinstdir}/bin/pg_test_timing +%{pgbaseinstdir}/bin/pg_upgrade +%{pgbaseinstdir}/bin/pg_xlogdump +%{pgbaseinstdir}/share/man/man1/oid2name.1 +%{pgbaseinstdir}/share/man/man1/pg_archivecleanup.1 +%{pgbaseinstdir}/share/man/man1/pg_standby.1 +%{pgbaseinstdir}/share/man/man1/pg_test_fsync.1 +%{pgbaseinstdir}/share/man/man1/pg_test_timing.1 +%{pgbaseinstdir}/share/man/man1/pg_upgrade.1 +%{pgbaseinstdir}/share/man/man1/pg_xlogdump.1 +%{pgbaseinstdir}/share/man/man1/pgbench.1 +%{pgbaseinstdir}/share/man/man1/vacuumlo.1 +%if %icu +%attr(755,root,root) %{pgbaseinstdir}/lib/libicuuc.so.*.* +%attr(755,root,root) %{pgbaseinstdir}/lib/libicui18n.so.*.* +%attr(755,root,root) %{pgbaseinstdir}/lib/libicudata.so.*.* +%endif + + + +%files libs -f pg_libpq5.lst +%defattr(-,root,root) +%{pgbaseinstdir}/lib/libpq.so.* +%{pgbaseinstdir}/lib/libecpg.so* +%{pgbaseinstdir}/lib/libpgtypes.so.* +%{pgbaseinstdir}/lib/libecpg_compat.so.* +%{pgbaseinstdir}/lib/libpqwalreceiver.so +%config(noreplace) %{pgbaseinstdir}/share/postgresql-9.3-libs.conf + +%files server -f pg_server.lst +%defattr(-,root,root) +%config(noreplace) /etc/rc.d/init.d/postgresql-%{majorversion} +%if %pam +%config(noreplace) /etc/pam.d/postgresql%{packageversion} +%endif +%attr (755,root,root) %dir /etc/sysconfig/pgsql +%{pgbaseinstdir}/bin/initdb +%{pgbaseinstdir}/bin/pg_controldata +%{pgbaseinstdir}/bin/pg_ctl +%{pgbaseinstdir}/bin/pg_resetxlog +%{pgbaseinstdir}/bin/postgres +%{pgbaseinstdir}/bin/postmaster +%{pgbaseinstdir}/share/man/man1/initdb.* +%{pgbaseinstdir}/share/man/man1/pg_controldata.* +%{pgbaseinstdir}/share/man/man1/pg_ctl.* +%{pgbaseinstdir}/share/man/man1/pg_resetxlog.* +%{pgbaseinstdir}/share/man/man1/postgres.* +%{pgbaseinstdir}/share/man/man1/postmaster.* +%{pgbaseinstdir}/share/postgres.bki +%{pgbaseinstdir}/share/postgres.description +%{pgbaseinstdir}/share/postgres.shdescription +%{pgbaseinstdir}/share/system_views.sql +%{pgbaseinstdir}/share/*.sample +%{pgbaseinstdir}/share/timezonesets/* +%{pgbaseinstdir}/share/tsearch_data/*.affix +%{pgbaseinstdir}/share/tsearch_data/*.dict +%{pgbaseinstdir}/share/tsearch_data/*.ths +%{pgbaseinstdir}/share/tsearch_data/*.rules +%{pgbaseinstdir}/share/tsearch_data/*.stop +%{pgbaseinstdir}/share/tsearch_data/*.syn +%{pgbaseinstdir}/lib/dict_int.so +%{pgbaseinstdir}/lib/dict_snowball.so +%{pgbaseinstdir}/lib/dict_xsyn.so +%{pgbaseinstdir}/lib/euc2004_sjis2004.so +%{pgbaseinstdir}/lib/plpgsql.so +%dir %{pgbaseinstdir}/share/extension +%{pgbaseinstdir}/share/extension/plpgsql* +%{pgbaseinstdir}/lib/test_parser.so +%{pgbaseinstdir}/lib/tsearch2.so + +%dir %{pgbaseinstdir}/lib +%dir %{pgbaseinstdir}/share +%attr(700,postgres,postgres) %dir /var/lib/pgsql +%attr(700,postgres,postgres) %dir /var/lib/pgsql/%{majorversion} +%attr(700,postgres,postgres) %dir /var/lib/pgsql/%{majorversion}/data +%attr(700,postgres,postgres) %dir /var/lib/pgsql/%{majorversion}/backups +%{pgbaseinstdir}/lib/*_and_*.so +%{pgbaseinstdir}/share/conversion_create.sql +%{pgbaseinstdir}/share/information_schema.sql +%{pgbaseinstdir}/share/snowball_create.sql +%{pgbaseinstdir}/share/sql_features.txt + +%files devel -f pg_devel.lst +%defattr(-,root,root) +%{pgbaseinstdir}/include/* +%{pgbaseinstdir}/bin/ecpg +%{pgbaseinstdir}/lib/libpq.so +%{pgbaseinstdir}/lib/libecpg.so +%{pgbaseinstdir}/lib/libpq.a +%{pgbaseinstdir}/lib/libecpg.a +%{pgbaseinstdir}/lib/libecpg_compat.so +%{pgbaseinstdir}/lib/libecpg_compat.a +%{pgbaseinstdir}/lib/libpgcommon.a +%{pgbaseinstdir}/lib/libpgport.a +%{pgbaseinstdir}/lib/libpgtypes.so +%{pgbaseinstdir}/lib/libpgtypes.a +%{pgbaseinstdir}/lib/pgxs/* +%{pgbaseinstdir}/lib/pkgconfig/* +%{pgbaseinstdir}/share/man/man1/ecpg.* + +%if %plperl +%files plperl -f pg_plperl.lst +%defattr(-,root,root) +%{pgbaseinstdir}/lib/plperl.so +%{pgbaseinstdir}/share/extension/plperl* +%endif + +%if %pltcl +%files pltcl -f pg_pltcl.lst +%defattr(-,root,root) +%{pgbaseinstdir}/lib/pltcl.so +%{pgbaseinstdir}/bin/pltcl_delmod +%{pgbaseinstdir}/bin/pltcl_listmod +%{pgbaseinstdir}/bin/pltcl_loadmod +%{pgbaseinstdir}/share/unknown.pltcl +%{pgbaseinstdir}/share/extension/pltcl* +%endif + +%if %plpython +%files plpython -f pg_plpython.lst +%defattr(-,root,root) +%{pgbaseinstdir}/lib/plpython*.so +%{pgbaseinstdir}/share/extension/plpython2u* +%{pgbaseinstdir}/share/extension/plpythonu* +%endif + +%if %test +%files test +%defattr(-,postgres,postgres) +%attr(-,postgres,postgres) %{pgbaseinstdir}/lib/test/* +%attr(-,postgres,postgres) %dir %{pgbaseinstdir}/lib/test +%endif + +%changelog +* Tue Mar 18 2014 Devrim GÜNDÜZ - 9.3.4-1PGDG +- Update to 9.3.4, per changes described at: + http://www.postgresql.org/docs/9.3/static/release-9-3-4.html + +* Tue Feb 18 2014 Devrim GÜNDÜZ - 9.3.3-1PGDG +- Update to 9.3.3, per changes described at: + http://www.postgresql.org/docs/9.3/static/release-9-3-3.html + +* Thu Dec 12 2013 Devrim GÜNDÜZ - 9.3.2-2PGDG +- Fix builds when uuid support is disabled, by adding missing conditional. +- Add process name to the status() call in init script. + Patch from Darrin Smart + +* Wed Dec 04 2013 Devrim GÜNDÜZ - 9.3.2-1PGDG +- Update to 9.3.2, per changes described at: + http://www.postgresql.org/docs/9.3/static/release-9-3-2.html + +* Tue Oct 8 2013 Devrim GÜNDÜZ - 9.3.1-1PGDG +- Update to 9.3.1, per changes described at: + http://www.postgresql.org/docs/9.3/static/release-9-3-1.html +- Fix issues with init script, per http://wiki.pgrpms.org/ticket/136. + +* Tue Sep 3 2013 Devrim GÜNDÜZ - 9.3.0-1PGDG +- Update to 9.3.0 + +* Tue Aug 20 2013 Devrim GÜNDÜZ - 9.3rc1-1PGDG +- Update to 9.3 RC1 + +* Wed Jun 26 2013 Jeff Frost - 9.3beta2-1PGDG +- Update to 9.3 beta 2 + +* Tue May 14 2013 Devrim GÜNDÜZ - 9.3beta1-4PGDG +- Revert #90. Per a report in pgsql-bugs mailing list. + +* Mon May 13 2013 Devrim GÜNDÜZ - 9.3beta1-3PGDG +- Fix paths in init script. Per repor from Vibhor Kumar. + +* Sun May 12 2013 Devrim GÜNDÜZ - 9.3beta1-2PGDG +- Support separated xlog directory at initdb. Per suggestion from + Magnus Hagander. Fixes #90. +- Remove hardcoded script names in init script. Fixes #102. +- Add support for pg_ctl promote. Per suggestion from Magnus Hagander. + Fixes #93. +- Set log_line_prefix in default config file to %m. Per suggestion + from Magnus. Fixes #91. + +* Tue May 07 2013 Jeff Frost - 9.3beta1-1PGDG +- Initial cut for 9.3 beta 1 + +* Wed Apr 17 2013 Devrim GÜNDÜZ - 9.2.4-3PGDG +- Fix pid file name in init script, so that it is more suitable for multiple + postmasters. Per suggestion from Andrew Dunstan. Fixes #92. + +* Thu Apr 11 2013 Devrim GÜNDÜZ - 9.2.4-2PGDG +- Add pg_basebackup to $PATH, per #75. + +* Tue Apr 02 2013 Jeff Frost - 9.2.4-1PGDG +- Update to 9.2.4, per changes described at: + http://www.postgresql.org/docs/9.2/static/release-9-2-4.html + which also includes fixes for CVE-2013-1899, CVE-2013-1900, and + CVE-2013-1901. + +* Fri Feb 8 2013 Devrim GÜNDÜZ - 9.2.3-2PGDG +- Fix bug in new installations, that prevents ld.so.conf.d file + to be installed. + +* Wed Feb 6 2013 Devrim GÜNDÜZ - 9.2.3-1PGDG +- Update to 9.2.3, per changes described at: + http://www.postgresql.org/docs/9.2/static/release-9-2-3.html +- Fix -libs issue while installing 9.1+ in parallel. Per various + bug reports. Install ld.so.conf.d file with -libs subpackage. +- Move $pidfile and $lockfile definitions before sysconfig call, + so that they can be included in sysconfig file. + +* Thu Dec 6 2012 Devrim GÜNDÜZ - 9.2.2-1PGDG +- Update to 9.2.2, per changes described at: + http://www.postgresql.org/docs/9.2/static/release-9-2-2.html + +* Thu Sep 20 2012 Devrim GÜNDÜZ - 9.2.1-1PGDG +- Update to 9.2.1, per changes described at: + http://www.postgresql.org/docs/9.2/static/release-9-2-1.html +- Add new functionality: Upgrade from previous version. + Usage: service postgresql-9.2 upgrade +- Fix version number in initdb warning message, per Jose Pedro Oliveira. + +* Thu Sep 6 2012 Devrim GÜNDÜZ - 9.2.0-1PGDG +- Update to 9.2.0 +- Split .control files in appropriate packages. This is a late port + from 9.1 branch. With this patch, pls can be created w/o installing + -contrib subpackage. +- Re-enable -test subpackage, removed accidentally. + +* Tue Aug 28 2012 Devrim GÜNDÜZ - 9.2rc1-2PGDG +- Install linker conf file with alternatives, so that the latest + version will always be used. Fixes #77. + +* Fri Aug 24 2012 Devrim GÜNDÜZ - 9.2rc1-1PGDG +- Update to 9.2 RC1 + +* Thu Aug 16 2012 Devrim GÜNDÜZ - 9.2beta4-1PGDG +- Update to 9.2 beta4, which also includes fixes for CVE-2012-3489 + and CVE-2012-3488. + +* Mon Aug 6 2012 Devrim GÜNDÜZ - 9.2beta3-1PGDG +- Update to 9.2 beta3 + +* Wed Jun 6 2012 Devrim GÜNDÜZ - 9.2beta2-1PGDG +- Update to 9.2 beta2, which also includes fixes for CVE-2012-2143, + CVE-2012-2655. + +* Fri May 18 2012 Devrim GÜNDÜZ - 9.2beta1-1PGDG +- Initial cut for 9.2 Beta 1 + diff --git a/postgresql-logging.patch b/postgresql-logging.patch new file mode 100644 index 0000000..9f0c8c8 --- /dev/null +++ b/postgresql-logging.patch @@ -0,0 +1,54 @@ +--- postgresql-9.3beta1/src/backend/utils/misc/postgresql.conf.sample.old 2013-05-09 08:37:36.803916642 +0300 ++++ postgresql-9.3beta1/src/backend/utils/misc/postgresql.conf.sample 2013-05-09 08:39:29.283912894 +0300 +@@ -295,25 +295,25 @@ + + # - Where to Log - + +-#log_destination = 'stderr' # Valid values are combinations of ++log_destination = 'stderr' # Valid values are combinations of + # stderr, csvlog, syslog, and eventlog, + # depending on platform. csvlog + # requires logging_collector to be on. + + # This is used when logging to stderr: +-#logging_collector = off # Enable capturing of stderr and csvlog ++logging_collector = on # Enable capturing of stderr and csvlog + # into log files. Required to be on for + # csvlogs. + # (change requires restart) + + # These are only used if logging_collector is on: +-#log_directory = 'pg_log' # directory where log files are written, ++log_directory = 'pg_log' # directory where log files are written, + # can be absolute or relative to PGDATA +-#log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' # log file name pattern, ++log_filename = 'postgresql-%a.log' # log file name pattern, + # can include strftime() escapes + #log_file_mode = 0600 # creation mode for log files, + # begin with 0 to use octal notation +-#log_truncate_on_rotation = off # If on, an existing log file with the ++log_truncate_on_rotation = on # If on, an existing log file with the + # same name as the new log file will be + # truncated rather than appended to. + # But such truncation only occurs on +@@ -321,9 +321,9 @@ + # or size-driven rotation. Default is + # off, meaning append to existing files + # in all cases. +-#log_rotation_age = 1d # Automatic rotation of logfiles will ++log_rotation_age = 1d # Automatic rotation of logfiles will + # happen after that time. 0 disables. +-#log_rotation_size = 10MB # Automatic rotation of logfiles will ++log_rotation_size = 0 # Automatic rotation of logfiles will + # happen after that much log output. + # 0 disables. + +@@ -393,7 +393,7 @@ + #log_duration = off + #log_error_verbosity = default # terse, default, or verbose messages + #log_hostname = off +-#log_line_prefix = '' # special values: ++log_line_prefix = '< %m >' # special values: + # %a = application name + # %u = user name + # %d = database name diff --git a/postgresql-perl-rpath.patch b/postgresql-perl-rpath.patch new file mode 100644 index 0000000..f434ba5 --- /dev/null +++ b/postgresql-perl-rpath.patch @@ -0,0 +1,20 @@ +We configure Postgres with --disable-rpath because for the most part we +want to leave it to ldconfig to determine where libraries are. However, +for some reason the Perl package puts libperl.so in a nonstandard place +and doesn't add that place to the ldconfig search path. I think this +is a Perl packaging bug, myself, but apparently it's not going to change. +So work around it by adding an rpath spec to plperl.so (only). +Per bug #162198. + +--- postgresql-9.2beta3/src/pl/plperl/GNUmakefile.old 2012-08-06 15:20:12.568068055 +0300 ++++ postgresql-9.2beta3/src/pl/plperl/GNUmakefile 2012-08-06 15:20:37.256068618 +0300 +@@ -43,6 +43,9 @@ + + SHLIB_LINK = $(perl_embed_ldflags) + ++# Force rpath to be used even though we disable it everywhere else ++SHLIB_LINK += $(rpath) ++ + REGRESS_OPTS = --dbname=$(PL_TESTDB) --load-extension=plperl --load-extension=plperlu + REGRESS = plperl plperl_lc plperl_trigger plperl_shared plperl_elog plperl_util plperl_init plperlu plperl_array + # if Perl can support two interpreters in one backend, diff --git a/postgresql-prefer-ncurses.patch b/postgresql-prefer-ncurses.patch new file mode 100644 index 0000000..ac9df60 --- /dev/null +++ b/postgresql-prefer-ncurses.patch @@ -0,0 +1,18 @@ +This patch hacks the Postgres configure script to prefer libncurses over +libtermcap. This is needed in the current Fedora environment because while +both are available, libtermcap is deprecated and will be removed in the +fairly near future. Because the need for this is short-term, I'm not +going to try to push it upstream; this patch can go away once libtermcap +is no longer present in the standard build environment. + +--- postgresql-9.1alpha3/config/programs.m4.old 2011-01-07 09:44:56.000000000 +0200 ++++ postgresql-9.1alpha3/config/programs.m4 2011-01-07 09:45:14.000000000 +0200 +@@ -125,7 +125,7 @@ + else READLINE_ORDER="-ledit -lreadline" + fi + for pgac_rllib in $READLINE_ORDER ; do +- for pgac_lib in "" " -ltermcap" " -lncurses" " -lcurses" ; do ++ for pgac_lib in "" " -lncurses" " -ltermcap" " -lcurses" ; do + LIBS="${pgac_rllib}${pgac_lib} $pgac_save_LIBS" + AC_TRY_LINK_FUNC([readline], [[ + # Older NetBSD, OpenBSD, and Irix have a broken linker that does not diff --git a/postgresql.init b/postgresql.init new file mode 100644 index 0000000..8f410d5 --- /dev/null +++ b/postgresql.init @@ -0,0 +1,391 @@ +#!/bin/sh +# +# postgresql This is the init script for starting up the PostgreSQL +# server. +# +# chkconfig: - 64 36 +# description: PostgreSQL database server. +# processname: postmaster +# pidfile="/var/run/${NAME}.pid" + +# This script is slightly unusual in that the name of the daemon (postmaster) +# is not the same as the name of the subsystem (postgresql) + +# Version 9.0 Devrim Gunduz +# Get rid of duplicate PGDATA assignment. +# Ensure pgstartup.log gets the right ownership/permissions during initdb + +# Version 9.1 Devrim Gunduz +# Update for 9.1 +# Add an option to initdb to specify locale (default is $LANG): +# service postgresql initdb tr_TR.UTF-8 + +# Version 9.2 Devrim Gunduz +# Update for 9.2 + +# Version 9.2.1 Devrim Gunduz +# Fix version number in initdb warning message, per Jose Pedro Oliveira. +# Add new functionality: Upgrade from previous version. +# Usage: service postgresql-9.2 upgrade + +# Version 9.2.3 Devrim Gunduz +# Fix longstanding bug: Enable pidfile and lockfile variables to be defined +# in sysconfig file. +# Use $pidfile in status(). + +# Version 9.2.4 Devrim Gunduz +# Fix pid file name in init script, so that it is more suitable for +# multiple postmasters. Per suggestion from Andrew Dunstan. Fixes #92. + +# Version 9.3.0 Devrim Gunduz +# Add support for pg_ctl promote. Per suggestion from Magnus Hagander. Fixes #93. +# Remove hardcoded script names in init script. Fixes #102. + +# Version 9.3.1 Devrim Gunduz +# Fix PGPREVMAJORVERSION parameter, per report from Igor Poteryaev. +# Remove extra whitespace in upgrade() code, per report from Igor Poteryaev. + +# Version 9.3.2 Devrim Gunduz +# Add process name to the status() call. Patch from Darrin Smart + +# PGVERSION is the full package version, e.g., 9.3.0 +# Note: the specfile inserts the correct value during package build +PGVERSION=xxxx +# PGMAJORVERSION is major version, e.g., 9.3 (this should match PG_VERSION) +PGMAJORVERSION=`echo "$PGVERSION" | sed 's/^\([0-9]*\.[0-9]*\).*$/\1/'` +PGPREVMAJORVERSION=9.2 + +# Source function library. +INITD=/etc/rc.d/init.d +. $INITD/functions + +# Get function listing for cross-distribution logic. +TYPESET=`typeset -f|grep "declare"` + +# Get network config. +. /etc/sysconfig/network + +# Find the name of the script +NAME=`basename $0` +if [ ${NAME:0:1} = "S" -o ${NAME:0:1} = "K" ] +then + NAME=${NAME:3} +fi + +# For SELinux we need to use 'runuser' not 'su' +if [ -x /sbin/runuser ] +then + SU=runuser +else + SU=su +fi + +# Define variable for locale parameter: +LOCALEPARAMETER=$2 + +# Set defaults for configuration variables +PGENGINE=/usr/pgsql-9.3/bin +PGPORT=5432 +PGDATA=/var/lib/pgsql/9.3/data +PGLOG=/var/lib/pgsql/9.3/pgstartup.log +# Log file for pg_upgrade +PGUPLOG=/var/lib/pgsql/$PGMAJORVERSION/pgupgrade.log + +lockfile="/var/lock/subsys/${NAME}" +pidfile="/var/run/${NAME}.pid" + +# Override defaults from /etc/sysconfig/pgsql if file is present +[ -f /etc/sysconfig/pgsql/${NAME} ] && . /etc/sysconfig/pgsql/${NAME} + +export PGDATA +export PGPORT + +[ -f "$PGENGINE/postmaster" ] || exit 1 + +script_result=0 + +start(){ + [ -x "$PGENGINE/postmaster" ] || exit 5 + + PSQL_START=$"Starting ${NAME} service: " + + # Make sure startup-time log file is valid + if [ ! -e "$PGLOG" -a ! -h "$PGLOG" ] + then + touch "$PGLOG" || exit 1 + chown postgres:postgres "$PGLOG" + chmod go-rwx "$PGLOG" + [ -x /sbin/restorecon ] && /sbin/restorecon "$PGLOG" + fi + + # Check for the PGDATA structure + if [ -f "$PGDATA/PG_VERSION" ] && [ -d "$PGDATA/base" ] + then + # Check version of existing PGDATA + + if [ x`cat "$PGDATA/PG_VERSION"` != x"$PGMAJORVERSION" ] + then + SYSDOCDIR="(Your System's documentation directory)" + if [ -d "/usr/doc/postgresql-$PGVERSION" ] + then + SYSDOCDIR=/usr/doc + fi + if [ -d "/usr/share/doc/postgresql-$PGVERSION" ] + then + SYSDOCDIR=/usr/share/doc + fi + if [ -d "/usr/doc/packages/postgresql-$PGVERSION" ] + then + SYSDOCDIR=/usr/doc/packages + fi + if [ -d "/usr/share/doc/packages/postgresql-$PGVERSION" ] + then + SYSDOCDIR=/usr/share/doc/packages + fi + echo + echo $"An old version of the database format was found." + echo $"You need to upgrade the data format before using PostgreSQL." + echo $"See $SYSDOCDIR/postgresql-$PGVERSION/README.rpm-dist for more information." + exit 1 + fi + else + # No existing PGDATA! Warn the user to initdb it. + + echo + echo "$PGDATA is missing. Use \"service $NAME initdb\" to initialize the cluster first." + echo_failure + echo + exit 1 + fi + + echo -n "$PSQL_START" + $SU -l postgres -c "$PGENGINE/postmaster -p '$PGPORT' -D '$PGDATA' ${PGOPTS} &" >> "$PGLOG" 2>&1 < /dev/null + sleep 2 + pid=`head -n 1 "$PGDATA/postmaster.pid" 2>/dev/null` + if [ "x$pid" != x ] + then + success "$PSQL_START" + touch "$lockfile" + echo $pid > "$pidfile" + echo + else + failure "$PSQL_START" + echo + script_result=1 + fi +} + +stop(){ + echo -n $"Stopping ${NAME} service: " + if [ -e "$lockfile" ] + then + $SU -l postgres -c "$PGENGINE/pg_ctl stop -D '$PGDATA' -s -m fast" > /dev/null 2>&1 < /dev/null + ret=$? + if [ $ret -eq 0 ] + then + echo_success + rm -f "$pidfile" + rm -f "$lockfile" + else + echo_failure + script_result=1 + fi + else + # not running; per LSB standards this is "ok" + echo_success + fi + echo +} + +restart(){ + stop + start +} + +initdb(){ + # If the locale name is specified just after the initdb parameter, use it: + if [ -z $LOCALEPARAMETER ] + then + LOCALE=`echo $LANG` + else + LOCALE=`echo $LOCALEPARAMETER` + fi + LOCALESTRING="--locale=$LOCALE" + + if [ -f "$PGDATA/PG_VERSION" ] + then + echo "Data directory is not empty!" + echo_failure + else + echo -n $"Initializing database: " + if [ ! -e "$PGDATA" -a ! -h "$PGDATA" ] + then + mkdir -p "$PGDATA" || exit 1 + chown postgres:postgres "$PGDATA" + chmod go-rwx "$PGDATA" + fi + # Clean up SELinux tagging for PGDATA + [ -x /sbin/restorecon ] && /sbin/restorecon "$PGDATA" + + # Make sure the startup-time log file is OK, too + if [ ! -e "$PGLOG" -a ! -h "$PGLOG" ] + then + touch "$PGLOG" || exit 1 + chown postgres:postgres "$PGLOG" + chmod go-rwx "$PGLOG" + [ -x /sbin/restorecon ] && /sbin/restorecon "$PGLOG" + fi + + # Initialize the database + $SU -l postgres -c "$PGENGINE/initdb --pgdata='$PGDATA' --auth='ident' $LOCALESTRING" >> "$PGLOG" 2>&1 < /dev/null + + # Create directory for postmaster log + mkdir "$PGDATA/pg_log" + chown postgres:postgres "$PGDATA/pg_log" + chmod go-rwx "$PGDATA/pg_log" + + [ -f "$PGDATA/PG_VERSION" ] && echo_success + [ ! -f "$PGDATA/PG_VERSION" ] && echo_failure + echo + fi +} + +upgrade(){ + +# The second parameter is the new database version, i.e. $PGMAJORVERSION in this case. +# Use "postgresql-$PGMAJORVERSION" service, if not specified. +INIT_SCRIPT="$2" +if [ x"$INIT_SCRIPT" = x ] +then + INIT_SCRIPT=postgresql-$PGMAJORVERSION +fi + +# The third parameter is the old database version, i.e. $PGPREVMAJORVERSION in this case. +# Use "postgresql-$PGPREVMAJORVERSION" service, if not specified. +OLD_INIT_SCRIPT="$3" +if [ x"$OLD_INIT_SCRIPT" = x ] +then + OLD_INIT_SCRIPT=postgresql-$PGPREVMAJORVERSION +fi + +# Find the init script of the new version: +if [ ! -f "/etc/init.d/${INIT_SCRIPT}" ] +then + echo "Could not find init script /etc/init.d/${INIT_SCRIPT}" +fi + +# Find the init script of the old version +if [ ! -f "/etc/init.d/${OLD_INIT_SCRIPT}" ] +then + echo "Could not find init script /etc/init.d/${OLD_INIT_SCRIPT}" + echo "Please install postgresql91-server RPM first." + exit +fi + +# Get port number and data directory of the old instance from the init script +OLDPGDATA=` sed -n 's/^PGDATA=//p' /etc/init.d/postgresql-$PGPREVMAJORVERSION` +OLDPGPORT=`sed -n 's/^PGPORT=//p' /etc/init.d/postgresql-$PGPREVMAJORVERSION` + +# Get port number and data directory of the new instance from the init script +NEWPGDATA=` sed -n 's/^PGDATA=//p' /etc/init.d/postgresql-$PGMAJORVERSION` +NEWPGPORT=`sed -n 's/^PGPORT=//p' /etc/init.d/postgresql-$PGMAJORVERSION` + +if [ ! -x "$PGENGINE/pg_upgrade" ] + then + echo + echo $"Please install the postgresql92-contrib RPM for pg_upgrade command." + echo + exit 5 +fi + +# Perform initdb on the new server +/sbin/service $NAME initdb +RETVAL=$? +if [ $RETVAL -ne 0 ] + then + echo "initdb failed!" + exit 1 +fi + +# Check the clusters first, without changing any data: +su -l postgres -c "$PGENGINE/pg_upgrade -b /usr/pgsql-$PGPREVMAJORVERSION/bin/ -B $PGENGINE/ -d $OLDPGDATA -D $NEWPGDATA -p $OLDPGPORT -P $NEWPGPORT -c" +RETVAL=$? +if [ $RETVAL -eq 0 ] + then + echo "Clusters checked successfully, proceeding with upgrade from $PGPREVMAJORVERSION to $PGMAJORVERSION" + echo "Stopping old cluster" + /sbin/service $OLD_INIT_SCRIPT stop + + # Set up log file for pg_upgrade + rm -f "$PGUPLOG" + touch "$PGUPLOG" || exit 1 + chown postgres:postgres "$PGUPLOG" + chmod go-rwx "$PGUPLOG" + [ -x /sbin/restorecon ] && /sbin/restorecon "$PGUPLOG" + + echo "Performing upgrade" + su -l postgres -c "$PGENGINE/pg_upgrade \ + -b /usr/pgsql-$PGPREVMAJORVERSION/bin/ -B $PGENGINE/ \ + -d $OLDPGDATA -D $NEWPGDATA \ + -p $OLDPGPORT -P $NEWPGPORT" >> "$PGUPLOG" 2>&1 < /dev/null + else + echo "Cluster check failed. Please see the output above." + exit 1 +fi + echo + +exit 0 +} + + +condrestart(){ + [ -e "$lockfile" ] && restart || : +} + +reload(){ + $SU -l postgres -c "$PGENGINE/pg_ctl reload -D '$PGDATA' -s" > /dev/null 2>&1 < /dev/null +} + +promote(){ + $SU -l postgres -c "$PGENGINE/pg_ctl promote -D '$PGDATA' -s" > /dev/null 2>&1 < /dev/null +} + +# See how we were called. +case "$1" in + start) + start + ;; + stop) + stop + ;; + status) + status -p $pidfile $NAME + script_result=$? + ;; + restart) + restart + ;; + initdb) + initdb + ;; + promote) + promote + ;; + upgrade) + upgrade + ;; + condrestart|try-restart) + condrestart + ;; + reload) + reload + ;; + force-reload) + restart + ;; + *) + echo $"Usage: $0 {start|stop|status|restart|upgrade|condrestart|try-restart|reload|force-reload|initdb|promote}" + exit 2 +esac + +exit $script_result diff --git a/postgresql.pam b/postgresql.pam new file mode 100644 index 0000000..1d78594 --- /dev/null +++ b/postgresql.pam @@ -0,0 +1,3 @@ +#%PAM-1.0 +auth include password-auth +account include password-auth diff --git a/rpm-pgsql.patch b/rpm-pgsql.patch new file mode 100644 index 0000000..8d4e3c3 --- /dev/null +++ b/rpm-pgsql.patch @@ -0,0 +1,55 @@ +--- postgresql-8.3devel/src/Makefile.global.in.old 2005-10-13 20:35:18.000000000 +0300 ++++ postgresql-8.3devel/src/Makefile.global.in 2005-10-13 20:37:01.000000000 +0300 +@@ -53,7 +53,7 @@ + # Installation directories + # + # These are set by the equivalent --xxxdir configure options. We +-# append "postgresql" to some of them, if the string does not already ++# append "pgsql" to some of them, if the string does not already + # contain "pgsql" or "postgres", in order to avoid directory clutter. + # + # In a PGXS build, we cannot use the values inserted into Makefile.global +@@ -73,14 +73,14 @@ + datadir := @datadir@ + ifeq "$(findstring pgsql, $(datadir))" "" + ifeq "$(findstring postgres, $(datadir))" "" +-override datadir := $(datadir)/postgresql ++override datadir := $(datadir)/pgsql + endif + endif + + sysconfdir := @sysconfdir@ + ifeq "$(findstring pgsql, $(sysconfdir))" "" + ifeq "$(findstring postgres, $(sysconfdir))" "" +-override sysconfdir := $(sysconfdir)/postgresql ++override sysconfdir := $(sysconfdir)/pgsql + endif + endif + +@@ -89,7 +89,7 @@ + pkglibdir = $(libdir) + ifeq "$(findstring pgsql, $(pkglibdir))" "" + ifeq "$(findstring postgres, $(pkglibdir))" "" +-override pkglibdir := $(pkglibdir)/postgresql ++override pkglibdir := $(pkglibdir)/pgsql + endif + endif + +@@ -98,7 +98,7 @@ + pkgincludedir = $(includedir) + ifeq "$(findstring pgsql, $(pkgincludedir))" "" + ifeq "$(findstring postgres, $(pkgincludedir))" "" +-override pkgincludedir := $(pkgincludedir)/postgresql ++override pkgincludedir := $(pkgincludedir)/pgsql + endif + endif + +@@ -109,7 +109,7 @@ + ifneq (,$(docdir)) + ifeq "$(findstring pgsql, $(docdir))" "" + ifeq "$(findstring postgres, $(docdir))" "" +-override docdir := $(docdir)/postgresql ++override docdir := $(docdir)/pgsql + endif + endif + endif