diff --git a/contrib/shark/Makefile b/contrib/shark/Makefile index 120f922bc45ffb4090058dc06ad6305d30887e72..aeed2833ea98df780252732ce1522af0ff71785e 100644 --- a/contrib/shark/Makefile +++ b/contrib/shark/Makefile @@ -9,6 +9,7 @@ OBJS = shark.o OBJS += $(BEPARSERDIR)/parser.o OBJS += $(BEPARSERDIR)/gram-backend.o OBJS += $(BEPARSERDIR)/keywords.o +OBJS += $(BEPARSERDIR)/varbinary.o OBJS += $(PLDIR)/pl_gram.o $(PLDIR)/pl_handler.o $(PLDIR)/pl_comp.o OBJS += $(PLDIR)/pl_scanner.o @@ -62,7 +63,7 @@ $(BEPARSERDIR)/scan-backend.l: $(top_srcdir)/src/common/backend/parser/scan.l $( $(PERL) $(BEPARSERDIR)/include.pl $(BEPARSERDIR) scan.l < $< > $@ # Force these dependencies to be known even without dependency info built: -$(BEPARSERDIR)/gram-backend.o $(BEPARSERDIR)/keywords.o $(BEPARSERDIR)/parser.o: $(BEPARSERDIR)/gram-backend.hpp $(BEPARSERDIR)/kwlist_d.h +$(BEPARSERDIR)/gram-backend.o $(BEPARSERDIR)/keywords.o $(BEPARSERDIR)/varbinary.o $(BEPARSERDIR)/parser.o: $(BEPARSERDIR)/gram-backend.hpp $(BEPARSERDIR)/kwlist_d.h # where to find gen_keywordlist.pl and subsidiary files TOOLSDIR = $(top_srcdir)/src/tools diff --git a/contrib/shark/expected/varbinary.out b/contrib/shark/expected/varbinary.out new file mode 100644 index 0000000000000000000000000000000000000000..76a45d06a9b1b7ff1377fc9c12ca59736e78a238 --- /dev/null +++ b/contrib/shark/expected/varbinary.out @@ -0,0 +1,437 @@ +CREATE DATABASE test_varbinary DBCOMPATIBILITY 'D'; +\c test_varbinary +CREATE EXTENSION shark; +CREATE TABLE t1 (id int, a VARBINARY(1)); +CREATE TABLE t2 (id int, a VARBINARY(2)); +CREATE TABLE t3 (id int, a VARBINARY(MAX)); +CREATE TABLE t4 (id int, a VARBINARY(1.1)); +ERROR: invalid input syntax for integer: "1.1" +LINE 1: CREATE TABLE t4 (id int, a VARBINARY(1.1)); + ^ +CREATE TABLE t5 (id int, a VARBINARY(-1)); +ERROR: length for type varbinary must be at least 1 +LINE 1: CREATE TABLE t5 (id int, a VARBINARY(-1)); + ^ +CREATE TABLE t6 (id int, a VARBINARY(0)); +ERROR: length for type varbinary must be at least 1 +LINE 1: CREATE TABLE t6 (id int, a VARBINARY(0)); + ^ +CREATE INDEX idx1 ON t1(a); +CREATE INDEX idx2 ON t2(a); +CREATE INDEX idx3 ON t3(a); +-- test t1 +INSERT INTO t1 VALUES (1, 12); +SELECT id, a FROM t1 WHERE id = 1; + id | a +----+------ + 1 | 0x0c +(1 row) + +SELECT id, a::int FROM t1 WHERE a::int = 12; + id | a +----+---- + 1 | 12 +(1 row) + +INSERT INTO t1 VALUES (2, 123456); +SELECT id, a FROM t1 WHERE id = 2; + id | a +----+------ + 2 | 0x40 +(1 row) + +SELECT id, a::int FROM t1 WHERE id = 2; + id | a +----+---- + 2 | 64 +(1 row) + +INSERT INTO t1 VALUES (3, '0x12'); +ERROR: String or binary data would be truncated. +The statement has been terminated. +CONTEXT: referenced column: a +INSERT INTO t1 VALUES (4, '\x12'); +ERROR: String or binary data would be truncated. +The statement has been terminated. +CONTEXT: referenced column: a +INSERT INTO t1 VALUES (5, '\xvv'); +ERROR: String or binary data would be truncated. +The statement has been terminated. +CONTEXT: referenced column: a +INSERT INTO t1 VALUES (6, '12'); +ERROR: String or binary data would be truncated. +The statement has been terminated. +CONTEXT: referenced column: a +INSERT INTO t1 VALUES (7, '123456'); +ERROR: String or binary data would be truncated. +The statement has been terminated. +CONTEXT: referenced column: a +INSERT INTO t1 VALUES (8, '123456fff'); +ERROR: String or binary data would be truncated. +The statement has been terminated. +CONTEXT: referenced column: a +INSERT INTO t1 VALUES (9, '0x123456'); +ERROR: String or binary data would be truncated. +The statement has been terminated. +CONTEXT: referenced column: a +INSERT INTO t1 VALUES (10, '\x123456'); +ERROR: String or binary data would be truncated. +The statement has been terminated. +CONTEXT: referenced column: a +INSERT INTO t1 VALUES (11, '\x123456vvv'); +ERROR: String or binary data would be truncated. +The statement has been terminated. +CONTEXT: referenced column: a +INSERT INTO t1 VALUES (12, 1::int); +SELECT id, a FROM t1 WHERE id = 12; + id | a +----+------ + 12 | 0x01 +(1 row) + +SELECT id, a::int FROM t1 WHERE id = 12; + id | a +----+--- + 12 | 1 +(1 row) + +INSERT INTO t1 VALUES (15, NULL); +SELECT id, a FROM t1 WHERE id = 15; + id | a +----+--- + 15 | +(1 row) + +INSERT INTO t1 VALUES (16, '111'::text); +ERROR: column "a" is of type varbinary but expression is of type text +LINE 1: INSERT INTO t1 VALUES (16, '111'::text); + ^ +HINT: You will need to rewrite or cast the expression. +CONTEXT: referenced column: a +INSERT INTO t1 VALUES (17, '111'::varchar); +ERROR: Implicit conversion from data type varchar to varbinary is not allowed. Use the CONVERT function to run this query. +CONTEXT: referenced column: a +INSERT INTO t1 VALUES (17, '111'::varchar::varbinary); +ERROR: String or binary data would be truncated. +The statement has been terminated. +CONTEXT: referenced column: a +INSERT INTO t1 VALUES (18, '111'::nvarchar); +ERROR: column "a" is of type varbinary but expression is of type nvarchar2 +LINE 1: INSERT INTO t1 VALUES (18, '111'::nvarchar); + ^ +HINT: You will need to rewrite or cast the expression. +CONTEXT: referenced column: a +INSERT INTO t1 VALUES (18, '111'::nvarchar::varbinary); +ERROR: String or binary data would be truncated. +The statement has been terminated. +CONTEXT: referenced column: a +-- test t2 +INSERT INTO t2 VALUES (1, 12); +SELECT id, a FROM t2 WHERE id = 1; + id | a +----+-------- + 1 | 0x000c +(1 row) + +SELECT id, a::int FROM t2 WHERE a::int = 12; + id | a +----+---- + 1 | 12 +(1 row) + +INSERT INTO t2 VALUES (2, 123456); +SELECT id, a FROM t2 WHERE id = 2; + id | a +----+-------- + 2 | 0xe240 +(1 row) + +SELECT id, a::int FROM t2 WHERE id = 2; + id | a +----+------- + 2 | 57920 +(1 row) + +INSERT INTO t2 VALUES (3, '0x12'); +ERROR: String or binary data would be truncated. +The statement has been terminated. +CONTEXT: referenced column: a +INSERT INTO t2 VALUES (4, '\x12'); +ERROR: String or binary data would be truncated. +The statement has been terminated. +CONTEXT: referenced column: a +INSERT INTO t2 VALUES (5, '\xvv'); +ERROR: String or binary data would be truncated. +The statement has been terminated. +CONTEXT: referenced column: a +INSERT INTO t2 VALUES (6, '12'); +INSERT INTO t2 VALUES (7, '123456'); +ERROR: String or binary data would be truncated. +The statement has been terminated. +CONTEXT: referenced column: a +INSERT INTO t2 VALUES (8, '123456fff'); +ERROR: String or binary data would be truncated. +The statement has been terminated. +CONTEXT: referenced column: a +INSERT INTO t2 VALUES (9, '0x123456'); +ERROR: String or binary data would be truncated. +The statement has been terminated. +CONTEXT: referenced column: a +INSERT INTO t2 VALUES (10, '\x123456'); +ERROR: String or binary data would be truncated. +The statement has been terminated. +CONTEXT: referenced column: a +INSERT INTO t2 VALUES (11, '\x123456vvv'); +ERROR: String or binary data would be truncated. +The statement has been terminated. +CONTEXT: referenced column: a +INSERT INTO t2 VALUES (12, 1::int); +SELECT id, a FROM t2 WHERE id = 12; + id | a +----+-------- + 12 | 0x0001 +(1 row) + +SELECT id, a::int FROM t2 WHERE id = 12; + id | a +----+--- + 12 | 1 +(1 row) + +INSERT INTO t2 VALUES (15, NULL); +SELECT id, a FROM t2 WHERE id = 15; + id | a +----+--- + 15 | +(1 row) + +INSERT INTO t2 VALUES (16, '111'::text); +ERROR: column "a" is of type varbinary but expression is of type text +LINE 1: INSERT INTO t2 VALUES (16, '111'::text); + ^ +HINT: You will need to rewrite or cast the expression. +CONTEXT: referenced column: a +INSERT INTO t2 VALUES (17, '111'::varchar); +ERROR: Implicit conversion from data type varchar to varbinary is not allowed. Use the CONVERT function to run this query. +CONTEXT: referenced column: a +INSERT INTO t2 VALUES (17, '111'::varchar::varbinary); +ERROR: String or binary data would be truncated. +The statement has been terminated. +CONTEXT: referenced column: a +INSERT INTO t2 VALUES (18, '111'::nvarchar); +ERROR: column "a" is of type varbinary but expression is of type nvarchar2 +LINE 1: INSERT INTO t2 VALUES (18, '111'::nvarchar); + ^ +HINT: You will need to rewrite or cast the expression. +CONTEXT: referenced column: a +INSERT INTO t2 VALUES (18, '111'::nvarchar::varbinary); +ERROR: String or binary data would be truncated. +The statement has been terminated. +CONTEXT: referenced column: a +-- test t3 max +INSERT INTO t3 VALUES (1, 12); +SELECT id, a FROM t3 WHERE id = 1; + id | a +----+------------ + 1 | 0x0000000c +(1 row) + +SELECT id, a::int FROM t3 WHERE a::int = 12; + id | a +----+---- + 1 | 12 +(1 row) + +INSERT INTO t3 VALUES (2, 123456); +SELECT id, a FROM t3 WHERE id = 2; + id | a +----+------------ + 2 | 0x0001e240 +(1 row) + +SELECT id, a::int FROM t3 WHERE id = 2; + id | a +----+-------- + 2 | 123456 +(1 row) + +INSERT INTO t3 VALUES (3, '0x12'); +SELECT id, a FROM t3 WHERE id = 3; + id | a +----+------------ + 3 | 0x30783132 +(1 row) + +SELECT id, a::varchar FROM t3 WHERE id = 3; + id | a +----+------ + 3 | 0x12 +(1 row) + +INSERT INTO t3 VALUES (4, '\x12'); +SELECT id, a FROM t3 WHERE id = 4; + id | a +----+------------ + 4 | 0x5c783132 +(1 row) + +SELECT id, a::varchar FROM t3 WHERE id = 4; + id | a +----+------ + 4 | \x12 +(1 row) + +INSERT INTO t3 VALUES (5, '\xvv'); +SELECT id, a FROM t3 WHERE id = 5; + id | a +----+------------ + 5 | 0x5c787676 +(1 row) + +SELECT id, a::varchar FROM t3 WHERE id = 5; + id | a +----+------ + 5 | \xvv +(1 row) + +INSERT INTO t3 VALUES (6, '12'); +SELECT id, a FROM t3 WHERE id = 6; + id | a +----+-------- + 6 | 0x3132 +(1 row) + +SELECT id, a::varchar FROM t3 WHERE id = 6; + id | a +----+---- + 6 | 12 +(1 row) + +INSERT INTO t3 VALUES (7, '123456'); +SELECT id, a FROM t3 WHERE id = 7; + id | a +----+---------------- + 7 | 0x313233343536 +(1 row) + +SELECT id, a::varchar FROM t3 WHERE id = 7; + id | a +----+-------- + 7 | 123456 +(1 row) + +INSERT INTO t3 VALUES (8, '123456fff'); +SELECT id, a FROM t3 WHERE id = 8; + id | a +----+---------------------- + 8 | 0x313233343536666666 +(1 row) + +SELECT id, a::varchar FROM t3 WHERE id = 8; + id | a +----+----------- + 8 | 123456fff +(1 row) + +INSERT INTO t3 VALUES (9, '0x123456'); +SELECT id, a FROM t3 WHERE id = 9; + id | a +----+-------------------- + 9 | 0x3078313233343536 +(1 row) + +SELECT id, a::varchar FROM t3 WHERE id = 9; + id | a +----+---------- + 9 | 0x123456 +(1 row) + +INSERT INTO t3 VALUES (10, '\x123456'); +SELECT id, a FROM t3 WHERE id = 10; + id | a +----+-------------------- + 10 | 0x5c78313233343536 +(1 row) + +SELECT id, a::varchar FROM t3 WHERE id = 10; + id | a +----+---------- + 10 | \x123456 +(1 row) + +INSERT INTO t3 VALUES (11, '\x123456vvv'); +SELECT id, a FROM t3 WHERE id = 11; + id | a +----+-------------------------- + 11 | 0x5c78313233343536767676 +(1 row) + +SELECT id, a::varchar FROM t3 WHERE id = 11; + id | a +----+------------- + 11 | \x123456vvv +(1 row) + +INSERT INTO t3 VALUES (12, 1::int); +SELECT id, a FROM t3 WHERE id = 12; + id | a +----+------------ + 12 | 0x00000001 +(1 row) + +SELECT id, a::int FROM t3 WHERE id = 12; + id | a +----+--- + 12 | 1 +(1 row) + +INSERT INTO t3 VALUES (15, NULL); +SELECT id, a FROM t3 WHERE id = 15; + id | a +----+--- + 15 | +(1 row) + +INSERT INTO t3 VALUES (16, '111'::text); +ERROR: column "a" is of type varbinary but expression is of type text +LINE 1: INSERT INTO t3 VALUES (16, '111'::text); + ^ +HINT: You will need to rewrite or cast the expression. +CONTEXT: referenced column: a +INSERT INTO t3 VALUES (17, '111'::varchar); +ERROR: Implicit conversion from data type varchar to varbinary is not allowed. Use the CONVERT function to run this query. +CONTEXT: referenced column: a +INSERT INTO t3 VALUES (17, '111'::varchar::varbinary); +SELECT id, a FROM t3 WHERE id = 17; + id | a +----+---------- + 17 | 0x313131 +(1 row) + +SELECT id, a::varchar FROM t3 WHERE id = 17; + id | a +----+----- + 17 | 111 +(1 row) + +INSERT INTO t3 VALUES (18, '111'::nvarchar); +ERROR: column "a" is of type varbinary but expression is of type nvarchar2 +LINE 1: INSERT INTO t3 VALUES (18, '111'::nvarchar); + ^ +HINT: You will need to rewrite or cast the expression. +CONTEXT: referenced column: a +INSERT INTO t3 VALUES (18, '111'::nvarchar::varbinary); +SELECT id, a FROM t3 WHERE id = 18; + id | a +----+---------- + 18 | 0x313131 +(1 row) + +SELECT id, a::varchar FROM t3 WHERE id = 18; + id | a +----+----- + 18 | 111 +(1 row) + +\c postgres +DROP DATABASE test_varbinary; diff --git a/contrib/shark/parallel_schedule b/contrib/shark/parallel_schedule index e2b24e038bfc6c23b6cc984633f98066294af42f..5a05d4a45c65cf3ca09fe2c768275338332878db 100644 --- a/contrib/shark/parallel_schedule +++ b/contrib/shark/parallel_schedule @@ -2,3 +2,4 @@ test: init test: basetest test: clustered_index insert_stmt sbr_test +test: varbinary diff --git a/contrib/shark/shark--1.0.sql b/contrib/shark/shark--1.0.sql index ab79280b27e5450d93ff9c9c6cabc1485ec1a47c..2b389c71fcadd74583df1079fb86863f1f2e0167 100644 --- a/contrib/shark/shark--1.0.sql +++ b/contrib/shark/shark--1.0.sql @@ -18,4 +18,274 @@ create trusted language pltsql grant usage on language pltsql to public; +-- varbinary.sql +-- VARBINARY +CREATE TYPE sys.VARBINARY; + +CREATE FUNCTION sys.varbinaryin(cstring, oid, integer) +RETURNS sys.VARBINARY +AS '$libdir/shark', 'varbinaryin' +LANGUAGE C IMMUTABLE STRICT; + +CREATE OR REPLACE FUNCTION sys.varbinaryout(sys.VARBINARY) +RETURNS cstring +AS '$libdir/shark', 'varbinaryout' +LANGUAGE C IMMUTABLE STRICT; + +CREATE OR REPLACE FUNCTION sys.varbinaryrecv(internal, oid, integer) +RETURNS sys.VARBINARY +AS '$libdir/shark', 'varbinaryrecv' +LANGUAGE C IMMUTABLE STRICT; + +CREATE OR REPLACE FUNCTION sys.varbinarysend(sys.VARBINARY) +RETURNS bytea +AS '$libdir/shark', 'varbinarysend' +LANGUAGE C IMMUTABLE STRICT; + +CREATE OR REPLACE FUNCTION sys.varbinarytypmodin(cstring[]) +RETURNS integer +AS '$libdir/shark', 'varbinarytypmodin' +LANGUAGE C IMMUTABLE STRICT; + +CREATE OR REPLACE FUNCTION sys.varbinarytypmodout(integer) +RETURNS cstring +AS '$libdir/shark', 'varbinarytypmodout' +LANGUAGE C IMMUTABLE STRICT; + +CREATE TYPE sys.VARBINARY ( + INPUT = sys.varbinaryin, + OUTPUT = sys.varbinaryout, + RECEIVE = sys.varbinaryrecv, + SEND = sys.varbinarysend, + TYPMOD_IN = sys.varbinarytypmodin, + TYPMOD_OUT = sys.varbinarytypmodout, + INTERNALLENGTH = VARIABLE, + ALIGNMENT = 'int4', + STORAGE = 'extended', + CATEGORY = 'U', + PREFERRED = false, + COLLATABLE = false +); + +CREATE OR REPLACE FUNCTION sys.varbinary(sys.VARBINARY, integer, boolean) +RETURNS sys.VARBINARY +AS '$libdir/shark', 'varbinary' +LANGUAGE C IMMUTABLE STRICT; + +-- typmod cast for sys.VARBINARY +CREATE CAST (sys.VARBINARY AS sys.VARBINARY) +WITH FUNCTION sys.varbinary(sys.VARBINARY, integer, BOOLEAN) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.byteavarbinary(pg_catalog.BYTEA, integer, boolean) +RETURNS sys.VARBINARY +AS '$libdir/shark', 'byteavarbinary' +LANGUAGE C IMMUTABLE STRICT; + +CREATE CAST (pg_catalog.BYTEA AS sys.VARBINARY) +WITH FUNCTION sys.byteavarbinary(pg_catalog.BYTEA, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.varbinarybytea(sys.VARBINARY, integer, boolean) +RETURNS pg_catalog.BYTEA +AS '$libdir/shark', 'byteavarbinary' +LANGUAGE C IMMUTABLE STRICT; + +CREATE CAST (sys.VARBINARY AS pg_catalog.BYTEA) +WITH FUNCTION sys.varbinarybytea(sys.VARBINARY, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.nvarcharvarbinary(pg_catalog.NVARCHAR2, integer, boolean) +RETURNS sys.VARBINARY +AS '$libdir/shark', 'nvarcharvarbinary' +LANGUAGE C IMMUTABLE STRICT; + +CREATE OR REPLACE FUNCTION sys.varcharvarbinary(pg_catalog.VARCHAR, integer, boolean) +RETURNS sys.VARBINARY +AS '$libdir/shark', 'varcharvarbinary' +LANGUAGE C IMMUTABLE STRICT; + +CREATE CAST (pg_catalog.VARCHAR AS sys.VARBINARY) +WITH FUNCTION sys.varcharvarbinary (pg_catalog.VARCHAR, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.bpcharvarbinary(pg_catalog.BPCHAR, integer, boolean) +RETURNS sys.VARBINARY +AS '$libdir/shark', 'bpcharvarbinary' +LANGUAGE C IMMUTABLE STRICT; + +CREATE CAST (pg_catalog.BPCHAR AS sys.VARBINARY) +WITH FUNCTION sys.bpcharvarbinary (pg_catalog.BPCHAR, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.varbinarysysnvarchar(sys.VARBINARY, integer, boolean) +RETURNS pg_catalog.NVARCHAR2 +AS '$libdir/shark', 'varbinarynvarchar' +LANGUAGE C IMMUTABLE STRICT; + +CREATE OR REPLACE FUNCTION sys.varbinaryvarchar(sys.VARBINARY, integer, boolean) +RETURNS pg_catalog.VARCHAR +AS '$libdir/shark', 'varbinaryvarchar' +LANGUAGE C IMMUTABLE STRICT; + +CREATE CAST (sys.VARBINARY AS pg_catalog.VARCHAR) +WITH FUNCTION sys.varbinaryvarchar (sys.VARBINARY, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.int2varbinary(INT2, integer, boolean) +RETURNS sys.VARBINARY +AS '$libdir/shark', 'int2varbinary' +LANGUAGE C IMMUTABLE STRICT; + +CREATE CAST (INT2 AS sys.VARBINARY) +WITH FUNCTION sys.int2varbinary (INT2, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.int4varbinary(INT4, integer, boolean) +RETURNS sys.VARBINARY +AS '$libdir/shark', 'int4varbinary' +LANGUAGE C IMMUTABLE STRICT; + +CREATE CAST (INT4 AS sys.VARBINARY) +WITH FUNCTION sys.int4varbinary (INT4, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.int8varbinary(INT8, integer, boolean) +RETURNS sys.VARBINARY +AS '$libdir/shark', 'int8varbinary' +LANGUAGE C IMMUTABLE STRICT; + +CREATE CAST (INT8 AS sys.VARBINARY) +WITH FUNCTION sys.int8varbinary (INT8, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.float4varbinary(REAL, integer, boolean) +RETURNS sys.VARBINARY +AS '$libdir/shark', 'float4varbinary' +LANGUAGE C IMMUTABLE STRICT; + +CREATE CAST (REAL AS sys.VARBINARY) +WITH FUNCTION sys.float4varbinary (REAL, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.float8varbinary(DOUBLE PRECISION, integer, boolean) +RETURNS sys.VARBINARY +AS '$libdir/shark', 'float8varbinary' +LANGUAGE C IMMUTABLE STRICT; + +CREATE CAST (DOUBLE PRECISION AS sys.VARBINARY) +WITH FUNCTION sys.float8varbinary (DOUBLE PRECISION, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.varbinaryint2(sys.VARBINARY) +RETURNS INT2 +AS '$libdir/shark', 'varbinaryint2' +LANGUAGE C IMMUTABLE STRICT; + +CREATE CAST (sys.VARBINARY as INT2) +WITH FUNCTION sys.varbinaryint2 (sys.VARBINARY) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.varbinaryint4(sys.VARBINARY) +RETURNS INT4 +AS '$libdir/shark', 'varbinaryint4' +LANGUAGE C IMMUTABLE STRICT; + +CREATE CAST (sys.VARBINARY as INT4) +WITH FUNCTION sys.varbinaryint4 (sys.VARBINARY) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.varbinaryint8(sys.VARBINARY) +RETURNS INT8 +AS '$libdir/shark', 'varbinaryint8' +LANGUAGE C IMMUTABLE STRICT; + +CREATE CAST (sys.VARBINARY as INT8) +WITH FUNCTION sys.varbinaryint8 (sys.VARBINARY) AS ASSIGNMENT; + +-- Add support for varbinary and binary with operators +-- Support equals +CREATE FUNCTION sys.varbinary_eq(leftarg sys.varbinary, rightarg sys.varbinary) +RETURNS boolean +AS '$libdir/shark', 'varbinary_eq' +LANGUAGE C IMMUTABLE STRICT; + +CREATE OPERATOR sys.= ( + LEFTARG = sys.varbinary, + RIGHTARG = sys.varbinary, + PROCEDURE = sys.varbinary_eq, + COMMUTATOR = =, + RESTRICT = eqsel +); + +-- Support not equals +CREATE FUNCTION sys.varbinary_neq(leftarg sys.varbinary, rightarg sys.varbinary) +RETURNS boolean +AS '$libdir/shark', 'varbinary_neq' +LANGUAGE C IMMUTABLE STRICT; + +CREATE OPERATOR sys.<> ( + LEFTARG = sys.varbinary, + RIGHTARG = sys.varbinary, + PROCEDURE = sys.varbinary_neq, + COMMUTATOR = <> +); + +-- Support greater than +CREATE FUNCTION sys.varbinary_gt(leftarg sys.varbinary, rightarg sys.varbinary) +RETURNS boolean +AS '$libdir/shark', 'varbinary_gt' +LANGUAGE C IMMUTABLE STRICT; + +CREATE OPERATOR sys.> ( + LEFTARG = sys.varbinary, + RIGHTARG = sys.varbinary, + PROCEDURE = sys.varbinary_gt, + COMMUTATOR = < +); + +-- Support greater than equals +CREATE FUNCTION sys.varbinary_geq(leftarg sys.varbinary, rightarg sys.varbinary) +RETURNS boolean +AS '$libdir/shark', 'varbinary_geq' +LANGUAGE C IMMUTABLE STRICT; + +CREATE OPERATOR sys.>= ( + LEFTARG = sys.varbinary, + RIGHTARG = sys.varbinary, + PROCEDURE = sys.varbinary_geq, + COMMUTATOR = <= +); + +-- Support less than +CREATE FUNCTION sys.varbinary_lt(leftarg sys.varbinary, rightarg sys.varbinary) +RETURNS boolean +AS '$libdir/shark', 'varbinary_lt' +LANGUAGE C IMMUTABLE STRICT; + +CREATE OPERATOR sys.< ( + LEFTARG = sys.varbinary, + RIGHTARG = sys.varbinary, + PROCEDURE = sys.varbinary_lt, + COMMUTATOR = > +); + +-- Support less than equals +CREATE FUNCTION sys.varbinary_leq(leftarg sys.varbinary, rightarg sys.varbinary) +RETURNS boolean +AS '$libdir/shark', 'varbinary_leq' +LANGUAGE C IMMUTABLE STRICT; + +CREATE OPERATOR sys.<= ( + LEFTARG = sys.varbinary, + RIGHTARG = sys.varbinary, + PROCEDURE = sys.varbinary_leq, + COMMUTATOR = >= +); + + +CREATE FUNCTION sys.varbinary_cmp(sys.varbinary, sys.varbinary) +RETURNS int +AS '$libdir/shark', 'varbinary_cmp' +LANGUAGE C IMMUTABLE STRICT; + + +CREATE OPERATOR CLASS sys.varbinary_ops +DEFAULT FOR TYPE sys.varbinary USING btree AS + OPERATOR 1 < (sys.varbinary, sys.varbinary), + OPERATOR 2 <= (sys.varbinary, sys.varbinary), + OPERATOR 3 = (sys.varbinary, sys.varbinary), + OPERATOR 4 >= (sys.varbinary, sys.varbinary), + OPERATOR 5 > (sys.varbinary, sys.varbinary), + FUNCTION 1 sys.varbinary_cmp(sys.varbinary, sys.varbinary); + +-- varbinary.sql end + reset search_path; \ No newline at end of file diff --git a/contrib/shark/shark.cpp b/contrib/shark/shark.cpp index 5af5a2c8e054fea226be7454ab2e02be5606528d..d0a465e96519074bfcdbff5692492e661e6212da 100644 --- a/contrib/shark/shark.cpp +++ b/contrib/shark/shark.cpp @@ -3,6 +3,7 @@ #include "src/backend_parser/scanner.h" #include "commands/extension.h" #include "shark.h" +#include "parser/parser.h" PG_MODULE_MAGIC; @@ -10,9 +11,50 @@ static bool global_hook_inited = false; static uint32 shark_index; extern List* tsql_raw_parser(const char* str, List** query_string_locationlist); +static List *rewrite_typmod_expr(List *expr_list); +static Node *makeIntConst(int val, int location); void _PG_init(void) -{} +{ + rewrite_typmod_expr_hook = rewrite_typmod_expr; +} + +static List *rewrite_typmod_expr(List *expr_list) +{ + /* + * Look for ( max ) if we are in tsql dialect, MAX can be used in + * sys.varchar, sys.nvarchar, sys.binary and sys.varbinary. map it to + * TSQLMaxTypmod + */ + Node *expr; + + expr = (Node*)linitial(expr_list); + if (list_length(expr_list) == 1 && IsA(expr, ColumnRef)) + { + ColumnRef *columnref = (ColumnRef *) expr; + + if (list_length(columnref->fields) == 1) + { + char *str = ((Value*)linitial(columnref->fields))->val.str; + + if (strcmp(str, "max") == 0) + return list_make1(makeIntConst(TSQLMaxTypmod, -1)); + } + } + + return expr_list; /* nothing to do */ +} + +static Node *makeIntConst(int val, int location) +{ + A_Const *n = makeNode(A_Const); + + n->val.type = T_Integer; + n->val.val.ival = val; + n->location = location; + + return (Node *)n; +} void init_session_vars(void) { diff --git a/contrib/shark/sql/varbinary.sql b/contrib/shark/sql/varbinary.sql new file mode 100644 index 0000000000000000000000000000000000000000..3ee3931a523c5b8b87d5e793c7921bfdb1a858ef --- /dev/null +++ b/contrib/shark/sql/varbinary.sql @@ -0,0 +1,118 @@ +CREATE DATABASE test_varbinary DBCOMPATIBILITY 'D'; +\c test_varbinary +CREATE EXTENSION shark; + +CREATE TABLE t1 (id int, a VARBINARY(1)); +CREATE TABLE t2 (id int, a VARBINARY(2)); +CREATE TABLE t3 (id int, a VARBINARY(MAX)); +CREATE TABLE t4 (id int, a VARBINARY(1.1)); +CREATE TABLE t5 (id int, a VARBINARY(-1)); +CREATE TABLE t6 (id int, a VARBINARY(0)); +CREATE INDEX idx1 ON t1(a); +CREATE INDEX idx2 ON t2(a); +CREATE INDEX idx3 ON t3(a); + +-- test t1 +INSERT INTO t1 VALUES (1, 12); +SELECT id, a FROM t1 WHERE id = 1; +SELECT id, a::int FROM t1 WHERE a::int = 12; +INSERT INTO t1 VALUES (2, 123456); +SELECT id, a FROM t1 WHERE id = 2; +SELECT id, a::int FROM t1 WHERE id = 2; +INSERT INTO t1 VALUES (3, '0x12'); +INSERT INTO t1 VALUES (4, '\x12'); +INSERT INTO t1 VALUES (5, '\xvv'); +INSERT INTO t1 VALUES (6, '12'); +INSERT INTO t1 VALUES (7, '123456'); +INSERT INTO t1 VALUES (8, '123456fff'); +INSERT INTO t1 VALUES (9, '0x123456'); +INSERT INTO t1 VALUES (10, '\x123456'); +INSERT INTO t1 VALUES (11, '\x123456vvv'); +INSERT INTO t1 VALUES (12, 1::int); +SELECT id, a FROM t1 WHERE id = 12; +SELECT id, a::int FROM t1 WHERE id = 12; +INSERT INTO t1 VALUES (15, NULL); +SELECT id, a FROM t1 WHERE id = 15; +INSERT INTO t1 VALUES (16, '111'::text); +INSERT INTO t1 VALUES (17, '111'::varchar); +INSERT INTO t1 VALUES (17, '111'::varchar::varbinary); +INSERT INTO t1 VALUES (18, '111'::nvarchar); +INSERT INTO t1 VALUES (18, '111'::nvarchar::varbinary); + +-- test t2 +INSERT INTO t2 VALUES (1, 12); +SELECT id, a FROM t2 WHERE id = 1; +SELECT id, a::int FROM t2 WHERE a::int = 12; +INSERT INTO t2 VALUES (2, 123456); +SELECT id, a FROM t2 WHERE id = 2; +SELECT id, a::int FROM t2 WHERE id = 2; +INSERT INTO t2 VALUES (3, '0x12'); +INSERT INTO t2 VALUES (4, '\x12'); +INSERT INTO t2 VALUES (5, '\xvv'); +INSERT INTO t2 VALUES (6, '12'); +INSERT INTO t2 VALUES (7, '123456'); +INSERT INTO t2 VALUES (8, '123456fff'); +INSERT INTO t2 VALUES (9, '0x123456'); +INSERT INTO t2 VALUES (10, '\x123456'); +INSERT INTO t2 VALUES (11, '\x123456vvv'); +INSERT INTO t2 VALUES (12, 1::int); +SELECT id, a FROM t2 WHERE id = 12; +SELECT id, a::int FROM t2 WHERE id = 12; +INSERT INTO t2 VALUES (15, NULL); +SELECT id, a FROM t2 WHERE id = 15; +INSERT INTO t2 VALUES (16, '111'::text); +INSERT INTO t2 VALUES (17, '111'::varchar); +INSERT INTO t2 VALUES (17, '111'::varchar::varbinary); +INSERT INTO t2 VALUES (18, '111'::nvarchar); +INSERT INTO t2 VALUES (18, '111'::nvarchar::varbinary); + +-- test t3 max +INSERT INTO t3 VALUES (1, 12); +SELECT id, a FROM t3 WHERE id = 1; +SELECT id, a::int FROM t3 WHERE a::int = 12; +INSERT INTO t3 VALUES (2, 123456); +SELECT id, a FROM t3 WHERE id = 2; +SELECT id, a::int FROM t3 WHERE id = 2; +INSERT INTO t3 VALUES (3, '0x12'); +SELECT id, a FROM t3 WHERE id = 3; +SELECT id, a::varchar FROM t3 WHERE id = 3; +INSERT INTO t3 VALUES (4, '\x12'); +SELECT id, a FROM t3 WHERE id = 4; +SELECT id, a::varchar FROM t3 WHERE id = 4; +INSERT INTO t3 VALUES (5, '\xvv'); +SELECT id, a FROM t3 WHERE id = 5; +SELECT id, a::varchar FROM t3 WHERE id = 5; +INSERT INTO t3 VALUES (6, '12'); +SELECT id, a FROM t3 WHERE id = 6; +SELECT id, a::varchar FROM t3 WHERE id = 6; +INSERT INTO t3 VALUES (7, '123456'); +SELECT id, a FROM t3 WHERE id = 7; +SELECT id, a::varchar FROM t3 WHERE id = 7; +INSERT INTO t3 VALUES (8, '123456fff'); +SELECT id, a FROM t3 WHERE id = 8; +SELECT id, a::varchar FROM t3 WHERE id = 8; +INSERT INTO t3 VALUES (9, '0x123456'); +SELECT id, a FROM t3 WHERE id = 9; +SELECT id, a::varchar FROM t3 WHERE id = 9; +INSERT INTO t3 VALUES (10, '\x123456'); +SELECT id, a FROM t3 WHERE id = 10; +SELECT id, a::varchar FROM t3 WHERE id = 10; +INSERT INTO t3 VALUES (11, '\x123456vvv'); +SELECT id, a FROM t3 WHERE id = 11; +SELECT id, a::varchar FROM t3 WHERE id = 11; +INSERT INTO t3 VALUES (12, 1::int); +SELECT id, a FROM t3 WHERE id = 12; +SELECT id, a::int FROM t3 WHERE id = 12; +INSERT INTO t3 VALUES (15, NULL); +SELECT id, a FROM t3 WHERE id = 15; +INSERT INTO t3 VALUES (16, '111'::text); +INSERT INTO t3 VALUES (17, '111'::varchar); +INSERT INTO t3 VALUES (17, '111'::varchar::varbinary); +SELECT id, a FROM t3 WHERE id = 17; +SELECT id, a::varchar FROM t3 WHERE id = 17; +INSERT INTO t3 VALUES (18, '111'::nvarchar); +INSERT INTO t3 VALUES (18, '111'::nvarchar::varbinary); +SELECT id, a FROM t3 WHERE id = 18; +SELECT id, a::varchar FROM t3 WHERE id = 18; +\c postgres +DROP DATABASE test_varbinary; diff --git a/contrib/shark/src/backend_parser/gram-tsql-epilogue.y.cpp b/contrib/shark/src/backend_parser/gram-tsql-epilogue.y.cpp index 791c4ebc2395d155f75ccd06c23885e12b82ca23..e7402d56b0668f2699f681df2f2d1f388676d878 100644 --- a/contrib/shark/src/backend_parser/gram-tsql-epilogue.y.cpp +++ b/contrib/shark/src/backend_parser/gram-tsql-epilogue.y.cpp @@ -10,5 +10,16 @@ pgtsql_base_yyerror(YYLTYPE * yylloc, core_yyscan_t yyscanner, const char *msg) base_yyerror(yylloc, yyscanner, msg); } +static Node * +makeTSQLHexStringConst(char *str, int location) +{ + A_Const *n = makeNode(A_Const); + n->val.type = T_TSQL_HexString; + n->val.val.str = str; + n->location = location; + + return (Node *) n; +} + #include "scan-backend.inc" #undef SCANINC diff --git a/contrib/shark/src/backend_parser/gram-tsql-prologue.y.h b/contrib/shark/src/backend_parser/gram-tsql-prologue.y.h index 229b99807c80f0ccf6e4470a6f2b84001a9d836d..4ef65b78cdad116fc564f6ea0876877a3cc499e2 100644 --- a/contrib/shark/src/backend_parser/gram-tsql-prologue.y.h +++ b/contrib/shark/src/backend_parser/gram-tsql-prologue.y.h @@ -1 +1,2 @@ static void pgtsql_base_yyerror(YYLTYPE * yylloc, core_yyscan_t yyscanner, const char *msg); +static Node *makeTSQLHexStringConst(char *str, int location); \ No newline at end of file diff --git a/contrib/shark/src/backend_parser/gram-tsql-rule.y b/contrib/shark/src/backend_parser/gram-tsql-rule.y index b5c567372cff56993dfcb4b14cff6d14dd27d02e..5fceb6d4f9ca7339553469fa46eb9f527505cf4c 100644 --- a/contrib/shark/src/backend_parser/gram-tsql-rule.y +++ b/contrib/shark/src/backend_parser/gram-tsql-rule.y @@ -7,6 +7,13 @@ stmtblock: DIALECT_TSQL tsql_stmtmulti } ; +AexprConst: + TSQL_XCONST + { + $$ = makeTSQLHexStringConst($1, @1); + } + ; + /* the thrashing around here is to discard "empty" statements... */ tsql_stmtmulti: tsql_stmtmulti ';' tsql_stmt { @@ -662,4 +669,5 @@ tsql_stmt : | /*EMPTY*/ { $$ = NULL; } | DelimiterStmt - ; \ No newline at end of file + ; + diff --git a/contrib/shark/src/backend_parser/scan-tsql-decl.l b/contrib/shark/src/backend_parser/scan-tsql-decl.l index ca62fb7a2ef93b7b15b906463c02c346ec3b99ec..02a10482f3807cf6098ce5be45228ef64c7bd2d2 100644 --- a/contrib/shark/src/backend_parser/scan-tsql-decl.l +++ b/contrib/shark/src/backend_parser/scan-tsql-decl.l @@ -1,8 +1,11 @@ %option prefix="pgtsql_core_yy" %s tsql +tsql_hex 0[xX]{hex_cont}* + %x xbr xbrstart \[ xbrstop \] xbrinside [^\]]+ +hex_cont [0-9A-Za-z] diff --git a/contrib/shark/src/backend_parser/scan-tsql-rule.l b/contrib/shark/src/backend_parser/scan-tsql-rule.l index 9d02e9e1e6465f60ca2e86fccdd839db008b5b88..d17bda7d82ec231b8e5ef6ae250cb039a7ef07fd 100644 --- a/contrib/shark/src/backend_parser/scan-tsql-rule.l +++ b/contrib/shark/src/backend_parser/scan-tsql-rule.l @@ -61,4 +61,9 @@ yyextra->is_hint_str = false; return IDENT; + } +{tsql_hex} { + SET_YYLLOC(); + yylval->str = pstrdup(yytext); + return TSQL_XCONST; } \ No newline at end of file diff --git a/contrib/shark/src/backend_parser/varbinary.cpp b/contrib/shark/src/backend_parser/varbinary.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3a2407a64c65c3d016ac19f5984fd39645c19a77 --- /dev/null +++ b/contrib/shark/src/backend_parser/varbinary.cpp @@ -0,0 +1,1653 @@ +/*------------------------------------------------------------------------- + * + * varbinary.c + * Functions for the variable-length binary type. + * + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include +#include + +#include "access/hash.h" +#include "catalog/pg_collation.h" +#include "catalog/pg_type.h" +// #include "collation.h" +#include "commands/trigger.h" +#include "common/int.h" +// #include "encoding/encoding.h" +#include "lib/hyperloglog.h" +#include "libpq/pqformat.h" +#include "miscadmin.h" +#include "parser/parser.h" +#include "parser/scansup.h" +#include "port/pg_bswap.h" +#include "regex/regex.h" +#include "replication/logicalworker.h" +#include "utils/array.h" +#include "utils/builtins.h" +#include "utils/bytea.h" +#include "utils/guc.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/pg_locale.h" +#include "utils/sortsupport.h" +// #include "utils/varlena.h" +#include "lib/stringinfo.h" +#include "gramparse.h" + +// #include "instr.h" +// #include "varchar.h" +// #include "typecode.h" + +PG_FUNCTION_INFO_V1(varbinaryin); +PG_FUNCTION_INFO_V1(varbinaryout); +PG_FUNCTION_INFO_V1(varbinaryrecv); +PG_FUNCTION_INFO_V1(varbinarysend); +PG_FUNCTION_INFO_V1(varbinary); +PG_FUNCTION_INFO_V1(binary); +PG_FUNCTION_INFO_V1(varbinarytypmodin); +PG_FUNCTION_INFO_V1(varbinarytypmodout); +PG_FUNCTION_INFO_V1(byteavarbinary); +PG_FUNCTION_INFO_V1(varbinarybytea); +PG_FUNCTION_INFO_V1(varbinaryrowversion); +PG_FUNCTION_INFO_V1(rowversionbinary); +PG_FUNCTION_INFO_V1(rowversionvarbinary); +PG_FUNCTION_INFO_V1(varcharvarbinary); +PG_FUNCTION_INFO_V1(nvarcharvarbinary); +PG_FUNCTION_INFO_V1(bpcharvarbinary); +PG_FUNCTION_INFO_V1(nvarcharbinary); +PG_FUNCTION_INFO_V1(varbinaryvarchar); +PG_FUNCTION_INFO_V1(varbinarynvarchar); +PG_FUNCTION_INFO_V1(varcharbinary); +PG_FUNCTION_INFO_V1(bpcharbinary); +PG_FUNCTION_INFO_V1(varcharrowversion); +PG_FUNCTION_INFO_V1(bpcharrowversion); +PG_FUNCTION_INFO_V1(int2varbinary); +PG_FUNCTION_INFO_V1(int4varbinary); +PG_FUNCTION_INFO_V1(int8varbinary); +PG_FUNCTION_INFO_V1(int2binary); +PG_FUNCTION_INFO_V1(int4binary); +PG_FUNCTION_INFO_V1(int8binary); +PG_FUNCTION_INFO_V1(int2rowversion); +PG_FUNCTION_INFO_V1(int4rowversion); +PG_FUNCTION_INFO_V1(int8rowversion); +PG_FUNCTION_INFO_V1(varbinaryint2); +PG_FUNCTION_INFO_V1(varbinaryint4); +PG_FUNCTION_INFO_V1(varbinaryint8); +PG_FUNCTION_INFO_V1(binaryint2); +PG_FUNCTION_INFO_V1(binaryint4); +PG_FUNCTION_INFO_V1(binaryint8); +PG_FUNCTION_INFO_V1(float4varbinary); +PG_FUNCTION_INFO_V1(float8varbinary); +PG_FUNCTION_INFO_V1(float4binary); +PG_FUNCTION_INFO_V1(float8binary); + +extern "C" Datum varbinaryin(PG_FUNCTION_ARGS); +extern "C" Datum varbinaryout(PG_FUNCTION_ARGS); +extern "C" Datum varbinaryrecv(PG_FUNCTION_ARGS); +extern "C" Datum varbinarysend(PG_FUNCTION_ARGS); +extern "C" Datum varbinary(PG_FUNCTION_ARGS); +extern "C" Datum binary(PG_FUNCTION_ARGS); +extern "C" Datum varbinarytypmodin(PG_FUNCTION_ARGS); +extern "C" Datum varbinarytypmodout(PG_FUNCTION_ARGS); +extern "C" Datum varbinaryin(PG_FUNCTION_ARGS); +extern "C" Datum varbinaryout(PG_FUNCTION_ARGS); +extern "C" Datum varbinaryrecv(PG_FUNCTION_ARGS); +extern "C" Datum varbinarysend(PG_FUNCTION_ARGS); +extern "C" Datum varbinary(PG_FUNCTION_ARGS); +extern "C" Datum binary(PG_FUNCTION_ARGS); +extern "C" Datum varbinarytypmodin(PG_FUNCTION_ARGS); +extern "C" Datum varbinarytypmodout(PG_FUNCTION_ARGS); +extern "C" Datum byteavarbinary(PG_FUNCTION_ARGS); +extern "C" Datum varbinarybytea(PG_FUNCTION_ARGS); +extern "C" Datum varbinaryrowversion(PG_FUNCTION_ARGS); +extern "C" Datum rowversionbinary(PG_FUNCTION_ARGS); +extern "C" Datum rowversionvarbinary(PG_FUNCTION_ARGS); +extern "C" Datum varcharvarbinary(PG_FUNCTION_ARGS); +extern "C" Datum nvarcharvarbinary(PG_FUNCTION_ARGS); +extern "C" Datum bpcharvarbinary(PG_FUNCTION_ARGS); +extern "C" Datum nvarcharbinary(PG_FUNCTION_ARGS); +extern "C" Datum varbinaryvarchar(PG_FUNCTION_ARGS); +extern "C" Datum varbinarynvarchar(PG_FUNCTION_ARGS); +extern "C" Datum varcharbinary(PG_FUNCTION_ARGS); +extern "C" Datum bpcharbinary(PG_FUNCTION_ARGS); +extern "C" Datum varcharrowversion(PG_FUNCTION_ARGS); +extern "C" Datum bpcharrowversion(PG_FUNCTION_ARGS); +extern "C" Datum int2varbinary(PG_FUNCTION_ARGS); +extern "C" Datum int4varbinary(PG_FUNCTION_ARGS); +extern "C" Datum int8varbinary(PG_FUNCTION_ARGS); +extern "C" Datum int2binary(PG_FUNCTION_ARGS); +extern "C" Datum int4binary(PG_FUNCTION_ARGS); +extern "C" Datum int8binary(PG_FUNCTION_ARGS); +extern "C" Datum int2rowversion(PG_FUNCTION_ARGS); +extern "C" Datum int4rowversion(PG_FUNCTION_ARGS); +extern "C" Datum int8rowversion(PG_FUNCTION_ARGS); +extern "C" Datum varbinaryint2(PG_FUNCTION_ARGS); +extern "C" Datum varbinaryint4(PG_FUNCTION_ARGS); +extern "C" Datum varbinaryint8(PG_FUNCTION_ARGS); +extern "C" Datum binaryint2(PG_FUNCTION_ARGS); +extern "C" Datum binaryint4(PG_FUNCTION_ARGS); +extern "C" Datum binaryint8(PG_FUNCTION_ARGS); +extern "C" Datum float4varbinary(PG_FUNCTION_ARGS); +extern "C" Datum float8varbinary(PG_FUNCTION_ARGS); +extern "C" Datum float4binary(PG_FUNCTION_ARGS); +extern "C" Datum float8binary(PG_FUNCTION_ARGS); + +/***************************************************************************** + * USER I/O ROUTINES * + *****************************************************************************/ + +#define VAL(CH) ((CH) - '0') +#define DIG(VAL) ((VAL) + '0') + +#define MAX_BINARY_SIZE 8000 +#define ROWVERSION_SIZE 8 + +static const int8 hexlookup[128] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +}; + +static inline char +get_hex(char c) +{ + int res = -1; + + if (c > 0 && c < 127) + res = hexlookup[(unsigned char) c]; + + if (res < 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid hexadecimal digit: \"%c\"", c))); + + return (char) res; +} + +/* A variant of PG's hex_decode function, but allows odd number of hex digits */ +static uint64 +hex_decode_allow_odd_digits(const char *src, unsigned len, char *dst) +{ + const char *s, + *srcend; + char v1, + v2, + *p; + + srcend = src + len; + s = src; + p = dst; + + if (len % 2 == 1) + { + /* + * If input has odd number of hex digits, add a 0 to the front to make + * it even + */ + v1 = '\0'; + v2 = get_hex(*s++); + *p++ = v1 | v2; + } + /* The rest of the input must have even number of digits */ + while (s < srcend) + { + if (*s == ' ' || *s == '\n' || *s == '\t' || *s == '\r') + { + s++; + continue; + } + v1 = get_hex(*s++) << 4; + v2 = get_hex(*s++); + *p++ = v1 | v2; + } + + return p - dst; +} + +/* + * varbinaryin - input function of varbinary + */ +Datum +varbinaryin(PG_FUNCTION_ARGS) +{ + char *inputText = PG_GETARG_CSTRING(0); + char *rp; + char *tp; + int len; + bytea *result; + int32 typmod = PG_GETARG_INT32(2); + + len = strlen(inputText); + + /* + * Assume that input string is already hex encoded for following cases: + * 1. Typmode is TSQLHexConstTypmod + * 2. dump_restore GUC is set. + * 3. This is logical replication applyworker. + */ + if (typmod == TSQLHexConstTypmod || + IsLogicalWorker()) + { + /* + * calculate length of the binary code e.g. 0xFF should be 1 byte + * (plus VARHDRSZ) and 0xF should also be 1 byte (plus VARHDRSZ). + */ + int bc = (len - 1) / 2 + VARHDRSZ; /* maximum possible length */ + + if (typmod >= (int32) VARHDRSZ && bc > typmod) + { + /* Verify that extra bytes are zeros, and clip them off */ + char *temp_result; + size_t i; + + temp_result = (char*)palloc0(bc); + bc = hex_decode_allow_odd_digits(inputText + 2, len - 2, temp_result); + + for (i = (typmod - VARHDRSZ); i < bc; i++) + { + if (temp_result[i] != 0) + ereport(ERROR, + (errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION), + errmsg("String or binary data would be truncated.\n" + "The statement has been terminated."))); + } + pfree(temp_result); + bc = typmod; + len = (typmod - VARHDRSZ) * 2 + 2; + } + + result = (bytea*)palloc(bc); + bc = hex_decode_allow_odd_digits(inputText + 2, len - 2, VARDATA(result)); + SET_VARSIZE(result, bc + VARHDRSZ); /* actual length */ + + PG_RETURN_BYTEA_P(result); + } + + tp = inputText; + + result = (bytea *) palloc(len + VARHDRSZ); + SET_VARSIZE(result, len + VARHDRSZ); + + rp = VARDATA(result); + memcpy(rp, tp, len); + + PG_RETURN_BYTEA_P(result); +} + +/* + * varbinaryout - converts to printable representation of byte array + * + * In the traditional escaped format, non-printable characters are + * printed as '\nnn' (octal) and '\' as '\\'. + * This routine is copied from byteaout + */ +Datum +varbinaryout(PG_FUNCTION_ARGS) +{ + bytea *vlena = PG_GETARG_BYTEA_PP(0); + char *result; + char *rp; + + if (u_sess->attr.attr_common.bytea_output == BYTEA_OUTPUT_HEX) + { + /* Print hex format */ + rp = result = (char*)palloc(VARSIZE_ANY_EXHDR(vlena) * 2 + 2 + 1); + *rp++ = '0'; + *rp++ = 'x'; + rp += hex_encode(VARDATA_ANY(vlena), VARSIZE_ANY_EXHDR(vlena), rp); + } + else if (u_sess->attr.attr_common.bytea_output == BYTEA_OUTPUT_ESCAPE) + { + /* Print traditional escaped format */ + char *vp; + int len; + int i; + + len = 1; /* empty string has 1 char */ + vp = VARDATA_ANY(vlena); + for (i = VARSIZE_ANY_EXHDR(vlena); i != 0; i--, vp++) + { + if (*vp == '\\') + len += 2; + else if ((unsigned char) *vp < 0x20 || (unsigned char) *vp > 0x7e) + len += 4; + else + len++; + } + rp = result = (char *) palloc(len); + vp = VARDATA_ANY(vlena); + for (i = VARSIZE_ANY_EXHDR(vlena); i != 0; i--, vp++) + { + if (*vp == '\\') + { + *rp++ = '\\'; + *rp++ = '\\'; + } + else if ((unsigned char) *vp < 0x20 || (unsigned char) *vp > 0x7e) + { + int val; /* holds unprintable chars */ + + val = *vp; + rp[0] = '\\'; + rp[3] = DIG(val & 07); + val >>= 3; + rp[2] = DIG(val & 07); + val >>= 3; + rp[1] = DIG(val & 03); + rp += 4; + } + else + *rp++ = *vp; + } + } + else + { + elog(ERROR, "unrecognized bytea_output setting: %d", + u_sess->attr.attr_common.bytea_output); + rp = result = NULL; /* keep compiler quiet */ + } + + if (rp) + *rp = '\0'; + + PG_RETURN_CSTRING(result); +} + +/* + * varbinaryrecv - converts external binary format to bytea + */ +Datum +varbinaryrecv(PG_FUNCTION_ARGS) +{ + StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); + bytea *result; + int nbytes; + + nbytes = buf->len - buf->cursor; + result = (bytea *) palloc(nbytes + VARHDRSZ); + SET_VARSIZE(result, nbytes + VARHDRSZ); + pq_copymsgbytes(buf, VARDATA(result), nbytes); + PG_RETURN_BYTEA_P(result); +} + +/* + * varbinarysend - converts bytea to binary format + * + * This is a special case: just copy the input... + */ +Datum +varbinarysend(PG_FUNCTION_ARGS) +{ + bytea *vlena = PG_GETARG_BYTEA_P_COPY(0); + + PG_RETURN_BYTEA_P(vlena); +} + +/* + * Converts a VARBINARY type to the specified size. + * + * maxlen is the typmod, ie, declared length plus VARHDRSZ bytes. + * + * Truncation rules: for an explicit cast, silently truncate to the given + * length; for an implicit cast, raise error. + * (This is sort-of per SQL: the spec would actually have us + * raise a "completion condition" for the explicit cast case, but Postgres + * hasn't got such a concept.) + */ +Datum +varbinary(PG_FUNCTION_ARGS) +{ + bytea *source = PG_GETARG_BYTEA_PP(0); + char *data = VARDATA_ANY(source); + int32 typmod = PG_GETARG_INT32(1); + bool isExplicit = PG_GETARG_BOOL(2); + int32 len, + maxlen; + + len = VARSIZE_ANY_EXHDR(source); + + /* If typmod is -1 (or invalid), use the actual length */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + if (!isExplicit) + if (len > maxlen) + ereport(ERROR, + (errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION), + errmsg("String or binary data would be truncated.\n" + "The statement has been terminated."))); + + /* No work if typmod is invalid or supplied data fits it already */ + if (maxlen < 0 || len <= maxlen) + PG_RETURN_BYTEA_P(source); + + /* + * Truncate the input data using cstring_to_text_with_len, notice text and + * bytea actually have the same struct. + */ + PG_RETURN_BYTEA_P((bytea *) cstring_to_text_with_len(data, maxlen)); +} + +/* + * Converts a BINARY type to the specified size. + * + * maxlen is the typmod, ie, declared length plus VARHDRSZ bytes. + * + * Truncation rules: for an explicit cast, silently truncate to the given + * length; for an implicit cast, raise error. + * (This is sort-of per SQL: the spec would actually have us + * raise a "completion condition" for the explicit cast case, but Postgres + * hasn't got such a concept.) + */ +Datum +binary(PG_FUNCTION_ARGS) +{ + bytea *source = PG_GETARG_BYTEA_PP(0); + char *data = VARDATA_ANY(source); + int32 typmod = PG_GETARG_INT32(1); + bool isExplicit = PG_GETARG_BOOL(2); + int32 len, + maxlen; + + len = VARSIZE_ANY_EXHDR(source); + + /* If typmod is -1 (or invalid), use the actual length */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + if (maxlen > MAX_BINARY_SIZE) + ereport(ERROR, + (errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION), + errmsg("The size (%d) given to the type 'binary' exceeds the maximum allowed (%d)", + maxlen, MAX_BINARY_SIZE))); + + if (!isExplicit) + if (len > maxlen) + ereport(ERROR, + (errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION), + errmsg("String or binary data would be truncated.\n" + "The statement has been terminated."))); + + /* No work if maxlen is invalid or supplied data fits it exactly */ + if (maxlen < 0 || len == maxlen) + PG_RETURN_BYTEA_P(source); + + if (len < maxlen) + { + bytea *result; + int total_size = maxlen + VARHDRSZ; + char *tp; + char *rp; + + result = (bytea *) palloc(total_size); + SET_VARSIZE(result, total_size); + tp = VARDATA(source); + rp = VARDATA(result); + + memcpy(rp, tp, len); + /* NULL pad the rest of the space */ + memset(rp + len, '\0', maxlen - len); + + PG_RETURN_BYTEA_P(result); + } + + /* + * Truncate the input data to maxlen using cstring_to_text_with_len, + * notice text and bytea actually have the same struct. + */ + PG_RETURN_BYTEA_P((bytea *) cstring_to_text_with_len(data, maxlen)); +} + +/* common code for varbinarytypmodin, bpchartypmodin and varchartypmodin */ +static int32 +anychar_typmodin(ArrayType *ta, const char *typname) +{ + int32 typmod; + int32 *tl; + int n; + + tl = ArrayGetIntegerTypmods(ta, &n); + + /* Allow typmod of VARBINARY(MAX) to go through as is */ + if (*tl == TSQLMaxTypmod) + { + return *tl; + } + + /* + * we're not too tense about good error message here because grammar + * shouldn't allow wrong number of modifiers for CHAR + */ + 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 %s must be at least 1", typname))); + if (*tl > MaxAttrSize) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("length for type %s cannot exceed %d", + typname, MaxAttrSize))); + + /* + * For largely historical reasons, the typmod is VARHDRSZ plus the number + * of characters; there is enough client-side code that knows about that + * that we'd better not change it. + */ + typmod = VARHDRSZ + *tl; + + return typmod; +} + +/* + * code for varbinarytypmodout + * copied from bpchartypmodout and varchartypmodout + */ +static char * +anychar_typmodout(int32 typmod) +{ + char *res = (char *) palloc(64); + + if (typmod > VARHDRSZ) + snprintf(res, 64, "(%d)", (int) (typmod - VARHDRSZ)); + else + *res = '\0'; + + return res; +} + +Datum +varbinarytypmodin(PG_FUNCTION_ARGS) +{ + ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0); + + PG_RETURN_INT32(anychar_typmodin(ta, "varbinary")); +} + +Datum +varbinarytypmodout(PG_FUNCTION_ARGS) +{ + int32 typmod = PG_GETARG_INT32(0); + + PG_RETURN_CSTRING(anychar_typmodout(typmod)); +} + + +static void +reverse_memcpy(char *dst, char *src, size_t n) +{ + size_t i; + + for (i = 0; i < n; i++) + dst[n - 1 - i] = src[i]; +} + +/* + * Cast functions + */ +Datum +byteavarbinary(PG_FUNCTION_ARGS) +{ + bytea *source = PG_GETARG_BYTEA_PP(0); + + PG_RETURN_BYTEA_P(source); +} + +Datum +varbinarybytea(PG_FUNCTION_ARGS) +{ + bytea *source = PG_GETARG_BYTEA_PP(0); + + PG_RETURN_BYTEA_P(source); +} + +Datum +varbinaryrowversion(PG_FUNCTION_ARGS) +{ + bytea *source = PG_GETARG_BYTEA_PP(0); + bytea *result; + char *data = VARDATA_ANY(source); + size_t len = VARSIZE_ANY_EXHDR(source); + char *rp; + + if (len > ROWVERSION_SIZE) + len = ROWVERSION_SIZE; + + result = (bytea *) palloc0(ROWVERSION_SIZE + VARHDRSZ); + SET_VARSIZE(result, ROWVERSION_SIZE + VARHDRSZ); + + rp = VARDATA(result); + memcpy(rp, data, len); + + PG_RETURN_BYTEA_P(result); +} + +Datum +rowversionbinary(PG_FUNCTION_ARGS) +{ + bytea *source = PG_GETARG_BYTEA_PP(0); + int32 typmod = PG_GETARG_INT32(1); + char *data = VARDATA_ANY(source); + char *rp; + size_t len = VARSIZE_ANY_EXHDR(source); + int32 maxlen; + bytea *result; + + /* If typmod is -1 (or invalid), use the actual length */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + if (len > maxlen) + len = maxlen; + + result = (bytea *) palloc0(maxlen + VARHDRSZ); + SET_VARSIZE(result, maxlen + VARHDRSZ); + + rp = VARDATA(result); + memcpy(rp, data, len); + + PG_RETURN_BYTEA_P(source); +} + +Datum +rowversionvarbinary(PG_FUNCTION_ARGS) +{ + bytea *source = PG_GETARG_BYTEA_PP(0); + int32 typmod = PG_GETARG_INT32(1); + char *data = VARDATA_ANY(source); + char *rp; + size_t len = VARSIZE_ANY_EXHDR(source); + int32 maxlen; + bytea *result; + + /* If typmod is -1 (or invalid), use the actual length */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + if (len > maxlen) + len = maxlen; + + result = (bytea *) palloc(len + VARHDRSZ); + SET_VARSIZE(result, len + VARHDRSZ); + + rp = VARDATA(result); + memcpy(rp, data, len); + + PG_RETURN_BYTEA_P(source); +} + +Datum +varcharvarbinary(PG_FUNCTION_ARGS) +{ + VarChar *source = PG_GETARG_VARCHAR_PP(0); + char *data = VARDATA_ANY(source); + char *rp; + size_t len = VARSIZE_ANY_EXHDR(source); + int32 typmod = PG_GETARG_INT32(1); + bool isExplicit = PG_GETARG_BOOL(2); + int32 maxlen; + bytea *result; + + if (!isExplicit) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("Implicit conversion from data type varchar to " + "varbinary is not allowed. Use the CONVERT function " + "to run this query."))); + + /* If typmod is -1 (or invalid), use the actual length */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + if (len > maxlen) + len = maxlen; + + result = (bytea *) palloc(maxlen + VARHDRSZ); + SET_VARSIZE(result, maxlen + VARHDRSZ); + + rp = VARDATA(result); + memcpy(rp, data, len); + + /* NULL pad the rest of the space */ + memset(rp + len, '\0', maxlen - len); + PG_RETURN_BYTEA_P(result); +} + +/* + * For nvarchar we need to convert the input string to UTF-16 encoding irrespective of input encoding + * So the source string is in UTF-8 encoding, we will convert it to UTF-16 encoding + */ +Datum +nvarcharvarbinary(PG_FUNCTION_ARGS) +{ + VarChar *source = PG_GETARG_VARCHAR_PP(0); + char *data = VARDATA_ANY(source); /* Source string is UTF-8 */ + char *rp; + size_t len = VARSIZE_ANY_EXHDR(source); + int32 typmod = PG_GETARG_INT32(1); + bool isExplicit = PG_GETARG_BOOL(2); + int32 maxlen; + bytea *result; + MemoryContext ccxt = CurrentMemoryContext; + + if (!isExplicit) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("Implicit conversion from data type nvarchar to " + "varbinary is not allowed. Use the CONVERT function " + "to run this query."))); + + /* + * If typmod is -1 (or invalid), use the actual length + * Length should be checked after encoding into server encoding + */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + if (len > maxlen) + len = maxlen; + + result = (bytea *) palloc0(len + VARHDRSZ); + SET_VARSIZE(result, len + VARHDRSZ); + + rp = VARDATA(result); + memcpy(rp, data, len); + + PG_RETURN_BYTEA_P(result); +} + +Datum +bpcharvarbinary(PG_FUNCTION_ARGS) +{ + BpChar *source = PG_GETARG_BPCHAR_PP(0); + char *data = VARDATA_ANY(source); + char *rp; + size_t len = VARSIZE_ANY_EXHDR(source); + int32 typmod = PG_GETARG_INT32(1); + bool isExplicit = PG_GETARG_BOOL(2); + int32 maxlen; + bytea *result; + + if (!isExplicit) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("Implicit conversion from data type bpchar to " + "varbinary is not allowed. Use the CONVERT function " + "to run this query."))); + + /* If typmod is -1 (or invalid), use the actual length */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + if (len > maxlen) + len = maxlen; + + result = (bytea *) palloc(len + VARHDRSZ); + SET_VARSIZE(result, len + VARHDRSZ); + + rp = VARDATA(result); + memcpy(rp, data, len); + + PG_RETURN_BYTEA_P(result); +} + +/* + * This function is currently being called with 1 and 3 arguments, + * Currently, the third argument is not being parsed in this function, + * Check for the number of args needs to be added if the third arg is + * being parsed in future + */ +Datum +varbinaryvarchar(PG_FUNCTION_ARGS) +{ + bytea *source = PG_GETARG_BYTEA_PP(0); + char *data = VARDATA_ANY(source); /* Source data is server encoded */ + VarChar *result; + size_t len = VARSIZE_ANY_EXHDR(source); + int32 typmod = -1; + int32 maxlen = -1; + MemoryContext ccxt = CurrentMemoryContext; + + /* + * Check whether the typmod argument exists, so that we + * will not be reading any garbage values for typmod + * which might cause Invalid read such as BABEL-4475 + */ + if (PG_NARGS() > 1) + { + typmod = PG_GETARG_INT32(1); + maxlen = typmod - VARHDRSZ; + } + + if (maxlen < 0 || len <= maxlen) + maxlen = len; + + result = (VarChar *) cstring_to_text_with_len(data, maxlen); + + PG_RETURN_VARCHAR_P(result); +} + +Datum +varbinarynvarchar(PG_FUNCTION_ARGS) +{ + bytea *source = PG_GETARG_BYTEA_PP(0); + char *data = VARDATA_ANY(source); + VarChar *result; + size_t len = VARSIZE_ANY_EXHDR(source); + int32 typmod = -1; + int maxlen = -1; + MemoryContext ccxt = CurrentMemoryContext; + + typmod = PG_GETARG_INT32(1); + maxlen = typmod - VARHDRSZ; + + if (maxlen < 0 || len <= maxlen) + maxlen = len; + + result = (VarChar *) cstring_to_text_with_len(data, maxlen); + + PG_RETURN_VARCHAR_P(result); +} + + +Datum +varcharbinary(PG_FUNCTION_ARGS) +{ + VarChar *source = PG_GETARG_VARCHAR_PP(0); + char *data = VARDATA_ANY(source); + char *rp; + size_t len = VARSIZE_ANY_EXHDR(source); + int32 typmod = PG_GETARG_INT32(1); + bool isExplicit = PG_GETARG_BOOL(2); + int32 maxlen; + bytea *result; + + if (!isExplicit) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("Implicit conversion from data type nvarchar to " + "binary is not allowed. Use the CONVERT function " + "to run this query."))); + + /* If typmod is -1 (or invalid), use the actual length */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + if (len > maxlen) + len = maxlen; + + result = (bytea *) palloc0(maxlen + VARHDRSZ); + SET_VARSIZE(result, maxlen + VARHDRSZ); + + rp = VARDATA(result); + memcpy(rp, data, len); + PG_RETURN_BYTEA_P(result); +} + +Datum +nvarcharbinary(PG_FUNCTION_ARGS) +{ + VarChar *source = PG_GETARG_VARCHAR_PP(0); + char *data = VARDATA_ANY(source); + char *rp; + size_t len = VARSIZE_ANY_EXHDR(source); + int32 typmod = PG_GETARG_INT32(1); + bool isExplicit = PG_GETARG_BOOL(2); + int32 maxlen; + bytea *result; + + if (!isExplicit) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("Implicit conversion from data type nvarchar to " + "binary is not allowed. Use the CONVERT function " + "to run this query."))); + + /* If typmod is -1 (or invalid), use the actual length */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + if (len > maxlen) + len = maxlen; + + result = (bytea *) palloc0(maxlen + VARHDRSZ); + SET_VARSIZE(result, maxlen + VARHDRSZ); + + rp = VARDATA(result); + memcpy(rp, data, len); + PG_RETURN_BYTEA_P(result); +} + +Datum +bpcharbinary(PG_FUNCTION_ARGS) +{ + BpChar *source = PG_GETARG_BPCHAR_PP(0); + char *data = VARDATA_ANY(source); + char *rp; + size_t len = VARSIZE_ANY_EXHDR(source); + int32 typmod = PG_GETARG_INT32(1); + bool isExplicit = PG_GETARG_BOOL(2); + int32 maxlen; + bytea *result; + + if (!isExplicit) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("Implicit conversion from data type char to " + "binary is not allowed. Use the CONVERT function " + "to run this query."))); + + /* If typmod is -1 (or invalid), use the actual length */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + if (len > maxlen) + len = maxlen; + + result = (bytea *) palloc(maxlen + VARHDRSZ); + SET_VARSIZE(result, maxlen + VARHDRSZ); + + rp = VARDATA(result); + memcpy(rp, data, len); + + /* NULL pad the rest of the space */ + memset(rp + len, '\0', maxlen - len); + PG_RETURN_BYTEA_P(result); +} + +Datum +varcharrowversion(PG_FUNCTION_ARGS) +{ + VarChar *source = PG_GETARG_VARCHAR_PP(0); + char *data = VARDATA_ANY(source); + char *rp; + size_t len = VARSIZE_ANY_EXHDR(source); + bool isExplicit = PG_GETARG_BOOL(2); + bytea *result; + + if (!isExplicit) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("Implicit conversion from data type varchar to " + "rowversion is not allowed. Use the CONVERT function " + "to run this query."))); + + if (len > ROWVERSION_SIZE) + len = ROWVERSION_SIZE; + + result = (bytea *) palloc0(ROWVERSION_SIZE + VARHDRSZ); + SET_VARSIZE(result, ROWVERSION_SIZE + VARHDRSZ); + + rp = VARDATA(result); + memcpy(rp, data, len); + + PG_RETURN_BYTEA_P(result); +} + +Datum +bpcharrowversion(PG_FUNCTION_ARGS) +{ + BpChar *source = PG_GETARG_BPCHAR_PP(0); + char *data = VARDATA_ANY(source); + char *rp; + size_t len = VARSIZE_ANY_EXHDR(source); + bool isExplicit = PG_GETARG_BOOL(2); + bytea *result; + + if (!isExplicit) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("Implicit conversion from data type bpchar to " + "rowversion is not allowed. Use the CONVERT function " + "to run this query."))); + + if (len > ROWVERSION_SIZE) + len = ROWVERSION_SIZE; + + result = (bytea *) palloc0(ROWVERSION_SIZE + VARHDRSZ); + SET_VARSIZE(result, ROWVERSION_SIZE + VARHDRSZ); + + rp = VARDATA(result); + memcpy(rp, data, len); + + PG_RETURN_BYTEA_P(result); +} + +Datum +int2varbinary(PG_FUNCTION_ARGS) +{ + int16 input = PG_GETARG_INT16(0); + int32 typmod = PG_GETARG_INT32(1); + int32 maxlen; + int len = sizeof(int16); + int actual_len; + bytea *result; + char *rp; + + /* If typmod is -1 (or invalid), use the actual length */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + actual_len = maxlen < len ? maxlen : len; + + result = (bytea *) palloc(actual_len + VARHDRSZ); + SET_VARSIZE(result, actual_len + VARHDRSZ); + + rp = VARDATA(result); + /* Need reverse copy because endianness is different in MSSQL */ + reverse_memcpy(rp, (char *) &input, actual_len); + + PG_RETURN_BYTEA_P(result); +} + +Datum +int4varbinary(PG_FUNCTION_ARGS) +{ + int32 input = PG_GETARG_INT32(0); + int32 typmod = PG_GETARG_INT32(1); + int32 maxlen; + int len = sizeof(int32); + int actual_len; + bytea *result; + char *rp; + + /* If typmod is -1 (or invalid), use the actual length */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + actual_len = maxlen < len ? maxlen : len; + + result = (bytea *) palloc(actual_len + VARHDRSZ); + SET_VARSIZE(result, actual_len + VARHDRSZ); + + rp = VARDATA(result); + /* Need reverse copy because endianness is different in MSSQL */ + reverse_memcpy(rp, (char *) &input, actual_len); + + PG_RETURN_BYTEA_P(result); +} + +Datum +int8varbinary(PG_FUNCTION_ARGS) +{ + int64 input = PG_GETARG_INT64(0); + int32 typmod = PG_GETARG_INT32(1); + int32 maxlen; + int len = sizeof(int64); + int actual_len; + bytea *result; + char *rp; + + /* If typmod is -1 (or invalid), use the actual length */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + actual_len = maxlen < len ? maxlen : len; + + result = (bytea *) palloc(actual_len + VARHDRSZ); + SET_VARSIZE(result, actual_len + VARHDRSZ); + + rp = VARDATA(result); + /* Need reverse copy because endianness is different in MSSQL */ + reverse_memcpy(rp, (char *) &input, actual_len); + + PG_RETURN_BYTEA_P(result); +} + +Datum +varbinaryint2(PG_FUNCTION_ARGS) +{ + bytea *source = PG_GETARG_BYTEA_PP(0); + char *data = VARDATA_ANY(source); + int32 len; + int32 result_len; + int16 *result = (int16*)palloc0(sizeof(int16)); + + len = VARSIZE_ANY_EXHDR(source); + result_len = len > sizeof(int16) ? sizeof(int16) : len; + reverse_memcpy((char *) result, data, result_len); + + PG_RETURN_INT16(*result); +} + +Datum +varbinaryint4(PG_FUNCTION_ARGS) +{ + bytea *source = PG_GETARG_BYTEA_PP(0); + char *data = VARDATA_ANY(source); + int32 len; + int32 result_len; + int32 *result = (int32*)palloc0(sizeof(int32)); + + len = VARSIZE_ANY_EXHDR(source); + result_len = len > sizeof(int32) ? sizeof(int32) : len; + reverse_memcpy((char *) result, data, result_len); + + PG_RETURN_INT32(*result); +} + +Datum +varbinaryint8(PG_FUNCTION_ARGS) +{ + bytea *source = PG_GETARG_BYTEA_PP(0); + char *data = VARDATA_ANY(source); + int32 len; + int32 result_len; + int64 *result = (int64*)palloc0(sizeof(int64)); + + len = VARSIZE_ANY_EXHDR(source); + result_len = len > sizeof(int64) ? sizeof(int64) : len; + reverse_memcpy((char *) result, data, result_len); + + PG_RETURN_INT64(*result); +} + +Datum +float4varbinary(PG_FUNCTION_ARGS) +{ + float4 input = PG_GETARG_FLOAT4(0); + int32 typmod = PG_GETARG_INT32(1); + int32 maxlen; + int len = sizeof(float4); + int actual_len; + bytea *result; + char *rp; + + /* If typmod is -1 (or invalid), use the actual length */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + actual_len = maxlen < len ? maxlen : len; + + result = (bytea *) palloc(actual_len + VARHDRSZ); + SET_VARSIZE(result, actual_len + VARHDRSZ); + + rp = VARDATA(result); + /* Need reverse copy because endianness is different in MSSQL */ + reverse_memcpy(rp, (char *) &input, actual_len); + + PG_RETURN_BYTEA_P(result); +} + +Datum +float8varbinary(PG_FUNCTION_ARGS) +{ + float8 input = PG_GETARG_FLOAT8(0); + int32 typmod = PG_GETARG_INT32(1); + int32 maxlen; + int len = sizeof(float8); + int actual_len; + bytea *result; + char *rp; + + /* If typmod is -1 (or invalid), use the actual length */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + actual_len = maxlen < len ? maxlen : len; + + result = (bytea *) palloc(actual_len + VARHDRSZ); + SET_VARSIZE(result, actual_len + VARHDRSZ); + + rp = VARDATA(result); + /* Need reverse copy because endianness is different in MSSQL */ + reverse_memcpy(rp, (char *) &input, actual_len); + + PG_RETURN_BYTEA_P(result); +} + +Datum +int2binary(PG_FUNCTION_ARGS) +{ + int16 input = PG_GETARG_INT16(0); + int32 typmod = PG_GETARG_INT32(1); + int32 maxlen; + int len = sizeof(int16); + bytea *result; + char *rp; + + /* If typmod is -1 (or invalid), use the actual length */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + result = (bytea *) palloc0(maxlen + VARHDRSZ); + SET_VARSIZE(result, maxlen + VARHDRSZ); + + rp = VARDATA(result); + if (maxlen <= len) + /* Need reverse copy because endianness is different in MSSQL */ + reverse_memcpy(rp, (char *) &input, maxlen); + else + /* Pad 0 to the left if maxlen is longer than input length */ + reverse_memcpy(rp + maxlen - len, (char *) &input, len); + + PG_RETURN_BYTEA_P(result); +} + +Datum +int4binary(PG_FUNCTION_ARGS) +{ + int32 input = PG_GETARG_INT32(0); + int32 typmod = PG_GETARG_INT32(1); + int32 maxlen; + int len = sizeof(int32); + bytea *result; + char *rp; + + /* If typmod is -1 (or invalid), use the actual length */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + + result = (bytea *) palloc0(maxlen + VARHDRSZ); + SET_VARSIZE(result, maxlen + VARHDRSZ); + + rp = VARDATA(result); + if (maxlen <= len) + /* Need reverse copy because endianness is different in MSSQL */ + reverse_memcpy(rp, (char *) &input, maxlen); + else + /* Pad 0 to the left if maxlen is longer than input length */ + reverse_memcpy(rp + maxlen - len, (char *) &input, len); + + PG_RETURN_BYTEA_P(result); +} + +Datum +int8binary(PG_FUNCTION_ARGS) +{ + int64 input = PG_GETARG_INT64(0); + int32 typmod = PG_GETARG_INT32(1); + int32 maxlen; + int len = sizeof(int64); + bytea *result; + char *rp; + + /* If typmod is -1 (or invalid), use the actual length */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + + result = (bytea *) palloc0(maxlen + VARHDRSZ); + SET_VARSIZE(result, maxlen + VARHDRSZ); + + rp = VARDATA(result); + if (maxlen <= len) + /* Need reverse copy because endianness is different in MSSQL */ + reverse_memcpy(rp, (char *) &input, maxlen); + else + /* Pad 0 to the left if maxlen is longer than input length */ + reverse_memcpy(rp + maxlen - len, (char *) &input, len); + + PG_RETURN_BYTEA_P(result); +} + +Datum +int2rowversion(PG_FUNCTION_ARGS) +{ + int16 input = PG_GETARG_INT16(0); + int len = sizeof(int16); + bytea *result; + char *rp; + + result = (bytea *) palloc0(ROWVERSION_SIZE + VARHDRSZ); + SET_VARSIZE(result, ROWVERSION_SIZE + VARHDRSZ); + + rp = VARDATA(result); + /* Need reverse copy because endianness is different in T-SQL */ + reverse_memcpy(rp + ROWVERSION_SIZE - len, (char *) &input, len); + + PG_RETURN_BYTEA_P(result); +} + +Datum +int4rowversion(PG_FUNCTION_ARGS) +{ + int32 input = PG_GETARG_INT32(0); + int len = sizeof(int32); + bytea *result; + char *rp; + + result = (bytea *) palloc0(ROWVERSION_SIZE + VARHDRSZ); + SET_VARSIZE(result, ROWVERSION_SIZE + VARHDRSZ); + + rp = VARDATA(result); + /* Need reverse copy because endianness is different in T-SQL */ + reverse_memcpy(rp + ROWVERSION_SIZE - len, (char *) &input, len); + + PG_RETURN_BYTEA_P(result); +} + +Datum +int8rowversion(PG_FUNCTION_ARGS) +{ + int64 input = PG_GETARG_INT64(0); + int len = sizeof(int64); + bytea *result; + char *rp; + + result = (bytea *) palloc0(ROWVERSION_SIZE + VARHDRSZ); + SET_VARSIZE(result, ROWVERSION_SIZE + VARHDRSZ); + + rp = VARDATA(result); + /* Need reverse copy because endianness is different in T-SQL */ + reverse_memcpy(rp, (char *) &input, len); + + PG_RETURN_BYTEA_P(result); +} + +Datum +binaryint2(PG_FUNCTION_ARGS) +{ + bytea *source = PG_GETARG_BYTEA_PP(0); + char *data = VARDATA_ANY(source); + int32 len; + int32 result_len; + int16 *result = (int16*)palloc0(sizeof(int16)); + + len = VARSIZE_ANY_EXHDR(source); + result_len = len > sizeof(int16) ? sizeof(int16) : len; + if (len > sizeof(int16)) + + /* + * Skip the potentially 0 padded part if the input binary is over + * length + */ + reverse_memcpy((char *) result, data + len - sizeof(int16), result_len); + else + reverse_memcpy((char *) result, data, result_len); + + PG_RETURN_INT16(*result); +} + +Datum +binaryint4(PG_FUNCTION_ARGS) +{ + bytea *source = PG_GETARG_BYTEA_PP(0); + char *data = VARDATA_ANY(source); + int32 len; + int32 result_len; + int32 *result = (int32*)palloc0(sizeof(int32)); + + len = VARSIZE_ANY_EXHDR(source); + result_len = len > sizeof(int32) ? sizeof(int32) : len; + if (len > sizeof(int32)) + + /* + * Skip the potentially 0 padded part if the input binary is over + * length + */ + reverse_memcpy((char *) result, data + len - sizeof(int32), result_len); + else + reverse_memcpy((char *) result, data, result_len); + + PG_RETURN_INT32(*result); +} + +Datum +binaryint8(PG_FUNCTION_ARGS) +{ + bytea *source = PG_GETARG_BYTEA_PP(0); + char *data = VARDATA_ANY(source); + int32 len; + int32 result_len; + int64 *result = (int64*)palloc0(sizeof(int64)); + + len = VARSIZE_ANY_EXHDR(source); + result_len = len > sizeof(int64) ? sizeof(int64) : len; + if (len > sizeof(int64)) + + /* + * Skip the potentially 0 padded part if the input binary is over + * length + */ + reverse_memcpy((char *) result, data + len - sizeof(int64), result_len); + else + reverse_memcpy((char *) result, data, result_len); + + PG_RETURN_INT64(*result); +} + +Datum +float4binary(PG_FUNCTION_ARGS) +{ + float4 input = PG_GETARG_FLOAT4(0); + int32 typmod = PG_GETARG_INT32(1); + int32 maxlen; + int len = sizeof(float4); + bytea *result; + char *rp; + + /* If typmod is -1 (or invalid), use the actual length */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + + result = (bytea *) palloc0(maxlen + VARHDRSZ); + SET_VARSIZE(result, maxlen + VARHDRSZ); + + rp = VARDATA(result); + if (maxlen <= len) + /* Need reverse copy because endianness is different in MSSQL */ + reverse_memcpy(rp, (char *) &input, maxlen); + else + /* Pad 0 to the left if maxlen is longer than input length */ + reverse_memcpy(rp + maxlen - len, (char *) &input, len); + + PG_RETURN_BYTEA_P(result); +} + +Datum +float8binary(PG_FUNCTION_ARGS) +{ + float8 input = PG_GETARG_FLOAT8(0); + int32 typmod = PG_GETARG_INT32(1); + int32 maxlen; + int len = sizeof(float8); + bytea *result; + char *rp; + + /* If typmod is -1 (or invalid), use the actual length */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + + result = (bytea *) palloc0(maxlen + VARHDRSZ); + SET_VARSIZE(result, maxlen + VARHDRSZ); + + rp = VARDATA(result); + if (maxlen <= len) + /* Need reverse copy because endianness is different in MSSQL */ + reverse_memcpy(rp, (char *) &input, maxlen); + else + /* Pad 0 to the left if maxlen is longer than input length */ + reverse_memcpy(rp + maxlen - len, (char *) &input, len); + + PG_RETURN_BYTEA_P(result); +} + + +int8 + varbinarycompare(bytea *source1, bytea *source2); + +int8 + inline +varbinarycompare(bytea *source1, bytea *source2) +{ + char *data1 = VARDATA_ANY(source1); + int32 len1 = VARSIZE_ANY_EXHDR(source1); + char *data2 = VARDATA_ANY(source2); + int32 len2 = VARSIZE_ANY_EXHDR(source2); + + unsigned char byte1; + unsigned char byte2; + int32 maxlen = len2 > len1 ? len2 : len1; + + /* loop all the bytes */ + for (int i = 0; i < maxlen; i++) + { + byte1 = i < len1 ? data1[i] : 0; + byte2 = i < len2 ? data2[i] : 0; + /* we've found a different byte */ + if (byte1 > byte2) + return 1; + else if (byte1 < byte2) + return -1; + } + return 0; +} + +PG_FUNCTION_INFO_V1(varbinary_eq); +PG_FUNCTION_INFO_V1(varbinary_neq); +PG_FUNCTION_INFO_V1(varbinary_gt); +PG_FUNCTION_INFO_V1(varbinary_geq); +PG_FUNCTION_INFO_V1(varbinary_lt); +PG_FUNCTION_INFO_V1(varbinary_leq); +PG_FUNCTION_INFO_V1(varbinary_cmp); + +extern "C" Datum varbinary_eq(PG_FUNCTION_ARGS); +extern "C" Datum varbinary_neq(PG_FUNCTION_ARGS); +extern "C" Datum varbinary_gt(PG_FUNCTION_ARGS); +extern "C" Datum varbinary_geq(PG_FUNCTION_ARGS); +extern "C" Datum varbinary_lt(PG_FUNCTION_ARGS); +extern "C" Datum varbinary_leq(PG_FUNCTION_ARGS); +extern "C" Datum varbinary_cmp(PG_FUNCTION_ARGS); + + +Datum +varbinary_eq(PG_FUNCTION_ARGS) +{ + bytea *source1 = PG_GETARG_BYTEA_PP(0); + bytea *source2 = PG_GETARG_BYTEA_PP(1); + bool result = varbinarycompare(source1, source2) == 0; + + PG_RETURN_BOOL(result); +} + +Datum +varbinary_neq(PG_FUNCTION_ARGS) +{ + bytea *source1 = PG_GETARG_BYTEA_PP(0); + bytea *source2 = PG_GETARG_BYTEA_PP(1); + bool result = varbinarycompare(source1, source2) != 0; + + PG_RETURN_BOOL(result); +} + +Datum +varbinary_gt(PG_FUNCTION_ARGS) +{ + bytea *source1 = PG_GETARG_BYTEA_PP(0); + bytea *source2 = PG_GETARG_BYTEA_PP(1); + bool result = varbinarycompare(source1, source2) > 0; + + PG_RETURN_BOOL(result); +} + +Datum +varbinary_geq(PG_FUNCTION_ARGS) +{ + bytea *source1 = PG_GETARG_BYTEA_PP(0); + bytea *source2 = PG_GETARG_BYTEA_PP(1); + bool result = varbinarycompare(source1, source2) >= 0; + + PG_RETURN_BOOL(result); +} + +Datum +varbinary_lt(PG_FUNCTION_ARGS) +{ + bytea *source1 = PG_GETARG_BYTEA_PP(0); + bytea *source2 = PG_GETARG_BYTEA_PP(1); + bool result = varbinarycompare(source1, source2) < 0; + + PG_RETURN_BOOL(result); +} + +Datum +varbinary_leq(PG_FUNCTION_ARGS) +{ + bytea *source1 = PG_GETARG_BYTEA_PP(0); + bytea *source2 = PG_GETARG_BYTEA_PP(1); + bool result = varbinarycompare(source1, source2) <= 0; + + PG_RETURN_BOOL(result); +} + + +Datum +varbinary_cmp(PG_FUNCTION_ARGS) +{ + bytea *source1 = PG_GETARG_BYTEA_PP(0); + bytea *source2 = PG_GETARG_BYTEA_PP(1); + + PG_RETURN_INT32(varbinarycompare(source1, source2)); +} + +PG_FUNCTION_INFO_V1(varbinary_length); +extern "C" Datum varbinary_length(PG_FUNCTION_ARGS); + +Datum +varbinary_length(PG_FUNCTION_ARGS) +{ + bytea *source = PG_GETARG_BYTEA_PP(0); + int32 limit = VARSIZE_ANY_EXHDR(source); + + PG_RETURN_INT32(limit); +} + diff --git a/contrib/shark/src/pltsql/gram.y b/contrib/shark/src/pltsql/gram.y index 1442fada9397368a18b285285eb005ace62abc61..bcf20fea4317d6e30491ba4444f7d296c5f287ef 100644 --- a/contrib/shark/src/pltsql/gram.y +++ b/contrib/shark/src/pltsql/gram.y @@ -452,7 +452,7 @@ static void HandleBlockLevel(); %token IDENT FCONST SCONST BCONST VCONST XCONST Op CmpOp CmpNullOp COMMENTSTRING SET_USER_IDENT SET_IDENT UNDERSCORE_CHARSET FCONST_F FCONST_D %token ICONST PARAM %token TYPECAST ORA_JOINOP DOT_DOT COLON_EQUALS PARA_EQUALS SET_IDENT_SESSION SET_IDENT_GLOBAL - +%token TSQL_XCONST %token DIALECT_TSQL /* diff --git a/src/bin/libog_query/scripts/extract_source_opengauss_dolphin.rb b/src/bin/libog_query/scripts/extract_source_opengauss_dolphin.rb index 6702fbcf0396c8096e028998222a99f118815c0c..8c59e21b882b794fb7f74005f8651c3f3634b44f 100644 --- a/src/bin/libog_query/scripts/extract_source_opengauss_dolphin.rb +++ b/src/bin/libog_query/scripts/extract_source_opengauss_dolphin.rb @@ -888,6 +888,7 @@ bool raw_expression_tree_walker(Node* node, bool (*walker)(), void* context) case T_Float: case T_String: case T_BitString: + case T_TSQL_HexString: case T_Null: case T_ParamRef: case T_A_Const: diff --git a/src/common/backend/nodes/copyfuncs.cpp b/src/common/backend/nodes/copyfuncs.cpp index e1ad7c4da564a64fc5fb2beaf96cc80f42fa0f34..ab95c22529c27bc5c079333b3bb5bc3ad2adb7d7 100644 --- a/src/common/backend/nodes/copyfuncs.cpp +++ b/src/common/backend/nodes/copyfuncs.cpp @@ -4227,6 +4227,7 @@ static A_Const* _copyAConst(const A_Const* from) case T_Float: case T_String: case T_BitString: + case T_TSQL_HexString: COPY_STRING_FIELD(val.val.str); break; case T_Null: @@ -7215,6 +7216,7 @@ static Value* _copyValue(const Value* from) case T_Float: case T_String: case T_BitString: + case T_TSQL_HexString: COPY_STRING_FIELD(val.str); break; case T_Null: @@ -8669,6 +8671,7 @@ void* copyObject(const void* from) case T_Float: case T_String: case T_BitString: + case T_TSQL_HexString: case T_Null: retval = _copyValue((Value*)from); break; diff --git a/src/common/backend/nodes/equalfuncs.cpp b/src/common/backend/nodes/equalfuncs.cpp index 95943583de22924c3cdc867d0bca83791adf3a4e..b3d2556588d2eb57a853accae73f32871f7b9e9d 100644 --- a/src/common/backend/nodes/equalfuncs.cpp +++ b/src/common/backend/nodes/equalfuncs.cpp @@ -3508,6 +3508,7 @@ static bool _equalValue(const Value* a, const Value* b) case T_Float: case T_String: case T_BitString: + case T_TSQL_HexString: COMPARE_STRING_FIELD(val.str); break; case T_Null: @@ -4042,6 +4043,7 @@ bool equal(const void* a, const void* b) case T_Float: case T_String: case T_BitString: + case T_TSQL_HexString: case T_Null: retval = _equalValue((Value*)a, (Value*)b); break; diff --git a/src/common/backend/nodes/nodeFuncs.cpp b/src/common/backend/nodes/nodeFuncs.cpp index 9ff1d106f7d0721a559f508cbd8b6a45cb22bd95..b86d8635cdc91c4364ec514e9e930b3b95356a5f 100644 --- a/src/common/backend/nodes/nodeFuncs.cpp +++ b/src/common/backend/nodes/nodeFuncs.cpp @@ -1776,6 +1776,7 @@ bool expression_tree_walker(Node* node, bool (*walker)(), void* context) case T_Float: case T_String: case T_BitString: + case T_TSQL_HexString: case T_Null: case T_PgFdwRemoteInfo: case T_Rownum: @@ -3139,6 +3140,7 @@ bool raw_expression_tree_walker(Node* node, bool (*walker)(), void* context) case T_Float: case T_String: case T_BitString: + case T_TSQL_HexString: case T_Null: case T_ParamRef: case T_A_Const: diff --git a/src/common/backend/nodes/nodes.cpp b/src/common/backend/nodes/nodes.cpp index 31f70441968d6f3f1fee93f71d8938f76502832a..1521e17e7ff83c91c0adcc0c1dc44b8de28fa994 100755 --- a/src/common/backend/nodes/nodes.cpp +++ b/src/common/backend/nodes/nodes.cpp @@ -315,6 +315,7 @@ static const TagStr g_tagStrArr[] = {{T_Invalid, "Invalid"}, {T_Float, "Float"}, {T_String, "String"}, {T_BitString, "BitString"}, + {T_TSQL_HexString, "HexString"}, {T_Null, "Null"}, {T_List, "List"}, {T_IntList, "IntList"}, diff --git a/src/common/backend/nodes/outfuncs.cpp b/src/common/backend/nodes/outfuncs.cpp index c4a487b703d2fa1e58ec8ba7ba10dee20d531453..cebee494c449a9a8e25fa7ed93914fe9c2450e11 100755 --- a/src/common/backend/nodes/outfuncs.cpp +++ b/src/common/backend/nodes/outfuncs.cpp @@ -5691,6 +5691,10 @@ static void _outValue(StringInfo str, Value* value) /* internal representation already has leading 'b' */ appendStringInfoString(str, value->val.str); break; + case T_TSQL_HexString: + /* internal representation already has leading '0x' */ + appendStringInfoString(str, value->val.str); + break; case T_Null: /* this is seen only within A_Const, not in transformed trees */ appendStringInfoString(str, "NULL"); diff --git a/src/common/backend/nodes/read.cpp b/src/common/backend/nodes/read.cpp index c574702cc197067d47651dee879efb2880dfe879..ade7ca0b78752eef2dd607ae1d696876af4e8a1d 100644 --- a/src/common/backend/nodes/read.cpp +++ b/src/common/backend/nodes/read.cpp @@ -180,7 +180,7 @@ char* debackslash(const char* token, int length) * nodeTokenType - * returns the type of the node token contained in token. * It returns one of the following valid NodeTags: - * T_Integer, T_Float, T_String, T_BitString + * T_Integer, T_Float, T_String, T_BitString, T_TSQL_HexString, * and some of its own: * RIGHT_PAREN, LEFT_PAREN, LEFT_BRACE, OTHER_TOKEN * @@ -239,6 +239,8 @@ static NodeTag nodeTokenType(char* token, int length) retval = (NodeTag)T_String; else if (*token == 'b') retval = (NodeTag)T_BitString; + else if (*token == '0' && (token[1] == 'x' || token[1] == 'X')) + retval = (NodeTag)T_TSQL_HexString; else retval = (NodeTag)OTHER_TOKEN; return retval; @@ -404,6 +406,14 @@ void* nodeRead(char* token, int tok_len) result = (Node*)makeBitString(val); break; } + case T_TSQL_HexString: { + char *val = (char*)palloc(tok_len); + /* skip leading '0x' */ + memcpy(val, token + 2, tok_len - 2); + val[tok_len - 2] = '\0'; + result = (Node *) makeTSQLHexString(val); + break; + } default: ereport(ERROR, (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), errmsg("unrecognized node type: %d", (int)type))); result = NULL; /* keep compiler happy */ diff --git a/src/common/backend/nodes/value.cpp b/src/common/backend/nodes/value.cpp index e7b7cb199092621105b2a61cb0ad0021281396f2..498a7e01377ec2c7fa6d22254d533e3b91ab7218 100644 --- a/src/common/backend/nodes/value.cpp +++ b/src/common/backend/nodes/value.cpp @@ -79,3 +79,15 @@ Value* makeBitString(char* str) v->val.str = str; return v; } +/* + * makeTSQLHexString + * + * Caller is responsible for passing a palloc'd string. + */ +Value* makeTSQLHexString(char *str) +{ + Value *v = makeNode(Value); + v->type = T_TSQL_HexString; + v->val.str = str; + return v; +} diff --git a/src/common/backend/parser/gram.y b/src/common/backend/parser/gram.y index 21053751e438e60497ec2ba6ce302c8031c8a52c..7d15f435775248d2aae621f31ec1e84f4ada606c 100644 --- a/src/common/backend/parser/gram.y +++ b/src/common/backend/parser/gram.y @@ -313,6 +313,7 @@ static void contain_unsupport_node(Node* node, bool* has_unsupport_default_node) /* Please note that the following line will be replaced with the contents of given file name even if with starting with a comment */ /*$$include "gram-tsql-prologue.y.h"*/ +rewrite_typmod_expr_hook_type rewrite_typmod_expr_hook = NULL; %} %define api.pure @@ -918,6 +919,7 @@ static void contain_unsupport_node(Node* node, bool* has_unsupport_default_node) %token TYPECAST ORA_JOINOP DOT_DOT COLON_EQUALS PARA_EQUALS SET_IDENT_SESSION SET_IDENT_GLOBAL %token DIALECT_TSQL +%token TSQL_XCONST /* * If you want to make any keyword changes, update the keyword table in @@ -27276,7 +27278,13 @@ GenericType: } ; -opt_type_modifiers: '(' expr_list ')' { $$ = $2; } +opt_type_modifiers: '(' expr_list ')' + { + $$ = $2; + if(rewrite_typmod_expr_hook != NULL) { + $$ = (*rewrite_typmod_expr_hook)($2); + } + } | '(' Iconst BYTE_P ')' { $$ = list_make1(makeIntConst($2, @2)); } | '(' Iconst CHAR_P ')' { diff --git a/src/common/backend/parser/parse_node.cpp b/src/common/backend/parser/parse_node.cpp index 92b009dd866ab1871da882a87ef58253f6af5a13..c93f937773b16aedcab5044b2262b1e31ff1827c 100644 --- a/src/common/backend/parser/parse_node.cpp +++ b/src/common/backend/parser/parse_node.cpp @@ -20,6 +20,7 @@ #include "catalog/pg_type.h" #include "mb/pg_wchar.h" #include "nodes/makefuncs.h" +#include "parser/parser.h" // Needed for TSQLHexConstTypmod #include "nodes/nodeFuncs.h" #include "parser/parsetree.h" #include "parser/parse_coerce.h" @@ -32,6 +33,11 @@ #include "utils/varbit.h" static void pcb_error_callback(void* arg); +static Oid lookup_varbinary_oid(); +static Oid lookup_varbinaryin_funcoid(); + +static Oid varbinary_oid = InvalidOid; +static PGFunction varbinaryin_funcaddr = NULL; /* * make_parsestate @@ -191,6 +197,132 @@ static void pcb_error_callback(void* arg) } } +/* + * Get Oid of sys.varbinary from pg_catalog.pg_type. + * Return InvalidOid if it doesn't exist. + */ +static Oid +lookup_varbinary_oid() +{ + int rc; + bool snapshot_set; + char *query; + TupleDesc tupdesc; + HeapTuple row; + bool isnull; + Oid varbinary_oid; + + /* + * Some statement type (i.e. CallStmt) does not captrue the active snapshot. + * (please see (analyze_requires_snapshot().) It may cause a crash while + * excuting a varbinary oid lookup query internally via SPI_execute(). + * If there is no active snapshot, captrue it. + */ + snapshot_set = false; + if (!ActiveSnapshotSet()) + { + PushActiveSnapshot(GetTransactionSnapshot()); + snapshot_set = true; + } + + /* Connect to the SPI manager */ + if ((rc = SPI_connect()) < 0) + elog(ERROR, "SPI_connect() failed in Parse Analyzer " + "with return code %d", rc); + + query = "SELECT T.oid FROM pg_catalog.pg_type T " + "JOIN pg_catalog.pg_namespace N ON N.oid = T.typnamespace " + "WHERE N.nspname = 'sys' AND T.typname = 'varbinary'"; + rc = SPI_execute(query, true, 0); + if (rc != SPI_OK_SELECT) + elog(ERROR, "SPI_execute() failed in Parse Analyzer " + "with return code %d", rc); + + Assert(SPI_processed <= 1); + if (SPI_processed == 1) + { + tupdesc = SPI_tuptable->tupdesc; + row = SPI_tuptable->vals[0]; + varbinary_oid = DatumGetObjectId(SPI_getbinval(row, tupdesc, 1, &isnull)); + } + else + /* sys.varbinary does not exist in pg_type catalog */ + varbinary_oid = InvalidOid; + + /* Cleanup and done */ + SPI_finish(); + + if (snapshot_set) + { + PopActiveSnapshot(); + } + + return varbinary_oid; +} + +/* + * Get Oid of sys.varbinary from pg_catalog.pg_type. + * Return InvalidOid if it doesn't exist. + */ +static Oid +lookup_varbinaryin_funcoid() +{ + int rc; + bool snapshot_set; + char *query; + TupleDesc tupdesc; + HeapTuple row; + bool isnull; + Oid func_oid; + + /* + * Some statement type (i.e. CallStmt) does not captrue the active snapshot. + * (please see (analyze_requires_snapshot().) It may cause a crash while + * excuting a varbinary oid lookup query internally via SPI_execute(). + * If there is no active snapshot, captrue it. + */ + snapshot_set = false; + if (!ActiveSnapshotSet()) + { + PushActiveSnapshot(GetTransactionSnapshot()); + snapshot_set = true; + } + + /* Connect to the SPI manager */ + if ((rc = SPI_connect()) < 0) + elog(ERROR, "SPI_connect() failed in Parse Analyzer " + "with return code %d", rc); + + query = "SELECT P.oid FROM pg_catalog.pg_proc P " + "JOIN pg_catalog.pg_namespace N ON N.oid = P.pronamespace " + "WHERE N.nspname = 'sys' AND P.proname = 'varbinaryin'"; + rc = SPI_execute(query, true, 0); + if (rc != SPI_OK_SELECT) + elog(ERROR, "SPI_execute() failed in Parse Analyzer " + "with return code %d", rc); + + Assert(SPI_processed <= 1); + if (SPI_processed == 1) + { + tupdesc = SPI_tuptable->tupdesc; + row = SPI_tuptable->vals[0]; + func_oid = DatumGetObjectId(SPI_getbinval(row, tupdesc, 1, &isnull)); + } + else + /* sys.varbinary does not exist in pg_type catalog */ + func_oid = InvalidOid; + + /* Cleanup and done */ + SPI_finish(); + + if (snapshot_set) + { + PopActiveSnapshot(); + } + + return func_oid; +} + /* * For timeseries table to identify hidden column type and return NULL or * build a Var node for an attribute identified by RTE and attrno for others. @@ -539,6 +671,37 @@ Const* make_const(ParseState* pstate, Value* value, int location) typelen = -1; typebyval = false; break; + /* Unquoted hex input such as 0x1F, process it as type sys.VARBINARY */ + case T_TSQL_HexString: + /* Lookup Oid of type sys.varbinary and the input function sys.varbinaryin */ + if (varbinary_oid == InvalidOid) + { + if ((varbinary_oid = lookup_varbinary_oid()) != InvalidOid) + { + Oid varbinaryin_funcoid = lookup_varbinaryin_funcoid(); + varbinaryin_funcaddr = + lookup_C_func_by_oid(varbinaryin_funcoid, "$libdir/shark", "varbinaryin"); + } + } + + if (varbinary_oid == InvalidOid || varbinaryin_funcaddr == NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("Type VARBINARY is not supported for input %s.", value->val.str), + errhint("Install babelfishpg_tsql extension to support Type VARBINARY"))); + + /* arrange to report location if the input function fails */ + setup_parser_errposition_callback(&pcbstate, pstate, location); + val = DirectFunctionCall3(varbinaryin_funcaddr, + CStringGetDatum(value->val.str), + ObjectIdGetDatum(InvalidOid), + Int32GetDatum(TSQLHexConstTypmod) + ); + cancel_parser_errposition_callback(&pcbstate); + typid = varbinary_oid; + typelen = -1; + typebyval = false; + break; case T_Null: /* return a null const */ diff --git a/src/common/backend/utils/fmgr/fmgr.cpp b/src/common/backend/utils/fmgr/fmgr.cpp index 615bfc67e151b0b11051149a9a0367034fa8deb8..42e905f462a91abd1f2396ecaf57a4050aaecff8 100755 --- a/src/common/backend/utils/fmgr/fmgr.cpp +++ b/src/common/backend/utils/fmgr/fmgr.cpp @@ -745,6 +745,40 @@ static CFuncHashTabEntry* lookup_C_func(HeapTuple procedureTuple) return NULL; /* entry is out of date */ } + +PGFunction +lookup_C_func_by_oid(Oid fn_oid, char* probinstring, char* prosrcstring) +{ + PGFunction user_fn = NULL; + CFunInfo c_fn; + + /* Lookup hash table CFuncHash If Functions are already cached */ + if (CFuncHash != NULL) + { + CFuncHashTabEntry *entry; + + entry = (CFuncHashTabEntry *) + hash_search(CFuncHash, + &fn_oid, + HASH_FIND, + NULL); + if (entry == NULL) + user_fn = NULL; /* no such entry */ + else + user_fn = entry->user_fn; /* OK */ + } + /* + * If haven't found using CFuncHash hash table, + * load the dynamically linked library to get the function + */ + if (user_fn == NULL) { + c_fn = load_external_function(probinstring, prosrcstring, true, NULL); + user_fn = c_fn.user_fn; + } + + return user_fn; +} + /* * record_C_func: enter (or update) info about a C function in the hash table */ diff --git a/src/include/fmgr.h b/src/include/fmgr.h index eededd2085a000132374d52558bef17852aa8c62..b275276454f0a3ba466ad00f2eecdb9e5277fcca 100644 --- a/src/include/fmgr.h +++ b/src/include/fmgr.h @@ -603,6 +603,8 @@ extern bool get_call_expr_arg_stable(fmNodePtr expr, int argnum); extern bool get_fn_expr_variadic(FmgrInfo* flinfo); extern bool CheckFunctionValidatorAccess(Oid validatorOid, Oid functionOid); +extern PGFunction lookup_C_func_by_oid(Oid fn_oid, char* probin, char* prosrc); + extern void check_external_function(const char* filepath, const char* filename, const char* funcname); extern CFunInfo load_external_function(const char* filename, char* funcname, bool signalNotFound, bool isValidate); extern char* expand_dynamic_library_name(const char* name); diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index f0ab195f4ac3777e8b5c0d2a9d167514e55e45de..b41c1a0b332a186c8941aab3ff09c04b02e73d1c 100755 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -425,6 +425,7 @@ typedef enum NodeTag { T_Float, T_String, T_BitString, + T_TSQL_HexString, T_Null, T_Nan, diff --git a/src/include/nodes/value.h b/src/include/nodes/value.h index 118525b087f9459edede846f489a9fd600561759..b6c8e66e6b577d9de109433f08d197584edcd69c 100644 --- a/src/include/nodes/value.h +++ b/src/include/nodes/value.h @@ -55,5 +55,6 @@ extern Value* makeInteger(long i); extern Value* makeFloat(char* numericStr); extern Value* makeString(char* str); extern Value* makeBitString(char* str); +extern Value* makeTSQLHexString(char *str); #endif /* VALUE_H */ diff --git a/src/include/parser/parser.h b/src/include/parser/parser.h index bafee71e4aab8ffa6aac30319020753dfc2e1dff..f3ac94620e8739978b8385a623eb99fad452568d 100644 --- a/src/include/parser/parser.h +++ b/src/include/parser/parser.h @@ -46,5 +46,13 @@ extern void fixResTargetNameWithAlias(List* clause_list, const char* aliasname); extern char* EscapeQuotes(const char* src); extern Oid get_func_oid(const char* funcname, Oid funcnamespace, Expr* expr, bool noPkg); +/* Hooks needed in grammar rule in gram.y */ +typedef List * (*rewrite_typmod_expr_hook_type) (List *expr_list); +extern PGDLLEXPORT rewrite_typmod_expr_hook_type rewrite_typmod_expr_hook; + +/* define for varbinary */ +#define TSQLMaxTypmod -8000 +#define TSQLMaxNumPrecision 38 +#define TSQLHexConstTypmod -16 #endif /* PARSER_H */