Skip to content

Commit 5bbee34

Browse files
committed
Avoid producing over-length specific_name outputs in information_schema.
information_schema output columns that are declared as being type sql_identifier are supposed to conform to the implementation's rules for valid identifiers, in particular the identifier length limit. Several places potentially violated this limit by concatenating a function's name and OID. (The OID is added to ensure name uniqueness within a schema, since the spec doesn't expect function name overloading.) Simply truncating the concatenation result to fit in "name" won't do, since losing part of the OID might wind up giving non-unique results. Instead, let's truncate the function name as necessary. The most practical way to do that is to do it in a C function; the information_schema.sql script doesn't have easy access to the value of NAMEDATALEN, nor does it have an easy way to truncate on the basis of resulting byte-length rather than number of characters. (There are still a couple of places that cast concatenation results to sql_identifier, but as far as I can see they are guaranteed not to produce over-length strings, at least with the normal value of NAMEDATALEN.) Discussion: https://postgr.es/m/23817.1545283477@sss.pgh.pa.us
1 parent 7b14bcc commit 5bbee34

File tree

4 files changed

+51
-10
lines changed

4 files changed

+51
-10
lines changed

src/backend/catalog/information_schema.sql

+11-9
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ CREATE VIEW check_constraint_routine_usage AS
416416
CAST(c.conname AS sql_identifier) AS constraint_name,
417417
CAST(current_database() AS sql_identifier) AS specific_catalog,
418418
CAST(np.nspname AS sql_identifier) AS specific_schema,
419-
CAST(p.proname || '_' || CAST(p.oid AS text) AS sql_identifier) AS specific_name
419+
CAST(nameconcatoid(p.proname, p.oid) AS sql_identifier) AS specific_name
420420
FROM pg_namespace nc, pg_constraint c, pg_depend d, pg_proc p, pg_namespace np
421421
WHERE nc.oid = c.connamespace
422422
AND c.contype = 'c'
@@ -1115,7 +1115,7 @@ GRANT SELECT ON key_column_usage TO PUBLIC;
11151115
CREATE VIEW parameters AS
11161116
SELECT CAST(current_database() AS sql_identifier) AS specific_catalog,
11171117
CAST(n_nspname AS sql_identifier) AS specific_schema,
1118-
CAST(proname || '_' || CAST(p_oid AS text) AS sql_identifier) AS specific_name,
1118+
CAST(nameconcatoid(proname, p_oid) AS sql_identifier) AS specific_name,
11191119
CAST((ss.x).n AS cardinal_number) AS ordinal_position,
11201120
CAST(
11211121
CASE WHEN proargmodes IS NULL THEN 'IN'
@@ -1320,7 +1320,7 @@ CREATE VIEW routine_privileges AS
13201320
CAST(grantee.rolname AS sql_identifier) AS grantee,
13211321
CAST(current_database() AS sql_identifier) AS specific_catalog,
13221322
CAST(n.nspname AS sql_identifier) AS specific_schema,
1323-
CAST(p.proname || '_' || CAST(p.oid AS text) AS sql_identifier) AS specific_name,
1323+
CAST(nameconcatoid(p.proname, p.oid) AS sql_identifier) AS specific_name,
13241324
CAST(current_database() AS sql_identifier) AS routine_catalog,
13251325
CAST(n.nspname AS sql_identifier) AS routine_schema,
13261326
CAST(p.proname AS sql_identifier) AS routine_name,
@@ -1409,7 +1409,7 @@ GRANT SELECT ON role_routine_grants TO PUBLIC;
14091409
CREATE VIEW routines AS
14101410
SELECT CAST(current_database() AS sql_identifier) AS specific_catalog,
14111411
CAST(n.nspname AS sql_identifier) AS specific_schema,
1412-
CAST(p.proname || '_' || CAST(p.oid AS text) AS sql_identifier) AS specific_name,
1412+
CAST(nameconcatoid(p.proname, p.oid) AS sql_identifier) AS specific_name,
14131413
CAST(current_database() AS sql_identifier) AS routine_catalog,
14141414
CAST(n.nspname AS sql_identifier) AS routine_schema,
14151415
CAST(p.proname AS sql_identifier) AS routine_name,
@@ -1964,7 +1964,7 @@ CREATE VIEW transforms AS
19641964
CAST(t.typname AS sql_identifier) AS udt_name,
19651965
CAST(current_database() AS sql_identifier) AS specific_catalog,
19661966
CAST(np.nspname AS sql_identifier) AS specific_schema,
1967-
CAST(p.proname || '_' || CAST(p.oid AS text) AS sql_identifier) AS specific_name,
1967+
CAST(nameconcatoid(p.proname, p.oid) AS sql_identifier) AS specific_name,
19681968
CAST(l.lanname AS sql_identifier) AS group_name,
19691969
CAST('FROM SQL' AS character_data) AS transform_type
19701970
FROM pg_type t JOIN pg_transform x ON t.oid = x.trftype
@@ -1980,7 +1980,7 @@ CREATE VIEW transforms AS
19801980
CAST(t.typname AS sql_identifier) AS udt_name,
19811981
CAST(current_database() AS sql_identifier) AS specific_catalog,
19821982
CAST(np.nspname AS sql_identifier) AS specific_schema,
1983-
CAST(p.proname || '_' || CAST(p.oid AS text) AS sql_identifier) AS specific_name,
1983+
CAST(nameconcatoid(p.proname, p.oid) AS sql_identifier) AS specific_name,
19841984
CAST(l.lanname AS sql_identifier) AS group_name,
19851985
CAST('TO SQL' AS character_data) AS transform_type
19861986
FROM pg_type t JOIN pg_transform x ON t.oid = x.trftype
@@ -2495,7 +2495,7 @@ CREATE VIEW view_routine_usage AS
24952495
CAST(v.relname AS sql_identifier) AS table_name,
24962496
CAST(current_database() AS sql_identifier) AS specific_catalog,
24972497
CAST(np.nspname AS sql_identifier) AS specific_schema,
2498-
CAST(p.proname || '_' || CAST(p.oid AS text) AS sql_identifier) AS specific_name
2498+
CAST(nameconcatoid(p.proname, p.oid) AS sql_identifier) AS specific_name
24992499

25002500
FROM pg_namespace nv, pg_class v, pg_depend dv,
25012501
pg_depend dp, pg_proc p, pg_namespace np
@@ -2712,7 +2712,8 @@ CREATE VIEW element_types AS
27122712
UNION ALL
27132713

27142714
/* parameters */
2715-
SELECT pronamespace, CAST(proname || '_' || CAST(oid AS text) AS sql_identifier),
2715+
SELECT pronamespace,
2716+
CAST(nameconcatoid(proname, oid) AS sql_identifier),
27162717
'ROUTINE'::text, (ss.x).n, (ss.x).x, 0
27172718
FROM (SELECT p.pronamespace, p.proname, p.oid,
27182719
_pg_expandarray(coalesce(p.proallargtypes, p.proargtypes::oid[])) AS x
@@ -2721,7 +2722,8 @@ CREATE VIEW element_types AS
27212722
UNION ALL
27222723

27232724
/* result types */
2724-
SELECT p.pronamespace, CAST(p.proname || '_' || CAST(p.oid AS text) AS sql_identifier),
2725+
SELECT p.pronamespace,
2726+
CAST(nameconcatoid(p.proname, p.oid) AS sql_identifier),
27252727
'ROUTINE'::text, 0, p.prorettype, 0
27262728
FROM pg_proc p
27272729

src/backend/utils/adt/name.c

+35
Original file line numberDiff line numberDiff line change
@@ -364,3 +364,38 @@ current_schemas(PG_FUNCTION_ARGS)
364364

365365
PG_RETURN_POINTER(array);
366366
}
367+
368+
/*
369+
* SQL-function nameconcatoid(name, oid) returns name
370+
*
371+
* This is used in the information_schema to produce specific_name columns,
372+
* which are supposed to be unique per schema. We achieve that (in an ugly
373+
* way) by appending the object's OID. The result is the same as
374+
* ($1::text || '_' || $2::text)::name
375+
* except that, if it would not fit in NAMEDATALEN, we make it do so by
376+
* truncating the name input (not the oid).
377+
*/
378+
Datum
379+
nameconcatoid(PG_FUNCTION_ARGS)
380+
{
381+
Name nam = PG_GETARG_NAME(0);
382+
Oid oid = PG_GETARG_OID(1);
383+
Name result;
384+
char suffix[20];
385+
int suflen;
386+
int namlen;
387+
388+
suflen = snprintf(suffix, sizeof(suffix), "_%u", oid);
389+
namlen = strlen(NameStr(*nam));
390+
391+
/* Truncate oversize input by truncating name part, not suffix */
392+
if (namlen + suflen >= NAMEDATALEN)
393+
namlen = pg_mbcliplen(NameStr(*nam), namlen, NAMEDATALEN - 1 - suflen);
394+
395+
/* We use palloc0 here to ensure result is zero-padded */
396+
result = (Name) palloc0(NAMEDATALEN);
397+
memcpy(NameStr(*result), NameStr(*nam), namlen);
398+
memcpy(NameStr(*result) + namlen, suffix, suflen);
399+
400+
PG_RETURN_NAME(result);
401+
}

src/include/catalog/catversion.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,6 @@
5353
*/
5454

5555
/* yyyymmddN */
56-
#define CATALOG_VERSION_NO 201812192
56+
#define CATALOG_VERSION_NO 201812201
5757

5858
#endif

src/include/catalog/pg_proc.dat

+4
Original file line numberDiff line numberDiff line change
@@ -716,6 +716,10 @@
716716
proname => 'bttextnamecmp', proleakproof => 't', prorettype => 'int4',
717717
proargtypes => 'text name', prosrc => 'bttextnamecmp' },
718718

719+
{ oid => '266', descr => 'concatenate name and oid',
720+
proname => 'nameconcatoid', prorettype => 'name', proargtypes => 'name oid',
721+
prosrc => 'nameconcatoid' },
722+
719723
{ oid => '274',
720724
descr => 'current date and time - increments during transactions',
721725
proname => 'timeofday', provolatile => 'v', prorettype => 'text',

0 commit comments

Comments
 (0)