diff --git a/contrib/btree_gist/btree_gist--1.1--1.2.sql b/contrib/btree_gist/btree_gist--1.1--1.2.sql
index 8487f9bfc8..d5a8c6cf90 100644
--- a/contrib/btree_gist/btree_gist--1.1--1.2.sql
+++ b/contrib/btree_gist/btree_gist--1.1--1.2.sql
@@ -8,56 +8,72 @@
-- wherein the signatures have been updated already. In that case to_regprocedure() will
-- return NULL and no updates will happen.
+DO LANGUAGE plpgsql
+$$
+DECLARE
+ my_schema pg_catalog.text := pg_catalog.quote_ident(pg_catalog.current_schema());
+ old_path pg_catalog.text := pg_catalog.current_setting('search_path');
+BEGIN
+-- for safety, transiently set search_path to just pg_catalog+pg_temp
+PERFORM pg_catalog.set_config('search_path', 'pg_catalog, pg_temp', true);
+
UPDATE pg_catalog.pg_proc SET
proargtypes = pg_catalog.array_to_string(newtypes::pg_catalog.oid[], ' ')::pg_catalog.oidvector,
pronargs = pg_catalog.array_length(newtypes, 1)
FROM (VALUES
-(NULL::pg_catalog.text, NULL::pg_catalog.regtype[]), -- establish column types
+(NULL::pg_catalog.text, NULL::pg_catalog.text[]), -- establish column types
('gbt_oid_distance(internal,oid,int2,oid)', '{internal,oid,int2,oid,internal}'),
('gbt_oid_union(bytea,internal)', '{internal,internal}'),
-('gbt_oid_same(internal,internal,internal)', '{gbtreekey8,gbtreekey8,internal}'),
+('gbt_oid_same(internal,internal,internal)', '{SCH.gbtreekey8,SCH.gbtreekey8,internal}'),
('gbt_int2_distance(internal,int2,int2,oid)', '{internal,int2,int2,oid,internal}'),
('gbt_int2_union(bytea,internal)', '{internal,internal}'),
-('gbt_int2_same(internal,internal,internal)', '{gbtreekey4,gbtreekey4,internal}'),
+('gbt_int2_same(internal,internal,internal)', '{SCH.gbtreekey4,SCH.gbtreekey4,internal}'),
('gbt_int4_distance(internal,int4,int2,oid)', '{internal,int4,int2,oid,internal}'),
('gbt_int4_union(bytea,internal)', '{internal,internal}'),
-('gbt_int4_same(internal,internal,internal)', '{gbtreekey8,gbtreekey8,internal}'),
+('gbt_int4_same(internal,internal,internal)', '{SCH.gbtreekey8,SCH.gbtreekey8,internal}'),
('gbt_int8_distance(internal,int8,int2,oid)', '{internal,int8,int2,oid,internal}'),
('gbt_int8_union(bytea,internal)', '{internal,internal}'),
-('gbt_int8_same(internal,internal,internal)', '{gbtreekey16,gbtreekey16,internal}'),
+('gbt_int8_same(internal,internal,internal)', '{SCH.gbtreekey16,SCH.gbtreekey16,internal}'),
('gbt_float4_distance(internal,float4,int2,oid)', '{internal,float4,int2,oid,internal}'),
('gbt_float4_union(bytea,internal)', '{internal,internal}'),
-('gbt_float4_same(internal,internal,internal)', '{gbtreekey8,gbtreekey8,internal}'),
+('gbt_float4_same(internal,internal,internal)', '{SCH.gbtreekey8,SCH.gbtreekey8,internal}'),
('gbt_float8_distance(internal,float8,int2,oid)', '{internal,float8,int2,oid,internal}'),
('gbt_float8_union(bytea,internal)', '{internal,internal}'),
-('gbt_float8_same(internal,internal,internal)', '{gbtreekey16,gbtreekey16,internal}'),
+('gbt_float8_same(internal,internal,internal)', '{SCH.gbtreekey16,SCH.gbtreekey16,internal}'),
('gbt_ts_distance(internal,timestamp,int2,oid)', '{internal,timestamp,int2,oid,internal}'),
('gbt_tstz_distance(internal,timestamptz,int2,oid)', '{internal,timestamptz,int2,oid,internal}'),
('gbt_ts_union(bytea,internal)', '{internal,internal}'),
-('gbt_ts_same(internal,internal,internal)', '{gbtreekey16,gbtreekey16,internal}'),
+('gbt_ts_same(internal,internal,internal)', '{SCH.gbtreekey16,SCH.gbtreekey16,internal}'),
('gbt_time_distance(internal,time,int2,oid)', '{internal,time,int2,oid,internal}'),
('gbt_time_union(bytea,internal)', '{internal,internal}'),
-('gbt_time_same(internal,internal,internal)', '{gbtreekey16,gbtreekey16,internal}'),
+('gbt_time_same(internal,internal,internal)', '{SCH.gbtreekey16,SCH.gbtreekey16,internal}'),
('gbt_date_distance(internal,date,int2,oid)', '{internal,date,int2,oid,internal}'),
('gbt_date_union(bytea,internal)', '{internal,internal}'),
-('gbt_date_same(internal,internal,internal)', '{gbtreekey8,gbtreekey8,internal}'),
+('gbt_date_same(internal,internal,internal)', '{SCH.gbtreekey8,SCH.gbtreekey8,internal}'),
('gbt_intv_distance(internal,interval,int2,oid)', '{internal,interval,int2,oid,internal}'),
('gbt_intv_union(bytea,internal)', '{internal,internal}'),
-('gbt_intv_same(internal,internal,internal)', '{gbtreekey32,gbtreekey32,internal}'),
+('gbt_intv_same(internal,internal,internal)', '{SCH.gbtreekey32,SCH.gbtreekey32,internal}'),
('gbt_cash_distance(internal,money,int2,oid)', '{internal,money,int2,oid,internal}'),
('gbt_cash_union(bytea,internal)', '{internal,internal}'),
-('gbt_cash_same(internal,internal,internal)', '{gbtreekey16,gbtreekey16,internal}'),
+('gbt_cash_same(internal,internal,internal)', '{SCH.gbtreekey16,SCH.gbtreekey16,internal}'),
('gbt_macad_union(bytea,internal)', '{internal,internal}'),
-('gbt_macad_same(internal,internal,internal)', '{gbtreekey16,gbtreekey16,internal}'),
+('gbt_macad_same(internal,internal,internal)', '{SCH.gbtreekey16,SCH.gbtreekey16,internal}'),
('gbt_text_union(bytea,internal)', '{internal,internal}'),
-('gbt_text_same(internal,internal,internal)', '{gbtreekey_var,gbtreekey_var,internal}'),
+('gbt_text_same(internal,internal,internal)', '{SCH.gbtreekey_var,SCH.gbtreekey_var,internal}'),
('gbt_bytea_union(bytea,internal)', '{internal,internal}'),
-('gbt_bytea_same(internal,internal,internal)', '{gbtreekey_var,gbtreekey_var,internal}'),
+('gbt_bytea_same(internal,internal,internal)', '{SCH.gbtreekey_var,SCH.gbtreekey_var,internal}'),
('gbt_numeric_union(bytea,internal)', '{internal,internal}'),
-('gbt_numeric_same(internal,internal,internal)', '{gbtreekey_var,gbtreekey_var,internal}'),
+('gbt_numeric_same(internal,internal,internal)', '{SCH.gbtreekey_var,SCH.gbtreekey_var,internal}'),
('gbt_bit_union(bytea,internal)', '{internal,internal}'),
-('gbt_bit_same(internal,internal,internal)', '{gbtreekey_var,gbtreekey_var,internal}'),
+('gbt_bit_same(internal,internal,internal)', '{SCH.gbtreekey_var,SCH.gbtreekey_var,internal}'),
('gbt_inet_union(bytea,internal)', '{internal,internal}'),
-('gbt_inet_same(internal,internal,internal)', '{gbtreekey16,gbtreekey16,internal}')
-) AS update_data (oldproc, newtypes)
-WHERE oid = pg_catalog.to_regprocedure(oldproc);
+('gbt_inet_same(internal,internal,internal)', '{SCH.gbtreekey16,SCH.gbtreekey16,internal}')
+) AS update_data (oldproc, newtypestext),
+LATERAL (
+ SELECT array_agg(replace(typ, 'SCH', my_schema)::regtype) as newtypes FROM unnest(newtypestext) typ
+) ls
+WHERE oid = to_regprocedure(my_schema || '.' || replace(oldproc, 'SCH', my_schema));
+
+PERFORM pg_catalog.set_config('search_path', old_path, true);
+END
+$$;
diff --git a/contrib/citext/citext--1.1--1.2.sql b/contrib/citext/citext--1.1--1.2.sql
index 4f0e4bc719..a8bba860a1 100644
--- a/contrib/citext/citext--1.1--1.2.sql
+++ b/contrib/citext/citext--1.1--1.2.sql
@@ -41,14 +41,28 @@ ALTER FUNCTION replace(citext, citext, citext) PARALLEL SAFE;
ALTER FUNCTION split_part(citext, citext, int) PARALLEL SAFE;
ALTER FUNCTION translate(citext, citext, text) PARALLEL SAFE;
-UPDATE pg_proc SET proparallel = 's'
-WHERE oid = 'min(citext)'::pg_catalog.regprocedure;
+-- We have to update aggregates the hard way for lack of ALTER support
+DO LANGUAGE plpgsql
+$$
+DECLARE
+ my_schema pg_catalog.text := pg_catalog.quote_ident(pg_catalog.current_schema());
+ old_path pg_catalog.text := pg_catalog.current_setting('search_path');
+BEGIN
+-- for safety, transiently set search_path to just pg_catalog+pg_temp
+PERFORM pg_catalog.set_config('search_path', 'pg_catalog, pg_temp', true);
UPDATE pg_proc SET proparallel = 's'
-WHERE oid = 'max(citext)'::pg_catalog.regprocedure;
+WHERE oid = (my_schema || '.min(' || my_schema || '.citext)')::pg_catalog.regprocedure;
-UPDATE pg_aggregate SET aggcombinefn = 'citext_smaller'
-WHERE aggfnoid = 'max(citext)'::pg_catalog.regprocedure;
+UPDATE pg_proc SET proparallel = 's'
+WHERE oid = (my_schema || '.max(' || my_schema || '.citext)')::pg_catalog.regprocedure;
-UPDATE pg_aggregate SET aggcombinefn = 'citext_larger'
-WHERE aggfnoid = 'max(citext)'::pg_catalog.regprocedure;
+UPDATE pg_aggregate SET aggcombinefn = (my_schema || '.citext_smaller')::regproc
+WHERE aggfnoid = (my_schema || '.max(' || my_schema || '.citext)')::pg_catalog.regprocedure;
+
+UPDATE pg_aggregate SET aggcombinefn = (my_schema || '.citext_larger')::regproc
+WHERE aggfnoid = (my_schema || '.max(' || my_schema || '.citext)')::pg_catalog.regprocedure;
+
+PERFORM pg_catalog.set_config('search_path', old_path, true);
+END
+$$;
diff --git a/contrib/citext/citext--1.2--1.3.sql b/contrib/citext/citext--1.2--1.3.sql
index 4ab867915c..24a71452c6 100644
--- a/contrib/citext/citext--1.2--1.3.sql
+++ b/contrib/citext/citext--1.2--1.3.sql
@@ -3,5 +3,19 @@
-- complain if script is sourced in psql, rather than via ALTER EXTENSION
\echo Use "ALTER EXTENSION citext UPDATE TO '1.3'" to load this file. \quit
-UPDATE pg_aggregate SET aggcombinefn = 'citext_smaller'
-WHERE aggfnoid = 'min(citext)'::pg_catalog.regprocedure;
+-- We have to update aggregates the hard way for lack of ALTER support
+DO LANGUAGE plpgsql
+$$
+DECLARE
+ my_schema pg_catalog.text := pg_catalog.quote_ident(pg_catalog.current_schema());
+ old_path pg_catalog.text := pg_catalog.current_setting('search_path');
+BEGIN
+-- for safety, transiently set search_path to just pg_catalog+pg_temp
+PERFORM pg_catalog.set_config('search_path', 'pg_catalog, pg_temp', true);
+
+UPDATE pg_aggregate SET aggcombinefn = (my_schema || '.citext_smaller')::regproc
+WHERE aggfnoid = (my_schema || '.min(' || my_schema || '.citext)')::pg_catalog.regprocedure;
+
+PERFORM pg_catalog.set_config('search_path', old_path, true);
+END
+$$;
diff --git a/contrib/cube/cube--1.1--1.2.sql b/contrib/cube/cube--1.1--1.2.sql
index 64a531e8b4..76aba239e5 100644
--- a/contrib/cube/cube--1.1--1.2.sql
+++ b/contrib/cube/cube--1.1--1.2.sql
@@ -7,16 +7,31 @@
-- We use to_regprocedure() so that query doesn't fail if run against 9.6beta1 definitions,
-- wherein the signatures have been updated already. In that case to_regprocedure() will
-- return NULL and no updates will happen.
+DO LANGUAGE plpgsql
+$$
+DECLARE
+ my_schema pg_catalog.text := pg_catalog.quote_ident(pg_catalog.current_schema());
+ old_path pg_catalog.text := pg_catalog.current_setting('search_path');
+BEGIN
+-- for safety, transiently set search_path to just pg_catalog+pg_temp
+PERFORM pg_catalog.set_config('search_path', 'pg_catalog, pg_temp', true);
UPDATE pg_catalog.pg_proc SET
proargtypes = pg_catalog.array_to_string(newtypes::pg_catalog.oid[], ' ')::pg_catalog.oidvector,
pronargs = pg_catalog.array_length(newtypes, 1)
FROM (VALUES
-(NULL::pg_catalog.text, NULL::pg_catalog.regtype[]), -- establish column types
-('g_cube_consistent(internal,cube,int4,oid,internal)', '{internal,cube,int2,oid,internal}'),
-('g_cube_distance(internal,cube,smallint,oid)', '{internal,cube,smallint,oid,internal}')
-) AS update_data (oldproc, newtypes)
-WHERE oid = pg_catalog.to_regprocedure(oldproc);
+(NULL::pg_catalog.text, NULL::pg_catalog.text[]), -- establish column types
+('g_cube_consistent(internal,SCH.cube,int4,oid,internal)', '{internal,SCH.cube,int2,oid,internal}'),
+('g_cube_distance(internal,SCH.cube,smallint,oid)', '{internal,SCH.cube,smallint,oid,internal}')
+) AS update_data (oldproc, newtypestext),
+LATERAL (
+ SELECT array_agg(replace(typ, 'SCH', my_schema)::regtype) as newtypes FROM unnest(newtypestext) typ
+) ls
+WHERE oid = to_regprocedure(my_schema || '.' || replace(oldproc, 'SCH', my_schema));
+
+PERFORM pg_catalog.set_config('search_path', old_path, true);
+END
+$$;
ALTER FUNCTION cube_in(cstring) PARALLEL SAFE;
ALTER FUNCTION cube(float8[], float8[]) PARALLEL SAFE;
diff --git a/contrib/cube/cube--1.3--1.4.sql b/contrib/cube/cube--1.3--1.4.sql
index 869820c0c8..41629395df 100644
--- a/contrib/cube/cube--1.3--1.4.sql
+++ b/contrib/cube/cube--1.3--1.4.sql
@@ -12,6 +12,15 @@
-- bound into a particular opclass. There's no SQL command for that,
-- so fake it with a manual update on pg_depend.
--
+DO LANGUAGE plpgsql
+$$
+DECLARE
+ my_schema pg_catalog.text := pg_catalog.quote_ident(pg_catalog.current_schema());
+ old_path pg_catalog.text := pg_catalog.current_setting('search_path');
+BEGIN
+-- for safety, transiently set search_path to just pg_catalog+pg_temp
+PERFORM pg_catalog.set_config('search_path', 'pg_catalog, pg_temp', true);
+
UPDATE pg_catalog.pg_depend
SET deptype = 'a'
WHERE classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass
@@ -20,26 +29,30 @@ WHERE classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass
FROM pg_catalog.pg_depend
WHERE classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass
AND refclassid = 'pg_catalog.pg_proc'::pg_catalog.regclass
- AND (refobjid = 'g_cube_compress(pg_catalog.internal)'::pg_catalog.regprocedure))
+ AND (refobjid = (my_schema || '.g_cube_compress(pg_catalog.internal)')::pg_catalog.regprocedure))
AND refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass
AND deptype = 'i';
+UPDATE pg_catalog.pg_depend
+SET deptype = 'a'
+WHERE classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass
+ AND objid =
+ (SELECT objid
+ FROM pg_catalog.pg_depend
+ WHERE classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass
+ AND refclassid = 'pg_catalog.pg_proc'::pg_catalog.regclass
+ AND (refobjid = (my_schema || '.g_cube_decompress(pg_catalog.internal)')::pg_catalog.regprocedure))
+ AND refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass
+ AND deptype = 'i';
+
+PERFORM pg_catalog.set_config('search_path', old_path, true);
+END
+$$;
+
ALTER OPERATOR FAMILY gist_cube_ops USING gist drop function 3 (cube);
ALTER EXTENSION cube DROP function g_cube_compress(pg_catalog.internal);
DROP FUNCTION g_cube_compress(pg_catalog.internal);
-UPDATE pg_catalog.pg_depend
-SET deptype = 'a'
-WHERE classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass
- AND objid =
- (SELECT objid
- FROM pg_catalog.pg_depend
- WHERE classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass
- AND refclassid = 'pg_catalog.pg_proc'::pg_catalog.regclass
- AND (refobjid = 'g_cube_decompress(pg_catalog.internal)'::pg_catalog.regprocedure))
- AND refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass
- AND deptype = 'i';
-
ALTER OPERATOR FAMILY gist_cube_ops USING gist drop function 4 (cube);
ALTER EXTENSION cube DROP function g_cube_decompress(pg_catalog.internal);
DROP FUNCTION g_cube_decompress(pg_catalog.internal);
diff --git a/contrib/earthdistance/earthdistance--1.1.sql b/contrib/earthdistance/earthdistance--1.1.sql
index 9136a54a7b..9ef20ab848 100644
--- a/contrib/earthdistance/earthdistance--1.1.sql
+++ b/contrib/earthdistance/earthdistance--1.1.sql
@@ -31,7 +31,7 @@ CREATE DOMAIN earth AS cube
CONSTRAINT not_point check(cube_is_point(value))
CONSTRAINT not_3d check(cube_dim(value) <= 3)
CONSTRAINT on_surface check(abs(cube_distance(value, '(0)'::cube) /
- earth() - 1) < '10e-7'::float8);
+ earth() - '1'::float8) < '10e-7'::float8);
CREATE FUNCTION sec_to_gc(float8)
RETURNS float8
diff --git a/contrib/earthdistance/earthdistance.control b/contrib/earthdistance/earthdistance.control
index 3df666dfc1..5816d22cdd 100644
--- a/contrib/earthdistance/earthdistance.control
+++ b/contrib/earthdistance/earthdistance.control
@@ -3,5 +3,4 @@ comment = 'calculate great-circle distances on the surface of the Earth'
default_version = '1.1'
module_pathname = '$libdir/earthdistance'
relocatable = true
-trusted = true
requires = 'cube'
diff --git a/contrib/hstore/hstore--1.1--1.2.sql b/contrib/hstore/hstore--1.1--1.2.sql
index a868ffe48e..cc69fc7f80 100644
--- a/contrib/hstore/hstore--1.1--1.2.sql
+++ b/contrib/hstore/hstore--1.1--1.2.sql
@@ -9,10 +9,13 @@
-- dependent on the extension.
DO LANGUAGE plpgsql
-
$$
-
+DECLARE
+ my_schema pg_catalog.text := pg_catalog.quote_ident(pg_catalog.current_schema());
+ old_path pg_catalog.text := pg_catalog.current_setting('search_path');
BEGIN
+-- for safety, transiently set search_path to just pg_catalog+pg_temp
+PERFORM pg_catalog.set_config('search_path', 'pg_catalog, pg_temp', true);
PERFORM 1
FROM pg_proc p
@@ -27,6 +30,7 @@ BEGIN
IF NOT FOUND
THEN
+ PERFORM pg_catalog.set_config('search_path', old_path, true);
CREATE FUNCTION hstore_to_json(hstore)
RETURNS json
@@ -43,6 +47,7 @@ BEGIN
END IF;
+PERFORM pg_catalog.set_config('search_path', old_path, true);
END;
$$;
diff --git a/contrib/hstore/hstore--1.3--1.4.sql b/contrib/hstore/hstore--1.3--1.4.sql
index d68956bb94..53f26f9fb8 100644
--- a/contrib/hstore/hstore--1.3--1.4.sql
+++ b/contrib/hstore/hstore--1.3--1.4.sql
@@ -7,23 +7,38 @@
-- We use to_regprocedure() so that query doesn't fail if run against 9.6beta1 definitions,
-- wherein the signatures have been updated already. In that case to_regprocedure() will
-- return NULL and no updates will happen.
+DO LANGUAGE plpgsql
+$$
+DECLARE
+ my_schema pg_catalog.text := pg_catalog.quote_ident(pg_catalog.current_schema());
+ old_path pg_catalog.text := pg_catalog.current_setting('search_path');
+BEGIN
+-- for safety, transiently set search_path to just pg_catalog+pg_temp
+PERFORM pg_catalog.set_config('search_path', 'pg_catalog, pg_temp', true);
UPDATE pg_catalog.pg_proc SET
proargtypes = pg_catalog.array_to_string(newtypes::pg_catalog.oid[], ' ')::pg_catalog.oidvector,
pronargs = pg_catalog.array_length(newtypes, 1)
FROM (VALUES
-(NULL::pg_catalog.text, NULL::pg_catalog.regtype[]), -- establish column types
-('ghstore_same(internal,internal,internal)', '{ghstore,ghstore,internal}'),
-('ghstore_consistent(internal,internal,int4,oid,internal)', '{internal,hstore,int2,oid,internal}'),
-('gin_extract_hstore(internal,internal)', '{hstore,internal}'),
-('gin_extract_hstore_query(internal,internal,int2,internal,internal)', '{hstore,internal,int2,internal,internal}'),
-('gin_consistent_hstore(internal,int2,internal,int4,internal,internal)', '{internal,int2,hstore,int4,internal,internal}')
-) AS update_data (oldproc, newtypes)
-WHERE oid = pg_catalog.to_regprocedure(oldproc);
+(NULL::pg_catalog.text, NULL::pg_catalog.text[]), -- establish column types
+('ghstore_same(internal,internal,internal)', '{SCH.ghstore,SCH.ghstore,internal}'),
+('ghstore_consistent(internal,internal,int4,oid,internal)', '{internal,SCH.hstore,int2,oid,internal}'),
+('gin_extract_hstore(internal,internal)', '{SCH.hstore,internal}'),
+('gin_extract_hstore_query(internal,internal,int2,internal,internal)', '{SCH.hstore,internal,int2,internal,internal}'),
+('gin_consistent_hstore(internal,int2,internal,int4,internal,internal)', '{internal,int2,SCH.hstore,int4,internal,internal}')
+) AS update_data (oldproc, newtypestext),
+LATERAL (
+ SELECT array_agg(replace(typ, 'SCH', my_schema)::regtype) as newtypes FROM unnest(newtypestext) typ
+) ls
+WHERE oid = to_regprocedure(my_schema || '.' || replace(oldproc, 'SCH', my_schema));
UPDATE pg_catalog.pg_proc SET
- prorettype = 'ghstore'::pg_catalog.regtype
-WHERE oid = pg_catalog.to_regprocedure('ghstore_union(internal,internal)');
+ prorettype = (my_schema || '.ghstore')::pg_catalog.regtype
+WHERE oid = pg_catalog.to_regprocedure((my_schema || '.ghstore_union(internal,internal)'));
+
+PERFORM pg_catalog.set_config('search_path', old_path, true);
+END
+$$;
ALTER FUNCTION hstore_in(cstring) PARALLEL SAFE;
ALTER FUNCTION hstore_out(hstore) PARALLEL SAFE;
diff --git a/contrib/hstore_plperl/hstore_plperl.control b/contrib/hstore_plperl/hstore_plperl.control
index 4b9fd13d04..16277f68c1 100644
--- a/contrib/hstore_plperl/hstore_plperl.control
+++ b/contrib/hstore_plperl/hstore_plperl.control
@@ -3,5 +3,4 @@ comment = 'transform between hstore and plperl'
default_version = '1.0'
module_pathname = '$libdir/hstore_plperl'
relocatable = true
-trusted = true
requires = 'hstore,plperl'
diff --git a/contrib/intagg/intagg--1.0--1.1.sql b/contrib/intagg/intagg--1.0--1.1.sql
index b2a2820b0c..c0cc17a033 100644
--- a/contrib/intagg/intagg--1.0--1.1.sql
+++ b/contrib/intagg/intagg--1.0--1.1.sql
@@ -6,6 +6,18 @@
ALTER FUNCTION int_agg_state(internal, int4) PARALLEL SAFE;
ALTER FUNCTION int_agg_final_array(internal) PARALLEL SAFE;
ALTER FUNCTION int_array_enum(int4[]) PARALLEL SAFE;
+DO LANGUAGE plpgsql
+$$
+DECLARE
+ my_schema pg_catalog.text := pg_catalog.quote_ident(pg_catalog.current_schema());
+ old_path pg_catalog.text := pg_catalog.current_setting('search_path');
+BEGIN
+-- for safety, transiently set search_path to just pg_catalog+pg_temp
+PERFORM pg_catalog.set_config('search_path', 'pg_catalog, pg_temp', true);
UPDATE pg_proc SET proparallel = 's'
-WHERE oid = 'int_array_aggregate(int4)'::pg_catalog.regprocedure;
+WHERE oid = (my_schema || '.int_array_aggregate(int4)')::pg_catalog.regprocedure;
+
+PERFORM pg_catalog.set_config('search_path', old_path, true);
+END
+$$;
diff --git a/contrib/intarray/intarray--1.1--1.2.sql b/contrib/intarray/intarray--1.1--1.2.sql
index 468f245ece..919340ef01 100644
--- a/contrib/intarray/intarray--1.1--1.2.sql
+++ b/contrib/intarray/intarray--1.1--1.2.sql
@@ -7,23 +7,38 @@
-- We use to_regprocedure() so that query doesn't fail if run against 9.6beta1 definitions,
-- wherein the signatures have been updated already. In that case to_regprocedure() will
-- return NULL and no updates will happen.
+DO LANGUAGE plpgsql
+$$
+DECLARE
+ my_schema pg_catalog.text := pg_catalog.quote_ident(pg_catalog.current_schema());
+ old_path pg_catalog.text := pg_catalog.current_setting('search_path');
+BEGIN
+-- for safety, transiently set search_path to just pg_catalog+pg_temp
+PERFORM pg_catalog.set_config('search_path', 'pg_catalog, pg_temp', true);
UPDATE pg_catalog.pg_proc SET
proargtypes = pg_catalog.array_to_string(newtypes::pg_catalog.oid[], ' ')::pg_catalog.oidvector,
pronargs = pg_catalog.array_length(newtypes, 1)
FROM (VALUES
-(NULL::pg_catalog.text, NULL::pg_catalog.regtype[]), -- establish column types
+(NULL::pg_catalog.text, NULL::pg_catalog.text[]), -- establish column types
('g_int_consistent(internal,_int4,int4,oid,internal)', '{internal,_int4,int2,oid,internal}'),
('g_intbig_consistent(internal,internal,int4,oid,internal)', '{internal,_int4,int2,oid,internal}'),
-('g_intbig_same(internal,internal,internal)', '{intbig_gkey,intbig_gkey,internal}'),
+('g_intbig_same(internal,internal,internal)', '{SCH.intbig_gkey,SCH.intbig_gkey,internal}'),
('ginint4_queryextract(internal,internal,int2,internal,internal,internal,internal)', '{_int4,internal,int2,internal,internal,internal,internal}'),
('ginint4_consistent(internal,int2,internal,int4,internal,internal,internal,internal)', '{internal,int2,_int4,int4,internal,internal,internal,internal}')
-) AS update_data (oldproc, newtypes)
-WHERE oid = pg_catalog.to_regprocedure(oldproc);
+) AS update_data (oldproc, newtypestext),
+LATERAL (
+ SELECT array_agg(replace(typ, 'SCH', my_schema)::regtype) as newtypes FROM unnest(newtypestext) typ
+) ls
+WHERE oid = to_regprocedure(my_schema || '.' || replace(oldproc, 'SCH', my_schema));
UPDATE pg_catalog.pg_proc SET
- prorettype = 'intbig_gkey'::pg_catalog.regtype
-WHERE oid = pg_catalog.to_regprocedure('g_intbig_union(internal,internal)');
+ prorettype = (my_schema || '.intbig_gkey')::pg_catalog.regtype
+WHERE oid = pg_catalog.to_regprocedure(my_schema || '.g_intbig_union(internal,internal)');
+
+PERFORM pg_catalog.set_config('search_path', old_path, true);
+END
+$$;
ALTER FUNCTION bqarr_in(cstring) PARALLEL SAFE;
ALTER FUNCTION bqarr_out(query_int) PARALLEL SAFE;
diff --git a/contrib/ltree/ltree--1.0--1.1.sql b/contrib/ltree/ltree--1.0--1.1.sql
index 155751aa3a..2ce6f5adbc 100644
--- a/contrib/ltree/ltree--1.0--1.1.sql
+++ b/contrib/ltree/ltree--1.0--1.1.sql
@@ -7,26 +7,41 @@
-- We use to_regprocedure() so that query doesn't fail if run against 9.6beta1 definitions,
-- wherein the signatures have been updated already. In that case to_regprocedure() will
-- return NULL and no updates will happen.
+DO LANGUAGE plpgsql
+$$
+DECLARE
+ my_schema pg_catalog.text := pg_catalog.quote_ident(pg_catalog.current_schema());
+ old_path pg_catalog.text := pg_catalog.current_setting('search_path');
+BEGIN
+-- for safety, transiently set search_path to just pg_catalog+pg_temp
+PERFORM pg_catalog.set_config('search_path', 'pg_catalog, pg_temp', true);
UPDATE pg_catalog.pg_proc SET
proargtypes = pg_catalog.array_to_string(newtypes::pg_catalog.oid[], ' ')::pg_catalog.oidvector,
pronargs = pg_catalog.array_length(newtypes, 1)
FROM (VALUES
-(NULL::pg_catalog.text, NULL::pg_catalog.regtype[]), -- establish column types
-('ltree_consistent(internal,internal,int2,oid,internal)', '{internal,ltree,int2,oid,internal}'),
-('ltree_same(internal,internal,internal)', '{ltree_gist,ltree_gist,internal}'),
-('_ltree_consistent(internal,internal,int2,oid,internal)', '{internal,_ltree,int2,oid,internal}'),
-('_ltree_same(internal,internal,internal)', '{ltree_gist,ltree_gist,internal}')
-) AS update_data (oldproc, newtypes)
-WHERE oid = pg_catalog.to_regprocedure(oldproc);
+(NULL::pg_catalog.text, NULL::pg_catalog.text[]), -- establish column types
+('ltree_consistent(internal,internal,int2,oid,internal)', '{internal,SCH.ltree,int2,oid,internal}'),
+('ltree_same(internal,internal,internal)', '{SCH.ltree_gist,SCH.ltree_gist,internal}'),
+('_ltree_consistent(internal,internal,int2,oid,internal)', '{internal,SCH._ltree,int2,oid,internal}'),
+('_ltree_same(internal,internal,internal)', '{SCH.ltree_gist,SCH.ltree_gist,internal}')
+) AS update_data (oldproc, newtypestext),
+LATERAL (
+ SELECT array_agg(replace(typ, 'SCH', my_schema)::regtype) as newtypes FROM unnest(newtypestext) typ
+) ls
+WHERE oid = to_regprocedure(my_schema || '.' || replace(oldproc, 'SCH', my_schema));
UPDATE pg_catalog.pg_proc SET
- prorettype = 'ltree_gist'::pg_catalog.regtype
-WHERE oid = pg_catalog.to_regprocedure('ltree_union(internal,internal)');
+ prorettype = (my_schema || '.ltree_gist')::pg_catalog.regtype
+WHERE oid = pg_catalog.to_regprocedure(my_schema || '.ltree_union(internal,internal)');
UPDATE pg_catalog.pg_proc SET
- prorettype = 'ltree_gist'::pg_catalog.regtype
-WHERE oid = pg_catalog.to_regprocedure('_ltree_union(internal,internal)');
+ prorettype = (my_schema || '.ltree_gist')::pg_catalog.regtype
+WHERE oid = pg_catalog.to_regprocedure(my_schema || '._ltree_union(internal,internal)');
+
+PERFORM pg_catalog.set_config('search_path', old_path, true);
+END
+$$;
ALTER FUNCTION ltree_in(cstring) PARALLEL SAFE;
ALTER FUNCTION ltree_out(ltree) PARALLEL SAFE;
diff --git a/contrib/pg_trgm/pg_trgm--1.2--1.3.sql b/contrib/pg_trgm/pg_trgm--1.2--1.3.sql
index b082dcd8d8..8dc772c407 100644
--- a/contrib/pg_trgm/pg_trgm--1.2--1.3.sql
+++ b/contrib/pg_trgm/pg_trgm--1.2--1.3.sql
@@ -7,21 +7,36 @@
-- We use to_regprocedure() so that query doesn't fail if run against 9.6beta1 definitions,
-- wherein the signatures have been updated already. In that case to_regprocedure() will
-- return NULL and no updates will happen.
+DO LANGUAGE plpgsql
+$$
+DECLARE
+ my_schema pg_catalog.text := pg_catalog.quote_ident(pg_catalog.current_schema());
+ old_path pg_catalog.text := pg_catalog.current_setting('search_path');
+BEGIN
+-- for safety, transiently set search_path to just pg_catalog+pg_temp
+PERFORM pg_catalog.set_config('search_path', 'pg_catalog, pg_temp', true);
UPDATE pg_catalog.pg_proc SET
proargtypes = pg_catalog.array_to_string(newtypes::pg_catalog.oid[], ' ')::pg_catalog.oidvector,
pronargs = pg_catalog.array_length(newtypes, 1)
FROM (VALUES
-(NULL::pg_catalog.text, NULL::pg_catalog.regtype[]), -- establish column types
+(NULL::pg_catalog.text, NULL::pg_catalog.text[]), -- establish column types
('gtrgm_consistent(internal,text,int4,oid,internal)', '{internal,text,int2,oid,internal}'),
('gtrgm_distance(internal,text,int4,oid)', '{internal,text,int2,oid,internal}'),
('gtrgm_union(bytea,internal)', '{internal,internal}')
-) AS update_data (oldproc, newtypes)
-WHERE oid = pg_catalog.to_regprocedure(oldproc);
+) AS update_data (oldproc, newtypestext),
+LATERAL (
+ SELECT array_agg(replace(typ, 'SCH', my_schema)::regtype) as newtypes FROM unnest(newtypestext) typ
+) ls
+WHERE oid = to_regprocedure(my_schema || '.' || replace(oldproc, 'SCH', my_schema));
UPDATE pg_catalog.pg_proc SET
- prorettype = 'gtrgm'::pg_catalog.regtype
-WHERE oid = pg_catalog.to_regprocedure('gtrgm_union(internal,internal)');
+ prorettype = (my_schema || '.gtrgm')::pg_catalog.regtype
+WHERE oid = pg_catalog.to_regprocedure(my_schema || '.gtrgm_union(internal,internal)');
+
+PERFORM pg_catalog.set_config('search_path', old_path, true);
+END
+$$;
ALTER FUNCTION set_limit(float4) PARALLEL UNSAFE;
ALTER FUNCTION show_limit() PARALLEL SAFE;
diff --git a/contrib/seg/seg--1.0--1.1.sql b/contrib/seg/seg--1.0--1.1.sql
index 2dcd4d4280..ae6cb2fba8 100644
--- a/contrib/seg/seg--1.0--1.1.sql
+++ b/contrib/seg/seg--1.0--1.1.sql
@@ -7,15 +7,30 @@
-- We use to_regprocedure() so that query doesn't fail if run against 9.6beta1 definitions,
-- wherein the signatures have been updated already. In that case to_regprocedure() will
-- return NULL and no updates will happen.
+DO LANGUAGE plpgsql
+$$
+DECLARE
+ my_schema pg_catalog.text := pg_catalog.quote_ident(pg_catalog.current_schema());
+ old_path pg_catalog.text := pg_catalog.current_setting('search_path');
+BEGIN
+-- for safety, transiently set search_path to just pg_catalog+pg_temp
+PERFORM pg_catalog.set_config('search_path', 'pg_catalog, pg_temp', true);
UPDATE pg_catalog.pg_proc SET
proargtypes = pg_catalog.array_to_string(newtypes::pg_catalog.oid[], ' ')::pg_catalog.oidvector,
pronargs = pg_catalog.array_length(newtypes, 1)
FROM (VALUES
-(NULL::pg_catalog.text, NULL::pg_catalog.regtype[]), -- establish column types
-('gseg_consistent(internal,seg,int4,oid,internal)', '{internal,seg,int2,oid,internal}')
-) AS update_data (oldproc, newtypes)
-WHERE oid = pg_catalog.to_regprocedure(oldproc);
+(NULL::pg_catalog.text, NULL::pg_catalog.text[]), -- establish column types
+('gseg_consistent(internal,SCH.seg,int4,oid,internal)', '{internal,SCH.seg,int2,oid,internal}')
+) AS update_data (oldproc, newtypestext),
+LATERAL (
+ SELECT array_agg(replace(typ, 'SCH', my_schema)::regtype) as newtypes FROM unnest(newtypestext) typ
+) ls
+WHERE oid = to_regprocedure(my_schema || '.' || replace(oldproc, 'SCH', my_schema));
+
+PERFORM pg_catalog.set_config('search_path', old_path, true);
+END
+$$;
ALTER FUNCTION seg_in(cstring) PARALLEL SAFE;
ALTER FUNCTION seg_out(seg) PARALLEL SAFE;
diff --git a/contrib/seg/seg--1.2--1.3.sql b/contrib/seg/seg--1.2--1.3.sql
index cd71a300f6..578e98953c 100644
--- a/contrib/seg/seg--1.2--1.3.sql
+++ b/contrib/seg/seg--1.2--1.3.sql
@@ -12,6 +12,15 @@
-- bound into a particular opclass. There's no SQL command for that,
-- so fake it with a manual update on pg_depend.
--
+DO LANGUAGE plpgsql
+$$
+DECLARE
+ my_schema pg_catalog.text := pg_catalog.quote_ident(pg_catalog.current_schema());
+ old_path pg_catalog.text := pg_catalog.current_setting('search_path');
+BEGIN
+-- for safety, transiently set search_path to just pg_catalog+pg_temp
+PERFORM pg_catalog.set_config('search_path', 'pg_catalog, pg_temp', true);
+
UPDATE pg_catalog.pg_depend
SET deptype = 'a'
WHERE classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass
@@ -20,26 +29,30 @@ WHERE classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass
FROM pg_catalog.pg_depend
WHERE classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass
AND refclassid = 'pg_catalog.pg_proc'::pg_catalog.regclass
- AND (refobjid = 'gseg_compress(pg_catalog.internal)'::pg_catalog.regprocedure))
+ AND (refobjid = (my_schema || '.gseg_compress(internal)')::pg_catalog.regprocedure))
AND refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass
AND deptype = 'i';
+UPDATE pg_catalog.pg_depend
+SET deptype = 'a'
+WHERE classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass
+ AND objid =
+ (SELECT objid
+ FROM pg_catalog.pg_depend
+ WHERE classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass
+ AND refclassid = 'pg_catalog.pg_proc'::pg_catalog.regclass
+ AND (refobjid = (my_schema || '.gseg_decompress(internal)')::pg_catalog.regprocedure))
+ AND refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass
+ AND deptype = 'i';
+
+PERFORM pg_catalog.set_config('search_path', old_path, true);
+END
+$$;
+
ALTER OPERATOR FAMILY gist_seg_ops USING gist drop function 3 (seg);
ALTER EXTENSION seg DROP function gseg_compress(pg_catalog.internal);
DROP function gseg_compress(pg_catalog.internal);
-UPDATE pg_catalog.pg_depend
-SET deptype = 'a'
-WHERE classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass
- AND objid =
- (SELECT objid
- FROM pg_catalog.pg_depend
- WHERE classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass
- AND refclassid = 'pg_catalog.pg_proc'::pg_catalog.regclass
- AND (refobjid = 'gseg_decompress(pg_catalog.internal)'::pg_catalog.regprocedure))
- AND refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass
- AND deptype = 'i';
-
ALTER OPERATOR FAMILY gist_seg_ops USING gist drop function 4 (seg);
ALTER EXTENSION seg DROP function gseg_decompress(pg_catalog.internal);
DROP function gseg_decompress(pg_catalog.internal);
diff --git a/doc/src/sgml/earthdistance.sgml b/doc/src/sgml/earthdistance.sgml
index 4ac52cb191..641e69c5e9 100644
--- a/doc/src/sgml/earthdistance.sgml
+++ b/doc/src/sgml/earthdistance.sgml
@@ -10,9 +10,8 @@
The earthdistance module provides two different approaches to
calculating great circle distances on the surface of the Earth. The one
- described first depends on the cube module (which
- must be installed before earthdistance can be
- installed). The second one is based on the built-in point data type,
+ described first depends on the cube module.
+ The second one is based on the built-in point data type,
using longitude and latitude for the coordinates.
@@ -24,11 +23,27 @@
- This module is considered trusted, that is, it can be
- installed by non-superusers who have CREATE privilege
- on the current database.
+ The cube module must be installed
+ before earthdistance can be installed
+ (although you can use the CASCADE option
+ of CREATE EXTENSION to install both in one command).
+
+
+ It is strongly recommended that earthdistance
+ and cube be installed in the same schema, and that
+ that schema be one for which CREATE privilege has not been and will not
+ be granted to any untrusted users.
+ Otherwise there are installation-time security hazards
+ if earthdistance's schema contains objects defined
+ by a hostile user.
+ Furthermore, when using earthdistance's functions
+ after installation, the entire search path should contain only trusted
+ schemas.
+
+
+
Cube-Based Earth Distances
diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml
index 890ff97b7a..641c9ce3c9 100644
--- a/doc/src/sgml/extend.sgml
+++ b/doc/src/sgml/extend.sgml
@@ -540,7 +540,7 @@ RETURNS anycompatible AS ...
The extension script may set privileges on objects that are part of the
- extension via GRANT and REVOKE
+ extension, using GRANT and REVOKE
statements. The final set of privileges for each object (if any are set)
will be stored in the
pg_init_privs
@@ -597,32 +597,6 @@ RETURNS anycompatible AS ...
dropping the whole extension.
-
- Defining Extension Objects
-
-
-
- Widely-distributed extensions should assume little about the database
- they occupy. In particular, unless you issued SET search_path =
- pg_temp, assume each unqualified name could resolve to an
- object that a malicious user has defined. Beware of constructs that
- depend on search_path implicitly: IN
- and CASE expression WHEN
- always select an operator using the search path. In their place, use
- OPERATOR(schema.=) ANY
- and CASE WHEN expression.
-
-
-
-
Extension Files
@@ -740,7 +714,8 @@ RETURNS anycompatible AS ...
If this parameter is true (which is the default),
only superusers can create the extension or update it to a new
- version. If it is set to false, just the privileges
+ version (but see also trusted, below).
+ If it is set to false, just the privileges
required to execute the commands in the installation or update script
are required.
This should normally be set to true if any of the
@@ -768,6 +743,9 @@ RETURNS anycompatible AS ...
Generally, this should not be set true for extensions that could
allow access to otherwise-superuser-only abilities, such as
file system access.
+ Also, marking an extension trusted requires significant extra effort
+ to write the extension's installation and update script(s) securely;
+ see .
@@ -921,7 +899,7 @@ RETURNS anycompatible AS ...
schema; that is, CREATE EXTENSION does the equivalent of
this:
-SET LOCAL search_path TO @extschema@;
+SET LOCAL search_path TO @extschema@, pg_temp;
This allows the objects created by the script file to go into the target
schema. The script file can change search_path if it wishes,
@@ -941,9 +919,15 @@ SET LOCAL search_path TO @extschema@;
If any prerequisite extensions are listed in requires
- in the control file, their target schemas are appended to the initial
- setting of search_path. This allows their objects to be
- visible to the new extension's script file.
+ in the control file, their target schemas are added to the initial
+ setting of search_path, following the new
+ extension's target schema. This allows their objects to be visible to
+ the new extension's script file.
+
+
+
+ For security, pg_temp is automatically appended to
+ the end of search_path in all cases.
@@ -1170,6 +1154,154 @@ SELECT * FROM pg_extension_update_paths('extension_name
+
+ Security Considerations for Extensions
+
+
+ Widely-distributed extensions should assume little about the database
+ they occupy. Therefore, it's appropriate to write functions provided
+ by an extension in a secure style that cannot be compromised by
+ search-path-based attacks.
+
+
+
+ An extension that has the superuser property set to
+ true must also consider security hazards for the actions taken within
+ its installation and update scripts. It is not terribly difficult for
+ a malicious user to create trojan-horse objects that will compromise
+ later execution of a carelessly-written extension script, allowing that
+ user to acquire superuser privileges.
+
+
+
+ If an extension is marked trusted, then its
+ installation schema can be selected by the installing user, who might
+ intentionally use an insecure schema in hopes of gaining superuser
+ privileges. Therefore, a trusted extension is extremely exposed from a
+ security standpoint, and all its script commands must be carefully
+ examined to ensure that no compromise is possible.
+
+
+
+ Advice about writing functions securely is provided in
+ below, and advice
+ about writing installation scripts securely is provided in
+ .
+
+
+
+ Security Considerations for Extension Functions
+
+
+ SQL-language and PL-language functions provided by extensions are at
+ risk of search-path-based attacks when they are executed, since
+ parsing of these functions occurs at execution time not creation time.
+
+
+
+ The CREATE
+ FUNCTION reference page contains advice about
+ writing SECURITY DEFINER functions safely. It's
+ good practice to apply those techniques for any function provided by
+ an extension, since the function might be called by a high-privilege
+ user.
+
+
+
+
+ If you cannot set the search_path to contain only
+ secure schemas, assume that each unqualified name could resolve to an
+ object that a malicious user has defined. Beware of constructs that
+ depend on search_path implicitly; for
+ example, IN
+ and CASE expression WHEN
+ always select an operator using the search path. In their place, use
+ OPERATOR(schema.=) ANY
+ and CASE WHEN expression.
+
+
+
+ A general-purpose extension usually should not assume that it's been
+ installed into a secure schema, which means that even schema-qualified
+ references to its own objects are not entirely risk-free. For
+ example, if the extension has defined a
+ function myschema.myfunc(bigint) then a call such
+ as myschema.myfunc(42) could be captured by a
+ hostile function myschema.myfunc(integer). Be
+ careful that the data types of function and operator parameters exactly
+ match the declared argument types, using explicit casts where necessary.
+
+
+
+
+ Security Considerations for Extension Scripts
+
+
+ An extension installation or update script should be written to guard
+ against search-path-based attacks occurring when the script executes.
+ If an object reference in the script can be made to resolve to some
+ other object than the script author intended, then a compromise might
+ occur immediately, or later when the mis-defined extension object is
+ used.
+
+
+
+ DDL commands such as CREATE FUNCTION
+ and CREATE OPERATOR CLASS are generally secure,
+ but beware of any command having a general-purpose expression as a
+ component. For example, CREATE VIEW needs to be
+ vetted, as does a DEFAULT expression
+ in CREATE FUNCTION.
+
+
+
+ Sometimes an extension script might need to execute general-purpose
+ SQL, for example to make catalog adjustments that aren't possible via
+ DDL. Be careful to execute such commands with a
+ secure search_path; do not
+ trust the path provided by CREATE/ALTER EXTENSION
+ to be secure. Best practice is to temporarily
+ set search_path to 'pg_catalog,
+ pg_temp' and insert references to the extension's
+ installation schema explicitly where needed. (This practice might
+ also be helpful for creating views.) Examples can be found in
+ the contrib modules in
+ the PostgreSQL source code distribution.
+
+
+
+ Cross-extension references are extremely difficult to make fully
+ secure, partially because of uncertainty about which schema the other
+ extension is in. The hazards are reduced if both extensions are
+ installed in the same schema, because then a hostile object cannot be
+ placed ahead of the referenced extension in the installation-time
+ search_path. However, no mechanism currently exists
+ to require that. For now, best practice is to not mark an extension
+ trusted if it depends on another one, unless that other one is always
+ installed in pg_catalog.
+
+
+
+ Do not use CREATE OR REPLACE
+ FUNCTION, except in an update script that must change the
+ definition of a function that is known to be an extension member
+ already. (Likewise for other OR REPLACE options.)
+ Using OR REPLACE unnecessarily not only has a risk
+ of accidentally overwriting someone else's function, but it creates a
+ security hazard since the overwritten function would still be owned by
+ its original owner, who could modify it.
+
+
+
+
Extension Example
@@ -1189,18 +1321,18 @@ SELECT * FROM pg_extension_update_paths('extension_name (LEFTARG = text, RIGHTARG = text, FUNCTION = pair);
-- "SET search_path" is easy to get right, but qualified names perform better.
-CREATE OR REPLACE FUNCTION lower(pair)
+CREATE FUNCTION lower(pair)
RETURNS pair LANGUAGE SQL
AS 'SELECT ROW(lower($1.k), lower($1.v))::@extschema@.pair;'
SET search_path = pg_temp;
-CREATE OR REPLACE FUNCTION pair_concat(pair, pair)
+CREATE FUNCTION pair_concat(pair, pair)
RETURNS pair LANGUAGE SQL
AS 'SELECT ROW($1.k OPERATOR(pg_catalog.||) $2.k,
$1.v OPERATOR(pg_catalog.||) $2.v)::@extschema@.pair;';
@@ -1215,6 +1347,7 @@ AS 'SELECT ROW($1.k OPERATOR(pg_catalog.||) $2.k,
# pair extension
comment = 'A key/value pair data type'
default_version = '1.0'
+# cannot be relocatable because of use of @extschema@
relocatable = false
diff --git a/doc/src/sgml/hstore.sgml b/doc/src/sgml/hstore.sgml
index fd75e92790..8a1caa3576 100644
--- a/doc/src/sgml/hstore.sgml
+++ b/doc/src/sgml/hstore.sgml
@@ -918,10 +918,14 @@ ALTER TABLE tablename ALTER hstorecol TYPE hstore USING hstorecol || '';
Python dictionaries.
-
- Of these additional extensions, hstore_plperl is
- considered trusted; the rest are not.
-
+
+
+ It is strongly recommended that the transform extensions be installed in
+ the same schema as hstore. Otherwise there are
+ installation-time security hazards if a transform extension's schema
+ contains objects defined by a hostile user.
+
+
diff --git a/doc/src/sgml/ltree.sgml b/doc/src/sgml/ltree.sgml
index dea453fc75..36aa2b5fad 100644
--- a/doc/src/sgml/ltree.sgml
+++ b/doc/src/sgml/ltree.sgml
@@ -835,6 +835,15 @@ ltreetest=> SELECT ins_label(path,2,'Space') FROM test WHERE path <@ 'Top.
creating a function, ltree values are mapped to Python lists.
(The reverse is currently not supported, however.)
+
+
+
+ It is strongly recommended that the transform extensions be installed in
+ the same schema as ltree. Otherwise there are
+ installation-time security hazards if a transform extension's schema
+ contains objects defined by a hostile user.
+
+
diff --git a/doc/src/sgml/ref/create_extension.sgml b/doc/src/sgml/ref/create_extension.sgml
index 756dd193f8..efd7fc6465 100644
--- a/doc/src/sgml/ref/create_extension.sgml
+++ b/doc/src/sgml/ref/create_extension.sgml
@@ -177,6 +177,33 @@ CREATE EXTENSION [ IF NOT EXISTS ] extension_name
system views.
+
+
+ Installing an extension as superuser requires trusting that the
+ extension's author wrote the extension installation script in a secure
+ fashion. It is not terribly difficult for a malicious user to create
+ trojan-horse objects that will compromise later execution of a
+ carelessly-written extension script, allowing that user to acquire
+ superuser privileges. However, trojan-horse objects are only hazardous
+ if they are in the search_path during script
+ execution, meaning that they are in the extension's installation target
+ schema or in the schema of some extension it depends on. Therefore, a
+ good rule of thumb when dealing with extensions whose scripts have not
+ been carefully vetted is to install them only into schemas for which
+ CREATE privilege has not been and will not be granted to any untrusted
+ users. Likewise for any extensions they depend on.
+
+
+
+ The extensions supplied with PostgreSQL are
+ believed to be secure against installation-time attacks of this sort,
+ except for a few that depend on other extensions. As stated in the
+ documentation for those extensions, they should be installed into secure
+ schemas, or installed into the same schemas as the extensions they
+ depend on, or both.
+
+
+
For information about writing new extensions, see
.
@@ -188,10 +215,16 @@ CREATE EXTENSION [ IF NOT EXISTS ] extension_name
Install the hstore extension into the
- current database:
+ current database, placing its objects in schema addons:
+CREATE EXTENSION hstore SCHEMA addons;
+
+ Another way to accomplish the same thing:
+
+SET search_path = addons;
CREATE EXTENSION hstore;
-
+
+
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index c796fcd8da..b5630b4c8d 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -908,9 +908,21 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
GUC_ACTION_SAVE, true, 0, false);
/*
- * Set up the search path to contain the target schema, then the schemas
- * of any prerequisite extensions, and nothing else. In particular this
- * makes the target schema be the default creation target namespace.
+ * Similarly disable check_function_bodies, to ensure that SQL functions
+ * won't be parsed during creation.
+ */
+ if (check_function_bodies)
+ (void) set_config_option("check_function_bodies", "off",
+ PGC_USERSET, PGC_S_SESSION,
+ GUC_ACTION_SAVE, true, 0, false);
+
+ /*
+ * Set up the search path to have the target schema first, making it be
+ * the default creation target namespace. Then add the schemas of any
+ * prerequisite extensions, unless they are in pg_catalog which would be
+ * searched anyway. (Listing pg_catalog explicitly in a non-first
+ * position would be bad for security.) Finally add pg_temp to ensure
+ * that temp objects can't take precedence over others.
*
* Note: it might look tempting to use PushOverrideSearchPath for this,
* but we cannot do that. We have to actually set the search_path GUC in
@@ -924,9 +936,10 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
Oid reqschema = lfirst_oid(lc);
char *reqname = get_namespace_name(reqschema);
- if (reqname)
+ if (reqname && strcmp(reqname, "pg_catalog") != 0)
appendStringInfo(&pathbuf, ", %s", quote_identifier(reqname));
}
+ appendStringInfoString(&pathbuf, ", pg_temp");
(void) set_config_option("search_path", pathbuf.data,
PGC_USERSET, PGC_S_SESSION,
diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c
index 0a53e9b93e..bf23937849 100644
--- a/src/backend/commands/operatorcmds.c
+++ b/src/backend/commands/operatorcmds.c
@@ -297,6 +297,7 @@ ValidateJoinEstimator(List *joinName)
{
Oid typeId[5];
Oid joinOid;
+ Oid joinOid2;
AclResult aclresult;
typeId[0] = INTERNALOID; /* PlannerInfo */
@@ -307,15 +308,26 @@ ValidateJoinEstimator(List *joinName)
/*
* As of Postgres 8.4, the preferred signature for join estimators has 5
- * arguments, but we still allow the old 4-argument form. Try the
- * preferred form first.
+ * arguments, but we still allow the old 4-argument form. Whine about
+ * ambiguity if both forms exist.
*/
joinOid = LookupFuncName(joinName, 5, typeId, true);
- if (!OidIsValid(joinOid))
- joinOid = LookupFuncName(joinName, 4, typeId, true);
- /* If not found, reference the 5-argument signature in error msg */
- if (!OidIsValid(joinOid))
- joinOid = LookupFuncName(joinName, 5, typeId, false);
+ joinOid2 = LookupFuncName(joinName, 4, typeId, true);
+ if (OidIsValid(joinOid))
+ {
+ if (OidIsValid(joinOid2))
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
+ errmsg("join estimator function %s has multiple matches",
+ NameListToString(joinName))));
+ }
+ else
+ {
+ joinOid = joinOid2;
+ /* If not found, reference the 5-argument signature in error msg */
+ if (!OidIsValid(joinOid))
+ joinOid = LookupFuncName(joinName, 5, typeId, false);
+ }
/* estimators must return float8 */
if (get_func_rettype(joinOid) != FLOAT8OID)
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 2e107ace39..483bb65ddc 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -1627,21 +1627,31 @@ findTypeInputFunction(List *procname, Oid typeOid)
{
Oid argList[3];
Oid procOid;
+ Oid procOid2;
/*
* Input functions can take a single argument of type CSTRING, or three
- * arguments (string, typioparam OID, typmod). They must return the
- * target type.
+ * arguments (string, typioparam OID, typmod). Whine about ambiguity if
+ * both forms exist.
*/
argList[0] = CSTRINGOID;
+ argList[1] = OIDOID;
+ argList[2] = INT4OID;
procOid = LookupFuncName(procname, 1, argList, true);
- if (!OidIsValid(procOid))
+ procOid2 = LookupFuncName(procname, 3, argList, true);
+ if (OidIsValid(procOid))
{
- argList[1] = OIDOID;
- argList[2] = INT4OID;
-
- procOid = LookupFuncName(procname, 3, argList, true);
+ if (OidIsValid(procOid2))
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
+ errmsg("type input function %s has multiple matches",
+ NameListToString(procname))));
+ }
+ else
+ {
+ procOid = procOid2;
+ /* If not found, reference the 1-argument signature in error msg */
if (!OidIsValid(procOid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
@@ -1649,6 +1659,7 @@ findTypeInputFunction(List *procname, Oid typeOid)
func_signature_string(procname, 1, NIL, argList))));
}
+ /* Input functions must return the target type. */
if (get_func_rettype(procOid) != typeOid)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
@@ -1714,21 +1725,31 @@ findTypeReceiveFunction(List *procname, Oid typeOid)
{
Oid argList[3];
Oid procOid;
+ Oid procOid2;
/*
* Receive functions can take a single argument of type INTERNAL, or three
- * arguments (internal, typioparam OID, typmod). They must return the
- * target type.
+ * arguments (internal, typioparam OID, typmod). Whine about ambiguity if
+ * both forms exist.
*/
argList[0] = INTERNALOID;
+ argList[1] = OIDOID;
+ argList[2] = INT4OID;
procOid = LookupFuncName(procname, 1, argList, true);
- if (!OidIsValid(procOid))
+ procOid2 = LookupFuncName(procname, 3, argList, true);
+ if (OidIsValid(procOid))
{
- argList[1] = OIDOID;
- argList[2] = INT4OID;
-
- procOid = LookupFuncName(procname, 3, argList, true);
+ if (OidIsValid(procOid2))
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
+ errmsg("type receive function %s has multiple matches",
+ NameListToString(procname))));
+ }
+ else
+ {
+ procOid = procOid2;
+ /* If not found, reference the 1-argument signature in error msg */
if (!OidIsValid(procOid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
@@ -1736,6 +1757,7 @@ findTypeReceiveFunction(List *procname, Oid typeOid)
func_signature_string(procname, 1, NIL, argList))));
}
+ /* Receive functions must return the target type. */
if (get_func_rettype(procOid) != typeOid)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),