mirror of
https://abf.rosa.ru/djam/postgresql93.git
synced 2025-02-23 09:12:48 +00:00
9121 lines
220 KiB
Groff
9121 lines
220 KiB
Groff
|
diff --git a/contrib/fasttrun/Makefile b/contrib/fasttrun/Makefile
|
||
|
new file mode 100644
|
||
|
index 0000000..06c47a1
|
||
|
--- /dev/null
|
||
|
+++ b/contrib/fasttrun/Makefile
|
||
|
@@ -0,0 +1,15 @@
|
||
|
+MODULE_big = fasttrun
|
||
|
+OBJS = fasttrun.o
|
||
|
+DATA_built = fasttrun.sql
|
||
|
+DOCS = README.fasttrun
|
||
|
+REGRESS = fasttrun
|
||
|
+
|
||
|
+ifdef USE_PGXS
|
||
|
+PGXS := $(shell pg_config --pgxs)
|
||
|
+include $(PGXS)
|
||
|
+else
|
||
|
+subdir = contrib/fasttrun
|
||
|
+top_builddir = ../..
|
||
|
+include $(top_builddir)/src/Makefile.global
|
||
|
+include $(top_srcdir)/contrib/contrib-global.mk
|
||
|
+endif
|
||
|
diff --git a/contrib/fasttrun/README.fasttrun b/contrib/fasttrun/README.fasttrun
|
||
|
new file mode 100644
|
||
|
index 0000000..4b1dfdc
|
||
|
--- /dev/null
|
||
|
+++ b/contrib/fasttrun/README.fasttrun
|
||
|
@@ -0,0 +1,17 @@
|
||
|
+select fasttruncate('TABLE_NAME');
|
||
|
+
|
||
|
+Function truncates the temporary table and doesn't grow
|
||
|
+pg_class size.
|
||
|
+
|
||
|
+Warning: function isn't transaction safe!
|
||
|
+
|
||
|
+For tests:
|
||
|
+create or replace function f() returns void as $$
|
||
|
+begin
|
||
|
+for i in 1..1000
|
||
|
+loop
|
||
|
+ PERFORM fasttruncate('tt1');
|
||
|
+end loop;
|
||
|
+end;
|
||
|
+$$ language plpgsql;
|
||
|
+
|
||
|
diff --git a/contrib/fasttrun/expected/fasttrun.out b/contrib/fasttrun/expected/fasttrun.out
|
||
|
new file mode 100644
|
||
|
index 0000000..9914e77
|
||
|
--- /dev/null
|
||
|
+++ b/contrib/fasttrun/expected/fasttrun.out
|
||
|
@@ -0,0 +1,115 @@
|
||
|
+\set ECHO none
|
||
|
+create table persist ( a int );
|
||
|
+insert into persist values (1);
|
||
|
+select fasttruncate('persist');
|
||
|
+ERROR: Relation isn't a temporary table
|
||
|
+insert into persist values (2);
|
||
|
+select * from persist order by a;
|
||
|
+ a
|
||
|
+---
|
||
|
+ 1
|
||
|
+ 2
|
||
|
+(2 rows)
|
||
|
+
|
||
|
+create temp table temp1 (a int);
|
||
|
+insert into temp1 values (1);
|
||
|
+BEGIN;
|
||
|
+create temp table temp2 (a int);
|
||
|
+insert into temp2 values (1);
|
||
|
+select * from temp1 order by a;
|
||
|
+ a
|
||
|
+---
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select * from temp2 order by a;
|
||
|
+ a
|
||
|
+---
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+insert into temp1 (select * from generate_series(1,10000));
|
||
|
+insert into temp2 (select * from generate_series(1,11000));
|
||
|
+analyze temp2;
|
||
|
+select relname, relpages>0, reltuples>0 from pg_class where relname in ('temp1', 'temp2') order by relname;
|
||
|
+ relname | ?column? | ?column?
|
||
|
+---------+----------+----------
|
||
|
+ temp1 | f | f
|
||
|
+ temp2 | t | t
|
||
|
+(2 rows)
|
||
|
+
|
||
|
+select fasttruncate('temp1');
|
||
|
+ fasttruncate
|
||
|
+--------------
|
||
|
+
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select fasttruncate('temp2');
|
||
|
+ fasttruncate
|
||
|
+--------------
|
||
|
+
|
||
|
+(1 row)
|
||
|
+
|
||
|
+insert into temp1 values (-2);
|
||
|
+insert into temp2 values (-2);
|
||
|
+select * from temp1 order by a;
|
||
|
+ a
|
||
|
+----
|
||
|
+ -2
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select * from temp2 order by a;
|
||
|
+ a
|
||
|
+----
|
||
|
+ -2
|
||
|
+(1 row)
|
||
|
+
|
||
|
+COMMIT;
|
||
|
+select * from temp1 order by a;
|
||
|
+ a
|
||
|
+----
|
||
|
+ -2
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select * from temp2 order by a;
|
||
|
+ a
|
||
|
+----
|
||
|
+ -2
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select relname, relpages>0, reltuples>0 from pg_class where relname in ('temp1', 'temp2') order by relname;
|
||
|
+ relname | ?column? | ?column?
|
||
|
+---------+----------+----------
|
||
|
+ temp1 | f | f
|
||
|
+ temp2 | f | f
|
||
|
+(2 rows)
|
||
|
+
|
||
|
+select fasttruncate('temp1');
|
||
|
+ fasttruncate
|
||
|
+--------------
|
||
|
+
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select fasttruncate('temp2');
|
||
|
+ fasttruncate
|
||
|
+--------------
|
||
|
+
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select * from temp1 order by a;
|
||
|
+ a
|
||
|
+---
|
||
|
+(0 rows)
|
||
|
+
|
||
|
+select * from temp2 order by a;
|
||
|
+ a
|
||
|
+---
|
||
|
+(0 rows)
|
||
|
+
|
||
|
+select relname, relpages>0, reltuples>0 from pg_class where relname in ('temp1', 'temp2') order by relname;
|
||
|
+ relname | ?column? | ?column?
|
||
|
+---------+----------+----------
|
||
|
+ temp1 | f | f
|
||
|
+ temp2 | f | f
|
||
|
+(2 rows)
|
||
|
+
|
||
|
diff --git a/contrib/fasttrun/fasttrun.c b/contrib/fasttrun/fasttrun.c
|
||
|
new file mode 100644
|
||
|
index 0000000..e9407e3
|
||
|
--- /dev/null
|
||
|
+++ b/contrib/fasttrun/fasttrun.c
|
||
|
@@ -0,0 +1,73 @@
|
||
|
+#include "postgres.h"
|
||
|
+
|
||
|
+#include "access/genam.h"
|
||
|
+#include "access/heapam.h"
|
||
|
+#include "miscadmin.h"
|
||
|
+#include "storage/lmgr.h"
|
||
|
+#include "storage/bufmgr.h"
|
||
|
+#include "catalog/namespace.h"
|
||
|
+#include "utils/lsyscache.h"
|
||
|
+#include "utils/builtins.h"
|
||
|
+#include <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 ( !isTempNamespace(get_rel_namespace(relOid)) )
|
||
|
+ elog(ERROR,"Relation isn't a temporary table");
|
||
|
+
|
||
|
+ heap_truncate(list_make1_oid(relOid));
|
||
|
+
|
||
|
+ if ( rel->rd_rel->relpages > 0 || rel->rd_rel->reltuples > 0 )
|
||
|
+ makeanalyze = true;
|
||
|
+
|
||
|
+ /*
|
||
|
+ * heap_truncate doesn't unlock the table,
|
||
|
+ * so we should unlock it.
|
||
|
+ */
|
||
|
+
|
||
|
+ heap_close(rel, AccessExclusiveLock);
|
||
|
+
|
||
|
+ if ( makeanalyze ) {
|
||
|
+ VacuumStmt *vac = makeNode(VacuumStmt);
|
||
|
+
|
||
|
+ vac->options = VACOPT_ANALYZE;
|
||
|
+ vac->relation = relvar;
|
||
|
+
|
||
|
+ vacuum(vac, relOid, false,
|
||
|
+ GetAccessStrategy(BAS_VACUUM), false, false);
|
||
|
+ }
|
||
|
+
|
||
|
+ PG_RETURN_VOID();
|
||
|
+}
|
||
|
diff --git a/contrib/fasttrun/fasttrun.sql.in b/contrib/fasttrun/fasttrun.sql.in
|
||
|
new file mode 100644
|
||
|
index 0000000..0895c77
|
||
|
--- /dev/null
|
||
|
+++ b/contrib/fasttrun/fasttrun.sql.in
|
||
|
@@ -0,0 +1,8 @@
|
||
|
+BEGIN;
|
||
|
+
|
||
|
+
|
||
|
+CREATE OR REPLACE FUNCTION fasttruncate(text)
|
||
|
+RETURNS void AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C RETURNS NULL ON NULL INPUT VOLATILE;
|
||
|
+
|
||
|
+COMMIT;
|
||
|
diff --git a/contrib/fasttrun/sql/fasttrun.sql b/contrib/fasttrun/sql/fasttrun.sql
|
||
|
new file mode 100644
|
||
|
index 0000000..73beaf4
|
||
|
--- /dev/null
|
||
|
+++ b/contrib/fasttrun/sql/fasttrun.sql
|
||
|
@@ -0,0 +1,50 @@
|
||
|
+\set ECHO none
|
||
|
+\i fasttrun.sql
|
||
|
+\set ECHO all
|
||
|
+
|
||
|
+create table persist ( a int );
|
||
|
+insert into persist values (1);
|
||
|
+select fasttruncate('persist');
|
||
|
+insert into persist values (2);
|
||
|
+select * from persist order by a;
|
||
|
+
|
||
|
+create temp table temp1 (a int);
|
||
|
+insert into temp1 values (1);
|
||
|
+
|
||
|
+BEGIN;
|
||
|
+
|
||
|
+create temp table temp2 (a int);
|
||
|
+insert into temp2 values (1);
|
||
|
+
|
||
|
+select * from temp1 order by a;
|
||
|
+select * from temp2 order by a;
|
||
|
+
|
||
|
+insert into temp1 (select * from generate_series(1,10000));
|
||
|
+insert into temp2 (select * from generate_series(1,11000));
|
||
|
+
|
||
|
+analyze temp2;
|
||
|
+select relname, relpages>0, reltuples>0 from pg_class where relname in ('temp1', 'temp2') order by relname;
|
||
|
+
|
||
|
+select fasttruncate('temp1');
|
||
|
+select fasttruncate('temp2');
|
||
|
+
|
||
|
+insert into temp1 values (-2);
|
||
|
+insert into temp2 values (-2);
|
||
|
+
|
||
|
+select * from temp1 order by a;
|
||
|
+select * from temp2 order by a;
|
||
|
+
|
||
|
+COMMIT;
|
||
|
+
|
||
|
+select * from temp1 order by a;
|
||
|
+select * from temp2 order by a;
|
||
|
+
|
||
|
+select relname, relpages>0, reltuples>0 from pg_class where relname in ('temp1', 'temp2') order by relname;
|
||
|
+
|
||
|
+select fasttruncate('temp1');
|
||
|
+select fasttruncate('temp2');
|
||
|
+
|
||
|
+select * from temp1 order by a;
|
||
|
+select * from temp2 order by a;
|
||
|
+
|
||
|
+select relname, relpages>0, reltuples>0 from pg_class where relname in ('temp1', 'temp2') order by relname;
|
||
|
diff --git a/contrib/fulleq/Makefile b/contrib/fulleq/Makefile
|
||
|
new file mode 100644
|
||
|
index 0000000..bc8bdd2
|
||
|
--- /dev/null
|
||
|
+++ b/contrib/fulleq/Makefile
|
||
|
@@ -0,0 +1,32 @@
|
||
|
+MODULE_big = fulleq
|
||
|
+OBJS = fulleq.o
|
||
|
+DATA_built = fulleq.sql
|
||
|
+DOCS = README.fulleq
|
||
|
+REGRESS = fulleq
|
||
|
+
|
||
|
+ARGTYPE = bool bytea char name int8 int2 int2vector int4 text \
|
||
|
+ oid xid cid oidvector float4 float8 abstime reltime macaddr \
|
||
|
+ inet cidr varchar date time timestamp timestamptz \
|
||
|
+ interval timetz
|
||
|
+
|
||
|
+EXTRA_CLEAN = fulleq.sql.in
|
||
|
+
|
||
|
+ifdef USE_PGXS
|
||
|
+PGXS := $(shell pg_config --pgxs)
|
||
|
+include $(PGXS)
|
||
|
+else
|
||
|
+subdir = contrib/fulleq
|
||
|
+top_builddir = ../..
|
||
|
+include $(top_builddir)/src/Makefile.global
|
||
|
+include $(top_srcdir)/contrib/contrib-global.mk
|
||
|
+endif
|
||
|
+
|
||
|
+fulleq.sql.in: fulleq.sql.in.in
|
||
|
+ echo 'BEGIN;' > $@
|
||
|
+ echo 'SET search_path = public;' >> $@
|
||
|
+ for type in $(ARGTYPE); \
|
||
|
+ do \
|
||
|
+ sed -e "s/ARGTYPE/$$type/g" < $< >> $@; \
|
||
|
+ done
|
||
|
+ echo 'COMMIT;' >> $@
|
||
|
+
|
||
|
diff --git a/contrib/fulleq/README.fulleq b/contrib/fulleq/README.fulleq
|
||
|
new file mode 100644
|
||
|
index 0000000..a677c49
|
||
|
--- /dev/null
|
||
|
+++ b/contrib/fulleq/README.fulleq
|
||
|
@@ -0,0 +1,3 @@
|
||
|
+Introduce operator == which returns true when
|
||
|
+operands are equal or both are nulls.
|
||
|
+
|
||
|
diff --git a/contrib/fulleq/expected/fulleq.out b/contrib/fulleq/expected/fulleq.out
|
||
|
new file mode 100644
|
||
|
index 0000000..4842d8c
|
||
|
--- /dev/null
|
||
|
+++ b/contrib/fulleq/expected/fulleq.out
|
||
|
@@ -0,0 +1,61 @@
|
||
|
+\set ECHO none
|
||
|
+select 4::int == 4;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 4::int == 5;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 4::int == NULL;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select NULL::int == 5;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select NULL::int == NULL;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select '4'::text == '4';
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select '4'::text == '5';
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select '4'::text == NULL;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select NULL::text == '5';
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select NULL::text == NULL;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
diff --git a/contrib/fulleq/fulleq.c b/contrib/fulleq/fulleq.c
|
||
|
new file mode 100644
|
||
|
index 0000000..8e8e17b
|
||
|
--- /dev/null
|
||
|
+++ b/contrib/fulleq/fulleq.c
|
||
|
@@ -0,0 +1,72 @@
|
||
|
+#include "postgres.h"
|
||
|
+#include "fmgr.h"
|
||
|
+#include "access/hash.h"
|
||
|
+#include "utils/builtins.h"
|
||
|
+#include "utils/bytea.h"
|
||
|
+#include "utils/int8.h"
|
||
|
+#include "utils/nabstime.h"
|
||
|
+#include "utils/timestamp.h"
|
||
|
+#include "utils/date.h"
|
||
|
+
|
||
|
+#ifdef PG_MODULE_MAGIC
|
||
|
+PG_MODULE_MAGIC;
|
||
|
+#endif
|
||
|
+
|
||
|
+#define NULLHASHVALUE (-2147483647)
|
||
|
+
|
||
|
+#define FULLEQ_FUNC(type, cmpfunc, hashfunc) \
|
||
|
+PG_FUNCTION_INFO_V1( isfulleq_##type ); \
|
||
|
+Datum isfulleq_##type(PG_FUNCTION_ARGS); \
|
||
|
+Datum \
|
||
|
+isfulleq_##type(PG_FUNCTION_ARGS) { \
|
||
|
+ if ( PG_ARGISNULL(0) && PG_ARGISNULL(1) ) \
|
||
|
+ PG_RETURN_BOOL(true); \
|
||
|
+ else if ( PG_ARGISNULL(0) || PG_ARGISNULL(1) ) \
|
||
|
+ PG_RETURN_BOOL(false); \
|
||
|
+ \
|
||
|
+ PG_RETURN_DATUM( DirectFunctionCall2( cmpfunc, \
|
||
|
+ PG_GETARG_DATUM(0), \
|
||
|
+ PG_GETARG_DATUM(1) \
|
||
|
+ ) ); \
|
||
|
+} \
|
||
|
+ \
|
||
|
+PG_FUNCTION_INFO_V1( fullhash_##type ); \
|
||
|
+Datum fullhash_##type(PG_FUNCTION_ARGS); \
|
||
|
+Datum \
|
||
|
+fullhash_##type(PG_FUNCTION_ARGS) { \
|
||
|
+ if ( PG_ARGISNULL(0) ) \
|
||
|
+ PG_RETURN_INT32(NULLHASHVALUE); \
|
||
|
+ \
|
||
|
+ PG_RETURN_DATUM( DirectFunctionCall1( hashfunc, \
|
||
|
+ PG_GETARG_DATUM(0) \
|
||
|
+ ) ); \
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+FULLEQ_FUNC( bool , booleq , hashchar );
|
||
|
+FULLEQ_FUNC( bytea , byteaeq , hashvarlena );
|
||
|
+FULLEQ_FUNC( char , chareq , hashchar );
|
||
|
+FULLEQ_FUNC( name , nameeq , hashname );
|
||
|
+FULLEQ_FUNC( int8 , int8eq , hashint8 );
|
||
|
+FULLEQ_FUNC( int2 , int2eq , hashint2 );
|
||
|
+FULLEQ_FUNC( int2vector , int2vectoreq , hashint2vector );
|
||
|
+FULLEQ_FUNC( int4 , int4eq , hashint4 );
|
||
|
+FULLEQ_FUNC( text , texteq , hashtext );
|
||
|
+FULLEQ_FUNC( oid , oideq , hashoid );
|
||
|
+FULLEQ_FUNC( xid , xideq , hashint4 );
|
||
|
+FULLEQ_FUNC( cid , cideq , hashint4 );
|
||
|
+FULLEQ_FUNC( oidvector , oidvectoreq , hashoidvector );
|
||
|
+FULLEQ_FUNC( float4 , float4eq , hashfloat4 );
|
||
|
+FULLEQ_FUNC( float8 , float8eq , hashfloat8 );
|
||
|
+FULLEQ_FUNC( abstime , abstimeeq , hashint4 );
|
||
|
+FULLEQ_FUNC( reltime , reltimeeq , hashint4 );
|
||
|
+FULLEQ_FUNC( macaddr , macaddr_eq , hashmacaddr );
|
||
|
+FULLEQ_FUNC( inet , network_eq , hashinet );
|
||
|
+FULLEQ_FUNC( cidr , network_eq , hashinet );
|
||
|
+FULLEQ_FUNC( varchar , texteq , hashtext );
|
||
|
+FULLEQ_FUNC( date , date_eq , hashint4 );
|
||
|
+FULLEQ_FUNC( time , time_eq , hashfloat8 );
|
||
|
+FULLEQ_FUNC( timestamp , timestamp_eq , hashfloat8 );
|
||
|
+FULLEQ_FUNC( timestamptz , timestamp_eq , hashfloat8 );
|
||
|
+FULLEQ_FUNC( interval , interval_eq , interval_hash );
|
||
|
+FULLEQ_FUNC( timetz , timetz_eq , timetz_hash );
|
||
|
diff --git a/contrib/fulleq/fulleq.sql.in.in b/contrib/fulleq/fulleq.sql.in.in
|
||
|
new file mode 100644
|
||
|
index 0000000..55e980e
|
||
|
--- /dev/null
|
||
|
+++ b/contrib/fulleq/fulleq.sql.in.in
|
||
|
@@ -0,0 +1,26 @@
|
||
|
+-- For ARGTYPE
|
||
|
+
|
||
|
+CREATE OR REPLACE FUNCTION isfulleq_ARGTYPE(ARGTYPE, ARGTYPE)
|
||
|
+RETURNS bool AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C CALLED ON NULL INPUT IMMUTABLE;
|
||
|
+
|
||
|
+CREATE OR REPLACE FUNCTION fullhash_ARGTYPE(ARGTYPE)
|
||
|
+RETURNS int4 AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C CALLED ON NULL INPUT IMMUTABLE;
|
||
|
+
|
||
|
+
|
||
|
+CREATE OPERATOR == (
|
||
|
+ LEFTARG = ARGTYPE,
|
||
|
+ RIGHTARG = ARGTYPE,
|
||
|
+ PROCEDURE = isfulleq_ARGTYPE,
|
||
|
+ COMMUTATOR = '==',
|
||
|
+ RESTRICT = eqsel,
|
||
|
+ JOIN = eqjoinsel,
|
||
|
+ HASHES
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR CLASS ARGTYPE_fill_ops
|
||
|
+ FOR TYPE ARGTYPE USING hash AS
|
||
|
+ OPERATOR 1 ==,
|
||
|
+ FUNCTION 1 fullhash_ARGTYPE(ARGTYPE);
|
||
|
+
|
||
|
diff --git a/contrib/fulleq/sql/fulleq.sql b/contrib/fulleq/sql/fulleq.sql
|
||
|
new file mode 100644
|
||
|
index 0000000..02b192c
|
||
|
--- /dev/null
|
||
|
+++ b/contrib/fulleq/sql/fulleq.sql
|
||
|
@@ -0,0 +1,16 @@
|
||
|
+\set ECHO none
|
||
|
+\i fulleq.sql
|
||
|
+\set ECHO all
|
||
|
+
|
||
|
+select 4::int == 4;
|
||
|
+select 4::int == 5;
|
||
|
+select 4::int == NULL;
|
||
|
+select NULL::int == 5;
|
||
|
+select NULL::int == NULL;
|
||
|
+
|
||
|
+select '4'::text == '4';
|
||
|
+select '4'::text == '5';
|
||
|
+select '4'::text == NULL;
|
||
|
+select NULL::text == '5';
|
||
|
+select NULL::text == NULL;
|
||
|
+
|
||
|
diff --git a/contrib/mchar/Changes b/contrib/mchar/Changes
|
||
|
new file mode 100644
|
||
|
index 0000000..b597ee7
|
||
|
--- /dev/null
|
||
|
+++ b/contrib/mchar/Changes
|
||
|
@@ -0,0 +1,19 @@
|
||
|
+0.17 add == operation:
|
||
|
+ a == b => ( a = b or a is null and b is null )
|
||
|
+0.16 fix pg_dump - now mchar in pg_catalog scheme, not public
|
||
|
+ fix bug in mvarchar_substr()
|
||
|
+0.15 add upper()/lower()
|
||
|
+0.14 Add ESCAPE for LIKE, SIMILAR TO [ESCAPE], POSIX regexp
|
||
|
+0.13 Outer binary format is now different from
|
||
|
+ inner: it's just a UTF-16 string
|
||
|
+0.12 Fix copy binary
|
||
|
+0.11 Force UTF-8 convertor if server_encoding='UTF8'
|
||
|
+0.10 add (mchar|mvarchar)_(send|recv) functions to
|
||
|
+ allow binary copying. Note: that functions
|
||
|
+ don't recode values.
|
||
|
+0.9 index support for like, improve recoding functions
|
||
|
+0.8 initial suport for like optimizioation with index:
|
||
|
+ still thres no algo to find the nearest greater string
|
||
|
+0.7 hash indexes and enable a hash joins
|
||
|
+0.6 implicit casting mchar-mvarchar
|
||
|
+ cross type comparison operations
|
||
|
diff --git a/contrib/mchar/Makefile b/contrib/mchar/Makefile
|
||
|
new file mode 100644
|
||
|
index 0000000..27302df
|
||
|
--- /dev/null
|
||
|
+++ b/contrib/mchar/Makefile
|
||
|
@@ -0,0 +1,27 @@
|
||
|
+MODULE_big = mchar
|
||
|
+OBJS = mchar_io.o mchar_proc.o mchar_op.o mchar_recode.o \
|
||
|
+ mchar_like.o
|
||
|
+DATA_built = mchar.sql
|
||
|
+DATA = uninstall_mchar.sql
|
||
|
+DOCS = README.mchar
|
||
|
+REGRESS = init mchar mvarchar mm like compat
|
||
|
+
|
||
|
+PG_CPPFLAGS=-I/usr/local/include
|
||
|
+
|
||
|
+ifdef USE_PGXS
|
||
|
+PGXS := $(shell pg_config --pgxs)
|
||
|
+include $(PGXS)
|
||
|
+else
|
||
|
+subdir = contrib/mchar
|
||
|
+top_builddir = ../..
|
||
|
+include $(top_builddir)/src/Makefile.global
|
||
|
+include $(top_srcdir)/contrib/contrib-global.mk
|
||
|
+endif
|
||
|
+
|
||
|
+ifeq ($(PORTNAME),win32)
|
||
|
+ICUNAME=icuin
|
||
|
+else
|
||
|
+ICUNAME=icui18n
|
||
|
+endif
|
||
|
+
|
||
|
+SHLIB_LINK += -L/usr/local/lib -licuuc -l$(ICUNAME) -Wl,-rpath,'$$ORIGIN'
|
||
|
diff --git a/contrib/mchar/README.mchar b/contrib/mchar/README.mchar
|
||
|
new file mode 100644
|
||
|
index 0000000..479a7d1
|
||
|
--- /dev/null
|
||
|
+++ b/contrib/mchar/README.mchar
|
||
|
@@ -0,0 +1,20 @@
|
||
|
+MCHAR & VARCHAR
|
||
|
+ type modifier
|
||
|
+ length()
|
||
|
+ substr(str, pos[, length])
|
||
|
+ || - concatenation with any (mchar,mvarchar) arguments
|
||
|
+ < <= = >= > - case-insensitive comparisons (libICU)
|
||
|
+ &< &<= &= &>= &> - case-sensitive comparisons (libICU)
|
||
|
+ implicit casting mchar<->mvarchar
|
||
|
+ B-tree and hash index
|
||
|
+ LIKE [ESCAPE]
|
||
|
+ SIMILAR TO [ESCAPE]
|
||
|
+ ~ (POSIX regexp)
|
||
|
+ index support for LIKE
|
||
|
+
|
||
|
+
|
||
|
+Authors:
|
||
|
+ Oleg Bartunov <oleg@sai.msu.ru>
|
||
|
+ Teodor Sigaev <teodor@sigaev.ru>
|
||
|
+
|
||
|
+
|
||
|
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..19aa916
|
||
|
--- /dev/null
|
||
|
+++ b/contrib/mchar/expected/init.out
|
||
|
@@ -0,0 +1,32 @@
|
||
|
+--
|
||
|
+-- first, define the datatype. Turn off echoing so that expected file
|
||
|
+-- does not depend on contents of mchar.sql.
|
||
|
+--
|
||
|
+\set ECHO none
|
||
|
+psql:mchar.sql:20: NOTICE: type "mchar" is not yet defined
|
||
|
+DETAIL: Creating a shell type definition.
|
||
|
+psql:mchar.sql:25: NOTICE: argument type mchar is only a shell
|
||
|
+psql:mchar.sql:30: NOTICE: argument type mchar is only a shell
|
||
|
+psql:mchar.sql:35: NOTICE: return type mchar is only a shell
|
||
|
+psql:mchar.sql:59: NOTICE: type "mvarchar" is not yet defined
|
||
|
+DETAIL: Creating a shell type definition.
|
||
|
+psql:mchar.sql:64: NOTICE: argument type mvarchar is only a shell
|
||
|
+psql:mchar.sql:69: NOTICE: argument type mvarchar is only a shell
|
||
|
+psql:mchar.sql:74: NOTICE: return type mvarchar is only a shell
|
||
|
+create table ch (
|
||
|
+ chcol mchar(32)
|
||
|
+) without oids;
|
||
|
+insert into ch values('abcd');
|
||
|
+insert into ch values('AbcD');
|
||
|
+insert into ch values('abcz');
|
||
|
+insert into ch values('defg');
|
||
|
+insert into ch values('dEfg');
|
||
|
+insert into ch values('ee');
|
||
|
+insert into ch values('Ee');
|
||
|
+create table chvch (
|
||
|
+ ch mchar(12),
|
||
|
+ vch mvarchar(12)
|
||
|
+) without oids;
|
||
|
+insert into chvch values('No spaces', 'No spaces');
|
||
|
+insert into chvch values('One space ', 'One space ');
|
||
|
+insert into chvch values('1 space', '1 space ');
|
||
|
diff --git a/contrib/mchar/expected/like.out b/contrib/mchar/expected/like.out
|
||
|
new file mode 100644
|
||
|
index 0000000..3a57082
|
||
|
--- /dev/null
|
||
|
+++ b/contrib/mchar/expected/like.out
|
||
|
@@ -0,0 +1,791 @@
|
||
|
+-- simplest examples
|
||
|
+-- E061-04 like predicate
|
||
|
+SELECT 'hawkeye'::mchar LIKE 'h%' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'hawkeye'::mchar NOT LIKE 'h%' AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'hawkeye'::mchar LIKE 'H%' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'hawkeye'::mchar NOT LIKE 'H%' AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'hawkeye'::mchar LIKE 'indio%' AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'hawkeye'::mchar NOT LIKE 'indio%' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'hawkeye'::mchar LIKE 'h%eye' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'hawkeye'::mchar NOT LIKE 'h%eye' AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'indio'::mchar LIKE '_ndio' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'indio'::mchar NOT LIKE '_ndio' AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'indio'::mchar LIKE 'in__o' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'indio'::mchar NOT LIKE 'in__o' AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'indio'::mchar LIKE 'in_o' AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'indio'::mchar NOT LIKE 'in_o' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'hawkeye'::mvarchar LIKE 'h%' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'hawkeye'::mvarchar NOT LIKE 'h%' AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'hawkeye'::mvarchar LIKE 'H%' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'hawkeye'::mvarchar NOT LIKE 'H%' AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'hawkeye'::mvarchar LIKE 'indio%' AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'hawkeye'::mvarchar NOT LIKE 'indio%' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'hawkeye'::mvarchar LIKE 'h%eye' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'hawkeye'::mvarchar NOT LIKE 'h%eye' AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'indio'::mvarchar LIKE '_ndio' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'indio'::mvarchar NOT LIKE '_ndio' AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'indio'::mvarchar LIKE 'in__o' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'indio'::mvarchar NOT LIKE 'in__o' AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'indio'::mvarchar LIKE 'in_o' AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'indio'::mvarchar NOT LIKE 'in_o' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+-- unused escape character
|
||
|
+SELECT 'hawkeye'::mchar LIKE 'h%'::mchar ESCAPE '#' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'hawkeye'::mchar NOT LIKE 'h%'::mchar ESCAPE '#' AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'indio'::mchar LIKE 'ind_o'::mchar ESCAPE '$' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'indio'::mchar NOT LIKE 'ind_o'::mchar ESCAPE '$' AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+-- escape character
|
||
|
+-- E061-05 like predicate with escape clause
|
||
|
+SELECT 'h%'::mchar LIKE 'h#%'::mchar ESCAPE '#' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'h%'::mchar NOT LIKE 'h#%'::mchar ESCAPE '#' AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'h%wkeye'::mchar LIKE 'h#%'::mchar ESCAPE '#' AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'h%wkeye'::mchar NOT LIKE 'h#%'::mchar ESCAPE '#' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'h%wkeye'::mchar LIKE 'h#%%'::mchar ESCAPE '#' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'h%wkeye'::mchar NOT LIKE 'h#%%'::mchar ESCAPE '#' AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'h%awkeye'::mchar LIKE 'h#%a%k%e'::mchar ESCAPE '#' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'h%awkeye'::mchar NOT LIKE 'h#%a%k%e'::mchar ESCAPE '#' AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'indio'::mchar LIKE '_ndio'::mchar ESCAPE '$' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'indio'::mchar NOT LIKE '_ndio'::mchar ESCAPE '$' AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'i_dio'::mchar LIKE 'i$_d_o'::mchar ESCAPE '$' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'i_dio'::mchar NOT LIKE 'i$_d_o'::mchar ESCAPE '$' AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'i_dio'::mchar LIKE 'i$_nd_o'::mchar ESCAPE '$' AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'i_dio'::mchar NOT LIKE 'i$_nd_o'::mchar ESCAPE '$' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'i_dio'::mchar LIKE 'i$_d%o'::mchar ESCAPE '$' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'i_dio'::mchar NOT LIKE 'i$_d%o'::mchar ESCAPE '$' AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+-- escape character same as pattern character
|
||
|
+SELECT 'maca'::mchar LIKE 'm%aca' ESCAPE '%'::mchar AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'maca'::mchar NOT LIKE 'm%aca' ESCAPE '%'::mchar AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'ma%a'::mchar LIKE 'm%a%%a' ESCAPE '%'::mchar AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'ma%a'::mchar NOT LIKE 'm%a%%a' ESCAPE '%'::mchar AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'bear'::mchar LIKE 'b_ear' ESCAPE '_'::mchar AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'bear'::mchar NOT LIKE 'b_ear'::mchar ESCAPE '_' AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'be_r'::mchar LIKE 'b_e__r' ESCAPE '_'::mchar AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'be_r'::mchar NOT LIKE 'b_e__r' ESCAPE '_'::mchar AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'be_r'::mchar LIKE '__e__r' ESCAPE '_'::mchar AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'be_r'::mchar NOT LIKE '__e__r'::mchar ESCAPE '_' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+-- unused escape character
|
||
|
+SELECT 'hawkeye'::mvarchar LIKE 'h%'::mvarchar ESCAPE '#' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'hawkeye'::mvarchar NOT LIKE 'h%'::mvarchar ESCAPE '#' AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'indio'::mvarchar LIKE 'ind_o'::mvarchar ESCAPE '$' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'indio'::mvarchar NOT LIKE 'ind_o'::mvarchar ESCAPE '$' AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+-- escape character
|
||
|
+-- E061-05 like predicate with escape clause
|
||
|
+SELECT 'h%'::mvarchar LIKE 'h#%'::mvarchar ESCAPE '#' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'h%'::mvarchar NOT LIKE 'h#%'::mvarchar ESCAPE '#' AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'h%wkeye'::mvarchar LIKE 'h#%'::mvarchar ESCAPE '#' AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'h%wkeye'::mvarchar NOT LIKE 'h#%'::mvarchar ESCAPE '#' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'h%wkeye'::mvarchar LIKE 'h#%%'::mvarchar ESCAPE '#' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'h%wkeye'::mvarchar NOT LIKE 'h#%%'::mvarchar ESCAPE '#' AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'h%awkeye'::mvarchar LIKE 'h#%a%k%e'::mvarchar ESCAPE '#' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'h%awkeye'::mvarchar NOT LIKE 'h#%a%k%e'::mvarchar ESCAPE '#' AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'indio'::mvarchar LIKE '_ndio'::mvarchar ESCAPE '$' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'indio'::mvarchar NOT LIKE '_ndio'::mvarchar ESCAPE '$' AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'i_dio'::mvarchar LIKE 'i$_d_o'::mvarchar ESCAPE '$' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'i_dio'::mvarchar NOT LIKE 'i$_d_o'::mvarchar ESCAPE '$' AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'i_dio'::mvarchar LIKE 'i$_nd_o'::mvarchar ESCAPE '$' AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'i_dio'::mvarchar NOT LIKE 'i$_nd_o'::mvarchar ESCAPE '$' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'i_dio'::mvarchar LIKE 'i$_d%o'::mvarchar ESCAPE '$' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'i_dio'::mvarchar NOT LIKE 'i$_d%o'::mvarchar ESCAPE '$' AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+-- escape character same as pattern character
|
||
|
+SELECT 'maca'::mvarchar LIKE 'm%aca' ESCAPE '%'::mvarchar AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'maca'::mvarchar NOT LIKE 'm%aca' ESCAPE '%'::mvarchar AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'ma%a'::mvarchar LIKE 'm%a%%a' ESCAPE '%'::mvarchar AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'ma%a'::mvarchar NOT LIKE 'm%a%%a' ESCAPE '%'::mvarchar AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'bear'::mvarchar LIKE 'b_ear' ESCAPE '_'::mvarchar AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'bear'::mvarchar NOT LIKE 'b_ear'::mvarchar ESCAPE '_' AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'be_r'::mvarchar LIKE 'b_e__r' ESCAPE '_'::mvarchar AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'be_r'::mvarchar NOT LIKE 'b_e__r' ESCAPE '_'::mvarchar AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'be_r'::mvarchar LIKE '__e__r' ESCAPE '_'::mvarchar AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'be_r'::mvarchar NOT LIKE '__e__r'::mvarchar ESCAPE '_' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+-- similar to
|
||
|
+SELECT 'abc'::mchar SIMILAR TO 'abc'::mchar AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'abc'::mchar SIMILAR TO 'a'::mchar AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'abc'::mchar SIMILAR TO '%(b|d)%'::mchar AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'abc'::mchar SIMILAR TO '(b|c)%'::mchar AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'h%'::mchar SIMILAR TO 'h#%'::mchar AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'h%'::mchar SIMILAR TO 'h#%'::mchar ESCAPE '#' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'abc'::mvarchar SIMILAR TO 'abc'::mvarchar AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'abc'::mvarchar SIMILAR TO 'a'::mvarchar AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'abc'::mvarchar SIMILAR TO '%(b|d)%'::mvarchar AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'abc'::mvarchar SIMILAR TO '(b|c)%'::mvarchar AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'h%'::mvarchar SIMILAR TO 'h#%'::mvarchar AS "false";
|
||
|
+ false
|
||
|
+-------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+SELECT 'h%'::mvarchar SIMILAR TO 'h#%'::mvarchar ESCAPE '#' AS "true";
|
||
|
+ true
|
||
|
+------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+-- index support
|
||
|
+SELECT * from ch where chcol like 'aB_d' order by chcol using &<;
|
||
|
+ chcol
|
||
|
+----------------------------------
|
||
|
+ AbcD
|
||
|
+ abcd
|
||
|
+(2 rows)
|
||
|
+
|
||
|
+SELECT * from ch where chcol like 'aB%d' order by chcol using &<;
|
||
|
+ chcol
|
||
|
+----------------------------------
|
||
|
+ AbcD
|
||
|
+ abcd
|
||
|
+(2 rows)
|
||
|
+
|
||
|
+SELECT * from ch where chcol like 'aB%' order by chcol using &<;
|
||
|
+ chcol
|
||
|
+----------------------------------
|
||
|
+ AbcD
|
||
|
+ abcd
|
||
|
+ abcz
|
||
|
+(3 rows)
|
||
|
+
|
||
|
+SELECT * from ch where chcol like '%BC%' order by chcol using &<;
|
||
|
+ chcol
|
||
|
+----------------------------------
|
||
|
+ AbcD
|
||
|
+ abcd
|
||
|
+ abcz
|
||
|
+(3 rows)
|
||
|
+
|
||
|
+set enable_seqscan = off;
|
||
|
+SELECT * from ch where chcol like 'aB_d' order by chcol using &<;
|
||
|
+ chcol
|
||
|
+----------------------------------
|
||
|
+ AbcD
|
||
|
+ abcd
|
||
|
+(2 rows)
|
||
|
+
|
||
|
+SELECT * from ch where chcol like 'aB%d' order by chcol using &<;
|
||
|
+ chcol
|
||
|
+----------------------------------
|
||
|
+ AbcD
|
||
|
+ abcd
|
||
|
+(2 rows)
|
||
|
+
|
||
|
+SELECT * from ch where chcol like 'aB%' order by chcol using &<;
|
||
|
+ chcol
|
||
|
+----------------------------------
|
||
|
+ AbcD
|
||
|
+ abcd
|
||
|
+ abcz
|
||
|
+(3 rows)
|
||
|
+
|
||
|
+SELECT * from ch where chcol like '%BC%' order by chcol using &<;
|
||
|
+ chcol
|
||
|
+----------------------------------
|
||
|
+ AbcD
|
||
|
+ abcd
|
||
|
+ abcz
|
||
|
+(3 rows)
|
||
|
+
|
||
|
+set enable_seqscan = on;
|
||
|
+create table testt (f1 mchar(10));
|
||
|
+insert into testt values ('Abc-000001');
|
||
|
+insert into testt values ('Abc-000002');
|
||
|
+insert into testt values ('0000000001');
|
||
|
+insert into testt values ('0000000002');
|
||
|
+select f1 from testt where f1::mvarchar like E'Abc\\-%'::mvarchar;
|
||
|
+ f1
|
||
|
+------------
|
||
|
+ Abc-000001
|
||
|
+ Abc-000002
|
||
|
+(2 rows)
|
||
|
+
|
||
|
+select * from testt where f1::mchar like E'Abc\\-%'::mchar;
|
||
|
+ f1
|
||
|
+------------
|
||
|
+ Abc-000001
|
||
|
+ Abc-000002
|
||
|
+(2 rows)
|
||
|
+
|
||
|
+create index testindex on testt(f1);
|
||
|
+set enable_seqscan=off;
|
||
|
+select f1 from testt where f1::mvarchar like E'Abc\\-%'::mvarchar;
|
||
|
+ f1
|
||
|
+------------
|
||
|
+ Abc-000001
|
||
|
+ Abc-000002
|
||
|
+(2 rows)
|
||
|
+
|
||
|
+select * from testt where f1::mchar like E'Abc\\-%'::mchar;
|
||
|
+ f1
|
||
|
+------------
|
||
|
+ Abc-000001
|
||
|
+ Abc-000002
|
||
|
+(2 rows)
|
||
|
+
|
||
|
+set enable_seqscan = on;
|
||
|
+drop table testt;
|
||
|
+create table testt (f1 mvarchar(10));
|
||
|
+insert into testt values ('Abc-000001');
|
||
|
+insert into testt values ('Abc-000002');
|
||
|
+insert into testt values ('0000000001');
|
||
|
+insert into testt values ('0000000002');
|
||
|
+select f1 from testt where f1::mvarchar like E'Abc\\-%'::mvarchar;
|
||
|
+ f1
|
||
|
+------------
|
||
|
+ Abc-000001
|
||
|
+ Abc-000002
|
||
|
+(2 rows)
|
||
|
+
|
||
|
+select * from testt where f1::mchar like E'Abc\\-%'::mchar;
|
||
|
+ f1
|
||
|
+------------
|
||
|
+ Abc-000001
|
||
|
+ Abc-000002
|
||
|
+(2 rows)
|
||
|
+
|
||
|
+select * from testt where f1::mchar like E'Abc\\- %'::mchar;
|
||
|
+ f1
|
||
|
+------------
|
||
|
+ Abc-000001
|
||
|
+ Abc-000002
|
||
|
+(2 rows)
|
||
|
+
|
||
|
+select * from testt where f1::mchar like E' %'::mchar;
|
||
|
+ f1
|
||
|
+------------
|
||
|
+ Abc-000001
|
||
|
+ Abc-000002
|
||
|
+ 0000000001
|
||
|
+ 0000000002
|
||
|
+(4 rows)
|
||
|
+
|
||
|
+create index testindex on testt(f1);
|
||
|
+set enable_seqscan=off;
|
||
|
+select f1 from testt where f1::mvarchar like E'Abc\\-%'::mvarchar;
|
||
|
+ f1
|
||
|
+------------
|
||
|
+ Abc-000001
|
||
|
+ Abc-000002
|
||
|
+(2 rows)
|
||
|
+
|
||
|
+select * from testt where f1::mchar like E'Abc\\-%'::mchar;
|
||
|
+ f1
|
||
|
+------------
|
||
|
+ Abc-000001
|
||
|
+ Abc-000002
|
||
|
+(2 rows)
|
||
|
+
|
||
|
+select * from testt where f1::mchar like E'Abc\\- %'::mchar;
|
||
|
+ f1
|
||
|
+------------
|
||
|
+ Abc-000001
|
||
|
+ Abc-000002
|
||
|
+(2 rows)
|
||
|
+
|
||
|
+select * from testt where f1::mchar like E' %'::mchar;
|
||
|
+ f1
|
||
|
+------------
|
||
|
+ 0000000001
|
||
|
+ 0000000002
|
||
|
+ Abc-000001
|
||
|
+ Abc-000002
|
||
|
+(4 rows)
|
||
|
+
|
||
|
+set enable_seqscan = on;
|
||
|
+drop table testt;
|
||
|
+CREATE TABLE test ( code mchar(5) NOT NULL );
|
||
|
+insert into test values('1111 ');
|
||
|
+insert into test values('111 ');
|
||
|
+insert into test values('11 ');
|
||
|
+insert into test values('1 ');
|
||
|
+SELECT * FROM test WHERE code LIKE ('% ');
|
||
|
+ code
|
||
|
+-------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
diff --git a/contrib/mchar/expected/mchar.out b/contrib/mchar/expected/mchar.out
|
||
|
new file mode 100644
|
||
|
index 0000000..c587424
|
||
|
--- /dev/null
|
||
|
+++ b/contrib/mchar/expected/mchar.out
|
||
|
@@ -0,0 +1,363 @@
|
||
|
+-- I/O tests
|
||
|
+select '1'::mchar;
|
||
|
+ mchar
|
||
|
+-------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select '2 '::mchar;
|
||
|
+ mchar
|
||
|
+-------
|
||
|
+ 2
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select '10 '::mchar;
|
||
|
+ mchar
|
||
|
+-------
|
||
|
+ 10
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select '1'::mchar(2);
|
||
|
+ mchar
|
||
|
+-------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select '2 '::mchar(2);
|
||
|
+ mchar
|
||
|
+-------
|
||
|
+ 2
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select '3 '::mchar(2);
|
||
|
+ mchar
|
||
|
+-------
|
||
|
+ 3
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select '10 '::mchar(2);
|
||
|
+ mchar
|
||
|
+-------
|
||
|
+ 10
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select ' '::mchar(10);
|
||
|
+ mchar
|
||
|
+------------
|
||
|
+
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select ' '::mchar;
|
||
|
+ mchar
|
||
|
+-------
|
||
|
+
|
||
|
+(1 row)
|
||
|
+
|
||
|
+-- operations & functions
|
||
|
+select length('1'::mchar);
|
||
|
+ length
|
||
|
+--------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select length('2 '::mchar);
|
||
|
+ length
|
||
|
+--------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select length('10 '::mchar);
|
||
|
+ length
|
||
|
+--------
|
||
|
+ 2
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select length('1'::mchar(2));
|
||
|
+ length
|
||
|
+--------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select length('2 '::mchar(2));
|
||
|
+ length
|
||
|
+--------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select length('3 '::mchar(2));
|
||
|
+ length
|
||
|
+--------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select length('10 '::mchar(2));
|
||
|
+ length
|
||
|
+--------
|
||
|
+ 2
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select length(' '::mchar(10));
|
||
|
+ length
|
||
|
+--------
|
||
|
+ 0
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select length(' '::mchar);
|
||
|
+ length
|
||
|
+--------
|
||
|
+ 0
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mchar(10) || '>'::mchar(10);
|
||
|
+ ?column?
|
||
|
+----------------------
|
||
|
+ asd >
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select length('asd'::mchar(10) || '>'::mchar(10));
|
||
|
+ length
|
||
|
+--------
|
||
|
+ 11
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mchar(2) || '>'::mchar(10);
|
||
|
+ ?column?
|
||
|
+--------------
|
||
|
+ as>
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select length('asd'::mchar(2) || '>'::mchar(10));
|
||
|
+ length
|
||
|
+--------
|
||
|
+ 3
|
||
|
+(1 row)
|
||
|
+
|
||
|
+-- Comparisons
|
||
|
+select 'asdf'::mchar = 'aSdf'::mchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mchar = 'aSdf '::mchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mchar = 'aSdf 1'::mchar(4);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mchar = 'aSdf 1'::mchar(5);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mchar = 'aSdf 1'::mchar(6);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mchar(3) = 'aSdf 1'::mchar(5);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mchar(3) = 'aSdf 1'::mchar(3);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mchar < 'aSdf'::mchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mchar < 'aSdf '::mchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mchar < 'aSdf 1'::mchar(4);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mchar < 'aSdf 1'::mchar(5);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mchar < 'aSdf 1'::mchar(6);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mchar <= 'aSdf'::mchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mchar <= 'aSdf '::mchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mchar <= 'aSdf 1'::mchar(4);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mchar <= 'aSdf 1'::mchar(5);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mchar <= 'aSdf 1'::mchar(6);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mchar >= 'aSdf'::mchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mchar >= 'aSdf '::mchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mchar >= 'aSdf 1'::mchar(4);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mchar >= 'aSdf 1'::mchar(5);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mchar >= 'aSdf 1'::mchar(6);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mchar > 'aSdf'::mchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mchar > 'aSdf '::mchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mchar > 'aSdf 1'::mchar(4);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mchar > 'aSdf 1'::mchar(5);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mchar > 'aSdf 1'::mchar(6);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select max(ch) from chvch;
|
||
|
+ max
|
||
|
+--------------
|
||
|
+ One space
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select min(ch) from chvch;
|
||
|
+ min
|
||
|
+--------------
|
||
|
+ 1 space
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select substr('1234567890'::mchar, 3) = '34567890' as "34567890";
|
||
|
+ 34567890
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select substr('1234567890'::mchar, 4, 3) = '456' as "456";
|
||
|
+ 456
|
||
|
+-----
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select lower('asdfASDF'::mchar);
|
||
|
+ lower
|
||
|
+----------
|
||
|
+ asdfasdf
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select upper('asdfASDF'::mchar);
|
||
|
+ upper
|
||
|
+----------
|
||
|
+ ASDFASDF
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mchar == 'aSd'::mchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mchar == 'aCd'::mchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mchar == NULL;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select NULL == 'aCd'::mchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select NULL::mchar == NULL;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
diff --git a/contrib/mchar/expected/mm.out b/contrib/mchar/expected/mm.out
|
||
|
new file mode 100644
|
||
|
index 0000000..fa2e924
|
||
|
--- /dev/null
|
||
|
+++ b/contrib/mchar/expected/mm.out
|
||
|
@@ -0,0 +1,805 @@
|
||
|
+select 'asd'::mchar::mvarchar;
|
||
|
+ mvarchar
|
||
|
+----------
|
||
|
+ asd
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mchar::mvarchar;
|
||
|
+ mvarchar
|
||
|
+----------
|
||
|
+ asd
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mchar(2)::mvarchar;
|
||
|
+ mvarchar
|
||
|
+----------
|
||
|
+ as
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mchar(2)::mvarchar;
|
||
|
+ mvarchar
|
||
|
+----------
|
||
|
+ as
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mchar(5)::mvarchar;
|
||
|
+ mvarchar
|
||
|
+----------
|
||
|
+ asd
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mchar(5)::mvarchar;
|
||
|
+ mvarchar
|
||
|
+----------
|
||
|
+ asd
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mchar::mvarchar(2);
|
||
|
+ mvarchar
|
||
|
+----------
|
||
|
+ as
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mchar::mvarchar(2);
|
||
|
+ mvarchar
|
||
|
+----------
|
||
|
+ as
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mchar(2)::mvarchar(2);
|
||
|
+ mvarchar
|
||
|
+----------
|
||
|
+ as
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mchar(2)::mvarchar(2);
|
||
|
+ mvarchar
|
||
|
+----------
|
||
|
+ as
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mchar(5)::mvarchar(2);
|
||
|
+ mvarchar
|
||
|
+----------
|
||
|
+ as
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mchar(5)::mvarchar(2);
|
||
|
+ mvarchar
|
||
|
+----------
|
||
|
+ as
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mchar::mvarchar(5);
|
||
|
+ mvarchar
|
||
|
+----------
|
||
|
+ asd
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mchar::mvarchar(5);
|
||
|
+ mvarchar
|
||
|
+----------
|
||
|
+ asd
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mchar(2)::mvarchar(5);
|
||
|
+ mvarchar
|
||
|
+----------
|
||
|
+ as
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mchar(2)::mvarchar(5);
|
||
|
+ mvarchar
|
||
|
+----------
|
||
|
+ as
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mchar(5)::mvarchar(5);
|
||
|
+ mvarchar
|
||
|
+----------
|
||
|
+ asd
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mchar(5)::mvarchar(5);
|
||
|
+ mvarchar
|
||
|
+----------
|
||
|
+ asd
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mvarchar::mchar;
|
||
|
+ mchar
|
||
|
+-------
|
||
|
+ asd
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mvarchar::mchar;
|
||
|
+ mchar
|
||
|
+-------
|
||
|
+ asd
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mvarchar(2)::mchar;
|
||
|
+ mchar
|
||
|
+-------
|
||
|
+ as
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mvarchar(2)::mchar;
|
||
|
+ mchar
|
||
|
+-------
|
||
|
+ as
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mvarchar(5)::mchar;
|
||
|
+ mchar
|
||
|
+-------
|
||
|
+ asd
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mvarchar(5)::mchar;
|
||
|
+ mchar
|
||
|
+-------
|
||
|
+ asd
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mvarchar::mchar(2);
|
||
|
+ mchar
|
||
|
+-------
|
||
|
+ as
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mvarchar::mchar(2);
|
||
|
+ mchar
|
||
|
+-------
|
||
|
+ as
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mvarchar(2)::mchar(2);
|
||
|
+ mchar
|
||
|
+-------
|
||
|
+ as
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mvarchar(2)::mchar(2);
|
||
|
+ mchar
|
||
|
+-------
|
||
|
+ as
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mvarchar(5)::mchar(2);
|
||
|
+ mchar
|
||
|
+-------
|
||
|
+ as
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mvarchar(5)::mchar(2);
|
||
|
+ mchar
|
||
|
+-------
|
||
|
+ as
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mvarchar::mchar(5);
|
||
|
+ mchar
|
||
|
+-------
|
||
|
+ asd
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mvarchar::mchar(5);
|
||
|
+ mchar
|
||
|
+-------
|
||
|
+ asd
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mvarchar(2)::mchar(5);
|
||
|
+ mchar
|
||
|
+-------
|
||
|
+ as
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mvarchar(2)::mchar(5);
|
||
|
+ mchar
|
||
|
+-------
|
||
|
+ as
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mvarchar(5)::mchar(5);
|
||
|
+ mchar
|
||
|
+-------
|
||
|
+ asd
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mvarchar(5)::mchar(5);
|
||
|
+ mchar
|
||
|
+-------
|
||
|
+ asd
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mchar || '123';
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mchar || '123'::mchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mchar || '123'::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mchar || '123';
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mchar || '123'::mchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mchar || '123'::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mchar || '123 ';
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mchar || '123 '::mchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mchar || '123 '::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mvarchar || '123';
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mvarchar || '123'::mchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mvarchar || '123'::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mvarchar || '123';
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd 123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mvarchar || '123'::mchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd 123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mvarchar || '123'::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd 123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mvarchar || '123 ';
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd 123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mvarchar || '123 '::mchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd 123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mvarchar || '123 '::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd 123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mchar(2) || '123';
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ as123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mchar(2) || '123'::mchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ as123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mchar(2) || '123'::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ as123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mchar(2) || '123';
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ as123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mchar(2) || '123'::mchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ as123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mchar(2) || '123'::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ as123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mchar(2) || '123 ';
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ as123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mchar(2) || '123 '::mchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ as123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mchar(2) || '123 '::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ as123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mvarchar(2) || '123';
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ as123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mvarchar(2) || '123'::mchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ as123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mvarchar(2) || '123'::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ as123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mvarchar(2) || '123';
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ as123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mvarchar(2) || '123'::mchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ as123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mvarchar(2) || '123'::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ as123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mvarchar(2) || '123 ';
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ as123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mvarchar(2) || '123 '::mchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ as123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mvarchar(2) || '123 '::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ as123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mchar(4) || '143';
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd 143
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mchar(4) || '123'::mchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd 123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mchar(4) || '123'::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd 123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mchar(4) || '123';
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd 123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mchar(4) || '123'::mchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd 123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mchar(4) || '123'::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd 123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mchar(4) || '123 ';
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd 123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mchar(4) || '123 '::mchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd 123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mchar(4) || '123 '::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd 123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mvarchar(4) || '123';
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mvarchar(4) || '123'::mchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mvarchar(4) || '123'::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mvarchar(4) || '123';
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd 123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mvarchar(4) || '123'::mchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd 123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mvarchar(4) || '123'::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd 123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mvarchar(4) || '123 ';
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd 123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mvarchar(4) || '123 '::mchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd 123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mvarchar(4) || '123 '::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd 123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mvarchar(4) || '123 '::mchar(4);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd 123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mvarchar(4) || '123 '::mvarchar(4);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd 123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mvarchar(4) || '123'::mchar(4);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd 123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd '::mvarchar(4) || '123'::mvarchar(4);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd 123
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 1 where 'f'::mchar='F'::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 1 where 'f'::mchar='F '::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 1 where 'f '::mchar='F'::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 1 where 'f '::mchar='F '::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 1 where 'f'::mchar='F'::mvarchar(2);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 1 where 'f'::mchar='F '::mvarchar(2);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 1 where 'f '::mchar='F'::mvarchar(2);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 1 where 'f '::mchar='F '::mvarchar(2);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 1 where 'f'::mchar(2)='F'::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 1 where 'f'::mchar(2)='F '::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 1 where 'f '::mchar(2)='F'::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 1 where 'f '::mchar(2)='F '::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 1 where 'f'::mchar(2)='F'::mvarchar(2);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 1 where 'f'::mchar(2)='F '::mvarchar(2);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 1 where 'f '::mchar(2)='F'::mvarchar(2);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 1 where 'f '::mchar(2)='F '::mvarchar(2);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 1 where 'foo'::mchar='FOO'::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 1 where 'foo'::mchar='FOO '::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 1 where 'foo '::mchar='FOO'::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 1 where 'foo '::mchar='FOO '::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 1 where 'foo'::mchar='FOO'::mvarchar(2);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+(0 rows)
|
||
|
+
|
||
|
+select 1 where 'foo'::mchar='FOO '::mvarchar(2);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+(0 rows)
|
||
|
+
|
||
|
+select 1 where 'foo '::mchar='FOO'::mvarchar(2);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+(0 rows)
|
||
|
+
|
||
|
+select 1 where 'foo '::mchar='FOO '::mvarchar(2);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+(0 rows)
|
||
|
+
|
||
|
+select 1 where 'foo'::mchar(2)='FOO'::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+(0 rows)
|
||
|
+
|
||
|
+select 1 where 'foo'::mchar(2)='FOO '::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+(0 rows)
|
||
|
+
|
||
|
+select 1 where 'foo '::mchar(2)='FOO'::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+(0 rows)
|
||
|
+
|
||
|
+select 1 where 'foo '::mchar(2)='FOO '::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+(0 rows)
|
||
|
+
|
||
|
+select 1 where 'foo'::mchar(2)='FOO'::mvarchar(2);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 1 where 'foo'::mchar(2)='FOO '::mvarchar(2);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 1 where 'foo '::mchar(2)='FOO'::mvarchar(2);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 1 where 'foo '::mchar(2)='FOO '::mvarchar(2);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+Select 'f'::mchar(1) Union Select 'o'::mvarchar(1);
|
||
|
+ mchar
|
||
|
+-------
|
||
|
+ f
|
||
|
+ o
|
||
|
+(2 rows)
|
||
|
+
|
||
|
+Select 'f'::mvarchar(1) Union Select 'o'::mchar(1);
|
||
|
+ mvarchar
|
||
|
+----------
|
||
|
+ f
|
||
|
+ o
|
||
|
+(2 rows)
|
||
|
+
|
||
|
+select * from chvch where ch=vch;
|
||
|
+ ch | vch
|
||
|
+--------------+------------
|
||
|
+ No spaces | No spaces
|
||
|
+ One space | One space
|
||
|
+ 1 space | 1 space
|
||
|
+(3 rows)
|
||
|
+
|
||
|
+select ch.* from ch, (select 'dEfg'::mvarchar as q) as p where chcol > p.q;
|
||
|
+ chcol
|
||
|
+----------------------------------
|
||
|
+ ee
|
||
|
+ Ee
|
||
|
+(2 rows)
|
||
|
+
|
||
|
+create index qq on ch (chcol);
|
||
|
+set enable_seqscan=off;
|
||
|
+select ch.* from ch, (select 'dEfg'::mvarchar as q) as p where chcol > p.q;
|
||
|
+ chcol
|
||
|
+----------------------------------
|
||
|
+ ee
|
||
|
+ Ee
|
||
|
+(2 rows)
|
||
|
+
|
||
|
+set enable_seqscan=on;
|
||
|
+--\copy chvch to 'results/chvch.dump' binary
|
||
|
+--truncate table chvch;
|
||
|
+--\copy chvch from 'results/chvch.dump' binary
|
||
|
+--test joins
|
||
|
+CREATE TABLE a (mchar2 MCHAR(2) NOT NULL);
|
||
|
+CREATE TABLE c (mvarchar255 mvarchar NOT NULL);
|
||
|
+SELECT * FROM a, c WHERE mchar2 = mvarchar255;
|
||
|
+ mchar2 | mvarchar255
|
||
|
+--------+-------------
|
||
|
+(0 rows)
|
||
|
+
|
||
|
+SELECT * FROM a, c WHERE mvarchar255 = mchar2;
|
||
|
+ mchar2 | mvarchar255
|
||
|
+--------+-------------
|
||
|
+(0 rows)
|
||
|
+
|
||
|
+DROP TABLE a;
|
||
|
+DROP TABLE c;
|
||
|
diff --git a/contrib/mchar/expected/mvarchar.out b/contrib/mchar/expected/mvarchar.out
|
||
|
new file mode 100644
|
||
|
index 0000000..5c866b4
|
||
|
--- /dev/null
|
||
|
+++ b/contrib/mchar/expected/mvarchar.out
|
||
|
@@ -0,0 +1,363 @@
|
||
|
+-- I/O tests
|
||
|
+select '1'::mvarchar;
|
||
|
+ mvarchar
|
||
|
+----------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select '2 '::mvarchar;
|
||
|
+ mvarchar
|
||
|
+----------
|
||
|
+ 2
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select '10 '::mvarchar;
|
||
|
+ mvarchar
|
||
|
+--------------
|
||
|
+ 10
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select '1'::mvarchar(2);
|
||
|
+ mvarchar
|
||
|
+----------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select '2 '::mvarchar(2);
|
||
|
+ mvarchar
|
||
|
+----------
|
||
|
+ 2
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select '3 '::mvarchar(2);
|
||
|
+ mvarchar
|
||
|
+----------
|
||
|
+ 3
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select '10 '::mvarchar(2);
|
||
|
+ mvarchar
|
||
|
+----------
|
||
|
+ 10
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select ' '::mvarchar(10);
|
||
|
+ mvarchar
|
||
|
+------------
|
||
|
+
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select ' '::mvarchar;
|
||
|
+ mvarchar
|
||
|
+--------------------
|
||
|
+
|
||
|
+(1 row)
|
||
|
+
|
||
|
+-- operations & functions
|
||
|
+select length('1'::mvarchar);
|
||
|
+ length
|
||
|
+--------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select length('2 '::mvarchar);
|
||
|
+ length
|
||
|
+--------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select length('10 '::mvarchar);
|
||
|
+ length
|
||
|
+--------
|
||
|
+ 2
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select length('1'::mvarchar(2));
|
||
|
+ length
|
||
|
+--------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select length('2 '::mvarchar(2));
|
||
|
+ length
|
||
|
+--------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select length('3 '::mvarchar(2));
|
||
|
+ length
|
||
|
+--------
|
||
|
+ 1
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select length('10 '::mvarchar(2));
|
||
|
+ length
|
||
|
+--------
|
||
|
+ 2
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select length(' '::mvarchar(10));
|
||
|
+ length
|
||
|
+--------
|
||
|
+ 0
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select length(' '::mvarchar);
|
||
|
+ length
|
||
|
+--------
|
||
|
+ 0
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mvarchar(10) || '>'::mvarchar(10);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ asd>
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select length('asd'::mvarchar(10) || '>'::mvarchar(10));
|
||
|
+ length
|
||
|
+--------
|
||
|
+ 4
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mvarchar(2) || '>'::mvarchar(10);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ as>
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select length('asd'::mvarchar(2) || '>'::mvarchar(10));
|
||
|
+ length
|
||
|
+--------
|
||
|
+ 3
|
||
|
+(1 row)
|
||
|
+
|
||
|
+-- Comparisons
|
||
|
+select 'asdf'::mvarchar = 'aSdf'::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mvarchar = 'aSdf '::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mvarchar = 'aSdf 1'::mvarchar(4);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mvarchar = 'aSdf 1'::mvarchar(5);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mvarchar = 'aSdf 1'::mvarchar(6);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mvarchar(3) = 'aSdf 1'::mvarchar(5);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mvarchar(3) = 'aSdf 1'::mvarchar(3);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mvarchar < 'aSdf'::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mvarchar < 'aSdf '::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mvarchar < 'aSdf 1'::mvarchar(4);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mvarchar < 'aSdf 1'::mvarchar(5);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mvarchar < 'aSdf 1'::mvarchar(6);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mvarchar <= 'aSdf'::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mvarchar <= 'aSdf '::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mvarchar <= 'aSdf 1'::mvarchar(4);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mvarchar <= 'aSdf 1'::mvarchar(5);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mvarchar <= 'aSdf 1'::mvarchar(6);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mvarchar >= 'aSdf'::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mvarchar >= 'aSdf '::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mvarchar >= 'aSdf 1'::mvarchar(4);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mvarchar >= 'aSdf 1'::mvarchar(5);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mvarchar >= 'aSdf 1'::mvarchar(6);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mvarchar > 'aSdf'::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mvarchar > 'aSdf '::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mvarchar > 'aSdf 1'::mvarchar(4);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mvarchar > 'aSdf 1'::mvarchar(5);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asdf'::mvarchar > 'aSdf 1'::mvarchar(6);
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select max(vch) from chvch;
|
||
|
+ max
|
||
|
+------------
|
||
|
+ One space
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select min(vch) from chvch;
|
||
|
+ min
|
||
|
+----------
|
||
|
+ 1 space
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select substr('1234567890'::mvarchar, 3) = '34567890' as "34567890";
|
||
|
+ 34567890
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select substr('1234567890'::mvarchar, 4, 3) = '456' as "456";
|
||
|
+ 456
|
||
|
+-----
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select lower('asdfASDF'::mvarchar);
|
||
|
+ lower
|
||
|
+----------
|
||
|
+ asdfasdf
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select upper('asdfASDF'::mvarchar);
|
||
|
+ upper
|
||
|
+----------
|
||
|
+ ASDFASDF
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mvarchar == 'aSd'::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mvarchar == 'aCd'::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select 'asd'::mvarchar == NULL;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select NULL == 'aCd'::mvarchar;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ f
|
||
|
+(1 row)
|
||
|
+
|
||
|
+select NULL::mvarchar == NULL;
|
||
|
+ ?column?
|
||
|
+----------
|
||
|
+ t
|
||
|
+(1 row)
|
||
|
+
|
||
|
diff --git a/contrib/mchar/mchar.h b/contrib/mchar/mchar.h
|
||
|
new file mode 100644
|
||
|
index 0000000..8823169
|
||
|
--- /dev/null
|
||
|
+++ b/contrib/mchar/mchar.h
|
||
|
@@ -0,0 +1,62 @@
|
||
|
+#ifndef __MCHAR_H__
|
||
|
+#define __MCHAR_H__
|
||
|
+
|
||
|
+#include "postgres.h"
|
||
|
+#include "mb/pg_wchar.h"
|
||
|
+#include "utils/builtins.h"
|
||
|
+#include "unicode/uchar.h"
|
||
|
+#include "unicode/ustring.h"
|
||
|
+
|
||
|
+typedef struct {
|
||
|
+ int32 len;
|
||
|
+ int32 typmod;
|
||
|
+ UChar data[1];
|
||
|
+} MChar;
|
||
|
+
|
||
|
+#define MCHARHDRSZ offsetof(MChar, data)
|
||
|
+#define MCHARLENGTH(m) ( VARSIZE(m)-MCHARHDRSZ )
|
||
|
+#define UCHARLENGTH(m) ( MCHARLENGTH(m)/sizeof(UChar) )
|
||
|
+
|
||
|
+#define DatumGetMChar(m) ((MChar*)DatumGetPointer(m))
|
||
|
+#define MCharGetDatum(m) PointerGetDatum(m)
|
||
|
+
|
||
|
+#define PG_GETARG_MCHAR(n) DatumGetMChar(PG_DETOAST_DATUM(PG_GETARG_DATUM(n)))
|
||
|
+#define PG_GETARG_MCHAR_COPY(n) DatumGetMChar(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(n)))
|
||
|
+
|
||
|
+#define PG_RETURN_MCHAR(m) PG_RETURN_POINTER(m)
|
||
|
+
|
||
|
+typedef struct {
|
||
|
+ int32 len;
|
||
|
+ UChar data[1];
|
||
|
+} MVarChar;
|
||
|
+
|
||
|
+#define MVARCHARHDRSZ offsetof(MVarChar, data)
|
||
|
+#define MVARCHARLENGTH(m) ( VARSIZE(m)-MVARCHARHDRSZ )
|
||
|
+#define UVARCHARLENGTH(m) ( MVARCHARLENGTH(m)/sizeof(UChar) )
|
||
|
+
|
||
|
+#define DatumGetMVarChar(m) ((MVarChar*)DatumGetPointer(m))
|
||
|
+#define MVarCharGetDatum(m) PointerGetDatum(m)
|
||
|
+
|
||
|
+#define PG_GETARG_MVARCHAR(n) DatumGetMVarChar(PG_DETOAST_DATUM(PG_GETARG_DATUM(n)))
|
||
|
+#define PG_GETARG_MVARCHAR_COPY(n) DatumGetMVarChar(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(n)))
|
||
|
+
|
||
|
+#define PG_RETURN_MVARCHAR(m) PG_RETURN_POINTER(m)
|
||
|
+
|
||
|
+
|
||
|
+int Char2UChar(const char * src, int srclen, UChar *dst);
|
||
|
+int UChar2Char(const UChar * src, int srclen, char *dst);
|
||
|
+int UChar2Wchar(UChar * src, int srclen, pg_wchar *dst);
|
||
|
+int UCharCompare(UChar * a, int alen, UChar *b, int blen);
|
||
|
+int UCharCaseCompare(UChar * a, int alen, UChar *b, int blen);
|
||
|
+
|
||
|
+void FillWhiteSpace( UChar *dst, int n );
|
||
|
+
|
||
|
+int lengthWithoutSpaceVarChar(MVarChar *m);
|
||
|
+int lengthWithoutSpaceChar(MChar *m);
|
||
|
+
|
||
|
+extern Datum mchar_hash(PG_FUNCTION_ARGS);
|
||
|
+extern Datum mvarchar_hash(PG_FUNCTION_ARGS);
|
||
|
+
|
||
|
+int m_isspace(UChar c); /* is == ' ' */
|
||
|
+
|
||
|
+#endif
|
||
|
diff --git a/contrib/mchar/mchar.sql.in b/contrib/mchar/mchar.sql.in
|
||
|
new file mode 100644
|
||
|
index 0000000..b5ef4e6
|
||
|
--- /dev/null
|
||
|
+++ b/contrib/mchar/mchar.sql.in
|
||
|
@@ -0,0 +1,1328 @@
|
||
|
+SET search_path = public;
|
||
|
+
|
||
|
+BEGIN;
|
||
|
+
|
||
|
+-- I/O functions
|
||
|
+
|
||
|
+CREATE FUNCTION mchartypmod_in(cstring[])
|
||
|
+RETURNS int4
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mchartypmod_out(int4)
|
||
|
+RETURNS cstring
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mchar_in(cstring)
|
||
|
+RETURNS mchar
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mchar_out(mchar)
|
||
|
+RETURNS cstring
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mchar_send(mchar)
|
||
|
+RETURNS bytea
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mchar_recv(internal)
|
||
|
+RETURNS mchar
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE TYPE mchar (
|
||
|
+ INTERNALLENGTH = -1,
|
||
|
+ INPUT = mchar_in,
|
||
|
+ OUTPUT = mchar_out,
|
||
|
+ TYPMOD_IN = mchartypmod_in,
|
||
|
+ TYPMOD_OUT = mchartypmod_out,
|
||
|
+ RECEIVE = mchar_recv,
|
||
|
+ SEND = mchar_send,
|
||
|
+ STORAGE = extended
|
||
|
+);
|
||
|
+
|
||
|
+CREATE FUNCTION mchar(mchar, integer, boolean)
|
||
|
+RETURNS mchar
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE CAST (mchar as mchar)
|
||
|
+WITH FUNCTION mchar(mchar, integer, boolean) as IMPLICIT;
|
||
|
+
|
||
|
+CREATE FUNCTION mvarchar_in(cstring)
|
||
|
+RETURNS mvarchar
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mvarchar_out(mvarchar)
|
||
|
+RETURNS cstring
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mvarchar_send(mvarchar)
|
||
|
+RETURNS bytea
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mvarchar_recv(internal)
|
||
|
+RETURNS mvarchar
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE TYPE mvarchar (
|
||
|
+ INTERNALLENGTH = -1,
|
||
|
+ INPUT = mvarchar_in,
|
||
|
+ OUTPUT = mvarchar_out,
|
||
|
+ TYPMOD_IN = mchartypmod_in,
|
||
|
+ TYPMOD_OUT = mchartypmod_out,
|
||
|
+ RECEIVE = mvarchar_recv,
|
||
|
+ SEND = mvarchar_send,
|
||
|
+ STORAGE = extended
|
||
|
+);
|
||
|
+
|
||
|
+CREATE FUNCTION mvarchar(mvarchar, integer, boolean)
|
||
|
+RETURNS mvarchar
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE CAST (mvarchar as mvarchar)
|
||
|
+WITH FUNCTION mvarchar(mvarchar, integer, boolean) as IMPLICIT;
|
||
|
+
|
||
|
+--Operations and functions
|
||
|
+
|
||
|
+CREATE FUNCTION length(mchar)
|
||
|
+RETURNS int4
|
||
|
+AS 'MODULE_PATHNAME', 'mchar_length'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION upper(mchar)
|
||
|
+RETURNS mchar
|
||
|
+AS 'MODULE_PATHNAME', 'mchar_upper'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION lower(mchar)
|
||
|
+RETURNS mchar
|
||
|
+AS 'MODULE_PATHNAME', 'mchar_lower'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mchar_hash(mchar)
|
||
|
+RETURNS int4
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mchar_concat(mchar, mchar)
|
||
|
+RETURNS mchar
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE OPERATOR || (
|
||
|
+ LEFTARG = mchar,
|
||
|
+ RIGHTARG = mchar,
|
||
|
+ PROCEDURE = mchar_concat
|
||
|
+);
|
||
|
+
|
||
|
+CREATE FUNCTION mchar_like(mchar, mvarchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mchar_notlike(mchar, mvarchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE OPERATOR ~~ (
|
||
|
+ LEFTARG = mchar,
|
||
|
+ RIGHTARG = mvarchar,
|
||
|
+ PROCEDURE = mchar_like,
|
||
|
+ RESTRICT = likesel,
|
||
|
+ JOIN = likejoinsel,
|
||
|
+ NEGATOR = '!~~'
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR !~~ (
|
||
|
+ LEFTARG = mchar,
|
||
|
+ RIGHTARG = mvarchar,
|
||
|
+ PROCEDURE = mchar_notlike,
|
||
|
+ RESTRICT = nlikesel,
|
||
|
+ JOIN = nlikejoinsel,
|
||
|
+ NEGATOR = '~~'
|
||
|
+);
|
||
|
+
|
||
|
+CREATE FUNCTION mchar_regexeq(mchar, mchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mchar_regexne(mchar, mchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE OPERATOR ~ (
|
||
|
+ LEFTARG = mchar,
|
||
|
+ RIGHTARG = mchar,
|
||
|
+ PROCEDURE = mchar_regexeq,
|
||
|
+ RESTRICT = regexeqsel,
|
||
|
+ JOIN = regexeqjoinsel,
|
||
|
+ NEGATOR = '!~'
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR !~ (
|
||
|
+ LEFTARG = mchar,
|
||
|
+ RIGHTARG = mchar,
|
||
|
+ PROCEDURE = mchar_regexne,
|
||
|
+ RESTRICT = regexnesel,
|
||
|
+ JOIN = regexnejoinsel,
|
||
|
+ NEGATOR = '~'
|
||
|
+);
|
||
|
+
|
||
|
+CREATE FUNCTION similar_escape(mchar, mchar)
|
||
|
+RETURNS mchar
|
||
|
+AS 'MODULE_PATHNAME', 'mchar_similar_escape'
|
||
|
+LANGUAGE C IMMUTABLE;
|
||
|
+
|
||
|
+CREATE FUNCTION length(mvarchar)
|
||
|
+RETURNS int4
|
||
|
+AS 'MODULE_PATHNAME', 'mvarchar_length'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION upper(mvarchar)
|
||
|
+RETURNS mvarchar
|
||
|
+AS 'MODULE_PATHNAME', 'mvarchar_upper'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION lower(mvarchar)
|
||
|
+RETURNS mvarchar
|
||
|
+AS 'MODULE_PATHNAME', 'mvarchar_lower'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mvarchar_hash(mvarchar)
|
||
|
+RETURNS int4
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mvarchar_concat(mvarchar, mvarchar)
|
||
|
+RETURNS mvarchar
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE OPERATOR || (
|
||
|
+ LEFTARG = mvarchar,
|
||
|
+ RIGHTARG = mvarchar,
|
||
|
+ PROCEDURE = mvarchar_concat
|
||
|
+);
|
||
|
+
|
||
|
+CREATE FUNCTION mvarchar_like(mvarchar, mvarchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION like_escape(mvarchar, mvarchar)
|
||
|
+RETURNS mvarchar
|
||
|
+AS 'MODULE_PATHNAME', 'mvarchar_like_escape'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mvarchar_notlike(mvarchar, mvarchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE OPERATOR ~~ (
|
||
|
+ LEFTARG = mvarchar,
|
||
|
+ RIGHTARG = mvarchar,
|
||
|
+ PROCEDURE = mvarchar_like,
|
||
|
+ RESTRICT = likesel,
|
||
|
+ JOIN = likejoinsel,
|
||
|
+ NEGATOR = '!~~'
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR !~~ (
|
||
|
+ LEFTARG = mvarchar,
|
||
|
+ RIGHTARG = mvarchar,
|
||
|
+ PROCEDURE = mvarchar_notlike,
|
||
|
+ RESTRICT = nlikesel,
|
||
|
+ JOIN = nlikejoinsel,
|
||
|
+ NEGATOR = '~~'
|
||
|
+);
|
||
|
+
|
||
|
+CREATE FUNCTION mvarchar_regexeq(mvarchar, mvarchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mvarchar_regexne(mvarchar, mvarchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE OPERATOR ~ (
|
||
|
+ LEFTARG = mvarchar,
|
||
|
+ RIGHTARG = mvarchar,
|
||
|
+ PROCEDURE = mvarchar_regexeq,
|
||
|
+ RESTRICT = regexeqsel,
|
||
|
+ JOIN = regexeqjoinsel,
|
||
|
+ NEGATOR = '!~'
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR !~ (
|
||
|
+ LEFTARG = mvarchar,
|
||
|
+ RIGHTARG = mvarchar,
|
||
|
+ PROCEDURE = mvarchar_regexne,
|
||
|
+ RESTRICT = regexnesel,
|
||
|
+ JOIN = regexnejoinsel,
|
||
|
+ NEGATOR = '~'
|
||
|
+);
|
||
|
+
|
||
|
+CREATE FUNCTION similar_escape(mvarchar, mvarchar)
|
||
|
+RETURNS mvarchar
|
||
|
+AS 'MODULE_PATHNAME', 'mvarchar_similar_escape'
|
||
|
+LANGUAGE C IMMUTABLE;
|
||
|
+
|
||
|
+CREATE FUNCTION substr (mchar, int4)
|
||
|
+RETURNS mchar
|
||
|
+AS 'MODULE_PATHNAME', 'mchar_substring_no_len'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION substr (mchar, int4, int4)
|
||
|
+RETURNS mchar
|
||
|
+AS 'MODULE_PATHNAME', 'mchar_substring'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION substr (mvarchar, int4)
|
||
|
+RETURNS mvarchar
|
||
|
+AS 'MODULE_PATHNAME', 'mvarchar_substring_no_len'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION substr (mvarchar, int4, int4)
|
||
|
+RETURNS mvarchar
|
||
|
+AS 'MODULE_PATHNAME', 'mvarchar_substring'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+-- Comparing
|
||
|
+-- MCHAR
|
||
|
+
|
||
|
+CREATE FUNCTION mchar_icase_cmp(mchar, mchar)
|
||
|
+RETURNS int4
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mchar_icase_eq(mchar, mchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mchar_icase_ne(mchar, mchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mchar_icase_lt(mchar, mchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mchar_icase_le(mchar, mchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mchar_icase_gt(mchar, mchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mchar_icase_ge(mchar, mchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+
|
||
|
+CREATE OPERATOR < (
|
||
|
+ LEFTARG = mchar,
|
||
|
+ RIGHTARG = mchar,
|
||
|
+ PROCEDURE = mchar_icase_lt,
|
||
|
+ COMMUTATOR = '>',
|
||
|
+ NEGATOR = '>=',
|
||
|
+ RESTRICT = scalarltsel,
|
||
|
+ JOIN = scalarltjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR > (
|
||
|
+ LEFTARG = mchar,
|
||
|
+ RIGHTARG = mchar,
|
||
|
+ PROCEDURE = mchar_icase_gt,
|
||
|
+ COMMUTATOR = '<',
|
||
|
+ NEGATOR = '<=',
|
||
|
+ RESTRICT = scalargtsel,
|
||
|
+ JOIN = scalargtjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR <= (
|
||
|
+ LEFTARG = mchar,
|
||
|
+ RIGHTARG = mchar,
|
||
|
+ PROCEDURE = mchar_icase_le,
|
||
|
+ COMMUTATOR = '>=',
|
||
|
+ NEGATOR = '>',
|
||
|
+ RESTRICT = scalarltsel,
|
||
|
+ JOIN = scalarltjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR >= (
|
||
|
+ LEFTARG = mchar,
|
||
|
+ RIGHTARG = mchar,
|
||
|
+ PROCEDURE = mchar_icase_ge,
|
||
|
+ COMMUTATOR = '<=',
|
||
|
+ NEGATOR = '<',
|
||
|
+ RESTRICT = scalargtsel,
|
||
|
+ JOIN = scalargtjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR = (
|
||
|
+ LEFTARG = mchar,
|
||
|
+ RIGHTARG = mchar,
|
||
|
+ PROCEDURE = mchar_icase_eq,
|
||
|
+ COMMUTATOR = '=',
|
||
|
+ NEGATOR = '<>',
|
||
|
+ RESTRICT = eqsel,
|
||
|
+ JOIN = eqjoinsel,
|
||
|
+ SORT1 = '<',
|
||
|
+ SORT2 = '<',
|
||
|
+ HASHES
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR <> (
|
||
|
+ LEFTARG = mchar,
|
||
|
+ RIGHTARG = mchar,
|
||
|
+ PROCEDURE = mchar_icase_ne,
|
||
|
+ COMMUTATOR = '<>',
|
||
|
+ NEGATOR = '=',
|
||
|
+ RESTRICT = neqsel,
|
||
|
+ JOIN = neqjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+CREATE FUNCTION mchar_case_cmp(mchar, mchar)
|
||
|
+RETURNS int4
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mchar_case_eq(mchar, mchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mchar_case_ne(mchar, mchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mchar_case_lt(mchar, mchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mchar_case_le(mchar, mchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mchar_case_gt(mchar, mchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mchar_case_ge(mchar, mchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+
|
||
|
+CREATE OPERATOR &< (
|
||
|
+ LEFTARG = mchar,
|
||
|
+ RIGHTARG = mchar,
|
||
|
+ PROCEDURE = mchar_case_lt,
|
||
|
+ COMMUTATOR = '&>',
|
||
|
+ NEGATOR = '&>=',
|
||
|
+ RESTRICT = scalarltsel,
|
||
|
+ JOIN = scalarltjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR &> (
|
||
|
+ LEFTARG = mchar,
|
||
|
+ RIGHTARG = mchar,
|
||
|
+ PROCEDURE = mchar_case_gt,
|
||
|
+ COMMUTATOR = '&<',
|
||
|
+ NEGATOR = '&<=',
|
||
|
+ RESTRICT = scalargtsel,
|
||
|
+ JOIN = scalargtjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR &<= (
|
||
|
+ LEFTARG = mchar,
|
||
|
+ RIGHTARG = mchar,
|
||
|
+ PROCEDURE = mchar_case_le,
|
||
|
+ COMMUTATOR = '&>=',
|
||
|
+ NEGATOR = '&>',
|
||
|
+ RESTRICT = scalarltsel,
|
||
|
+ JOIN = scalarltjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR &>= (
|
||
|
+ LEFTARG = mchar,
|
||
|
+ RIGHTARG = mchar,
|
||
|
+ PROCEDURE = mchar_case_ge,
|
||
|
+ COMMUTATOR = '&<=',
|
||
|
+ NEGATOR = '&<',
|
||
|
+ RESTRICT = scalargtsel,
|
||
|
+ JOIN = scalargtjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR &= (
|
||
|
+ LEFTARG = mchar,
|
||
|
+ RIGHTARG = mchar,
|
||
|
+ PROCEDURE = mchar_case_eq,
|
||
|
+ COMMUTATOR = '&=',
|
||
|
+ NEGATOR = '&<>',
|
||
|
+ RESTRICT = eqsel,
|
||
|
+ JOIN = eqjoinsel,
|
||
|
+ SORT1 = '&<',
|
||
|
+ SORT2 = '&<'
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR &<> (
|
||
|
+ LEFTARG = mchar,
|
||
|
+ RIGHTARG = mchar,
|
||
|
+ PROCEDURE = mchar_case_ne,
|
||
|
+ COMMUTATOR = '&<>',
|
||
|
+ NEGATOR = '&=',
|
||
|
+ RESTRICT = neqsel,
|
||
|
+ JOIN = neqjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+--MVARCHAR
|
||
|
+
|
||
|
+CREATE FUNCTION mvarchar_icase_cmp(mvarchar, mvarchar)
|
||
|
+RETURNS int4
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mvarchar_icase_eq(mvarchar, mvarchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mvarchar_icase_ne(mvarchar, mvarchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mvarchar_icase_lt(mvarchar, mvarchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mvarchar_icase_le(mvarchar, mvarchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mvarchar_icase_gt(mvarchar, mvarchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mvarchar_icase_ge(mvarchar, mvarchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+
|
||
|
+CREATE OPERATOR < (
|
||
|
+ LEFTARG = mvarchar,
|
||
|
+ RIGHTARG = mvarchar,
|
||
|
+ PROCEDURE = mvarchar_icase_lt,
|
||
|
+ COMMUTATOR = '>',
|
||
|
+ NEGATOR = '>=',
|
||
|
+ RESTRICT = scalarltsel,
|
||
|
+ JOIN = scalarltjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR > (
|
||
|
+ LEFTARG = mvarchar,
|
||
|
+ RIGHTARG = mvarchar,
|
||
|
+ PROCEDURE = mvarchar_icase_gt,
|
||
|
+ COMMUTATOR = '<',
|
||
|
+ NEGATOR = '<=',
|
||
|
+ RESTRICT = scalargtsel,
|
||
|
+ JOIN = scalargtjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR <= (
|
||
|
+ LEFTARG = mvarchar,
|
||
|
+ RIGHTARG = mvarchar,
|
||
|
+ PROCEDURE = mvarchar_icase_le,
|
||
|
+ COMMUTATOR = '>=',
|
||
|
+ NEGATOR = '>',
|
||
|
+ RESTRICT = scalarltsel,
|
||
|
+ JOIN = scalarltjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR >= (
|
||
|
+ LEFTARG = mvarchar,
|
||
|
+ RIGHTARG = mvarchar,
|
||
|
+ PROCEDURE = mvarchar_icase_ge,
|
||
|
+ COMMUTATOR = '<=',
|
||
|
+ NEGATOR = '<',
|
||
|
+ RESTRICT = scalargtsel,
|
||
|
+ JOIN = scalargtjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR = (
|
||
|
+ LEFTARG = mvarchar,
|
||
|
+ RIGHTARG = mvarchar,
|
||
|
+ PROCEDURE = mvarchar_icase_eq,
|
||
|
+ COMMUTATOR = '=',
|
||
|
+ NEGATOR = '<>',
|
||
|
+ RESTRICT = eqsel,
|
||
|
+ JOIN = eqjoinsel,
|
||
|
+ SORT1 = '<',
|
||
|
+ SORT2 = '<',
|
||
|
+ HASHES
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR <> (
|
||
|
+ LEFTARG = mvarchar,
|
||
|
+ RIGHTARG = mvarchar,
|
||
|
+ PROCEDURE = mvarchar_icase_ne,
|
||
|
+ COMMUTATOR = '<>',
|
||
|
+ NEGATOR = '=',
|
||
|
+ RESTRICT = neqsel,
|
||
|
+ JOIN = neqjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+CREATE FUNCTION mvarchar_case_cmp(mvarchar, mvarchar)
|
||
|
+RETURNS int4
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mvarchar_case_eq(mvarchar, mvarchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mvarchar_case_ne(mvarchar, mvarchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mvarchar_case_lt(mvarchar, mvarchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mvarchar_case_le(mvarchar, mvarchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mvarchar_case_gt(mvarchar, mvarchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mvarchar_case_ge(mvarchar, mvarchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+
|
||
|
+CREATE OPERATOR &< (
|
||
|
+ LEFTARG = mvarchar,
|
||
|
+ RIGHTARG = mvarchar,
|
||
|
+ PROCEDURE = mvarchar_case_lt,
|
||
|
+ COMMUTATOR = '&>',
|
||
|
+ NEGATOR = '&>=',
|
||
|
+ RESTRICT = scalarltsel,
|
||
|
+ JOIN = scalarltjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR &> (
|
||
|
+ LEFTARG = mvarchar,
|
||
|
+ RIGHTARG = mvarchar,
|
||
|
+ PROCEDURE = mvarchar_case_gt,
|
||
|
+ COMMUTATOR = '&<',
|
||
|
+ NEGATOR = '&<=',
|
||
|
+ RESTRICT = scalargtsel,
|
||
|
+ JOIN = scalargtjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR &<= (
|
||
|
+ LEFTARG = mvarchar,
|
||
|
+ RIGHTARG = mvarchar,
|
||
|
+ PROCEDURE = mvarchar_case_le,
|
||
|
+ COMMUTATOR = '&>=',
|
||
|
+ NEGATOR = '&>',
|
||
|
+ RESTRICT = scalarltsel,
|
||
|
+ JOIN = scalarltjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR &>= (
|
||
|
+ LEFTARG = mvarchar,
|
||
|
+ RIGHTARG = mvarchar,
|
||
|
+ PROCEDURE = mvarchar_case_ge,
|
||
|
+ COMMUTATOR = '&<=',
|
||
|
+ NEGATOR = '&<',
|
||
|
+ RESTRICT = scalargtsel,
|
||
|
+ JOIN = scalargtjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR &= (
|
||
|
+ LEFTARG = mvarchar,
|
||
|
+ RIGHTARG = mvarchar,
|
||
|
+ PROCEDURE = mvarchar_case_eq,
|
||
|
+ COMMUTATOR = '&=',
|
||
|
+ NEGATOR = '&<>',
|
||
|
+ RESTRICT = eqsel,
|
||
|
+ JOIN = eqjoinsel,
|
||
|
+ SORT1 = '&<',
|
||
|
+ SORT2 = '&<'
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR &<> (
|
||
|
+ LEFTARG = mvarchar,
|
||
|
+ RIGHTARG = mvarchar,
|
||
|
+ PROCEDURE = mvarchar_case_ne,
|
||
|
+ COMMUTATOR = '&<>',
|
||
|
+ NEGATOR = '&=',
|
||
|
+ RESTRICT = neqsel,
|
||
|
+ JOIN = neqjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+-- MCHAR <> MVARCHAR
|
||
|
+
|
||
|
+CREATE FUNCTION mc_mv_icase_cmp(mchar, mvarchar)
|
||
|
+RETURNS int4
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mc_mv_icase_eq(mchar, mvarchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mc_mv_icase_ne(mchar, mvarchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mc_mv_icase_lt(mchar, mvarchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mc_mv_icase_le(mchar, mvarchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mc_mv_icase_gt(mchar, mvarchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mc_mv_icase_ge(mchar, mvarchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+
|
||
|
+CREATE OPERATOR < (
|
||
|
+ LEFTARG = mchar,
|
||
|
+ RIGHTARG = mvarchar,
|
||
|
+ PROCEDURE = mc_mv_icase_lt,
|
||
|
+ COMMUTATOR = '>',
|
||
|
+ NEGATOR = '>=',
|
||
|
+ RESTRICT = scalarltsel,
|
||
|
+ JOIN = scalarltjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR > (
|
||
|
+ LEFTARG = mchar,
|
||
|
+ RIGHTARG = mvarchar,
|
||
|
+ PROCEDURE = mc_mv_icase_gt,
|
||
|
+ COMMUTATOR = '<',
|
||
|
+ NEGATOR = '<=',
|
||
|
+ RESTRICT = scalargtsel,
|
||
|
+ JOIN = scalargtjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR <= (
|
||
|
+ LEFTARG = mchar,
|
||
|
+ RIGHTARG = mvarchar,
|
||
|
+ PROCEDURE = mc_mv_icase_le,
|
||
|
+ COMMUTATOR = '>=',
|
||
|
+ NEGATOR = '>',
|
||
|
+ RESTRICT = scalarltsel,
|
||
|
+ JOIN = scalarltjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR >= (
|
||
|
+ LEFTARG = mchar,
|
||
|
+ RIGHTARG = mvarchar,
|
||
|
+ PROCEDURE = mc_mv_icase_ge,
|
||
|
+ COMMUTATOR = '<=',
|
||
|
+ NEGATOR = '<',
|
||
|
+ RESTRICT = scalargtsel,
|
||
|
+ JOIN = scalargtjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR = (
|
||
|
+ LEFTARG = mchar,
|
||
|
+ RIGHTARG = mvarchar,
|
||
|
+ PROCEDURE = mc_mv_icase_eq,
|
||
|
+ COMMUTATOR = '=',
|
||
|
+ NEGATOR = '<>',
|
||
|
+ RESTRICT = eqsel,
|
||
|
+ JOIN = eqjoinsel,
|
||
|
+ SORT1 = '<',
|
||
|
+ SORT2 = '<'
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR <> (
|
||
|
+ LEFTARG = mchar,
|
||
|
+ RIGHTARG = mvarchar,
|
||
|
+ PROCEDURE = mc_mv_icase_ne,
|
||
|
+ COMMUTATOR = '<>',
|
||
|
+ NEGATOR = '=',
|
||
|
+ RESTRICT = neqsel,
|
||
|
+ JOIN = neqjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+CREATE FUNCTION mc_mv_case_cmp(mchar, mvarchar)
|
||
|
+RETURNS int4
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mc_mv_case_eq(mchar, mvarchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mc_mv_case_ne(mchar, mvarchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mc_mv_case_lt(mchar, mvarchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mc_mv_case_le(mchar, mvarchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mc_mv_case_gt(mchar, mvarchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mc_mv_case_ge(mchar, mvarchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+
|
||
|
+CREATE OPERATOR &< (
|
||
|
+ LEFTARG = mchar,
|
||
|
+ RIGHTARG = mvarchar,
|
||
|
+ PROCEDURE = mc_mv_case_lt,
|
||
|
+ COMMUTATOR = '&>',
|
||
|
+ NEGATOR = '&>=',
|
||
|
+ RESTRICT = scalarltsel,
|
||
|
+ JOIN = scalarltjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR &> (
|
||
|
+ LEFTARG = mchar,
|
||
|
+ RIGHTARG = mvarchar,
|
||
|
+ PROCEDURE = mc_mv_case_gt,
|
||
|
+ COMMUTATOR = '&<',
|
||
|
+ NEGATOR = '&<=',
|
||
|
+ RESTRICT = scalargtsel,
|
||
|
+ JOIN = scalargtjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR &<= (
|
||
|
+ LEFTARG = mchar,
|
||
|
+ RIGHTARG = mvarchar,
|
||
|
+ PROCEDURE = mc_mv_case_le,
|
||
|
+ COMMUTATOR = '&>=',
|
||
|
+ NEGATOR = '&>',
|
||
|
+ RESTRICT = scalarltsel,
|
||
|
+ JOIN = scalarltjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR &>= (
|
||
|
+ LEFTARG = mchar,
|
||
|
+ RIGHTARG = mvarchar,
|
||
|
+ PROCEDURE = mc_mv_case_ge,
|
||
|
+ COMMUTATOR = '&<=',
|
||
|
+ NEGATOR = '&<',
|
||
|
+ RESTRICT = scalargtsel,
|
||
|
+ JOIN = scalargtjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR &= (
|
||
|
+ LEFTARG = mchar,
|
||
|
+ RIGHTARG = mvarchar,
|
||
|
+ PROCEDURE = mc_mv_case_eq,
|
||
|
+ COMMUTATOR = '&=',
|
||
|
+ NEGATOR = '&<>',
|
||
|
+ RESTRICT = eqsel,
|
||
|
+ JOIN = eqjoinsel,
|
||
|
+ SORT1 = '&<',
|
||
|
+ SORT2 = '&<'
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR &<> (
|
||
|
+ LEFTARG = mchar,
|
||
|
+ RIGHTARG = mvarchar,
|
||
|
+ PROCEDURE = mc_mv_case_ne,
|
||
|
+ COMMUTATOR = '&<>',
|
||
|
+ NEGATOR = '&=',
|
||
|
+ RESTRICT = neqsel,
|
||
|
+ JOIN = neqjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+-- MVARCHAR <> MCHAR
|
||
|
+
|
||
|
+CREATE FUNCTION mv_mc_icase_cmp(mvarchar, mchar)
|
||
|
+RETURNS int4
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mv_mc_icase_eq(mvarchar, mchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mv_mc_icase_ne(mvarchar, mchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mv_mc_icase_lt(mvarchar, mchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mv_mc_icase_le(mvarchar, mchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mv_mc_icase_gt(mvarchar, mchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mv_mc_icase_ge(mvarchar, mchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+
|
||
|
+CREATE OPERATOR < (
|
||
|
+ LEFTARG = mvarchar,
|
||
|
+ RIGHTARG = mchar,
|
||
|
+ PROCEDURE = mv_mc_icase_lt,
|
||
|
+ COMMUTATOR = '>',
|
||
|
+ NEGATOR = '>=',
|
||
|
+ RESTRICT = scalarltsel,
|
||
|
+ JOIN = scalarltjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR > (
|
||
|
+ LEFTARG = mvarchar,
|
||
|
+ RIGHTARG = mchar,
|
||
|
+ PROCEDURE = mv_mc_icase_gt,
|
||
|
+ COMMUTATOR = '<',
|
||
|
+ NEGATOR = '<=',
|
||
|
+ RESTRICT = scalargtsel,
|
||
|
+ JOIN = scalargtjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR <= (
|
||
|
+ LEFTARG = mvarchar,
|
||
|
+ RIGHTARG = mchar,
|
||
|
+ PROCEDURE = mv_mc_icase_le,
|
||
|
+ COMMUTATOR = '>=',
|
||
|
+ NEGATOR = '>',
|
||
|
+ RESTRICT = scalarltsel,
|
||
|
+ JOIN = scalarltjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR >= (
|
||
|
+ LEFTARG = mvarchar,
|
||
|
+ RIGHTARG = mchar,
|
||
|
+ PROCEDURE = mv_mc_icase_ge,
|
||
|
+ COMMUTATOR = '<=',
|
||
|
+ NEGATOR = '<',
|
||
|
+ RESTRICT = scalargtsel,
|
||
|
+ JOIN = scalargtjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR = (
|
||
|
+ LEFTARG = mvarchar,
|
||
|
+ RIGHTARG = mchar,
|
||
|
+ PROCEDURE = mv_mc_icase_eq,
|
||
|
+ COMMUTATOR = '=',
|
||
|
+ NEGATOR = '<>',
|
||
|
+ RESTRICT = eqsel,
|
||
|
+ JOIN = eqjoinsel,
|
||
|
+ SORT1 = '<',
|
||
|
+ SORT2 = '<'
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR <> (
|
||
|
+ LEFTARG = mvarchar,
|
||
|
+ RIGHTARG = mchar,
|
||
|
+ PROCEDURE = mv_mc_icase_ne,
|
||
|
+ COMMUTATOR = '<>',
|
||
|
+ NEGATOR = '=',
|
||
|
+ RESTRICT = neqsel,
|
||
|
+ JOIN = neqjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+CREATE FUNCTION mv_mc_case_cmp(mvarchar, mchar)
|
||
|
+RETURNS int4
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mv_mc_case_eq(mvarchar, mchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mv_mc_case_ne(mvarchar, mchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mv_mc_case_lt(mvarchar, mchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mv_mc_case_le(mvarchar, mchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mv_mc_case_gt(mvarchar, mchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mv_mc_case_ge(mvarchar, mchar)
|
||
|
+RETURNS bool
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+
|
||
|
+CREATE OPERATOR &< (
|
||
|
+ LEFTARG = mvarchar,
|
||
|
+ RIGHTARG = mchar,
|
||
|
+ PROCEDURE = mv_mc_case_lt,
|
||
|
+ COMMUTATOR = '&>',
|
||
|
+ NEGATOR = '&>=',
|
||
|
+ RESTRICT = scalarltsel,
|
||
|
+ JOIN = scalarltjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR &> (
|
||
|
+ LEFTARG = mvarchar,
|
||
|
+ RIGHTARG = mchar,
|
||
|
+ PROCEDURE = mv_mc_case_gt,
|
||
|
+ COMMUTATOR = '&<',
|
||
|
+ NEGATOR = '&<=',
|
||
|
+ RESTRICT = scalargtsel,
|
||
|
+ JOIN = scalargtjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR &<= (
|
||
|
+ LEFTARG = mvarchar,
|
||
|
+ RIGHTARG = mchar,
|
||
|
+ PROCEDURE = mv_mc_case_le,
|
||
|
+ COMMUTATOR = '&>=',
|
||
|
+ NEGATOR = '&>',
|
||
|
+ RESTRICT = scalarltsel,
|
||
|
+ JOIN = scalarltjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR &>= (
|
||
|
+ LEFTARG = mvarchar,
|
||
|
+ RIGHTARG = mchar,
|
||
|
+ PROCEDURE = mv_mc_case_ge,
|
||
|
+ COMMUTATOR = '&<=',
|
||
|
+ NEGATOR = '&<',
|
||
|
+ RESTRICT = scalargtsel,
|
||
|
+ JOIN = scalargtjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR &= (
|
||
|
+ LEFTARG = mvarchar,
|
||
|
+ RIGHTARG = mchar,
|
||
|
+ PROCEDURE = mv_mc_case_eq,
|
||
|
+ COMMUTATOR = '&=',
|
||
|
+ NEGATOR = '&<>',
|
||
|
+ RESTRICT = eqsel,
|
||
|
+ JOIN = eqjoinsel,
|
||
|
+ SORT1 = '&<',
|
||
|
+ SORT2 = '&<'
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR &<> (
|
||
|
+ LEFTARG = mvarchar,
|
||
|
+ RIGHTARG = mchar,
|
||
|
+ PROCEDURE = mv_mc_case_ne,
|
||
|
+ COMMUTATOR = '&<>',
|
||
|
+ NEGATOR = '&=',
|
||
|
+ RESTRICT = neqsel,
|
||
|
+ JOIN = neqjoinsel
|
||
|
+);
|
||
|
+
|
||
|
+-- MCHAR - VARCHAR operations
|
||
|
+
|
||
|
+CREATE FUNCTION mchar_mvarchar_concat(mchar, mvarchar)
|
||
|
+RETURNS mvarchar
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE OPERATOR || (
|
||
|
+ LEFTARG = mchar,
|
||
|
+ RIGHTARG = mvarchar,
|
||
|
+ PROCEDURE = mchar_mvarchar_concat
|
||
|
+);
|
||
|
+
|
||
|
+CREATE FUNCTION mvarchar_mchar_concat(mvarchar, mchar)
|
||
|
+RETURNS mvarchar
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE OPERATOR || (
|
||
|
+ LEFTARG = mvarchar,
|
||
|
+ RIGHTARG = mchar,
|
||
|
+ PROCEDURE = mvarchar_mchar_concat
|
||
|
+);
|
||
|
+
|
||
|
+CREATE FUNCTION mvarchar_mchar(mvarchar, integer, boolean)
|
||
|
+RETURNS mchar
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE CAST (mvarchar as mchar)
|
||
|
+WITH FUNCTION mvarchar_mchar(mvarchar, integer, boolean) as IMPLICIT;
|
||
|
+
|
||
|
+CREATE FUNCTION mchar_mvarchar(mchar, integer, boolean)
|
||
|
+RETURNS mvarchar
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE CAST (mchar as mvarchar)
|
||
|
+WITH FUNCTION mchar_mvarchar(mchar, integer, boolean) as IMPLICIT;
|
||
|
+
|
||
|
+-- Aggregates
|
||
|
+
|
||
|
+CREATE FUNCTION mchar_larger(mchar, mchar)
|
||
|
+RETURNS mchar
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE AGGREGATE max (
|
||
|
+ BASETYPE = mchar,
|
||
|
+ SFUNC = mchar_larger,
|
||
|
+ STYPE = mchar,
|
||
|
+ SORTOP = '>'
|
||
|
+);
|
||
|
+
|
||
|
+CREATE FUNCTION mchar_smaller(mchar, mchar)
|
||
|
+RETURNS mchar
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE AGGREGATE min (
|
||
|
+ BASETYPE = mchar,
|
||
|
+ SFUNC = mchar_smaller,
|
||
|
+ STYPE = mchar,
|
||
|
+ SORTOP = '<'
|
||
|
+);
|
||
|
+
|
||
|
+CREATE FUNCTION mvarchar_larger(mvarchar, mvarchar)
|
||
|
+RETURNS mvarchar
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE AGGREGATE max (
|
||
|
+ BASETYPE = mvarchar,
|
||
|
+ SFUNC = mvarchar_larger,
|
||
|
+ STYPE = mvarchar,
|
||
|
+ SORTOP = '>'
|
||
|
+);
|
||
|
+
|
||
|
+CREATE FUNCTION mvarchar_smaller(mvarchar, mvarchar)
|
||
|
+RETURNS mvarchar
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE AGGREGATE min (
|
||
|
+ BASETYPE = mvarchar,
|
||
|
+ SFUNC = mvarchar_smaller,
|
||
|
+ STYPE = mvarchar,
|
||
|
+ SORTOP = '<'
|
||
|
+);
|
||
|
+
|
||
|
+-- B-tree support
|
||
|
+CREATE OPERATOR FAMILY icase_ops USING btree;
|
||
|
+CREATE OPERATOR FAMILY case_ops USING btree;
|
||
|
+
|
||
|
+CREATE OPERATOR CLASS mchar_icase_ops
|
||
|
+DEFAULT FOR TYPE mchar USING btree FAMILY icase_ops AS
|
||
|
+ OPERATOR 1 < ,
|
||
|
+ OPERATOR 2 <= ,
|
||
|
+ OPERATOR 3 = ,
|
||
|
+ OPERATOR 4 >= ,
|
||
|
+ OPERATOR 5 > ,
|
||
|
+ FUNCTION 1 mchar_icase_cmp(mchar, mchar),
|
||
|
+ OPERATOR 1 < (mchar, mvarchar),
|
||
|
+ OPERATOR 2 <= (mchar, mvarchar),
|
||
|
+ OPERATOR 3 = (mchar, mvarchar),
|
||
|
+ OPERATOR 4 >= (mchar, mvarchar),
|
||
|
+ OPERATOR 5 > (mchar, mvarchar),
|
||
|
+ FUNCTION 1 mc_mv_icase_cmp(mchar, mvarchar);
|
||
|
+
|
||
|
+CREATE OPERATOR CLASS mchar_case_ops
|
||
|
+FOR TYPE mchar USING btree FAMILY case_ops AS
|
||
|
+ OPERATOR 1 &< ,
|
||
|
+ OPERATOR 2 &<= ,
|
||
|
+ OPERATOR 3 &= ,
|
||
|
+ OPERATOR 4 &>= ,
|
||
|
+ OPERATOR 5 &> ,
|
||
|
+ FUNCTION 1 mchar_case_cmp(mchar, mchar),
|
||
|
+ OPERATOR 1 &< (mchar, mvarchar),
|
||
|
+ OPERATOR 2 &<= (mchar, mvarchar),
|
||
|
+ OPERATOR 3 &= (mchar, mvarchar),
|
||
|
+ OPERATOR 4 &>= (mchar, mvarchar),
|
||
|
+ OPERATOR 5 &> (mchar, mvarchar),
|
||
|
+ FUNCTION 1 mc_mv_case_cmp(mchar, mvarchar);
|
||
|
+
|
||
|
+CREATE OPERATOR CLASS mchar_icase_ops
|
||
|
+DEFAULT FOR TYPE mchar USING hash AS
|
||
|
+ OPERATOR 1 = ,
|
||
|
+ FUNCTION 1 mchar_hash(mchar);
|
||
|
+
|
||
|
+CREATE OPERATOR CLASS mvarchar_icase_ops
|
||
|
+DEFAULT FOR TYPE mvarchar USING btree FAMILY icase_ops AS
|
||
|
+ OPERATOR 1 < ,
|
||
|
+ OPERATOR 2 <= ,
|
||
|
+ OPERATOR 3 = ,
|
||
|
+ OPERATOR 4 >= ,
|
||
|
+ OPERATOR 5 > ,
|
||
|
+ FUNCTION 1 mvarchar_icase_cmp(mvarchar, mvarchar),
|
||
|
+ OPERATOR 1 < (mvarchar, mchar),
|
||
|
+ OPERATOR 2 <= (mvarchar, mchar),
|
||
|
+ OPERATOR 3 = (mvarchar, mchar),
|
||
|
+ OPERATOR 4 >= (mvarchar, mchar),
|
||
|
+ OPERATOR 5 > (mvarchar, mchar),
|
||
|
+ FUNCTION 1 mv_mc_icase_cmp(mvarchar, mchar);
|
||
|
+
|
||
|
+CREATE OPERATOR CLASS mvarchar_case_ops
|
||
|
+FOR TYPE mvarchar USING btree FAMILY case_ops AS
|
||
|
+ OPERATOR 1 &< ,
|
||
|
+ OPERATOR 2 &<= ,
|
||
|
+ OPERATOR 3 &= ,
|
||
|
+ OPERATOR 4 &>= ,
|
||
|
+ OPERATOR 5 &> ,
|
||
|
+ FUNCTION 1 mvarchar_case_cmp(mvarchar, mvarchar),
|
||
|
+ OPERATOR 1 &< (mvarchar, mchar),
|
||
|
+ OPERATOR 2 &<= (mvarchar, mchar),
|
||
|
+ OPERATOR 3 &= (mvarchar, mchar),
|
||
|
+ OPERATOR 4 &>= (mvarchar, mchar),
|
||
|
+ OPERATOR 5 &> (mvarchar, mchar),
|
||
|
+ FUNCTION 1 mv_mc_case_cmp(mvarchar, mchar);
|
||
|
+
|
||
|
+CREATE OPERATOR CLASS mvarchar_icase_ops
|
||
|
+DEFAULT FOR TYPE mvarchar USING hash AS
|
||
|
+ OPERATOR 1 = ,
|
||
|
+ FUNCTION 1 mvarchar_hash(mvarchar);
|
||
|
+
|
||
|
+
|
||
|
+-- Index support for LIKE
|
||
|
+
|
||
|
+CREATE FUNCTION mchar_pattern_fixed_prefix(internal, internal, internal)
|
||
|
+RETURNS int4
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE FUNCTION mchar_greaterstring(internal)
|
||
|
+RETURNS internal
|
||
|
+AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||
|
+
|
||
|
+CREATE OR REPLACE FUNCTION isfulleq_mchar(mchar, mchar)
|
||
|
+RETURNS bool AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C CALLED ON NULL INPUT IMMUTABLE;
|
||
|
+
|
||
|
+CREATE OR REPLACE FUNCTION fullhash_mchar(mchar)
|
||
|
+RETURNS int4 AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C CALLED ON NULL INPUT IMMUTABLE;
|
||
|
+
|
||
|
+
|
||
|
+CREATE OPERATOR == (
|
||
|
+ LEFTARG = mchar,
|
||
|
+ RIGHTARG = mchar,
|
||
|
+ PROCEDURE = isfulleq_mchar,
|
||
|
+ COMMUTATOR = '==',
|
||
|
+ RESTRICT = eqsel,
|
||
|
+ JOIN = eqjoinsel,
|
||
|
+ HASHES
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR CLASS mchar_fill_ops
|
||
|
+ FOR TYPE mchar USING hash AS
|
||
|
+ OPERATOR 1 ==,
|
||
|
+ FUNCTION 1 fullhash_mchar(mchar);
|
||
|
+
|
||
|
+CREATE OR REPLACE FUNCTION isfulleq_mvarchar(mvarchar, mvarchar)
|
||
|
+RETURNS bool AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C CALLED ON NULL INPUT IMMUTABLE;
|
||
|
+
|
||
|
+CREATE OR REPLACE FUNCTION fullhash_mvarchar(mvarchar)
|
||
|
+RETURNS int4 AS 'MODULE_PATHNAME'
|
||
|
+LANGUAGE C CALLED ON NULL INPUT IMMUTABLE;
|
||
|
+
|
||
|
+
|
||
|
+CREATE OPERATOR == (
|
||
|
+ LEFTARG = mvarchar,
|
||
|
+ RIGHTARG = mvarchar,
|
||
|
+ PROCEDURE = isfulleq_mvarchar,
|
||
|
+ COMMUTATOR = '==',
|
||
|
+ RESTRICT = eqsel,
|
||
|
+ JOIN = eqjoinsel,
|
||
|
+ HASHES
|
||
|
+);
|
||
|
+
|
||
|
+CREATE OPERATOR CLASS mvarchar_fill_ops
|
||
|
+ FOR TYPE mvarchar USING hash AS
|
||
|
+ OPERATOR 1 ==,
|
||
|
+ FUNCTION 1 fullhash_mvarchar(mvarchar);
|
||
|
+
|
||
|
+COMMIT;
|
||
|
+SET search_path = public;
|
||
|
+
|
||
|
diff --git a/contrib/mchar/mchar_io.c b/contrib/mchar/mchar_io.c
|
||
|
new file mode 100644
|
||
|
index 0000000..9179412
|
||
|
--- /dev/null
|
||
|
+++ b/contrib/mchar/mchar_io.c
|
||
|
@@ -0,0 +1,372 @@
|
||
|
+#include "mchar.h"
|
||
|
+#include "mb/pg_wchar.h"
|
||
|
+#include "fmgr.h"
|
||
|
+#include "libpq/pqformat.h"
|
||
|
+#include <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;
|
||
|
+ int32 slen = strlen(s), rlen;
|
||
|
+
|
||
|
+ pg_verifymbstr(s, slen, false);
|
||
|
+
|
||
|
+ result = (MChar*)palloc( MCHARHDRSZ + slen * sizeof(UChar) * 4 /* upper limit of length */ );
|
||
|
+ rlen = Char2UChar( s, slen, result->data );
|
||
|
+ SET_VARSIZE(result, sizeof(UChar) * rlen + MCHARHDRSZ);
|
||
|
+
|
||
|
+ mchar_strip(result, atttypmod);
|
||
|
+
|
||
|
+ PG_RETURN_MCHAR(result);
|
||
|
+}
|
||
|
+
|
||
|
+Datum
|
||
|
+mchar_out(PG_FUNCTION_ARGS) {
|
||
|
+ MChar *in = PG_GETARG_MCHAR(0);
|
||
|
+ char *out;
|
||
|
+ size_t size, inlen = UCHARLENGTH(in);
|
||
|
+ size_t charlen = u_countChar32(in->data, inlen);
|
||
|
+
|
||
|
+ Assert( in->typmod < 0 || charlen<=in->typmod );
|
||
|
+ size = ( in->typmod < 0 ) ? inlen : in->typmod;
|
||
|
+ size *= pg_database_encoding_max_length();
|
||
|
+
|
||
|
+ out = (char*)palloc( size+1 );
|
||
|
+ size = UChar2Char( in->data, inlen, out );
|
||
|
+
|
||
|
+ if ( in->typmod>0 && charlen < in->typmod ) {
|
||
|
+ memset( out+size, ' ', in->typmod - charlen);
|
||
|
+ size += in->typmod - charlen;
|
||
|
+ }
|
||
|
+
|
||
|
+ out[size] = '\0';
|
||
|
+
|
||
|
+ PG_FREE_IF_COPY(in,0);
|
||
|
+
|
||
|
+ PG_RETURN_CSTRING(out);
|
||
|
+}
|
||
|
+
|
||
|
+Datum
|
||
|
+mchar(PG_FUNCTION_ARGS) {
|
||
|
+ MChar *source = PG_GETARG_MCHAR(0);
|
||
|
+ MChar *result;
|
||
|
+ int32 typmod = PG_GETARG_INT32(1);
|
||
|
+#ifdef NOT_USED
|
||
|
+ bool isExplicit = PG_GETARG_BOOL(2);
|
||
|
+#endif
|
||
|
+
|
||
|
+ result = palloc( VARSIZE(source) );
|
||
|
+ memcpy( result, source, VARSIZE(source) );
|
||
|
+ PG_FREE_IF_COPY(source,0);
|
||
|
+
|
||
|
+ mchar_strip(result, typmod);
|
||
|
+
|
||
|
+ PG_RETURN_MCHAR(result);
|
||
|
+}
|
||
|
+
|
||
|
+Datum
|
||
|
+mvarchar_in(PG_FUNCTION_ARGS) {
|
||
|
+ char *s = PG_GETARG_CSTRING(0);
|
||
|
+#ifdef NOT_USED
|
||
|
+ Oid typelem = PG_GETARG_OID(1);
|
||
|
+#endif
|
||
|
+ int32 atttypmod = PG_GETARG_INT32(2);
|
||
|
+ MVarChar *result;
|
||
|
+ int32 slen = strlen(s), rlen;
|
||
|
+
|
||
|
+ pg_verifymbstr(s, slen, false);
|
||
|
+
|
||
|
+ result = (MVarChar*)palloc( MVARCHARHDRSZ + slen * sizeof(UChar) * 2 /* upper limit of length */ );
|
||
|
+ rlen = Char2UChar( s, slen, result->data );
|
||
|
+ SET_VARSIZE(result, sizeof(UChar) * rlen + MVARCHARHDRSZ);
|
||
|
+
|
||
|
+ if ( atttypmod > 0 && atttypmod < u_countChar32(result->data, UVARCHARLENGTH(result)) )
|
||
|
+ elog(ERROR,"value too long for type mvarchar(%d)", atttypmod);
|
||
|
+
|
||
|
+ PG_RETURN_MVARCHAR(result);
|
||
|
+}
|
||
|
+
|
||
|
+Datum
|
||
|
+mvarchar_out(PG_FUNCTION_ARGS) {
|
||
|
+ MVarChar *in = PG_GETARG_MVARCHAR(0);
|
||
|
+ char *out;
|
||
|
+ size_t size = UVARCHARLENGTH(in);
|
||
|
+
|
||
|
+ size *= pg_database_encoding_max_length();
|
||
|
+
|
||
|
+ out = (char*)palloc( size+1 );
|
||
|
+ size = UChar2Char( in->data, UVARCHARLENGTH(in), out );
|
||
|
+
|
||
|
+ out[size] = '\0';
|
||
|
+
|
||
|
+ PG_FREE_IF_COPY(in,0);
|
||
|
+
|
||
|
+ PG_RETURN_CSTRING(out);
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+mvarchar_strip(MVarChar *m, int atttypmod) {
|
||
|
+ int charlen = u_countChar32(m->data, UVARCHARLENGTH(m));
|
||
|
+
|
||
|
+ if ( atttypmod>=0 && atttypmod < charlen ) {
|
||
|
+ int i=0;
|
||
|
+ U16_FWD_N( m->data, i, charlen, atttypmod);
|
||
|
+ SET_VARSIZE(m, sizeof(UChar) * i + MVARCHARHDRSZ);
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+Datum
|
||
|
+mvarchar(PG_FUNCTION_ARGS) {
|
||
|
+ MVarChar *source = PG_GETARG_MVARCHAR(0);
|
||
|
+ MVarChar *result;
|
||
|
+ int32 typmod = PG_GETARG_INT32(1);
|
||
|
+ bool isExplicit = PG_GETARG_BOOL(2);
|
||
|
+ int charlen = u_countChar32(source->data, UVARCHARLENGTH(source));
|
||
|
+
|
||
|
+ result = palloc( VARSIZE(source) );
|
||
|
+ memcpy( result, source, VARSIZE(source) );
|
||
|
+ PG_FREE_IF_COPY(source,0);
|
||
|
+
|
||
|
+ if ( typmod>=0 && typmod < charlen ) {
|
||
|
+ if ( isExplicit )
|
||
|
+ mvarchar_strip(result, typmod);
|
||
|
+ else
|
||
|
+ elog(ERROR,"value too long for type mvarchar(%d)", typmod);
|
||
|
+ }
|
||
|
+
|
||
|
+ PG_RETURN_MVARCHAR(result);
|
||
|
+}
|
||
|
+
|
||
|
+PG_FUNCTION_INFO_V1(mvarchar_mchar);
|
||
|
+Datum mvarchar_mchar(PG_FUNCTION_ARGS);
|
||
|
+Datum
|
||
|
+mvarchar_mchar(PG_FUNCTION_ARGS) {
|
||
|
+ MVarChar *source = PG_GETARG_MVARCHAR(0);
|
||
|
+ MChar *result;
|
||
|
+ int32 typmod = PG_GETARG_INT32(1);
|
||
|
+#ifdef NOT_USED
|
||
|
+ bool isExplicit = PG_GETARG_BOOL(2);
|
||
|
+#endif
|
||
|
+
|
||
|
+ result = palloc( MVARCHARLENGTH(source) + MCHARHDRSZ );
|
||
|
+ SET_VARSIZE(result, MVARCHARLENGTH(source) + MCHARHDRSZ);
|
||
|
+ memcpy( result->data, source->data, MVARCHARLENGTH(source));
|
||
|
+
|
||
|
+ PG_FREE_IF_COPY(source,0);
|
||
|
+
|
||
|
+ mchar_strip( result, typmod );
|
||
|
+
|
||
|
+ PG_RETURN_MCHAR(result);
|
||
|
+}
|
||
|
+
|
||
|
+PG_FUNCTION_INFO_V1(mchar_mvarchar);
|
||
|
+Datum mchar_mvarchar(PG_FUNCTION_ARGS);
|
||
|
+Datum
|
||
|
+mchar_mvarchar(PG_FUNCTION_ARGS) {
|
||
|
+ MChar *source = PG_GETARG_MCHAR(0);
|
||
|
+ MVarChar *result;
|
||
|
+ int32 typmod = PG_GETARG_INT32(1);
|
||
|
+ int32 scharlen = u_countChar32(source->data, UCHARLENGTH(source));
|
||
|
+ int32 curlen = 0, maxcharlen;
|
||
|
+#ifdef NOT_USED
|
||
|
+ bool isExplicit = PG_GETARG_BOOL(2);
|
||
|
+#endif
|
||
|
+
|
||
|
+ maxcharlen = (source->typmod > 0) ? source->typmod : scharlen;
|
||
|
+
|
||
|
+ result = palloc( MVARCHARHDRSZ + sizeof(UChar) * 2 * maxcharlen );
|
||
|
+
|
||
|
+ curlen = UCHARLENGTH( source );
|
||
|
+ if ( curlen > 0 )
|
||
|
+ memcpy( result->data, source->data, MCHARLENGTH(source) );
|
||
|
+ if ( source->typmod > 0 && scharlen < source->typmod ) {
|
||
|
+ FillWhiteSpace( result->data + curlen, source->typmod-scharlen );
|
||
|
+ curlen += source->typmod-scharlen;
|
||
|
+ }
|
||
|
+ SET_VARSIZE(result, MVARCHARHDRSZ + curlen *sizeof(UChar));
|
||
|
+
|
||
|
+ PG_FREE_IF_COPY(source,0);
|
||
|
+
|
||
|
+ mvarchar_strip( result, typmod );
|
||
|
+
|
||
|
+ PG_RETURN_MCHAR(result);
|
||
|
+}
|
||
|
+
|
||
|
+PG_FUNCTION_INFO_V1(mchar_send);
|
||
|
+Datum mchar_send(PG_FUNCTION_ARGS);
|
||
|
+Datum
|
||
|
+mchar_send(PG_FUNCTION_ARGS) {
|
||
|
+ MChar *in = PG_GETARG_MCHAR(0);
|
||
|
+ size_t inlen = UCHARLENGTH(in);
|
||
|
+ size_t charlen = u_countChar32(in->data, inlen);
|
||
|
+ StringInfoData buf;
|
||
|
+
|
||
|
+ Assert( in->typmod < 0 || charlen<=in->typmod );
|
||
|
+
|
||
|
+ pq_begintypsend(&buf);
|
||
|
+ pq_sendbytes(&buf, (char*)in->data, inlen * sizeof(UChar) );
|
||
|
+
|
||
|
+ if ( in->typmod>0 && charlen < in->typmod ) {
|
||
|
+ int nw = in->typmod - charlen;
|
||
|
+ UChar *white = palloc( sizeof(UChar) * nw );
|
||
|
+
|
||
|
+ FillWhiteSpace( white, nw );
|
||
|
+ pq_sendbytes(&buf, (char*)white, sizeof(UChar) * nw);
|
||
|
+ pfree(white);
|
||
|
+ }
|
||
|
+
|
||
|
+ PG_FREE_IF_COPY(in,0);
|
||
|
+
|
||
|
+ PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
|
||
|
+}
|
||
|
+
|
||
|
+PG_FUNCTION_INFO_V1(mchar_recv);
|
||
|
+Datum mchar_recv(PG_FUNCTION_ARGS);
|
||
|
+Datum
|
||
|
+mchar_recv(PG_FUNCTION_ARGS) {
|
||
|
+ StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
|
||
|
+ MChar *res;
|
||
|
+ int nbytes;
|
||
|
+#ifdef NOT_USED
|
||
|
+ Oid typelem = PG_GETARG_OID(1);
|
||
|
+#endif
|
||
|
+ int32 atttypmod = PG_GETARG_INT32(2);
|
||
|
+
|
||
|
+ nbytes = buf->len - buf->cursor;
|
||
|
+ res = (MChar*)palloc( nbytes + MCHARHDRSZ );
|
||
|
+ res->len = nbytes + MCHARHDRSZ;
|
||
|
+ res->typmod = -1;
|
||
|
+ SET_VARSIZE(res, res->len);
|
||
|
+ pq_copymsgbytes(buf, (char*)res->data, nbytes);
|
||
|
+
|
||
|
+ mchar_strip( res, atttypmod );
|
||
|
+
|
||
|
+ PG_RETURN_MCHAR(res);
|
||
|
+}
|
||
|
+
|
||
|
+PG_FUNCTION_INFO_V1(mvarchar_send);
|
||
|
+Datum mvarchar_send(PG_FUNCTION_ARGS);
|
||
|
+Datum
|
||
|
+mvarchar_send(PG_FUNCTION_ARGS) {
|
||
|
+ MVarChar *in = PG_GETARG_MVARCHAR(0);
|
||
|
+ size_t inlen = UVARCHARLENGTH(in);
|
||
|
+ StringInfoData buf;
|
||
|
+
|
||
|
+ pq_begintypsend(&buf);
|
||
|
+ pq_sendbytes(&buf, (char*)in->data, inlen * sizeof(UChar) );
|
||
|
+
|
||
|
+ PG_FREE_IF_COPY(in,0);
|
||
|
+
|
||
|
+ PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
|
||
|
+}
|
||
|
+
|
||
|
+PG_FUNCTION_INFO_V1(mvarchar_recv);
|
||
|
+Datum mvarchar_recv(PG_FUNCTION_ARGS);
|
||
|
+Datum
|
||
|
+mvarchar_recv(PG_FUNCTION_ARGS) {
|
||
|
+ StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
|
||
|
+ MVarChar *res;
|
||
|
+ int nbytes;
|
||
|
+#ifdef NOT_USED
|
||
|
+ Oid typelem = PG_GETARG_OID(1);
|
||
|
+#endif
|
||
|
+ int32 atttypmod = PG_GETARG_INT32(2);
|
||
|
+
|
||
|
+ nbytes = buf->len - buf->cursor;
|
||
|
+ res = (MVarChar*)palloc( nbytes + MVARCHARHDRSZ );
|
||
|
+ res->len = nbytes + MVARCHARHDRSZ;
|
||
|
+ SET_VARSIZE(res, res->len);
|
||
|
+ pq_copymsgbytes(buf, (char*)res->data, nbytes);
|
||
|
+
|
||
|
+ mvarchar_strip( res, atttypmod );
|
||
|
+
|
||
|
+ PG_RETURN_MVARCHAR(res);
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
diff --git a/contrib/mchar/mchar_like.c b/contrib/mchar/mchar_like.c
|
||
|
new file mode 100644
|
||
|
index 0000000..3af13d8
|
||
|
--- /dev/null
|
||
|
+++ b/contrib/mchar/mchar_like.c
|
||
|
@@ -0,0 +1,862 @@
|
||
|
+#include "mchar.h"
|
||
|
+#include "mb/pg_wchar.h"
|
||
|
+
|
||
|
+#include "catalog/pg_collation.h"
|
||
|
+#include "utils/selfuncs.h"
|
||
|
+#include "nodes/primnodes.h"
|
||
|
+#include "nodes/makefuncs.h"
|
||
|
+#include "regex/regex.h"
|
||
|
+
|
||
|
+/*
|
||
|
+** Originally written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.
|
||
|
+** Rich $alz is now <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;
|
||
|
+ int32 slen, prefixlen=0, restlen=0, i=0;
|
||
|
+ MVarChar *sprefix;
|
||
|
+ MVarChar *srest;
|
||
|
+ Pattern_Prefix_Status status = Pattern_Prefix_None;
|
||
|
+
|
||
|
+ *prefix = NULL;
|
||
|
+
|
||
|
+ if ( ptype != Pattern_Type_Like )
|
||
|
+ PG_RETURN_INT32(Pattern_Prefix_None);
|
||
|
+
|
||
|
+ SET_UCHAR;
|
||
|
+
|
||
|
+ spatt = ((MVarChar*)DatumGetPointer(patt->constvalue))->data;
|
||
|
+ slen = UVARCHARLENGTH( DatumGetPointer(patt->constvalue) );
|
||
|
+
|
||
|
+ sprefix = (MVarChar*)palloc( MCHARHDRSZ /*The biggest hdr!! */ + sizeof(UChar) * slen );
|
||
|
+ srest = (MVarChar*)palloc( MCHARHDRSZ /*The biggest hdr!! */ + sizeof(UChar) * slen );
|
||
|
+
|
||
|
+ while( prefixlen < slen && i < slen ) {
|
||
|
+ if ( spatt[i] == UCharPercent || spatt[i] == UCharUnderLine )
|
||
|
+ break;
|
||
|
+ else if ( spatt[i] == UCharBackSlesh ) {
|
||
|
+ i++;
|
||
|
+ if ( i>= slen )
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ sprefix->data[ prefixlen++ ] = spatt[i++];
|
||
|
+ }
|
||
|
+
|
||
|
+ while( prefixlen > 0 ) {
|
||
|
+ if ( ! u_isspace( sprefix->data[ prefixlen-1 ] ) )
|
||
|
+ break;
|
||
|
+ prefixlen--;
|
||
|
+ }
|
||
|
+
|
||
|
+ if ( prefixlen == 0 )
|
||
|
+ PG_RETURN_INT32(Pattern_Prefix_None);
|
||
|
+
|
||
|
+ for(;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..165d44c
|
||
|
--- /dev/null
|
||
|
+++ b/contrib/mchar/mchar_proc.c
|
||
|
@@ -0,0 +1,339 @@
|
||
|
+#include "mchar.h"
|
||
|
+#include "mb/pg_wchar.h"
|
||
|
+#include "access/hash.h"
|
||
|
+
|
||
|
+PG_FUNCTION_INFO_V1(mchar_length);
|
||
|
+Datum mchar_length(PG_FUNCTION_ARGS);
|
||
|
+
|
||
|
+Datum
|
||
|
+mchar_length(PG_FUNCTION_ARGS) {
|
||
|
+ MChar *m = PG_GETARG_MCHAR(0);
|
||
|
+ int32 l = UCHARLENGTH(m);
|
||
|
+
|
||
|
+ while( l>0 && m_isspace( m->data[ l-1 ] ) )
|
||
|
+ l--;
|
||
|
+
|
||
|
+ l = u_countChar32(m->data, l);
|
||
|
+
|
||
|
+ PG_FREE_IF_COPY(m,0);
|
||
|
+
|
||
|
+ PG_RETURN_INT32(l);
|
||
|
+}
|
||
|
+
|
||
|
+PG_FUNCTION_INFO_V1(mvarchar_length);
|
||
|
+Datum mvarchar_length(PG_FUNCTION_ARGS);
|
||
|
+
|
||
|
+Datum
|
||
|
+mvarchar_length(PG_FUNCTION_ARGS) {
|
||
|
+ MVarChar *m = PG_GETARG_MVARCHAR(0);
|
||
|
+ int32 l = UVARCHARLENGTH(m);
|
||
|
+
|
||
|
+ while( l>0 && m_isspace( m->data[ l-1 ] ) )
|
||
|
+ l--;
|
||
|
+
|
||
|
+ l = u_countChar32(m->data, l);
|
||
|
+
|
||
|
+ PG_FREE_IF_COPY(m,0);
|
||
|
+
|
||
|
+ PG_RETURN_INT32(l);
|
||
|
+}
|
||
|
+
|
||
|
+static int32
|
||
|
+uchar_substring(
|
||
|
+ UChar *str, int32 strl,
|
||
|
+ int32 start, int32 length, bool length_not_specified,
|
||
|
+ UChar *dst) {
|
||
|
+ int32 S = start-1; /* start position */
|
||
|
+ int32 S1; /* adjusted start position */
|
||
|
+ int32 L1; /* adjusted substring length */
|
||
|
+ int32 subbegin=0, subend=0;
|
||
|
+
|
||
|
+ S1 = Max(S, 0);
|
||
|
+ if (length_not_specified)
|
||
|
+ L1 = -1;
|
||
|
+ else {
|
||
|
+ /* end position */
|
||
|
+ int32 E = S + length;
|
||
|
+
|
||
|
+ /*
|
||
|
+ * A negative value for L is the only way for the end position to
|
||
|
+ * be before the start. SQL99 says to throw an error.
|
||
|
+ */
|
||
|
+
|
||
|
+ if (E < S)
|
||
|
+ ereport(ERROR,
|
||
|
+ (errcode(ERRCODE_SUBSTRING_ERROR),
|
||
|
+ errmsg("negative substring length not allowed")));
|
||
|
+
|
||
|
+ /*
|
||
|
+ * A zero or negative value for the end position can happen if the
|
||
|
+ * start was negative or one. SQL99 says to return a zero-length
|
||
|
+ * string.
|
||
|
+ */
|
||
|
+ if (E < 0)
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ L1 = E - S1;
|
||
|
+ }
|
||
|
+
|
||
|
+ U16_FWD_N( str, subbegin, strl, S1 );
|
||
|
+ if ( subbegin >= strl )
|
||
|
+ return 0;
|
||
|
+ subend = subbegin;
|
||
|
+ U16_FWD_N( str, subend, strl, L1 );
|
||
|
+
|
||
|
+ memcpy( dst, str+subbegin, sizeof(UChar)*(subend-subbegin) );
|
||
|
+
|
||
|
+ return subend-subbegin;
|
||
|
+}
|
||
|
+
|
||
|
+PG_FUNCTION_INFO_V1(mchar_substring);
|
||
|
+Datum mchar_substring(PG_FUNCTION_ARGS);
|
||
|
+Datum
|
||
|
+mchar_substring(PG_FUNCTION_ARGS) {
|
||
|
+ MChar *src = PG_GETARG_MCHAR(0);
|
||
|
+ MChar *dst;
|
||
|
+ int32 length;
|
||
|
+
|
||
|
+ dst = (MChar*)palloc( VARSIZE(src) );
|
||
|
+ length = uchar_substring(
|
||
|
+ src->data, UCHARLENGTH(src),
|
||
|
+ PG_GETARG_INT32(1), PG_GETARG_INT32(2), false,
|
||
|
+ dst->data);
|
||
|
+
|
||
|
+ dst->typmod = src->typmod;
|
||
|
+ SET_VARSIZE(dst, MCHARHDRSZ + length *sizeof(UChar));
|
||
|
+
|
||
|
+ PG_FREE_IF_COPY(src, 0);
|
||
|
+ PG_RETURN_MCHAR(dst);
|
||
|
+}
|
||
|
+
|
||
|
+PG_FUNCTION_INFO_V1(mchar_substring_no_len);
|
||
|
+Datum mchar_substring_no_len(PG_FUNCTION_ARGS);
|
||
|
+Datum
|
||
|
+mchar_substring_no_len(PG_FUNCTION_ARGS) {
|
||
|
+ MChar *src = PG_GETARG_MCHAR(0);
|
||
|
+ MChar *dst;
|
||
|
+ int32 length;
|
||
|
+
|
||
|
+ dst = (MChar*)palloc( VARSIZE(src) );
|
||
|
+ length = uchar_substring(
|
||
|
+ src->data, UCHARLENGTH(src),
|
||
|
+ PG_GETARG_INT32(1), -1, true,
|
||
|
+ dst->data);
|
||
|
+
|
||
|
+ dst->typmod = src->typmod;
|
||
|
+ SET_VARSIZE(dst, MCHARHDRSZ + length *sizeof(UChar));
|
||
|
+
|
||
|
+ PG_FREE_IF_COPY(src, 0);
|
||
|
+ PG_RETURN_MCHAR(dst);
|
||
|
+}
|
||
|
+
|
||
|
+PG_FUNCTION_INFO_V1(mvarchar_substring);
|
||
|
+Datum mvarchar_substring(PG_FUNCTION_ARGS);
|
||
|
+Datum
|
||
|
+mvarchar_substring(PG_FUNCTION_ARGS) {
|
||
|
+ MVarChar *src = PG_GETARG_MVARCHAR(0);
|
||
|
+ MVarChar *dst;
|
||
|
+ int32 length;
|
||
|
+
|
||
|
+ dst = (MVarChar*)palloc( VARSIZE(src) );
|
||
|
+ length = uchar_substring(
|
||
|
+ src->data, UVARCHARLENGTH(src),
|
||
|
+ PG_GETARG_INT32(1), PG_GETARG_INT32(2), false,
|
||
|
+ dst->data);
|
||
|
+
|
||
|
+ SET_VARSIZE(dst, MVARCHARHDRSZ + length *sizeof(UChar));
|
||
|
+
|
||
|
+ PG_FREE_IF_COPY(src, 0);
|
||
|
+ PG_RETURN_MVARCHAR(dst);
|
||
|
+}
|
||
|
+
|
||
|
+PG_FUNCTION_INFO_V1(mvarchar_substring_no_len);
|
||
|
+Datum mvarchar_substring_no_len(PG_FUNCTION_ARGS);
|
||
|
+Datum
|
||
|
+mvarchar_substring_no_len(PG_FUNCTION_ARGS) {
|
||
|
+ MVarChar *src = PG_GETARG_MVARCHAR(0);
|
||
|
+ MVarChar *dst;
|
||
|
+ int32 length;
|
||
|
+
|
||
|
+ dst = (MVarChar*)palloc( VARSIZE(src) );
|
||
|
+ length = uchar_substring(
|
||
|
+ src->data, UVARCHARLENGTH(src),
|
||
|
+ PG_GETARG_INT32(1), -1, true,
|
||
|
+ dst->data);
|
||
|
+
|
||
|
+ SET_VARSIZE(dst, MVARCHARHDRSZ + length *sizeof(UChar));
|
||
|
+
|
||
|
+ PG_FREE_IF_COPY(src, 0);
|
||
|
+ PG_RETURN_MVARCHAR(dst);
|
||
|
+}
|
||
|
+
|
||
|
+static Datum
|
||
|
+hash_uchar( UChar *s, int len ) {
|
||
|
+ int32 length;
|
||
|
+ UErrorCode err = 0;
|
||
|
+ UChar *d;
|
||
|
+ Datum res;
|
||
|
+
|
||
|
+ if ( len == 0 )
|
||
|
+ return hash_any( NULL, 0 );
|
||
|
+
|
||
|
+ err = 0;
|
||
|
+ d = (UChar*) palloc( sizeof(UChar) * len * 2 );
|
||
|
+ length = u_strFoldCase(d, len*2, s, len, U_FOLD_CASE_DEFAULT, &err);
|
||
|
+
|
||
|
+ if ( U_FAILURE(err) )
|
||
|
+ elog(ERROR,"ICU u_strFoldCase fails and returns %d (%s)", err, u_errorName(err));
|
||
|
+
|
||
|
+ res = hash_any( (unsigned char*) d, length * sizeof(UChar) );
|
||
|
+
|
||
|
+ pfree(d);
|
||
|
+ return res;
|
||
|
+}
|
||
|
+
|
||
|
+PG_FUNCTION_INFO_V1(mvarchar_hash);
|
||
|
+Datum
|
||
|
+mvarchar_hash(PG_FUNCTION_ARGS) {
|
||
|
+ MVarChar *src = PG_GETARG_MVARCHAR(0);
|
||
|
+ Datum res;
|
||
|
+
|
||
|
+ res = hash_uchar( src->data, lengthWithoutSpaceVarChar(src) );
|
||
|
+
|
||
|
+ PG_FREE_IF_COPY(src, 0);
|
||
|
+ PG_RETURN_DATUM( res );
|
||
|
+}
|
||
|
+
|
||
|
+PG_FUNCTION_INFO_V1(mchar_hash);
|
||
|
+Datum
|
||
|
+mchar_hash(PG_FUNCTION_ARGS) {
|
||
|
+ MChar *src = PG_GETARG_MCHAR(0);
|
||
|
+ Datum res;
|
||
|
+
|
||
|
+ res = hash_uchar( src->data, lengthWithoutSpaceChar(src) );
|
||
|
+
|
||
|
+ PG_FREE_IF_COPY(src, 0);
|
||
|
+ PG_RETURN_DATUM( res );
|
||
|
+}
|
||
|
+
|
||
|
+PG_FUNCTION_INFO_V1(mchar_upper);
|
||
|
+Datum mchar_upper(PG_FUNCTION_ARGS);
|
||
|
+Datum
|
||
|
+mchar_upper(PG_FUNCTION_ARGS) {
|
||
|
+ MChar *src = PG_GETARG_MCHAR(0);
|
||
|
+ MChar *dst = (MChar*)palloc( VARSIZE(src) * 2 );
|
||
|
+
|
||
|
+ dst->len = MCHARHDRSZ;
|
||
|
+ dst->typmod = src->typmod;
|
||
|
+ if ( UCHARLENGTH(src) != 0 ) {
|
||
|
+ int length;
|
||
|
+ UErrorCode err=0;
|
||
|
+
|
||
|
+ length = u_strToUpper( dst->data, VARSIZE(src) * 2 - MCHARHDRSZ,
|
||
|
+ src->data, UCHARLENGTH(src),
|
||
|
+ NULL, &err );
|
||
|
+
|
||
|
+ Assert( length <= VARSIZE(src) * 2 - MCHARHDRSZ );
|
||
|
+
|
||
|
+ if ( U_FAILURE(err) )
|
||
|
+ elog(ERROR,"ICU u_strToUpper fails and returns %d (%s)", err, u_errorName(err));
|
||
|
+
|
||
|
+ dst->len += sizeof(UChar) * length;
|
||
|
+ }
|
||
|
+
|
||
|
+ SET_VARSIZE( dst, dst->len );
|
||
|
+ PG_FREE_IF_COPY(src, 0);
|
||
|
+ PG_RETURN_MCHAR( dst );
|
||
|
+}
|
||
|
+
|
||
|
+PG_FUNCTION_INFO_V1(mchar_lower);
|
||
|
+Datum mchar_lower(PG_FUNCTION_ARGS);
|
||
|
+Datum
|
||
|
+mchar_lower(PG_FUNCTION_ARGS) {
|
||
|
+ MChar *src = PG_GETARG_MCHAR(0);
|
||
|
+ MChar *dst = (MChar*)palloc( VARSIZE(src) * 2 );
|
||
|
+
|
||
|
+ dst->len = MCHARHDRSZ;
|
||
|
+ dst->typmod = src->typmod;
|
||
|
+ if ( UCHARLENGTH(src) != 0 ) {
|
||
|
+ int length;
|
||
|
+ UErrorCode err=0;
|
||
|
+
|
||
|
+ length = u_strToLower( dst->data, VARSIZE(src) * 2 - MCHARHDRSZ,
|
||
|
+ src->data, UCHARLENGTH(src),
|
||
|
+ NULL, &err );
|
||
|
+
|
||
|
+ Assert( length <= VARSIZE(src) * 2 - MCHARHDRSZ );
|
||
|
+
|
||
|
+ if ( U_FAILURE(err) )
|
||
|
+ elog(ERROR,"ICU u_strToLower fails and returns %d (%s)", err, u_errorName(err));
|
||
|
+
|
||
|
+ dst->len += sizeof(UChar) * length;
|
||
|
+ }
|
||
|
+
|
||
|
+ SET_VARSIZE( dst, dst->len );
|
||
|
+ PG_FREE_IF_COPY(src, 0);
|
||
|
+ PG_RETURN_MCHAR( dst );
|
||
|
+}
|
||
|
+
|
||
|
+PG_FUNCTION_INFO_V1(mvarchar_upper);
|
||
|
+Datum mvarchar_upper(PG_FUNCTION_ARGS);
|
||
|
+Datum
|
||
|
+mvarchar_upper(PG_FUNCTION_ARGS) {
|
||
|
+ MVarChar *src = PG_GETARG_MVARCHAR(0);
|
||
|
+ MVarChar *dst = (MVarChar*)palloc( VARSIZE(src) * 2 );
|
||
|
+
|
||
|
+ dst->len = MVARCHARHDRSZ;
|
||
|
+
|
||
|
+ if ( UVARCHARLENGTH(src) != 0 ) {
|
||
|
+ int length;
|
||
|
+ UErrorCode err=0;
|
||
|
+
|
||
|
+ length = u_strToUpper( dst->data, VARSIZE(src) * 2 - MVARCHARHDRSZ,
|
||
|
+ src->data, UVARCHARLENGTH(src),
|
||
|
+ NULL, &err );
|
||
|
+
|
||
|
+ Assert( length <= VARSIZE(src) * 2 - MVARCHARHDRSZ );
|
||
|
+
|
||
|
+ if ( U_FAILURE(err) )
|
||
|
+ elog(ERROR,"ICU u_strToUpper fails and returns %d (%s)", err, u_errorName(err));
|
||
|
+
|
||
|
+ dst->len += sizeof(UChar) * length;
|
||
|
+ }
|
||
|
+
|
||
|
+ SET_VARSIZE( dst, dst->len );
|
||
|
+ PG_FREE_IF_COPY(src, 0);
|
||
|
+ PG_RETURN_MVARCHAR( dst );
|
||
|
+}
|
||
|
+
|
||
|
+PG_FUNCTION_INFO_V1(mvarchar_lower);
|
||
|
+Datum mvarchar_lower(PG_FUNCTION_ARGS);
|
||
|
+Datum
|
||
|
+mvarchar_lower(PG_FUNCTION_ARGS) {
|
||
|
+ MVarChar *src = PG_GETARG_MVARCHAR(0);
|
||
|
+ MVarChar *dst = (MVarChar*)palloc( VARSIZE(src) * 2 );
|
||
|
+
|
||
|
+ dst->len = MVARCHARHDRSZ;
|
||
|
+
|
||
|
+ if ( UVARCHARLENGTH(src) != 0 ) {
|
||
|
+ int length;
|
||
|
+ UErrorCode err=0;
|
||
|
+
|
||
|
+ length = u_strToLower( dst->data, VARSIZE(src) * 2 - MVARCHARHDRSZ,
|
||
|
+ src->data, UVARCHARLENGTH(src),
|
||
|
+ NULL, &err );
|
||
|
+
|
||
|
+ Assert( length <= VARSIZE(src) * 2 - MVARCHARHDRSZ );
|
||
|
+
|
||
|
+ if ( U_FAILURE(err) )
|
||
|
+ elog(ERROR,"ICU u_strToLower fails and returns %d (%s)", err, u_errorName(err));
|
||
|
+
|
||
|
+ dst->len += sizeof(UChar) * length;
|
||
|
+ }
|
||
|
+
|
||
|
+ SET_VARSIZE( dst, dst->len );
|
||
|
+ PG_FREE_IF_COPY(src, 0);
|
||
|
+ PG_RETURN_MVARCHAR( dst );
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
diff --git a/contrib/mchar/mchar_recode.c b/contrib/mchar/mchar_recode.c
|
||
|
new file mode 100644
|
||
|
index 0000000..d4f3659
|
||
|
--- /dev/null
|
||
|
+++ b/contrib/mchar/mchar_recode.c
|
||
|
@@ -0,0 +1,142 @@
|
||
|
+#include "mchar.h"
|
||
|
+
|
||
|
+#include "unicode/ucol.h"
|
||
|
+#include "unicode/ucnv.h"
|
||
|
+
|
||
|
+static UConverter *cnvDB = NULL;
|
||
|
+static UCollator *colCaseInsensitive = NULL;
|
||
|
+static UCollator *colCaseSensitive = NULL;
|
||
|
+
|
||
|
+static void
|
||
|
+createUObjs() {
|
||
|
+ if ( !cnvDB ) {
|
||
|
+ UErrorCode err = 0;
|
||
|
+
|
||
|
+ if ( GetDatabaseEncoding() == PG_UTF8 )
|
||
|
+ cnvDB = ucnv_open("UTF8", &err);
|
||
|
+ else
|
||
|
+ cnvDB = ucnv_open(NULL, &err);
|
||
|
+ if ( U_FAILURE(err) || cnvDB == NULL )
|
||
|
+ elog(ERROR,"ICU ucnv_open returns %d (%s)", err, u_errorName(err));
|
||
|
+ }
|
||
|
+
|
||
|
+ if ( !colCaseInsensitive ) {
|
||
|
+ UErrorCode err = 0;
|
||
|
+
|
||
|
+ colCaseInsensitive = ucol_open("", &err);
|
||
|
+ if ( U_FAILURE(err) || cnvDB == NULL ) {
|
||
|
+ if ( colCaseSensitive )
|
||
|
+ ucol_close( colCaseSensitive );
|
||
|
+ colCaseSensitive = NULL;
|
||
|
+ elog(ERROR,"ICU ucol_open returns %d (%s)", err, u_errorName(err));
|
||
|
+ }
|
||
|
+
|
||
|
+ ucol_setStrength( colCaseInsensitive, UCOL_SECONDARY );
|
||
|
+ }
|
||
|
+
|
||
|
+ if ( !colCaseSensitive ) {
|
||
|
+ UErrorCode err = 0;
|
||
|
+
|
||
|
+ colCaseSensitive = ucol_open("", &err);
|
||
|
+ if ( U_FAILURE(err) || cnvDB == NULL ) {
|
||
|
+ if ( colCaseSensitive )
|
||
|
+ ucol_close( colCaseSensitive );
|
||
|
+ colCaseSensitive = NULL;
|
||
|
+ elog(ERROR,"ICU ucol_open returns %d (%s)", err, u_errorName(err));
|
||
|
+ }
|
||
|
+
|
||
|
+ ucol_setAttribute(colCaseSensitive, UCOL_CASE_FIRST, UCOL_UPPER_FIRST, &err);
|
||
|
+ if (U_FAILURE(err)) {
|
||
|
+ if ( colCaseSensitive )
|
||
|
+ ucol_close( colCaseSensitive );
|
||
|
+ colCaseSensitive = NULL;
|
||
|
+ elog(ERROR,"ICU ucol_setAttribute returns %d (%s)", err, u_errorName(err));
|
||
|
+ }
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+int
|
||
|
+Char2UChar(const char * src, int srclen, UChar *dst) {
|
||
|
+ int dstlen=0;
|
||
|
+ UErrorCode err = 0;
|
||
|
+
|
||
|
+ createUObjs();
|
||
|
+ dstlen = ucnv_toUChars( cnvDB, dst, srclen*4, src, srclen, &err );
|
||
|
+ if ( U_FAILURE(err))
|
||
|
+ elog(ERROR,"ICU ucnv_toUChars returns %d (%s)", err, u_errorName(err));
|
||
|
+
|
||
|
+ return dstlen;
|
||
|
+}
|
||
|
+
|
||
|
+int
|
||
|
+UChar2Char(const UChar * src, int srclen, char *dst) {
|
||
|
+ int dstlen=0;
|
||
|
+ UErrorCode err = 0;
|
||
|
+
|
||
|
+ createUObjs();
|
||
|
+ dstlen = ucnv_fromUChars( cnvDB, dst, srclen*4, src, srclen, &err );
|
||
|
+ if ( U_FAILURE(err) )
|
||
|
+ elog(ERROR,"ICU ucnv_fromUChars returns %d (%s)", err, u_errorName(err));
|
||
|
+
|
||
|
+ return dstlen;
|
||
|
+}
|
||
|
+
|
||
|
+int
|
||
|
+UChar2Wchar(UChar * src, int srclen, pg_wchar *dst) {
|
||
|
+ int dstlen=0;
|
||
|
+ char *utf = palloc(sizeof(char)*srclen*4);
|
||
|
+
|
||
|
+ dstlen = UChar2Char(src, srclen, utf);
|
||
|
+ dstlen = pg_mb2wchar_with_len( utf, dst, dstlen );
|
||
|
+ pfree(utf);
|
||
|
+
|
||
|
+ return dstlen;
|
||
|
+}
|
||
|
+
|
||
|
+static UChar UCharWhiteSpace = 0;
|
||
|
+
|
||
|
+void
|
||
|
+FillWhiteSpace( UChar *dst, int n ) {
|
||
|
+ if ( UCharWhiteSpace == 0 ) {
|
||
|
+ int len;
|
||
|
+ UErrorCode err = 0;
|
||
|
+
|
||
|
+ u_strFromUTF8( &UCharWhiteSpace, 1, &len, " ", 1, &err);
|
||
|
+
|
||
|
+ Assert( len==1 );
|
||
|
+ Assert( !U_FAILURE(err) );
|
||
|
+ }
|
||
|
+
|
||
|
+ while( n-- > 0 )
|
||
|
+ *dst++ = UCharWhiteSpace;
|
||
|
+}
|
||
|
+
|
||
|
+int
|
||
|
+UCharCaseCompare(UChar * a, int alen, UChar *b, int blen) {
|
||
|
+ int len = Min(alen, blen);
|
||
|
+ int res;
|
||
|
+
|
||
|
+ createUObjs();
|
||
|
+
|
||
|
+ res = (int)ucol_strcoll( colCaseInsensitive,
|
||
|
+ a, len,
|
||
|
+ b, len);
|
||
|
+ if ( res == 0 && alen != blen )
|
||
|
+ return (alen > blen) ? 1 : - 1;
|
||
|
+ return res;
|
||
|
+}
|
||
|
+
|
||
|
+int
|
||
|
+UCharCompare(UChar * a, int alen, UChar *b, int blen) {
|
||
|
+ int len = Min(alen, blen);
|
||
|
+ int res;
|
||
|
+
|
||
|
+ createUObjs();
|
||
|
+
|
||
|
+ res = (int)ucol_strcoll( colCaseSensitive,
|
||
|
+ a, len,
|
||
|
+ b, len);
|
||
|
+ if ( res == 0 && alen != blen )
|
||
|
+ return (alen > blen) ? 1 : - 1;
|
||
|
+ return res;
|
||
|
+}
|
||
|
diff --git a/contrib/mchar/sql/compat.sql b/contrib/mchar/sql/compat.sql
|
||
|
new file mode 100644
|
||
|
index 0000000..d5b6a98
|
||
|
--- /dev/null
|
||
|
+++ b/contrib/mchar/sql/compat.sql
|
||
|
@@ -0,0 +1,11 @@
|
||
|
+--- table based checks
|
||
|
+
|
||
|
+select '<' || ch || '>', '<' || vch || '>' from chvch;
|
||
|
+select * from chvch where vch = 'One space';
|
||
|
+select * from chvch where vch = 'One space ';
|
||
|
+
|
||
|
+select * from ch where chcol = 'abcd' order by chcol;
|
||
|
+select * from ch t1 join ch t2 on t1.chcol = t2.chcol order by t1.chcol, t2.chcol;
|
||
|
+select * from ch where chcol > 'abcd' and chcol<'ee';
|
||
|
+select * from ch order by chcol;
|
||
|
+
|
||
|
diff --git a/contrib/mchar/sql/init.sql b/contrib/mchar/sql/init.sql
|
||
|
new file mode 100644
|
||
|
index 0000000..a303feb
|
||
|
--- /dev/null
|
||
|
+++ b/contrib/mchar/sql/init.sql
|
||
|
@@ -0,0 +1,33 @@
|
||
|
+
|
||
|
+--
|
||
|
+-- first, define the datatype. Turn off echoing so that expected file
|
||
|
+-- does not depend on contents of mchar.sql.
|
||
|
+--
|
||
|
+
|
||
|
+\set ECHO none
|
||
|
+\i mchar.sql
|
||
|
+--- load for table based checks
|
||
|
+SET search_path = public;
|
||
|
+\set ECHO all
|
||
|
+
|
||
|
+create table ch (
|
||
|
+ chcol mchar(32)
|
||
|
+) without oids;
|
||
|
+
|
||
|
+insert into ch values('abcd');
|
||
|
+insert into ch values('AbcD');
|
||
|
+insert into ch values('abcz');
|
||
|
+insert into ch values('defg');
|
||
|
+insert into ch values('dEfg');
|
||
|
+insert into ch values('ee');
|
||
|
+insert into ch values('Ee');
|
||
|
+
|
||
|
+create table chvch (
|
||
|
+ ch mchar(12),
|
||
|
+ vch mvarchar(12)
|
||
|
+) without oids;
|
||
|
+
|
||
|
+insert into chvch values('No spaces', 'No spaces');
|
||
|
+insert into chvch values('One space ', 'One space ');
|
||
|
+insert into chvch values('1 space', '1 space ');
|
||
|
+
|
||
|
diff --git a/contrib/mchar/sql/like.sql b/contrib/mchar/sql/like.sql
|
||
|
new file mode 100644
|
||
|
index 0000000..aebf924
|
||
|
--- /dev/null
|
||
|
+++ b/contrib/mchar/sql/like.sql
|
||
|
@@ -0,0 +1,216 @@
|
||
|
+-- simplest examples
|
||
|
+-- E061-04 like predicate
|
||
|
+SELECT 'hawkeye'::mchar LIKE 'h%' AS "true";
|
||
|
+SELECT 'hawkeye'::mchar NOT LIKE 'h%' AS "false";
|
||
|
+
|
||
|
+SELECT 'hawkeye'::mchar LIKE 'H%' AS "true";
|
||
|
+SELECT 'hawkeye'::mchar NOT LIKE 'H%' AS "false";
|
||
|
+
|
||
|
+SELECT 'hawkeye'::mchar LIKE 'indio%' AS "false";
|
||
|
+SELECT 'hawkeye'::mchar NOT LIKE 'indio%' AS "true";
|
||
|
+
|
||
|
+SELECT 'hawkeye'::mchar LIKE 'h%eye' AS "true";
|
||
|
+SELECT 'hawkeye'::mchar NOT LIKE 'h%eye' AS "false";
|
||
|
+
|
||
|
+SELECT 'indio'::mchar LIKE '_ndio' AS "true";
|
||
|
+SELECT 'indio'::mchar NOT LIKE '_ndio' AS "false";
|
||
|
+
|
||
|
+SELECT 'indio'::mchar LIKE 'in__o' AS "true";
|
||
|
+SELECT 'indio'::mchar NOT LIKE 'in__o' AS "false";
|
||
|
+
|
||
|
+SELECT 'indio'::mchar LIKE 'in_o' AS "false";
|
||
|
+SELECT 'indio'::mchar NOT LIKE 'in_o' AS "true";
|
||
|
+
|
||
|
+SELECT 'hawkeye'::mvarchar LIKE 'h%' AS "true";
|
||
|
+SELECT 'hawkeye'::mvarchar NOT LIKE 'h%' AS "false";
|
||
|
+
|
||
|
+SELECT 'hawkeye'::mvarchar LIKE 'H%' AS "true";
|
||
|
+SELECT 'hawkeye'::mvarchar NOT LIKE 'H%' AS "false";
|
||
|
+
|
||
|
+SELECT 'hawkeye'::mvarchar LIKE 'indio%' AS "false";
|
||
|
+SELECT 'hawkeye'::mvarchar NOT LIKE 'indio%' AS "true";
|
||
|
+
|
||
|
+SELECT 'hawkeye'::mvarchar LIKE 'h%eye' AS "true";
|
||
|
+SELECT 'hawkeye'::mvarchar NOT LIKE 'h%eye' AS "false";
|
||
|
+
|
||
|
+SELECT 'indio'::mvarchar LIKE '_ndio' AS "true";
|
||
|
+SELECT 'indio'::mvarchar NOT LIKE '_ndio' AS "false";
|
||
|
+
|
||
|
+SELECT 'indio'::mvarchar LIKE 'in__o' AS "true";
|
||
|
+SELECT 'indio'::mvarchar NOT LIKE 'in__o' AS "false";
|
||
|
+
|
||
|
+SELECT 'indio'::mvarchar LIKE 'in_o' AS "false";
|
||
|
+SELECT 'indio'::mvarchar NOT LIKE 'in_o' AS "true";
|
||
|
+
|
||
|
+-- unused escape character
|
||
|
+SELECT 'hawkeye'::mchar LIKE 'h%'::mchar ESCAPE '#' AS "true";
|
||
|
+SELECT 'hawkeye'::mchar NOT LIKE 'h%'::mchar ESCAPE '#' AS "false";
|
||
|
+
|
||
|
+SELECT 'indio'::mchar LIKE 'ind_o'::mchar ESCAPE '$' AS "true";
|
||
|
+SELECT 'indio'::mchar NOT LIKE 'ind_o'::mchar ESCAPE '$' AS "false";
|
||
|
+
|
||
|
+-- escape character
|
||
|
+-- E061-05 like predicate with escape clause
|
||
|
+SELECT 'h%'::mchar LIKE 'h#%'::mchar ESCAPE '#' AS "true";
|
||
|
+SELECT 'h%'::mchar NOT LIKE 'h#%'::mchar ESCAPE '#' AS "false";
|
||
|
+
|
||
|
+SELECT 'h%wkeye'::mchar LIKE 'h#%'::mchar ESCAPE '#' AS "false";
|
||
|
+SELECT 'h%wkeye'::mchar NOT LIKE 'h#%'::mchar ESCAPE '#' AS "true";
|
||
|
+
|
||
|
+SELECT 'h%wkeye'::mchar LIKE 'h#%%'::mchar ESCAPE '#' AS "true";
|
||
|
+SELECT 'h%wkeye'::mchar NOT LIKE 'h#%%'::mchar ESCAPE '#' AS "false";
|
||
|
+
|
||
|
+SELECT 'h%awkeye'::mchar LIKE 'h#%a%k%e'::mchar ESCAPE '#' AS "true";
|
||
|
+SELECT 'h%awkeye'::mchar NOT LIKE 'h#%a%k%e'::mchar ESCAPE '#' AS "false";
|
||
|
+
|
||
|
+SELECT 'indio'::mchar LIKE '_ndio'::mchar ESCAPE '$' AS "true";
|
||
|
+SELECT 'indio'::mchar NOT LIKE '_ndio'::mchar ESCAPE '$' AS "false";
|
||
|
+
|
||
|
+SELECT 'i_dio'::mchar LIKE 'i$_d_o'::mchar ESCAPE '$' AS "true";
|
||
|
+SELECT 'i_dio'::mchar NOT LIKE 'i$_d_o'::mchar ESCAPE '$' AS "false";
|
||
|
+
|
||
|
+SELECT 'i_dio'::mchar LIKE 'i$_nd_o'::mchar ESCAPE '$' AS "false";
|
||
|
+SELECT 'i_dio'::mchar NOT LIKE 'i$_nd_o'::mchar ESCAPE '$' AS "true";
|
||
|
+
|
||
|
+SELECT 'i_dio'::mchar LIKE 'i$_d%o'::mchar ESCAPE '$' AS "true";
|
||
|
+SELECT 'i_dio'::mchar NOT LIKE 'i$_d%o'::mchar ESCAPE '$' AS "false";
|
||
|
+
|
||
|
+-- escape character same as pattern character
|
||
|
+SELECT 'maca'::mchar LIKE 'm%aca' ESCAPE '%'::mchar AS "true";
|
||
|
+SELECT 'maca'::mchar NOT LIKE 'm%aca' ESCAPE '%'::mchar AS "false";
|
||
|
+
|
||
|
+SELECT 'ma%a'::mchar LIKE 'm%a%%a' ESCAPE '%'::mchar AS "true";
|
||
|
+SELECT 'ma%a'::mchar NOT LIKE 'm%a%%a' ESCAPE '%'::mchar AS "false";
|
||
|
+
|
||
|
+SELECT 'bear'::mchar LIKE 'b_ear' ESCAPE '_'::mchar AS "true";
|
||
|
+SELECT 'bear'::mchar NOT LIKE 'b_ear'::mchar ESCAPE '_' AS "false";
|
||
|
+
|
||
|
+SELECT 'be_r'::mchar LIKE 'b_e__r' ESCAPE '_'::mchar AS "true";
|
||
|
+SELECT 'be_r'::mchar NOT LIKE 'b_e__r' ESCAPE '_'::mchar AS "false";
|
||
|
+
|
||
|
+SELECT 'be_r'::mchar LIKE '__e__r' ESCAPE '_'::mchar AS "false";
|
||
|
+SELECT 'be_r'::mchar NOT LIKE '__e__r'::mchar ESCAPE '_' AS "true";
|
||
|
+
|
||
|
+-- unused escape character
|
||
|
+SELECT 'hawkeye'::mvarchar LIKE 'h%'::mvarchar ESCAPE '#' AS "true";
|
||
|
+SELECT 'hawkeye'::mvarchar NOT LIKE 'h%'::mvarchar ESCAPE '#' AS "false";
|
||
|
+
|
||
|
+SELECT 'indio'::mvarchar LIKE 'ind_o'::mvarchar ESCAPE '$' AS "true";
|
||
|
+SELECT 'indio'::mvarchar NOT LIKE 'ind_o'::mvarchar ESCAPE '$' AS "false";
|
||
|
+
|
||
|
+-- escape character
|
||
|
+-- E061-05 like predicate with escape clause
|
||
|
+SELECT 'h%'::mvarchar LIKE 'h#%'::mvarchar ESCAPE '#' AS "true";
|
||
|
+SELECT 'h%'::mvarchar NOT LIKE 'h#%'::mvarchar ESCAPE '#' AS "false";
|
||
|
+
|
||
|
+SELECT 'h%wkeye'::mvarchar LIKE 'h#%'::mvarchar ESCAPE '#' AS "false";
|
||
|
+SELECT 'h%wkeye'::mvarchar NOT LIKE 'h#%'::mvarchar ESCAPE '#' AS "true";
|
||
|
+
|
||
|
+SELECT 'h%wkeye'::mvarchar LIKE 'h#%%'::mvarchar ESCAPE '#' AS "true";
|
||
|
+SELECT 'h%wkeye'::mvarchar NOT LIKE 'h#%%'::mvarchar ESCAPE '#' AS "false";
|
||
|
+
|
||
|
+SELECT 'h%awkeye'::mvarchar LIKE 'h#%a%k%e'::mvarchar ESCAPE '#' AS "true";
|
||
|
+SELECT 'h%awkeye'::mvarchar NOT LIKE 'h#%a%k%e'::mvarchar ESCAPE '#' AS "false";
|
||
|
+
|
||
|
+SELECT 'indio'::mvarchar LIKE '_ndio'::mvarchar ESCAPE '$' AS "true";
|
||
|
+SELECT 'indio'::mvarchar NOT LIKE '_ndio'::mvarchar ESCAPE '$' AS "false";
|
||
|
+
|
||
|
+SELECT 'i_dio'::mvarchar LIKE 'i$_d_o'::mvarchar ESCAPE '$' AS "true";
|
||
|
+SELECT 'i_dio'::mvarchar NOT LIKE 'i$_d_o'::mvarchar ESCAPE '$' AS "false";
|
||
|
+
|
||
|
+SELECT 'i_dio'::mvarchar LIKE 'i$_nd_o'::mvarchar ESCAPE '$' AS "false";
|
||
|
+SELECT 'i_dio'::mvarchar NOT LIKE 'i$_nd_o'::mvarchar ESCAPE '$' AS "true";
|
||
|
+
|
||
|
+SELECT 'i_dio'::mvarchar LIKE 'i$_d%o'::mvarchar ESCAPE '$' AS "true";
|
||
|
+SELECT 'i_dio'::mvarchar NOT LIKE 'i$_d%o'::mvarchar ESCAPE '$' AS "false";
|
||
|
+
|
||
|
+-- escape character same as pattern character
|
||
|
+SELECT 'maca'::mvarchar LIKE 'm%aca' ESCAPE '%'::mvarchar AS "true";
|
||
|
+SELECT 'maca'::mvarchar NOT LIKE 'm%aca' ESCAPE '%'::mvarchar AS "false";
|
||
|
+
|
||
|
+SELECT 'ma%a'::mvarchar LIKE 'm%a%%a' ESCAPE '%'::mvarchar AS "true";
|
||
|
+SELECT 'ma%a'::mvarchar NOT LIKE 'm%a%%a' ESCAPE '%'::mvarchar AS "false";
|
||
|
+
|
||
|
+SELECT 'bear'::mvarchar LIKE 'b_ear' ESCAPE '_'::mvarchar AS "true";
|
||
|
+SELECT 'bear'::mvarchar NOT LIKE 'b_ear'::mvarchar ESCAPE '_' AS "false";
|
||
|
+
|
||
|
+SELECT 'be_r'::mvarchar LIKE 'b_e__r' ESCAPE '_'::mvarchar AS "true";
|
||
|
+SELECT 'be_r'::mvarchar NOT LIKE 'b_e__r' ESCAPE '_'::mvarchar AS "false";
|
||
|
+
|
||
|
+SELECT 'be_r'::mvarchar LIKE '__e__r' ESCAPE '_'::mvarchar AS "false";
|
||
|
+SELECT 'be_r'::mvarchar NOT LIKE '__e__r'::mvarchar ESCAPE '_' AS "true";
|
||
|
+
|
||
|
+-- similar to
|
||
|
+
|
||
|
+SELECT 'abc'::mchar SIMILAR TO 'abc'::mchar AS "true";
|
||
|
+SELECT 'abc'::mchar SIMILAR TO 'a'::mchar AS "false";
|
||
|
+SELECT 'abc'::mchar SIMILAR TO '%(b|d)%'::mchar AS "true";
|
||
|
+SELECT 'abc'::mchar SIMILAR TO '(b|c)%'::mchar AS "false";
|
||
|
+SELECT 'h%'::mchar SIMILAR TO 'h#%'::mchar AS "false";
|
||
|
+SELECT 'h%'::mchar SIMILAR TO 'h#%'::mchar ESCAPE '#' AS "true";
|
||
|
+
|
||
|
+SELECT 'abc'::mvarchar SIMILAR TO 'abc'::mvarchar AS "true";
|
||
|
+SELECT 'abc'::mvarchar SIMILAR TO 'a'::mvarchar AS "false";
|
||
|
+SELECT 'abc'::mvarchar SIMILAR TO '%(b|d)%'::mvarchar AS "true";
|
||
|
+SELECT 'abc'::mvarchar SIMILAR TO '(b|c)%'::mvarchar AS "false";
|
||
|
+SELECT 'h%'::mvarchar SIMILAR TO 'h#%'::mvarchar AS "false";
|
||
|
+SELECT 'h%'::mvarchar SIMILAR TO 'h#%'::mvarchar ESCAPE '#' AS "true";
|
||
|
+
|
||
|
+-- index support
|
||
|
+
|
||
|
+SELECT * from ch where chcol like 'aB_d' order by chcol using &<;
|
||
|
+SELECT * from ch where chcol like 'aB%d' order by chcol using &<;
|
||
|
+SELECT * from ch where chcol like 'aB%' order by chcol using &<;
|
||
|
+SELECT * from ch where chcol like '%BC%' order by chcol using &<;
|
||
|
+set enable_seqscan = off;
|
||
|
+SELECT * from ch where chcol like 'aB_d' order by chcol using &<;
|
||
|
+SELECT * from ch where chcol like 'aB%d' order by chcol using &<;
|
||
|
+SELECT * from ch where chcol like 'aB%' order by chcol using &<;
|
||
|
+SELECT * from ch where chcol like '%BC%' order by chcol using &<;
|
||
|
+set enable_seqscan = on;
|
||
|
+
|
||
|
+
|
||
|
+create table testt (f1 mchar(10));
|
||
|
+insert into testt values ('Abc-000001');
|
||
|
+insert into testt values ('Abc-000002');
|
||
|
+insert into testt values ('0000000001');
|
||
|
+insert into testt values ('0000000002');
|
||
|
+
|
||
|
+select f1 from testt where f1::mvarchar like E'Abc\\-%'::mvarchar;
|
||
|
+select * from testt where f1::mchar like E'Abc\\-%'::mchar;
|
||
|
+create index testindex on testt(f1);
|
||
|
+set enable_seqscan=off;
|
||
|
+select f1 from testt where f1::mvarchar like E'Abc\\-%'::mvarchar;
|
||
|
+select * from testt where f1::mchar like E'Abc\\-%'::mchar;
|
||
|
+set enable_seqscan = on;
|
||
|
+drop table testt;
|
||
|
+
|
||
|
+create table testt (f1 mvarchar(10));
|
||
|
+insert into testt values ('Abc-000001');
|
||
|
+insert into testt values ('Abc-000002');
|
||
|
+insert into testt values ('0000000001');
|
||
|
+insert into testt values ('0000000002');
|
||
|
+
|
||
|
+select f1 from testt where f1::mvarchar like E'Abc\\-%'::mvarchar;
|
||
|
+select * from testt where f1::mchar like E'Abc\\-%'::mchar;
|
||
|
+select * from testt where f1::mchar like E'Abc\\- %'::mchar;
|
||
|
+select * from testt where f1::mchar like E' %'::mchar;
|
||
|
+create index testindex on testt(f1);
|
||
|
+set enable_seqscan=off;
|
||
|
+select f1 from testt where f1::mvarchar like E'Abc\\-%'::mvarchar;
|
||
|
+select * from testt where f1::mchar like E'Abc\\-%'::mchar;
|
||
|
+select * from testt where f1::mchar like E'Abc\\- %'::mchar;
|
||
|
+select * from testt where f1::mchar like E' %'::mchar;
|
||
|
+set enable_seqscan = on;
|
||
|
+drop table testt;
|
||
|
+
|
||
|
+
|
||
|
+CREATE TABLE test ( code mchar(5) NOT NULL );
|
||
|
+insert into test values('1111 ');
|
||
|
+insert into test values('111 ');
|
||
|
+insert into test values('11 ');
|
||
|
+insert into test values('1 ');
|
||
|
+
|
||
|
+SELECT * FROM test WHERE code LIKE ('% ');
|
||
|
+
|
||
|
+
|
||
|
diff --git a/contrib/mchar/sql/mchar.sql b/contrib/mchar/sql/mchar.sql
|
||
|
new file mode 100644
|
||
|
index 0000000..640f166
|
||
|
--- /dev/null
|
||
|
+++ b/contrib/mchar/sql/mchar.sql
|
||
|
@@ -0,0 +1,81 @@
|
||
|
+-- I/O tests
|
||
|
+
|
||
|
+select '1'::mchar;
|
||
|
+select '2 '::mchar;
|
||
|
+select '10 '::mchar;
|
||
|
+
|
||
|
+select '1'::mchar(2);
|
||
|
+select '2 '::mchar(2);
|
||
|
+select '3 '::mchar(2);
|
||
|
+select '10 '::mchar(2);
|
||
|
+
|
||
|
+select ' '::mchar(10);
|
||
|
+select ' '::mchar;
|
||
|
+
|
||
|
+-- operations & functions
|
||
|
+
|
||
|
+select length('1'::mchar);
|
||
|
+select length('2 '::mchar);
|
||
|
+select length('10 '::mchar);
|
||
|
+
|
||
|
+select length('1'::mchar(2));
|
||
|
+select length('2 '::mchar(2));
|
||
|
+select length('3 '::mchar(2));
|
||
|
+select length('10 '::mchar(2));
|
||
|
+
|
||
|
+select length(' '::mchar(10));
|
||
|
+select length(' '::mchar);
|
||
|
+
|
||
|
+select 'asd'::mchar(10) || '>'::mchar(10);
|
||
|
+select length('asd'::mchar(10) || '>'::mchar(10));
|
||
|
+select 'asd'::mchar(2) || '>'::mchar(10);
|
||
|
+select length('asd'::mchar(2) || '>'::mchar(10));
|
||
|
+
|
||
|
+-- Comparisons
|
||
|
+
|
||
|
+select 'asdf'::mchar = 'aSdf'::mchar;
|
||
|
+select 'asdf'::mchar = 'aSdf '::mchar;
|
||
|
+select 'asdf'::mchar = 'aSdf 1'::mchar(4);
|
||
|
+select 'asdf'::mchar = 'aSdf 1'::mchar(5);
|
||
|
+select 'asdf'::mchar = 'aSdf 1'::mchar(6);
|
||
|
+select 'asdf'::mchar(3) = 'aSdf 1'::mchar(5);
|
||
|
+select 'asdf'::mchar(3) = 'aSdf 1'::mchar(3);
|
||
|
+
|
||
|
+select 'asdf'::mchar < 'aSdf'::mchar;
|
||
|
+select 'asdf'::mchar < 'aSdf '::mchar;
|
||
|
+select 'asdf'::mchar < 'aSdf 1'::mchar(4);
|
||
|
+select 'asdf'::mchar < 'aSdf 1'::mchar(5);
|
||
|
+select 'asdf'::mchar < 'aSdf 1'::mchar(6);
|
||
|
+
|
||
|
+select 'asdf'::mchar <= 'aSdf'::mchar;
|
||
|
+select 'asdf'::mchar <= 'aSdf '::mchar;
|
||
|
+select 'asdf'::mchar <= 'aSdf 1'::mchar(4);
|
||
|
+select 'asdf'::mchar <= 'aSdf 1'::mchar(5);
|
||
|
+select 'asdf'::mchar <= 'aSdf 1'::mchar(6);
|
||
|
+
|
||
|
+select 'asdf'::mchar >= 'aSdf'::mchar;
|
||
|
+select 'asdf'::mchar >= 'aSdf '::mchar;
|
||
|
+select 'asdf'::mchar >= 'aSdf 1'::mchar(4);
|
||
|
+select 'asdf'::mchar >= 'aSdf 1'::mchar(5);
|
||
|
+select 'asdf'::mchar >= 'aSdf 1'::mchar(6);
|
||
|
+
|
||
|
+select 'asdf'::mchar > 'aSdf'::mchar;
|
||
|
+select 'asdf'::mchar > 'aSdf '::mchar;
|
||
|
+select 'asdf'::mchar > 'aSdf 1'::mchar(4);
|
||
|
+select 'asdf'::mchar > 'aSdf 1'::mchar(5);
|
||
|
+select 'asdf'::mchar > 'aSdf 1'::mchar(6);
|
||
|
+
|
||
|
+select max(ch) from chvch;
|
||
|
+select min(ch) from chvch;
|
||
|
+
|
||
|
+select substr('1234567890'::mchar, 3) = '34567890' as "34567890";
|
||
|
+select substr('1234567890'::mchar, 4, 3) = '456' as "456";
|
||
|
+
|
||
|
+select lower('asdfASDF'::mchar);
|
||
|
+select upper('asdfASDF'::mchar);
|
||
|
+
|
||
|
+select 'asd'::mchar == 'aSd'::mchar;
|
||
|
+select 'asd'::mchar == 'aCd'::mchar;
|
||
|
+select 'asd'::mchar == NULL;
|
||
|
+select NULL == 'aCd'::mchar;
|
||
|
+select NULL::mchar == NULL;
|
||
|
diff --git a/contrib/mchar/sql/mm.sql b/contrib/mchar/sql/mm.sql
|
||
|
new file mode 100644
|
||
|
index 0000000..c16aaa1
|
||
|
--- /dev/null
|
||
|
+++ b/contrib/mchar/sql/mm.sql
|
||
|
@@ -0,0 +1,185 @@
|
||
|
+select 'asd'::mchar::mvarchar;
|
||
|
+select 'asd '::mchar::mvarchar;
|
||
|
+select 'asd'::mchar(2)::mvarchar;
|
||
|
+select 'asd '::mchar(2)::mvarchar;
|
||
|
+select 'asd'::mchar(5)::mvarchar;
|
||
|
+select 'asd '::mchar(5)::mvarchar;
|
||
|
+select 'asd'::mchar::mvarchar(2);
|
||
|
+select 'asd '::mchar::mvarchar(2);
|
||
|
+select 'asd'::mchar(2)::mvarchar(2);
|
||
|
+select 'asd '::mchar(2)::mvarchar(2);
|
||
|
+select 'asd'::mchar(5)::mvarchar(2);
|
||
|
+select 'asd '::mchar(5)::mvarchar(2);
|
||
|
+select 'asd'::mchar::mvarchar(5);
|
||
|
+select 'asd '::mchar::mvarchar(5);
|
||
|
+select 'asd'::mchar(2)::mvarchar(5);
|
||
|
+select 'asd '::mchar(2)::mvarchar(5);
|
||
|
+select 'asd'::mchar(5)::mvarchar(5);
|
||
|
+select 'asd '::mchar(5)::mvarchar(5);
|
||
|
+
|
||
|
+select 'asd'::mvarchar::mchar;
|
||
|
+select 'asd '::mvarchar::mchar;
|
||
|
+select 'asd'::mvarchar(2)::mchar;
|
||
|
+select 'asd '::mvarchar(2)::mchar;
|
||
|
+select 'asd'::mvarchar(5)::mchar;
|
||
|
+select 'asd '::mvarchar(5)::mchar;
|
||
|
+select 'asd'::mvarchar::mchar(2);
|
||
|
+select 'asd '::mvarchar::mchar(2);
|
||
|
+select 'asd'::mvarchar(2)::mchar(2);
|
||
|
+select 'asd '::mvarchar(2)::mchar(2);
|
||
|
+select 'asd'::mvarchar(5)::mchar(2);
|
||
|
+select 'asd '::mvarchar(5)::mchar(2);
|
||
|
+select 'asd'::mvarchar::mchar(5);
|
||
|
+select 'asd '::mvarchar::mchar(5);
|
||
|
+select 'asd'::mvarchar(2)::mchar(5);
|
||
|
+select 'asd '::mvarchar(2)::mchar(5);
|
||
|
+select 'asd'::mvarchar(5)::mchar(5);
|
||
|
+select 'asd '::mvarchar(5)::mchar(5);
|
||
|
+
|
||
|
+select 'asd'::mchar || '123';
|
||
|
+select 'asd'::mchar || '123'::mchar;
|
||
|
+select 'asd'::mchar || '123'::mvarchar;
|
||
|
+
|
||
|
+select 'asd '::mchar || '123';
|
||
|
+select 'asd '::mchar || '123'::mchar;
|
||
|
+select 'asd '::mchar || '123'::mvarchar;
|
||
|
+
|
||
|
+select 'asd '::mchar || '123 ';
|
||
|
+select 'asd '::mchar || '123 '::mchar;
|
||
|
+select 'asd '::mchar || '123 '::mvarchar;
|
||
|
+
|
||
|
+
|
||
|
+select 'asd'::mvarchar || '123';
|
||
|
+select 'asd'::mvarchar || '123'::mchar;
|
||
|
+select 'asd'::mvarchar || '123'::mvarchar;
|
||
|
+
|
||
|
+select 'asd '::mvarchar || '123';
|
||
|
+select 'asd '::mvarchar || '123'::mchar;
|
||
|
+select 'asd '::mvarchar || '123'::mvarchar;
|
||
|
+
|
||
|
+select 'asd '::mvarchar || '123 ';
|
||
|
+select 'asd '::mvarchar || '123 '::mchar;
|
||
|
+select 'asd '::mvarchar || '123 '::mvarchar;
|
||
|
+
|
||
|
+
|
||
|
+select 'asd'::mchar(2) || '123';
|
||
|
+select 'asd'::mchar(2) || '123'::mchar;
|
||
|
+select 'asd'::mchar(2) || '123'::mvarchar;
|
||
|
+
|
||
|
+
|
||
|
+select 'asd '::mchar(2) || '123';
|
||
|
+select 'asd '::mchar(2) || '123'::mchar;
|
||
|
+select 'asd '::mchar(2) || '123'::mvarchar;
|
||
|
+
|
||
|
+
|
||
|
+select 'asd '::mchar(2) || '123 ';
|
||
|
+select 'asd '::mchar(2) || '123 '::mchar;
|
||
|
+select 'asd '::mchar(2) || '123 '::mvarchar;
|
||
|
+
|
||
|
+select 'asd'::mvarchar(2) || '123';
|
||
|
+select 'asd'::mvarchar(2) || '123'::mchar;
|
||
|
+select 'asd'::mvarchar(2) || '123'::mvarchar;
|
||
|
+
|
||
|
+select 'asd '::mvarchar(2) || '123';
|
||
|
+select 'asd '::mvarchar(2) || '123'::mchar;
|
||
|
+select 'asd '::mvarchar(2) || '123'::mvarchar;
|
||
|
+
|
||
|
+select 'asd '::mvarchar(2) || '123 ';
|
||
|
+select 'asd '::mvarchar(2) || '123 '::mchar;
|
||
|
+select 'asd '::mvarchar(2) || '123 '::mvarchar;
|
||
|
+
|
||
|
+select 'asd'::mchar(4) || '143';
|
||
|
+select 'asd'::mchar(4) || '123'::mchar;
|
||
|
+select 'asd'::mchar(4) || '123'::mvarchar;
|
||
|
+
|
||
|
+select 'asd '::mchar(4) || '123';
|
||
|
+select 'asd '::mchar(4) || '123'::mchar;
|
||
|
+select 'asd '::mchar(4) || '123'::mvarchar;
|
||
|
+
|
||
|
+select 'asd '::mchar(4) || '123 ';
|
||
|
+select 'asd '::mchar(4) || '123 '::mchar;
|
||
|
+select 'asd '::mchar(4) || '123 '::mvarchar;
|
||
|
+
|
||
|
+select 'asd'::mvarchar(4) || '123';
|
||
|
+select 'asd'::mvarchar(4) || '123'::mchar;
|
||
|
+select 'asd'::mvarchar(4) || '123'::mvarchar;
|
||
|
+
|
||
|
+select 'asd '::mvarchar(4) || '123';
|
||
|
+select 'asd '::mvarchar(4) || '123'::mchar;
|
||
|
+select 'asd '::mvarchar(4) || '123'::mvarchar;
|
||
|
+
|
||
|
+select 'asd '::mvarchar(4) || '123 ';
|
||
|
+select 'asd '::mvarchar(4) || '123 '::mchar;
|
||
|
+select 'asd '::mvarchar(4) || '123 '::mvarchar;
|
||
|
+
|
||
|
+
|
||
|
+select 'asd '::mvarchar(4) || '123 '::mchar(4);
|
||
|
+select 'asd '::mvarchar(4) || '123 '::mvarchar(4);
|
||
|
+select 'asd '::mvarchar(4) || '123'::mchar(4);
|
||
|
+select 'asd '::mvarchar(4) || '123'::mvarchar(4);
|
||
|
+
|
||
|
+
|
||
|
+select 1 where 'f'::mchar='F'::mvarchar;
|
||
|
+select 1 where 'f'::mchar='F '::mvarchar;
|
||
|
+select 1 where 'f '::mchar='F'::mvarchar;
|
||
|
+select 1 where 'f '::mchar='F '::mvarchar;
|
||
|
+
|
||
|
+select 1 where 'f'::mchar='F'::mvarchar(2);
|
||
|
+select 1 where 'f'::mchar='F '::mvarchar(2);
|
||
|
+select 1 where 'f '::mchar='F'::mvarchar(2);
|
||
|
+select 1 where 'f '::mchar='F '::mvarchar(2);
|
||
|
+
|
||
|
+select 1 where 'f'::mchar(2)='F'::mvarchar;
|
||
|
+select 1 where 'f'::mchar(2)='F '::mvarchar;
|
||
|
+select 1 where 'f '::mchar(2)='F'::mvarchar;
|
||
|
+select 1 where 'f '::mchar(2)='F '::mvarchar;
|
||
|
+
|
||
|
+select 1 where 'f'::mchar(2)='F'::mvarchar(2);
|
||
|
+select 1 where 'f'::mchar(2)='F '::mvarchar(2);
|
||
|
+select 1 where 'f '::mchar(2)='F'::mvarchar(2);
|
||
|
+select 1 where 'f '::mchar(2)='F '::mvarchar(2);
|
||
|
+
|
||
|
+select 1 where 'foo'::mchar='FOO'::mvarchar;
|
||
|
+select 1 where 'foo'::mchar='FOO '::mvarchar;
|
||
|
+select 1 where 'foo '::mchar='FOO'::mvarchar;
|
||
|
+select 1 where 'foo '::mchar='FOO '::mvarchar;
|
||
|
+
|
||
|
+select 1 where 'foo'::mchar='FOO'::mvarchar(2);
|
||
|
+select 1 where 'foo'::mchar='FOO '::mvarchar(2);
|
||
|
+select 1 where 'foo '::mchar='FOO'::mvarchar(2);
|
||
|
+select 1 where 'foo '::mchar='FOO '::mvarchar(2);
|
||
|
+
|
||
|
+select 1 where 'foo'::mchar(2)='FOO'::mvarchar;
|
||
|
+select 1 where 'foo'::mchar(2)='FOO '::mvarchar;
|
||
|
+select 1 where 'foo '::mchar(2)='FOO'::mvarchar;
|
||
|
+select 1 where 'foo '::mchar(2)='FOO '::mvarchar;
|
||
|
+
|
||
|
+select 1 where 'foo'::mchar(2)='FOO'::mvarchar(2);
|
||
|
+select 1 where 'foo'::mchar(2)='FOO '::mvarchar(2);
|
||
|
+select 1 where 'foo '::mchar(2)='FOO'::mvarchar(2);
|
||
|
+select 1 where 'foo '::mchar(2)='FOO '::mvarchar(2);
|
||
|
+
|
||
|
+Select 'f'::mchar(1) Union Select 'o'::mvarchar(1);
|
||
|
+Select 'f'::mvarchar(1) Union Select 'o'::mchar(1);
|
||
|
+
|
||
|
+select * from chvch where ch=vch;
|
||
|
+
|
||
|
+select ch.* from ch, (select 'dEfg'::mvarchar as q) as p where chcol > p.q;
|
||
|
+create index qq on ch (chcol);
|
||
|
+set enable_seqscan=off;
|
||
|
+select ch.* from ch, (select 'dEfg'::mvarchar as q) as p where chcol > p.q;
|
||
|
+set enable_seqscan=on;
|
||
|
+
|
||
|
+
|
||
|
+--\copy chvch to 'results/chvch.dump' binary
|
||
|
+--truncate table chvch;
|
||
|
+--\copy chvch from 'results/chvch.dump' binary
|
||
|
+
|
||
|
+--test joins
|
||
|
+CREATE TABLE a (mchar2 MCHAR(2) NOT NULL);
|
||
|
+CREATE TABLE c (mvarchar255 mvarchar NOT NULL);
|
||
|
+SELECT * FROM a, c WHERE mchar2 = mvarchar255;
|
||
|
+SELECT * FROM a, c WHERE mvarchar255 = mchar2;
|
||
|
+DROP TABLE a;
|
||
|
+DROP TABLE c;
|
||
|
+
|
||
|
diff --git a/contrib/mchar/sql/mvarchar.sql b/contrib/mchar/sql/mvarchar.sql
|
||
|
new file mode 100644
|
||
|
index 0000000..91b0981
|
||
|
--- /dev/null
|
||
|
+++ b/contrib/mchar/sql/mvarchar.sql
|
||
|
@@ -0,0 +1,82 @@
|
||
|
+-- I/O tests
|
||
|
+
|
||
|
+select '1'::mvarchar;
|
||
|
+select '2 '::mvarchar;
|
||
|
+select '10 '::mvarchar;
|
||
|
+
|
||
|
+select '1'::mvarchar(2);
|
||
|
+select '2 '::mvarchar(2);
|
||
|
+select '3 '::mvarchar(2);
|
||
|
+select '10 '::mvarchar(2);
|
||
|
+
|
||
|
+select ' '::mvarchar(10);
|
||
|
+select ' '::mvarchar;
|
||
|
+
|
||
|
+-- operations & functions
|
||
|
+
|
||
|
+select length('1'::mvarchar);
|
||
|
+select length('2 '::mvarchar);
|
||
|
+select length('10 '::mvarchar);
|
||
|
+
|
||
|
+select length('1'::mvarchar(2));
|
||
|
+select length('2 '::mvarchar(2));
|
||
|
+select length('3 '::mvarchar(2));
|
||
|
+select length('10 '::mvarchar(2));
|
||
|
+
|
||
|
+select length(' '::mvarchar(10));
|
||
|
+select length(' '::mvarchar);
|
||
|
+
|
||
|
+select 'asd'::mvarchar(10) || '>'::mvarchar(10);
|
||
|
+select length('asd'::mvarchar(10) || '>'::mvarchar(10));
|
||
|
+select 'asd'::mvarchar(2) || '>'::mvarchar(10);
|
||
|
+select length('asd'::mvarchar(2) || '>'::mvarchar(10));
|
||
|
+
|
||
|
+-- Comparisons
|
||
|
+
|
||
|
+select 'asdf'::mvarchar = 'aSdf'::mvarchar;
|
||
|
+select 'asdf'::mvarchar = 'aSdf '::mvarchar;
|
||
|
+select 'asdf'::mvarchar = 'aSdf 1'::mvarchar(4);
|
||
|
+select 'asdf'::mvarchar = 'aSdf 1'::mvarchar(5);
|
||
|
+select 'asdf'::mvarchar = 'aSdf 1'::mvarchar(6);
|
||
|
+select 'asdf'::mvarchar(3) = 'aSdf 1'::mvarchar(5);
|
||
|
+select 'asdf'::mvarchar(3) = 'aSdf 1'::mvarchar(3);
|
||
|
+
|
||
|
+select 'asdf'::mvarchar < 'aSdf'::mvarchar;
|
||
|
+select 'asdf'::mvarchar < 'aSdf '::mvarchar;
|
||
|
+select 'asdf'::mvarchar < 'aSdf 1'::mvarchar(4);
|
||
|
+select 'asdf'::mvarchar < 'aSdf 1'::mvarchar(5);
|
||
|
+select 'asdf'::mvarchar < 'aSdf 1'::mvarchar(6);
|
||
|
+
|
||
|
+select 'asdf'::mvarchar <= 'aSdf'::mvarchar;
|
||
|
+select 'asdf'::mvarchar <= 'aSdf '::mvarchar;
|
||
|
+select 'asdf'::mvarchar <= 'aSdf 1'::mvarchar(4);
|
||
|
+select 'asdf'::mvarchar <= 'aSdf 1'::mvarchar(5);
|
||
|
+select 'asdf'::mvarchar <= 'aSdf 1'::mvarchar(6);
|
||
|
+
|
||
|
+select 'asdf'::mvarchar >= 'aSdf'::mvarchar;
|
||
|
+select 'asdf'::mvarchar >= 'aSdf '::mvarchar;
|
||
|
+select 'asdf'::mvarchar >= 'aSdf 1'::mvarchar(4);
|
||
|
+select 'asdf'::mvarchar >= 'aSdf 1'::mvarchar(5);
|
||
|
+select 'asdf'::mvarchar >= 'aSdf 1'::mvarchar(6);
|
||
|
+
|
||
|
+select 'asdf'::mvarchar > 'aSdf'::mvarchar;
|
||
|
+select 'asdf'::mvarchar > 'aSdf '::mvarchar;
|
||
|
+select 'asdf'::mvarchar > 'aSdf 1'::mvarchar(4);
|
||
|
+select 'asdf'::mvarchar > 'aSdf 1'::mvarchar(5);
|
||
|
+select 'asdf'::mvarchar > 'aSdf 1'::mvarchar(6);
|
||
|
+
|
||
|
+select max(vch) from chvch;
|
||
|
+select min(vch) from chvch;
|
||
|
+
|
||
|
+select substr('1234567890'::mvarchar, 3) = '34567890' as "34567890";
|
||
|
+select substr('1234567890'::mvarchar, 4, 3) = '456' as "456";
|
||
|
+
|
||
|
+select lower('asdfASDF'::mvarchar);
|
||
|
+select upper('asdfASDF'::mvarchar);
|
||
|
+
|
||
|
+select 'asd'::mvarchar == 'aSd'::mvarchar;
|
||
|
+select 'asd'::mvarchar == 'aCd'::mvarchar;
|
||
|
+select 'asd'::mvarchar == NULL;
|
||
|
+select NULL == 'aCd'::mvarchar;
|
||
|
+select NULL::mvarchar == NULL;
|
||
|
+
|
||
|
diff --git a/contrib/mchar/uninstall_mchar.sql b/contrib/mchar/uninstall_mchar.sql
|
||
|
new file mode 100644
|
||
|
index 0000000..59f61e2
|
||
|
--- /dev/null
|
||
|
+++ b/contrib/mchar/uninstall_mchar.sql
|
||
|
@@ -0,0 +1,9 @@
|
||
|
+SET search_path = public;
|
||
|
+BEGIN;
|
||
|
+
|
||
|
+DROP FUNCTION mchar_pattern_fixed_prefix(internal, internal, internal);
|
||
|
+DROP FUNCTION mchar_greaterstring(internal);
|
||
|
+DROP TYPE MCHAR CASCADE;
|
||
|
+DROP TYPE MVARCHAR CASCADE;
|
||
|
+
|
||
|
+COMMIT;
|
||
|
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
|
||
|
index f6e2114..19ad0eb 100644
|
||
|
--- a/src/backend/nodes/outfuncs.c
|
||
|
+++ b/src/backend/nodes/outfuncs.c
|
||
|
@@ -1585,6 +1585,7 @@ _outAppendPath(StringInfo str, const AppendPath *node)
|
||
|
_outPathInfo(str, (const Path *) node);
|
||
|
|
||
|
WRITE_NODE_FIELD(subpaths);
|
||
|
+ WRITE_BOOL_FIELD(pull_tlist);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
|
||
|
index bfd3809..a7cab3b 100644
|
||
|
--- a/src/backend/optimizer/path/allpaths.c
|
||
|
+++ b/src/backend/optimizer/path/allpaths.c
|
||
|
@@ -396,6 +396,9 @@ set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
|
||
|
/* Consider index scans */
|
||
|
create_index_paths(root, rel);
|
||
|
|
||
|
+ /* Consider index scans with rewrited quals */
|
||
|
+ keybased_rewrite_index_paths(root, rel);
|
||
|
+
|
||
|
/* Consider TID scans */
|
||
|
create_tidscan_paths(root, rel);
|
||
|
|
||
|
@@ -799,7 +802,7 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
|
||
|
* if we have zero or one live subpath due to constraint exclusion.)
|
||
|
*/
|
||
|
if (subpaths_valid)
|
||
|
- add_path(rel, (Path *) create_append_path(rel, subpaths, NULL));
|
||
|
+ add_path(rel, (Path *) create_append_path(rel, subpaths, NULL, false, NIL));
|
||
|
|
||
|
/*
|
||
|
* Also build unparameterized MergeAppend paths based on the collected
|
||
|
@@ -849,7 +852,7 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
|
||
|
|
||
|
if (subpaths_valid)
|
||
|
add_path(rel, (Path *)
|
||
|
- create_append_path(rel, subpaths, required_outer));
|
||
|
+ create_append_path(rel, subpaths, required_outer, false, NIL));
|
||
|
}
|
||
|
|
||
|
/* Select cheapest paths */
|
||
|
@@ -1067,7 +1070,7 @@ set_dummy_rel_pathlist(RelOptInfo *rel)
|
||
|
/* Discard any pre-existing paths; no further need for them */
|
||
|
rel->pathlist = NIL;
|
||
|
|
||
|
- add_path(rel, (Path *) create_append_path(rel, NIL, NULL));
|
||
|
+ add_path(rel, (Path *) create_append_path(rel, NIL, NULL, false, NIL));
|
||
|
|
||
|
/* Select cheapest path (pretty easy in this case...) */
|
||
|
set_cheapest(rel);
|
||
|
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
|
||
|
index 606734a..56d240c 100644
|
||
|
--- a/src/backend/optimizer/path/indxpath.c
|
||
|
+++ b/src/backend/optimizer/path/indxpath.c
|
||
|
@@ -38,6 +38,14 @@
|
||
|
#include "utils/pg_locale.h"
|
||
|
#include "utils/selfuncs.h"
|
||
|
|
||
|
+/*
|
||
|
+ * index support for LIKE mchar
|
||
|
+ */
|
||
|
+#include "fmgr.h"
|
||
|
+#include "access/htup_details.h"
|
||
|
+#include "utils/catcache.h"
|
||
|
+#include "utils/syscache.h"
|
||
|
+#include "parser/parse_type.h"
|
||
|
|
||
|
#define IsBooleanOpfamily(opfamily) \
|
||
|
((opfamily) == BOOL_BTREE_FAM_OID || (opfamily) == BOOL_HASH_FAM_OID)
|
||
|
@@ -2968,6 +2976,208 @@ match_index_to_operand(Node *operand,
|
||
|
}
|
||
|
|
||
|
/****************************************************************************
|
||
|
+ * ---- ROUTINES FOR "SPECIAL" INDEXABLE OPERATORS FOR
|
||
|
+ * SPECIAL USER_DEFINED TYPES ----
|
||
|
+ * -- teodor
|
||
|
+ ****************************************************************************/
|
||
|
+
|
||
|
+static Oid mmPFPOid = InvalidOid;
|
||
|
+static Oid mmGTOid = InvalidOid;
|
||
|
+static Oid mcharOid = InvalidOid;
|
||
|
+static Oid mvarcharOid = InvalidOid;
|
||
|
+
|
||
|
+static bool
|
||
|
+fillMCharOIDS() {
|
||
|
+ CatCList *catlist;
|
||
|
+ HeapTuple tup;
|
||
|
+ char *funcname = "mchar_pattern_fixed_prefix";
|
||
|
+ int n_members;
|
||
|
+
|
||
|
+ catlist = SearchSysCacheList(PROCNAMEARGSNSP, 1,
|
||
|
+ CStringGetDatum(funcname),
|
||
|
+ 0, 0, 0);
|
||
|
+ n_members = catlist->n_members;
|
||
|
+
|
||
|
+ if ( n_members != 1 ) {
|
||
|
+ ReleaseSysCacheList(catlist);
|
||
|
+ if ( n_members > 1 )
|
||
|
+ elog(ERROR,"There are %d candidates for '%s' function'", n_members, funcname);
|
||
|
+ return false;
|
||
|
+ }
|
||
|
+
|
||
|
+ tup = &catlist->members[0]->tuple;
|
||
|
+
|
||
|
+ if ( HeapTupleGetOid(tup) != mmPFPOid ) {
|
||
|
+ TypeName *typename;
|
||
|
+ Type typtup;
|
||
|
+ char *quals_funcname = "mchar_greaterstring";
|
||
|
+ Oid tmp_mmPFPOid = HeapTupleGetOid(tup);
|
||
|
+
|
||
|
+ ReleaseSysCacheList(catlist);
|
||
|
+
|
||
|
+ typename = makeTypeName("mchar");
|
||
|
+ typtup = LookupTypeName(NULL, typename, NULL);
|
||
|
+ if ( typtup ) {
|
||
|
+ mcharOid = typeTypeId(typtup);
|
||
|
+ ReleaseSysCache(typtup);
|
||
|
+ }
|
||
|
+
|
||
|
+ typename = makeTypeName("mvarchar");
|
||
|
+ typtup = LookupTypeName(NULL, typename, NULL);
|
||
|
+ if ( typtup ) {
|
||
|
+ mvarcharOid = typeTypeId(typtup);
|
||
|
+ ReleaseSysCache(typtup);
|
||
|
+ }
|
||
|
+
|
||
|
+
|
||
|
+ if ( mcharOid == InvalidOid || mvarcharOid == InvalidOid ) {
|
||
|
+ elog(LOG,"Can't find mchar/mvarvarchar types: mchar=%d mvarchar=%d",
|
||
|
+ mcharOid, mvarcharOid);
|
||
|
+ return false;
|
||
|
+ }
|
||
|
+
|
||
|
+ catlist = SearchSysCacheList(PROCNAMEARGSNSP, 1,
|
||
|
+ CStringGetDatum(quals_funcname),
|
||
|
+ 0, 0, 0);
|
||
|
+ n_members = catlist->n_members;
|
||
|
+
|
||
|
+ if ( n_members != 1 ) {
|
||
|
+ ReleaseSysCacheList(catlist);
|
||
|
+ if ( n_members > 1 )
|
||
|
+ elog(ERROR,"There are %d candidates for '%s' function'", n_members, quals_funcname);
|
||
|
+ return false;
|
||
|
+ }
|
||
|
+
|
||
|
+ tup = &catlist->members[0]->tuple;
|
||
|
+ mmGTOid = HeapTupleGetOid(tup);
|
||
|
+ mmPFPOid = tmp_mmPFPOid;
|
||
|
+ }
|
||
|
+
|
||
|
+ ReleaseSysCacheList(catlist);
|
||
|
+
|
||
|
+ return true;
|
||
|
+}
|
||
|
+
|
||
|
+static Pattern_Prefix_Status
|
||
|
+mchar_pattern_fixed_prefix(Oid opOid, Oid opfamilyOid, Const *patt, Pattern_Type ptype,
|
||
|
+ Const **prefix, Oid *leftTypeOid) {
|
||
|
+ HeapTuple tup;
|
||
|
+ Form_pg_operator oprForm;
|
||
|
+ bool isMCharLike = true;
|
||
|
+
|
||
|
+ if ( !fillMCharOIDS() )
|
||
|
+ return Pattern_Prefix_None;
|
||
|
+
|
||
|
+ tup = SearchSysCache(OPEROID, opOid, 0, 0, 0);
|
||
|
+ oprForm = (Form_pg_operator) GETSTRUCT(tup);
|
||
|
+
|
||
|
+ if ( strncmp(oprForm->oprname.data, "~~", 2) != 0 )
|
||
|
+ isMCharLike = false;
|
||
|
+
|
||
|
+ if ( oprForm->oprright != mvarcharOid )
|
||
|
+ isMCharLike = false;
|
||
|
+
|
||
|
+ if ( !( oprForm->oprleft == mcharOid || oprForm->oprleft == mvarcharOid ) )
|
||
|
+ isMCharLike = false;
|
||
|
+
|
||
|
+ if ( patt->consttype != mvarcharOid )
|
||
|
+ isMCharLike = false;
|
||
|
+
|
||
|
+ if (leftTypeOid)
|
||
|
+ *leftTypeOid = oprForm->oprleft;
|
||
|
+
|
||
|
+ ReleaseSysCache(tup);
|
||
|
+
|
||
|
+ if ( !isMCharLike )
|
||
|
+ return Pattern_Prefix_None;
|
||
|
+
|
||
|
+ if ( opfamilyOid != InvalidOid ) {
|
||
|
+ Form_pg_opfamily claForm;
|
||
|
+
|
||
|
+ tup = SearchSysCache(OPFAMILYOID, opfamilyOid, 0, 0, 0);
|
||
|
+ claForm = (Form_pg_opfamily) GETSTRUCT(tup);
|
||
|
+
|
||
|
+ if ( claForm->opfmethod != BTREE_AM_OID )
|
||
|
+ isMCharLike = false;
|
||
|
+
|
||
|
+ if ( mcharOid && strncmp(claForm->opfname.data, "icase_ops", 9 /* strlen(icase_ops) */ ) != 0 )
|
||
|
+ isMCharLike = false;
|
||
|
+
|
||
|
+ ReleaseSysCache(tup);
|
||
|
+ }
|
||
|
+
|
||
|
+ if ( !isMCharLike )
|
||
|
+ return Pattern_Prefix_None;
|
||
|
+
|
||
|
+ return (Pattern_Prefix_Status)DatumGetInt32( OidFunctionCall3(
|
||
|
+ mmPFPOid,
|
||
|
+ PointerGetDatum( patt ),
|
||
|
+ Int32GetDatum( ptype ),
|
||
|
+ PointerGetDatum( prefix )
|
||
|
+ ) );
|
||
|
+}
|
||
|
+
|
||
|
+static Oid
|
||
|
+get_opclass_member_mchar(Oid opclass, Oid leftTypeOid, int strategy) {
|
||
|
+ Oid oproid;
|
||
|
+
|
||
|
+ oproid = get_opfamily_member(opclass, leftTypeOid, mvarcharOid, strategy);
|
||
|
+
|
||
|
+ if ( oproid == InvalidOid )
|
||
|
+ elog(ERROR, "no operator for opclass %u for strategy %u for left type %u", opclass, strategy, leftTypeOid);
|
||
|
+
|
||
|
+ return oproid;
|
||
|
+}
|
||
|
+
|
||
|
+static List *
|
||
|
+mchar_prefix_quals(Node *leftop, Oid leftTypeOid, Oid opclass,
|
||
|
+ Const *prefix_const, Pattern_Prefix_Status pstatus) {
|
||
|
+ Oid oproid;
|
||
|
+ Expr *expr;
|
||
|
+ List *result;
|
||
|
+ Const *greaterstr;
|
||
|
+
|
||
|
+ Assert(pstatus != Pattern_Prefix_None);
|
||
|
+ if ( pstatus == Pattern_Prefix_Exact ) {
|
||
|
+ oproid = get_opclass_member_mchar(opclass, leftTypeOid, BTEqualStrategyNumber);
|
||
|
+
|
||
|
+ expr = make_opclause(oproid, BOOLOID, false,
|
||
|
+ (Expr *) leftop, (Expr *) prefix_const,
|
||
|
+ InvalidOid, InvalidOid);
|
||
|
+ result = list_make1(make_simple_restrictinfo(expr));
|
||
|
+ return result;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* We can always say "x >= prefix". */
|
||
|
+ oproid = get_opclass_member_mchar(opclass, leftTypeOid, BTGreaterEqualStrategyNumber);
|
||
|
+
|
||
|
+ expr = make_opclause(oproid, BOOLOID, false,
|
||
|
+ (Expr *) leftop, (Expr *) prefix_const,
|
||
|
+ InvalidOid, InvalidOid);
|
||
|
+ result = list_make1(make_simple_restrictinfo(expr));
|
||
|
+
|
||
|
+ /* If we can create a string larger than the prefix, we can say
|
||
|
+ * "x < greaterstr". */
|
||
|
+
|
||
|
+ greaterstr = (Const*)DatumGetPointer( OidFunctionCall1(
|
||
|
+ mmGTOid,
|
||
|
+ PointerGetDatum( prefix_const )
|
||
|
+ ) );
|
||
|
+
|
||
|
+ if (greaterstr) {
|
||
|
+ oproid = get_opclass_member_mchar(opclass, leftTypeOid, BTLessStrategyNumber);
|
||
|
+
|
||
|
+ expr = make_opclause(oproid, BOOLOID, false,
|
||
|
+ (Expr *) leftop, (Expr *) greaterstr,
|
||
|
+ InvalidOid, InvalidOid);
|
||
|
+ result = lappend(result, make_simple_restrictinfo(expr));
|
||
|
+ }
|
||
|
+
|
||
|
+ return result;
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+/****************************************************************************
|
||
|
* ---- ROUTINES FOR "SPECIAL" INDEXABLE OPERATORS ----
|
||
|
****************************************************************************/
|
||
|
|
||
|
@@ -3159,9 +3369,16 @@ match_special_index_operator(Expr *clause, Oid opfamily, Oid idxcollation,
|
||
|
pfree(prefix);
|
||
|
}
|
||
|
|
||
|
- /* done if the expression doesn't look indexable */
|
||
|
- if (!isIndexable)
|
||
|
+ if ( !isIndexable ) {
|
||
|
+ /* done if the expression doesn't look indexable,
|
||
|
+ but we should previously check it for mchar/mvarchar types */
|
||
|
+ if ( mchar_pattern_fixed_prefix(expr_op, InvalidOid,
|
||
|
+ patt, Pattern_Type_Like,
|
||
|
+ &prefix, NULL) != Pattern_Prefix_None ) {
|
||
|
+ return true;
|
||
|
+ }
|
||
|
return false;
|
||
|
+ }
|
||
|
|
||
|
/*
|
||
|
* Must also check that index's opfamily supports the operators we will
|
||
|
@@ -3412,6 +3629,14 @@ expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily, Oid idxcollation)
|
||
|
Const *patt = (Const *) rightop;
|
||
|
Const *prefix = NULL;
|
||
|
Pattern_Prefix_Status pstatus;
|
||
|
+ Oid leftTypeOid;
|
||
|
+
|
||
|
+ pstatus = mchar_pattern_fixed_prefix(expr_op, opfamily,
|
||
|
+ patt, Pattern_Type_Like,
|
||
|
+ &prefix, &leftTypeOid);
|
||
|
+
|
||
|
+ if ( pstatus != Pattern_Prefix_None )
|
||
|
+ return mchar_prefix_quals(leftop, leftTypeOid, opfamily, prefix, pstatus);
|
||
|
|
||
|
/*
|
||
|
* LIKE and regex operators are not members of any btree index opfamily,
|
||
|
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
|
||
|
index d627f9e..589b728 100644
|
||
|
--- a/src/backend/optimizer/path/joinrels.c
|
||
|
+++ b/src/backend/optimizer/path/joinrels.c
|
||
|
@@ -1053,7 +1053,7 @@ mark_dummy_rel(RelOptInfo *rel)
|
||
|
rel->pathlist = NIL;
|
||
|
|
||
|
/* Set up the dummy path */
|
||
|
- add_path(rel, (Path *) create_append_path(rel, NIL, NULL));
|
||
|
+ add_path(rel, (Path *) create_append_path(rel, NIL, NULL, false, NIL));
|
||
|
|
||
|
/* Set or update cheapest_total_path and related fields */
|
||
|
set_cheapest(rel);
|
||
|
diff --git a/src/backend/optimizer/path/orindxpath.c b/src/backend/optimizer/path/orindxpath.c
|
||
|
index 16f29d3..a0b6c53 100644
|
||
|
--- a/src/backend/optimizer/path/orindxpath.c
|
||
|
+++ b/src/backend/optimizer/path/orindxpath.c
|
||
|
@@ -15,10 +15,58 @@
|
||
|
|
||
|
#include "postgres.h"
|
||
|
|
||
|
+#include "access/skey.h"
|
||
|
+#include "catalog/pg_am.h"
|
||
|
#include "optimizer/cost.h"
|
||
|
+#include "optimizer/clauses.h"
|
||
|
#include "optimizer/paths.h"
|
||
|
+#include "optimizer/pathnode.h"
|
||
|
+#include "optimizer/planmain.h"
|
||
|
+#include "optimizer/predtest.h"
|
||
|
#include "optimizer/restrictinfo.h"
|
||
|
+#include "utils/lsyscache.h"
|
||
|
|
||
|
+typedef struct CKey {
|
||
|
+ RestrictInfo *rinfo; /* original rinfo */
|
||
|
+ int n; /* IndexPath's number in bitmapquals */
|
||
|
+ OpExpr *normalizedexpr; /* expression with Var on left */
|
||
|
+ Var *var;
|
||
|
+ Node *value;
|
||
|
+ Oid opfamily;
|
||
|
+ int strategy;
|
||
|
+ uint8 strategyMask;
|
||
|
+} CKey;
|
||
|
+#define BTMASK(x) ( 1<<(x) )
|
||
|
+
|
||
|
+static List* find_common_quals( BitmapOrPath *path );
|
||
|
+static RestrictInfo* unionOperation(CKey *key);
|
||
|
+static BitmapOrPath* cleanup_nested_quals( PlannerInfo *root, RelOptInfo *rel, BitmapOrPath *path );
|
||
|
+static List* sortIndexScans( List* ipaths );
|
||
|
+static List* reverseScanDirIdxPaths(List *indexPaths);
|
||
|
+static IndexPath* reverseScanDirIdxPath(IndexPath *ipath);
|
||
|
+
|
||
|
+#define IS_LESS(a) ( (a) == BTLessStrategyNumber || (a)== BTLessEqualStrategyNumber )
|
||
|
+#define IS_GREATER(a) ( (a) == BTGreaterStrategyNumber || (a) == BTGreaterEqualStrategyNumber )
|
||
|
+#define IS_ONE_DIRECTION(a,b) ( \
|
||
|
+ ( IS_LESS(a) && IS_LESS(b) ) \
|
||
|
+ || \
|
||
|
+ ( IS_GREATER(a) && IS_GREATER(b) ) \
|
||
|
+)
|
||
|
+
|
||
|
+typedef struct ExExpr {
|
||
|
+ OpExpr *expr;
|
||
|
+ Oid opfamily;
|
||
|
+ Oid lefttype;
|
||
|
+ Oid righttype;
|
||
|
+ int strategy;
|
||
|
+ int attno;
|
||
|
+} ExExpr;
|
||
|
+
|
||
|
+
|
||
|
+typedef struct IndexPathEx {
|
||
|
+ IndexPath *path;
|
||
|
+ List *preparedquals; /* list of ExExpr */
|
||
|
+} IndexPathEx;
|
||
|
|
||
|
/*----------
|
||
|
* create_or_index_quals
|
||
|
@@ -185,3 +233,912 @@ create_or_index_quals(PlannerInfo *root, RelOptInfo *rel)
|
||
|
/* Tell caller to recompute partial index status and rowcount estimate */
|
||
|
return true;
|
||
|
}
|
||
|
+
|
||
|
+
|
||
|
+/*----------
|
||
|
+ * keybased_rewrite_or_index_quals
|
||
|
+ * Examine join OR-of-AND quals to see if any useful common restriction
|
||
|
+ * clauses can be extracted. If so, try to use for creating new index paths.
|
||
|
+ *
|
||
|
+ * For example consider
|
||
|
+ * WHERE ( a.x=5 and a.y>10 ) OR a.x>5
|
||
|
+ * and there is an index on a.x or (a.x, a.y). So, plan
|
||
|
+ * will be seqscan or BitmapOr(IndexPath,IndexPath)
|
||
|
+ * So, we can add some restriction:
|
||
|
+ * WHERE (( a.x=5 and a.y>10 ) OR a.x>5) AND a.x>=5
|
||
|
+ * and plan may be so
|
||
|
+ * Index Scan (a.x>=5)
|
||
|
+ * Filter( (( a.x=5 and a.y>10 ) OR a.x>5) )
|
||
|
+ *
|
||
|
+ * We don't want to add new clauses to baserestrictinfo, just
|
||
|
+ * use it as index quals.
|
||
|
+ *
|
||
|
+ * Next thing which it possible to test is use append of
|
||
|
+ * searches instead of OR.
|
||
|
+ * For example consider
|
||
|
+ * WHERE ( a.x=5 and a.y>10 ) OR a.x>6
|
||
|
+ * and there is an index on (a.x) (a.x, a.y)
|
||
|
+ * So, we can suggest follow plan:
|
||
|
+ * Append
|
||
|
+ * Filter ( a.x=5 and a.y>10 ) OR (a.x>6)
|
||
|
+ * Index Scan (a.x=5) --in case of index on (a.x)
|
||
|
+ * Index Scan (a.x>6)
|
||
|
+ * For that we should proof that index quals isn't overlapped,
|
||
|
+ * also, some index quals may be containedi in other, so it can be eliminated
|
||
|
+ */
|
||
|
+
|
||
|
+void
|
||
|
+keybased_rewrite_index_paths(PlannerInfo *root, RelOptInfo *rel)
|
||
|
+{
|
||
|
+ BitmapOrPath *bestpath = NULL;
|
||
|
+ ListCell *i;
|
||
|
+ List *commonquals;
|
||
|
+ AppendPath *appendidxpath;
|
||
|
+ List *indexPaths;
|
||
|
+ IndexOptInfo *index;
|
||
|
+
|
||
|
+ foreach(i, rel->baserestrictinfo)
|
||
|
+ {
|
||
|
+ RestrictInfo *rinfo = (RestrictInfo *) lfirst(i);
|
||
|
+
|
||
|
+ if (restriction_is_or_clause(rinfo) &&
|
||
|
+ !rinfo->outerjoin_delayed)
|
||
|
+ {
|
||
|
+ /*
|
||
|
+ * Use the generate_bitmap_or_paths() machinery to estimate the
|
||
|
+ * value of each OR clause. We can use regular restriction
|
||
|
+ * clauses along with the OR clause contents to generate
|
||
|
+ * indexquals. We pass outer_rel = NULL so that sub-clauses
|
||
|
+ * that are actually joins will be ignored.
|
||
|
+ */
|
||
|
+ List *orpaths;
|
||
|
+ ListCell *k;
|
||
|
+
|
||
|
+ orpaths = generate_bitmap_or_paths(root, rel,
|
||
|
+ list_make1(rinfo),
|
||
|
+ rel->baserestrictinfo,
|
||
|
+ true);
|
||
|
+
|
||
|
+ /* Locate the cheapest OR path */
|
||
|
+ foreach(k, orpaths)
|
||
|
+ {
|
||
|
+ BitmapOrPath *path = (BitmapOrPath *) lfirst(k);
|
||
|
+
|
||
|
+ Assert(IsA(path, BitmapOrPath));
|
||
|
+ if (bestpath == NULL ||
|
||
|
+ path->path.total_cost < bestpath->path.total_cost)
|
||
|
+ {
|
||
|
+ bestpath = path;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ /* Fail if no suitable clauses found */
|
||
|
+ if (bestpath == NULL)
|
||
|
+ return;
|
||
|
+
|
||
|
+ commonquals = find_common_quals(bestpath);
|
||
|
+ /* Found quals with the same args, but with, may be, different
|
||
|
+ operations */
|
||
|
+ if ( commonquals != NULL ) {
|
||
|
+ List *addon=NIL;
|
||
|
+
|
||
|
+ foreach(i, commonquals) {
|
||
|
+ CKey *key = (CKey*)lfirst(i);
|
||
|
+ RestrictInfo *rinfo;
|
||
|
+
|
||
|
+ /*
|
||
|
+ * get 'union' of operation for key
|
||
|
+ */
|
||
|
+ rinfo = unionOperation(key);
|
||
|
+ if ( rinfo )
|
||
|
+ addon = lappend(addon, rinfo);
|
||
|
+ }
|
||
|
+
|
||
|
+ /*
|
||
|
+ * Ok, we found common quals and union it, so we will try to
|
||
|
+ * create new possible index paths
|
||
|
+ */
|
||
|
+ if ( addon ) {
|
||
|
+ List *origbaserestrictinfo = list_copy(rel->baserestrictinfo);
|
||
|
+
|
||
|
+ rel->baserestrictinfo = list_concat(rel->baserestrictinfo, addon);
|
||
|
+
|
||
|
+ create_index_paths(root, rel);
|
||
|
+
|
||
|
+ rel->baserestrictinfo = origbaserestrictinfo;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ /*
|
||
|
+ * Check if indexquals isn't overlapped and all index scan
|
||
|
+ * are on the same index.
|
||
|
+ */
|
||
|
+ if ( (bestpath = cleanup_nested_quals( root, rel, bestpath )) == NULL )
|
||
|
+ return;
|
||
|
+
|
||
|
+ if (IsA(bestpath, IndexPath)) {
|
||
|
+ IndexPath *ipath = (IndexPath*)bestpath;
|
||
|
+
|
||
|
+ Assert(list_length(ipath->indexquals) == list_length(ipath->indexqualcols));
|
||
|
+ /*
|
||
|
+ * It's possible to do only one index scan :)
|
||
|
+ */
|
||
|
+ index = ipath->indexinfo;
|
||
|
+
|
||
|
+ if ( root->query_pathkeys != NIL && index->sortopfamily && OidIsValid(index->sortopfamily[0]) )
|
||
|
+ {
|
||
|
+ List *pathkeys;
|
||
|
+
|
||
|
+ pathkeys = build_index_pathkeys(root, index,
|
||
|
+ ForwardScanDirection);
|
||
|
+ pathkeys = truncate_useless_pathkeys(root, rel,
|
||
|
+ pathkeys);
|
||
|
+
|
||
|
+ ipath->path.pathkeys = pathkeys;
|
||
|
+ add_path(rel, (Path *) ipath);
|
||
|
+
|
||
|
+ /*
|
||
|
+ * add path ordered in backward direction if our pathkeys
|
||
|
+ * is still unusable...
|
||
|
+ */
|
||
|
+ if ( pathkeys == NULL || pathkeys_useful_for_ordering(root, pathkeys) == 0 )
|
||
|
+ {
|
||
|
+ pathkeys = build_index_pathkeys(root, index,
|
||
|
+ BackwardScanDirection);
|
||
|
+ pathkeys = truncate_useless_pathkeys(root, rel,
|
||
|
+ pathkeys);
|
||
|
+
|
||
|
+ ipath = reverseScanDirIdxPath( ipath );
|
||
|
+
|
||
|
+ ipath->path.pathkeys = pathkeys;
|
||
|
+ add_path(rel, (Path *) ipath);
|
||
|
+ }
|
||
|
+ } else
|
||
|
+ add_path(rel, (Path *) ipath);
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* recount costs */
|
||
|
+ foreach(i, bestpath->bitmapquals ) {
|
||
|
+ IndexPath *ipath = (IndexPath*)lfirst(i);
|
||
|
+
|
||
|
+ Assert( IsA(ipath, IndexPath) );
|
||
|
+ Assert(list_length(ipath->indexquals) == list_length(ipath->indexqualcols));
|
||
|
+ ipath->path.rows = rel->tuples * clauselist_selectivity(root,
|
||
|
+ ipath->indexquals,
|
||
|
+ rel->relid,
|
||
|
+ JOIN_INNER,
|
||
|
+ NULL);
|
||
|
+ ipath->path.rows = clamp_row_est(ipath->path.rows);
|
||
|
+ cost_index(ipath, root, 1);
|
||
|
+ }
|
||
|
+
|
||
|
+ /*
|
||
|
+ * Check if append index can suggest ordering of result
|
||
|
+ *
|
||
|
+ * Also, we should say to AppendPath about targetlist:
|
||
|
+ * target list will be taked from indexscan
|
||
|
+ */
|
||
|
+ index = ((IndexPath*)linitial(bestpath->bitmapquals))->indexinfo;
|
||
|
+ if ( root->query_pathkeys != NIL && index->sortopfamily && OidIsValid(index->sortopfamily[0]) &&
|
||
|
+ (indexPaths = sortIndexScans( bestpath->bitmapquals )) !=NULL ) {
|
||
|
+ List *pathkeys;
|
||
|
+
|
||
|
+ pathkeys = build_index_pathkeys(root, index,
|
||
|
+ ForwardScanDirection);
|
||
|
+ pathkeys = truncate_useless_pathkeys(root, rel,
|
||
|
+ pathkeys);
|
||
|
+
|
||
|
+ appendidxpath = create_append_path(rel, indexPaths, NULL, true, pathkeys);
|
||
|
+ add_path(rel, (Path *) appendidxpath);
|
||
|
+
|
||
|
+ /*
|
||
|
+ * add path ordered in backward direction if our pathkeys
|
||
|
+ * is still unusable...
|
||
|
+ */
|
||
|
+ if ( pathkeys == NULL || pathkeys_useful_for_ordering(root, pathkeys) == 0 ) {
|
||
|
+
|
||
|
+ pathkeys = build_index_pathkeys(root, index,
|
||
|
+ BackwardScanDirection);
|
||
|
+ pathkeys = truncate_useless_pathkeys(root, rel,
|
||
|
+ pathkeys);
|
||
|
+
|
||
|
+ indexPaths = reverseScanDirIdxPaths(indexPaths);
|
||
|
+ appendidxpath = create_append_path(rel, indexPaths, NULL, true, pathkeys);
|
||
|
+ add_path(rel, (Path *) appendidxpath);
|
||
|
+ }
|
||
|
+ } else {
|
||
|
+ appendidxpath = create_append_path(rel, bestpath->bitmapquals, NULL, true, NIL);
|
||
|
+ add_path(rel, (Path *) appendidxpath);
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+/*
|
||
|
+ * transformToCkey - transform RestrictionInfo
|
||
|
+ * to CKey struct. Fucntion checks possibility and correctness of
|
||
|
+ * RestrictionInfo to use it as common key, normalizes
|
||
|
+ * expression and "caches" some information. Note,
|
||
|
+ * original RestrictInfo isn't touched
|
||
|
+ */
|
||
|
+
|
||
|
+static CKey*
|
||
|
+transformToCkey( IndexOptInfo *index, RestrictInfo* rinfo, int indexcol) {
|
||
|
+ CKey *key;
|
||
|
+ OpExpr *expr = (OpExpr*)rinfo->clause;
|
||
|
+
|
||
|
+ if ( rinfo->outerjoin_delayed )
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ if ( !IsA(expr, OpExpr) )
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ if ( contain_mutable_functions((Node*)expr) )
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ if ( list_length( expr->args ) != 2 )
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ key = (CKey*)palloc(sizeof(CKey));
|
||
|
+ key->rinfo = rinfo;
|
||
|
+
|
||
|
+ key->normalizedexpr = (OpExpr*)copyObject( expr );
|
||
|
+ if (!bms_equal(rinfo->left_relids, index->rel->relids))
|
||
|
+ CommuteOpExpr(key->normalizedexpr);
|
||
|
+
|
||
|
+ /*
|
||
|
+ * fix_indexqual_operand returns copy of object
|
||
|
+ */
|
||
|
+ key->var = (Var*)fix_indexqual_operand(linitial(key->normalizedexpr->args), index, indexcol);
|
||
|
+ Assert( IsA(key->var, Var) );
|
||
|
+
|
||
|
+ key->opfamily = index->opfamily[ key->var->varattno - 1 ];
|
||
|
+
|
||
|
+ /* restore varattno, because it may be different in different index */
|
||
|
+ key->var->varattno = key->var->varoattno;
|
||
|
+
|
||
|
+ key->value = (Node*)lsecond(key->normalizedexpr->args);
|
||
|
+
|
||
|
+ key->strategy = get_op_opfamily_strategy( key->normalizedexpr->opno, key->opfamily);
|
||
|
+ Assert( key->strategy != InvalidStrategy );
|
||
|
+
|
||
|
+ key->strategyMask = BTMASK(key->strategy);
|
||
|
+
|
||
|
+ return key;
|
||
|
+}
|
||
|
+
|
||
|
+/*
|
||
|
+ * get_index_quals - get list of quals in
|
||
|
+ * CKeys form
|
||
|
+ */
|
||
|
+
|
||
|
+static List*
|
||
|
+get_index_quals(IndexPath *path, int cnt) {
|
||
|
+ ListCell *i, *c;
|
||
|
+ List *quals = NIL;
|
||
|
+
|
||
|
+ Assert(list_length(path->indexquals) == list_length(path->indexqualcols));
|
||
|
+ forboth(i, path->indexquals, c, path->indexqualcols) {
|
||
|
+ CKey *k = transformToCkey( path->indexinfo, (RestrictInfo*)lfirst(i), lfirst_int(c) );
|
||
|
+ if ( k ) {
|
||
|
+ k->n = cnt;
|
||
|
+ quals = lappend(quals, k);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ return quals;
|
||
|
+}
|
||
|
+
|
||
|
+/*
|
||
|
+ * extract all quals from bitmapquals->indexquals for
|
||
|
+ */
|
||
|
+static List*
|
||
|
+find_all_quals( BitmapOrPath *path, int *counter ) {
|
||
|
+ ListCell *i,*j;
|
||
|
+ List *allquals = NIL;
|
||
|
+
|
||
|
+ *counter = 0;
|
||
|
+
|
||
|
+ foreach(i, path->bitmapquals )
|
||
|
+ {
|
||
|
+ Path *subpath = (Path *) lfirst(i);
|
||
|
+
|
||
|
+ if ( IsA(subpath, BitmapAndPath) ) {
|
||
|
+ foreach(j, ((BitmapAndPath*)subpath)->bitmapquals) {
|
||
|
+ Path *subsubpath = (Path *) lfirst(i);
|
||
|
+
|
||
|
+ if ( IsA(subsubpath, IndexPath) ) {
|
||
|
+ if ( ((IndexPath*)subsubpath)->indexinfo->relam != BTREE_AM_OID )
|
||
|
+ return NIL;
|
||
|
+ allquals = list_concat(allquals, get_index_quals( (IndexPath*)subsubpath, *counter ));
|
||
|
+ } else
|
||
|
+ return NIL;
|
||
|
+ }
|
||
|
+ } else if ( IsA(subpath, IndexPath) ) {
|
||
|
+ if ( ((IndexPath*)subpath)->indexinfo->relam != BTREE_AM_OID )
|
||
|
+ return NIL;
|
||
|
+ allquals = list_concat(allquals, get_index_quals( (IndexPath*)subpath, *counter ));
|
||
|
+ } else
|
||
|
+ return NIL;
|
||
|
+
|
||
|
+ (*counter)++;
|
||
|
+ }
|
||
|
+
|
||
|
+ return allquals;
|
||
|
+}
|
||
|
+
|
||
|
+/*
|
||
|
+ * Compares aruments of operation
|
||
|
+ */
|
||
|
+static bool
|
||
|
+iseqCKeyArgs( CKey *a, CKey *b ) {
|
||
|
+ if ( a->opfamily != b->opfamily )
|
||
|
+ return false;
|
||
|
+
|
||
|
+ if ( !equal( a->value, b->value ) )
|
||
|
+ return false;
|
||
|
+
|
||
|
+ if ( !equal( a->var, b->var ) )
|
||
|
+ return false;
|
||
|
+
|
||
|
+ return true;
|
||
|
+}
|
||
|
+
|
||
|
+/*
|
||
|
+ * Count entries of CKey with the same arguments
|
||
|
+ */
|
||
|
+static int
|
||
|
+count_entry( List *allquals, CKey *tocmp ) {
|
||
|
+ ListCell *i;
|
||
|
+ int curcnt=0;
|
||
|
+
|
||
|
+ foreach(i, allquals) {
|
||
|
+ CKey *key = lfirst(i);
|
||
|
+
|
||
|
+ if ( key->n == curcnt ) {
|
||
|
+ continue;
|
||
|
+ } else if ( key->n == curcnt+1 ) {
|
||
|
+ if ( iseqCKeyArgs( key, tocmp ) ) {
|
||
|
+ tocmp->strategyMask |= key->strategyMask;
|
||
|
+ curcnt++;
|
||
|
+ }
|
||
|
+ } else
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+
|
||
|
+ return curcnt+1;
|
||
|
+}
|
||
|
+
|
||
|
+/*
|
||
|
+ * Finds all CKey with the same arguments
|
||
|
+ */
|
||
|
+static List*
|
||
|
+find_common_quals( BitmapOrPath *path ) {
|
||
|
+ List *allquals;
|
||
|
+ List *commonquals = NIL;
|
||
|
+ ListCell *i;
|
||
|
+ int counter;
|
||
|
+
|
||
|
+ if ( (allquals = find_all_quals( path, &counter ))==NIL )
|
||
|
+ return NIL;
|
||
|
+
|
||
|
+ foreach(i, allquals) {
|
||
|
+ CKey *key = lfirst(i);
|
||
|
+
|
||
|
+ if ( key->n != 0 )
|
||
|
+ break;
|
||
|
+
|
||
|
+ if ( counter == count_entry(allquals, key) )
|
||
|
+ commonquals = lappend( commonquals, key );
|
||
|
+ }
|
||
|
+
|
||
|
+ return commonquals;
|
||
|
+}
|
||
|
+
|
||
|
+/*
|
||
|
+ * unionOperation - make RestrictInfo with combined operation
|
||
|
+ */
|
||
|
+
|
||
|
+static RestrictInfo*
|
||
|
+unionOperation(CKey *key) {
|
||
|
+ RestrictInfo *rinfo;
|
||
|
+ Oid lefttype, righttype;
|
||
|
+ int strategy;
|
||
|
+
|
||
|
+ switch( key->strategyMask ) {
|
||
|
+ case BTMASK(BTLessStrategyNumber):
|
||
|
+ case BTMASK(BTLessEqualStrategyNumber):
|
||
|
+ case BTMASK(BTEqualStrategyNumber):
|
||
|
+ case BTMASK(BTGreaterEqualStrategyNumber):
|
||
|
+ case BTMASK(BTGreaterStrategyNumber):
|
||
|
+ /* trivial case */
|
||
|
+ break;
|
||
|
+ case BTMASK(BTLessStrategyNumber) | BTMASK(BTLessEqualStrategyNumber):
|
||
|
+ case BTMASK(BTLessStrategyNumber) | BTMASK(BTLessEqualStrategyNumber) | BTMASK(BTEqualStrategyNumber):
|
||
|
+ case BTMASK(BTLessStrategyNumber) | BTMASK(BTEqualStrategyNumber):
|
||
|
+ case BTMASK(BTLessEqualStrategyNumber) | BTMASK(BTEqualStrategyNumber):
|
||
|
+ /* any subset of <, <=, = can be unioned with <= */
|
||
|
+ key->strategy = BTLessEqualStrategyNumber;
|
||
|
+ break;
|
||
|
+ case BTMASK(BTGreaterEqualStrategyNumber) | BTMASK(BTGreaterStrategyNumber):
|
||
|
+ case BTMASK(BTEqualStrategyNumber) | BTMASK(BTGreaterEqualStrategyNumber) | BTMASK(BTGreaterStrategyNumber):
|
||
|
+ case BTMASK(BTEqualStrategyNumber) | BTMASK(BTGreaterStrategyNumber):
|
||
|
+ case BTMASK(BTEqualStrategyNumber) | BTMASK(BTGreaterEqualStrategyNumber):
|
||
|
+ /* any subset of >, >=, = can be unioned with >= */
|
||
|
+ key->strategy = BTGreaterEqualStrategyNumber;
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ /*
|
||
|
+ * Can't make common restrict qual
|
||
|
+ */
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ get_op_opfamily_properties(key->normalizedexpr->opno, key->opfamily, false,
|
||
|
+ &strategy, &lefttype, &righttype);
|
||
|
+
|
||
|
+ if ( strategy != key->strategy ) {
|
||
|
+ /*
|
||
|
+ * We should check because it's possible to have "strange"
|
||
|
+ * opfamilies - without some strategies...
|
||
|
+ */
|
||
|
+ key->normalizedexpr->opno = get_opfamily_member(key->opfamily, lefttype, righttype, key->strategy);
|
||
|
+
|
||
|
+ if ( key->normalizedexpr->opno == InvalidOid )
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ key->normalizedexpr->opfuncid = get_opcode( key->normalizedexpr->opno );
|
||
|
+ Assert ( key->normalizedexpr->opfuncid != InvalidOid );
|
||
|
+ }
|
||
|
+
|
||
|
+ rinfo = make_simple_restrictinfo((Expr*)key->normalizedexpr);
|
||
|
+
|
||
|
+ return rinfo;
|
||
|
+}
|
||
|
+
|
||
|
+/*
|
||
|
+ * Remove unneeded RestrioctionInfo nodes as it
|
||
|
+ * needed by predicate_*_by()
|
||
|
+ */
|
||
|
+static void
|
||
|
+make_predicate(List *indexquals, List *indexqualcols, List **preds, List **predcols) {
|
||
|
+ ListCell *i, *c;
|
||
|
+
|
||
|
+ *preds = NIL;
|
||
|
+ *predcols = NIL;
|
||
|
+
|
||
|
+ forboth(i, indexquals, c, indexqualcols)
|
||
|
+ {
|
||
|
+ RestrictInfo *rinfo = lfirst(i);
|
||
|
+ OpExpr *expr = (OpExpr*)rinfo->clause;
|
||
|
+
|
||
|
+ if ( rinfo->outerjoin_delayed )
|
||
|
+ continue;
|
||
|
+
|
||
|
+ if ( !IsA(expr, OpExpr) )
|
||
|
+ continue;
|
||
|
+
|
||
|
+ if ( list_length( expr->args ) != 2 )
|
||
|
+ continue;
|
||
|
+
|
||
|
+ *preds = lappend(*preds, rinfo);
|
||
|
+ *predcols = lappend(*predcols, lfirst(c));
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+#define CELL_GET_QUALS(x) ( ((IndexPath*)lfirst(x))->indexquals )
|
||
|
+#define CELL_GET_CLAUSES(x) ( ((IndexPath*)lfirst(x))->indexclauses )
|
||
|
+
|
||
|
+static List*
|
||
|
+listRInfo2OpExpr(List *listRInfo) {
|
||
|
+ ListCell *i;
|
||
|
+ List *listOpExpr=NULL;
|
||
|
+
|
||
|
+ foreach(i, listRInfo)
|
||
|
+ {
|
||
|
+ RestrictInfo *rinfo = lfirst(i);
|
||
|
+ OpExpr *expr = (OpExpr*)rinfo->clause;
|
||
|
+
|
||
|
+ listOpExpr = lappend(listOpExpr, expr);
|
||
|
+ }
|
||
|
+
|
||
|
+ return listOpExpr;
|
||
|
+}
|
||
|
+
|
||
|
+/*
|
||
|
+ * returns list of all nested quals
|
||
|
+ */
|
||
|
+static List*
|
||
|
+contained_quals(List *nested, List* quals, ListCell *check) {
|
||
|
+ ListCell *i;
|
||
|
+ List *checkpred;
|
||
|
+
|
||
|
+ if ( list_member_ptr( nested, lfirst(check) ) )
|
||
|
+ return nested;
|
||
|
+
|
||
|
+ if (equal(CELL_GET_QUALS(check), CELL_GET_CLAUSES(check)) == false)
|
||
|
+ return nested;
|
||
|
+
|
||
|
+ checkpred = listRInfo2OpExpr(CELL_GET_QUALS(check));
|
||
|
+
|
||
|
+ if ( contain_mutable_functions((Node*)checkpred) )
|
||
|
+ return nested;
|
||
|
+
|
||
|
+ foreach(i, quals )
|
||
|
+ {
|
||
|
+ if ( check == i )
|
||
|
+ continue;
|
||
|
+
|
||
|
+ if ( list_member_ptr( nested, lfirst(i) ) )
|
||
|
+ continue;
|
||
|
+
|
||
|
+ if ( equal(CELL_GET_QUALS(i), CELL_GET_CLAUSES(i)) &&
|
||
|
+ predicate_implied_by( checkpred, CELL_GET_QUALS(i) ) )
|
||
|
+ nested = lappend( nested, lfirst(i) );
|
||
|
+ }
|
||
|
+ return nested;
|
||
|
+}
|
||
|
+
|
||
|
+/*
|
||
|
+ * Checks that one row can be in several quals.
|
||
|
+ * It's guaranteed by predicate_refuted_by()
|
||
|
+ */
|
||
|
+static bool
|
||
|
+is_intersect(ListCell *check) {
|
||
|
+ ListCell *i;
|
||
|
+ List *checkpred=NULL;
|
||
|
+
|
||
|
+ checkpred=listRInfo2OpExpr(CELL_GET_QUALS(check));
|
||
|
+ Assert( checkpred != NULL );
|
||
|
+
|
||
|
+ for_each_cell(i, check) {
|
||
|
+ if ( i==check )
|
||
|
+ continue;
|
||
|
+
|
||
|
+ if ( predicate_refuted_by( checkpred, CELL_GET_QUALS(i) ) == false )
|
||
|
+ return true;
|
||
|
+ }
|
||
|
+
|
||
|
+ return false;
|
||
|
+}
|
||
|
+
|
||
|
+/*
|
||
|
+ * Removes nested quals and gurantees that quals are not intersected,
|
||
|
+ * ie one row can't satisfy to several quals. It's open a possibility of
|
||
|
+ * Append node using instead of BitmapOr
|
||
|
+ */
|
||
|
+static BitmapOrPath*
|
||
|
+cleanup_nested_quals( PlannerInfo *root, RelOptInfo *rel, BitmapOrPath *path ) {
|
||
|
+ ListCell *i;
|
||
|
+ IndexOptInfo *index=NULL;
|
||
|
+ List *nested = NULL;
|
||
|
+
|
||
|
+ /*
|
||
|
+ * check all path to use only one index
|
||
|
+ */
|
||
|
+ foreach(i, path->bitmapquals )
|
||
|
+ {
|
||
|
+
|
||
|
+ if ( IsA(lfirst(i), IndexPath) ) {
|
||
|
+ List *preds, *predcols;
|
||
|
+ IndexPath *subpath = (IndexPath *) lfirst(i);
|
||
|
+
|
||
|
+ if ( subpath->indexinfo->relam != BTREE_AM_OID )
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ if ( index == NULL )
|
||
|
+ index = subpath->indexinfo;
|
||
|
+ else if ( index->indexoid != subpath->indexinfo->indexoid )
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ /*
|
||
|
+ * work only with optimizable quals
|
||
|
+ */
|
||
|
+ Assert(list_length(subpath->indexquals) == list_length(subpath->indexqualcols));
|
||
|
+ make_predicate(subpath->indexquals, subpath->indexqualcols, &preds, &predcols);
|
||
|
+ if (preds == NIL)
|
||
|
+ return NULL;
|
||
|
+ subpath->indexquals = preds;
|
||
|
+ subpath->indexqualcols = predcols;
|
||
|
+ Assert(list_length(subpath->indexquals) == list_length(subpath->indexqualcols));
|
||
|
+ } else
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ /*
|
||
|
+ * eliminate nested quals
|
||
|
+ */
|
||
|
+ foreach(i, path->bitmapquals ) {
|
||
|
+ nested = contained_quals(nested, path->bitmapquals, i);
|
||
|
+ }
|
||
|
+
|
||
|
+ if ( nested != NIL ) {
|
||
|
+ path->bitmapquals = list_difference_ptr( path->bitmapquals, nested );
|
||
|
+
|
||
|
+ Assert( list_length( path->bitmapquals )>0 );
|
||
|
+
|
||
|
+ /*
|
||
|
+ * All quals becomes only one after eliminating nested quals
|
||
|
+ */
|
||
|
+ if (list_length( path->bitmapquals ) == 1)
|
||
|
+ return (BitmapOrPath*)linitial(path->bitmapquals);
|
||
|
+ }
|
||
|
+
|
||
|
+ /*
|
||
|
+ * Checks for intersection
|
||
|
+ */
|
||
|
+ foreach(i, path->bitmapquals ) {
|
||
|
+ if ( is_intersect( i ) )
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ return path;
|
||
|
+}
|
||
|
+
|
||
|
+/*
|
||
|
+ * Checks if whole result of one simple operation is contained
|
||
|
+ * in another
|
||
|
+ */
|
||
|
+static int
|
||
|
+simpleCmpExpr( ExExpr *a, ExExpr *b ) {
|
||
|
+ if ( predicate_implied_by((List*)a->expr, (List*)b->expr) )
|
||
|
+ /*
|
||
|
+ * a:( Var < 15 ) > b:( Var <= 10 )
|
||
|
+ */
|
||
|
+ return 1;
|
||
|
+ else if ( predicate_implied_by((List*)b->expr, (List*)a->expr) )
|
||
|
+ /*
|
||
|
+ * a:( Var <= 10 ) < b:( Var < 15 )
|
||
|
+ */
|
||
|
+ return -1;
|
||
|
+ else
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+/*
|
||
|
+ * Trys to define where is equation - on left or right side
|
||
|
+ * a(< 10) b(=11) - on right
|
||
|
+ * a(> 10) b(=9) - on left
|
||
|
+ * a(= 10) b(=11) - on right
|
||
|
+ * a(= 10) b(=9) - on left
|
||
|
+ * Any other - result is 0;
|
||
|
+ */
|
||
|
+static int
|
||
|
+cmpEqExpr( ExExpr *a, ExExpr *b ) {
|
||
|
+ Oid oldop = b->expr->opno;
|
||
|
+ int res=0;
|
||
|
+
|
||
|
+ b->expr->opno = get_opfamily_member(b->opfamily, b->lefttype, b->righttype, BTLessStrategyNumber);
|
||
|
+ if ( b->expr->opno != InvalidOid ) {
|
||
|
+ b->expr->opfuncid = get_opcode( b->expr->opno );
|
||
|
+ res = simpleCmpExpr(a,b);
|
||
|
+ }
|
||
|
+
|
||
|
+ if ( res == 0 ) {
|
||
|
+ b->expr->opno = get_opfamily_member(b->opfamily, b->lefttype, b->righttype, BTGreaterStrategyNumber);
|
||
|
+ if ( b->expr->opno != InvalidOid ) {
|
||
|
+ b->expr->opfuncid = get_opcode( b->expr->opno );
|
||
|
+ res = -simpleCmpExpr(a,b);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ b->expr->opno = oldop;
|
||
|
+ b->expr->opfuncid = get_opcode( b->expr->opno );
|
||
|
+
|
||
|
+ return res;
|
||
|
+}
|
||
|
+
|
||
|
+/*
|
||
|
+ * Is result of a contained in result of b or on the contrary?
|
||
|
+ */
|
||
|
+static int
|
||
|
+cmpNegCmp( ExExpr *a, ExExpr *b ) {
|
||
|
+ Oid oldop = b->expr->opno;
|
||
|
+ int res = 0;
|
||
|
+
|
||
|
+ b->expr->opno = get_negator( b->expr->opno );
|
||
|
+ if ( b->expr->opno != InvalidOid ) {
|
||
|
+ b->expr->opfuncid = get_opcode( b->expr->opno );
|
||
|
+ res = simpleCmpExpr(a,b);
|
||
|
+ }
|
||
|
+
|
||
|
+ b->expr->opno = oldop;
|
||
|
+ b->expr->opfuncid = get_opcode( b->expr->opno );
|
||
|
+
|
||
|
+ return ( IS_LESS(a->strategy) ) ? res : -res;
|
||
|
+}
|
||
|
+
|
||
|
+/*
|
||
|
+ * Returns 1 if whole result of a is on left comparing with result of b
|
||
|
+ * Returns -1 if whole result of a is on right comparing with result of b
|
||
|
+ * Return 0 if it's impossible to define or results is overlapped
|
||
|
+ * Expressions should use the same attribute of index and should be
|
||
|
+ * a simple: just one operation with index.
|
||
|
+ */
|
||
|
+static int
|
||
|
+cmpExpr( ExExpr *a, ExExpr *b ) {
|
||
|
+ int res;
|
||
|
+
|
||
|
+ /*
|
||
|
+ * If a and b are overlapped, we can't decide which one is
|
||
|
+ * lefter or righter
|
||
|
+ */
|
||
|
+ if ( IS_ONE_DIRECTION(a->strategy, b->strategy) || predicate_refuted_by((List*)a->expr, (List*)b->expr) == false )
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ /*
|
||
|
+ * In this place it's impossible to have a row which satisfies
|
||
|
+ * a and b expressions, so we will try to find relatiove position of that results
|
||
|
+ */
|
||
|
+ if ( b->strategy == BTEqualStrategyNumber ) {
|
||
|
+ return -cmpEqExpr(a, b); /* Covers cases with any operations in a */
|
||
|
+ } else if ( a->strategy == BTEqualStrategyNumber ) {
|
||
|
+ return cmpEqExpr(b, a);
|
||
|
+ } else if ( (res = cmpNegCmp(a, b)) == 0 ) { /* so, a(<10) b(>20) */
|
||
|
+ res = -cmpNegCmp(b, a);
|
||
|
+ }
|
||
|
+
|
||
|
+ return res;
|
||
|
+}
|
||
|
+
|
||
|
+/*
|
||
|
+ * Try to define positions of result which satisfy indexquals a and b per
|
||
|
+ * one index's attribute.
|
||
|
+ */
|
||
|
+static int
|
||
|
+cmpColumnQuals( List *a, List *b, int attno ) {
|
||
|
+ int res = 0;
|
||
|
+ ListCell *ai, *bi;
|
||
|
+
|
||
|
+ foreach(ai, a) {
|
||
|
+ ExExpr *ae = (ExExpr*)lfirst(ai);
|
||
|
+
|
||
|
+ if ( attno != ae->attno )
|
||
|
+ continue;
|
||
|
+
|
||
|
+ foreach(bi, b) {
|
||
|
+ ExExpr *be = (ExExpr*)lfirst(bi);
|
||
|
+
|
||
|
+ if ( attno != be->attno )
|
||
|
+ continue;
|
||
|
+
|
||
|
+ if ((res=cmpExpr(ae, be))!=0)
|
||
|
+ return res;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static IndexOptInfo *sortingIndex = NULL;
|
||
|
+static bool volatile unableToDefine = false;
|
||
|
+
|
||
|
+/*
|
||
|
+ * Compare result of two indexquals.
|
||
|
+ * Warinig: it use PG_RE_THROW(), so any call should be wrapped with
|
||
|
+ * PG_TRY(). Try/catch construction is used here for minimize unneeded
|
||
|
+ * actions when sorting is impossible
|
||
|
+ */
|
||
|
+static int
|
||
|
+cmpIndexPathEx(const void *a, const void *b) {
|
||
|
+ IndexPathEx *aipe = (IndexPathEx*)a;
|
||
|
+ IndexPathEx *bipe = (IndexPathEx*)b;
|
||
|
+ int attno, res = 0;
|
||
|
+
|
||
|
+ for(attno=1; res==0 && attno<=sortingIndex->ncolumns; attno++)
|
||
|
+ res=cmpColumnQuals(aipe->preparedquals, bipe->preparedquals, attno);
|
||
|
+
|
||
|
+ if ( res==0 ) {
|
||
|
+ unableToDefine = true;
|
||
|
+ PG_RE_THROW(); /* it should be PG_THROW(), but it's the same */
|
||
|
+ }
|
||
|
+
|
||
|
+ return res;
|
||
|
+}
|
||
|
+
|
||
|
+/*
|
||
|
+ * Initialize lists of operation in useful form
|
||
|
+ */
|
||
|
+static List*
|
||
|
+prepareQuals(IndexOptInfo *index, List *indexquals, List *indexqualcols) {
|
||
|
+ ListCell *i, *c;
|
||
|
+ List *res=NULL;
|
||
|
+ ExExpr *ex;
|
||
|
+
|
||
|
+ Assert(list_length(indexquals) == list_length(indexqualcols));
|
||
|
+ forboth(i, indexquals, c, indexqualcols)
|
||
|
+ {
|
||
|
+ RestrictInfo *rinfo = lfirst(i);
|
||
|
+ OpExpr *expr = (OpExpr*)rinfo->clause;
|
||
|
+
|
||
|
+ if ( rinfo->outerjoin_delayed )
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ if ( !IsA(expr, OpExpr) )
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ if ( list_length( expr->args ) != 2 )
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ if ( contain_mutable_functions((Node*)expr) )
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ ex = (ExExpr*)palloc(sizeof(ExExpr));
|
||
|
+ ex->expr = (OpExpr*)copyObject( expr );
|
||
|
+ if (!bms_equal(rinfo->left_relids, index->rel->relids))
|
||
|
+ CommuteOpExpr(ex->expr);
|
||
|
+ linitial(ex->expr->args) = fix_indexqual_operand(linitial(ex->expr->args), index, lfirst_int(c));
|
||
|
+ ex->attno = ((Var*)linitial(ex->expr->args))->varattno;
|
||
|
+ ex->opfamily = index->opfamily[ ex->attno - 1 ];
|
||
|
+ get_op_opfamily_properties( ex->expr->opno, ex->opfamily, false,
|
||
|
+ &ex->strategy, &ex->lefttype, &ex->righttype);
|
||
|
+
|
||
|
+
|
||
|
+ res = lappend(res, ex);
|
||
|
+ }
|
||
|
+
|
||
|
+ return res;
|
||
|
+}
|
||
|
+
|
||
|
+/*
|
||
|
+ * sortIndexScans - sorts index scans to get sorted results.
|
||
|
+ * Function supposed that index is the same for all
|
||
|
+ * index scans
|
||
|
+ */
|
||
|
+static List*
|
||
|
+sortIndexScans( List* ipaths ) {
|
||
|
+ ListCell *i;
|
||
|
+ int j=0;
|
||
|
+ IndexPathEx *ipe = (IndexPathEx*)palloc( sizeof(IndexPathEx)*list_length(ipaths) );
|
||
|
+ List *orderedPaths = NIL;
|
||
|
+ IndexOptInfo *index = ((IndexPath*)linitial(ipaths))->indexinfo;
|
||
|
+
|
||
|
+ foreach(i, ipaths) {
|
||
|
+ ipe[j].path = (IndexPath*)lfirst(i);
|
||
|
+ ipe[j].preparedquals = prepareQuals( index, ipe[j].path->indexquals, ipe[j].path->indexqualcols );
|
||
|
+
|
||
|
+ if (ipe[j].preparedquals == NULL)
|
||
|
+ return NULL;
|
||
|
+ j++;
|
||
|
+ }
|
||
|
+
|
||
|
+ sortingIndex = index;
|
||
|
+ unableToDefine = false;
|
||
|
+ PG_TRY(); {
|
||
|
+ qsort(ipe, list_length(ipaths), sizeof(IndexPathEx), cmpIndexPathEx);
|
||
|
+ } PG_CATCH(); {
|
||
|
+ if ( unableToDefine == false )
|
||
|
+ PG_RE_THROW(); /* not our problem */
|
||
|
+ } PG_END_TRY();
|
||
|
+
|
||
|
+ if ( unableToDefine == true )
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ for(j=0;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 6724996..8bb74cc 100644
|
||
|
--- a/src/backend/optimizer/path/pathkeys.c
|
||
|
+++ b/src/backend/optimizer/path/pathkeys.c
|
||
|
@@ -1379,7 +1379,7 @@ right_merge_direction(PlannerInfo *root, PathKey *pathkey)
|
||
|
* no good to order by just the first key(s) of the requested ordering.
|
||
|
* So the result is always either 0 or list_length(root->query_pathkeys).
|
||
|
*/
|
||
|
-static int
|
||
|
+int
|
||
|
pathkeys_useful_for_ordering(PlannerInfo *root, List *pathkeys)
|
||
|
{
|
||
|
if (root->query_pathkeys == NIL)
|
||
|
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
|
||
|
index c501737..3745e10 100644
|
||
|
--- a/src/backend/optimizer/plan/createplan.c
|
||
|
+++ b/src/backend/optimizer/plan/createplan.c
|
||
|
@@ -89,7 +89,6 @@ static void process_subquery_nestloop_params(PlannerInfo *root,
|
||
|
List *subplan_params);
|
||
|
static List *fix_indexqual_references(PlannerInfo *root, IndexPath *index_path);
|
||
|
static List *fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path);
|
||
|
-static Node *fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol);
|
||
|
static List *get_switched_clauses(List *clauses, Relids outerrelids);
|
||
|
static List *order_qual_clauses(PlannerInfo *root, List *clauses);
|
||
|
static void copy_path_costsize(Plan *dest, Path *src);
|
||
|
@@ -679,7 +678,7 @@ static Plan *
|
||
|
create_append_plan(PlannerInfo *root, AppendPath *best_path)
|
||
|
{
|
||
|
Append *plan;
|
||
|
- List *tlist = build_path_tlist(root, &best_path->path);
|
||
|
+ List *tlist;
|
||
|
List *subplans = NIL;
|
||
|
ListCell *subpaths;
|
||
|
|
||
|
@@ -695,6 +694,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
|
||
|
if (best_path->subpaths == NIL)
|
||
|
{
|
||
|
/* Generate a Result plan with constant-FALSE gating qual */
|
||
|
+ tlist = build_path_tlist(root, &best_path->path);
|
||
|
return (Plan *) make_result(root,
|
||
|
tlist,
|
||
|
(Node *) list_make1(makeBoolConst(false,
|
||
|
@@ -717,6 +717,11 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
|
||
|
* parent-rel Vars it'll be asked to emit.
|
||
|
*/
|
||
|
|
||
|
+ if ( best_path->pull_tlist )
|
||
|
+ tlist = copyObject( ((Plan*)linitial(subplans))->targetlist );
|
||
|
+ else
|
||
|
+ tlist = build_path_tlist(root, &best_path->path);
|
||
|
+
|
||
|
plan = make_append(subplans, tlist);
|
||
|
|
||
|
return (Plan *) plan;
|
||
|
@@ -2957,7 +2962,7 @@ fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path)
|
||
|
* Most of the code here is just for sanity cross-checking that the given
|
||
|
* expression actually matches the index column it's claimed to.
|
||
|
*/
|
||
|
-static Node *
|
||
|
+Node *
|
||
|
fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol)
|
||
|
{
|
||
|
Var *result;
|
||
|
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
|
||
|
index b78d727..3a6b826 100644
|
||
|
--- a/src/backend/optimizer/plan/setrefs.c
|
||
|
+++ b/src/backend/optimizer/plan/setrefs.c
|
||
|
@@ -1692,6 +1692,10 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
|
||
|
{
|
||
|
Var *var = (Var *) node;
|
||
|
|
||
|
+ /* join_references_mutator already checks this node */
|
||
|
+ if ( var->varno == OUTER_VAR )
|
||
|
+ return (Node*)copyObject(var);
|
||
|
+
|
||
|
/* First look for the var in the input tlists */
|
||
|
newvar = search_indexed_tlist_for_var(var,
|
||
|
context->outer_itlist,
|
||
|
@@ -1701,6 +1705,9 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
|
||
|
return (Node *) newvar;
|
||
|
if (context->inner_itlist)
|
||
|
{
|
||
|
+ if ( var->varno == INNER_VAR )
|
||
|
+ return (Node*)copyObject(var);
|
||
|
+
|
||
|
newvar = search_indexed_tlist_for_var(var,
|
||
|
context->inner_itlist,
|
||
|
INNER_VAR,
|
||
|
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
|
||
|
index 64b17051..8ed8f50 100644
|
||
|
--- a/src/backend/optimizer/util/pathnode.c
|
||
|
+++ b/src/backend/optimizer/util/pathnode.c
|
||
|
@@ -888,7 +888,7 @@ create_tidscan_path(PlannerInfo *root, RelOptInfo *rel, List *tidquals,
|
||
|
* Note that we must handle subpaths = NIL, representing a dummy access path.
|
||
|
*/
|
||
|
AppendPath *
|
||
|
-create_append_path(RelOptInfo *rel, List *subpaths, Relids required_outer)
|
||
|
+create_append_path(RelOptInfo *rel, List *subpaths, Relids required_outer, bool pull_tlist, List *pathkeys)
|
||
|
{
|
||
|
AppendPath *pathnode = makeNode(AppendPath);
|
||
|
ListCell *l;
|
||
|
@@ -897,9 +897,10 @@ create_append_path(RelOptInfo *rel, List *subpaths, Relids required_outer)
|
||
|
pathnode->path.parent = rel;
|
||
|
pathnode->path.param_info = get_appendrel_parampathinfo(rel,
|
||
|
required_outer);
|
||
|
- pathnode->path.pathkeys = NIL; /* result is always considered
|
||
|
- * unsorted */
|
||
|
+ pathnode->path.pathkeys = pathkeys; /* !=NIL in case of append OR index scans */
|
||
|
+
|
||
|
pathnode->subpaths = subpaths;
|
||
|
+ pathnode->pull_tlist = pull_tlist;
|
||
|
|
||
|
/*
|
||
|
* We don't bother with inventing a cost_append(), but just do it here.
|
||
|
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
|
||
|
index 5094226..048a790 100644
|
||
|
--- a/src/backend/parser/gram.y
|
||
|
+++ b/src/backend/parser/gram.y
|
||
|
@@ -10549,7 +10549,7 @@ a_expr: c_expr { $$ = $1; }
|
||
|
| a_expr LIKE a_expr ESCAPE a_expr
|
||
|
{
|
||
|
FuncCall *n = makeNode(FuncCall);
|
||
|
- n->funcname = SystemFuncName("like_escape");
|
||
|
+ n->funcname = list_make1(makeString("like_escape"));
|
||
|
n->args = list_make2($3, $5);
|
||
|
n->agg_order = NIL;
|
||
|
n->agg_star = FALSE;
|
||
|
@@ -10564,7 +10564,7 @@ a_expr: c_expr { $$ = $1; }
|
||
|
| a_expr NOT LIKE a_expr ESCAPE a_expr
|
||
|
{
|
||
|
FuncCall *n = makeNode(FuncCall);
|
||
|
- n->funcname = SystemFuncName("like_escape");
|
||
|
+ n->funcname = list_make1(makeString("like_escape"));
|
||
|
n->args = list_make2($4, $6);
|
||
|
n->agg_order = NIL;
|
||
|
n->agg_star = FALSE;
|
||
|
@@ -10579,7 +10579,7 @@ a_expr: c_expr { $$ = $1; }
|
||
|
| a_expr ILIKE a_expr ESCAPE a_expr
|
||
|
{
|
||
|
FuncCall *n = makeNode(FuncCall);
|
||
|
- n->funcname = SystemFuncName("like_escape");
|
||
|
+ n->funcname = list_make1(makeString("like_escape"));
|
||
|
n->args = list_make2($3, $5);
|
||
|
n->agg_order = NIL;
|
||
|
n->agg_star = FALSE;
|
||
|
@@ -10594,7 +10594,7 @@ a_expr: c_expr { $$ = $1; }
|
||
|
| a_expr NOT ILIKE a_expr ESCAPE a_expr
|
||
|
{
|
||
|
FuncCall *n = makeNode(FuncCall);
|
||
|
- n->funcname = SystemFuncName("like_escape");
|
||
|
+ n->funcname = list_make1(makeString("like_escape"));
|
||
|
n->args = list_make2($4, $6);
|
||
|
n->agg_order = NIL;
|
||
|
n->agg_star = FALSE;
|
||
|
@@ -10608,7 +10608,7 @@ a_expr: c_expr { $$ = $1; }
|
||
|
| a_expr SIMILAR TO a_expr %prec SIMILAR
|
||
|
{
|
||
|
FuncCall *n = makeNode(FuncCall);
|
||
|
- n->funcname = SystemFuncName("similar_escape");
|
||
|
+ n->funcname = list_make1(makeString("similar_escape"));
|
||
|
n->args = list_make2($4, makeNullAConst(-1));
|
||
|
n->agg_order = NIL;
|
||
|
n->agg_star = FALSE;
|
||
|
@@ -10621,7 +10621,7 @@ a_expr: c_expr { $$ = $1; }
|
||
|
| a_expr SIMILAR TO a_expr ESCAPE a_expr
|
||
|
{
|
||
|
FuncCall *n = makeNode(FuncCall);
|
||
|
- n->funcname = SystemFuncName("similar_escape");
|
||
|
+ n->funcname = list_make1(makeString("similar_escape"));
|
||
|
n->args = list_make2($4, $6);
|
||
|
n->agg_order = NIL;
|
||
|
n->agg_star = FALSE;
|
||
|
@@ -10634,7 +10634,7 @@ a_expr: c_expr { $$ = $1; }
|
||
|
| a_expr NOT SIMILAR TO a_expr %prec SIMILAR
|
||
|
{
|
||
|
FuncCall *n = makeNode(FuncCall);
|
||
|
- n->funcname = SystemFuncName("similar_escape");
|
||
|
+ n->funcname = list_make1(makeString("similar_escape"));
|
||
|
n->args = list_make2($5, makeNullAConst(-1));
|
||
|
n->agg_order = NIL;
|
||
|
n->agg_star = FALSE;
|
||
|
@@ -10647,7 +10647,7 @@ a_expr: c_expr { $$ = $1; }
|
||
|
| a_expr NOT SIMILAR TO a_expr ESCAPE a_expr
|
||
|
{
|
||
|
FuncCall *n = makeNode(FuncCall);
|
||
|
- n->funcname = SystemFuncName("similar_escape");
|
||
|
+ n->funcname = list_make1(makeString("similar_escape"));
|
||
|
n->args = list_make2($5, $7);
|
||
|
n->agg_order = NIL;
|
||
|
n->agg_star = FALSE;
|
||
|
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
|
||
|
index a2853fb..e67ee63 100644
|
||
|
--- a/src/include/nodes/relation.h
|
||
|
+++ b/src/include/nodes/relation.h
|
||
|
@@ -879,6 +879,11 @@ typedef struct AppendPath
|
||
|
{
|
||
|
Path path;
|
||
|
List *subpaths; /* list of component Paths */
|
||
|
+ bool pull_tlist; /* if = true, create_append_plan()
|
||
|
+ should get targetlist from any
|
||
|
+ subpath - they are the same,
|
||
|
+ because the only place - append
|
||
|
+ index scan for range OR */
|
||
|
} AppendPath;
|
||
|
|
||
|
#define IS_DUMMY_PATH(p) \
|
||
|
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
|
||
|
index bc68789..5108fa1 100644
|
||
|
--- a/src/include/optimizer/pathnode.h
|
||
|
+++ b/src/include/optimizer/pathnode.h
|
||
|
@@ -57,7 +57,7 @@ extern BitmapOrPath *create_bitmap_or_path(PlannerInfo *root,
|
||
|
extern TidPath *create_tidscan_path(PlannerInfo *root, RelOptInfo *rel,
|
||
|
List *tidquals, Relids required_outer);
|
||
|
extern AppendPath *create_append_path(RelOptInfo *rel, List *subpaths,
|
||
|
- Relids required_outer);
|
||
|
+ Relids required_outer, bool pull_tlist, List *pathkeys);
|
||
|
extern MergeAppendPath *create_merge_append_path(PlannerInfo *root,
|
||
|
RelOptInfo *rel,
|
||
|
List *subpaths,
|
||
|
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
|
||
|
index 9ef93c7..eda1ae1 100644
|
||
|
--- a/src/include/optimizer/paths.h
|
||
|
+++ b/src/include/optimizer/paths.h
|
||
|
@@ -66,6 +66,7 @@ extern Expr *adjust_rowcompare_for_index(RowCompareExpr *clause,
|
||
|
* additional routines for indexable OR clauses
|
||
|
*/
|
||
|
extern bool create_or_index_quals(PlannerInfo *root, RelOptInfo *rel);
|
||
|
+extern void keybased_rewrite_index_paths(PlannerInfo *root, RelOptInfo *rel);
|
||
|
|
||
|
/*
|
||
|
* tidpath.h
|
||
|
@@ -188,6 +189,7 @@ extern List *select_outer_pathkeys_for_merge(PlannerInfo *root,
|
||
|
extern List *make_inner_pathkeys_for_merge(PlannerInfo *root,
|
||
|
List *mergeclauses,
|
||
|
List *outer_pathkeys);
|
||
|
+extern int pathkeys_useful_for_ordering(PlannerInfo *root, List *pathkeys);
|
||
|
extern List *truncate_useless_pathkeys(PlannerInfo *root,
|
||
|
RelOptInfo *rel,
|
||
|
List *pathkeys);
|
||
|
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
|
||
|
index 33eaf32..0379141 100644
|
||
|
--- a/src/include/optimizer/planmain.h
|
||
|
+++ b/src/include/optimizer/planmain.h
|
||
|
@@ -88,7 +88,7 @@ extern ModifyTable *make_modifytable(PlannerInfo *root,
|
||
|
List *resultRelations, List *subplans, List *returningLists,
|
||
|
List *rowMarks, int epqParam);
|
||
|
extern bool is_projection_capable_plan(Plan *plan);
|
||
|
-
|
||
|
+extern Node * fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol);
|
||
|
/*
|
||
|
* prototypes for plan/initsplan.c
|
||
|
*/
|
||
|
diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out
|
||
|
index 81c64e5..43f0340 100644
|
||
|
--- a/src/test/regress/expected/create_index.out
|
||
|
+++ b/src/test/regress/expected/create_index.out
|
||
|
@@ -2625,18 +2625,12 @@ DROP TABLE onek_with_null;
|
||
|
EXPLAIN (COSTS OFF)
|
||
|
SELECT * FROM tenk1
|
||
|
WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3 OR tenthous = 42);
|
||
|
- QUERY PLAN
|
||
|
------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
- Bitmap Heap Scan on tenk1
|
||
|
- Recheck Cond: (((thousand = 42) AND (tenthous = 1)) OR ((thousand = 42) AND (tenthous = 3)) OR ((thousand = 42) AND (tenthous = 42)))
|
||
|
- -> BitmapOr
|
||
|
- -> Bitmap Index Scan on tenk1_thous_tenthous
|
||
|
- Index Cond: ((thousand = 42) AND (tenthous = 1))
|
||
|
- -> Bitmap Index Scan on tenk1_thous_tenthous
|
||
|
- Index Cond: ((thousand = 42) AND (tenthous = 3))
|
||
|
- -> Bitmap Index Scan on tenk1_thous_tenthous
|
||
|
- Index Cond: ((thousand = 42) AND (tenthous = 42))
|
||
|
-(9 rows)
|
||
|
+ QUERY PLAN
|
||
|
+-----------------------------------------------------------------
|
||
|
+ Index Scan using tenk1_thous_tenthous on tenk1
|
||
|
+ Index Cond: ((thousand = 42) AND (thousand = 42))
|
||
|
+ Filter: ((tenthous = 1) OR (tenthous = 3) OR (tenthous = 42))
|
||
|
+(3 rows)
|
||
|
|
||
|
SELECT * FROM tenk1
|
||
|
WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3 OR tenthous = 42);
|
||
|
diff --git a/src/test/regress/expected/select.out b/src/test/regress/expected/select.out
|
||
|
index c376523..185d177 100644
|
||
|
--- a/src/test/regress/expected/select.out
|
||
|
+++ b/src/test/regress/expected/select.out
|
||
|
@@ -518,6 +518,124 @@ TABLE int8_tbl;
|
||
|
(9 rows)
|
||
|
|
||
|
--
|
||
|
+-- test order by NULLS (FIRST|LAST)
|
||
|
+--
|
||
|
+select unique1, unique2 into onek_with_null from onek;
|
||
|
+insert into onek_with_null (unique1,unique2) values (NULL, -1), (NULL, NULL);
|
||
|
+select * from onek_with_null order by unique1 nulls first , unique2 limit 3;
|
||
|
+ unique1 | unique2
|
||
|
+---------+---------
|
||
|
+ | -1
|
||
|
+ |
|
||
|
+ 0 | 998
|
||
|
+(3 rows)
|
||
|
+
|
||
|
+select * from onek_with_null order by unique1 nulls last , unique2 limit 3;
|
||
|
+ unique1 | unique2
|
||
|
+---------+---------
|
||
|
+ 0 | 998
|
||
|
+ 1 | 214
|
||
|
+ 2 | 326
|
||
|
+(3 rows)
|
||
|
+
|
||
|
+select * from onek_with_null order by unique1 nulls first , unique2 nulls first limit 3;
|
||
|
+ unique1 | unique2
|
||
|
+---------+---------
|
||
|
+ |
|
||
|
+ | -1
|
||
|
+ 0 | 998
|
||
|
+(3 rows)
|
||
|
+
|
||
|
+select * from onek_with_null order by unique1 nulls last , unique2 nulls first limit 3;
|
||
|
+ unique1 | unique2
|
||
|
+---------+---------
|
||
|
+ 0 | 998
|
||
|
+ 1 | 214
|
||
|
+ 2 | 326
|
||
|
+(3 rows)
|
||
|
+
|
||
|
+select * from onek_with_null order by unique1 nulls first , unique2 nulls last limit 3;
|
||
|
+ unique1 | unique2
|
||
|
+---------+---------
|
||
|
+ | -1
|
||
|
+ |
|
||
|
+ 0 | 998
|
||
|
+(3 rows)
|
||
|
+
|
||
|
+select * from onek_with_null order by unique1 nulls last , unique2 nulls last limit 3;
|
||
|
+ unique1 | unique2
|
||
|
+---------+---------
|
||
|
+ 0 | 998
|
||
|
+ 1 | 214
|
||
|
+ 2 | 326
|
||
|
+(3 rows)
|
||
|
+
|
||
|
+select * from onek_with_null order by unique1 desc nulls first , unique2 desc limit 3;
|
||
|
+ unique1 | unique2
|
||
|
+---------+---------
|
||
|
+ |
|
||
|
+ | -1
|
||
|
+ 999 | 152
|
||
|
+(3 rows)
|
||
|
+
|
||
|
+select * from onek_with_null order by unique1 desc nulls last , unique2 desc limit 3;
|
||
|
+ unique1 | unique2
|
||
|
+---------+---------
|
||
|
+ 999 | 152
|
||
|
+ 998 | 549
|
||
|
+ 997 | 21
|
||
|
+(3 rows)
|
||
|
+
|
||
|
+select * from onek_with_null order by unique1 desc nulls first , unique2 desc nulls first limit 3;
|
||
|
+ unique1 | unique2
|
||
|
+---------+---------
|
||
|
+ |
|
||
|
+ | -1
|
||
|
+ 999 | 152
|
||
|
+(3 rows)
|
||
|
+
|
||
|
+select * from onek_with_null order by unique1 desc nulls last , unique2 desc nulls first limit 3;
|
||
|
+ unique1 | unique2
|
||
|
+---------+---------
|
||
|
+ 999 | 152
|
||
|
+ 998 | 549
|
||
|
+ 997 | 21
|
||
|
+(3 rows)
|
||
|
+
|
||
|
+select * from onek_with_null order by unique1 desc nulls first , unique2 desc nulls last limit 3;
|
||
|
+ unique1 | unique2
|
||
|
+---------+---------
|
||
|
+ | -1
|
||
|
+ |
|
||
|
+ 999 | 152
|
||
|
+(3 rows)
|
||
|
+
|
||
|
+select * from onek_with_null order by unique1 desc nulls last , unique2 desc nulls last limit 3;
|
||
|
+ unique1 | unique2
|
||
|
+---------+---------
|
||
|
+ 999 | 152
|
||
|
+ 998 | 549
|
||
|
+ 997 | 21
|
||
|
+(3 rows)
|
||
|
+
|
||
|
+select unique1 as u1, unique2 as u2 from onek_with_null order by u1 nulls first , u2 nulls first limit 3;
|
||
|
+ u1 | u2
|
||
|
+----+-----
|
||
|
+ |
|
||
|
+ | -1
|
||
|
+ 0 | 998
|
||
|
+(3 rows)
|
||
|
+
|
||
|
+select unique1 as u1, unique2 as u2 from onek_with_null order by u1 asc nulls first , u2 desc nulls first limit 3;
|
||
|
+ u1 | u2
|
||
|
+----+-----
|
||
|
+ |
|
||
|
+ | -1
|
||
|
+ 0 | 998
|
||
|
+(3 rows)
|
||
|
+
|
||
|
+drop table onek_with_null;
|
||
|
+--
|
||
|
-- Test ORDER BY options
|
||
|
--
|
||
|
CREATE TEMP TABLE foo (f1 int);
|
||
|
diff --git a/src/test/regress/sql/select.sql b/src/test/regress/sql/select.sql
|
||
|
index b99fb13..582e5d6 100644
|
||
|
--- a/src/test/regress/sql/select.sql
|
||
|
+++ b/src/test/regress/sql/select.sql
|
||
|
@@ -149,6 +149,33 @@ UNION ALL
|
||
|
TABLE int8_tbl;
|
||
|
|
||
|
--
|
||
|
+-- test order by NULLS (FIRST|LAST)
|
||
|
+--
|
||
|
+
|
||
|
+select unique1, unique2 into onek_with_null from onek;
|
||
|
+insert into onek_with_null (unique1,unique2) values (NULL, -1), (NULL, NULL);
|
||
|
+
|
||
|
+
|
||
|
+select * from onek_with_null order by unique1 nulls first , unique2 limit 3;
|
||
|
+select * from onek_with_null order by unique1 nulls last , unique2 limit 3;
|
||
|
+select * from onek_with_null order by unique1 nulls first , unique2 nulls first limit 3;
|
||
|
+select * from onek_with_null order by unique1 nulls last , unique2 nulls first limit 3;
|
||
|
+select * from onek_with_null order by unique1 nulls first , unique2 nulls last limit 3;
|
||
|
+select * from onek_with_null order by unique1 nulls last , unique2 nulls last limit 3;
|
||
|
+
|
||
|
+select * from onek_with_null order by unique1 desc nulls first , unique2 desc limit 3;
|
||
|
+select * from onek_with_null order by unique1 desc nulls last , unique2 desc limit 3;
|
||
|
+select * from onek_with_null order by unique1 desc nulls first , unique2 desc nulls first limit 3;
|
||
|
+select * from onek_with_null order by unique1 desc nulls last , unique2 desc nulls first limit 3;
|
||
|
+select * from onek_with_null order by unique1 desc nulls first , unique2 desc nulls last limit 3;
|
||
|
+select * from onek_with_null order by unique1 desc nulls last , unique2 desc nulls last limit 3;
|
||
|
+
|
||
|
+select unique1 as u1, unique2 as u2 from onek_with_null order by u1 nulls first , u2 nulls first limit 3;
|
||
|
+select unique1 as u1, unique2 as u2 from onek_with_null order by u1 asc nulls first , u2 desc nulls first limit 3;
|
||
|
+
|
||
|
+drop table onek_with_null;
|
||
|
+
|
||
|
+--
|
||
|
-- Test ORDER BY options
|
||
|
--
|
||
|
|
||
|
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
|
||
|
index 7964c01..dc38388 100644
|
||
|
--- a/src/tools/msvc/Mkvcbuild.pm
|
||
|
+++ b/src/tools/msvc/Mkvcbuild.pm
|
||
|
@@ -44,7 +44,7 @@ my @contrib_uselibpgcommon = (
|
||
|
'pg_test_fsync', 'pg_test_timing',
|
||
|
'pg_upgrade', 'pg_xlogdump',
|
||
|
'vacuumlo');
|
||
|
-my $contrib_extralibs = { 'pgbench' => ['wsock32.lib'] };
|
||
|
+my $contrib_extralibs = { 'pgbench' => ['wsock32.lib'], 'mchar' => ['icuin.lib', 'icuuc.lib'] };
|
||
|
my $contrib_extraincludes =
|
||
|
{ 'tsearch2' => ['contrib/tsearch2'], 'dblink' => ['src/backend'] };
|
||
|
my $contrib_extrasource = {
|