mirror of
https://github.com/beard7n/bsdports.git
synced 2026-04-13 12:01:19 +02:00
9930 lines
246 KiB
Diff
9930 lines
246 KiB
Diff
diff --git a/contrib/fasttrun/Makefile b/contrib/fasttrun/Makefile
|
||
new file mode 100644
|
||
index 00000000000..78e92b86cbe
|
||
--- /dev/null
|
||
+++ contrib/fasttrun/Makefile
|
||
@@ -0,0 +1,17 @@
|
||
+MODULE_big = fasttrun
|
||
+OBJS = fasttrun.o
|
||
+DATA = fasttrun--2.0.sql fasttrun--unpackaged--2.0.sql
|
||
+DOCS = README.fasttrun
|
||
+REGRESS = fasttrun
|
||
+EXTENSION=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 contrib/fasttrun/README.fasttrun
|
||
new file mode 100644
|
||
index 00000000000..4b1dfdc1e60
|
||
--- /dev/null
|
||
+++ 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 contrib/fasttrun/expected/fasttrun.out
|
||
new file mode 100644
|
||
index 00000000000..ef64fa6400e
|
||
--- /dev/null
|
||
+++ contrib/fasttrun/expected/fasttrun.out
|
||
@@ -0,0 +1,115 @@
|
||
+CREATE EXTENSION fasttrun;
|
||
+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--2.0.sql contrib/fasttrun/fasttrun--2.0.sql
|
||
new file mode 100644
|
||
index 00000000000..55484024604
|
||
--- /dev/null
|
||
+++ contrib/fasttrun/fasttrun--2.0.sql
|
||
@@ -0,0 +1,7 @@
|
||
+\echo Use "CREATE EXTENSION fasttrun" to load this file. \quit
|
||
+
|
||
+
|
||
+CREATE OR REPLACE FUNCTION fasttruncate(text)
|
||
+RETURNS void AS 'MODULE_PATHNAME'
|
||
+LANGUAGE C RETURNS NULL ON NULL INPUT VOLATILE;
|
||
+
|
||
diff --git a/contrib/fasttrun/fasttrun--unpackaged--2.0.sql contrib/fasttrun/fasttrun--unpackaged--2.0.sql
|
||
new file mode 100644
|
||
index 00000000000..f97896ac5cb
|
||
--- /dev/null
|
||
+++ contrib/fasttrun/fasttrun--unpackaged--2.0.sql
|
||
@@ -0,0 +1,4 @@
|
||
+\echo Use "CREATE EXTENSION fasttrun FROM unpackaged" to load this file. \quit
|
||
+
|
||
+ALTER EXTENSION fasttrun ADD function fasttruncate(text);
|
||
+
|
||
diff --git a/contrib/fasttrun/fasttrun.c contrib/fasttrun/fasttrun.c
|
||
new file mode 100644
|
||
index 00000000000..ed59cdc8906
|
||
--- /dev/null
|
||
+++ contrib/fasttrun/fasttrun.c
|
||
@@ -0,0 +1,84 @@
|
||
+#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>
|
||
+#include <utils/regproc.h>
|
||
+#include <utils/varlena.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 ) {
|
||
+ VacuumParams params;
|
||
+ VacuumRelation *rel;
|
||
+
|
||
+ params.freeze_min_age = -1;
|
||
+ params.freeze_table_age = -1;
|
||
+ params.multixact_freeze_min_age = -1;
|
||
+ params.multixact_freeze_table_age = -1;
|
||
+ params.is_wraparound = false;
|
||
+ params.log_min_duration = -1;
|
||
+
|
||
+ rel = makeNode(VacuumRelation);
|
||
+ rel->relation = relvar;
|
||
+ rel->oid = relOid;
|
||
+ rel->va_cols = NULL;
|
||
+ vacuum(VACOPT_ANALYZE, list_make1(rel), ¶ms,
|
||
+ GetAccessStrategy(BAS_VACUUM), false);
|
||
+ }
|
||
+
|
||
+ PG_RETURN_VOID();
|
||
+}
|
||
diff --git a/contrib/fasttrun/fasttrun.control contrib/fasttrun/fasttrun.control
|
||
new file mode 100644
|
||
index 00000000000..00271c75a52
|
||
--- /dev/null
|
||
+++ contrib/fasttrun/fasttrun.control
|
||
@@ -0,0 +1,5 @@
|
||
+comment = 'fast transaction-unsafe truncate'
|
||
+default_version = '2.0'
|
||
+module_pathname = '$libdir/fasttrun'
|
||
+relocatable = true
|
||
+
|
||
diff --git a/contrib/fasttrun/sql/fasttrun.sql contrib/fasttrun/sql/fasttrun.sql
|
||
new file mode 100644
|
||
index 00000000000..0e3cb6c9beb
|
||
--- /dev/null
|
||
+++ contrib/fasttrun/sql/fasttrun.sql
|
||
@@ -0,0 +1,48 @@
|
||
+CREATE EXTENSION fasttrun;
|
||
+
|
||
+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 contrib/fulleq/Makefile
|
||
new file mode 100644
|
||
index 00000000000..77ca7af4dbe
|
||
--- /dev/null
|
||
+++ contrib/fulleq/Makefile
|
||
@@ -0,0 +1,45 @@
|
||
+MODULE_big = fulleq
|
||
+OBJS = fulleq.o
|
||
+DOCS = README.fulleq
|
||
+REGRESS = fulleq
|
||
+DATA_built = fulleq--2.0.sql fulleq--unpackaged--2.0.sql
|
||
+EXTENSION=fulleq
|
||
+
|
||
+ARGTYPE = bool bytea char name int8 int2 int4 text \
|
||
+ oid xid cid oidvector float4 float8 abstime reltime macaddr \
|
||
+ inet cidr varchar date time timestamp timestamptz \
|
||
+ interval timetz
|
||
+
|
||
+EXTRA_CLEAN = fulleq--2.0.sql fulleq--unpackaged--2.0.sql
|
||
+
|
||
+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
|
||
+
|
||
+all: fulleq--2.0.sql fulleq--unpackaged--2.0.sql
|
||
+
|
||
+fulleq--2.0.sql: fulleq.sql.in
|
||
+ echo '\echo Use "CREATE EXTENSION fulleq" to load this file. \quit' > $@
|
||
+ for type in $(ARGTYPE); \
|
||
+ do \
|
||
+ sed -e "s/ARGTYPE/$$type/g" < $< >> $@; \
|
||
+ done
|
||
+
|
||
+fulleq--unpackaged--2.0.sql: fulleq-unpackaged.sql.in
|
||
+ echo '\echo Use "CREATE EXTENSION fulleq FROM unpackaged" to load this file. \quit' > $@
|
||
+ echo 'DROP OPERATOR CLASS IF EXISTS int2vector_fill_ops USING hash;' >> $@
|
||
+ echo 'DROP OPERATOR FAMILY IF EXISTS int2vector_fill_ops USING hash;' >> $@
|
||
+ echo 'DROP FUNCTION IF EXISTS fullhash_int2vector(int2vector);' >> $@
|
||
+ echo 'DROP OPERATOR IF EXISTS == (int2vector, int2vector);' >> $@
|
||
+ echo 'DROP FUNCTION IF EXISTS isfulleq_int2vector(int2vector, int2vector);' >> $@
|
||
+ for type in $(ARGTYPE); \
|
||
+ do \
|
||
+ sed -e "s/ARGTYPE/$$type/g" < $< >> $@; \
|
||
+ done
|
||
+
|
||
diff --git a/contrib/fulleq/README.fulleq contrib/fulleq/README.fulleq
|
||
new file mode 100644
|
||
index 00000000000..a677c49c41b
|
||
--- /dev/null
|
||
+++ 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 contrib/fulleq/expected/fulleq.out
|
||
new file mode 100644
|
||
index 00000000000..452f8593432
|
||
--- /dev/null
|
||
+++ contrib/fulleq/expected/fulleq.out
|
||
@@ -0,0 +1,61 @@
|
||
+CREATE EXTENSION fulleq;
|
||
+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-unpackaged.sql.in contrib/fulleq/fulleq-unpackaged.sql.in
|
||
new file mode 100644
|
||
index 00000000000..8d759d8221f
|
||
--- /dev/null
|
||
+++ contrib/fulleq/fulleq-unpackaged.sql.in
|
||
@@ -0,0 +1,10 @@
|
||
+-- For ARGTYPE
|
||
+
|
||
+ALTER EXTENSION fulleq ADD FUNCTION isfulleq_ARGTYPE(ARGTYPE, ARGTYPE);
|
||
+
|
||
+ALTER EXTENSION fulleq ADD FUNCTION fullhash_ARGTYPE(ARGTYPE);
|
||
+
|
||
+ALTER EXTENSION fulleq ADD OPERATOR == (ARGTYPE, ARGTYPE);
|
||
+
|
||
+ALTER EXTENSION fulleq ADD OPERATOR CLASS ARGTYPE_fill_ops USING hash;
|
||
+
|
||
diff --git a/contrib/fulleq/fulleq.c contrib/fulleq/fulleq.c
|
||
new file mode 100644
|
||
index 00000000000..689a1ff33cf
|
||
--- /dev/null
|
||
+++ contrib/fulleq/fulleq.c
|
||
@@ -0,0 +1,102 @@
|
||
+#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) \
|
||
+ ) ); \
|
||
+}
|
||
+
|
||
+
|
||
+static Datum
|
||
+hashint2vector(PG_FUNCTION_ARGS)
|
||
+{
|
||
+ int2vector *key = (int2vector *) PG_GETARG_POINTER(0);
|
||
+
|
||
+ return hash_any((unsigned char *) key->values, key->dim1 * sizeof(int16));
|
||
+}
|
||
+
|
||
+/*
|
||
+ * We don't have a complete set of int2vector support routines,
|
||
+ * but we need int2vectoreq for catcache indexing.
|
||
+ */
|
||
+static Datum
|
||
+int2vectoreq(PG_FUNCTION_ARGS)
|
||
+{
|
||
+ int2vector *a = (int2vector *) PG_GETARG_POINTER(0);
|
||
+ int2vector *b = (int2vector *) PG_GETARG_POINTER(1);
|
||
+
|
||
+ if (a->dim1 != b->dim1)
|
||
+ PG_RETURN_BOOL(false);
|
||
+ PG_RETURN_BOOL(memcmp(a->values, b->values, a->dim1 * sizeof(int16)) == 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( 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 );
|
||
+
|
||
+/*
|
||
+ * v10 drop * support for int2vector equality and hash operator in commit
|
||
+ * 5c80642aa8de8393b08cd3cbf612b325cedd98dc, but for compatibility
|
||
+ * we still add this operators
|
||
+ */
|
||
+FULLEQ_FUNC( int2vector , int2vectoreq , hashint2vector );
|
||
diff --git a/contrib/fulleq/fulleq.control contrib/fulleq/fulleq.control
|
||
new file mode 100644
|
||
index 00000000000..c827b9fb4f7
|
||
--- /dev/null
|
||
+++ contrib/fulleq/fulleq.control
|
||
@@ -0,0 +1,5 @@
|
||
+comment = 'exact equal operation'
|
||
+default_version = '2.0'
|
||
+module_pathname = '$libdir/fulleq'
|
||
+relocatable = true
|
||
+
|
||
diff --git a/contrib/fulleq/fulleq.sql.in contrib/fulleq/fulleq.sql.in
|
||
new file mode 100644
|
||
index 00000000000..55e980e1235
|
||
--- /dev/null
|
||
+++ contrib/fulleq/fulleq.sql.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 contrib/fulleq/sql/fulleq.sql
|
||
new file mode 100644
|
||
index 00000000000..e491e276486
|
||
--- /dev/null
|
||
+++ contrib/fulleq/sql/fulleq.sql
|
||
@@ -0,0 +1,14 @@
|
||
+CREATE EXTENSION fulleq;
|
||
+
|
||
+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 contrib/mchar/Changes
|
||
new file mode 100644
|
||
index 00000000000..b7f6e0c5718
|
||
--- /dev/null
|
||
+++ contrib/mchar/Changes
|
||
@@ -0,0 +1,20 @@
|
||
+2.0 make an extension
|
||
+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 contrib/mchar/Makefile
|
||
new file mode 100644
|
||
index 00000000000..f1b58b64c73
|
||
--- /dev/null
|
||
+++ contrib/mchar/Makefile
|
||
@@ -0,0 +1,28 @@
|
||
+MODULE_big = mchar
|
||
+OBJS = mchar_io.o mchar_proc.o mchar_op.o mchar_recode.o \
|
||
+ mchar_like.o
|
||
+EXTENSION=mchar
|
||
+DATA = mchar--2.0.sql mchar--unpackaged--2.0.sql
|
||
+DOCS = README.mchar
|
||
+REGRESS = init mchar mvarchar mm like compat
|
||
+ENCODING = UTF8
|
||
+
|
||
+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 contrib/mchar/README.mchar
|
||
new file mode 100644
|
||
index 00000000000..479a7d1f40a
|
||
--- /dev/null
|
||
+++ 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 contrib/mchar/expected/compat.out
|
||
new file mode 100644
|
||
index 00000000000..480a286e8f6
|
||
--- /dev/null
|
||
+++ 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 contrib/mchar/expected/init.out
|
||
new file mode 100644
|
||
index 00000000000..7bae978ec35
|
||
--- /dev/null
|
||
+++ contrib/mchar/expected/init.out
|
||
@@ -0,0 +1,18 @@
|
||
+CREATE EXTENSION mchar;
|
||
+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 contrib/mchar/expected/like.out
|
||
new file mode 100644
|
||
index 00000000000..3a57082e45a
|
||
--- /dev/null
|
||
+++ 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 contrib/mchar/expected/mchar.out
|
||
new file mode 100644
|
||
index 00000000000..f6c592fd16f
|
||
--- /dev/null
|
||
+++ contrib/mchar/expected/mchar.out
|
||
@@ -0,0 +1,382 @@
|
||
+-- 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)
|
||
+
|
||
+--Note: here we use different space symbols, be carefull to copy it!
|
||
+select v, count(*) from
|
||
+(values (1, '4 242'::mchar), (2, '4 242'), (3, 'aSDF'), (4, 'asdf')) as t(i,v) group by v;
|
||
+ v | count
|
||
+-------+-------
|
||
+ aSDF | 2
|
||
+ 4 242 | 2
|
||
+(2 rows)
|
||
+
|
||
+set enable_hashagg=off;
|
||
+select v, count(*) from
|
||
+(values (1, '4 242'::mchar), (2, '4 242'), (3, 'aSDF'), (4, 'asdf')) as t(i,v) group by v;
|
||
+ v | count
|
||
+-------+-------
|
||
+ 4 242 | 2
|
||
+ aSDF | 2
|
||
+(2 rows)
|
||
+
|
||
+reset enable_hashagg;
|
||
diff --git a/contrib/mchar/expected/mm.out contrib/mchar/expected/mm.out
|
||
new file mode 100644
|
||
index 00000000000..c5b36c21611
|
||
--- /dev/null
|
||
+++ contrib/mchar/expected/mm.out
|
||
@@ -0,0 +1,855 @@
|
||
+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;
|
||
+select * from (values
|
||
+ ('е'::mchar),('ё'),('еа'),('еб'),('ее'),('еж'),('ёа'),('ёб'),('ёё'),('ёж'),('ёе'),('её'))
|
||
+ z order by 1;
|
||
+ column1
|
||
+---------
|
||
+ е
|
||
+ ё
|
||
+ еа
|
||
+ ёа
|
||
+ еб
|
||
+ ёб
|
||
+ ее
|
||
+ её
|
||
+ ёе
|
||
+ ёё
|
||
+ еж
|
||
+ ёж
|
||
+(12 rows)
|
||
+
|
||
+select 'ё'::mchar = 'е';
|
||
+ ?column?
|
||
+----------
|
||
+ f
|
||
+(1 row)
|
||
+
|
||
+select 'Ё'::mchar = 'Е';
|
||
+ ?column?
|
||
+----------
|
||
+ f
|
||
+(1 row)
|
||
+
|
||
+select 'й'::mchar = 'и';
|
||
+ ?column?
|
||
+----------
|
||
+ f
|
||
+(1 row)
|
||
+
|
||
+select 'Й'::mchar = 'И';
|
||
+ ?column?
|
||
+----------
|
||
+ f
|
||
+(1 row)
|
||
+
|
||
+select mvarchar_icase_cmp('ёа','еб'), mvarchar_icase_cmp('еб','ё'),
|
||
+ mvarchar_icase_cmp('ё', 'ёа');
|
||
+ mvarchar_icase_cmp | mvarchar_icase_cmp | mvarchar_icase_cmp
|
||
+--------------------+--------------------+--------------------
|
||
+ -1 | 1 | -1
|
||
+(1 row)
|
||
+
|
||
diff --git a/contrib/mchar/expected/mvarchar.out contrib/mchar/expected/mvarchar.out
|
||
new file mode 100644
|
||
index 00000000000..5c866b43e71
|
||
--- /dev/null
|
||
+++ 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--2.0.sql contrib/mchar/mchar--2.0.sql
|
||
new file mode 100644
|
||
index 00000000000..2a49a13abf6
|
||
--- /dev/null
|
||
+++ contrib/mchar/mchar--2.0.sql
|
||
@@ -0,0 +1,1324 @@
|
||
+\echo Use "CREATE EXTENSION mchar" to load this file. \quit
|
||
+
|
||
+-- 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);
|
||
+
|
||
+
|
||
diff --git a/contrib/mchar/mchar--unpackaged--2.0.sql contrib/mchar/mchar--unpackaged--2.0.sql
|
||
new file mode 100644
|
||
index 00000000000..1acc4ccec1e
|
||
--- /dev/null
|
||
+++ contrib/mchar/mchar--unpackaged--2.0.sql
|
||
@@ -0,0 +1,404 @@
|
||
+\echo Use "CREATE EXTENSION mchar FROM unpackaged" to load this file. \quit
|
||
+
|
||
+-- I/O functions
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mchartypmod_in(cstring[]);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mchartypmod_out(int4);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mchar_in(cstring);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mchar_out(mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mchar_send(mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mchar_recv(internal);
|
||
+
|
||
+ALTER EXTENSION mchar ADD TYPE mchar;
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mchar(mchar, integer, boolean);
|
||
+
|
||
+ALTER EXTENSION mchar ADD CAST (mchar as mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mvarchar_in(cstring);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mvarchar_out(mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mvarchar_send(mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mvarchar_recv(internal);
|
||
+
|
||
+ALTER EXTENSION mchar ADD TYPE mvarchar;
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mvarchar(mvarchar, integer, boolean);
|
||
+
|
||
+ALTER EXTENSION mchar ADD CAST (mvarchar as mvarchar);
|
||
+
|
||
+--Operations and functions
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION length(mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION upper(mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION lower(mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mchar_hash(mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mchar_concat(mchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR || (mchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mchar_like(mchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mchar_notlike(mchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR ~~ (mchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR !~~ (mchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mchar_regexeq(mchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mchar_regexne(mchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR ~ (mchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR !~ (mchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION similar_escape(mchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION length(mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION upper(mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION lower(mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mvarchar_hash(mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mvarchar_concat(mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR || (mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mvarchar_like(mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION like_escape(mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mvarchar_notlike(mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR ~~ (mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR !~~ (mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mvarchar_regexeq(mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mvarchar_regexne(mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR ~ (mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR !~ (mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION similar_escape(mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION substr (mchar, int4);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION substr (mchar, int4, int4);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION substr (mvarchar, int4);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION substr (mvarchar, int4, int4);
|
||
+
|
||
+-- Comparing
|
||
+-- MCHAR
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mchar_icase_cmp(mchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mchar_icase_eq(mchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mchar_icase_ne(mchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mchar_icase_lt(mchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mchar_icase_le(mchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mchar_icase_gt(mchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mchar_icase_ge(mchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR < (mchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR > (mchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR <= (mchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR >= (mchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR = (mchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR <> (mchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mchar_case_cmp(mchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mchar_case_eq(mchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mchar_case_ne(mchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mchar_case_lt(mchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mchar_case_le(mchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mchar_case_gt(mchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mchar_case_ge(mchar, mchar);
|
||
+
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR &< (mchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR &> (mchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR &<= (mchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR &>= (mchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR &= (mchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR &<> (mchar, mchar);
|
||
+
|
||
+--MVARCHAR
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mvarchar_icase_cmp(mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mvarchar_icase_eq(mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mvarchar_icase_ne(mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mvarchar_icase_lt(mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mvarchar_icase_le(mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mvarchar_icase_gt(mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mvarchar_icase_ge(mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR < (mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR > (mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR <= (mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR >= (mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR = (mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR <> (mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mvarchar_case_cmp(mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mvarchar_case_eq(mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mvarchar_case_ne(mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mvarchar_case_lt(mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mvarchar_case_le(mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mvarchar_case_gt(mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mvarchar_case_ge(mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR &< (mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR &> (mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR &<= (mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR &>= (mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR &= (mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR &<> (mvarchar, mvarchar);
|
||
+
|
||
+-- MCHAR <> MVARCHAR
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mc_mv_icase_cmp(mchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mc_mv_icase_eq(mchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mc_mv_icase_ne(mchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mc_mv_icase_lt(mchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mc_mv_icase_le(mchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mc_mv_icase_gt(mchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mc_mv_icase_ge(mchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR < (mchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR > (mchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR <= (mchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR >= (mchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR = (mchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR <> (mchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mc_mv_case_cmp(mchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mc_mv_case_eq(mchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mc_mv_case_ne(mchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mc_mv_case_lt(mchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mc_mv_case_le(mchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mc_mv_case_gt(mchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mc_mv_case_ge(mchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR &< (mchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR &> (mchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR &<= (mchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR &>= (mchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR &= (mchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR &<> (mchar, mvarchar);
|
||
+
|
||
+-- MVARCHAR <> MCHAR
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mv_mc_icase_cmp(mvarchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mv_mc_icase_eq(mvarchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mv_mc_icase_ne(mvarchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mv_mc_icase_lt(mvarchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mv_mc_icase_le(mvarchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mv_mc_icase_gt(mvarchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mv_mc_icase_ge(mvarchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR < (mvarchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR > (mvarchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR <= (mvarchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR >= (mvarchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR = (mvarchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR <> (mvarchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mv_mc_case_cmp(mvarchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mv_mc_case_eq(mvarchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mv_mc_case_ne(mvarchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mv_mc_case_lt(mvarchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mv_mc_case_le(mvarchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mv_mc_case_gt(mvarchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mv_mc_case_ge(mvarchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR &< (mvarchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR &> (mvarchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR &<= (mvarchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR &>= (mvarchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR &= (mvarchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR &<> (mvarchar, mchar);
|
||
+
|
||
+-- MCHAR - VARCHAR operations
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mchar_mvarchar_concat(mchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR || (mchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mvarchar_mchar_concat(mvarchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR || (mvarchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mvarchar_mchar(mvarchar, integer, boolean);
|
||
+
|
||
+ALTER EXTENSION mchar ADD CAST (mvarchar as mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mchar_mvarchar(mchar, integer, boolean);
|
||
+
|
||
+ALTER EXTENSION mchar ADD CAST (mchar as mvarchar);
|
||
+
|
||
+-- Aggregates
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mchar_larger(mchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD AGGREGATE max (mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mchar_smaller(mchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD AGGREGATE min (mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mvarchar_larger(mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD AGGREGATE max (mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mvarchar_smaller(mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD AGGREGATE min (mvarchar);
|
||
+
|
||
+-- B-tree support
|
||
+ALTER EXTENSION mchar ADD OPERATOR FAMILY icase_ops USING btree;
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR FAMILY case_ops USING btree;
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR CLASS mchar_icase_ops USING btree;
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR CLASS mchar_case_ops USING btree;
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR CLASS mchar_icase_ops USING hash;
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR CLASS mvarchar_icase_ops USING btree;
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR CLASS mvarchar_case_ops USING btree;
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR CLASS mvarchar_icase_ops USING hash;
|
||
+
|
||
+
|
||
+-- Index support for LIKE
|
||
+
|
||
+--mchar_pattern_fixed_prefix could be with wrong number of arguments
|
||
+ALTER EXTENSION mchar ADD FUNCTION mchar_pattern_fixed_prefix;
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION mchar_greaterstring(internal);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION isfulleq_mchar(mchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION fullhash_mchar(mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR == (mchar, mchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR CLASS mchar_fill_ops USING hash;
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION isfulleq_mvarchar(mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD FUNCTION fullhash_mvarchar(mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR == (mvarchar, mvarchar);
|
||
+
|
||
+ALTER EXTENSION mchar ADD OPERATOR CLASS mvarchar_fill_ops USING hash;
|
||
+
|
||
+
|
||
diff --git a/contrib/mchar/mchar.control contrib/mchar/mchar.control
|
||
new file mode 100644
|
||
index 00000000000..1ddda5a5206
|
||
--- /dev/null
|
||
+++ contrib/mchar/mchar.control
|
||
@@ -0,0 +1,6 @@
|
||
+# mchar extension
|
||
+comment = 'SQL Server text type'
|
||
+default_version = '2.0'
|
||
+module_pathname = '$libdir/mchar'
|
||
+relocatable = true
|
||
+
|
||
diff --git a/contrib/mchar/mchar.h contrib/mchar/mchar.h
|
||
new file mode 100644
|
||
index 00000000000..a88a0e1eb76
|
||
--- /dev/null
|
||
+++ contrib/mchar/mchar.h
|
||
@@ -0,0 +1,63 @@
|
||
+#ifndef __MCHAR_H__
|
||
+#define __MCHAR_H__
|
||
+
|
||
+#include "postgres.h"
|
||
+#include "mb/pg_wchar.h"
|
||
+#include "utils/builtins.h"
|
||
+#include "unicode/uchar.h"
|
||
+#include "unicode/ustring.h"
|
||
+
|
||
+typedef struct {
|
||
+ 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 == ' ' */
|
||
+
|
||
+Datum hash_uchar( UChar *s, int len );
|
||
+#endif
|
||
diff --git a/contrib/mchar/mchar_io.c contrib/mchar/mchar_io.c
|
||
new file mode 100644
|
||
index 00000000000..9179412ae5d
|
||
--- /dev/null
|
||
+++ 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 contrib/mchar/mchar_like.c
|
||
new file mode 100644
|
||
index 00000000000..c1a09358f4b
|
||
--- /dev/null
|
||
+++ 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);
|
||
+ int 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);
|
||
+ int result;
|
||
+ bool 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 = DatumGetInt32( DirectFunctionCall2(
|
||
+ mchar_like,
|
||
+ PG_GETARG_DATUM(0),
|
||
+ PG_GETARG_DATUM(1)
|
||
+ )) == LIKE_TRUE;
|
||
+
|
||
+ 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 contrib/mchar/mchar_op.c
|
||
new file mode 100644
|
||
index 00000000000..4694d9cf3c3
|
||
--- /dev/null
|
||
+++ 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 contrib/mchar/mchar_proc.c
|
||
new file mode 100644
|
||
index 00000000000..edabfb5eb66
|
||
--- /dev/null
|
||
+++ contrib/mchar/mchar_proc.c
|
||
@@ -0,0 +1,315 @@
|
||
+#include "mchar.h"
|
||
+#include "mb/pg_wchar.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);
|
||
+}
|
||
+
|
||
+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 contrib/mchar/mchar_recode.c
|
||
new file mode 100644
|
||
index 00000000000..12bc6d4f3aa
|
||
--- /dev/null
|
||
+++ contrib/mchar/mchar_recode.c
|
||
@@ -0,0 +1,166 @@
|
||
+#include "mchar.h"
|
||
+#include "access/hash.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) {
|
||
+
|
||
+ createUObjs();
|
||
+
|
||
+ return (int)ucol_strcoll( colCaseInsensitive,
|
||
+ a, alen,
|
||
+ b, blen);
|
||
+}
|
||
+
|
||
+int
|
||
+UCharCompare(UChar * a, int alen, UChar *b, int blen) {
|
||
+
|
||
+ createUObjs();
|
||
+
|
||
+ return (int)ucol_strcoll( colCaseSensitive,
|
||
+ a, alen,
|
||
+ b, blen);
|
||
+}
|
||
+
|
||
+Datum
|
||
+hash_uchar( UChar *s, int len ) {
|
||
+ int32 length = INT_MAX, i;
|
||
+ Datum res;
|
||
+ uint8 *d;
|
||
+
|
||
+ if ( len == 0 )
|
||
+ return hash_any( NULL, 0 );
|
||
+
|
||
+ createUObjs();
|
||
+
|
||
+ for(i=2;; i*=2)
|
||
+ {
|
||
+ d = palloc(len * i);
|
||
+ length = ucol_getSortKey(colCaseInsensitive, s, len, d, len*i);
|
||
+
|
||
+ if (length == 0)
|
||
+ elog(ERROR,"ICU ucol_getSortKey fails");
|
||
+
|
||
+ if (length < len*i)
|
||
+ break;
|
||
+
|
||
+ pfree(d);
|
||
+ }
|
||
+
|
||
+ res = hash_any( (unsigned char*) d, length);
|
||
+
|
||
+ pfree(d);
|
||
+
|
||
+ return res;
|
||
+}
|
||
+
|
||
diff --git a/contrib/mchar/sql/compat.sql contrib/mchar/sql/compat.sql
|
||
new file mode 100644
|
||
index 00000000000..d5b6a986960
|
||
--- /dev/null
|
||
+++ 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 contrib/mchar/sql/init.sql
|
||
new file mode 100644
|
||
index 00000000000..04310044458
|
||
--- /dev/null
|
||
+++ contrib/mchar/sql/init.sql
|
||
@@ -0,0 +1,23 @@
|
||
+CREATE EXTENSION mchar;
|
||
+
|
||
+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 contrib/mchar/sql/like.sql
|
||
new file mode 100644
|
||
index 00000000000..aebf92446fa
|
||
--- /dev/null
|
||
+++ 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 contrib/mchar/sql/mchar.sql
|
||
new file mode 100644
|
||
index 00000000000..2ec0e659f4f
|
||
--- /dev/null
|
||
+++ contrib/mchar/sql/mchar.sql
|
||
@@ -0,0 +1,90 @@
|
||
+-- 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;
|
||
+
|
||
+
|
||
+--Note: here we use different space symbols, be carefull to copy it!
|
||
+select v, count(*) from
|
||
+(values (1, '4 242'::mchar), (2, '4 242'), (3, 'aSDF'), (4, 'asdf')) as t(i,v) group by v;
|
||
+set enable_hashagg=off;
|
||
+select v, count(*) from
|
||
+(values (1, '4 242'::mchar), (2, '4 242'), (3, 'aSDF'), (4, 'asdf')) as t(i,v) group by v;
|
||
+reset enable_hashagg;
|
||
diff --git a/contrib/mchar/sql/mm.sql contrib/mchar/sql/mm.sql
|
||
new file mode 100644
|
||
index 00000000000..2e11b937040
|
||
--- /dev/null
|
||
+++ contrib/mchar/sql/mm.sql
|
||
@@ -0,0 +1,196 @@
|
||
+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;
|
||
+
|
||
+select * from (values
|
||
+ ('е'::mchar),('ё'),('еа'),('еб'),('ее'),('еж'),('ёа'),('ёб'),('ёё'),('ёж'),('ёе'),('её'))
|
||
+ z order by 1;
|
||
+
|
||
+select 'ё'::mchar = 'е';
|
||
+select 'Ё'::mchar = 'Е';
|
||
+select 'й'::mchar = 'и';
|
||
+select 'Й'::mchar = 'И';
|
||
+
|
||
+select mvarchar_icase_cmp('ёа','еб'), mvarchar_icase_cmp('еб','ё'),
|
||
+ mvarchar_icase_cmp('ё', 'ёа');
|
||
diff --git a/contrib/mchar/sql/mvarchar.sql contrib/mchar/sql/mvarchar.sql
|
||
new file mode 100644
|
||
index 00000000000..91b0981075d
|
||
--- /dev/null
|
||
+++ 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/src/backend/nodes/outfuncs.c src/backend/nodes/outfuncs.c
|
||
index f5d786d79ad..9efe66f7519 100644
|
||
--- a/src/backend/nodes/outfuncs.c
|
||
+++ src/backend/nodes/outfuncs.c
|
||
@@ -1943,6 +1943,7 @@ _outAppendPath(StringInfo str, const AppendPath *node)
|
||
WRITE_NODE_FIELD(partitioned_rels);
|
||
WRITE_NODE_FIELD(subpaths);
|
||
WRITE_INT_FIELD(first_partial_path);
|
||
+ WRITE_BOOL_FIELD(pull_tlist);
|
||
}
|
||
|
||
static void
|
||
diff --git a/src/backend/optimizer/path/Makefile src/backend/optimizer/path/Makefile
|
||
index 6864a621327..ef250e0f248 100644
|
||
--- a/src/backend/optimizer/path/Makefile
|
||
+++ src/backend/optimizer/path/Makefile
|
||
@@ -12,7 +12,7 @@ subdir = src/backend/optimizer/path
|
||
top_builddir = ../../../..
|
||
include $(top_builddir)/src/Makefile.global
|
||
|
||
-OBJS = allpaths.o clausesel.o costsize.o equivclass.o indxpath.o \
|
||
- joinpath.o joinrels.o pathkeys.o tidpath.o
|
||
+OBJS = allpaths.o appendorpath.o clausesel.o costsize.o equivclass.o \
|
||
+ indxpath.o joinpath.o joinrels.o pathkeys.o tidpath.o
|
||
|
||
include $(top_srcdir)/src/backend/common.mk
|
||
diff --git a/src/backend/optimizer/path/allpaths.c src/backend/optimizer/path/allpaths.c
|
||
index 56ccde977cc..1b8bdabd4ac 100644
|
||
--- a/src/backend/optimizer/path/allpaths.c
|
||
+++ src/backend/optimizer/path/allpaths.c
|
||
@@ -729,6 +729,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);
|
||
}
|
||
@@ -1601,7 +1604,8 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
|
||
if (subpaths_valid)
|
||
add_path(rel, (Path *) create_append_path(root, rel, subpaths, NIL,
|
||
NULL, 0, false,
|
||
- partitioned_rels, -1));
|
||
+ partitioned_rels, -1,
|
||
+ false, NIL));
|
||
|
||
/*
|
||
* Consider an append of unordered, unparameterized partial paths. Make
|
||
@@ -1644,7 +1648,8 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
|
||
appendpath = create_append_path(root, rel, NIL, partial_subpaths,
|
||
NULL, parallel_workers,
|
||
enable_parallel_append,
|
||
- partitioned_rels, -1);
|
||
+ partitioned_rels, -1,
|
||
+ false, NIL);
|
||
|
||
/*
|
||
* Make sure any subsequent partial paths use the same row count
|
||
@@ -1693,7 +1698,8 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
|
||
appendpath = create_append_path(root, rel, pa_nonpartial_subpaths,
|
||
pa_partial_subpaths,
|
||
NULL, parallel_workers, true,
|
||
- partitioned_rels, partial_rows);
|
||
+ partitioned_rels, partial_rows,
|
||
+ false, NIL);
|
||
add_partial_path(rel, (Path *) appendpath);
|
||
}
|
||
|
||
@@ -1755,7 +1761,8 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
|
||
add_path(rel, (Path *)
|
||
create_append_path(root, rel, subpaths, NIL,
|
||
required_outer, 0, false,
|
||
- partitioned_rels, -1));
|
||
+ partitioned_rels, -1,
|
||
+ false, NIL));
|
||
}
|
||
}
|
||
|
||
@@ -2024,7 +2031,8 @@ set_dummy_rel_pathlist(RelOptInfo *rel)
|
||
/* Set up the dummy path */
|
||
add_path(rel, (Path *) create_append_path(NULL, rel, NIL, NIL,
|
||
rel->lateral_relids,
|
||
- 0, false, NIL, -1));
|
||
+ 0, false, NIL, -1,
|
||
+ false, NIL));
|
||
|
||
/*
|
||
* We set the cheapest-path fields immediately, just in case they were
|
||
diff --git a/src/backend/optimizer/path/appendorpath.c src/backend/optimizer/path/appendorpath.c
|
||
new file mode 100644
|
||
index 00000000000..3127d7f59bf
|
||
--- /dev/null
|
||
+++ src/backend/optimizer/path/appendorpath.c
|
||
@@ -0,0 +1,983 @@
|
||
+/*
|
||
+ * support Append plan for ORed clauses
|
||
+ * Teodor Sigaev <teodor@sigaev.ru>
|
||
+ */
|
||
+#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/fmgroids.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;
|
||
+
|
||
+
|
||
+/*----------
|
||
+ * 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);
|
||
+
|
||
+ /* 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, false);
|
||
+ }
|
||
+
|
||
+ /*
|
||
+ * 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(root, rel, indexPaths, NIL, NULL, 0,
|
||
+ false, NIL, -1.0, 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(root, rel, indexPaths, NIL, NULL,
|
||
+ 0, false, NIL, -1.0,
|
||
+ true, pathkeys);
|
||
+ add_path(rel, (Path *) appendidxpath);
|
||
+ }
|
||
+ } else {
|
||
+ appendidxpath = create_append_path(root, rel, bestpath->bitmapquals,
|
||
+ NIL, NULL,
|
||
+ 0, false, NIL, -1.0, 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), false ) )
|
||
+ 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 ) == 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, false) )
|
||
+ /*
|
||
+ * a:( Var < 15 ) > b:( Var <= 10 )
|
||
+ */
|
||
+ return 1;
|
||
+ else if ( predicate_implied_by((List*)b->expr, (List*)a->expr, false) )
|
||
+ /*
|
||
+ * 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) == 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;
|
||
+}
|
||
+
|
||
+static IndexOptInfo *sortingIndex = NULL;
|
||
+static bool volatile unableToDefine = false;
|
||
+
|
||
+/*
|
||
+ * 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;
|
||
+
|
||
+ if (res == 0 && ae->strategy == be->strategy &&
|
||
+ be->strategy != BTEqualStrategyNumber &&
|
||
+ equal(ae->expr, be->expr))
|
||
+ {
|
||
+ /*
|
||
+ * It's impossible to get defined order for non-eq the same clauses
|
||
+ */
|
||
+ unableToDefine = true;
|
||
+ PG_RE_THROW(); /* it should be PG_THROW(), but it's the same */
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+/*
|
||
+ * 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/indxpath.c src/backend/optimizer/path/indxpath.c
|
||
index 658700c2a43..d9b9b6ce873 100644
|
||
--- a/src/backend/optimizer/path/indxpath.c
|
||
+++ src/backend/optimizer/path/indxpath.c
|
||
@@ -39,6 +39,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"
|
||
|
||
/* XXX see PartCollMatchesExprColl */
|
||
#define IndexCollMatchesExprColl(idxcollation, exprcollation) \
|
||
@@ -115,8 +123,6 @@ static List *build_index_paths(PlannerInfo *root, RelOptInfo *rel,
|
||
bool *skip_lower_saop);
|
||
static List *build_paths_for_OR(PlannerInfo *root, RelOptInfo *rel,
|
||
List *clauses, List *other_clauses);
|
||
-static List *generate_bitmap_or_paths(PlannerInfo *root, RelOptInfo *rel,
|
||
- List *clauses, List *other_clauses);
|
||
static Path *choose_bitmap_and(PlannerInfo *root, RelOptInfo *rel,
|
||
List *paths);
|
||
static int path_usage_comparator(const void *a, const void *b);
|
||
@@ -1260,7 +1266,7 @@ build_paths_for_OR(PlannerInfo *root, RelOptInfo *rel,
|
||
* for the purpose of generating indexquals, but are not to be searched for
|
||
* ORs. (See build_paths_for_OR() for motivation.)
|
||
*/
|
||
-static List *
|
||
+List *
|
||
generate_bitmap_or_paths(PlannerInfo *root, RelOptInfo *rel,
|
||
List *clauses, List *other_clauses)
|
||
{
|
||
@@ -3298,6 +3304,224 @@ match_index_to_operand(Node *operand,
|
||
return false;
|
||
}
|
||
|
||
+/****************************************************************************
|
||
+ * ---- 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 Oid
|
||
+findTypeOid(char *typname)
|
||
+{
|
||
+ CatCList *catlist;
|
||
+ HeapTuple tup;
|
||
+ int n_members;
|
||
+ Oid typoid;
|
||
+
|
||
+ catlist = SearchSysCacheList(TYPENAMENSP, 1,
|
||
+ CStringGetDatum(typname), 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' type",
|
||
+ n_members, typname);
|
||
+ return InvalidOid;
|
||
+ }
|
||
+
|
||
+ tup = &catlist->members[0]->tuple;
|
||
+
|
||
+ typoid = HeapTupleGetOid(tup);
|
||
+
|
||
+ ReleaseSysCacheList(catlist);
|
||
+
|
||
+ return typoid;
|
||
+}
|
||
+
|
||
+static bool
|
||
+fillMCharOIDS() {
|
||
+ CatCList *catlist;
|
||
+ HeapTuple tup;
|
||
+ char *funcname = "mchar_pattern_fixed_prefix";
|
||
+ int n_members;
|
||
+
|
||
+ catlist = SearchSysCacheList(PROCNAMEARGSNSP, 1,
|
||
+ CStringGetDatum(funcname), 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 ) {
|
||
+ char *quals_funcname = "mchar_greaterstring";
|
||
+ Oid tmp_mmPFPOid = HeapTupleGetOid(tup);
|
||
+
|
||
+ ReleaseSysCacheList(catlist);
|
||
+
|
||
+ mcharOid = findTypeOid("mchar");
|
||
+ mvarcharOid = findTypeOid("mvarchar");
|
||
+
|
||
+ 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);
|
||
+ 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 ----
|
||
****************************************************************************/
|
||
@@ -3490,9 +3714,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
|
||
@@ -3748,6 +3979,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 src/backend/optimizer/path/joinrels.c
|
||
index 7079b6ac3fe..b76e0d8edb0 100644
|
||
--- a/src/backend/optimizer/path/joinrels.c
|
||
+++ src/backend/optimizer/path/joinrels.c
|
||
@@ -1255,7 +1255,8 @@ mark_dummy_rel(RelOptInfo *rel)
|
||
/* Set up the dummy path */
|
||
add_path(rel, (Path *) create_append_path(NULL, rel, NIL, NIL,
|
||
rel->lateral_relids,
|
||
- 0, false, NIL, -1));
|
||
+ 0, false, NIL, -1,
|
||
+ false, NIL));
|
||
|
||
/* Set or update cheapest_total_path and related fields */
|
||
set_cheapest(rel);
|
||
diff --git a/src/backend/optimizer/path/pathkeys.c src/backend/optimizer/path/pathkeys.c
|
||
index 5579c6b0679..4fa9fd2eeab 100644
|
||
--- a/src/backend/optimizer/path/pathkeys.c
|
||
+++ src/backend/optimizer/path/pathkeys.c
|
||
@@ -1625,7 +1625,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 src/backend/optimizer/plan/createplan.c
|
||
index 5f6d2bad7be..b6dd080565c 100644
|
||
--- a/src/backend/optimizer/plan/createplan.c
|
||
+++ src/backend/optimizer/plan/createplan.c
|
||
@@ -154,7 +154,6 @@ static Node *replace_nestloop_params(PlannerInfo *root, Node *expr);
|
||
static Node *replace_nestloop_params_mutator(Node *node, PlannerInfo *root);
|
||
static List *fix_indexqual_references(PlannerInfo *root, IndexPath *index_path);
|
||
static List *fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path);
|
||
-static Node *fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol);
|
||
static List *get_switched_clauses(List *clauses, Relids outerrelids);
|
||
static List *order_qual_clauses(PlannerInfo *root, List *clauses);
|
||
static void copy_generic_path_info(Plan *dest, Path *src);
|
||
@@ -1027,7 +1026,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;
|
||
RelOptInfo *rel = best_path->path.parent;
|
||
@@ -1047,6 +1046,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
|
||
/* Generate a Result plan with constant-FALSE gating qual */
|
||
Plan *plan;
|
||
|
||
+ tlist = build_path_tlist(root, &best_path->path);
|
||
plan = (Plan *) make_result(tlist,
|
||
(Node *) list_make1(makeBoolConst(false,
|
||
false)),
|
||
@@ -1115,6 +1115,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, best_path->first_partial_path,
|
||
tlist, best_path->partitioned_rels,
|
||
partpruneinfo);
|
||
@@ -4534,7 +4539,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/planner.c src/backend/optimizer/plan/planner.c
|
||
index 8e152078c59..6a3f4667e89 100644
|
||
--- a/src/backend/optimizer/plan/planner.c
|
||
+++ src/backend/optimizer/plan/planner.c
|
||
@@ -1590,7 +1590,8 @@ inheritance_planner(PlannerInfo *root)
|
||
|
||
/* Make a dummy path, cf set_dummy_rel_pathlist() */
|
||
dummy_path = (Path *) create_append_path(NULL, final_rel, NIL, NIL,
|
||
- NULL, 0, false, NIL, -1);
|
||
+ NULL, 0, false, NIL, -1,
|
||
+ false, NIL);
|
||
|
||
/* These lists must be nonempty to make a valid ModifyTable node */
|
||
subpaths = list_make1(dummy_path);
|
||
@@ -3958,7 +3959,8 @@ create_degenerate_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel,
|
||
0,
|
||
false,
|
||
NIL,
|
||
- -1);
|
||
+ -1,
|
||
+ false, NIL);
|
||
}
|
||
else
|
||
{
|
||
diff --git a/src/backend/optimizer/plan/setrefs.c src/backend/optimizer/plan/setrefs.c
|
||
index 80e6e0da0d6..ea7b1ca1d7c 100644
|
||
--- a/src/backend/optimizer/plan/setrefs.c
|
||
+++ src/backend/optimizer/plan/setrefs.c
|
||
@@ -2252,6 +2252,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);
|
||
+
|
||
/* Look for the var in the input tlists, first in the outer */
|
||
if (context->outer_itlist)
|
||
{
|
||
@@ -2266,6 +2270,9 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
|
||
/* then in the inner. */
|
||
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/prep/prepunion.c src/backend/optimizer/prep/prepunion.c
|
||
index c8465a14af5..59cce11b7f0 100644
|
||
--- a/src/backend/optimizer/prep/prepunion.c
|
||
+++ src/backend/optimizer/prep/prepunion.c
|
||
@@ -656,7 +656,7 @@ generate_union_paths(SetOperationStmt *op, PlannerInfo *root,
|
||
* Append the child results together.
|
||
*/
|
||
path = (Path *) create_append_path(root, result_rel, pathlist, NIL,
|
||
- NULL, 0, false, NIL, -1);
|
||
+ NULL, 0, false, NIL, -1, false, NIL);
|
||
|
||
/*
|
||
* For UNION ALL, we just need the Append path. For UNION, need to add
|
||
@@ -712,7 +712,7 @@ generate_union_paths(SetOperationStmt *op, PlannerInfo *root,
|
||
ppath = (Path *)
|
||
create_append_path(root, result_rel, NIL, partial_pathlist,
|
||
NULL, parallel_workers, enable_parallel_append,
|
||
- NIL, -1);
|
||
+ NIL, -1, false, NIL);
|
||
ppath = (Path *)
|
||
create_gather_path(root, result_rel, ppath,
|
||
result_rel->reltarget, NULL, NULL);
|
||
@@ -822,7 +822,7 @@ generate_nonunion_paths(SetOperationStmt *op, PlannerInfo *root,
|
||
* Append the child results together.
|
||
*/
|
||
path = (Path *) create_append_path(root, result_rel, pathlist, NIL,
|
||
- NULL, 0, false, NIL, -1);
|
||
+ NULL, 0, false, NIL, -1, false, NIL);
|
||
|
||
/* Identify the grouping semantics */
|
||
groupList = generate_setop_grouplist(op, tlist);
|
||
diff --git a/src/backend/optimizer/util/pathnode.c src/backend/optimizer/util/pathnode.c
|
||
index 4736d84a837..93e844bf7f7 100644
|
||
--- a/src/backend/optimizer/util/pathnode.c
|
||
+++ src/backend/optimizer/util/pathnode.c
|
||
@@ -1221,7 +1221,8 @@ create_append_path(PlannerInfo *root,
|
||
List *subpaths, List *partial_subpaths,
|
||
Relids required_outer,
|
||
int parallel_workers, bool parallel_aware,
|
||
- List *partitioned_rels, double rows)
|
||
+ List *partitioned_rels, double rows,
|
||
+ bool pull_tlist, List *pathkeys)
|
||
{
|
||
AppendPath *pathnode = makeNode(AppendPath);
|
||
ListCell *l;
|
||
@@ -1253,8 +1254,10 @@ create_append_path(PlannerInfo *root,
|
||
pathnode->path.parallel_aware = parallel_aware;
|
||
pathnode->path.parallel_safe = rel->consider_parallel;
|
||
pathnode->path.parallel_workers = parallel_workers;
|
||
- pathnode->path.pathkeys = NIL; /* result is always considered unsorted */
|
||
+ pathnode->path.pathkeys = pathkeys; /* !=NIL in case of append OR index
|
||
+ scans */
|
||
pathnode->partitioned_rels = list_copy(partitioned_rels);
|
||
+ pathnode->pull_tlist = pull_tlist;
|
||
|
||
/*
|
||
* For parallel append, non-partial paths are sorted by descending total
|
||
@@ -3614,7 +3617,8 @@ reparameterize_path(PlannerInfo *root, Path *path,
|
||
apath->path.parallel_workers,
|
||
apath->path.parallel_aware,
|
||
apath->partitioned_rels,
|
||
- -1);
|
||
+ -1,
|
||
+ apath->pull_tlist, apath->path.pathkeys);
|
||
}
|
||
default:
|
||
break;
|
||
diff --git a/src/backend/parser/gram.y src/backend/parser/gram.y
|
||
index bc65319c2c4..77347e8cb76 100644
|
||
--- a/src/backend/parser/gram.y
|
||
+++ src/backend/parser/gram.y
|
||
@@ -13027,7 +13027,7 @@ a_expr: c_expr { $$ = $1; }
|
||
}
|
||
| a_expr LIKE a_expr ESCAPE a_expr %prec LIKE
|
||
{
|
||
- FuncCall *n = makeFuncCall(SystemFuncName("like_escape"),
|
||
+ FuncCall *n = makeFuncCall(list_make1(makeString("like_escape")),
|
||
list_make2($3, $5),
|
||
@2);
|
||
$$ = (Node *) makeSimpleA_Expr(AEXPR_LIKE, "~~",
|
||
@@ -13040,7 +13040,7 @@ a_expr: c_expr { $$ = $1; }
|
||
}
|
||
| a_expr NOT_LA LIKE a_expr ESCAPE a_expr %prec NOT_LA
|
||
{
|
||
- FuncCall *n = makeFuncCall(SystemFuncName("like_escape"),
|
||
+ FuncCall *n = makeFuncCall(list_make1(makeString("like_escape")),
|
||
list_make2($4, $6),
|
||
@2);
|
||
$$ = (Node *) makeSimpleA_Expr(AEXPR_LIKE, "!~~",
|
||
@@ -13053,7 +13053,7 @@ a_expr: c_expr { $$ = $1; }
|
||
}
|
||
| a_expr ILIKE a_expr ESCAPE a_expr %prec ILIKE
|
||
{
|
||
- FuncCall *n = makeFuncCall(SystemFuncName("like_escape"),
|
||
+ FuncCall *n = makeFuncCall(list_make1(makeString("like_escape")),
|
||
list_make2($3, $5),
|
||
@2);
|
||
$$ = (Node *) makeSimpleA_Expr(AEXPR_ILIKE, "~~*",
|
||
@@ -13066,7 +13066,7 @@ a_expr: c_expr { $$ = $1; }
|
||
}
|
||
| a_expr NOT_LA ILIKE a_expr ESCAPE a_expr %prec NOT_LA
|
||
{
|
||
- FuncCall *n = makeFuncCall(SystemFuncName("like_escape"),
|
||
+ FuncCall *n = makeFuncCall(list_make1(makeString("like_escape")),
|
||
list_make2($4, $6),
|
||
@2);
|
||
$$ = (Node *) makeSimpleA_Expr(AEXPR_ILIKE, "!~~*",
|
||
@@ -13075,7 +13075,7 @@ a_expr: c_expr { $$ = $1; }
|
||
|
||
| a_expr SIMILAR TO a_expr %prec SIMILAR
|
||
{
|
||
- FuncCall *n = makeFuncCall(SystemFuncName("similar_escape"),
|
||
+ FuncCall *n = makeFuncCall(list_make1(makeString("similar_escape")),
|
||
list_make2($4, makeNullAConst(-1)),
|
||
@2);
|
||
$$ = (Node *) makeSimpleA_Expr(AEXPR_SIMILAR, "~",
|
||
@@ -13083,7 +13083,7 @@ a_expr: c_expr { $$ = $1; }
|
||
}
|
||
| a_expr SIMILAR TO a_expr ESCAPE a_expr %prec SIMILAR
|
||
{
|
||
- FuncCall *n = makeFuncCall(SystemFuncName("similar_escape"),
|
||
+ FuncCall *n = makeFuncCall(list_make1(makeString("similar_escape")),
|
||
list_make2($4, $6),
|
||
@2);
|
||
$$ = (Node *) makeSimpleA_Expr(AEXPR_SIMILAR, "~",
|
||
@@ -13091,7 +13091,7 @@ a_expr: c_expr { $$ = $1; }
|
||
}
|
||
| a_expr NOT_LA SIMILAR TO a_expr %prec NOT_LA
|
||
{
|
||
- FuncCall *n = makeFuncCall(SystemFuncName("similar_escape"),
|
||
+ FuncCall *n = makeFuncCall(list_make1(makeString("similar_escape")),
|
||
list_make2($5, makeNullAConst(-1)),
|
||
@2);
|
||
$$ = (Node *) makeSimpleA_Expr(AEXPR_SIMILAR, "!~",
|
||
@@ -13099,7 +13099,7 @@ a_expr: c_expr { $$ = $1; }
|
||
}
|
||
| a_expr NOT_LA SIMILAR TO a_expr ESCAPE a_expr %prec NOT_LA
|
||
{
|
||
- FuncCall *n = makeFuncCall(SystemFuncName("similar_escape"),
|
||
+ FuncCall *n = makeFuncCall(list_make1(makeString("similar_escape")),
|
||
list_make2($5, $7),
|
||
@2);
|
||
$$ = (Node *) makeSimpleA_Expr(AEXPR_SIMILAR, "!~",
|
||
diff --git a/src/include/nodes/relation.h src/include/nodes/relation.h
|
||
index ffeb3eb1fee..20cbb78abde 100644
|
||
--- a/src/include/nodes/relation.h
|
||
+++ src/include/nodes/relation.h
|
||
@@ -1319,6 +1319,11 @@ typedef struct AppendPath
|
||
/* RT indexes of non-leaf tables in a partition tree */
|
||
List *partitioned_rels;
|
||
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 */
|
||
|
||
/* Index of first partial path in subpaths */
|
||
int first_partial_path;
|
||
diff --git a/src/include/optimizer/pathnode.h src/include/optimizer/pathnode.h
|
||
index 7c5ff226501..e1fa74f8b8c 100644
|
||
--- a/src/include/optimizer/pathnode.h
|
||
+++ src/include/optimizer/pathnode.h
|
||
@@ -68,7 +68,8 @@ extern AppendPath *create_append_path(PlannerInfo *root, RelOptInfo *rel,
|
||
List *subpaths, List *partial_subpaths,
|
||
Relids required_outer,
|
||
int parallel_workers, bool parallel_aware,
|
||
- List *partitioned_rels, double rows);
|
||
+ List *partitioned_rels, double rows,
|
||
+ 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 src/include/optimizer/paths.h
|
||
index cafde307adb..4facfc14266 100644
|
||
--- a/src/include/optimizer/paths.h
|
||
+++ src/include/optimizer/paths.h
|
||
@@ -71,6 +71,8 @@ extern void debug_print_rel(PlannerInfo *root, RelOptInfo *rel);
|
||
* routines to generate index paths
|
||
*/
|
||
extern void create_index_paths(PlannerInfo *root, RelOptInfo *rel);
|
||
+extern List *generate_bitmap_or_paths(PlannerInfo *root, RelOptInfo *rel,
|
||
+ List *clauses, List *other_clauses);
|
||
extern bool relation_has_unique_index_for(PlannerInfo *root, RelOptInfo *rel,
|
||
List *restrictlist,
|
||
List *exprlist, List *oprlist);
|
||
@@ -227,6 +229,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 *trim_mergeclauses_for_inner_pathkeys(PlannerInfo *root,
|
||
List *mergeclauses,
|
||
List *pathkeys);
|
||
@@ -234,6 +237,7 @@ extern List *truncate_useless_pathkeys(PlannerInfo *root,
|
||
RelOptInfo *rel,
|
||
List *pathkeys);
|
||
extern bool has_useful_pathkeys(PlannerInfo *root, RelOptInfo *rel);
|
||
+extern void keybased_rewrite_index_paths(PlannerInfo *root, RelOptInfo *rel);
|
||
extern PathKey *make_canonical_pathkey(PlannerInfo *root,
|
||
EquivalenceClass *eclass, Oid opfamily,
|
||
int strategy, bool nulls_first);
|
||
diff --git a/src/include/optimizer/planmain.h src/include/optimizer/planmain.h
|
||
index a081ca689a3..d30ea7dc490 100644
|
||
--- a/src/include/optimizer/planmain.h
|
||
+++ src/include/optimizer/planmain.h
|
||
@@ -59,6 +59,9 @@ extern Plan *materialize_finished_plan(Plan *subplan);
|
||
extern bool is_projection_capable_path(Path *path);
|
||
extern bool is_projection_capable_plan(Plan *plan);
|
||
|
||
+extern Node * fix_indexqual_operand(Node *node, IndexOptInfo *index, int
|
||
+ indexcol);
|
||
+
|
||
/* External use of these functions is deprecated: */
|
||
extern Sort *make_sort_from_sortclauses(List *sortcls, Plan *lefttree);
|
||
extern Agg *make_agg(List *tlist, List *qual,
|
||
diff --git a/src/test/regress/expected/create_index.out src/test/regress/expected/create_index.out
|
||
index be25101db24..6f64f96c167 100644
|
||
--- a/src/test/regress/expected/create_index.out
|
||
+++ src/test/regress/expected/create_index.out
|
||
@@ -2861,18 +2861,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 src/test/regress/expected/select.out
|
||
index 7ad1f0bc773..69f238906ba 100644
|
||
--- a/src/test/regress/expected/select.out
|
||
+++ src/test/regress/expected/select.out
|
||
@@ -517,6 +517,124 @@ TABLE int8_tbl;
|
||
4567890123456789 | -4567890123456789
|
||
(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
|
||
--
|
||
diff --git a/src/test/regress/sql/select.sql src/test/regress/sql/select.sql
|
||
index fdab06d5bc1..787a88cfe4c 100644
|
||
--- a/src/test/regress/sql/select.sql
|
||
+++ src/test/regress/sql/select.sql
|
||
@@ -148,6 +148,33 @@ SELECT 2+2, 57
|
||
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 src/tools/msvc/Mkvcbuild.pm
|
||
index a083ac77f6e..56ad6c9577a 100644
|
||
--- a/src/tools/msvc/Mkvcbuild.pm
|
||
+++ src/tools/msvc/Mkvcbuild.pm
|
||
@@ -35,8 +35,8 @@ my $contrib_defines = { 'refint' => 'REFINT_VERBOSE' };
|
||
my @contrib_uselibpq = ('dblink', 'oid2name', 'postgres_fdw', 'vacuumlo');
|
||
my @contrib_uselibpgport = ('oid2name', 'pg_standby', 'vacuumlo');
|
||
my @contrib_uselibpgcommon = ('oid2name', 'pg_standby', 'vacuumlo');
|
||
-my $contrib_extralibs = undef;
|
||
-my $contrib_extraincludes = { 'dblink' => ['src/backend'] };
|
||
+my $contrib_extralibs = {'mchar' => ['$(ICU46_LIB)\icuin.lib', '$(ICU46_LIB)\icuuc.lib']};
|
||
+my $contrib_extraincludes = { 'dblink' => ['src/backend'], 'mchar' => ['$(ICU46_INCLUDE)'] };
|
||
my $contrib_extrasource = {
|
||
'cube' => [ 'contrib/cube/cubescan.l', 'contrib/cube/cubeparse.y' ],
|
||
'seg' => [ 'contrib/seg/segscan.l', 'contrib/seg/segparse.y' ],
|
||
@@ -65,6 +65,7 @@ my $frontend_extralibs = {
|
||
'initdb' => ['ws2_32.lib'],
|
||
'pg_restore' => ['ws2_32.lib'],
|
||
'pgbench' => ['ws2_32.lib'],
|
||
+ 'mchar' => ['$(ICU46_LIB)\icuin.lib', '$(ICU46_LIB)\icuuc.lib'],
|
||
'psql' => ['ws2_32.lib']
|
||
};
|
||
my $frontend_extraincludes = {
|
||
@@ -462,6 +463,7 @@ sub mkvcbuild
|
||
$pgcrypto->AddLibrary('ws2_32.lib');
|
||
my $mf = Project::read_file('contrib/pgcrypto/Makefile');
|
||
GenerateContribSqlFiles('pgcrypto', $mf);
|
||
+ GenerateFulleqSql();
|
||
|
||
foreach my $subdir ('contrib', 'src/test/modules')
|
||
{
|
||
@@ -1014,6 +1016,59 @@ sub GenerateContribSqlFiles
|
||
return;
|
||
}
|
||
|
||
+sub GenerateFulleqSql
|
||
+{
|
||
+ my @argtypes = ('bool', 'bytea', 'char', 'name', 'int8', 'int2', 'int4',
|
||
+ 'text', 'oid', 'xid', 'cid', 'oidvector', 'float4',
|
||
+ 'float8', 'abstime', 'reltime', 'macaddr', 'inet', 'cidr',
|
||
+ 'varchar', 'date', 'time', 'timestamp', 'timestamptz',
|
||
+ 'interval', 'timetz');
|
||
+
|
||
+ #form extension script
|
||
+ my $i;
|
||
+ open($i, '<', "contrib/fulleq/fulleq.sql.in") ||
|
||
+ croak "Could not read contrib/fulleq/fulleq.sql.in";
|
||
+ my $template = do { local $/; <$i> };
|
||
+ close($i);
|
||
+
|
||
+ my $o;
|
||
+ open($o, '>', "contrib/fulleq/fulleq--2.0.sql") ||
|
||
+ croak "Could not write to contrib/fulleq/fulleq--2.0.sql";
|
||
+ print $o "\\echo Use \"CREATE EXTENSION fulleq\" to load this file. \\quit\n";
|
||
+ foreach my $argtype (@argtypes)
|
||
+ {
|
||
+ my $newtype = $template;
|
||
+ $newtype =~ s/ARGTYPE/$argtype/g;
|
||
+ print $o $newtype;
|
||
+ }
|
||
+ close($o);
|
||
+
|
||
+ #form migration script
|
||
+ $template = undef;
|
||
+ open($i, '<', "contrib/fulleq/fulleq-unpackaged.sql.in") ||
|
||
+ croak "Could not read contrib/fulleq/fulleq-unpackaged.sql.in";
|
||
+ $template = do { local $/; <$i> };
|
||
+ close($i);
|
||
+
|
||
+ open($o, '>', "contrib/fulleq/fulleq--unpackaged--2.0.sql") ||
|
||
+ croak "Could not write to contrib/fulleq/fulleq--2.0.sql";
|
||
+
|
||
+ print $o "\\echo Use \"CREATE EXTENSION fulleq FROM unpackaged\" to load this file. \\quit\n";
|
||
+ print $o "DROP OPERATOR CLASS IF EXISTS int2vector_fill_ops USING hash;\n";
|
||
+ print $o "DROP OPERATOR FAMILY IF EXISTS int2vector_fill_ops USING hash;\n";
|
||
+ print $o "DROP FUNCTION IF EXISTS fullhash_int2vector(int2vector);\n";
|
||
+ print $o "DROP OPERATOR IF EXISTS == (int2vector, int2vector);\n";
|
||
+ print $o "DROP FUNCTION IF EXISTS isfulleq_int2vector(int2vector, int2vector);\n";
|
||
+
|
||
+ foreach my $argtype (@argtypes)
|
||
+ {
|
||
+ my $newtype = $template;
|
||
+ $newtype =~ s/ARGTYPE/$argtype/g;
|
||
+ print $o $newtype;
|
||
+ }
|
||
+ close($o);
|
||
+}
|
||
+
|
||
sub AdjustContribProj
|
||
{
|
||
my $proj = shift;
|