From 05d8135bb9ca595ea9939d7b810ba233d756593b Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Mon, 11 Oct 2010 14:15:45 -0500 Subject: [PATCH 1/3] Adding oracle_enhanced support to spatial_adapter. --- README.rdoc | 4 + Rakefile | 2 +- lib/spatial_adapter.rb | 1 + .../common/active_record_base.rb | 12 + .../common/schema_definitions.rb | 1 - lib/spatial_adapter/common/schema_dumper.rb | 14 +- lib/spatial_adapter/mysql.rb | 4 + lib/spatial_adapter/oracle_enhanced.rb | 651 ++++++++++++++++++ lib/spatial_adapter/postgresql.rb | 9 +- lib/spatial_adapter/railtie.rb | 2 +- rails/init.rb | 7 +- spec/db/oracle_enhanced_raw.rb | 199 ++++++ spec/mysql/models_spec.rb | 1 + .../connection_adapter_spec.rb | 122 ++++ spec/oracle_enhanced/migration_spec.rb | 234 +++++++ spec/oracle_enhanced/models_spec.rb | 103 +++ spec/oracle_enhanced/schema_dumper_spec.rb | 62 ++ spec/postgresql/models_spec.rb | 1 + spec/shared_examples.rb | 16 +- spec/spec_helper.rb | 40 +- 20 files changed, 1458 insertions(+), 27 deletions(-) create mode 100644 lib/spatial_adapter/common/active_record_base.rb create mode 100644 lib/spatial_adapter/oracle_enhanced.rb create mode 100644 spec/db/oracle_enhanced_raw.rb create mode 100644 spec/oracle_enhanced/connection_adapter_spec.rb create mode 100644 spec/oracle_enhanced/migration_spec.rb create mode 100644 spec/oracle_enhanced/models_spec.rb create mode 100644 spec/oracle_enhanced/schema_dumper_spec.rb diff --git a/README.rdoc b/README.rdoc index 118beff..c55733e 100644 --- a/README.rdoc +++ b/README.rdoc @@ -5,6 +5,7 @@ handle spatial datatypes in the following databases: - PostgreSQL (using PostGIS) - MySQL (using Spatial Extensions) +- Oracle (using Locator or Oracle Spatial) == Dependencies @@ -55,6 +56,7 @@ where [database] should be replaced with one of the following: - postgresql - mysql +- oracle_enhanced For example to use the PostgreSQL spatial adapter: @@ -104,6 +106,8 @@ Here is a related statement valid for MySql version <= 5.0.16: can have spatial columns. In addition, only MyISAM tables may have spatial indexes. +- On Oracle, :with_m is not supported. + === Models Create your ActiveRecord models normally. Spatial Adapter will automatically diff --git a/Rakefile b/Rakefile index afefa07..c886258 100644 --- a/Rakefile +++ b/Rakefile @@ -1,7 +1,7 @@ require 'rubygems' require 'spec/rake/spectask' -[:mysql, :postgresql].each do |adapter| +[:mysql, :postgresql, :oracle_enhanced].each do |adapter| desc "Run specs for #{adapter} adapter" Spec::Rake::SpecTask.new("spec:#{adapter.to_s}") do |t| t.spec_files = FileList["spec/#{adapter}/*_spec.rb"] diff --git a/lib/spatial_adapter.rb b/lib/spatial_adapter.rb index 01a8c31..a783eee 100644 --- a/lib/spatial_adapter.rb +++ b/lib/spatial_adapter.rb @@ -38,4 +38,5 @@ class NotCompatibleError < ::StandardError require 'spatial_adapter/common/schema_definitions' require 'spatial_adapter/common/schema_dumper' require 'spatial_adapter/common/table_definition' +require 'spatial_adapter/common/active_record_base' require 'spatial_adapter/railtie' if defined?(Rails::Railtie) diff --git a/lib/spatial_adapter/common/active_record_base.rb b/lib/spatial_adapter/common/active_record_base.rb new file mode 100644 index 0000000..579ec76 --- /dev/null +++ b/lib/spatial_adapter/common/active_record_base.rb @@ -0,0 +1,12 @@ + +ActiveRecord::Base.class_eval do + #require 'active_record/version' + + def self.spatial_conditions(geom1, relationship, geom2) + connection.spatial_conditions(geom1, relationship, geom2) + end + + def self.spherical_distance( geom1, geom2 ) + connection.spherical_distance( geom1, geom2 ) + end +end \ No newline at end of file diff --git a/lib/spatial_adapter/common/schema_definitions.rb b/lib/spatial_adapter/common/schema_definitions.rb index a003d3f..1b87827 100644 --- a/lib/spatial_adapter/common/schema_definitions.rb +++ b/lib/spatial_adapter/common/schema_definitions.rb @@ -2,7 +2,6 @@ ActiveRecord::ConnectionAdapters::IndexDefinition.class_eval do attr_accessor :spatial - alias_method :initialize_without_spatial, :initialize def initialize(table, name, unique, columns, spatial = false) initialize_without_spatial(table, name, unique, columns) diff --git a/lib/spatial_adapter/common/schema_dumper.rb b/lib/spatial_adapter/common/schema_dumper.rb index 854e333..6371f61 100644 --- a/lib/spatial_adapter/common/schema_dumper.rb +++ b/lib/spatial_adapter/common/schema_dumper.rb @@ -76,8 +76,12 @@ def table(table, stream) stream.print tbl.read rescue => e stream.puts "# Could not dump table #{table.inspect} because of following #{e.class}" - stream.puts "# #{e.message}" + stream.puts "# #{e.message} #{e.backtrace.join("\n")}" stream.puts + puts "# Could not dump table #{table.inspect} because of following #{e.class}" + puts "# #{e.message} #{e.backtrace.join("\n")}" + puts + raise e end stream @@ -116,7 +120,7 @@ def column_spec(column) else column.type.to_s end - spec[:limit] = column.limit.inspect if column.limit != @types[column.type][:limit] && spec[:type] != 'decimal' + spec[:limit] = column.limit.inspect if column.limit && column.limit != @types[column.type][:limit] && spec[:type] != 'decimal' spec[:precision] = column.precision.inspect if !column.precision.nil? spec[:scale] = column.scale.inspect if !column.scale.nil? spec[:null] = 'false' if !column.null @@ -126,7 +130,11 @@ def column_spec(column) if column.is_a?(SpatialColumn) # Override with specific geometry type spec[:type] = column.geometry_type.to_s - spec[:srid] = column.srid.inspect if column.srid != -1 + if column.srid == @connection.default_srid + spec[:srid] = ":default_srid" + elsif column.srid != -1 + spec[:srid] = column.srid.inspect + end spec[:with_z] = 'true' if column.with_z spec[:with_m] = 'true' if column.with_m spec[:geographic] = 'true' if column.geographic? diff --git a/lib/spatial_adapter/mysql.rb b/lib/spatial_adapter/mysql.rb index 8a3b6ed..a71af4a 100644 --- a/lib/spatial_adapter/mysql.rb +++ b/lib/spatial_adapter/mysql.rb @@ -8,6 +8,10 @@ def supports_geographic? false end + def default_srid + 4326 + end + alias :original_native_database_types :native_database_types def native_database_types original_native_database_types.merge!(geometry_data_types) diff --git a/lib/spatial_adapter/oracle_enhanced.rb b/lib/spatial_adapter/oracle_enhanced.rb new file mode 100644 index 0000000..5814a7f --- /dev/null +++ b/lib/spatial_adapter/oracle_enhanced.rb @@ -0,0 +1,651 @@ +require 'spatial_adapter' +require 'active_record/connection_adapters/oracle_enhanced_adapter' + +include GeoRuby::SimpleFeatures +include SpatialAdapter + + +module ActiveRecord + module ConnectionAdapters + class OracleEnhancedAdapter + + def spatial? + rval = select_value("select object_id from all_objects where owner = 'MDSYS' and object_name = 'SDO_GEOMETRY'").to_i + rval > 0 ? true : false + end + + def columns_without_cache(table_name, name = nil) #:nodoc: + table_name = table_name.to_s + # get ignored_columns by original table name + ignored_columns = ignored_table_columns(table_name) + + (owner, desc_table_name, db_link) = @connection.describe(table_name) + + @@do_not_prefetch_primary_key[table_name] = + !has_primary_key?(table_name, owner, desc_table_name, db_link) || + has_primary_key_trigger?(table_name, owner, desc_table_name, db_link) + + table_cols = <<-SQL + select column_name as name, data_type as sql_type, data_default, nullable, + decode(data_type, 'NUMBER', data_precision, + 'FLOAT', data_precision, + 'VARCHAR2', decode(char_used, 'C', char_length, data_length), + 'CHAR', decode(char_used, 'C', char_length, data_length), + null) as limit, + decode(data_type, 'NUMBER', data_scale, null) as scale + from all_tab_columns#{db_link} + where owner = '#{owner}' + and table_name = '#{desc_table_name}' + order by column_id + SQL + + raw_geom_infos = column_spatial_info(desc_table_name) + + # added deletion of ignored columns + select_all(table_cols, name).delete_if do |row| + ignored_columns && ignored_columns.include?(row['name'].downcase) + end.map do |row| + limit, scale = row['limit'], row['scale'] + if limit || scale + row['sql_type'] += "(#{(limit || 38).to_i}" + ((scale = scale.to_i) > 0 ? ",#{scale})" : ")") + end + + # clean up odd default spacing from Oracle + if row['data_default'] + row['data_default'].sub!(/^(.*?)\s*$/, '\1') + + # If a default contains a newline these cleanup regexes need to + # match newlines. + row['data_default'].sub!(/^'(.*)'$/m, '\1') + row['data_default'] = nil if row['data_default'] =~ /^(null|empty_[bc]lob\(\))$/i + end + + if row['sql_type'] =~ /sdo_geometry/i + raw_geom_info = raw_geom_infos[oracle_downcase(row['name'])] + if(raw_geom_info.nil?) + puts "Couldn't find geom info for #{row['name']} in #{raw_geom_infos.inspect}" + end + ActiveRecord::ConnectionAdapters::SpatialOracleColumn.new( + oracle_downcase(row['name']), + row['data_default'], + raw_geom_info.type, + row['nullable'] == 'Y', + raw_geom_info.srid, + raw_geom_info.with_z, + raw_geom_info.with_m) + else + OracleEnhancedColumn.new(oracle_downcase(row['name']), + row['data_default'], + row['sql_type'], + row['nullable'] == 'Y', + # pass table name for table specific column definitions + table_name, + # pass column type if specified in class definition + get_type_for_column(table_name, oracle_downcase(row['name']))) + end + end + end + end + end +end + +ActiveRecord::ConnectionAdapters::OracleEnhancedIndexDefinition.class_eval do + def spatial + type == "MDSYS.SPATIAL_INDEX" + end +end + +ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.class_eval do + + include SpatialAdapter + + SPATIAL_TOLERANCE = 0.5 + + def supports_geographic? + false + end + + def default_srid + 8307 + end + + alias :original_native_database_types :native_database_types + def native_database_types + types = original_native_database_types.merge!(geometry_data_types) + + # Change mapping of :float from NUMBER to FLOAT, as oracle + # recognizes the ansi keyword and it helps keep the schema + # stable, even though it ends up mapping to the same underlying + # native type. + types[:float] = {:name => "FLOAT", :limit => 126 } + + types + end + + #Redefines the quote method to add behaviour for when a Geometry is encountered ; used when binding variables in find_by methods + def quote(value, column = nil) + if value.kind_of?(GeoRuby::SimpleFeatures::Geometry) + quote_generic_geom( value ) + elsif value && column && [:text, :binary].include?(column.type) + %Q{empty_#{ column.sql_type.downcase rescue 'blob' }()} + else + super + end + end + + def quote_point_geom( value ) + if !value.with_z && !value.with_m + "SDO_GEOMETRY( 2001, #{value.srid}, MDSYS.SDO_POINT_TYPE( #{value.x}, #{value.y}, NULL ), NULL, NULL )" + elsif value.with_z && value.with_m + raise ArgumentError, "4d points not currently supported in the oracle_enhanced spatial adapter." + #"SDO_GEOMETRY( 2001, #{value.srid}, MDSYS.SDO_POINT_TYPE( #{value.x}, #{value.y}, #{value.z}, #{value.m} ), NULL, NULL )" + elsif value.with_z + "SDO_GEOMETRY( 3001, #{value.srid}, MDSYS.SDO_POINT_TYPE( #{value.x}, #{value.y}, #{value.z} ), NULL, NULL )" + elsif value.with_m + raise ArgumentError, "3d points (with M coord) not currently supported in the oracle_enhanced spatial adapter." + #"SDO_GEOMETRY( 2001, #{value.srid}, MDSYS.SDO_POINT_TYPE( #{value.x}, #{value.y}, #{value.m} ), NULL, NULL )" + end + end + + # This technique only supports 2d geometries + def quote_generic_geom( value ) + if value.kind_of?( GeoRuby::SimpleFeatures::Point ) + # Small optimization for the most commonly used type: points. Oracle's WKT parsing seems slow + quote_point_geom( value ) + else + "SDO_GEOMETRY( '#{value.as_wkt}', #{value.srid} )" + end + end + + def create_table(name, options = {}) + table_definition = ActiveRecord::ConnectionAdapters::OracleTableDefinition.new(self) + table_definition.primary_key(options[:primary_key] || "id") unless options[:id] == false + + yield table_definition if block_given? + + # table_exists? is slow + #if options[:force] && table_exists?(name) + if options[:force] + drop_table(name) rescue nil + end + + create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE " + create_sql << "#{name} (" + create_sql << table_definition.to_sql + create_sql << ") #{options[:options]}" + execute create_sql + execute "CREATE SEQUENCE #{name}_seq START WITH 10000" unless options[:id] == false + + #added to create the geometric columns identified during the table definition + unless table_definition.geom_columns.nil? + table_definition.geom_columns.each do |geom_column| + geom_column.sql_create_statements(name).each do |stmt| + execute stmt + end + end + end + end + + # We override this for three reasons: + # 1) Drop spatial indexes before dropping the tables, as "drop table cascade constraints" + # seems to leave metadata around in SDO_INDEX_METADATA_TABLE + # 2) Avoid dropping sequences that end in '$', as those are for the spatial indexes (which get dropped above) + # 3) Need to clear out USER_SDO_GEOM_METADATA after everything else is done. + def structure_drop + s = [] + select_values("select sdo_index_name from user_sdo_index_metadata").uniq.each do |idx| + s << "DROP INDEX \"#{idx}\"" + end + + select_values("select sequence_name from user_sequences order by 1").each do |seq| + s << "DROP SEQUENCE \"#{seq}\"" unless seq[-1,1] == '$' + end + select_values("select table_name from all_tables t + where owner = sys_context('userenv','session_user') and secondary='N' + and not exists (select mv.mview_name from all_mviews mv where mv.owner = t.owner and mv.mview_name = t.table_name) + and not exists (select mvl.log_table from all_mview_logs mvl where mvl.log_owner = t.owner and mvl.log_table = t.table_name) + order by 1").each do |table| + s << "DROP TABLE \"#{table}\" CASCADE CONSTRAINTS" + end + s << "DELETE FROM USER_SDO_GEOM_METADATA" + join_with_statement_token(s) + end + + alias :original_remove_column :remove_column + def remove_column(table_name,column_name) + columns(table_name).each do |col| + if col.name.to_s == column_name.to_s + #check if the column is geometric + if col.is_a?(SpatialColumn) && col.spatial? + execute "DELETE FROM USER_SDO_GEOM_METADATA WHERE TABLE_NAME = '#{table_name.upcase}' AND COLUMN_NAME = '#{column_name.to_s.upcase}'" + execute "ALTER TABLE #{table_name} DROP COLUMN #{column_name}" + else + original_remove_column(table_name,column_name) + end + end + end + end + + alias :original_add_column :add_column + def add_column(table_name, column_name, type, options = {}) + unless geometry_data_types[type].nil? + geom_column = ActiveRecord::ConnectionAdapters::OracleColumnDefinition.new(self,column_name, type, nil,nil,options[:null],options[:srid] || -1 , options[:with_z] || false , options[:with_m] || false) + geom_column.sql_create_statements(table_name).each do |stmt| + execute stmt + end + else + original_add_column(table_name,column_name,type,options) + end + end + + #Adds a spatial index to a column. Its name will be index__on_ unless the key :name is present in the options hash, in which case its value is taken as the name of the index. + def add_index(table_name,column_name,options = {}) + # invalidate index cache + self.all_schema_indexes = nil + index_name = options[:name] || "index_#{table_name}_on_#{column_name}" + if options[:spatial] + execute "CREATE INDEX #{index_name} ON #{table_name} (#{column_name}) INDEXTYPE IS mdsys.spatial_index" + else + index_type = options[:unique] ? "UNIQUE" : "" + #all together + execute "CREATE #{index_type} INDEX #{index_name} ON #{table_name} (#{Array(column_name).join(", ")})" + end + end + + private + + def column_spatial_info(table_name) + rows = select_all <<-end_sql + SELECT column_name, diminfo, srid + FROM user_sdo_geom_metadata + WHERE table_name = '#{table_name}' + end_sql + + raw_geom_infos = {} + rows.each do |row| + column_name = oracle_downcase(row['column_name']) + dims = row['diminfo'].to_ary + raw_geom_info = raw_geom_infos[column_name] || ActiveRecord::ConnectionAdapters::RawGeomInfo.new + raw_geom_info.type = "geometry" + raw_geom_info.with_m = dims.any? {|d| d.sdo_dimname == 'M'} + raw_geom_info.with_z = dims.any? {|d| d.sdo_dimname == 'Z'} + raw_geom_info.srid = row['srid'].to_i + #raw_geom_info.diminfo = row['diminfo'] + raw_geom_infos[column_name] = raw_geom_info + end #constr.each + + raw_geom_infos + end + +end + +module ActiveRecord + module ConnectionAdapters + class RawGeomInfo < Struct.new(:type,:srid,:dimension,:with_z,:with_m) #:nodoc: + end + end +end + + +module ActiveRecord + module ConnectionAdapters + class OracleTableDefinition < TableDefinition + attr_reader :geom_columns + + def column(name, type, options = {}) + unless @base.geometry_data_types[type.to_sym].nil? + geom_column = OracleColumnDefinition.new(@base, name, type) + geom_column.null = options[:null] + srid = options[:srid] || -1 + srid = @base.default_srid if srid == :default_srid + geom_column.srid = srid + geom_column.with_z = options[:with_z] || false + geom_column.with_m = options[:with_m] || false + + @geom_columns = [] if @geom_columns.nil? + @geom_columns << geom_column + else + super(name,type,options) + end + end + + SpatialAdapter.geometry_data_types.keys.each do |column_type| + class_eval <<-EOV + def #{column_type}(*args) + options = args.extract_options! + column_names = args + + column_names.each { |name| column(name, '#{column_type}', options) } + end + EOV + end + end + + class OracleColumnDefinition < ColumnDefinition + attr_accessor :srid, :with_z,:with_m + attr_reader :spatial + + def initialize(base = nil, name = nil, type=nil, limit=nil, default=nil,null=nil,srid=-1,with_z=false,with_m=false) + super(base, name, type, limit, default,null) + @spatial=true + @srid=srid + @with_z=with_z + @with_m=with_m + end + + def sql_create_statements(table_name) + type_sql = type_to_sql(type) + + #column_sql = "SELECT AddGeometryColumn('#{table_name}','#{name}',#{srid},'#{type_sql}',#{dimension})" + column_sql = "ALTER TABLE #{table_name} ADD (#{name} #{type_sql}" + column_sql += " NOT NULL" if null == false + column_sql += ")" + stmts = [column_sql] + if srid == 8307 # There are others we should probably support, but this is common + dim_elems = ["MDSYS.SDO_DIM_ELEMENT('X', -180.0, 180.0, 0.005)", + "MDSYS.SDO_DIM_ELEMENT('Y', -90.0, 90.0, 0.005)"] + else + dim_elems = ["MDSYS.SDO_DIM_ELEMENT('X', -1000, 1000, 0.005)", + "MDSYS.SDO_DIM_ELEMENT('Y', -1000, 1000, 0.005)"] + end + if @with_z + dim_elems << "MDSYS.SDO_DIM_ELEMENT('Z', -1000, 1000, 0.005)" + end + if @with_m + dim_elems << "MDSYS.SDO_DIM_ELEMENT('M', -1000, 1000, 0.005)" + end + + stmts << "DELETE FROM USER_SDO_GEOM_METADATA WHERE TABLE_NAME = '#{table_name.to_s.upcase}' AND COLUMN_NAME = '#{name.to_s.upcase}'" + stmts << "INSERT INTO USER_SDO_GEOM_METADATA (TABLE_NAME, COLUMN_NAME, DIMINFO, SRID) VALUES (" + + "'#{table_name}', '#{name}', MDSYS.SDO_DIM_ARRAY(#{dim_elems.join(',')}),#{srid})" + stmts + end + + def to_sql(table_name) + if @spatial + raise "Got here!" + else + super + end + end + + + private + def type_to_sql(name, limit=nil) + case name.to_s + when /geometry|point|line_string|polygon|multipoint|multilinestring|multipolygon|geometrycollection/i + "MDSYS.SDO_GEOMETRY" + else base.type_to_sql(name, limit) rescue name + end + end + + end + + end +end + +#Would prefer creation of a OracleColumn type instead but I would need to reimplement methods where Column objects are instantiated so I leave it like this +module ActiveRecord + module ConnectionAdapters + class SpatialOracleColumn < Column + + include SpatialColumn + + # With the ruby-oci8 adapter, we get objects back from spatial columns + # so this method name is a bit of a misnomer. TODO: change this method name to 'to_geometry' + def self.string_to_geometry(obj) + return obj unless obj && obj.class.to_s == "OCI8::Object::Mdsys::SdoGeometry" + raise "Bad #{obj.class} object: #{obj.inspect}" if obj.sdo_gtype.nil? + ndim = obj.sdo_gtype.to_int/1000 + gtype = obj.sdo_gtype.to_int%1000 + eleminfo = obj.sdo_elem_info.instance_variable_get('@attributes') + ordinates = obj.sdo_ordinates.instance_variable_get('@attributes') + case gtype + when 1 + geom = point_from_sdo_geometry(eleminfo, ordinates, ndim, obj.sdo_point) + when 2 + geom = linestrings_from_sdo_geometry(eleminfo, ordinates, ndim)[0] + when 3 + geom = polygons_from_sdo_geometry(eleminfo, ordinates, ndim)[0] + when 4 + geom = geomcollection_from_sdo_geometry(eleminfo, ordinates, ndim) + when 5 + geom = MultiPoint.from_coordinates(group_ordinates(ordinates, ndim)) + when 6 + linestrings = linestrings_from_sdo_geometry(eleminfo, ordinates, ndim) + geom = MultiLineString.from_line_strings(linestrings) + when 7 + polygons = polygons_from_sdo_geometry(eleminfo, ordinates, ndim) + geom = MultiPolygon.from_polygons(polygons) + else + raise "Unhandled geometry type #{obj.sdo_gtype.to_int}" + end + geom.srid = obj.sdo_srid.to_i + geom + end + + def self.group_ordinates(ordinates, num_dims) + coords = [] + ordinates.each_slice(num_dims) do |coord| + x,y = coord + coords << [x.to_f, y.to_f] + end + coords + end + + def self.point_from_sdo_point(sdo_point, ndim) + if ndim == 2 + Point.from_x_y(sdo_point.x.to_f, sdo_point.y.to_f) + elsif ndim == 3 + Point.from_x_y_z(sdo_point.x.to_f, sdo_point.y.to_f, sdo_point.z.to_f) + else + raise "Not supporting #{ndim} dimensional points" + end + end + + def self.point_from_sdo_geometry(elem_info, ordinates, ndim, sdo_point) + if sdo_point + point_from_sdo_point(sdo_point, ndim) + else + coords = ordinates.slice(0,ndim) + if ndim == 2 + Point.from_x_y(ordinates[0].to_f, ordinates[1].to_f) + elsif ndim == 3 + Point.from_x_y_z(ordinates[0].to_f, ordinates[1].to_f, ordinates[2].to_f) + end + end + end + + def self.linestrings_from_sdo_geometry(elem_info, ordinates, ndim) + num_ls = elem_info.size / 3 + linestrings = [] + 0.upto(num_ls-1) do |ring_idx| + (start_ord, ring_type, gtype, end_ord) = elem_info.slice(ring_idx*3,4) + end_ord ||= ordinates.size + 1 + num_ords = end_ord - start_ord + linestrings << LineString.from_coordinates(group_ordinates(ordinates.slice(start_ord-1, num_ords), ndim)) + end + linestrings + end + + def self.polygons_from_sdo_geometry(elem_info, ordinates, ndim) + num_rings = elem_info.size / 3 + polygons = [] + rings = [] + cur_polygon = nil + 0.upto(num_rings-1) do |ring_idx| + (start_ord, etype, interpretation, end_ord) = elem_info.slice(ring_idx*3,4) + end_ord ||= ordinates.size + 1 + num_ords = end_ord - start_ord + if etype == 1003 && rings.size > 0 # exterior ring + polygons << Polygon.from_linear_rings(rings) + rings = [] + end + rings << LinearRing.from_coordinates(group_ordinates(ordinates.slice(start_ord-1, num_ords), ndim)) + end + if rings.size > 0 # exterior ring + polygons << Polygon.from_linear_rings(rings) + rings = [] + end + polygons + end + + def self.geomcollection_from_sdo_geometry(elem_info, ordinates, ndim) + num_elems = elem_info.size / 3 + geometries = [] + 0.upto(num_elems-1) do |idx| + (start_ord, etype, interpretation, end_ord) = elem_info.slice(idx*3,4) + end_ord ||= ordinates.size + 1 + num_ords = end_ord - start_ord + geom = nil + case etype + when 1 + if ndim == 2 + geom = Point.from_x_y(ordinates[start_ord-1].to_f, ordinates[start_ord].to_f) + elsif ndim == 3 + geom = Point.from_x_y_z(ordinates[start_ord-1].to_f, ordinates[start_ord].to_f, ordinates[start_ord+1].to_f) + else + raise "Not supporting #{ndim} dimensional points" + end + when 2 + geom = LineString.from_coordinates(group_ordinates(ordinates.slice(start_ord-1, num_ords), ndim)) + when 1003 + raise "Unsupported interpretation #{interpretation} for etype 1003" if interpretation != 1 + ring = LinearRing.from_coordinates(group_ordinates(ordinates.slice(start_ord-1, num_ords), ndim)) + geom = Polygon.from_linear_rings([ring]) + when 2003 + raise "Unsupported interpretation #{interpretation} for etype 2003" if interpretation != 1 + ring = LinearRing.from_coordinates(group_ordinates(ordinates.slice(start_ord-1, num_ords), ndim)) + last_geom = geometries.pop + geom = Polygon.from_linear_rings(last_geom.rings + [ring]) + else + raise "Unsupported type in collection: etype = #{etype}, interpretation = #{interpretation}" + end + geometries << geom if geom + end + GeometryCollection.from_geometries(geometries) + end + end + end +end + +ActiveRecord::SchemaDumper.class_eval do + def oracle_enhanced_table(table, stream) + columns = @connection.columns(table) + begin + tbl = StringIO.new + + # first dump primary key column + if @connection.respond_to?(:pk_and_sequence_for) + pk, pk_seq = @connection.pk_and_sequence_for(table) + elsif @connection.respond_to?(:primary_key) + pk = @connection.primary_key(table) + end + + tbl.print " create_table #{table.inspect}" + + # addition to make temporary option work + tbl.print ", :temporary => true" if @connection.temporary_table?(table) + + if columns.detect { |c| c.name == pk } + if pk != 'id' + tbl.print %Q(, :primary_key => "#{pk}") + end + else + tbl.print ", :id => false" + end + tbl.print ", :force => true" + tbl.puts " do |t|" + + # then dump all non-primary key columns + column_specs = columns.map do |column| + raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" if @types[column.type].nil? + next if column.name == pk + spec = column_spec(column) + (spec.keys - [:name, :type]).each{ |k| spec[k].insert(0, "#{k.inspect} => ")} + spec + end.compact + + # find all migration keys used in this table + keys = [:name, :limit, :precision, :scale, :default, :null, :with_z, :with_m, :srid] & column_specs.map(&:keys).flatten + + # figure out the lengths for each column based on above keys + lengths = keys.map{ |key| column_specs.map{ |spec| spec[key] ? spec[key].length + 2 : 0 }.max } + + # the string we're going to sprintf our values against, with standardized column widths + format_string = lengths.map{ |len| "%-#{len}s" } + + # find the max length for the 'type' column, which is special + type_length = column_specs.map{ |column| column[:type].length }.max + + # add column type definition to our format string + format_string.unshift " t.%-#{type_length}s " + + format_string *= '' + + column_specs.each do |colspec| + values = keys.zip(lengths).map{ |key, len| colspec.key?(key) ? colspec[key] + ", " : " " * len } + values.unshift colspec[:type] + tbl.print((format_string % values).gsub(/,\s*$/, '')) + tbl.puts + end + + tbl.puts " end" + tbl.puts + + indexes(table, tbl) + + tbl.rewind + stream.print tbl.read + rescue => e + stream.puts "# Could not dump table #{table.inspect} because of following #{e.class}" + stream.puts "# #{e.message} #{e.backtrace.join("\n")}" + stream.puts + end + + stream + end + + private + + def indexes_with_oracle_enhanced_spatial(table, stream) + if (indexes = @connection.indexes(table)).any? + add_index_statements = indexes.map do |index| + case index.type + when nil + # use table.inspect as it will remove prefix and suffix + statement_parts = [ ('add_index ' + table.inspect) ] + statement_parts << index.columns.inspect + statement_parts << (':name => ' + index.name.inspect) + statement_parts << ':unique => true' if index.unique + statement_parts << ':tablespace => ' + index.tablespace.inspect if index.tablespace + when 'MDSYS.SPATIAL_INDEX' + statement_parts = [ ('add_index ' + table.inspect) ] + statement_parts << index.columns.inspect + statement_parts << (':name => ' + index.name.inspect) + statement_parts << ':unique => true' if index.unique + statement_parts << ':spatial => true' if index.spatial + statement_parts << ':tablespace => ' + index.tablespace.inspect if index.tablespace + when 'CTXSYS.CONTEXT' + if index.statement_parameters + statement_parts = [ ('add_context_index ' + table.inspect) ] + statement_parts << index.statement_parameters + else + statement_parts = [ ('add_context_index ' + table.inspect) ] + statement_parts << index.columns.inspect + statement_parts << (':name => ' + index.name.inspect) + end + else + # unrecognized index type + statement_parts = ["# unrecognized index #{index.name.inspect} with type #{index.type.inspect}"] + end + ' ' + statement_parts.join(', ') + end + + stream.puts add_index_statements.sort.join("\n") + stream.puts + end + end + alias_method_chain :indexes, :oracle_enhanced_spatial + +end diff --git a/lib/spatial_adapter/postgresql.rb b/lib/spatial_adapter/postgresql.rb index d0da480..b92190b 100644 --- a/lib/spatial_adapter/postgresql.rb +++ b/lib/spatial_adapter/postgresql.rb @@ -30,6 +30,10 @@ def supports_geographic? postgis_major_version > 1 || (postgis_major_version == 1 && postgis_minor_version >= 5) end + def default_srid + 4326 + end + alias :original_native_database_types :native_database_types def native_database_types original_native_database_types.merge!(geometry_data_types) @@ -239,6 +243,7 @@ def column_spatial_info(table_name) raw_geom_infos end + end module ActiveRecord @@ -252,7 +257,9 @@ def column(name, type, options = {}) column = self[name] || PostgreSQLColumnDefinition.new(@base, name, type) column.null = options[:null] - column.srid = options[:srid] || -1 + srid = options[:srid] || -1 + srid = @base.default_srid if srid == :default_srid + column.srid = srid column.with_z = options[:with_z] || false column.with_m = options[:with_m] || false column.geographic = options[:geographic] || false diff --git a/lib/spatial_adapter/railtie.rb b/lib/spatial_adapter/railtie.rb index 7a2ab28..cffeba6 100644 --- a/lib/spatial_adapter/railtie.rb +++ b/lib/spatial_adapter/railtie.rb @@ -4,7 +4,7 @@ class Railtie < Rails::Railtie adapter = ActiveRecord::Base.configurations[Rails.env]['adapter'] begin require "spatial_adapter/#{adapter}" - rescue LoadError + rescue LoadError => e raise SpatialAdapter::NotCompatibleError.new("spatial_adapter does not currently support the #{adapter} database.") end end diff --git a/rails/init.rb b/rails/init.rb index 17965d2..10742fa 100644 --- a/rails/init.rb +++ b/rails/init.rb @@ -6,6 +6,11 @@ begin adapter = ActiveRecord::Base.configurations[RAILS_ENV]['adapter'] require "spatial_adapter/#{adapter}" -rescue LoadError + # Also load the adapter for the test environment. In 2.2, at least, when running db:test:prepare, the first + # connection instantiated is the RAILS_ENV one, not 'test'. + test_adapter = ActiveRecord::Base.configurations['test']['adapter'] + require "spatial_adapter/#{test_adapter}" +rescue LoadError => e + puts "Caught #{e} #{e.message} #{e.backtrace.join("\n")}" raise SpatialAdapter::NotCompatibleError.new("spatial_adapter does not currently support the #{adapter} database.") end diff --git a/spec/db/oracle_enhanced_raw.rb b/spec/db/oracle_enhanced_raw.rb new file mode 100644 index 0000000..79d4c2f --- /dev/null +++ b/spec/db/oracle_enhanced_raw.rb @@ -0,0 +1,199 @@ +oracle_enhanced_connection + +ActiveRecord::Schema.define() do + execute "delete from user_sdo_geom_metadata" + + execute "drop table point_models" rescue nil + execute "drop sequence point_models_seq" rescue nil + execute "create table point_models + ( + id NUMBER(38,0) NOT NULL, + extra varchar2(255), + more_extra varchar2(255), + geom SDO_GEOMETRY, + PRIMARY KEY (id) + )" + execute "CREATE SEQUENCE point_models_seq START WITH 10000" + execute "insert into user_sdo_geom_metadata (TABLE_NAME, COLUMN_NAME, DIMINFO, SRID) + VALUES ('point_models', 'geom', + SDO_DIM_ARRAY( + MDSYS.SDO_DIM_ELEMENT('X', -180.0, 180.0, 0.005), + MDSYS.SDO_DIM_ELEMENT('Y', -90.0, 90.0, 0.005)), + 8307)" + execute "create index index_point_models_on_geom ON point_models(geom) INDEXTYPE IS MDSYS.SPATIAL_INDEX" + execute "create index index_point_models_on_extra on point_models (extra, more_extra)" + + execute "drop table line_string_models" rescue nil + execute "drop sequence line_string_models_seq" rescue nil + execute "create table line_string_models + ( + id NUMBER(38,0) NOT NULL, + extra varchar2(255), + geom SDO_GEOMETRY, + PRIMARY KEY (id) + )" + execute "CREATE SEQUENCE line_string_models_seq START WITH 10000" + execute "insert into user_sdo_geom_metadata (TABLE_NAME, COLUMN_NAME, DIMINFO, SRID) + VALUES ('line_string_models', 'geom', + SDO_DIM_ARRAY( + MDSYS.SDO_DIM_ELEMENT('X', -180.0, 180.0, 0.005), + MDSYS.SDO_DIM_ELEMENT('Y', -90.0, 90.0, 0.005)), + 8307)" + + execute "drop table polygon_models" rescue nil + execute "drop sequence polygon_models_seq" rescue nil + execute "create table polygon_models + ( + id NUMBER(38,0) NOT NULL, + extra varchar2(255), + geom SDO_GEOMETRY, + PRIMARY KEY (id) + )" + execute "CREATE SEQUENCE polygon_models_seq START WITH 10000" + execute "insert into user_sdo_geom_metadata (TABLE_NAME, COLUMN_NAME, DIMINFO, SRID) + VALUES ('polygon_models', 'geom', + SDO_DIM_ARRAY( + MDSYS.SDO_DIM_ELEMENT('X', -180.0, 180.0, 0.005), + MDSYS.SDO_DIM_ELEMENT('Y', -90.0, 90.0, 0.005)), + 8307)" + + execute "drop table multi_point_models" rescue nil + execute "drop sequence multi_point_models_seq" rescue nil + execute "create table multi_point_models + ( + id NUMBER(38,0) NOT NULL, + extra varchar2(255), + geom SDO_GEOMETRY, + PRIMARY KEY (id) + )" + execute "CREATE SEQUENCE multi_point_models_seq START WITH 10000" + execute "insert into user_sdo_geom_metadata (TABLE_NAME, COLUMN_NAME, DIMINFO, SRID) + VALUES ('multi_point_models', 'geom', + SDO_DIM_ARRAY( + MDSYS.SDO_DIM_ELEMENT('X', -180.0, 180.0, 0.005), + MDSYS.SDO_DIM_ELEMENT('Y', -90.0, 90.0, 0.005)), + 8307)" + + execute "drop table multi_line_string_models" rescue nil + execute "drop sequence multi_line_string_models_seq" rescue nil + execute "create table multi_line_string_models + ( + id NUMBER(38,0) NOT NULL, + extra varchar2(255), + geom SDO_GEOMETRY, + PRIMARY KEY (id) + )" + execute "CREATE SEQUENCE multi_line_string_models_seq START WITH 10000" + execute "insert into user_sdo_geom_metadata (TABLE_NAME, COLUMN_NAME, DIMINFO, SRID) + VALUES ('multi_line_string_models', 'geom', + SDO_DIM_ARRAY( + MDSYS.SDO_DIM_ELEMENT('X', -180.0, 180.0, 0.005), + MDSYS.SDO_DIM_ELEMENT('Y', -90.0, 90.0, 0.005)), + 8307)" + + execute "drop table multi_polygon_models" rescue nil + execute "drop sequence multi_polygon_models_seq" rescue nil + execute "create table multi_polygon_models + ( + id NUMBER(38,0) NOT NULL, + extra varchar2(255), + geom SDO_GEOMETRY, + PRIMARY KEY (id) + )" + execute "CREATE SEQUENCE multi_polygon_models_seq START WITH 10000" + execute "insert into user_sdo_geom_metadata (TABLE_NAME, COLUMN_NAME, DIMINFO, SRID) + VALUES ('multi_polygon_models', 'geom', + SDO_DIM_ARRAY( + MDSYS.SDO_DIM_ELEMENT('X', -180.0, 180.0, 0.005), + MDSYS.SDO_DIM_ELEMENT('Y', -90.0, 90.0, 0.005)), + 8307)" + + execute "drop table geometry_collection_models" rescue nil + execute "drop sequence geometry_collection_models_seq" rescue nil + execute "create table geometry_collection_models + ( + id NUMBER(38,0) NOT NULL, + extra varchar2(255), + geom SDO_GEOMETRY, + PRIMARY KEY (id) + )" + execute "CREATE SEQUENCE geometry_collection_models_seq START WITH 10000" + execute "insert into user_sdo_geom_metadata (TABLE_NAME, COLUMN_NAME, DIMINFO, SRID) + VALUES ('geometry_collection_models', 'geom', + SDO_DIM_ARRAY( + MDSYS.SDO_DIM_ELEMENT('X', -180.0, 180.0, 0.005), + MDSYS.SDO_DIM_ELEMENT('Y', -90.0, 90.0, 0.005)), + 8307)" + + execute "drop table geometry_models" rescue nil + execute "drop sequence geometry_models_seq" rescue nil + execute "create table geometry_models + ( + id NUMBER(38,0) NOT NULL, + extra varchar2(255), + geom SDO_GEOMETRY, + PRIMARY KEY (id) + )" + execute "CREATE SEQUENCE geometry_models_seq START WITH 10000" + execute "insert into user_sdo_geom_metadata (TABLE_NAME, COLUMN_NAME, DIMINFO, SRID) + VALUES ('geometry_models', 'geom', + SDO_DIM_ARRAY( + MDSYS.SDO_DIM_ELEMENT('X', -180.0, 180.0, 0.005), + MDSYS.SDO_DIM_ELEMENT('Y', -90.0, 90.0, 0.005)), + 8307)" + + execute "drop table pointz_models" rescue nil + execute "drop sequence pointz_models_seq" rescue nil + execute "create table pointz_models + ( + id NUMBER(38,0) NOT NULL, + extra varchar2(255), + geom SDO_GEOMETRY, + PRIMARY KEY (id) + )" + execute "CREATE SEQUENCE pointz_models_seq START WITH 10000" + execute "insert into user_sdo_geom_metadata (TABLE_NAME, COLUMN_NAME, DIMINFO, SRID) + VALUES ('pointz_models', 'geom', + SDO_DIM_ARRAY( + MDSYS.SDO_DIM_ELEMENT('X', -180.0, 180.0, 0.005), + MDSYS.SDO_DIM_ELEMENT('Y', -90.0, 90.0, 0.005), + MDSYS.SDO_DIM_ELEMENT('Z', -90.0, 90.0, 0.005)), + 8307)" + + #execute "drop table pointm_models" rescue nil + #execute "drop sequence pointm_models_seq" rescue nil + #execute "create table pointm_models + # ( + # id NUMBER(38,0) NOT NULL, + # extra varchar2(255), + # geom SDO_GEOMETRY, + # PRIMARY KEY (id) + # )" + #execute "CREATE SEQUENCE pointm_models_seq START WITH 10000" + #execute "insert into user_sdo_geom_metadata (TABLE_NAME, COLUMN_NAME, DIMINFO, SRID) + # VALUES ('pointm_models', 'geom', + # SDO_DIM_ARRAY( + # MDSYS.SDO_DIM_ELEMENT('X', -180.0, 180.0, 0.005), + # MDSYS.SDO_DIM_ELEMENT('Y', -90.0, 90.0, 0.005), + # MDSYS.SDO_DIM_ELEMENT('M', -90.0, 90.0, 0.005)), + # 8307)" + + #execute "drop table point4_models" rescue nil + #execute "drop sequence point4_models_seq" rescue nil + #execute "create table point4_models + # ( + # id NUMBER(38,0) NOT NULL, + # extra varchar2(255), + # geom SDO_GEOMETRY, + # PRIMARY KEY (id) + # )" + #execute "CREATE SEQUENCE point4_models_seq START WITH 10000" + #execute "insert into user_sdo_geom_metadata (TABLE_NAME, COLUMN_NAME, DIMINFO, SRID) + # VALUES ('point4_models', 'geom', + # SDO_DIM_ARRAY( + # MDSYS.SDO_DIM_ELEMENT('X', -180.0, 180.0, 0.005), + # MDSYS.SDO_DIM_ELEMENT('Y', -90.0, 90.0, 0.005), + # MDSYS.SDO_DIM_ELEMENT('Z', -90.0, 90.0, 0.005), + # MDSYS.SDO_DIM_ELEMENT('M', -90.0, 90.0, 0.005)), + # 8307)" +end diff --git a/spec/mysql/models_spec.rb b/spec/mysql/models_spec.rb index 8681392..8b32478 100644 --- a/spec/mysql/models_spec.rb +++ b/spec/mysql/models_spec.rb @@ -8,6 +8,7 @@ before :each do mysql_connection @connection = ActiveRecord::Base.connection + GeometryFactory.default_srid = @connection.default_srid end describe "inserting records" do diff --git a/spec/oracle_enhanced/connection_adapter_spec.rb b/spec/oracle_enhanced/connection_adapter_spec.rb new file mode 100644 index 0000000..35a681e --- /dev/null +++ b/spec/oracle_enhanced/connection_adapter_spec.rb @@ -0,0 +1,122 @@ +require 'spec_helper' +require 'spatial_adapter/oracle_enhanced' +require 'db/oracle_enhanced_raw' +require 'models/common' + +describe "Modified OracleEnhancedAdapter" do + before :each do + oracle_enhanced_connection + @connection = ActiveRecord::Base.connection + end + + describe '#spatial?' do + it 'should be true if Spatial is installed' do + @connection.should_receive(:select_value).with("select object_id from all_objects where owner = 'MDSYS' and object_name = 'SDO_GEOMETRY'").and_return(1) + @connection.should be_spatial + end + + it 'should be false if Spatial is not installed' do + @connection.should_receive(:select_value).with("select object_id from all_objects where owner = 'MDSYS' and object_name = 'SDO_GEOMETRY'").and_return(0) + @connection.should_not be_spatial + end + end + + describe "#columns" do + describe "type" do + it "should be a regular SpatialOracleColumn if column is a geometry data type" do + column = PointModel.columns.select{|c| c.name == 'geom'}.first + column.should be_a(ActiveRecord::ConnectionAdapters::SpatialOracleColumn) + column.geometry_type.should == :geometry + column.should_not be_geographic + end + + it "should be OracleEnhancedColumn if column is not a spatial data type" do + PointModel.columns.select{|c| c.name == 'extra'}.first.should be_a(ActiveRecord::ConnectionAdapters::OracleEnhancedColumn) + end + end + + describe "@geometry_type" do + # Oracle does not provide an easy way to denote particular types of geometries. + # We could use some constraint, but not sure of performance impacts. + + it "should be :geometry for geometry columns" do + PointModel.columns.select{|c| c.name == 'geom'}.first.geometry_type.should == :geometry + end + + #it "should be :point for geometry columns restricted to POINT types" do + # PointModel.columns.select{|c| c.name == 'geom'}.first.geometry_type.should == :point + #end + + #it "should be :line_string for geometry columns restricted to LINESTRING types" do + # LineStringModel.columns.select{|c| c.name == 'geom'}.first.geometry_type.should == :line_string + #end + + #it "should be :polygon for geometry columns restricted to POLYGON types" do + # PolygonModel.columns.select{|c| c.name == 'geom'}.first.geometry_type.should == :polygon + #end + + #it "should be :multi_point for geometry columns restricted to MULTIPOINT types" do + # MultiPointModel.columns.select{|c| c.name == 'geom'}.first.geometry_type.should == :multi_point + #end + + #it "should be :multi_line_string for geometry columns restricted to MULTILINESTRING types" do + # MultiLineStringModel.columns.select{|c| c.name == 'geom'}.first.geometry_type.should == :multi_line_string + #end + + #it "should be :multi_polygon for geometry columns restricted to MULTIPOLYGON types" do + # MultiPolygonModel.columns.select{|c| c.name == 'geom'}.first.geometry_type.should == :multi_polygon + #end + + #it "should be :geometry_collection for geometry columns restricted to GEOMETRYCOLLECTION types" do + # GeometryCollectionModel.columns.select{|c| c.name == 'geom'}.first.geometry_type.should == :geometry_collection + #end + + #it "should be :geometry for geometry columns not restricted to a type" do + # GeometryModel.columns.select{|c| c.name == 'geom'}.first.geometry_type.should == :geometry + #end + end + end + + describe "#indexes" do + before :each do + @indexes = @connection.indexes('point_models') + end + + it "should return an OracleEnhancedIndexDefinition for each index on the table" do + @indexes.should have(2).items + @indexes.each do |i| + i.should be_a(ActiveRecord::ConnectionAdapters::OracleEnhancedIndexDefinition) + end + end + + it "should indicate the correct columns in the index" do + @indexes.select{|i| i.name == 'index_point_models_on_geom'}.first.columns.should == ['geom'] + @indexes.select{|i| i.name == 'index_point_models_on_extra'}.first.columns.should == ['extra', 'more_extra'] + end + + it "should be marked as spatial if a MDSYS.SPATIAL_INDEX index on a geometry column" do + @indexes.select{|i| i.name == 'index_point_models_on_geom'}.first.spatial.should == true + end + + it "should not be marked as spatial if not a MDSYS.SPATIAL_INDEX index" do + @indexes.select{|i| i.name == 'index_point_models_on_extra'}.first.spatial.should == false + end + + end + + describe "#add_index" do + after :each do + @connection.remove_index('geometry_models', 'geom') + end + + it "should create a spatial index given :spatial => true" do + @connection.should_receive(:execute).with(/INDEXTYPE IS mdsys.spatial_index/i) + @connection.add_index('geometry_models', 'geom', :spatial => true) + end + + it "should not create a spatial index unless specified" do + @connection.should_not_receive(:execute).with(/INDEXTYPE IS mdsys.spatial_index/i) + @connection.add_index('geometry_models', 'extra') + end + end +end diff --git a/spec/oracle_enhanced/migration_spec.rb b/spec/oracle_enhanced/migration_spec.rb new file mode 100644 index 0000000..8a05eac --- /dev/null +++ b/spec/oracle_enhanced/migration_spec.rb @@ -0,0 +1,234 @@ +require 'spec_helper' +require 'spatial_adapter/oracle_enhanced' + +class MigratedGeometryModel < ActiveRecord::Base +end + +describe "Spatially-enabled Migrations" do + before :each do + oracle_enhanced_connection + @connection = ActiveRecord::Base.connection + end + + describe "creating tables" do + after :each do + @connection.drop_table "migrated_geometry_models" + end + + SpatialAdapter.geometry_data_types.keys.each do |type| + it "should create #{type.to_s} columns" do + ActiveRecord::Schema.define do + create_table :migrated_geometry_models, :force => true do |t| + t.integer :extra + t.send(type, :geom) + end + end + + geom_column = @connection.columns(:migrated_geometry_models).select{|c| c.name == 'geom'}.first + geom_column.should be_a(SpatialAdapter::SpatialColumn) + geom_column.type.should == :string + geom_column.geometry_type.should == :geometry + geom_column.should_not be_geographic + geom_column.with_z.should == false + geom_column.with_m.should == false + geom_column.srid.should == -1 + end + end + + + it "should create 3d (xyz) geometry columns" do + ActiveRecord::Schema.define do + create_table :migrated_geometry_models, :force => true do |t| + t.integer :extra + t.point :geom, :with_z => true + end + end + + geom_column = @connection.columns(:migrated_geometry_models).select{|c| c.name == 'geom'}.first + geom_column.should be_a(SpatialAdapter::SpatialColumn) + geom_column.with_z.should == true + geom_column.with_m.should == false + geom_column.srid.should == -1 + end + + + it "should create 3d (xym) geometry columns" do + ActiveRecord::Schema.define do + create_table :migrated_geometry_models, :force => true do |t| + t.integer :extra + t.point :geom, :with_m => true + end + end + + geom_column = @connection.columns(:migrated_geometry_models).select{|c| c.name == 'geom'}.first + geom_column.should be_a(SpatialAdapter::SpatialColumn) + geom_column.geometry_type.should == :geometry + geom_column.type.should == :string + geom_column.with_z.should == false + geom_column.with_m.should == true + geom_column.srid.should == -1 + end + + + it "should create 4d (xyzm) geometry columns" do + ActiveRecord::Schema.define do + create_table :migrated_geometry_models, :force => true do |t| + t.integer :extra + t.point :geom, :with_z => true, :with_m => true + end + end + + geom_column = @connection.columns(:migrated_geometry_models).select{|c| c.name == 'geom'}.first + geom_column.should be_a(SpatialAdapter::SpatialColumn) + geom_column.geometry_type.should == :geometry + geom_column.type.should == :string + geom_column.with_z.should == true + geom_column.with_m.should == true + geom_column.srid.should == -1 + end + + it "should create geometry columns with specified SRID" do + ActiveRecord::Schema.define do + create_table :migrated_geometry_models, :force => true do |t| + t.integer :extra + t.geometry :geom, :srid => 4326 + end + end + + geom_column = @connection.columns(:migrated_geometry_models).select{|c| c.name == 'geom'}.first + geom_column.should be_a(SpatialAdapter::SpatialColumn) + geom_column.type.should == :string + geom_column.geometry_type.should == :geometry + geom_column.with_z.should == false + geom_column.with_m.should == false + geom_column.srid.should == 4326 + end + end + + describe "adding columns" do + before :each do + ActiveRecord::Schema.define do + create_table :migrated_geometry_models, :force => true do |t| + t.integer :extra + end + end + end + + after :each do + @connection.drop_table "migrated_geometry_models" + end + + SpatialAdapter.geometry_data_types.keys.each do |type| + it "should add #{type.to_s} columns" do + ActiveRecord::Schema.define do + add_column :migrated_geometry_models, :geom, type + end + + geom_column = @connection.columns(:migrated_geometry_models).select{|c| c.name == 'geom'}.first + geom_column.should be_a(SpatialAdapter::SpatialColumn) + geom_column.type.should == :string + geom_column.geometry_type.should == :geometry + geom_column.with_z.should == false + geom_column.with_m.should == false + geom_column.srid.should == -1 + end + end + + it "should add 3d (xyz) geometry columns" do + ActiveRecord::Schema.define do + add_column :migrated_geometry_models, :geom, :point, :with_z => true + end + + geom_column = @connection.columns(:migrated_geometry_models).select{|c| c.name == 'geom'}.first + geom_column.should be_a(SpatialAdapter::SpatialColumn) + geom_column.type.should == :string + geom_column.geometry_type.should == :geometry + geom_column.with_z.should == true + geom_column.with_m.should == false + geom_column.srid.should == -1 + end + + + it "should add 3d (xym) geometry columns" do + ActiveRecord::Schema.define do + add_column :migrated_geometry_models, :geom, :point, :with_m => true + end + + geom_column = @connection.columns(:migrated_geometry_models).select{|c| c.name == 'geom'}.first + geom_column.should be_a(SpatialAdapter::SpatialColumn) + geom_column.type.should == :string + geom_column.geometry_type.should == :geometry + geom_column.with_z.should == false + geom_column.with_m.should == true + geom_column.srid.should == -1 + end + + + it "should add 4d (xyzm) geometry columns" do + ActiveRecord::Schema.define do + add_column :migrated_geometry_models, :geom, :point, :with_z => true, :with_m => true + end + + geom_column = @connection.columns(:migrated_geometry_models).select{|c| c.name == 'geom'}.first + geom_column.should be_a(SpatialAdapter::SpatialColumn) + geom_column.type.should == :string + geom_column.geometry_type.should == :geometry + geom_column.with_z.should == true + geom_column.with_m.should == true + geom_column.srid.should == -1 + end + + it "should add GEOMETRY columns with specified SRID" do + ActiveRecord::Schema.define do + add_column :migrated_geometry_models, :geom, :geometry, :srid => 4326 + end + + geom_column = @connection.columns(:migrated_geometry_models).select{|c| c.name == 'geom'}.first + geom_column.should be_a(SpatialAdapter::SpatialColumn) + geom_column.geometry_type.should == :geometry + geom_column.type.should == :string + geom_column.with_z.should == false + geom_column.with_m.should == false + geom_column.srid.should == 4326 + end + end + + describe "removing columns" do + after :each do + @connection.drop_table "migrated_geometry_models" + end + + SpatialAdapter.geometry_data_types.keys.each do |type| + it "should remove #{type.to_s} column metadata from user_sdo_geom_metadata" do + ActiveRecord::Schema.define do + create_table :migrated_geometry_models, :force => true do |t| + t.integer :extra + t.send(type, :geom) + end + end + + @connection.should_receive(:execute).with(/DELETE FROM USER_SDO_GEOM_METADATA/).once + @connection.should_receive(:execute).with(anything()) + ActiveRecord::Schema.define do + remove_column :migrated_geometry_models, :geom + end + @connection.should_receive(:execute).with(anything()).twice + end + end + + it "should still remove non-spatial columns using ALTER TABLE DROP COLUMN" do + ActiveRecord::Schema.define do + create_table :migrated_geometry_models, :force => true do |t| + t.integer :extra + t.point :geom + end + end + + @connection.should_receive(:execute).with(/alter table.*migrated_geometry_models.*drop.*extra/i) + ActiveRecord::Schema.define do + remove_column :migrated_geometry_models, :extra + end + @connection.should_receive(:execute).with(anything()).twice + end + end +end \ No newline at end of file diff --git a/spec/oracle_enhanced/models_spec.rb b/spec/oracle_enhanced/models_spec.rb new file mode 100644 index 0000000..8b148c6 --- /dev/null +++ b/spec/oracle_enhanced/models_spec.rb @@ -0,0 +1,103 @@ +require 'spec_helper' +require 'shared_examples' +require 'spatial_adapter/oracle_enhanced' +require 'db/oracle_enhanced_raw' +require 'models/common' + +describe "Spatially-enabled Models" do + before :each do + oracle_enhanced_connection + @connection = ActiveRecord::Base.connection + GeometryFactory.default_srid = @connection.default_srid + end + + describe "inserting records" do + it 'should save Point objects' do + model = PointModel.new(:extra => 'test', :geom => GeometryFactory.point) + @connection.should_receive(:insert_sql).with("INSERT INTO \"POINT_MODELS\" (\"ID\", \"MORE_EXTRA\", \"EXTRA\", \"GEOM\") VALUES(10000, NULL, 'test', SDO_GEOMETRY( 2001, 8307, MDSYS.SDO_POINT_TYPE( 1.0, 2.0, NULL ), NULL, NULL ))", "PointModel Create", "id", 10000, "point_models_seq") + model.save.should == true + end + + it 'should save LineString objects' do + model = LineStringModel.new(:extra => 'test', :geom => GeometryFactory.line_string) + @connection.should_receive(:insert_sql).with("INSERT INTO \"LINE_STRING_MODELS\" (\"ID\", \"EXTRA\", \"GEOM\") VALUES(10000, 'test', SDO_GEOMETRY( 'LINESTRING(1.4 2.5,1.5 6.7)', 8307 ))", "LineStringModel Create", "id", 10000, "line_string_models_seq") + model.save.should == true + end + + it 'should save Polygon objects' do + model = PolygonModel.new(:extra => 'test', :geom => GeometryFactory.polygon) + @connection.should_receive(:insert_sql).with("INSERT INTO \"POLYGON_MODELS\" (\"ID\", \"EXTRA\", \"GEOM\") VALUES(10000, 'test', SDO_GEOMETRY( 'POLYGON((12.4 -45.3,45.4 41.6,4.456 1.0698,12.4 -45.3),(2.4 5.3,5.4 1.4263,14.46 1.06,2.4 5.3))', 8307 ))", "PolygonModel Create", "id", 10000, "polygon_models_seq") + model.save.should == true + end + + it 'should save MultiPoint objects' do + model = MultiPointModel.new(:extra => 'test', :geom => GeometryFactory.multi_point) + @connection.should_receive(:insert_sql).with("INSERT INTO \"MULTI_POINT_MODELS\" (\"ID\", \"EXTRA\", \"GEOM\") VALUES(10000, 'test', SDO_GEOMETRY( 'MULTIPOINT((12.4 -23.3),(-65.1 23.4),(23.55555555 23.0))', 8307 ))", "MultiPointModel Create", "id", 10000, "multi_point_models_seq") + model.save.should == true + end + + it 'should save MultiLineString objects' do + model = MultiLineStringModel.new(:extra => 'test', :geom => GeometryFactory.multi_line_string) + @connection.should_receive(:insert_sql).with("INSERT INTO \"MULTI_LINE_STRING_MODELS\" (\"ID\", \"EXTRA\", \"GEOM\") VALUES(10000, 'test', SDO_GEOMETRY( 'MULTILINESTRING((1.5 45.2,-54.12312 -0.012),(1.5 45.2,-54.12312 -0.012,45.123 23.3))', 8307 ))", "MultiLineStringModel Create", "id", 10000, "multi_line_string_models_seq") + model.save.should == true + end + + it 'should save MultiPolygon objects' do + model = MultiPolygonModel.new(:extra => 'test', :geom => GeometryFactory.multi_polygon) + @connection.should_receive(:insert_sql).with("INSERT INTO \"MULTI_POLYGON_MODELS\" (\"ID\", \"EXTRA\", \"GEOM\") VALUES(10000, 'test', SDO_GEOMETRY( 'MULTIPOLYGON(((12.4 -45.3,45.4 41.6,4.456 1.0698,12.4 -45.3),(2.4 5.3,5.4 1.4263,14.46 1.06,2.4 5.3)),((0.0 0.0,4.0 0.0,4.0 4.0,0.0 4.0,0.0 0.0),(1.0 1.0,3.0 1.0,3.0 3.0,1.0 3.0,1.0 1.0)))', 8307 ))", "MultiPolygonModel Create", "id", 10000, "multi_polygon_models_seq") + model.save.should == true + end + + it 'should save GeometryCollection objects' do + model = GeometryCollectionModel.new(:extra => 'test', :geom => GeometryFactory.geometry_collection) + @connection.should_receive(:insert_sql).with("INSERT INTO \"GEOMETRY_COLLECTION_MODELS\" (\"ID\", \"EXTRA\", \"GEOM\") VALUES(10000, 'test', SDO_GEOMETRY( 'GEOMETRYCOLLECTION(POINT(4.67 45.4),LINESTRING(5.7 12.45,67.55 54.0))', 8307 ))", "GeometryCollectionModel Create", "id", 10000, "geometry_collection_models_seq") + model.save.should == true + end + + it 'should save Geometry objects' do + model = GeometryModel.new(:extra => 'test', :geom => GeometryFactory.point) + @connection.should_receive(:insert_sql).with("INSERT INTO \"GEOMETRY_MODELS\" (\"ID\", \"EXTRA\", \"GEOM\") VALUES(10000, 'test', SDO_GEOMETRY( 2001, 8307, MDSYS.SDO_POINT_TYPE( 1.0, 2.0, NULL ), NULL, NULL ))", "GeometryModel Create", "id", 10000, "geometry_models_seq") + model.save.should == true + end + + it 'should save 3D Point (with Z coord) objects' do + model = PointzModel.new(:extra => 'test', :geom => GeometryFactory.pointz) + @connection.should_receive(:insert_sql).with("INSERT INTO \"POINTZ_MODELS\" (\"ID\", \"EXTRA\", \"GEOM\") VALUES(10000, 'test', SDO_GEOMETRY( 3001, 8307, MDSYS.SDO_POINT_TYPE( 1.0, 2.0, 3.0 ), NULL, NULL ))", "PointzModel Create", "id", 10000, "pointz_models_seq") + model.save.should == true + end + + #it 'should save 3D Point (with M coord) objects' do + # model = PointmModel.new(:extra => 'test', :geom => GeometryFactory.pointm) + # @connection.should_receive(:insert_sql).with("INSERT INTO \"POINTM_MODELS\" (\"ID\", \"EXTRA\", \"GEOM\") VALUES(10000, 'test', SDO_GEOMETRY( 2001, 8307, MDSYS.SDO_POINT_TYPE( 1.0, 2.0, 3.0 ), NULL, NULL ))", "PointmModel Create", "id", 10000, "pointm_models_seq") + # model.save.should == true + #end + + #it 'should save 4D Point objects' do + # model = Point4Model.new(:extra => 'test', :geom => GeometryFactory.point4) + # @connection.should_receive(:insert_sql).with("INSERT INTO \"POINT4_MODELS\" (\"ID\", \"EXTRA\", \"GEOM\") VALUES(10000, 'test', SDO_GEOMETRY( 2001, 8307, MDSYS.SDO_POINT_TYPE( 1.0, 2.0, 3.0, 4.0 ), NULL, NULL ))", "Point4Model Create", "id", 10000, "point4_models_seq") + # model.save.should == true + #end + + end + + include CommonModelActions + + describe "finding records" do + it 'should retrieve 3D Point (with Z coord) objects' do + model = PointzModel.create(:extra => 'test', :geom => GeometryFactory.pointz) + PointzModel.find(model.id).geom.as_ewkt.should == GeometryFactory.pointz.as_ewkt + end + + #it 'should retrieve 3D Point (with M coord) objects' do + # model = GeographyPointmModel.create(:extra => 'test', :geom => GeometryFactory.pointm) + # GeographyPointmModel.find(model.id).geom.as_ewkt.should == GeometryFactory.pointm.as_ewkt + #end + + #it 'should retrieve 4D Point objects' do + # model = GeographyPoint4Model.create(:extra => 'test', :geom => GeometryFactory.point4) + # GeographyPoint4Model.find(model.id).geom.as_ewkt.should == GeometryFactory.point4.as_ewkt + #end + + end +end + diff --git a/spec/oracle_enhanced/schema_dumper_spec.rb b/spec/oracle_enhanced/schema_dumper_spec.rb new file mode 100644 index 0000000..6b8f167 --- /dev/null +++ b/spec/oracle_enhanced/schema_dumper_spec.rb @@ -0,0 +1,62 @@ +require 'spec_helper' +require 'spatial_adapter/oracle_enhanced' + +describe "Spatially-enabled Schema Dumps" do + before :all do + oracle_enhanced_connection + @connection = ActiveRecord::Base.connection + + @connection.execute_structure_dump(@connection.full_drop) + + # Create a new table + ActiveRecord::Schema.define do + create_table :migrated_geometry_models, :force => true do |t| + t.integer :extra + t.point :geom, :with_m => false, :with_z => true, :srid => :default_srid + end + add_index :migrated_geometry_models, :geom, :spatial => true, :name => 'test_spatial_index' + + end + + File.open('schema.rb', "w") do |file| + ActiveRecord::SchemaDumper.dump(@connection, file) + end + + # Drop the original tables + @connection.drop_table "migrated_geometry_models" + + # Load the dumped schema + load('schema.rb') + end + + after :all do + # delete the schema file + File.delete('schema.rb') + + # Drop the new tables + @connection.drop_table "migrated_geometry_models" + end + + it "should preserve spatial attributes of geometry tables" do + columns = @connection.columns("migrated_geometry_models") + + columns.should have(3).items + geom_column = columns.select{|c| c.name == 'geom'}.first + geom_column.should be_a(SpatialAdapter::SpatialColumn) + geom_column.geometry_type.should == :geometry + geom_column.type.should == :string + geom_column.with_z.should == true + geom_column.with_m.should == false + geom_column.srid.should == @connection.default_srid + end + + it "should preserve spatial indexes" do + indexes = @connection.indexes("migrated_geometry_models") + + indexes.should have(1).item + + indexes.first.name.should == 'test_spatial_index' + indexes.first.columns.should == ["geom"] + indexes.first.spatial.should == true + end +end \ No newline at end of file diff --git a/spec/postgresql/models_spec.rb b/spec/postgresql/models_spec.rb index 38742cf..9b5995a 100644 --- a/spec/postgresql/models_spec.rb +++ b/spec/postgresql/models_spec.rb @@ -8,6 +8,7 @@ before :each do postgis_connection @connection = ActiveRecord::Base.connection + GeometryFactory.default_srid = @connection.default_srid end describe "inserting records" do diff --git a/spec/shared_examples.rb b/spec/shared_examples.rb index 8ccc585..31b0f87 100644 --- a/spec/shared_examples.rb +++ b/spec/shared_examples.rb @@ -2,42 +2,42 @@ describe 'finding records' do it 'should retrieve Point objects' do model = PointModel.create(:extra => 'test', :geom => GeometryFactory.point) - PointModel.find(model.id).geom.should == GeometryFactory.point + PointModel.find(model.id).geom.as_ewkt.should == GeometryFactory.point.as_ewkt end it 'should retrieve LineString objects' do model = LineStringModel.create(:extra => 'test', :geom => GeometryFactory.line_string) - LineStringModel.find(model.id).geom.should == GeometryFactory.line_string + LineStringModel.find(model.id).geom.as_ewkt.should == GeometryFactory.line_string.as_ewkt end it 'should retrieve Polygon objects' do model = PolygonModel.create(:extra => 'test', :geom => GeometryFactory.polygon) - PolygonModel.find(model.id).geom.should == GeometryFactory.polygon + PolygonModel.find(model.id).geom.as_ewkt.should == GeometryFactory.polygon.as_ewkt end it 'should retrieve MultiPoint objects' do model = MultiPointModel.create(:extra => 'test', :geom => GeometryFactory.multi_point) - MultiPointModel.find(model.id).geom.should == GeometryFactory.multi_point + MultiPointModel.find(model.id).geom.as_ewkt.should == GeometryFactory.multi_point.as_ewkt end it 'should retrieve MultiLineString objects' do model = MultiLineStringModel.create(:extra => 'test', :geom => GeometryFactory.multi_line_string) - MultiLineStringModel.find(model.id).geom.should == GeometryFactory.multi_line_string + MultiLineStringModel.find(model.id).geom.as_ewkt.should == GeometryFactory.multi_line_string.as_ewkt end it 'should retrieve MultiPolygon objects' do model = MultiPolygonModel.create(:extra => 'test', :geom => GeometryFactory.multi_polygon) - MultiPolygonModel.find(model.id).geom.should == GeometryFactory.multi_polygon + MultiPolygonModel.find(model.id).geom.as_ewkt.should == GeometryFactory.multi_polygon.as_ewkt end it 'should retrieve GeometryCollection objects' do model = GeometryCollectionModel.create(:extra => 'test', :geom => GeometryFactory.geometry_collection) - GeometryCollectionModel.find(model.id).geom.should == GeometryFactory.geometry_collection + GeometryCollectionModel.find(model.id).geom.as_ewkt.should == GeometryFactory.geometry_collection.as_ewkt end it 'should retrieve Geometry objects' do model = GeometryModel.create(:extra => 'test', :geom => GeometryFactory.point) - GeometryModel.find(model.id).geom.should == GeometryFactory.point + GeometryModel.find(model.id).geom.as_ewkt.should == GeometryFactory.point.as_ewkt end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index e1ab844..adcf7c6 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,7 +1,7 @@ require 'rubygems' require 'spec' require 'geo_ruby' -gem 'activerecord', '=2.3.8' +gem 'activerecord', '=2.2.2' #gem 'activerecord', '=3.0.0' require 'active_record' @@ -34,46 +34,64 @@ def mysql_connection ActiveRecord::Migration.verbose = false end +def oracle_enhanced_connection + ActiveRecord::Base.establish_connection( + :adapter => 'oracle_enhanced', + :database => '//localhost:1521/orcl', + :username => 'spatial_adapter', + :password => 'test' + ) + + # Don't output migration logging + ActiveRecord::Migration.verbose = false +end + class GeometryFactory + @@default_srid = 4326 + class << self + def default_srid=(new_srid) + @@default_srid = new_srid + end + def point - Point.from_x_y(1, 2, 4326) + Point.from_x_y(1.0, 2.0, @@default_srid) end def line_string - LineString.from_coordinates([[1.4,2.5],[1.5,6.7]], 4326) + LineString.from_coordinates([[1.4,2.5],[1.5,6.7]], @@default_srid) end def polygon - Polygon.from_coordinates([[[12.4,-45.3],[45.4,41.6],[4.456,1.0698],[12.4,-45.3]],[[2.4,5.3],[5.4,1.4263],[14.46,1.06],[2.4,5.3]]], 4326) + Polygon.from_coordinates([[[12.4,-45.3],[45.4,41.6],[4.456,1.0698],[12.4,-45.3]],[[2.4,5.3],[5.4,1.4263],[14.46,1.06],[2.4,5.3]]], @@default_srid) end def multi_point - MultiPoint.from_coordinates([[12.4,-23.3],[-65.1,23.4],[23.55555555,23]], 4326) + MultiPoint.from_coordinates([[12.4,-23.3],[-65.1,23.4],[23.55555555,23.0]], @@default_srid) end def multi_line_string - MultiLineString.from_line_strings([LineString.from_coordinates([[1.5,45.2],[-54.12312,-0.012]]),LineString.from_coordinates([[1.5,45.2],[-54.12312,-0.012],[45.123,23.3]])], 4326) + MultiLineString.from_line_strings([LineString.from_coordinates([[1.5,45.2],[-54.12312,-0.012]]),LineString.from_coordinates([[1.5,45.2],[-54.12312,-0.012],[45.123,23.3]])], @@default_srid) end def multi_polygon - MultiPolygon.from_polygons([Polygon.from_coordinates([[[12.4,-45.3],[45.4,41.6],[4.456,1.0698],[12.4,-45.3]],[[2.4,5.3],[5.4,1.4263],[14.46,1.06],[2.4,5.3]]]),Polygon.from_coordinates([[[0,0],[4,0],[4,4],[0,4],[0,0]],[[1,1],[3,1],[3,3],[1,3],[1,1]]])], 4326) + MultiPolygon.from_polygons([Polygon.from_coordinates([[[12.4,-45.3],[45.4,41.6],[4.456,1.0698],[12.4,-45.3]],[[2.4,5.3],[5.4,1.4263],[14.46,1.06],[2.4,5.3]]]),Polygon.from_coordinates([[[0.0,0.0],[4.0,0.0],[4.0,4.0],[0.0,4.0],[0.0,0.0]],[[1.0,1.0],[3.0,1.0],[3.0,3.0],[1.0,3.0],[1.0,1.0]]])], @@default_srid) end def geometry_collection - GeometryCollection.from_geometries([Point.from_x_y(4.67,45.4),LineString.from_coordinates([[5.7,12.45],[67.55,54]])], 4326) + GeometryCollection.from_geometries([Point.from_x_y(4.67,45.4),LineString.from_coordinates([[5.7,12.45],[67.55,54.0]])], @@default_srid) end def pointz - Point.from_x_y_z(1, 2, 3, 4326) + Point.from_x_y_z(1.0, 2.0, 3.0, @@default_srid) end def pointm - Point.from_x_y_m(1, 2, 3, 4326) + Point.from_x_y_m(1.0, 2.0, 3.0, @@default_srid) end def point4 - Point.from_x_y_z_m(1, 2, 3, 4, 4326) + Point.from_x_y_z_m(1.0, 2.0, 3.0, 4.0, @@default_srid) end end end From affa226333713de8fffab6a4cfe6927639b75db6 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Mon, 11 Oct 2010 14:24:36 -0500 Subject: [PATCH 2/3] Removing spatial_conditions from ActiveRecord::Base. That has been extracted and will be submitted later. --- lib/spatial_adapter.rb | 1 - lib/spatial_adapter/common/active_record_base.rb | 12 ------------ 2 files changed, 13 deletions(-) delete mode 100644 lib/spatial_adapter/common/active_record_base.rb diff --git a/lib/spatial_adapter.rb b/lib/spatial_adapter.rb index a783eee..01a8c31 100644 --- a/lib/spatial_adapter.rb +++ b/lib/spatial_adapter.rb @@ -38,5 +38,4 @@ class NotCompatibleError < ::StandardError require 'spatial_adapter/common/schema_definitions' require 'spatial_adapter/common/schema_dumper' require 'spatial_adapter/common/table_definition' -require 'spatial_adapter/common/active_record_base' require 'spatial_adapter/railtie' if defined?(Rails::Railtie) diff --git a/lib/spatial_adapter/common/active_record_base.rb b/lib/spatial_adapter/common/active_record_base.rb deleted file mode 100644 index 579ec76..0000000 --- a/lib/spatial_adapter/common/active_record_base.rb +++ /dev/null @@ -1,12 +0,0 @@ - -ActiveRecord::Base.class_eval do - #require 'active_record/version' - - def self.spatial_conditions(geom1, relationship, geom2) - connection.spatial_conditions(geom1, relationship, geom2) - end - - def self.spherical_distance( geom1, geom2 ) - connection.spherical_distance( geom1, geom2 ) - end -end \ No newline at end of file From 3811bbc8f77755d1474530a742690c049a26e3d6 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Thu, 14 Oct 2010 09:55:18 -0500 Subject: [PATCH 3/3] Handle column names specified as strings. --- lib/spatial_adapter/postgresql.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/spatial_adapter/postgresql.rb b/lib/spatial_adapter/postgresql.rb index b92190b..b3a2ae0 100644 --- a/lib/spatial_adapter/postgresql.rb +++ b/lib/spatial_adapter/postgresql.rb @@ -99,7 +99,7 @@ def create_table(table_name, options = {}) alias :original_remove_column :remove_column def remove_column(table_name, *column_names) - column_names = column_names.flatten + column_names = column_names.flatten.map {|c| c.to_sym} columns(table_name).each do |col| if column_names.include?(col.name.to_sym) # Geometry columns have to be removed using DropGeometryColumn