spatial_adapter 1.2.0 → 1.3.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 (45) hide show
  1. data/.gitignore +3 -0
  2. data/Gemfile +3 -0
  3. data/README.rdoc +30 -17
  4. data/Rakefile +11 -0
  5. data/init.rb +1 -0
  6. data/lib/spatial_adapter/base/mysql/adapter.rb +54 -0
  7. data/lib/spatial_adapter/base/mysql/spatial_column.rb +12 -0
  8. data/lib/spatial_adapter/base/mysql.rb +9 -0
  9. data/lib/spatial_adapter/common/schema_dumper.rb +12 -12
  10. data/lib/spatial_adapter/common/spatial_column.rb +4 -4
  11. data/lib/spatial_adapter/common/table_definition.rb +8 -10
  12. data/lib/spatial_adapter/common.rb +10 -0
  13. data/lib/spatial_adapter/jdbcmysql.rb +50 -0
  14. data/lib/spatial_adapter/mysql.rb +43 -85
  15. data/lib/spatial_adapter/mysql2.rb +39 -86
  16. data/lib/spatial_adapter/postgresql.rb +315 -324
  17. data/lib/spatial_adapter/version.rb +3 -0
  18. data/lib/spatial_adapter.rb +3 -7
  19. data/spatial_adapter.gemspec +39 -0
  20. data/spec/db/jdbcmysql_raw.rb +70 -0
  21. data/spec/db/mysql2_raw.rb +9 -9
  22. data/spec/db/mysql_raw.rb +9 -9
  23. data/spec/jdbcmysql_spec.rb +25 -0
  24. data/spec/mysql2_spec.rb +31 -0
  25. data/spec/mysql_spec.rb +17 -0
  26. data/spec/postgresql/connection_adapter_spec.rb +34 -39
  27. data/spec/postgresql/migration_spec.rb +51 -51
  28. data/spec/postgresql/models_spec.rb +37 -37
  29. data/spec/postgresql/schema_dumper_spec.rb +12 -12
  30. data/spec/postgresql_spec.rb +5 -0
  31. data/spec/{shared_examples.rb → shared/common_model_actions_spec.rb} +2 -2
  32. data/spec/shared/mysql_connection_adapter_spec.rb +110 -0
  33. data/spec/{mysql/migration_spec.rb → shared/mysql_migration_spec.rb} +17 -17
  34. data/spec/shared/mysql_models_spec.rb +58 -0
  35. data/spec/{mysql/schema_dumper_spec.rb → shared/mysql_schema_dumper_spec.rb} +24 -27
  36. data/spec/spec_helper.rb +25 -21
  37. metadata +131 -84
  38. data/VERSION +0 -1
  39. data/spec/README.txt +0 -22
  40. data/spec/mysql/connection_adapter_spec.rb +0 -106
  41. data/spec/mysql/models_spec.rb +0 -65
  42. data/spec/mysql2/connection_adapter_spec.rb +0 -106
  43. data/spec/mysql2/migration_spec.rb +0 -64
  44. data/spec/mysql2/models_spec.rb +0 -65
  45. data/spec/mysql2/schema_dumper_spec.rb +0 -56
@@ -1,388 +1,379 @@
1
1
  require 'spatial_adapter'
2
2
  require 'active_record/connection_adapters/postgresql_adapter'
3
3
 
4
- ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do
5
- include SpatialAdapter
6
-
7
- def postgis_version
8
- begin
9
- select_value("SELECT postgis_full_version()").scan(/POSTGIS="([\d\.]*)"/)[0][0]
10
- rescue ActiveRecord::StatementInvalid
11
- nil
4
+ module ActiveRecord::ConnectionAdapters
5
+ class PostgreSQLAdapter
6
+ def postgis_version
7
+ begin
8
+ select_value("SELECT postgis_full_version()").scan(/POSTGIS="([\d\.]*)"/)[0][0]
9
+ rescue ActiveRecord::StatementInvalid
10
+ nil
11
+ end
12
12
  end
13
- end
14
-
15
- def postgis_major_version
16
- version = postgis_version
17
- version ? version.scan(/^(\d)\.\d\.\d$/)[0][0].to_i : nil
18
- end
19
-
20
- def postgis_minor_version
21
- version = postgis_version
22
- version ? version.scan(/^\d\.(\d)\.\d$/)[0][0].to_i : nil
23
- end
24
-
25
- def spatial?
26
- !postgis_version.nil?
27
- end
28
-
29
- def supports_geographic?
30
- postgis_major_version > 1 || (postgis_major_version == 1 && postgis_minor_version >= 5)
31
- end
32
-
33
- alias :original_native_database_types :native_database_types
34
- def native_database_types
35
- original_native_database_types.merge!(geometry_data_types)
36
- end
37
13
 
38
- alias :original_quote :quote
39
- #Redefines the quote method to add behaviour for when a Geometry is encountered
40
- def quote(value, column = nil)
41
- if value.kind_of?(GeoRuby::SimpleFeatures::Geometry)
42
- "'#{value.as_hex_ewkb}'"
43
- else
44
- original_quote(value,column)
14
+ def postgis_major_version
15
+ version = postgis_version
16
+ version ? version.scan(/^(\d)\.\d\.\d$/)[0][0].to_i : nil
17
+ end
18
+
19
+ def postgis_minor_version
20
+ version = postgis_version
21
+ version ? version.scan(/^\d\.(\d)\.\d$/)[0][0].to_i : nil
22
+ end
23
+
24
+ def spatial?
25
+ !postgis_version.nil?
26
+ end
27
+
28
+ def supports_geographic?
29
+ postgis_major_version > 1 || (postgis_major_version == 1 && postgis_minor_version >= 5)
30
+ end
31
+
32
+ alias :original_native_database_types :native_database_types
33
+ def native_database_types
34
+ original_native_database_types.merge!(SpatialAdapter.geometry_data_types)
35
+ end
36
+
37
+ alias :original_quote :quote
38
+ #Redefines the quote method to add behaviour for when a Geometry is encountered
39
+ def quote(value, column = nil)
40
+ if value.kind_of?(GeoRuby::SimpleFeatures::Geometry)
41
+ "'#{value.as_hex_ewkb}'"
42
+ else
43
+ original_quote(value,column)
44
+ end
45
45
  end
46
- end
47
46
 
48
- def columns(table_name, name = nil) #:nodoc:
49
- raw_geom_infos = column_spatial_info(table_name)
50
-
51
- column_definitions(table_name).collect do |name, type, default, notnull|
52
- case type
53
- when /geography/i
54
- ActiveRecord::ConnectionAdapters::SpatialPostgreSQLColumn.create_from_geography(name, default, type, notnull == 'f')
55
- when /geometry/i
56
- raw_geom_info = raw_geom_infos[name]
57
- if raw_geom_info.nil?
58
- # This column isn't in the geometry_columns table, so we don't know anything else about it
59
- ActiveRecord::ConnectionAdapters::SpatialPostgreSQLColumn.create_simplified(name, default, notnull == "f")
47
+ def columns(table_name, name = nil) #:nodoc:
48
+ raw_geom_infos = column_spatial_info(table_name)
49
+
50
+ column_definitions(table_name).collect do |name, type, default, notnull|
51
+ case type
52
+ when /geography/i
53
+ ActiveRecord::ConnectionAdapters::SpatialPostgreSQLColumn.create_from_geography(name, default, type, notnull == 'f')
54
+ when /geometry/i
55
+ raw_geom_info = raw_geom_infos[name]
56
+ if raw_geom_info.nil?
57
+ # This column isn't in the geometry_columns table, so we don't know anything else about it
58
+ ActiveRecord::ConnectionAdapters::SpatialPostgreSQLColumn.create_simplified(name, default, notnull == "f")
59
+ else
60
+ ActiveRecord::ConnectionAdapters::SpatialPostgreSQLColumn.new(name, default, raw_geom_info.type, notnull == "f", raw_geom_info.srid, raw_geom_info.with_z, raw_geom_info.with_m)
61
+ end
60
62
  else
61
- ActiveRecord::ConnectionAdapters::SpatialPostgreSQLColumn.new(name, default, raw_geom_info.type, notnull == "f", raw_geom_info.srid, raw_geom_info.with_z, raw_geom_info.with_m)
63
+ ActiveRecord::ConnectionAdapters::PostgreSQLColumn.new(name, default, type, notnull == "f")
62
64
  end
63
- else
64
- ActiveRecord::ConnectionAdapters::PostgreSQLColumn.new(name, default, type, notnull == "f")
65
65
  end
66
66
  end
67
- end
68
67
 
69
- def create_table(table_name, options = {})
70
- # Using the subclassed table definition
71
- table_definition = ActiveRecord::ConnectionAdapters::PostgreSQLTableDefinition.new(self)
72
- table_definition.primary_key(options[:primary_key] || ActiveRecord::Base.get_primary_key(table_name.to_s.singularize)) unless options[:id] == false
68
+ def create_table(table_name, options = {})
69
+ # Using the subclassed table definition
70
+ table_definition = ActiveRecord::ConnectionAdapters::PostgreSQLTableDefinition.new(self)
71
+ table_definition.primary_key(options[:primary_key] || ActiveRecord::Base.get_primary_key(table_name.to_s.singularize)) unless options[:id] == false
73
72
 
74
- yield table_definition if block_given?
73
+ yield table_definition if block_given?
75
74
 
76
- if options[:force] && table_exists?(table_name)
77
- drop_table(table_name, options)
78
- end
75
+ if options[:force] && table_exists?(table_name)
76
+ drop_table(table_name, options)
77
+ end
79
78
 
80
- create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE "
81
- create_sql << "#{quote_table_name(table_name)} ("
82
- create_sql << table_definition.to_sql
83
- create_sql << ") #{options[:options]}"
79
+ create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE "
80
+ create_sql << "#{quote_table_name(table_name)} ("
81
+ create_sql << table_definition.to_sql
82
+ create_sql << ") #{options[:options]}"
84
83
 
85
- # This is the additional portion for PostGIS
86
- unless table_definition.geom_columns.nil?
87
- table_definition.geom_columns.each do |geom_column|
88
- geom_column.table_name = table_name
89
- create_sql << "; " + geom_column.to_sql
84
+ # This is the additional portion for PostGIS
85
+ unless table_definition.geom_columns.nil?
86
+ table_definition.geom_columns.each do |geom_column|
87
+ geom_column.table_name = table_name
88
+ create_sql << "; " + geom_column.to_sql
89
+ end
90
90
  end
91
- end
92
91
 
93
- execute create_sql
94
- end
92
+ execute create_sql
93
+ end
95
94
 
96
- alias :original_remove_column :remove_column
97
- def remove_column(table_name, *column_names)
98
- column_names = column_names.flatten
99
- columns(table_name).each do |col|
100
- if column_names.include?(col.name.to_sym)
101
- # Geometry columns have to be removed using DropGeometryColumn
102
- if col.is_a?(SpatialColumn) && col.spatial? && !col.geographic?
103
- execute "SELECT DropGeometryColumn('#{table_name}','#{col.name}')"
104
- else
105
- original_remove_column(table_name, col.name)
95
+ alias :original_remove_column :remove_column
96
+ def remove_column(table_name, *column_names)
97
+ column_names = column_names.flatten
98
+ columns(table_name).each do |col|
99
+ if column_names.include?(col.name.to_sym)
100
+ # Geometry columns have to be removed using DropGeometryColumn
101
+ if col.is_a?(::SpatialAdapter::SpatialColumn) && col.spatial? && !col.geographic?
102
+ execute "SELECT DropGeometryColumn('#{table_name}','#{col.name}')"
103
+ else
104
+ original_remove_column(table_name, col.name)
105
+ end
106
106
  end
107
107
  end
108
108
  end
109
- end
110
-
111
- alias :original_add_column :add_column
112
- def add_column(table_name, column_name, type, options = {})
113
- unless geometry_data_types[type].nil?
114
- geom_column = ActiveRecord::ConnectionAdapters::PostgreSQLColumnDefinition.new(self, column_name, type, nil, nil, options[:null], options[:srid] || -1 , options[:with_z] || false , options[:with_m] || false, options[:geographic] || false)
115
- if geom_column.geographic
116
- default = options[:default]
117
- notnull = options[:null] == false
118
-
119
- execute("ALTER TABLE #{quote_table_name(table_name)} ADD COLUMN #{geom_column.to_sql}")
120
-
121
- change_column_default(table_name, column_name, default) if options_include_default?(options)
122
- change_column_null(table_name, column_name, false, default) if notnull
109
+
110
+ alias :original_add_column :add_column
111
+ def add_column(table_name, column_name, type, options = {})
112
+ unless SpatialAdapter.geometry_data_types[type].nil?
113
+ geom_column = ActiveRecord::ConnectionAdapters::PostgreSQLColumnDefinition.new(self, column_name, type, nil, nil, options[:null], options[:srid] || -1 , options[:with_z] || false , options[:with_m] || false, options[:geographic] || false)
114
+ if geom_column.geographic
115
+ default = options[:default]
116
+ notnull = options[:null] == false
117
+
118
+ execute("ALTER TABLE #{quote_table_name(table_name)} ADD COLUMN #{geom_column.to_sql}")
119
+
120
+ change_column_default(table_name, column_name, default) if options_include_default?(options)
121
+ change_column_null(table_name, column_name, false, default) if notnull
122
+ else
123
+ geom_column.table_name = table_name
124
+ execute geom_column.to_sql
125
+ end
123
126
  else
124
- geom_column.table_name = table_name
125
- execute geom_column.to_sql
127
+ original_add_column(table_name, column_name, type, options)
126
128
  end
127
- else
128
- original_add_column(table_name, column_name, type, options)
129
129
  end
130
- end
131
130
 
132
- # Adds an index to a column.
133
- def add_index(table_name, column_name, options = {})
134
- column_names = Array(column_name)
135
- index_name = index_name(table_name, :column => column_names)
136
-
137
- if Hash === options # legacy support, since this param was a string
138
- index_type = options[:unique] ? "UNIQUE" : ""
139
- index_name = options[:name] || index_name
140
- index_method = options[:spatial] ? 'USING GIST' : ""
141
- else
142
- index_type = options
131
+ # Adds an index to a column.
132
+ def add_index(table_name, column_name, options = {})
133
+ column_names = Array(column_name)
134
+ index_name = index_name(table_name, :column => column_names)
135
+
136
+ if Hash === options # legacy support, since this param was a string
137
+ index_type = options[:unique] ? "UNIQUE" : ""
138
+ index_name = options[:name] || index_name
139
+ index_method = options[:spatial] ? 'USING GIST' : ""
140
+ else
141
+ index_type = options
142
+ end
143
+ quoted_column_names = column_names.map { |e| quote_column_name(e) }.join(", ")
144
+ execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} #{index_method} (#{quoted_column_names})"
143
145
  end
144
- quoted_column_names = column_names.map { |e| quote_column_name(e) }.join(", ")
145
- execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} #{index_method} (#{quoted_column_names})"
146
- end
147
146
 
148
- # Returns the list of all indexes for a table.
149
- #
150
- # This is a full replacement for the ActiveRecord method and as a result
151
- # has a higher probability of breaking in future releases.
152
- def indexes(table_name, name = nil)
153
- schemas = schema_search_path.split(/,/).map { |p| quote(p) }.join(',')
154
-
155
- # Changed from upstread: link to pg_am to grab the index type (e.g. "gist")
156
- result = query(<<-SQL, name)
157
- SELECT distinct i.relname, d.indisunique, d.indkey, t.oid, am.amname
158
- FROM pg_class t, pg_class i, pg_index d, pg_attribute a, pg_am am
159
- WHERE i.relkind = 'i'
160
- AND d.indexrelid = i.oid
161
- AND d.indisprimary = 'f'
162
- AND t.oid = d.indrelid
163
- AND t.relname = '#{table_name}'
164
- AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname IN (#{schemas}) )
165
- AND i.relam = am.oid
166
- AND a.attrelid = t.oid
167
- ORDER BY i.relname
168
- SQL
169
-
170
-
171
- indexes = []
172
-
173
- indexes = result.map do |row|
174
- index_name = row[0]
175
- unique = row[1] == 't'
176
- indkey = row[2].split(" ")
177
- oid = row[3]
178
- indtype = row[4]
179
-
180
- # Changed from upstream: need to get the column types to test for spatial indexes
181
- columns = query(<<-SQL, "Columns for index #{row[0]} on #{table_name}").inject({}) {|attlist, r| attlist[r[1]] = [r[0], r[2]]; attlist}
182
- SELECT a.attname, a.attnum, t.typname
183
- FROM pg_attribute a, pg_type t
184
- WHERE a.attrelid = #{oid}
185
- AND a.attnum IN (#{indkey.join(",")})
186
- AND a.atttypid = t.oid
147
+ # Returns the list of all indexes for a table.
148
+ #
149
+ # This is a full replacement for the ActiveRecord method and as a result
150
+ # has a higher probability of breaking in future releases.
151
+ def indexes(table_name, name = nil)
152
+ schemas = schema_search_path.split(/,/).map { |p| quote(p) }.join(',')
153
+
154
+ # Changed from upstread: link to pg_am to grab the index type (e.g. "gist")
155
+ result = query(<<-SQL, name)
156
+ SELECT distinct i.relname, d.indisunique, d.indkey, t.oid, am.amname
157
+ FROM pg_class t, pg_class i, pg_index d, pg_attribute a, pg_am am
158
+ WHERE i.relkind = 'i'
159
+ AND d.indexrelid = i.oid
160
+ AND d.indisprimary = 'f'
161
+ AND t.oid = d.indrelid
162
+ AND t.relname = '#{table_name}'
163
+ AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname IN (#{schemas}) )
164
+ AND i.relam = am.oid
165
+ AND a.attrelid = t.oid
166
+ ORDER BY i.relname
187
167
  SQL
188
168
 
189
- # Only GiST indexes on spatial columns denote a spatial index
190
- spatial = indtype == 'gist' && columns.size == 1 && (columns.values.first[1] == 'geometry' || columns.values.first[1] == 'geography')
191
169
 
192
- column_names = indkey.map {|attnum| columns[attnum] ? columns[attnum][0] : nil }
193
- ActiveRecord::ConnectionAdapters::IndexDefinition.new(table_name, index_name, unique, column_names, spatial)
194
- end
170
+ indexes = []
195
171
 
196
- indexes
197
- end
172
+ indexes = result.map do |row|
173
+ index_name = row[0]
174
+ unique = row[1] == 't'
175
+ indkey = row[2].split(" ")
176
+ oid = row[3]
177
+ indtype = row[4]
198
178
 
199
- def disable_referential_integrity(&block) #:nodoc:
200
- if supports_disable_referential_integrity?() then
201
- execute(tables_without_postgis.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";"))
202
- end
203
- yield
204
- ensure
205
- if supports_disable_referential_integrity?() then
206
- execute(tables_without_postgis.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(";"))
179
+ # Changed from upstream: need to get the column types to test for spatial indexes
180
+ columns = query(<<-SQL, "Columns for index #{row[0]} on #{table_name}").inject({}) {|attlist, r| attlist[r[1]] = [r[0], r[2]]; attlist}
181
+ SELECT a.attname, a.attnum, t.typname
182
+ FROM pg_attribute a, pg_type t
183
+ WHERE a.attrelid = #{oid}
184
+ AND a.attnum IN (#{indkey.join(",")})
185
+ AND a.atttypid = t.oid
186
+ SQL
187
+
188
+ # Only GiST indexes on spatial columns denote a spatial index
189
+ spatial = indtype == 'gist' && columns.size == 1 && (columns.values.first[1] == 'geometry' || columns.values.first[1] == 'geography')
190
+
191
+ column_names = indkey.map {|attnum| columns[attnum] ? columns[attnum][0] : nil }
192
+ ActiveRecord::ConnectionAdapters::IndexDefinition.new(table_name, index_name, unique, column_names, spatial)
193
+ end
194
+
195
+ indexes
207
196
  end
208
- end
209
197
 
210
- private
211
-
212
- def tables_without_postgis
213
- tables - %w{ geometry_columns spatial_ref_sys }
214
- end
215
-
216
- def column_spatial_info(table_name)
217
- constr = query("SELECT * FROM geometry_columns WHERE f_table_name = '#{table_name}'")
218
-
219
- raw_geom_infos = {}
220
- constr.each do |constr_def_a|
221
- raw_geom_infos[constr_def_a[3]] ||= SpatialAdapter::RawGeomInfo.new
222
- raw_geom_infos[constr_def_a[3]].type = constr_def_a[6]
223
- raw_geom_infos[constr_def_a[3]].dimension = constr_def_a[4].to_i
224
- raw_geom_infos[constr_def_a[3]].srid = constr_def_a[5].to_i
225
-
226
- if raw_geom_infos[constr_def_a[3]].type[-1] == ?M
227
- raw_geom_infos[constr_def_a[3]].with_m = true
228
- raw_geom_infos[constr_def_a[3]].type.chop!
229
- else
230
- raw_geom_infos[constr_def_a[3]].with_m = false
198
+ def disable_referential_integrity(&block) #:nodoc:
199
+ if supports_disable_referential_integrity?() then
200
+ execute(tables_without_postgis.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";"))
201
+ end
202
+ yield
203
+ ensure
204
+ if supports_disable_referential_integrity?() then
205
+ execute(tables_without_postgis.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(";"))
231
206
  end
232
207
  end
233
208
 
234
- raw_geom_infos.each_value do |raw_geom_info|
235
- #check the presence of z and m
236
- raw_geom_info.convert!
209
+ private
210
+
211
+ def tables_without_postgis
212
+ tables - %w{ geometry_columns spatial_ref_sys }
237
213
  end
238
214
 
239
- raw_geom_infos
215
+ def column_spatial_info(table_name)
216
+ constr = query("SELECT * FROM geometry_columns WHERE f_table_name = '#{table_name}'")
240
217
 
241
- end
242
- end
218
+ raw_geom_infos = {}
219
+ constr.each do |constr_def_a|
220
+ raw_geom_infos[constr_def_a[3]] ||= SpatialAdapter::RawGeomInfo.new
221
+ raw_geom_infos[constr_def_a[3]].type = constr_def_a[6]
222
+ raw_geom_infos[constr_def_a[3]].dimension = constr_def_a[4].to_i
223
+ raw_geom_infos[constr_def_a[3]].srid = constr_def_a[5].to_i
243
224
 
244
- module ActiveRecord
245
- module ConnectionAdapters
246
- class PostgreSQLTableDefinition < TableDefinition
247
- attr_reader :geom_columns
248
-
249
- def column(name, type, options = {})
250
- unless (@base.geometry_data_types[type.to_sym].nil? or
251
- (options[:create_using_addgeometrycolumn] == false))
252
-
253
- column = self[name] || PostgreSQLColumnDefinition.new(@base, name, type)
254
- column.null = options[:null]
255
- column.srid = options[:srid] || -1
256
- column.with_z = options[:with_z] || false
257
- column.with_m = options[:with_m] || false
258
- column.geographic = options[:geographic] || false
259
-
260
- if column.geographic
261
- @columns << column unless @columns.include? column
262
- else
263
- # Hold this column for later
264
- @geom_columns ||= []
265
- @geom_columns << column
266
- end
267
- self
225
+ if raw_geom_infos[constr_def_a[3]].type[-1] == ?M
226
+ raw_geom_infos[constr_def_a[3]].with_m = true
227
+ raw_geom_infos[constr_def_a[3]].type.chop!
268
228
  else
269
- super(name, type, options)
229
+ raw_geom_infos[constr_def_a[3]].with_m = false
270
230
  end
271
- end
272
- end
231
+ end
273
232
 
274
- class PostgreSQLColumnDefinition < ColumnDefinition
275
- attr_accessor :table_name
276
- attr_accessor :srid, :with_z, :with_m, :geographic
277
- attr_reader :spatial
278
-
279
- def initialize(base = nil, name = nil, type=nil, limit=nil, default=nil, null=nil, srid=-1, with_z=false, with_m=false, geographic=false)
280
- super(base, name, type, limit, default, null)
281
- @table_name = nil
282
- @spatial = true
283
- @srid = srid
284
- @with_z = with_z
285
- @with_m = with_m
286
- @geographic = geographic
233
+ raw_geom_infos.each_value do |raw_geom_info|
234
+ #check the presence of z and m
235
+ raw_geom_info.convert!
287
236
  end
288
-
289
- def sql_type
290
- if geographic
291
- type_sql = base.geometry_data_types[type.to_sym][:name]
292
- type_sql += "Z" if with_z
293
- type_sql += "M" if with_m
294
- # SRID is not yet supported (defaults to 4326)
295
- #type_sql += ", #{srid}" if (srid && srid != -1)
296
- type_sql = "geography(#{type_sql})"
297
- type_sql
237
+
238
+ raw_geom_infos
239
+ end
240
+ end
241
+
242
+ class PostgreSQLTableDefinition < TableDefinition
243
+ attr_reader :geom_columns
244
+
245
+ def column(name, type, options = {})
246
+ unless (SpatialAdapter.geometry_data_types[type.to_sym].nil? or
247
+ (options[:create_using_addgeometrycolumn] == false))
248
+
249
+ column = self[name] || PostgreSQLColumnDefinition.new(@base, name, type)
250
+ column.null = options[:null]
251
+ column.srid = options[:srid] || -1
252
+ column.with_z = options[:with_z] || false
253
+ column.with_m = options[:with_m] || false
254
+ column.geographic = options[:geographic] || false
255
+
256
+ if column.geographic
257
+ @columns << column unless @columns.include? column
298
258
  else
299
- super
259
+ # Hold this column for later
260
+ @geom_columns ||= []
261
+ @geom_columns << column
300
262
  end
263
+ self
264
+ else
265
+ super(name, type, options)
301
266
  end
302
-
303
- def to_sql
304
- if spatial && !geographic
305
- type_sql = base.geometry_data_types[type.to_sym][:name]
306
- type_sql += "M" if with_m and !with_z
307
- if with_m and with_z
308
- dimension = 4
309
- elsif with_m or with_z
310
- dimension = 3
311
- else
312
- dimension = 2
313
- end
314
-
315
- column_sql = "SELECT AddGeometryColumn('#{table_name}','#{name}',#{srid},'#{type_sql}',#{dimension})"
316
- column_sql += ";ALTER TABLE #{table_name} ALTER #{name} SET NOT NULL" if null == false
317
- column_sql
267
+ end
268
+ end
269
+
270
+ class PostgreSQLColumnDefinition < ColumnDefinition
271
+ attr_accessor :table_name
272
+ attr_accessor :srid, :with_z, :with_m, :geographic
273
+ attr_reader :spatial
274
+
275
+ def initialize(base = nil, name = nil, type=nil, limit=nil, default=nil, null=nil, srid=-1, with_z=false, with_m=false, geographic=false)
276
+ super(base, name, type, limit, default, null)
277
+ @table_name = nil
278
+ @spatial = true
279
+ @srid = srid
280
+ @with_z = with_z
281
+ @with_m = with_m
282
+ @geographic = geographic
283
+ end
284
+
285
+ def sql_type
286
+ if geographic
287
+ type_sql = SpatialAdapter.geometry_data_types[type.to_sym][:name]
288
+ type_sql += "Z" if with_z
289
+ type_sql += "M" if with_m
290
+ # SRID is not yet supported (defaults to 4326)
291
+ #type_sql += ", #{srid}" if (srid && srid != -1)
292
+ type_sql = "geography(#{type_sql})"
293
+ type_sql
294
+ else
295
+ super
296
+ end
297
+ end
298
+
299
+ def to_sql
300
+ if spatial && !geographic
301
+ type_sql = SpatialAdapter.geometry_data_types[type.to_sym][:name]
302
+ type_sql += "M" if with_m and !with_z
303
+ if with_m and with_z
304
+ dimension = 4
305
+ elsif with_m or with_z
306
+ dimension = 3
318
307
  else
319
- super
308
+ dimension = 2
320
309
  end
310
+
311
+ column_sql = "SELECT AddGeometryColumn('#{table_name}','#{name}',#{srid},'#{type_sql}',#{dimension})"
312
+ column_sql += ";ALTER TABLE #{table_name} ALTER #{name} SET NOT NULL" if null == false
313
+ column_sql
314
+ else
315
+ super
321
316
  end
322
317
  end
323
318
  end
324
- end
325
319
 
326
- module ActiveRecord
327
- module ConnectionAdapters
328
- class SpatialPostgreSQLColumn < PostgreSQLColumn
329
- include SpatialAdapter::SpatialColumn
320
+ class SpatialPostgreSQLColumn < PostgreSQLColumn
321
+ include SpatialAdapter::SpatialColumn
330
322
 
331
- def initialize(name, default, sql_type = nil, null = true, srid=-1, with_z=false, with_m=false, geographic = false)
332
- super(name, default, sql_type, null, srid, with_z, with_m)
333
- @geographic = geographic
334
- end
323
+ def initialize(name, default, sql_type = nil, null = true, srid=-1, with_z=false, with_m=false, geographic = false)
324
+ super(name, default, sql_type, null, srid, with_z, with_m)
325
+ @geographic = geographic
326
+ end
335
327
 
336
- def geographic?
337
- @geographic
338
- end
339
-
340
- #Transforms a string to a geometry. PostGIS returns a HewEWKB string.
341
- def self.string_to_geometry(string)
342
- return string unless string.is_a?(String)
343
- GeoRuby::SimpleFeatures::Geometry.from_hex_ewkb(string) rescue nil
344
- end
328
+ def geographic?
329
+ @geographic
330
+ end
345
331
 
346
- def self.create_simplified(name, default, null = true)
347
- new(name, default, "geometry", null)
348
- end
349
-
350
- def self.create_from_geography(name, default, sql_type, null = true)
351
- params = extract_geography_params(sql_type)
352
- new(name, default, sql_type, null, params[:srid], params[:with_z], params[:with_m], true)
353
- end
354
-
355
- private
356
-
357
- # Add detection of PostGIS-specific geography columns
358
- def geometry_simplified_type(sql_type)
359
- case sql_type
360
- when /geography\(point/i then :point
361
- when /geography\(linestring/i then :line_string
362
- when /geography\(polygon/i then :polygon
363
- when /geography\(multipoint/i then :multi_point
364
- when /geography\(multilinestring/i then :multi_line_string
365
- when /geography\(multipolygon/i then :multi_polygon
366
- when /geography\(geometrycollection/i then :geometry_collection
367
- when /geography/i then :geometry
368
- else
369
- super
370
- end
332
+ #Transforms a string to a geometry. PostGIS returns a HewEWKB string.
333
+ def self.string_to_geometry(string)
334
+ return string unless string.is_a?(String)
335
+ GeoRuby::SimpleFeatures::Geometry.from_hex_ewkb(string) rescue nil
336
+ end
337
+
338
+ def self.create_simplified(name, default, null = true)
339
+ new(name, default, "geometry", null)
340
+ end
341
+
342
+ def self.create_from_geography(name, default, sql_type, null = true)
343
+ params = extract_geography_params(sql_type)
344
+ new(name, default, sql_type, null, params[:srid], params[:with_z], params[:with_m], true)
345
+ end
346
+
347
+ private
348
+
349
+ # Add detection of PostGIS-specific geography columns
350
+ def geometry_simplified_type(sql_type)
351
+ case sql_type
352
+ when /geography\(point/i then :point
353
+ when /geography\(linestring/i then :line_string
354
+ when /geography\(polygon/i then :polygon
355
+ when /geography\(multipoint/i then :multi_point
356
+ when /geography\(multilinestring/i then :multi_line_string
357
+ when /geography\(multipolygon/i then :multi_polygon
358
+ when /geography\(geometrycollection/i then :geometry_collection
359
+ when /geography/i then :geometry
360
+ else
361
+ super
371
362
  end
363
+ end
372
364
 
373
- def self.extract_geography_params(sql_type)
374
- params = {
375
- :srid => 0,
376
- :with_z => false,
377
- :with_m => false
378
- }
379
- if sql_type =~ /geography(?:\((?:\w+?)(Z)?(M)?(?:,(\d+))?\))?/i
380
- params[:with_z] = $1 == 'Z'
381
- params[:with_m] = $2 == 'M'
382
- params[:srid] = $3.to_i
383
- end
384
- params
365
+ def self.extract_geography_params(sql_type)
366
+ params = {
367
+ :srid => 0,
368
+ :with_z => false,
369
+ :with_m => false
370
+ }
371
+ if sql_type =~ /geography(?:\((?:\w+?)(Z)?(M)?(?:,(\d+))?\))?/i
372
+ params[:with_z] = $1 == 'Z'
373
+ params[:with_m] = $2 == 'M'
374
+ params[:srid] = $3.to_i
385
375
  end
376
+ params
386
377
  end
387
378
  end
388
379
  end