File 1c_FULL_92-0.22 of Package postgresql92-libs

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..a678b9f
--- /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 <fmgr.h>
+#include <funcapi.h>
+#include <access/heapam.h>
+#include <catalog/pg_type.h>
+#include <catalog/heap.h>
+#include <commands/vacuum.h>
+
+#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 ( !RelationUsesTempNamespace(rel) )
+		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 <oleg@sai.msu.ru>
+	Teodor Sigaev <teodor@sigaev.ru>
+
+
diff --git a/contrib/mchar/data/ch.sql b/contrib/mchar/data/ch.sql
new file mode 100644
index 0000000..4ff7722
--- /dev/null
+++ b/contrib/mchar/data/ch.sql
@@ -0,0 +1,11 @@
+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');
diff --git a/contrib/mchar/data/chvch.sql b/contrib/mchar/data/chvch.sql
new file mode 100644
index 0000000..34ceb75
--- /dev/null
+++ b/contrib/mchar/data/chvch.sql
@@ -0,0 +1,9 @@
+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/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?   
+----------------+--------------
+ <No spaces   > | <No spaces>
+ <One space   > | <One space >
+ <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..d648344
--- /dev/null
+++ b/contrib/mchar/expected/init.out
@@ -0,0 +1,15 @@
+--
+-- 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
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..9e48500
--- /dev/null
+++ b/contrib/mchar/mchar.h
@@ -0,0 +1,63 @@
+#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 {
+	int4	len;
+	int4 	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 {
+	int4	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..70f63ce
--- /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 <utils/array.h>
+
+#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;
+	int4	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;
+	int4		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..82f87a1
--- /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 <rsalz@bbn.com>.
+**  Special thanks to Lars Mathiesen <thorinn@diku.dk> 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. <keith@mtcc.demon.co.uk>
+**
+**  SQL92 lets you specify the escape character by saying
+**  LIKE <pattern> ESCAPE <escape character>. 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;	
+	int4			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(;i<slen;i++) 
+		srest->data[ 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..54ef9d5
--- /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);
+	int4	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);
+	int4	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..f295427
--- /dev/null
+++ b/contrib/mchar/sql/init.sql
@@ -0,0 +1,14 @@
+
+--
+-- 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;
+\i data/chvch.sql
+\i data/ch.sql
+\set ECHO all
+
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 f635208..ee159b6 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1582,6 +1582,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 654ee58..8f0ff30 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -381,6 +381,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);
 
@@ -778,7 +781,7 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
 	 * (Note: this is correct even if we have zero or one live subpath due to
 	 * constraint exclusion.)
 	 */
-	add_path(rel, (Path *) create_append_path(rel, subpaths, NULL));
+	add_path(rel, (Path *) create_append_path(rel, subpaths, NULL, false, NIL));
 
 	/*
 	 * Build unparameterized MergeAppend paths based on the collected list of
@@ -836,7 +839,7 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
 
 		if (ok)
 			add_path(rel, (Path *)
-					 create_append_path(rel, subpaths, required_outer));
+					 create_append_path(rel, subpaths, required_outer, false, NIL));
 	}
 
 	/* Select cheapest paths */
@@ -980,7 +983,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 73ac3d4..f5ff6be 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -38,6 +38,13 @@
 #include "utils/pg_locale.h"
 #include "utils/selfuncs.h"
 
+/*
+ * index support for LIKE mchar
+ */
+#include "fmgr.h"
+#include "utils/lsyscache.h"
+#include "utils/syscache.h"
+#include "parser/parse_type.h"
 
 #define IsBooleanOpfamily(opfamily) \
 	((opfamily) == BOOL_BTREE_FAM_OID || (opfamily) == BOOL_HASH_FAM_OID)
@@ -2809,6 +2816,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  ----
  ****************************************************************************/
 
@@ -3000,9 +3209,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
@@ -3253,6 +3469,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 1a01ae9..ec2e0f2 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -979,7 +979,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 c918c4e..45c2f19 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;j<list_length(ipaths);j++)
+		orderedPaths = lappend(orderedPaths, ipe[j].path);	
+
+	return  orderedPaths;	
+}
+
+static  IndexPath*
+reverseScanDirIdxPath(IndexPath *ipath) {
+	IndexPath   *n = makeNode(IndexPath);
+
+	*n = *ipath;
+
+	n->indexscandir = 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 20a5644..ed90316 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1474,7 +1474,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 99412b6..6c633b8 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -86,7 +86,6 @@ static Node *replace_nestloop_params(PlannerInfo *root, Node *expr);
 static Node *replace_nestloop_params_mutator(Node *node, PlannerInfo *root);
 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);
@@ -664,7 +663,7 @@ static Plan *
 create_append_plan(PlannerInfo *root, AppendPath *best_path)
 {
 	Append	   *plan;
-	List	   *tlist = build_relation_tlist(best_path->path.parent);
+	List	   *tlist;
 	List	   *subplans = NIL;
 	ListCell   *subpaths;
 
@@ -680,6 +679,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_relation_tlist(best_path->path.parent);
 		return (Plan *) make_result(root,
 									tlist,
 									(Node *) list_make1(makeBoolConst(false,
@@ -695,6 +695,11 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
 		subplans = lappend(subplans, create_plan_recurse(root, subpath));
 	}
 
+	if ( best_path->pull_tlist ) {
+		tlist = copyObject( ((Plan*)linitial(subplans))->targetlist );
+	} else
+		tlist = build_relation_tlist(best_path->path.parent);
+
 	plan = make_append(subplans, tlist);
 
 	return (Plan *) plan;
@@ -2777,7 +2782,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 ccd69fc..1451f1b 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -1553,6 +1553,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,
@@ -1562,6 +1566,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 00052f5..d56a853 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -923,7 +923,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;
@@ -932,9 +932,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 44d0b3c..a9f2d9a 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -10166,7 +10166,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;
@@ -10181,7 +10181,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;
@@ -10196,7 +10196,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;
@@ -10211,7 +10211,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;
@@ -10225,7 +10225,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;
@@ -10238,7 +10238,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;
@@ -10251,7 +10251,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;
@@ -10264,7 +10264,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 5e8f562..d9dc280 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -863,6 +863,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 385bae6..5a34308 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);
 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 b6fb8ee..2793516 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -69,6 +69,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
@@ -184,6 +185,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 5a9e677..e73dd1c 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -83,7 +83,7 @@ extern ModifyTable *make_modifytable(CmdType operation, bool canSetTag,
 				 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 2ae991e..bba96d4 100644
--- a/src/test/regress/expected/create_index.out
+++ b/src/test/regress/expected/create_index.out
@@ -2605,18 +2605,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 64e53cb..278904c 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -35,7 +35,7 @@ my @contrib_uselibpgport = (
 	'pg_standby',    'pg_archivecleanup',
 	'pg_test_fsync', 'pg_test_timing',
 	'pg_upgrade',    '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 = {