ar-postgis 0.7.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 (26) hide show
  1. checksums.yaml +7 -0
  2. data/Documentation.rdoc +292 -0
  3. data/History.rdoc +147 -0
  4. data/LICENSE.txt +29 -0
  5. data/README.rdoc +78 -0
  6. data/lib/active_record/connection_adapters/postgis_adapter.rb +38 -0
  7. data/lib/active_record/connection_adapters/postgis_adapter/rails4/create_connection.rb +29 -0
  8. data/lib/active_record/connection_adapters/postgis_adapter/rails4/databases.rake +17 -0
  9. data/lib/active_record/connection_adapters/postgis_adapter/rails4/main_adapter.rb +202 -0
  10. data/lib/active_record/connection_adapters/postgis_adapter/rails4/postgis_database_tasks.rb +204 -0
  11. data/lib/active_record/connection_adapters/postgis_adapter/rails4/spatial_column.rb +184 -0
  12. data/lib/active_record/connection_adapters/postgis_adapter/rails4/spatial_table_definition.rb +121 -0
  13. data/lib/active_record/connection_adapters/postgis_adapter/railtie.rb +3 -0
  14. data/lib/active_record/connection_adapters/postgis_adapter/shared/arel_tosql.rb +23 -0
  15. data/lib/active_record/connection_adapters/postgis_adapter/shared/common_adapter_methods.rb +46 -0
  16. data/lib/active_record/connection_adapters/postgis_adapter/shared/railtie.rb +13 -0
  17. data/lib/active_record/connection_adapters/postgis_adapter/shared/setup.rb +21 -0
  18. data/lib/active_record/connection_adapters/postgis_adapter/version.rb +7 -0
  19. data/lib/activerecord-postgis-adapter.rb +1 -0
  20. data/test/database.yml +5 -0
  21. data/test/tc_basic.rb +212 -0
  22. data/test/tc_ddl.rb +303 -0
  23. data/test/tc_nested_class.rb +48 -0
  24. data/test/tc_spatial_queries.rb +126 -0
  25. data/test/tc_tasks.rb +112 -0
  26. metadata +149 -0
data/README.rdoc ADDED
@@ -0,0 +1,78 @@
1
+ == PostGIS \ActiveRecord Adapter
2
+
3
+ The activerecord-postgis-adapter is a plugin that provides access to features of the PostGIS geospatial database from \ActiveRecord. Technically, it extends the standard postgresql adapter to provide support for the spatial data types and features added by the PostGIS extension. It uses the {RGeo}[http://github.com/dazuma/rgeo] library to represent spatial data in Ruby.
4
+
5
+ Original project: https://github.com/dazuma/activerecord-postgis-adapter/
6
+
7
+ == About the PostGIS Adapter
8
+
9
+ This is a brief summary covering how to use activerecord-postgis-adapter. For full documentation, see Documentation.rdoc.
10
+
11
+ === Features
12
+
13
+ The adapter provides three basic capabilities.
14
+
15
+ First, it provides *spatial migrations*. It extends the \ActiveRecord migration syntax to support creating spatially-typed columns and spatial indexes. You can control the various PostGIS-provided attributes such as srid, dimension, and geographic vs geometric math.
16
+
17
+ Second, it recognizes spatial types and casts them properly to RGeo geometry objects. The adapter can configure these objects automatically based on the srid and dimension in the database table, or you can tell it to convert the data to a different form. You can also set attribute data using WKT format.
18
+
19
+ Third, it lets you include simple spatial data in queries. WKT format data and RGeo objects can be embedded in where clauses. If you include the Squeel gem, the adapter also supports advanced queries utilizing the standard SQL spatial function set.
20
+
21
+ === Requirements
22
+
23
+ The adapter has the following requirements.
24
+
25
+ * Ruby 2.0.0 or later.
26
+ * PostgreSQL 9.0 or later.
27
+ * PostGIS 2.0, or later.
28
+ * ActiveRecord 4.0.2 or later.
29
+ * rgeo and rgeo-activerecord.
30
+
31
+ === Installation and Configuration
32
+
33
+ To install this adapter in a Rails application, add it to your Gemfile:
34
+
35
+ gem 'activerecord-postgis-adapter'
36
+
37
+ and run bundle install to update your bundle.
38
+
39
+ Alternately, if you are not using bundler, install it separately as a gem:
40
+
41
+ gem install activerecord-postgis-adapter
42
+
43
+ Please note that this adapter uses the rgeo gem, which may have additional dependencies. Please see the \README documentation for rgeo for more information.
44
+
45
+ Once you have installed the adapter, you'll need to edit your config/database.yml to call for it. At minimum, this means changing the adapter name from "postgresql" to "postgis". It may also require other settings to ensure that other functions (such as rake test) continue to work as expected. We recommend reading the Configuration section in the Documentation.rdoc file carefully before starting to use this adapter.
46
+
47
+ == Development and Support
48
+
49
+ Original project info:
50
+
51
+ \Documentation is available at http://dazuma.github.com/activerecord-postgis-adapter/rdoc
52
+
53
+ Source code is hosted on Github at http://github.com/dazuma/activerecord-postgis-adapter
54
+
55
+ Contributions are welcome. Fork the project on Github.
56
+
57
+ Report bugs on Github issues at http://github.org/dazuma/activerecord-postgis-adapter/issues
58
+
59
+ Support available on the rgeo-users google group at http://groups.google.com/group/rgeo-users
60
+
61
+ Contact the author at dazuma at gmail dot com.
62
+
63
+ == Acknowledgments
64
+
65
+ The PostGIS Adapter and its supporting libraries (including RGeo) are
66
+ written by Daniel Azuma (http://www.daniel-azuma.com).
67
+
68
+ Development is supported by Pirq. (http://pirq.com).
69
+
70
+ This adapter implementation owes some debt to the spatial_adapter plugin
71
+ (http://github.com/fragility/spatial_adapter). Although we made some
72
+ different design decisions for this adapter, studying the spatial_adapter
73
+ source gave us a head start on the implementation.
74
+
75
+ == License
76
+
77
+ https://github.com/neighborland/activerecord-postgis-adapter/LICENSE.txt
78
+
@@ -0,0 +1,38 @@
1
+ # The activerecord-postgis-adapter gem installs the *postgis*
2
+ # connection adapter into ActiveRecord.
3
+
4
+ module ActiveRecord
5
+ # All ActiveRecord adapters go in this namespace.
6
+ # This adapter is installed into the PostGISAdapter submodule.
7
+ module ConnectionAdapters
8
+ # The PostGIS Adapter lives in this namespace.
9
+ module PostGISAdapter
10
+ # The name returned by the adapter_name method of this adapter.
11
+ ADAPTER_NAME = 'PostGIS'.freeze
12
+ end
13
+ end
14
+ end
15
+
16
+ # :stopdoc:
17
+
18
+ require 'active_record'
19
+ require 'active_record/connection_adapters/postgresql_adapter'
20
+ require 'rgeo/active_record'
21
+
22
+ require 'active_record/connection_adapters/postgis_adapter/version.rb'
23
+ require 'active_record/connection_adapters/postgis_adapter/shared/common_adapter_methods.rb'
24
+ require 'active_record/connection_adapters/postgis_adapter/rails4/main_adapter.rb'
25
+ require 'active_record/connection_adapters/postgis_adapter/rails4/spatial_table_definition.rb'
26
+ require 'active_record/connection_adapters/postgis_adapter/rails4/spatial_column.rb'
27
+ require 'active_record/connection_adapters/postgis_adapter/shared/arel_tosql.rb'
28
+ require 'active_record/connection_adapters/postgis_adapter/shared/setup.rb'
29
+ require 'active_record/connection_adapters/postgis_adapter/rails4/create_connection'
30
+ require 'active_record/connection_adapters/postgis_adapter/rails4/postgis_database_tasks.rb'
31
+
32
+ ::ActiveRecord::ConnectionAdapters::PostGISAdapter.initial_setup
33
+
34
+ if defined?(::Rails::Railtie)
35
+ load ::File.expand_path('postgis_adapter/shared/railtie.rb', ::File.dirname(__FILE__))
36
+ end
37
+
38
+ # :startdoc:
@@ -0,0 +1,29 @@
1
+ module ActiveRecord # :nodoc:
2
+
3
+ module ConnectionHandling # :nodoc:
4
+ require 'pg'
5
+
6
+ # Based on the default <tt>postgresql_connection</tt> definition from
7
+ # ActiveRecord.
8
+
9
+ def postgis_connection(config_)
10
+ # FULL REPLACEMENT because we need to create a different class.
11
+ conn_params_ = config_.symbolize_keys
12
+
13
+ conn_params_.delete_if { |_, v_| v_.nil? }
14
+
15
+ # Map ActiveRecords param names to PGs.
16
+ conn_params_[:user] = conn_params_.delete(:username) if conn_params_[:username]
17
+ conn_params_[:dbname] = conn_params_.delete(:database) if conn_params_[:database]
18
+
19
+ # Forward only valid config params to PGconn.connect.
20
+ conn_params_.keep_if { |k_, _| VALID_CONN_PARAMS.include?(k_) }
21
+
22
+ # The postgres drivers don't allow the creation of an unconnected PGconn object,
23
+ # so just pass a nil connection object for the time being.
24
+ ::ActiveRecord::ConnectionAdapters::PostGISAdapter::MainAdapter.new(nil, logger, conn_params_, config_)
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,17 @@
1
+ # :stopdoc:
2
+
3
+ namespace :db do
4
+ namespace :gis do
5
+ desc "Setup PostGIS data in the database"
6
+ task :setup => [:load_config] do
7
+ environments_ = [::Rails.env]
8
+ environments_ << 'test' if ::Rails.env.development?
9
+ configs_ = ::ActiveRecord::Base.configurations.values_at(*environments_).compact.reject{ |config_| config_['database'].blank? }
10
+ configs_.each do |config_|
11
+ ::ActiveRecord::ConnectionAdapters::PostGISAdapter::PostGISDatabaseTasks.new(config_).setup_gis
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ # :startdoc:
@@ -0,0 +1,202 @@
1
+ module ActiveRecord # :nodoc:
2
+ module ConnectionAdapters # :nodoc:
3
+ module PostGISAdapter # :nodoc:
4
+ class MainAdapter < PostgreSQLAdapter # :nodoc:
5
+ def initialize(*args_)
6
+ # Overridden to change the visitor
7
+ super
8
+ @visitor = ::Arel::Visitors::PostGIS.new(self)
9
+ end
10
+
11
+ include PostGISAdapter::CommonAdapterMethods
12
+
13
+ @@native_database_types = nil
14
+
15
+ def native_database_types
16
+ # Overridden to add the :spatial type
17
+ @@native_database_types ||= super.merge(
18
+ :spatial => {:name => 'geometry'},
19
+ :geography => {:name => 'geography'})
20
+ end
21
+
22
+ def type_cast(value_, column_, array_member_=false)
23
+ if ::RGeo::Feature::Geometry.check_type(value_)
24
+ ::RGeo::WKRep::WKBGenerator.new(:hex_format => true, :type_format => :ewkb, :emit_ewkb_srid => true).generate(value_)
25
+ else
26
+ super
27
+ end
28
+ end
29
+
30
+ def columns(table_name_, name_=nil)
31
+ # FULL REPLACEMENT. RE-CHECK ON NEW VERSIONS.
32
+ # We needed to return a spatial column subclass.
33
+ table_name_ = table_name_.to_s
34
+ spatial_info_ = spatial_column_info(table_name_)
35
+ column_definitions(table_name_).collect do |col_name_, type_, default_, notnull_, oid_, fmod_|
36
+ oid_ = type_map.fetch(oid_.to_i, fmod_.to_i) {
37
+ OID::Identity.new
38
+ }
39
+ SpatialColumn.new(@rgeo_factory_settings, table_name_, col_name_, default_, oid_, type_,
40
+ notnull_ == 'f', type_ =~ /geometry/i ? spatial_info_[col_name_] : nil)
41
+ end
42
+ end
43
+
44
+ def indexes(table_name_, name_=nil)
45
+ # FULL REPLACEMENT. RE-CHECK ON NEW VERSIONS.
46
+ result_ = query(<<-SQL, 'SCHEMA')
47
+ SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid
48
+ FROM pg_class t
49
+ INNER JOIN pg_index d ON t.oid = d.indrelid
50
+ INNER JOIN pg_class i ON d.indexrelid = i.oid
51
+ WHERE i.relkind = 'i'
52
+ AND d.indisprimary = 'f'
53
+ AND t.relname = '#{table_name_}'
54
+ AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = ANY (current_schemas(false)) )
55
+ ORDER BY i.relname
56
+ SQL
57
+
58
+ result_.map do |row_|
59
+ index_name_ = row_[0]
60
+ unique_ = row_[1] == 't'
61
+ indkey_ = row_[2].split(" ")
62
+ inddef_ = row_[3]
63
+ oid_ = row_[4]
64
+
65
+ columns_ = query(<<-SQL, "SCHEMA")
66
+ SELECT a.attnum, a.attname, t.typname
67
+ FROM pg_attribute a, pg_type t
68
+ WHERE a.attrelid = #{oid_}
69
+ AND a.attnum IN (#{indkey_.join(",")})
70
+ AND a.atttypid = t.oid
71
+ SQL
72
+ columns_ = columns_.inject({}){ |h_, r_| h_[r_[0].to_s] = [r_[1], r_[2]]; h_ }
73
+ column_names_ = columns_.values_at(*indkey_).compact.map{ |a_| a_[0] }
74
+
75
+ # add info on sort order for columns (only desc order is explicitly specified, asc is the default)
76
+ desc_order_columns_ = inddef_.scan(/(\w+) DESC/).flatten
77
+ orders_ = desc_order_columns_.any? ? Hash[desc_order_columns_.map {|order_column_| [order_column_, :desc]}] : {}
78
+ where_ = inddef_.scan(/WHERE (.+)$/).flatten[0]
79
+ spatial_ = inddef_ =~ /using\s+gist/i && columns_.size == 1 &&
80
+ (columns_.values.first[1] == 'geometry' || columns_.values.first[1] == 'geography')
81
+
82
+ if column_names_.empty?
83
+ nil
84
+ else
85
+ ::RGeo::ActiveRecord::SpatialIndexDefinition.new(table_name_, index_name_, unique_, column_names_, [], orders_, where_, spatial_ ? true : false)
86
+ end
87
+ end.compact
88
+ end
89
+
90
+ def create_table_definition(name_, temporary_, options_, as_=nil)
91
+ # Override to create a spatial table definition (post-4.0.0.beta1)
92
+ PostGISAdapter::TableDefinition.new(native_database_types, name_, temporary_, options_, as_, self)
93
+ end
94
+
95
+ def create_table(table_name_, options_={}, &block_)
96
+ table_name_ = table_name_.to_s
97
+ # Call super and snag the table definition
98
+ table_definition_ = nil
99
+ super(table_name_, options_) do |td_|
100
+ block_.call(td_) if block_
101
+ table_definition_ = td_
102
+ end
103
+ table_definition_.non_geographic_spatial_columns.each do |col_|
104
+ type_ = col_.spatial_type.gsub('_', '').upcase
105
+ has_z_ = col_.has_z?
106
+ has_m_ = col_.has_m?
107
+ type_ = "#{type_}M" if has_m_ && !has_z_
108
+ dimensions_ = 2
109
+ dimensions_ += 1 if has_z_
110
+ dimensions_ += 1 if has_m_
111
+ execute("SELECT AddGeometryColumn('#{quote_string(table_name_)}', '#{quote_string(col_.name.to_s)}', #{col_.srid}, '#{quote_string(type_)}', #{dimensions_})")
112
+ end
113
+ end
114
+
115
+ def drop_table(table_name_, *options_)
116
+ if postgis_lib_version.to_s.split('.').first.to_i == 1
117
+ execute("DELETE from geometry_columns where f_table_name='#{quote_string(table_name_.to_s)}'")
118
+ end
119
+ super
120
+ end
121
+
122
+ def add_column(table_name_, column_name_, type_, options_={})
123
+ table_name_ = table_name_.to_s
124
+ column_name_ = column_name_.to_s
125
+ if (info_ = spatial_column_constructor(type_.to_sym))
126
+ limit_ = options_[:limit]
127
+ if type_.to_s == 'geometry' &&
128
+ (options_[:no_constraints] || limit_.is_a?(::Hash) && limit_[:no_constraints])
129
+ then
130
+ options_.delete(:limit)
131
+ super
132
+ else
133
+ options_.merge!(limit_) if limit_.is_a?(::Hash)
134
+ type_ = (options_[:type] || info_[:type] || type_).to_s.gsub('_', '').upcase
135
+ has_z_ = options_[:has_z]
136
+ has_m_ = options_[:has_m]
137
+ srid_ = (options_[:srid] || -1).to_i
138
+ if options_[:geographic]
139
+ type_ << 'Z' if has_z_
140
+ type_ << 'M' if has_m_
141
+ execute("ALTER TABLE #{quote_table_name(table_name_)} ADD COLUMN #{quote_column_name(column_name_)} GEOGRAPHY(#{type_},#{srid_})")
142
+ change_column_default(table_name_, column_name_, options_[:default]) if options_include_default?(options_)
143
+ change_column_null(table_name_, column_name_, false, options_[:default]) if options_[:null] == false
144
+ else
145
+ type_ = "#{type_}M" if has_m_ && !has_z_
146
+ dimensions_ = 2
147
+ dimensions_ += 1 if has_z_
148
+ dimensions_ += 1 if has_m_
149
+ execute("SELECT AddGeometryColumn('#{quote_string(table_name_)}', '#{quote_string(column_name_)}', #{srid_}, '#{quote_string(type_)}', #{dimensions_})")
150
+ end
151
+ end
152
+ else
153
+ super
154
+ end
155
+ end
156
+
157
+ def remove_column(table_name_, column_name_, type_=nil, options_={})
158
+ table_name_ = table_name_.to_s
159
+ column_name_ = column_name_.to_s
160
+ spatial_info_ = spatial_column_info(table_name_)
161
+ if spatial_info_.include?(column_name_)
162
+ execute("SELECT DropGeometryColumn('#{quote_string(table_name_)}','#{quote_string(column_name_)}')")
163
+ else
164
+ super
165
+ end
166
+ end
167
+
168
+ def add_index(table_name_, column_name_, options_={})
169
+ # FULL REPLACEMENT. RE-CHECK ON NEW VERSIONS.
170
+ # We have to fully-replace because of the gist_clause.
171
+ options_ ||= {}
172
+ gist_clause_ = options_.delete(:spatial) ? ' USING GIST' : ''
173
+ index_name_, index_type_, index_columns_, index_options_ = add_index_options(table_name_, column_name_, options_)
174
+ execute "CREATE #{index_type_} INDEX #{quote_column_name(index_name_)} ON #{quote_table_name(table_name_)}#{gist_clause_} (#{index_columns_})#{index_options_}"
175
+ end
176
+
177
+ def spatial_column_info(table_name_)
178
+ info_ = query("SELECT f_geometry_column,coord_dimension,srid,type FROM geometry_columns WHERE f_table_name='#{quote_string(table_name_.to_s)}'")
179
+ result_ = {}
180
+ info_.each do |row_|
181
+ name_ = row_[0]
182
+ type_ = row_[3]
183
+ dimension_ = row_[1].to_i
184
+ has_m_ = type_ =~ /m$/i ? true : false
185
+ type_.sub!(/m$/, '')
186
+ has_z_ = dimension_ > 3 || dimension_ == 3 && !has_m_
187
+ result_[name_] = {
188
+ :name => name_,
189
+ :type => type_,
190
+ :dimension => dimension_,
191
+ :srid => row_[2].to_i,
192
+ :has_z => has_z_,
193
+ :has_m => has_m_,
194
+ }
195
+ end
196
+ result_
197
+ end
198
+
199
+ end
200
+ end
201
+ end
202
+ end
@@ -0,0 +1,204 @@
1
+ module ActiveRecord # :nodoc:
2
+
3
+ module ConnectionAdapters # :nodoc:
4
+
5
+ module PostGISAdapter # :nodoc:
6
+
7
+ class PostGISDatabaseTasks < ::ActiveRecord::Tasks::PostgreSQLDatabaseTasks # :nodoc:
8
+
9
+
10
+ def initialize(config_)
11
+ super
12
+ ensure_installation_configs
13
+ end
14
+
15
+
16
+ def setup_gis
17
+ establish_su_connection
18
+ setup_gis_schemas
19
+ if script_dir
20
+ setup_gis_from_script_dir
21
+ elsif extension_names
22
+ setup_gis_from_extension
23
+ end
24
+ if has_su? && (script_dir || extension_names)
25
+ setup_gis_grant_privileges
26
+ end
27
+ establish_connection(configuration)
28
+ end
29
+
30
+
31
+ # Overridden to set the database owner and call setup_gis
32
+
33
+ def create(master_established_=false)
34
+ establish_master_connection unless master_established_
35
+ extra_configs_ = {'encoding' => encoding}
36
+ extra_configs_['owner'] = username if has_su?
37
+ connection.create_database(configuration['database'], configuration.merge(extra_configs_))
38
+ setup_gis
39
+ rescue ::ActiveRecord::StatementInvalid => error_
40
+ if /database .* already exists/ === error_.message
41
+ raise ::ActiveRecord::Tasks::DatabaseAlreadyExists
42
+ else
43
+ raise
44
+ end
45
+ end
46
+
47
+
48
+ # Overridden to remove postgis schema
49
+
50
+ def structure_dump(filename_)
51
+ set_psql_env
52
+ search_path_ = search_path.dup
53
+ search_path_.delete('postgis')
54
+ search_path_ = ['public'] if search_path_.length == 0
55
+ search_path_clause_ = search_path_.map{ |part_| "--schema=#{::Shellwords.escape(part_)}" }.join(' ')
56
+ command_ = "pg_dump -i -s -x -O -f #{::Shellwords.escape(filename_)} #{search_path_clause_} #{::Shellwords.escape(configuration['database'])}"
57
+ raise 'Error dumping database' unless ::Kernel.system(command_)
58
+ ::File.open(filename_, "a") { |f_| f_ << "SET search_path TO #{ActiveRecord::Base.connection.schema_search_path};\n\n" }
59
+ end
60
+
61
+
62
+ private
63
+
64
+
65
+ # Overridden to use su_username and su_password
66
+
67
+ def establish_master_connection
68
+ establish_connection(configuration.merge(
69
+ 'database' => 'postgres',
70
+ 'schema_search_path' => 'public',
71
+ 'username' => su_username,
72
+ 'password' => su_password))
73
+ end
74
+
75
+
76
+ def establish_su_connection
77
+ establish_connection(configuration.merge(
78
+ 'schema_search_path' => 'public',
79
+ 'username' => su_username,
80
+ 'password' => su_password))
81
+ end
82
+
83
+
84
+ def username
85
+ @username ||= configuration['username']
86
+ end
87
+
88
+ def quoted_username
89
+ @quoted_username ||= ::ActiveRecord::Base.connection.quote_column_name(username)
90
+ end
91
+
92
+ def password
93
+ @password ||= configuration['password']
94
+ end
95
+
96
+ def su_username
97
+ @su_username ||= configuration['su_username'] || username
98
+ end
99
+
100
+ def su_password
101
+ @su_password ||= configuration['su_password'] || password
102
+ end
103
+
104
+ def has_su?
105
+ @has_su = configuration.include?('su_username') unless defined?(@has_su)
106
+ @has_su
107
+ end
108
+
109
+ def search_path
110
+ @search_path ||= configuration['schema_search_path'].to_s.strip.split(',').map(&:strip)
111
+ end
112
+
113
+ def postgis_schema
114
+ @postgis_schema ||= search_path.include?('postgis') ? 'postgis' : (search_path.last || 'public')
115
+ end
116
+
117
+ def script_dir
118
+ @script_dir = configuration['script_dir'] unless defined?(@script_dir)
119
+ @script_dir
120
+ end
121
+
122
+ def extension_names
123
+ @extension_names ||= begin
124
+ ext_ = configuration['postgis_extension']
125
+ case ext_
126
+ when ::String
127
+ ext_.split(',')
128
+ when ::Array
129
+ ext_
130
+ else
131
+ ['postgis']
132
+ end
133
+ end
134
+ end
135
+
136
+
137
+ def ensure_installation_configs
138
+ if configuration['setup'] == 'default' && !configuration['script_dir'] && !configuration['postgis_extension']
139
+ share_dir_ = `pg_config --sharedir`.strip rescue '/usr/share'
140
+ script_dir_ = ::File.expand_path('contrib/postgis-1.5', share_dir_)
141
+ control_file_ = ::File.expand_path('extension/postgis.control', share_dir_)
142
+ if ::File.readable?(control_file_)
143
+ configuration['postgis_extension'] = 'postgis'
144
+ elsif ::File.directory?(script_dir_)
145
+ configuration['script_dir'] = script_dir_
146
+ end
147
+ end
148
+ end
149
+
150
+
151
+ def setup_gis_schemas
152
+ auth_ = has_su? ? " AUTHORIZATION #{quoted_username}" : ''
153
+ search_path.each do |schema_|
154
+ if schema_.downcase != 'public' && !connection.execute("SELECT 1 FROM pg_catalog.pg_namespace WHERE nspname='#{schema_}'").try(:first)
155
+ connection.execute("CREATE SCHEMA #{schema_}#{auth_}")
156
+ end
157
+ end
158
+ end
159
+
160
+
161
+ def setup_gis_from_extension
162
+ extension_names.each do |extname_|
163
+ if extname_ == 'postgis_topology'
164
+ raise ::ArgumentError, "'topology' must be in schema_search_path for postgis_topology" unless search_path.include?('topology')
165
+ connection.execute("CREATE EXTENSION IF NOT EXISTS #{extname_} SCHEMA topology")
166
+ else
167
+ connection.execute("CREATE EXTENSION IF NOT EXISTS #{extname_} SCHEMA #{postgis_schema}")
168
+ end
169
+ end
170
+ end
171
+
172
+
173
+ def setup_gis_from_script_dir
174
+ connection.execute("SET search_path TO #{postgis_schema}")
175
+ connection.execute(::File.read(::File.expand_path('postgis.sql', script_dir)))
176
+ connection.execute(::File.read(::File.expand_path('spatial_ref_sys.sql', script_dir)))
177
+ end
178
+
179
+
180
+ def setup_gis_grant_privileges
181
+ connection.execute("GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA #{postgis_schema} TO #{quoted_username}")
182
+ connection.execute("GRANT ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA #{postgis_schema} TO #{quoted_username}")
183
+ postgis_version_ = connection.execute( "SELECT #{postgis_schema}.postgis_version();" ).first['postgis_version']
184
+ if postgis_version_ =~ /^2/
185
+ connection.execute("ALTER VIEW #{postgis_schema}.geometry_columns OWNER TO #{quoted_username}")
186
+ connection.execute("ALTER VIEW #{postgis_schema}.geography_columns OWNER TO #{quoted_username}")
187
+ else
188
+ connection.execute("ALTER TABLE #{postgis_schema}.geometry_columns OWNER TO #{quoted_username}")
189
+ end
190
+ connection.execute("ALTER TABLE #{postgis_schema}.spatial_ref_sys OWNER TO #{quoted_username}")
191
+ end
192
+
193
+
194
+ end
195
+
196
+
197
+ ::ActiveRecord::Tasks::DatabaseTasks.register_task(/postgis/, PostGISDatabaseTasks)
198
+
199
+
200
+ end
201
+
202
+ end
203
+
204
+ end