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),