Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA384:256) (Exim 4.89) (envelope-from ) id 1esBYn-0008AX-Tc for pgsql-hackers@arkaria.postgresql.org; Sat, 03 Mar 2018 18:07:06 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.89) (envelope-from ) id 1esBYm-0007UO-QH for pgsql-hackers@arkaria.postgresql.org; Sat, 03 Mar 2018 18:07:04 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA384:256) (Exim 4.89) (envelope-from ) id 1esBV8-0002Ku-T3 for pgsql-hackers@lists.postgresql.org; Sat, 03 Mar 2018 18:03:19 +0000 Received: from mail-wr0-x243.google.com ([2a00:1450:400c:c0c::243]) by magus.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1esBV4-0006X1-0L for pgsql-hackers@postgresql.org; Sat, 03 Mar 2018 18:03:17 +0000 Received: by mail-wr0-x243.google.com with SMTP id k9so13162789wre.9 for ; Sat, 03 Mar 2018 10:03:13 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=2ndquadrant-com.20150623.gappssmtp.com; s=20150623; h=subject:from:to:cc:references:message-id:date:user-agent :mime-version:in-reply-to:content-language; bh=RR9Ca8Ykspjw+FRhHzcF2c7uFDcH/4OKYss01TY4j0M=; b=NrBGkcT3QdmTU7HeABZqMbDmwyduk1nTzZ/XXTL3QXIvUfmSi3kN7pPM+HT0XYmvoi 1Gp2QGffmhs3CcNRuPSk9TQBk/lClfh1dCQAAuC4p2QBwjgSFbyDnlsGJ3C8kbrg7inv pk84R/GBAfLYb8kCfTXOvZWro2NoUcx8fFs+YsG3yw5t13or5egqcDx2AQN24EPW69pb VxT3jW6R0p4I0AFoRwcZzKjMp/okYWhm8XORz/FukZgQGNGBeRumkDQTHhB0joMdoNji DpecqRjEKTZshUCLFM5b5vlNsyK0WOtHcD1az5e2Uh0PCaISI3sedr8yhzRcA4qqWFgK WUxg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:subject:from:to:cc:references:message-id:date :user-agent:mime-version:in-reply-to:content-language; bh=RR9Ca8Ykspjw+FRhHzcF2c7uFDcH/4OKYss01TY4j0M=; b=KSvkVjygu5Y+GcbJ4t/sLOevALxSfQTFAwJt75HKeLmhyi45He0KFTfltjPZOdO41L mfcxhb/DvilWoWMJsh1aa3HUsyrj8vbGcq++QLgGX3QdxF1gUMXUGZtn96NAiRs4amob BHkBgxI2QLGu/rkcy1DnMOnQUFLQGwTz69TK8HYAQhARE03/d2945+SBx5j6ighh5nFQ JkNbUg91deOk906+7A0bI0540DMVC96SQSxnL26CXX95VbGPNDIbAgWYrDT9x6pUIi27 TO8/B0XbMvt1NhH5+/HPvREwsi5bFhsXKsZy2ItA9qDj9i/gvERnsm+VHb4EO7cuomOW xZaQ== X-Gm-Message-State: APf1xPApZ8erm9lMNkMQ3Hfz6Qlc9HfyELQ2H0B3hgAk/te1E/D+083X 8e28nEgsIQKkUubfNGu8D60yUE/LctgnHoM/E3/aXwqv/ta/zXtLhCh+Vu8vSye3OMDNRHH1VNU SVVa6/EpYAZnCNPqYDIIJo4EyLTxCKEMdgufiCRURsH3M2NzCkA7/Acudn00G7aO5ghzX8OYXBl 7gTqhM7Wjz X-Google-Smtp-Source: AG47ELs1KOwdUm59kQ//crvyZ8096+m4Tobc7bl9U/iEwBGsDQSbNJgl5QT0YfLgPm+FPUU6YBMrrg== X-Received: by 10.223.146.133 with SMTP id 5mr8505913wrn.109.1520100191896; Sat, 03 Mar 2018 10:03:11 -0800 (PST) Received: from [10.137.2.19] (ip-78-102-97-226.net.upcbroadband.cz. [78.102.97.226]) by smtp.gmail.com with ESMTPSA id p29sm5144616wmf.3.2018.03.03.10.03.10 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sat, 03 Mar 2018 10:03:11 -0800 (PST) Subject: Re: [HACKERS] user-defined numeric data types triggering ERROR: unsupported type From: Tomas Vondra To: Tom Lane Cc: PostgreSQL-development References: <9389.1506003842@sss.pgh.pa.us> <4b444362-4240-49e0-3523-724e5932b060@2ndquadrant.com> <24767.1506180446@sss.pgh.pa.us> <14597.1520038563@sss.pgh.pa.us> <42f4e37e-f923-6ea9-4172-cdec5a8c2d25@2ndquadrant.com> Message-ID: Date: Sat, 3 Mar 2018 19:03:07 +0100 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.4.0 MIME-Version: 1.0 In-Reply-To: <42f4e37e-f923-6ea9-4172-cdec5a8c2d25@2ndquadrant.com> Content-Type: multipart/mixed; boundary="------------23DF37F91CFE0EE510DA46FD" Content-Language: en-US List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Precedence: bulk This is a multi-part message in MIME format. --------------23DF37F91CFE0EE510DA46FD Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 7bit Hi, Here is v2 of the fix. It does handle all the convert_to_scalar calls for various data types, just like the numeric one did in v1 with the exception of bytea. The bytea case is fixed by checking that the boundary values are varlenas. This seems better than checking for BYTEAOID explicitly, which would fail for custom varlena-based types. At first I've been thinking there might be issues when the data types has mismatching ordering, but I don't think the patch makes it any worse. I've also added a bunch of regression tests, checking each case. The bytea test it should cause segfault on master, of course. regards -- Tomas Vondra http://www.2ndQuadrant.com PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services --------------23DF37F91CFE0EE510DA46FD Content-Type: text/x-patch; name="unsupported-type-fix-v2.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="unsupported-type-fix-v2.patch" diff --git a/src/backend/utils/adt/network.c b/src/backend/utils/adt/network.c index aac7621..34cf336 100644 --- a/src/backend/utils/adt/network.c +++ b/src/backend/utils/adt/network.c @@ -904,7 +904,7 @@ inet_merge(PG_FUNCTION_ARGS) * involving network types. */ double -convert_network_to_scalar(Datum value, Oid typid) +convert_network_to_scalar(Datum value, Oid typid, bool *unknown_type) { switch (typid) { @@ -960,7 +960,7 @@ convert_network_to_scalar(Datum value, Oid typid) * Can't get here unless someone tries to use scalarineqsel() on an * operator with one network and one non-network operand. */ - elog(ERROR, "unsupported type: %u", typid); + *unknown_type = true; return 0; } diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index fcc8323..b210541 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -176,7 +176,7 @@ static bool estimate_multivariate_ndistinct(PlannerInfo *root, static bool convert_to_scalar(Datum value, Oid valuetypid, double *scaledvalue, Datum lobound, Datum hibound, Oid boundstypid, double *scaledlobound, double *scaledhibound); -static double convert_numeric_to_scalar(Datum value, Oid typid); +static double convert_numeric_to_scalar(Datum value, Oid typid, bool *unknown_type); static void convert_string_to_scalar(char *value, double *scaledvalue, char *lobound, @@ -193,8 +193,9 @@ static double convert_one_string_to_scalar(char *value, int rangelo, int rangehi); static double convert_one_bytea_to_scalar(unsigned char *value, int valuelen, int rangelo, int rangehi); -static char *convert_string_datum(Datum value, Oid typid); -static double convert_timevalue_to_scalar(Datum value, Oid typid); +static char *convert_string_datum(Datum value, Oid typid, bool *unknown_type); +static double convert_timevalue_to_scalar(Datum value, Oid typid, + bool *unknown_type); static void examine_simple_variable(PlannerInfo *root, Var *var, VariableStatData *vardata); static bool get_variable_range(PlannerInfo *root, VariableStatData *vardata, @@ -4071,10 +4072,18 @@ convert_to_scalar(Datum value, Oid valuetypid, double *scaledvalue, case REGDICTIONARYOID: case REGROLEOID: case REGNAMESPACEOID: - *scaledvalue = convert_numeric_to_scalar(value, valuetypid); - *scaledlobound = convert_numeric_to_scalar(lobound, boundstypid); - *scaledhibound = convert_numeric_to_scalar(hibound, boundstypid); - return true; + { + bool unknown_type = false; + + *scaledvalue = convert_numeric_to_scalar(value, valuetypid, + &unknown_type); + *scaledlobound = convert_numeric_to_scalar(lobound, boundstypid, + &unknown_type); + *scaledhibound = convert_numeric_to_scalar(hibound, boundstypid, + &unknown_type); + + return (!unknown_type); + } /* * Built-in string types @@ -4085,9 +4094,18 @@ convert_to_scalar(Datum value, Oid valuetypid, double *scaledvalue, case TEXTOID: case NAMEOID: { - char *valstr = convert_string_datum(value, valuetypid); - char *lostr = convert_string_datum(lobound, boundstypid); - char *histr = convert_string_datum(hibound, boundstypid); + bool unknown_type = false; + + char *valstr = convert_string_datum(value, valuetypid, + &unknown_type); + char *lostr = convert_string_datum(lobound, boundstypid, + &unknown_type); + char *histr = convert_string_datum(hibound, boundstypid, + &unknown_type); + + /* bail out if any of the values is not a string */ + if (unknown_type) + return false; convert_string_to_scalar(valstr, scaledvalue, lostr, scaledlobound, @@ -4103,6 +4121,18 @@ convert_to_scalar(Datum value, Oid valuetypid, double *scaledvalue, */ case BYTEAOID: { + TypeCacheEntry *typcache; + + /* + * Ensure parameters passed to convert_bytea_to_scalar are + * all varlena. We're dealing with bytea values, but this + * seems like a reasonable way to handle custom types. + */ + typcache = lookup_type_cache(boundstypid, 0); + + if (typcache->typlen != -1) + return false; + convert_bytea_to_scalar(value, scaledvalue, lobound, scaledlobound, hibound, scaledhibound); @@ -4121,10 +4151,18 @@ convert_to_scalar(Datum value, Oid valuetypid, double *scaledvalue, case TINTERVALOID: case TIMEOID: case TIMETZOID: - *scaledvalue = convert_timevalue_to_scalar(value, valuetypid); - *scaledlobound = convert_timevalue_to_scalar(lobound, boundstypid); - *scaledhibound = convert_timevalue_to_scalar(hibound, boundstypid); - return true; + { + bool unknown_type = false; + + *scaledvalue = convert_timevalue_to_scalar(value, valuetypid, + &unknown_type); + *scaledlobound = convert_timevalue_to_scalar(lobound, boundstypid, + &unknown_type); + *scaledhibound = convert_timevalue_to_scalar(hibound, boundstypid, + &unknown_type); + + return (!unknown_type); + } /* * Built-in network types @@ -4133,10 +4171,18 @@ convert_to_scalar(Datum value, Oid valuetypid, double *scaledvalue, case CIDROID: case MACADDROID: case MACADDR8OID: - *scaledvalue = convert_network_to_scalar(value, valuetypid); - *scaledlobound = convert_network_to_scalar(lobound, boundstypid); - *scaledhibound = convert_network_to_scalar(hibound, boundstypid); - return true; + { + bool unknown_type = false; + + *scaledvalue = convert_network_to_scalar(value, valuetypid, + &unknown_type); + *scaledlobound = convert_network_to_scalar(lobound, boundstypid, + &unknown_type); + *scaledhibound = convert_network_to_scalar(hibound, boundstypid, + &unknown_type); + + return (!unknown_type); + } } /* Don't know how to convert */ *scaledvalue = *scaledlobound = *scaledhibound = 0; @@ -4147,7 +4193,7 @@ convert_to_scalar(Datum value, Oid valuetypid, double *scaledvalue, * Do convert_to_scalar()'s work for any numeric data type. */ static double -convert_numeric_to_scalar(Datum value, Oid typid) +convert_numeric_to_scalar(Datum value, Oid typid, bool *unknown_type) { switch (typid) { @@ -4185,9 +4231,11 @@ convert_numeric_to_scalar(Datum value, Oid typid) /* * Can't get here unless someone tries to use scalarineqsel() on an - * operator with one numeric and one non-numeric operand. + * operator with one numeric and one non-numeric operand. Or more + * precisely, operand that we don't know how to transform to scalar. */ - elog(ERROR, "unsupported type: %u", typid); + *unknown_type = true; + return 0; } @@ -4340,7 +4388,7 @@ convert_one_string_to_scalar(char *value, int rangelo, int rangehi) * before continuing, so as to generate correct locale-specific results. */ static char * -convert_string_datum(Datum value, Oid typid) +convert_string_datum(Datum value, Oid typid, bool *unknown_type) { char *val; @@ -4369,7 +4417,7 @@ convert_string_datum(Datum value, Oid typid) * Can't get here unless someone tries to use scalarineqsel() on * an operator with one string and one non-string operand. */ - elog(ERROR, "unsupported type: %u", typid); + *unknown_type = true; return NULL; } @@ -4524,7 +4572,7 @@ convert_one_bytea_to_scalar(unsigned char *value, int valuelen, * Do convert_to_scalar()'s work for any timevalue data type. */ static double -convert_timevalue_to_scalar(Datum value, Oid typid) +convert_timevalue_to_scalar(Datum value, Oid typid, bool *unknown_type) { switch (typid) { @@ -4574,7 +4622,7 @@ convert_timevalue_to_scalar(Datum value, Oid typid) * Can't get here unless someone tries to use scalarineqsel() on an * operator with one timevalue and one non-timevalue operand. */ - elog(ERROR, "unsupported type: %u", typid); + *unknown_type = true; return 0; } diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 3e462f1..630eeba 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -103,7 +103,8 @@ extern int inet_net_pton(int af, const char *src, void *dst, size_t size); /* network.c */ -extern double convert_network_to_scalar(Datum value, Oid typid); +extern double convert_network_to_scalar(Datum value, Oid typid, + bool *unknown_type); extern Datum network_scan_first(Datum in); extern Datum network_scan_last(Datum in); extern void clean_ipv6_addr(int addr_family, char *addr); diff --git a/src/test/regress/expected/convert_to_scalar.out b/src/test/regress/expected/convert_to_scalar.out new file mode 100644 index 0000000..7b4fe2b --- /dev/null +++ b/src/test/regress/expected/convert_to_scalar.out @@ -0,0 +1,105 @@ +-- tables used in these tests +create table scalar_text (a text); +insert into scalar_text select i::text from generate_series(1,1000) s(i); +analyze scalar_text; +create table scalar_int (a int); +insert into scalar_int select i from generate_series(1,1000) s(i); +analyze scalar_int; +-- convert_bytea_to_scalar +CREATE OR REPLACE FUNCTION int_bytea_proc(int, bytea) RETURNS bool AS $$ +BEGIN + RETURN ($1 > 500); +END; +$$ LANGUAGE plpgsql; +CREATE OPERATOR > ( + LEFTARG = int, + RIGHTARG = bytea, + PROCEDURE = int_bytea_proc, + RESTRICT = scalargtsel +); +EXPLAIN (COSTS OFF) SELECT * FROM scalar_int +WHERE a > E'\x37666565353'::bytea; + QUERY PLAN +------------------------------------------------- + Seq Scan on scalar_int + Filter: (a > '\x37363636353635333533'::bytea) +(2 rows) + +-- convert_numeric_to_scalar +CREATE OR REPLACE FUNCTION text_int_proc(text, int) RETURNS bool AS $$ +BEGIN + RETURN ($1::int > 500); +END; +$$ LANGUAGE plpgsql; +CREATE OPERATOR > ( + LEFTARG = text, + RIGHTARG = int, + PROCEDURE = text_int_proc, + RESTRICT = scalargtsel +); +EXPLAIN (COSTS OFF) SELECT * FROM scalar_text WHERE a > 30; + QUERY PLAN +------------------------- + Seq Scan on scalar_text + Filter: (a > 30) +(2 rows) + +-- convert_string_datum +CREATE OR REPLACE FUNCTION int_text_proc(int, text) RETURNS bool AS $$ +BEGIN + RETURN ($1 > 500); +END; +$$ LANGUAGE plpgsql; +CREATE OPERATOR > ( + LEFTARG = int, + RIGHTARG = text, + PROCEDURE = int_text_proc, + RESTRICT = scalargtsel +); +EXPLAIN (COSTS OFF) SELECT * FROM scalar_int WHERE a > '30'::text; + QUERY PLAN +---------------------------- + Seq Scan on scalar_int + Filter: (a > '30'::text) +(2 rows) + +-- convert_timevalue_to_scalar +CREATE OR REPLACE FUNCTION int_timestamp_proc(int, timestamptz) RETURNS bool AS $$ +BEGIN + RETURN ($1 > 500); +END; +$$ LANGUAGE plpgsql; +CREATE OPERATOR > ( + LEFTARG = int, + RIGHTARG = timestamptz, + PROCEDURE = int_timestamp_proc, + RESTRICT = scalargtsel +); +EXPLAIN (COSTS OFF) SELECT * FROM scalar_int WHERE a > now(); + QUERY PLAN +------------------------ + Seq Scan on scalar_int + Filter: (a > now()) +(2 rows) + +-- convert_network_to_scalar +CREATE OR REPLACE FUNCTION int_inet_proc(int, inet) RETURNS bool AS $$ +BEGIN + RETURN ($1 > 500); +END; +$$ LANGUAGE plpgsql; +CREATE OPERATOR > ( + LEFTARG = int, + RIGHTARG = inet, + PROCEDURE = int_inet_proc, + RESTRICT = scalargtsel +); +EXPLAIN (COSTS OFF) SELECT * FROM scalar_int WHERE a > '127.0.0.1'::inet; + QUERY PLAN +----------------------------------- + Seq Scan on scalar_int + Filter: (a > '127.0.0.1'::inet) +(2 rows) + +DROP TABLE scalar_int; +DROP TABLE scalar_text; diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index ad9434f..019dcf5 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -60,7 +60,7 @@ test: create_index create_view # ---------- # Another group of parallel tests # ---------- -test: create_aggregate create_function_3 create_cast constraints triggers inherit create_table_like typed_table vacuum drop_if_exists updatable_views rolenames roleattributes create_am hash_func +test: create_aggregate create_function_3 create_cast constraints triggers inherit create_table_like typed_table vacuum drop_if_exists updatable_views rolenames roleattributes create_am hash_func convert_to_scalar # ---------- # sanity_check does a vacuum, affecting the sort order of SELECT * diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index 27cd498..050421e 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -69,6 +69,7 @@ test: create_view test: create_aggregate test: create_function_3 test: create_cast +test: convert_to_scalar test: constraints test: triggers test: inherit diff --git a/src/test/regress/sql/convert_to_scalar.sql b/src/test/regress/sql/convert_to_scalar.sql new file mode 100644 index 0000000..f984f48 --- /dev/null +++ b/src/test/regress/sql/convert_to_scalar.sql @@ -0,0 +1,105 @@ +-- tables used in these tests + +create table scalar_text (a text); + +insert into scalar_text select i::text from generate_series(1,1000) s(i); + +analyze scalar_text; + + +create table scalar_int (a int); + +insert into scalar_int select i from generate_series(1,1000) s(i); + +analyze scalar_int; + + +-- convert_bytea_to_scalar + +CREATE OR REPLACE FUNCTION int_bytea_proc(int, bytea) RETURNS bool AS $$ +BEGIN + RETURN ($1 > 500); +END; +$$ LANGUAGE plpgsql; + +CREATE OPERATOR > ( + LEFTARG = int, + RIGHTARG = bytea, + PROCEDURE = int_bytea_proc, + RESTRICT = scalargtsel +); + +EXPLAIN (COSTS OFF) SELECT * FROM scalar_int +WHERE a > E'\x37666565353'::bytea; + + +-- convert_numeric_to_scalar + +CREATE OR REPLACE FUNCTION text_int_proc(text, int) RETURNS bool AS $$ +BEGIN + RETURN ($1::int > 500); +END; +$$ LANGUAGE plpgsql; + +CREATE OPERATOR > ( + LEFTARG = text, + RIGHTARG = int, + PROCEDURE = text_int_proc, + RESTRICT = scalargtsel +); + +EXPLAIN (COSTS OFF) SELECT * FROM scalar_text WHERE a > 30; + +-- convert_string_datum + +CREATE OR REPLACE FUNCTION int_text_proc(int, text) RETURNS bool AS $$ +BEGIN + RETURN ($1 > 500); +END; +$$ LANGUAGE plpgsql; + +CREATE OPERATOR > ( + LEFTARG = int, + RIGHTARG = text, + PROCEDURE = int_text_proc, + RESTRICT = scalargtsel +); + +EXPLAIN (COSTS OFF) SELECT * FROM scalar_int WHERE a > '30'::text; + +-- convert_timevalue_to_scalar + +CREATE OR REPLACE FUNCTION int_timestamp_proc(int, timestamptz) RETURNS bool AS $$ +BEGIN + RETURN ($1 > 500); +END; +$$ LANGUAGE plpgsql; + +CREATE OPERATOR > ( + LEFTARG = int, + RIGHTARG = timestamptz, + PROCEDURE = int_timestamp_proc, + RESTRICT = scalargtsel +); + +EXPLAIN (COSTS OFF) SELECT * FROM scalar_int WHERE a > now(); + +-- convert_network_to_scalar + +CREATE OR REPLACE FUNCTION int_inet_proc(int, inet) RETURNS bool AS $$ +BEGIN + RETURN ($1 > 500); +END; +$$ LANGUAGE plpgsql; + +CREATE OPERATOR > ( + LEFTARG = int, + RIGHTARG = inet, + PROCEDURE = int_inet_proc, + RESTRICT = scalargtsel +); + +EXPLAIN (COSTS OFF) SELECT * FROM scalar_int WHERE a > '127.0.0.1'::inet; + +DROP TABLE scalar_int; +DROP TABLE scalar_text; --------------23DF37F91CFE0EE510DA46FD--