mirror of
https://github.com/postgres/postgres.git
synced 2025-07-28 23:42:10 +03:00
Prevent invoking I/O conversion casts via functional/attribute notation.
PG 8.4 added a built-in feature for casting pretty much any data type to string types (text, varchar, etc). We allowed this to work in any of the historically-allowed syntaxes: CAST(x AS text), x::text, text(x), or x.text. However, multiple complaints have shown that it's too easy to invoke such casts unintentionally in the latter two styles, particularly field selection. To cure the problem with the narrowest possible change of behavior, disallow use of I/O conversion casts from composite types to string types via functional/attribute syntax. The new functionality is still available via cast syntax. In passing, document the equivalence of functional and attribute syntax in a more visible place.
This commit is contained in:
@ -985,8 +985,13 @@ func_get_detail(List *funcname,
|
||||
* can't write "foo[] (something)" as a function call. In theory
|
||||
* someone might want to invoke it as "_foo (something)" but we have
|
||||
* never supported that historically, so we can insist that people
|
||||
* write it as a normal cast instead. Lack of historical support is
|
||||
* also the reason for not considering composite-type casts here.
|
||||
* write it as a normal cast instead.
|
||||
*
|
||||
* We also reject the specific case of COERCEVIAIO for a composite
|
||||
* source type and a string-category target type. This is a case that
|
||||
* find_coercion_pathway() allows by default, but experience has shown
|
||||
* that it's too commonly invoked by mistake. So, again, insist that
|
||||
* people use cast syntax if they want to do that.
|
||||
*
|
||||
* NB: it's important that this code does not exceed what coerce_type
|
||||
* can do, because the caller will try to apply coerce_type if we
|
||||
@ -1017,8 +1022,23 @@ func_get_detail(List *funcname,
|
||||
cpathtype = find_coercion_pathway(targetType, sourceType,
|
||||
COERCION_EXPLICIT,
|
||||
&cfuncid);
|
||||
iscoercion = (cpathtype == COERCION_PATH_RELABELTYPE ||
|
||||
cpathtype == COERCION_PATH_COERCEVIAIO);
|
||||
switch (cpathtype)
|
||||
{
|
||||
case COERCION_PATH_RELABELTYPE:
|
||||
iscoercion = true;
|
||||
break;
|
||||
case COERCION_PATH_COERCEVIAIO:
|
||||
if ((sourceType == RECORDOID ||
|
||||
ISCOMPLEX(sourceType)) &&
|
||||
TypeCategory(targetType) == TYPCATEGORY_STRING)
|
||||
iscoercion = false;
|
||||
else
|
||||
iscoercion = true;
|
||||
break;
|
||||
default:
|
||||
iscoercion = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (iscoercion)
|
||||
|
@ -324,3 +324,49 @@ select * from price;
|
||||
(3 rows)
|
||||
|
||||
rollback;
|
||||
--
|
||||
-- We allow I/O conversion casts from composite types to strings to be
|
||||
-- invoked via cast syntax, but not functional syntax. This is because
|
||||
-- the latter is too prone to be invoked unintentionally.
|
||||
--
|
||||
select cast (fullname as text) from fullname;
|
||||
fullname
|
||||
----------
|
||||
(0 rows)
|
||||
|
||||
select fullname::text from fullname;
|
||||
fullname
|
||||
----------
|
||||
(0 rows)
|
||||
|
||||
select text(fullname) from fullname; -- error
|
||||
ERROR: function text(fullname) does not exist
|
||||
LINE 1: select text(fullname) from fullname;
|
||||
^
|
||||
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
||||
select fullname.text from fullname; -- error
|
||||
ERROR: column fullname.text does not exist
|
||||
LINE 1: select fullname.text from fullname;
|
||||
^
|
||||
-- same, but RECORD instead of named composite type:
|
||||
select cast (row('Jim', 'Beam') as text);
|
||||
row
|
||||
------------
|
||||
(Jim,Beam)
|
||||
(1 row)
|
||||
|
||||
select (row('Jim', 'Beam'))::text;
|
||||
row
|
||||
------------
|
||||
(Jim,Beam)
|
||||
(1 row)
|
||||
|
||||
select text(row('Jim', 'Beam')); -- error
|
||||
ERROR: function text(record) does not exist
|
||||
LINE 1: select text(row('Jim', 'Beam'));
|
||||
^
|
||||
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
||||
select (row('Jim', 'Beam')).text; -- error
|
||||
ERROR: could not identify column "text" in record data type
|
||||
LINE 1: select (row('Jim', 'Beam')).text;
|
||||
^
|
||||
|
@ -157,3 +157,18 @@ UPDATE price
|
||||
select * from price;
|
||||
|
||||
rollback;
|
||||
|
||||
--
|
||||
-- We allow I/O conversion casts from composite types to strings to be
|
||||
-- invoked via cast syntax, but not functional syntax. This is because
|
||||
-- the latter is too prone to be invoked unintentionally.
|
||||
--
|
||||
select cast (fullname as text) from fullname;
|
||||
select fullname::text from fullname;
|
||||
select text(fullname) from fullname; -- error
|
||||
select fullname.text from fullname; -- error
|
||||
-- same, but RECORD instead of named composite type:
|
||||
select cast (row('Jim', 'Beam') as text);
|
||||
select (row('Jim', 'Beam'))::text;
|
||||
select text(row('Jim', 'Beam')); -- error
|
||||
select (row('Jim', 'Beam')).text; -- error
|
||||
|
Reference in New Issue
Block a user