activerecord-postgis-adapter 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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