File: postgis_proc_set_search_path.pl

package info (click to toggle)
postgis 2.3.1%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 58,660 kB
  • ctags: 10,181
  • sloc: ansic: 132,858; sql: 131,148; xml: 46,460; sh: 4,832; perl: 4,476; makefile: 2,749; python: 1,198; yacc: 442; lex: 131
file content (183 lines) | stat: -rw-r--r-- 5,015 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
#!/usr/bin/perl -w

#
# PostGIS - Spatial Types for PostgreSQL
# http://postgis.net
#
# Copyright (C) 2016 Regina Obe <lr@pcorp.us>
#
# This is free software; you can redistribute and/or modify it under
# the terms of the GNU General Public Licence. See the COPYING file.
#

#
# This script produces an .sql file containing
# ALTER FUNCTION SET PATH calls for each function
# in postgis.sql

# Having postgis functions force the search path 
# to just where postgis is installed is needed
# so that functions that call other functions during 
# database restore, materialized view creation, foreign table calls
# will always be able to find the companion functions
#

eval "exec perl -w $0 $@"
	if (0);

use strict;
use warnings;

#
# Commandline argument handling
#
($#ARGV == 0) ||
die "Usage: perl postgis_proc_set_path.pl <postgis.sql> <version_from> [<schema>]\nCreates a new SQL script 
to set search path for all functions in input script file.\n"
	if ( @ARGV < 1 || @ARGV > 3 );

my $sql_file = $ARGV[0];
my $module = 'postgis';
my $soname = '';
my $version_to = "";
my $version_to_num = 0;
my $version_from = $ARGV[1];
my $version_from_num = 0;
my $schema = "";
$schema = $ARGV[2] if @ARGV > 2;

die "Unable to open input SQL file $sql_file\n"
	if ( ! -f $sql_file );

## Header of do	
print 'DO language plpgsql $$';
my $dofunc_start = <<"EOF";
DECLARE param_postgis_schema text;
BEGIN
-- check if PostGIS is already installed
param_postgis_schema = (SELECT n.nspname from pg_extension e join pg_namespace n on e.extnamespace = n.oid WHERE extname = 'postgis');

-- if in middle install, it will be the current_schema or what was there already
param_postgis_schema = COALESCE(param_postgis_schema, current_schema());

IF param_postgis_schema != current_schema() THEN
	EXECUTE 'set search_path TO ' || quote_ident(param_postgis_schema);
END IF;

-- PostGIS set search path of functions
EOF
print $dofunc_start;

#
# Search the SQL file for the target version number (the 
# version we are upgrading *to*.
#
open( INPUT, $sql_file ) || die "Couldn't open file: $sql_file\n";


print qq{
--
-- ALTER FUNCTION script
--

};

#print "BEGIN;\n";
print "SET search_path TO $schema;\n" if $schema;

#
# Go through the SQL file and find all functions
# for each create an ALTER FUNCTION statement
# to set the search_path to schema postgis is installed in
open( INPUT, $sql_file ) || die "Couldn't open file: $sql_file\n";
while(<INPUT>)
{

	if ( /^create or replace function([^\)]+)([\)]{0,1})/i )
	{
		my $funchead = $1; # contains function header except the end )
		my $endhead = 0;
		my $endfunchead = $2;
		my $search_path_safe = -1; # we can put a search path on it without disrupting spatial index use
		
		if ($2 eq ')') ## reached end of header
		{
			$endhead = 1;
		}
		
		if ( /((add|drop)[\_]*(geometry|overview|raster)|internal)/i){
			# can't put search_path on addgeometrycolumn or addrasterconstraints 
			# since table names are sometimes passed in non-qualified
			# also can't work on some functions that take internals
			$search_path_safe = 0; 
		}
		
		if ( /st_transform/i){
			# st_transform functions query spatial_ref_sys
			# so could fail in materialized views and spatial indexes,
			# though often all done in C
			$search_path_safe = 1; 
		}

		
		#raster folks decided to break their func head in multiple lines 
		# so we need to do this crazy thing
		if ($endhead != 1)
		{
			while(<INPUT>)
			{
				#look for expressions with no ( and optionally ending in )
				if ( /^([^\)]*)([\)]{0,1})/i )
				{
					$funchead .= $1;
					$endfunchead = $2;
					if ($2 eq ')') ## reached end of header
					{
						$endhead = 1;
						
					}
				}
				last if ( $endhead );
	
			}
		}
		#strip quoted , trips up the default strip
		$funchead =~ s/(',')+//ig;
		#strip off default args from the function header
		$funchead =~ s/(default\s+[A-Za-z\.\+\-0-9\'\[\]\:\s]*)//ig;
		
		#check to see if function is STRICT or c or plpgsql
		# we can't put search path on non-STRICT sql since search path breaks SQL inlining
		# breaking sql inlining will break use of spatial index
		my $endfunc = 0;
		while(<INPUT>)
		{
			$endfunc = 1 if /^\s*(\$\$\s*)?LANGUAGE /i;
			if ( $endfunc == 1 && $search_path_safe == -1 ){
				$search_path_safe = 1 if /LANGUAGE\s+[\']*(plpgsql)/i;
				$search_path_safe = 1 if /STRICT/i;
				#exclude C functions unless we've include, 
				# in most cases except ST_Transform
				# c functions don't call dependent functions or tables
				$search_path_safe = 0 if /LANGUAGE\s+[\']*(c)/i;
			}
			last if ( $endfunc && /\;/ );
		}

		
		if ($search_path_safe == 1)
		{
			print "EXECUTE 'ALTER FUNCTION $funchead $endfunchead SET search_path=' || quote_ident(param_postgis_schema) || ',pg_catalog;';\n";
		}
	}

}

close( INPUT );

## End of DO
print 'END;';
print '$$;';

__END__