activerecord-postgis-adapter 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. data/Documentation.rdoc +322 -0
  2. data/History.rdoc +5 -0
  3. data/README.rdoc +42 -290
  4. data/Version +1 -1
  5. data/lib/active_record/connection_adapters/postgis_adapter.rb +35 -21
  6. data/lib/active_record/connection_adapters/postgis_adapter/rails3/create_connection.rb +96 -0
  7. data/lib/active_record/connection_adapters/postgis_adapter/{databases.rake → rails3/databases.rake} +7 -1
  8. data/lib/active_record/connection_adapters/postgis_adapter/{main_adapter.rb → rails3/main_adapter.rb} +9 -9
  9. data/lib/active_record/connection_adapters/postgis_adapter/{spatial_column.rb → rails3/spatial_column.rb} +4 -8
  10. data/lib/active_record/connection_adapters/postgis_adapter/{spatial_table_definition.rb → rails3/spatial_table_definition.rb} +5 -9
  11. data/lib/active_record/connection_adapters/postgis_adapter/rails4/create_connection.rb +88 -0
  12. data/lib/active_record/connection_adapters/postgis_adapter/rails4/databases.rake +52 -0
  13. data/lib/active_record/connection_adapters/postgis_adapter/rails4/main_adapter.rb +310 -0
  14. data/lib/active_record/connection_adapters/postgis_adapter/rails4/postgis_database_tasks.rb +232 -0
  15. data/lib/active_record/connection_adapters/postgis_adapter/rails4/spatial_column.rb +220 -0
  16. data/lib/active_record/connection_adapters/postgis_adapter/rails4/spatial_table_definition.rb +140 -0
  17. data/lib/active_record/connection_adapters/postgis_adapter/railtie.rb +3 -28
  18. data/lib/active_record/connection_adapters/postgis_adapter/{arel_tosql.rb → shared/arel_tosql.rb} +3 -7
  19. data/lib/active_record/connection_adapters/postgis_adapter/shared/jdbc_compat.rb +133 -0
  20. data/lib/active_record/connection_adapters/postgis_adapter/shared/railtie.rb +66 -0
  21. data/lib/active_record/connection_adapters/postgis_adapter/shared/setup.rb +57 -0
  22. data/lib/active_record/connection_adapters/postgis_adapter/{version.rb → shared/version.rb} +1 -1
  23. data/lib/activerecord-postgis-adapter.rb +37 -0
  24. data/lib/rgeo/active_record/postgis_adapter/railtie.rb +1 -1
  25. data/test/tc_basic.rb +43 -16
  26. data/test/tc_ddl.rb +2 -2
  27. data/test/tc_nested_class.rb +2 -2
  28. data/test/tc_spatial_queries.rb +14 -9
  29. data/test/tc_tasks.rb +110 -0
  30. metadata +27 -14
  31. data/lib/active_record/connection_adapters/postgis_adapter/jdbc_connection.rb +0 -78
  32. data/lib/active_record/connection_adapters/postgis_adapter/pg_connection.rb +0 -27
@@ -0,0 +1,310 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # PostGIS adapter for ActiveRecord
4
+ #
5
+ # -----------------------------------------------------------------------------
6
+ # Copyright 2010-2012 Daniel Azuma
7
+ #
8
+ # All rights reserved.
9
+ #
10
+ # Redistribution and use in source and binary forms, with or without
11
+ # modification, are permitted provided that the following conditions are met:
12
+ #
13
+ # * Redistributions of source code must retain the above copyright notice,
14
+ # this list of conditions and the following disclaimer.
15
+ # * Redistributions in binary form must reproduce the above copyright notice,
16
+ # this list of conditions and the following disclaimer in the documentation
17
+ # and/or other materials provided with the distribution.
18
+ # * Neither the name of the copyright holder, nor the names of any other
19
+ # contributors to this software, may be used to endorse or promote products
20
+ # derived from this software without specific prior written permission.
21
+ #
22
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
+ # POSSIBILITY OF SUCH DAMAGE.
33
+ # -----------------------------------------------------------------------------
34
+ ;
35
+
36
+
37
+ module ActiveRecord # :nodoc:
38
+
39
+ module ConnectionAdapters # :nodoc:
40
+
41
+ module PostGISAdapter # :nodoc:
42
+
43
+
44
+ class MainAdapter < PostgreSQLAdapter # :nodoc:
45
+
46
+
47
+ SPATIAL_COLUMN_CONSTRUCTORS = ::RGeo::ActiveRecord::DEFAULT_SPATIAL_COLUMN_CONSTRUCTORS.merge(
48
+ :geography => {:type => 'geometry', :geographic => true}
49
+ )
50
+
51
+ @@native_database_types = nil
52
+
53
+
54
+ # Overridden to change the visitor
55
+
56
+ def initialize(*args_)
57
+ super
58
+ @visitor = ::Arel::Visitors::PostGIS.new(self)
59
+ end
60
+
61
+
62
+ def set_rgeo_factory_settings(factory_settings_)
63
+ @rgeo_factory_settings = factory_settings_
64
+ end
65
+
66
+
67
+ def adapter_name
68
+ PostGISAdapter::ADAPTER_NAME
69
+ end
70
+
71
+
72
+ def spatial_column_constructor(name_)
73
+ SPATIAL_COLUMN_CONSTRUCTORS[name_]
74
+ end
75
+
76
+
77
+ # Overridden to add the :spatial type
78
+
79
+ def native_database_types
80
+ @@native_database_types ||= super.merge(:spatial => {:name => 'geometry'})
81
+ end
82
+
83
+
84
+ def postgis_lib_version
85
+ unless defined?(@postgis_lib_version)
86
+ @postgis_lib_version = select_value("SELECT PostGIS_Lib_Version()") rescue nil
87
+ end
88
+ @postgis_lib_version
89
+ end
90
+
91
+
92
+ def srs_database_columns
93
+ {:srtext_column => 'srtext', :proj4text_column => 'proj4text', :auth_name_column => 'auth_name', :auth_srid_column => 'auth_srid'}
94
+ end
95
+
96
+
97
+ def quote(value_, column_=nil)
98
+ # Overridden to recognize geometry types
99
+ if ::RGeo::Feature::Geometry.check_type(value_)
100
+ "'#{::RGeo::WKRep::WKBGenerator.new(:hex_format => true, :type_format => :ewkb, :emit_ewkb_srid => true).generate(value_)}'"
101
+ elsif value_.is_a?(::RGeo::Cartesian::BoundingBox)
102
+ "'#{value_.min_x},#{value_.min_y},#{value_.max_x},#{value_.max_y}'::box"
103
+ else
104
+ super
105
+ end
106
+ end
107
+
108
+
109
+ def type_cast(value_, column_, array_member_=false)
110
+ # Overridden to recognize geometry types
111
+ if ::RGeo::Feature::Geometry.check_type(value_)
112
+ ::RGeo::WKRep::WKBGenerator.new(:hex_format => true, :type_format => :ewkb, :emit_ewkb_srid => true).generate(value_)
113
+ else
114
+ super
115
+ end
116
+ end
117
+
118
+
119
+ def columns(table_name_, name_=nil)
120
+ # FULL REPLACEMENT. RE-CHECK ON NEW VERSIONS.
121
+ # We needed to return a spatial column subclass.
122
+ table_name_ = table_name_.to_s
123
+ spatial_info_ = spatial_column_info(table_name_)
124
+ column_definitions(table_name_).collect do |col_name_, type_, default_, notnull_, oid_, fmod_|
125
+ # JDBC support: JDBC adapter returns a hash for column definitions,
126
+ # instead of an array of values.
127
+ if col_name_.kind_of?(::Hash)
128
+ notnull_ = col_name_["column_not_null"]
129
+ default_ = col_name_["column_default"]
130
+ type_ = col_name_["column_type"]
131
+ col_name_ = col_name_["column_name"]
132
+ # TODO: get oid and fmod from jdbc
133
+ end
134
+ oid_ = OID::TYPE_MAP.fetch(oid_.to_i, fmod_.to_i) {
135
+ OID::Identity.new
136
+ }
137
+ SpatialColumn.new(@rgeo_factory_settings, table_name_, col_name_, default_, oid_, type_,
138
+ notnull_ == 'f', type_ =~ /geometry/i ? spatial_info_[col_name_] : nil)
139
+ end
140
+ end
141
+
142
+
143
+ def indexes(table_name_, name_=nil)
144
+ # FULL REPLACEMENT. RE-CHECK ON NEW VERSIONS.
145
+ result_ = query(<<-SQL, 'SCHEMA')
146
+ SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid
147
+ FROM pg_class t
148
+ INNER JOIN pg_index d ON t.oid = d.indrelid
149
+ INNER JOIN pg_class i ON d.indexrelid = i.oid
150
+ WHERE i.relkind = 'i'
151
+ AND d.indisprimary = 'f'
152
+ AND t.relname = '#{table_name_}'
153
+ AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = ANY (current_schemas(false)) )
154
+ ORDER BY i.relname
155
+ SQL
156
+
157
+ result_.map do |row_|
158
+ index_name_ = row_[0]
159
+ unique_ = row_[1] == 't'
160
+ indkey_ = row_[2].split(" ")
161
+ inddef_ = row_[3]
162
+ oid_ = row_[4]
163
+
164
+ columns_ = query(<<-SQL, "SCHEMA")
165
+ SELECT a.attnum, a.attname, t.typname
166
+ FROM pg_attribute a, pg_type t
167
+ WHERE a.attrelid = #{oid_}
168
+ AND a.attnum IN (#{indkey_.join(",")})
169
+ AND a.atttypid = t.oid
170
+ SQL
171
+ columns_ = columns_.inject({}){ |h_, r_| h_[r_[0].to_s] = [r_[1], r_[2]]; h_ }
172
+ column_names_ = columns_.values_at(*indkey_).compact.map{ |a_| a_[0] }
173
+
174
+ # add info on sort order for columns (only desc order is explicitly specified, asc is the default)
175
+ desc_order_columns_ = inddef_.scan(/(\w+) DESC/).flatten
176
+ orders_ = desc_order_columns_.any? ? Hash[desc_order_columns_.map {|order_column_| [order_column_, :desc]}] : {}
177
+ where_ = inddef_.scan(/WHERE (.+)$/).flatten[0]
178
+ spatial_ = inddef_ =~ /using\s+gist/i && columns_.size == 1 &&
179
+ (columns_.values.first[1] == 'geometry' || columns_.values.first[1] == 'geography')
180
+
181
+ if column_names_.empty?
182
+ nil
183
+ else
184
+ ::RGeo::ActiveRecord::SpatialIndexDefinition.new(table_name_, index_name_, unique_, column_names_, [], orders_, where_, spatial_ ? true : false)
185
+ end
186
+ end.compact
187
+ end
188
+
189
+
190
+ def table_definition
191
+ # Override to create a spatial table definition
192
+ SpatialTableDefinition.new(self)
193
+ end
194
+
195
+
196
+ def create_table(table_name_, options_={}, &block_)
197
+ table_name_ = table_name_.to_s
198
+ # Call super and snag the table definition
199
+ table_definition_ = nil
200
+ super(table_name_, options_) do |td_|
201
+ block_.call(td_) if block_
202
+ table_definition_ = td_
203
+ end
204
+ table_definition_.non_geographic_spatial_columns.each do |col_|
205
+ type_ = col_.spatial_type.gsub('_', '').upcase
206
+ has_z_ = col_.has_z?
207
+ has_m_ = col_.has_m?
208
+ type_ = "#{type_}M" if has_m_ && !has_z_
209
+ dimensions_ = 2
210
+ dimensions_ += 1 if has_z_
211
+ dimensions_ += 1 if has_m_
212
+ execute("SELECT AddGeometryColumn('#{quote_string(table_name_)}', '#{quote_string(col_.name.to_s)}', #{col_.srid}, '#{quote_string(type_)}', #{dimensions_})")
213
+ end
214
+ end
215
+
216
+
217
+ def drop_table(table_name_, *options_)
218
+ execute("DELETE from geometry_columns where f_table_name='#{quote_string(table_name_.to_s)}'")
219
+ super
220
+ end
221
+
222
+
223
+ def add_column(table_name_, column_name_, type_, options_={})
224
+ table_name_ = table_name_.to_s
225
+ column_name_ = column_name_.to_s
226
+ if (info_ = spatial_column_constructor(type_.to_sym))
227
+ limit_ = options_[:limit]
228
+ if type_.to_s == 'geometry' &&
229
+ (options_[:no_constraints] || limit_.is_a?(::Hash) && limit_[:no_constraints])
230
+ then
231
+ options_.delete(:limit)
232
+ super
233
+ else
234
+ options_.merge!(limit_) if limit_.is_a?(::Hash)
235
+ type_ = (options_[:type] || info_[:type] || type_).to_s.gsub('_', '').upcase
236
+ has_z_ = options_[:has_z]
237
+ has_m_ = options_[:has_m]
238
+ srid_ = (options_[:srid] || -1).to_i
239
+ if options_[:geographic]
240
+ type_ << 'Z' if has_z_
241
+ type_ << 'M' if has_m_
242
+ execute("ALTER TABLE #{quote_table_name(table_name_)} ADD COLUMN #{quote_column_name(column_name_)} GEOGRAPHY(#{type_},#{srid_})")
243
+ change_column_default(table_name_, column_name_, options_[:default]) if options_include_default?(options_)
244
+ change_column_null(table_name_, column_name_, false, options_[:default]) if options_[:null] == false
245
+ else
246
+ type_ = "#{type_}M" if has_m_ && !has_z_
247
+ dimensions_ = 2
248
+ dimensions_ += 1 if has_z_
249
+ dimensions_ += 1 if has_m_
250
+ execute("SELECT AddGeometryColumn('#{quote_string(table_name_)}', '#{quote_string(column_name_)}', #{srid_}, '#{quote_string(type_)}', #{dimensions_})")
251
+ end
252
+ end
253
+ else
254
+ super
255
+ end
256
+ end
257
+
258
+
259
+ def remove_column(table_name_, column_name_, type_=nil, options_={})
260
+ table_name_ = table_name_.to_s
261
+ column_name_ = column_name_.to_s
262
+ spatial_info_ = spatial_column_info(table_name_)
263
+ if spatial_info_.include?(column_name_)
264
+ execute("SELECT DropGeometryColumn('#{quote_string(table_name_)}','#{quote_string(column_name_)}')")
265
+ else
266
+ super
267
+ end
268
+ end
269
+
270
+
271
+ def add_index(table_name_, column_name_, options_={})
272
+ # FULL REPLACEMENT. RE-CHECK ON NEW VERSIONS.
273
+ # We have to fully-replace because of the gist_clause.
274
+ gist_clause_ = options_.delete(:spatial) ? ' USING GIST' : ''
275
+ index_name_, index_type_, index_columns_, index_options_ = add_index_options(table_name_, column_name_, options_)
276
+ execute "CREATE #{index_type_} INDEX #{quote_column_name(index_name_)} ON #{quote_table_name(table_name_)}#{gist_clause_} (#{index_columns_})#{index_options_}"
277
+ end
278
+
279
+
280
+ def spatial_column_info(table_name_)
281
+ info_ = query("SELECT f_geometry_column,coord_dimension,srid,type FROM geometry_columns WHERE f_table_name='#{quote_string(table_name_.to_s)}'")
282
+ result_ = {}
283
+ info_.each do |row_|
284
+ name_ = row_[0]
285
+ type_ = row_[3]
286
+ dimension_ = row_[1].to_i
287
+ has_m_ = type_ =~ /m$/i ? true : false
288
+ type_.sub!(/m$/, '')
289
+ has_z_ = dimension_ > 3 || dimension_ == 3 && !has_m_
290
+ result_[name_] = {
291
+ :name => name_,
292
+ :type => type_,
293
+ :dimension => dimension_,
294
+ :srid => row_[2].to_i,
295
+ :has_z => has_z_,
296
+ :has_m => has_m_,
297
+ }
298
+ end
299
+ result_
300
+ end
301
+
302
+
303
+ end
304
+
305
+
306
+ end
307
+
308
+ end
309
+
310
+ end
@@ -0,0 +1,232 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # PostGIS adapter for ActiveRecord
4
+ #
5
+ # -----------------------------------------------------------------------------
6
+ # Copyright 2010-2012 Daniel Azuma
7
+ #
8
+ # All rights reserved.
9
+ #
10
+ # Redistribution and use in source and binary forms, with or without
11
+ # modification, are permitted provided that the following conditions are met:
12
+ #
13
+ # * Redistributions of source code must retain the above copyright notice,
14
+ # this list of conditions and the following disclaimer.
15
+ # * Redistributions in binary form must reproduce the above copyright notice,
16
+ # this list of conditions and the following disclaimer in the documentation
17
+ # and/or other materials provided with the distribution.
18
+ # * Neither the name of the copyright holder, nor the names of any other
19
+ # contributors to this software, may be used to endorse or promote products
20
+ # derived from this software without specific prior written permission.
21
+ #
22
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
+ # POSSIBILITY OF SUCH DAMAGE.
33
+ # -----------------------------------------------------------------------------
34
+ ;
35
+
36
+
37
+ module ActiveRecord # :nodoc:
38
+
39
+ module ConnectionAdapters # :nodoc:
40
+
41
+ module PostGISAdapter # :nodoc:
42
+
43
+ class PostGISDatabaseTasks < ::ActiveRecord::Tasks::PostgreSQLDatabaseTasks # :nodoc:
44
+
45
+
46
+ def initialize(config_)
47
+ super
48
+ ensure_installation_configs
49
+ end
50
+
51
+
52
+ def setup_gis
53
+ setup_gis_schemas
54
+ if script_dir
55
+ setup_gis_from_script_dir
56
+ elsif extension_names
57
+ setup_gis_from_extension
58
+ end
59
+ if has_su? && (script_dir || extension_names)
60
+ setup_gis_grant_privileges
61
+ end
62
+ end
63
+
64
+
65
+ # Overridden to set the database owner and call setup_gis
66
+
67
+ def create(master_established_=false)
68
+ establish_master_connection unless master_established_
69
+ extra_configs_ = {'encoding' => encoding}
70
+ extra_configs_['owner'] = username if has_su?
71
+ connection.create_database(configuration['database'], configuration.merge(extra_configs_))
72
+ establish_connection(configuration) unless master_established_
73
+ setup_gis
74
+ rescue ::ActiveRecord::StatementInvalid => error_
75
+ if /database .* already exists/ === error_.message
76
+ raise ::ActiveRecord::Tasks::DatabaseAlreadyExists
77
+ else
78
+ raise
79
+ end
80
+ end
81
+
82
+
83
+ # Overridden to remove postgis schema
84
+
85
+ def structure_dump(filename_)
86
+ set_psql_env
87
+ search_path_ = search_path.dup
88
+ search_path_.delete('postgis')
89
+ search_path_ = ['public'] if search_path_.length == 0
90
+ search_path_clause_ = search_path_.map{ |part_| "--schema=#{::Shellwords.escape(part_)}" }.join(' ')
91
+ command_ = "pg_dump -i -s -x -O -f #{::Shellwords.escape(filename_)} #{search_path_clause_} #{::Shellwords.escape(configuration['database'])}"
92
+ raise 'Error dumping database' unless ::Kernel.system(command_)
93
+ ::File.open(filename_, "a") { |f_| f_ << "SET search_path TO #{ActiveRecord::Base.connection.schema_search_path};\n\n" }
94
+ end
95
+
96
+
97
+ private
98
+
99
+
100
+ # Overridden to use su_username and su_password
101
+
102
+ def establish_master_connection
103
+ establish_connection(configuration.merge(
104
+ 'database' => 'postgres',
105
+ 'schema_search_path' => 'public',
106
+ 'username' => su_username,
107
+ 'password' => su_password))
108
+ end
109
+
110
+
111
+ def username
112
+ @username ||= configuration['username']
113
+ end
114
+
115
+ def quoted_username
116
+ @quoted_username ||= ::PGconn.quote_ident(username)
117
+ end
118
+
119
+ def password
120
+ @password ||= configuration['password']
121
+ end
122
+
123
+ def su_username
124
+ @su_username ||= configuration['su_username'] || username
125
+ end
126
+
127
+ def su_password
128
+ @su_password ||= configuration['su_password'] || password
129
+ end
130
+
131
+ def has_su?
132
+ @has_su = configuration.include?('su_username') unless defined?(@has_su)
133
+ @has_su
134
+ end
135
+
136
+ def search_path
137
+ @search_path ||= configuration['schema_search_path'].to_s.strip.split(',').map(&:strip)
138
+ end
139
+
140
+ def postgis_schema
141
+ @postgis_schema ||= search_path.include?('postgis') ? 'postgis' : (search_path.last || 'public')
142
+ end
143
+
144
+ def script_dir
145
+ @script_dir = configuration['script_dir'] unless defined?(@script_dir)
146
+ @script_dir
147
+ end
148
+
149
+ def extension_names
150
+ @extension_names ||= begin
151
+ ext_ = configuration['postgis_extension']
152
+ case ext_
153
+ when ::String
154
+ ext_.split(',')
155
+ when ::Array
156
+ ext_
157
+ else
158
+ ['postgis']
159
+ end
160
+ end
161
+ end
162
+
163
+
164
+ def ensure_installation_configs
165
+ if configuration['setup'] == 'default' && !configuration['script_dir'] && !configuration['postgis_extension']
166
+ share_dir_ = `pg_config --sharedir`.strip rescue '/usr/share'
167
+ script_dir_ = ::File.expand_path('contrib/postgis-1.5', share_dir_)
168
+ control_file_ = ::File.expand_path('extension/postgis.control', share_dir_)
169
+ if ::File.readable?(control_file_)
170
+ configuration['postgis_extension'] = 'postgis'
171
+ elsif ::File.directory?(script_dir_)
172
+ configuration['script_dir'] = script_dir_
173
+ end
174
+ end
175
+ end
176
+
177
+
178
+ def setup_gis_schemas
179
+ establish_connection(configuration.merge('schema_search_path' => 'public'))
180
+ auth_ = has_su? ? " AUTHORIZATION #{quoted_username}" : ''
181
+ search_path.each do |schema_|
182
+ if schema_.downcase != 'public' && !connection.execute("SELECT 1 FROM pg_catalog.pg_namespace WHERE nspname='#{schema_}'").try(:first)
183
+ connection.execute("CREATE SCHEMA #{schema_}#{auth_}")
184
+ end
185
+ end
186
+ establish_connection(configuration)
187
+ end
188
+
189
+
190
+ def setup_gis_from_extension
191
+ extension_names.each do |extname_|
192
+ if extname_ == 'postgis_topology'
193
+ raise ::ArgumentError, "'topology' must be in schema_search_path for postgis_topology" unless search_path.include?('topology')
194
+ connection.execute("CREATE EXTENSION #{extname_} SCHEMA topology")
195
+ else
196
+ connection.execute("CREATE EXTENSION #{extname_} SCHEMA #{postgis_schema}")
197
+ end
198
+ end
199
+ end
200
+
201
+
202
+ def setup_gis_from_script_dir
203
+ connection.execute("SET search_path TO #{postgis_schema}")
204
+ connection.execute(::File.read(::File.expand_path('postgis.sql', script_dir)))
205
+ connection.execute(::File.read(::File.expand_path('spatial_ref_sys.sql', script_dir)))
206
+ end
207
+
208
+
209
+ def setup_gis_grant_privileges
210
+ connection.execute("GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA #{postgis_schema} TO #{quoted_username}")
211
+ connection.execute("GRANT ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA #{postgis_schema} TO #{quoted_username}")
212
+ postgis_version_ = connection.execute( "SELECT #{postgis_schema}.postgis_version();" ).first['postgis_version']
213
+ if postgis_version_ =~ /^2/
214
+ connection.execute("ALTER VIEW #{postgis_schema}.geometry_columns OWNER TO #{quoted_username}")
215
+ else
216
+ connection.execute("ALTER TABLE #{postgis_schema}.geometry_columns OWNER TO #{quoted_username}")
217
+ end
218
+ connection.execute("ALTER TABLE #{postgis_schema}.spatial_ref_sys OWNER TO #{quoted_username}")
219
+ end
220
+
221
+
222
+ end
223
+
224
+
225
+ ::ActiveRecord::Tasks::DatabaseTasks.register_task(/postgis/, PostGISDatabaseTasks)
226
+
227
+
228
+ end
229
+
230
+ end
231
+
232
+ end