Changeset 4596
- Timestamp:
- 07/08/06 20:35:56 (2 years ago)
- Files:
-
- trunk/activerecord/CHANGELOG (modified) (1 diff)
- trunk/activerecord/lib/active_record/base.rb (modified) (1 diff)
- trunk/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb (modified) (1 diff)
- trunk/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb (modified) (1 diff)
- trunk/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb (modified) (15 diffs)
- trunk/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb (modified) (2 diffs)
- trunk/activerecord/lib/active_record/connection_adapters/db2_adapter.rb (modified) (1 diff)
- trunk/activerecord/lib/active_record/connection_adapters/frontbase_adapter.rb (modified) (5 diffs)
- trunk/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb (modified) (3 diffs)
- trunk/activerecord/lib/active_record/connection_adapters/openbase_adapter.rb (modified) (2 diffs)
- trunk/activerecord/lib/active_record/connection_adapters/oracle_adapter.rb (modified) (2 diffs)
- trunk/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb (modified) (7 diffs)
- trunk/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb (modified) (1 diff)
- trunk/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb (modified) (11 diffs)
- trunk/activerecord/lib/active_record/connection_adapters/sybase_adapter.rb (modified) (4 diffs)
- trunk/activerecord/lib/active_record/migration.rb (modified) (1 diff)
- trunk/activerecord/lib/active_record/schema_dumper.rb (modified) (2 diffs)
- trunk/activerecord/test/base_test.rb (modified) (2 diffs)
- trunk/activerecord/test/calculations_test.rb (modified) (1 diff)
- trunk/activerecord/test/defaults_test.rb (modified) (1 diff)
- trunk/activerecord/test/fixtures/db_definitions/db2.drop.sql (modified) (1 diff)
- trunk/activerecord/test/fixtures/db_definitions/db2.sql (modified) (1 diff)
- trunk/activerecord/test/fixtures/db_definitions/firebird.drop.sql (modified) (2 diffs)
- trunk/activerecord/test/fixtures/db_definitions/firebird.sql (modified) (1 diff)
- trunk/activerecord/test/fixtures/db_definitions/frontbase.drop.sql (modified) (1 diff)
- trunk/activerecord/test/fixtures/db_definitions/frontbase.sql (modified) (1 diff)
- trunk/activerecord/test/fixtures/db_definitions/mysql.drop.sql (modified) (1 diff)
- trunk/activerecord/test/fixtures/db_definitions/mysql.sql (modified) (1 diff)
- trunk/activerecord/test/fixtures/db_definitions/openbase.sql (modified) (1 diff)
- trunk/activerecord/test/fixtures/db_definitions/oracle.drop.sql (modified) (2 diffs)
- trunk/activerecord/test/fixtures/db_definitions/oracle.sql (modified) (1 diff)
- trunk/activerecord/test/fixtures/db_definitions/postgresql.drop.sql (modified) (2 diffs)
- trunk/activerecord/test/fixtures/db_definitions/postgresql.sql (modified) (2 diffs)
- trunk/activerecord/test/fixtures/db_definitions/sqlite.drop.sql (modified) (1 diff)
- trunk/activerecord/test/fixtures/db_definitions/sqlite.sql (modified) (1 diff)
- trunk/activerecord/test/fixtures/db_definitions/sqlserver.drop.sql (modified) (2 diffs)
- trunk/activerecord/test/fixtures/db_definitions/sqlserver.sql (modified) (2 diffs)
- trunk/activerecord/test/fixtures/db_definitions/sybase.drop.sql (modified) (1 diff)
- trunk/activerecord/test/fixtures/db_definitions/sybase.sql (modified) (1 diff)
- trunk/activerecord/test/fixtures/migrations_with_decimal (added)
- trunk/activerecord/test/fixtures/migrations_with_decimal/1_give_me_big_numbers.rb (added)
- trunk/activerecord/test/locking_test.rb (modified) (1 diff)
- trunk/activerecord/test/migration_test.rb (modified) (6 diffs)
- trunk/activerecord/test/schema_dumper_test.rb (modified) (3 diffs)
- trunk/activerecord/test/validations_test.rb (modified) (5 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/activerecord/CHANGELOG
r4594 r4596 1 1 *SVN* 2 3 * Numeric and decimal columns map to BigDecimal instead of Float. Those with scale 0 map to Integer. #5454 [robbat2@gentoo.org, work@ashleymoran.me.uk] 2 4 3 5 * Firebird migrations support. #5337 [Ken Kunz <kennethkunz@gmail.com>] trunk/activerecord/lib/active_record/base.rb
r4586 r4596 1727 1727 self.id = connection.next_sequence_value(self.class.sequence_name) 1728 1728 end 1729 1729 1730 1730 self.id = connection.insert( 1731 1731 "INSERT INTO #{self.class.table_name} " + trunk/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
r3914 r4596 1 1 require 'benchmark' 2 2 require 'date' 3 require 'bigdecimal' 4 require 'bigdecimal/util' 3 5 4 6 require 'active_record/connection_adapters/abstract/schema_definitions' trunk/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
r4391 r4596 17 17 "'#{quote_string(value)}'" # ' (for ruby-mode) 18 18 end 19 when NilClass then "NULL" 20 when TrueClass then (column && column.type == :integer ? '1' : quoted_true) 21 when FalseClass then (column && column.type == :integer ? '0' : quoted_false) 22 when Float, Fixnum, Bignum then value.to_s 23 when Date then "'#{value.to_s}'" 24 when Time, DateTime then "'#{quoted_date(value)}'" 25 else "'#{quote_string(value.to_yaml)}'" 19 when NilClass then "NULL" 20 when TrueClass then (column && column.type == :integer ? '1' : quoted_true) 21 when FalseClass then (column && column.type == :integer ? '0' : quoted_false) 22 when Float, Fixnum, Bignum then value.to_s 23 # BigDecimals need to be output in a non-normalized form and quoted. 24 when BigDecimal then value.to_s('F') 25 when Date then "'#{value.to_s}'" 26 when Time, DateTime then "'#{quoted_date(value)}'" 27 else "'#{quote_string(value.to_yaml)}'" 26 28 end 27 29 end trunk/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
r4593 r4596 1 1 require 'date' 2 require 'bigdecimal' 3 require 'bigdecimal/util' 2 4 3 5 module ActiveRecord … … 5 7 # An abstract definition of a column in a table. 6 8 class Column 7 attr_reader :name, :default, :type, :limit, :null, :sql_type 9 attr_reader :name, :default, :type, :limit, :null, :sql_type, :precision, :scale 8 10 attr_accessor :primary 9 11 … … 16 18 def initialize(name, default, sql_type = nil, null = true) 17 19 @name, @sql_type, @null, @limit = name, sql_type, null, extract_limit(sql_type) 20 @precision, @scale = extract_precision(sql_type), extract_scale(sql_type) 18 21 19 22 # simplified_type may depend on #limit, type_cast depends on #type … … 29 32 30 33 def number? 31 [:float, :integer ].include? type34 [:float, :integer, :decimal].include? type 32 35 end 33 36 … … 37 40 when :integer then Fixnum 38 41 when :float then Float 42 when :decimal then BigDecimal 39 43 when :datetime then Time 40 44 when :date then Date … … 55 59 when :integer then value.to_i rescue value ? 1 : 0 56 60 when :float then value.to_f 61 when :decimal then self.class.value_to_decimal(value) 57 62 when :datetime then self.class.string_to_time(value) 58 63 when :timestamp then self.class.string_to_time(value) … … 71 76 when :integer then "(#{var_name}.to_i rescue #{var_name} ? 1 : 0)" 72 77 when :float then "#{var_name}.to_f" 78 when :decimal then "#{self.class.name}.value_to_decimal(#{var_name})" 73 79 when :datetime then "#{self.class.name}.string_to_time(#{var_name})" 74 80 when :timestamp then "#{self.class.name}.string_to_time(#{var_name})" … … 128 134 # convert something to a boolean 129 135 def self.value_to_boolean(value) 130 return value if value==true || value==false 131 case value.to_s.downcase 132 when "true", "t", "1" then true 133 else false 136 if value == true || value == false 137 value 138 else 139 %w(true t 1).include?(value.to_s.downcase) 140 end 141 end 142 143 # convert something to a BigDecimal 144 def self.value_to_decimal(value) 145 if value.is_a?(BigDecimal) 146 value 147 elsif value.respond_to?(:to_d) 148 value.to_d 149 else 150 value.to_s.to_d 134 151 end 135 152 end … … 143 160 144 161 def extract_limit(sql_type) 145 return unless sql_type146 162 $1.to_i if sql_type =~ /\((.*)\)/ 163 end 164 165 def extract_precision(sql_type) 166 $2.to_i if sql_type =~ /^(numeric|decimal)\((\d+)(,\d+)?\)/i 167 end 168 169 def extract_scale(sql_type) 170 case sql_type 171 when /^(numeric|decimal)\((\d+)\)/i then 0 172 when /^(numeric|decimal)\((\d+)(,(\d+))\)/i then $4.to_i 173 end 147 174 end 148 175 … … 151 178 when /int/i 152 179 :integer 153 when /float|double |decimal|numeric/i180 when /float|double/i 154 181 :float 182 when /decimal|numeric/i 183 extract_scale(field_type) == 0 ? :integer : :decimal 155 184 when /datetime/i 156 185 :datetime … … 176 205 end 177 206 178 class ColumnDefinition < Struct.new(:base, :name, :type, :limit, : default, :null) #:nodoc:207 class ColumnDefinition < Struct.new(:base, :name, :type, :limit, :precision, :scale, :default, :null) #:nodoc: 179 208 def to_sql 180 column_sql = "#{base.quote_column_name(name)} #{type_to_sql(type.to_sym, limit )}"209 column_sql = "#{base.quote_column_name(name)} #{type_to_sql(type.to_sym, limit, precision, scale)}" 181 210 add_column_options!(column_sql, :null => null, :default => default) 182 211 column_sql … … 185 214 186 215 private 187 def type_to_sql(name, limit )188 base.type_to_sql(name, limit ) rescue name216 def type_to_sql(name, limit, precision, scale) 217 base.type_to_sql(name, limit, precision, scale) rescue name 189 218 end 190 219 … … 218 247 # The +type+ parameter must be one of the following values: 219 248 # <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>, 220 # <tt>:integer</tt>, <tt>:float</tt>, <tt>:d atetime</tt>,221 # <tt>: timestamp</tt>, <tt>:time</tt>, <tt>:date</tt>,222 # <tt>: binary</tt>, <tt>:boolean</tt>.249 # <tt>:integer</tt>, <tt>:float</tt>, <tt>:decimal</tt>, 250 # <tt>:datetime</tt>, <tt>:timestamp</tt>, <tt>:time</tt>, 251 # <tt>:date</tt>, <tt>:binary</tt>, <tt>:boolean</tt>. 223 252 # 224 253 # Available options are (none of these exists by default): … … 233 262 # Allows or disallows +NULL+ values in the column. This option could 234 263 # have been named <tt>:null_allowed</tt>. 264 # * <tt>:precision</tt>: 265 # Specifies the precision for a <tt>:decimal</tt> column. 266 # * <tt>:scale</tt>: 267 # Specifies the scale for a <tt>:decimal</tt> column. 268 # 269 # Please be aware of different RDBMS implementations behavior with 270 # <tt>:decimal</tt> columns: 271 # * The SQL standard says the default scale should be 0, <tt>:scale</tt> <= 272 # <tt>:precision</tt>, and makes no comments about the requirements of 273 # <tt>:precision</tt>. 274 # * MySQL: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..30]. 275 # Default is (10,0). 276 # * PostGres?: <tt>:precision</tt> [1..infinity], 277 # <tt>:scale</tt> [0..infinity]. No default. 278 # * Sqlite2: Any <tt>:precision</tt> and <tt>:scale</tt> may be used. 279 # Internal storage as strings. No default. 280 # * Sqlite3: No restrictions on <tt>:precision</tt> and <tt>:scale</tt>, 281 # but the maximum supported <tt>:precision</tt> is 16. No default. 282 # * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127]. 283 # Default is (38,0). 284 # * DB2: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..62]. 285 # Default unknown. 286 # * Firebird: <tt>:precision</tt> [1..18], <tt>:scale</tt> [0..18]. 287 # Default (9,0). Internal types NUMERIC and DECIMAL have different 288 # storage rules, decimal being better. 289 # * FrontBase?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38]. 290 # Default (38,0). WARNING Max <tt>:precision</tt>/<tt>:scale</tt> for 291 # NUMERIC is 19, and DECIMAL is 38. 292 # * SqlServer?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38]. 293 # Default (38,0). 294 # * Sybase: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38]. 295 # Default (38,0). 296 # * OpenBase?: Documentation unclear. Claims storage in <tt>double</tt>. 235 297 # 236 298 # This method returns <tt>self</tt>. … … 246 308 # td.column(:sales_stage, :string, :limit => 20, :default => 'new', :null => false) 247 309 # #=> sales_stage VARCHAR(20) DEFAULT 'new' NOT NULL 310 # 311 # def.column(:bill_gates_money, :decimal, :precision => 15, :scale => 2) 312 # #=> bill_gates_money DECIMAL(15,2) 313 # 314 # def.column(:sensor_reading, :decimal, :precision => 30, :scale => 20) 315 # #=> sensor_reading DECIMAL(30,20) 316 # 317 # # While <tt>:scale</tt> defaults to zero on most databases, it 318 # # probably wouldn't hurt to include it. 319 # def.column(:huge_integer, :decimal, :precision => 30) 320 # #=> huge_integer DECIMAL(30) 248 321 def column(name, type, options = {}) 249 322 column = self[name] || ColumnDefinition.new(@base, name, type) 250 323 column.limit = options[:limit] || native[type.to_sym][:limit] if options[:limit] or native[type.to_sym] 324 column.precision = options[:precision] 325 column.scale = options[:scale] 251 326 column.default = options[:default] 252 327 column.null = options[:null] trunk/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
r4239 r4596 120 120 # See TableDefinition#column for details of the options you can use. 121 121 def add_column(table_name, column_name, type, options = {}) 122 add_column_sql = "ALTER TABLE #{table_name} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit] )}"122 add_column_sql = "ALTER TABLE #{table_name} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" 123 123 add_column_options!(add_column_sql, options) 124 124 execute(add_column_sql) … … 255 255 256 256 257 def type_to_sql(type, limit = nil ) #:nodoc:257 def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc: 258 258 native = native_database_types[type] 259 limit ||= native[:limit]260 259 column_type_sql = native[:name] 261 column_type_sql << "(#{limit})" if limit 262 column_type_sql 260 if type == :decimal # ignore limit, use precison and scale 261 precision ||= native[:precision] 262 scale ||= native[:scale] 263 if precision 264 if scale 265 column_type_sql << "(#{precision},#{scale})" 266 else 267 column_type_sql << "(#{precision})" 268 end 269 else 270 raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale if specifed" if scale 271 end 272 column_type_sql 273 else 274 limit ||= native[:limit] 275 column_type_sql << "(#{limit})" if limit 276 column_type_sql 277 end 263 278 end 264 279 trunk/activerecord/lib/active_record/connection_adapters/db2_adapter.rb
r4013 r4596 163 163 :integer => { :name => 'int' }, 164 164 :float => { :name => 'float' }, 165 :decimal => { :name => 'decimal' }, 165 166 :datetime => { :name => 'timestamp' }, 166 167 :timestamp => { :name => 'timestamp' }, trunk/activerecord/lib/active_record/connection_adapters/frontbase_adapter.rb
r4393 r4596 161 161 @null = nullable == "YES" 162 162 @text = [:string, :text].include? @type 163 @number = [:float, :integer ].include? @type163 @number = [:float, :integer, :decimal].include? @type 164 164 @fb_autogen = false 165 165 … … 279 279 :integer => { :name => "INTEGER" }, 280 280 :float => { :name => "FLOAT" }, 281 :decimal => { :name => "DECIMAL" }, 281 282 :datetime => { :name => "TIMESTAMP" }, 282 283 :timestamp => { :name => "TIMESTAMP" }, … … 320 321 when :float 321 322 value.to_f.to_s 323 when :decimal 324 value.to_d.to_s("F") 322 325 when :datetime, :timestamp 323 326 "TIMESTAMP '#{value.strftime("%Y-%m-%d %H:%M:%S")}'" … … 360 363 s = value.unpack("H*").first 361 364 "X'#{s}'" 362 elsif column && [:integer, :float ].include?(column.type)365 elsif column && [:integer, :float, :decimal].include?(column.type) 363 366 value.to_s 364 367 else … … 371 374 when FalseClass 372 375 (column && column.type == :integer ? '0' : quoted_false) 373 when Float, Fixnum, Bignum 376 when Float, Fixnum, Bignum, BigDecimal 374 377 value.to_s 375 378 when Time, Date, DateTime trunk/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
r4340 r4596 103 103 :integer => { :name => "int", :limit => 11 }, 104 104 :float => { :name => "float" }, 105 :decimal => { :name => "decimal" }, 105 106 :datetime => { :name => "datetime" }, 106 107 :timestamp => { :name => "datetime" }, … … 119 120 s = column.class.string_to_binary(value).unpack("H*")[0] 120 121 "x'#{s}'" 122 elsif value.kind_of?(BigDecimal) 123 "'#{value.to_s("F")}'" 121 124 else 122 125 super … … 313 316 end 314 317 315 change_column_sql = "ALTER TABLE #{table_name} CHANGE #{column_name} #{column_name} #{type_to_sql(type, options[:limit] )}"318 change_column_sql = "ALTER TABLE #{table_name} CHANGE #{column_name} #{column_name} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" 316 319 add_column_options!(change_column_sql, options) 317 320 execute(change_column_sql) trunk/activerecord/lib/active_record/connection_adapters/openbase_adapter.rb
r4366 r4596 33 33 def simplified_type(field_type) 34 34 return :integer if field_type.downcase =~ /long/ 35 return : floatif field_type.downcase == "money"35 return :decimal if field_type.downcase == "money" 36 36 return :binary if field_type.downcase == "object" 37 37 super … … 69 69 :integer => { :name => "integer" }, 70 70 :float => { :name => "float" }, 71 :decimal => { :name => "decimal" }, 71 72 :datetime => { :name => "datetime" }, 72 73 :timestamp => { :name => "timestamp" }, trunk/activerecord/lib/active_record/connection_adapters/oracle_adapter.rb
r4574 r4596 94 94 return :boolean if OracleAdapter.emulate_booleans && field_type == 'NUMBER(1)' 95 95 case field_type 96 when /num/i : @scale == 0 ? :integer : :float 97 when /date|time/i : :datetime 98 else super 96 when /date|time/i then :datetime 97 else super 99 98 end 100 99 end … … 162 161 :integer => { :name => "NUMBER", :limit => 38 }, 163 162 :float => { :name => "NUMBER" }, 163 :decimal => { :name => "DECIMAL" }, 164 164 :datetime => { :name => "DATE" }, 165 165 :timestamp => { :name => "DATE" }, trunk/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
r4580 r4596 94 94 :integer => { :name => "integer" }, 95 95 :float => { :name => "float" }, 96 :decimal => { :name => "decimal" }, 96 97 :datetime => { :name => "timestamp" }, 97 98 :timestamp => { :name => "timestamp" }, … … 233 234 234 235 def columns(table_name, name = nil) #:nodoc: 235 column_definitions(table_name).collect do |name, type, default, notnull |236 Column.new(name, default_value(default), translate_field_type(type),237 notnull == "f")236 column_definitions(table_name).collect do |name, type, default, notnull, typmod| 237 # typmod now unused as limit, precision, scale all handled by superclass 238 Column.new(name, default_value(default), translate_field_type(type), notnull == "f") 238 239 end 239 240 end … … 347 348 def change_column(table_name, column_name, type, options = {}) #:nodoc: 348 349 begin 349 execute "ALTER TABLE #{table_name} ALTER #{column_name} TYPE #{type_to_sql(type, options[:limit])}"350 execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} TYPE #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" 350 351 rescue ActiveRecord::StatementInvalid 351 352 # This is PG7, so we use a more arcane way of doing it. 352 353 begin_db_transaction 353 354 add_column(table_name, "#{column_name}_ar_tmp", type, options) 354 execute "UPDATE #{table_name} SET #{column_name}_ar_tmp = CAST(#{column_name} AS #{type_to_sql(type, options[:limit] )})"355 execute "UPDATE #{table_name} SET #{column_name}_ar_tmp = CAST(#{column_name} AS #{type_to_sql(type, options[:limit], options[:precision], options[:scale])})" 355 356 remove_column(table_name, column_name) 356 357 rename_column(table_name, "#{column_name}_ar_tmp", column_name) … … 361 362 362 363 def change_column_default(table_name, column_name, default) #:nodoc: 363 execute "ALTER TABLE #{table_name} ALTER COLUMN #{ column_name} SET DEFAULT '#{default}'"364 execute "ALTER TABLE #{table_name} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT '#{default}'" 364 365 end 365 366 366 367 def rename_column(table_name, column_name, new_column_name) #:nodoc: 367 execute "ALTER TABLE #{table_name} RENAME COLUMN #{ column_name} TO #{new_column_name}"368 execute "ALTER TABLE #{table_name} RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}" 368 369 end 369 370 … … 372 373 end 373 374 374 def type_to_sql(type, limit = nil ) #:nodoc:375 def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc: 375 376 return super unless type.to_s == 'integer' 376 377 … … 386 387 private 387 388 BYTEA_COLUMN_TYPE_OID = 17 389 NUMERIC_COLUMN_TYPE_OID = 1700 388 390 TIMESTAMPOID = 1114 389 391 TIMESTAMPTZOID = 1184 … … 418 420 when TIMESTAMPTZOID, TIMESTAMPOID 419 421 column = cast_to_time(column) 422 when NUMERIC_COLUMN_TYPE_OID 423 column = column.to_d if column.respond_to?(:to_d) 420 424 end 421 425 trunk/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
r4460 r4596 111 111 :integer => { :name => "integer" }, 112 112 :float => { :name => "float" }, 113 :decimal => { :name => "decimal" }, 113 114 :datetime => { :name => "datetime" }, 114 115 :timestamp => { :name => "datetime" }, trunk/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb
r4418 r4596 1 1 require 'active_record/connection_adapters/abstract_adapter' 2 3 require 'bigdecimal' 4 require 'bigdecimal/util' 2 5 3 6 # sqlserver_adapter.rb -- ActiveRecord adapter for Microsoft SQL Server … … 46 49 47 50 module ConnectionAdapters 48 class ColumnWithIdentity< Column# :nodoc:49 attr_reader :identity, :is_special , :scale50 51 def initialize(name, default, sql_type = nil, i s_identity = false, null = true, scale_value = 0)51 class SQLServerColumn < Column# :nodoc: 52 attr_reader :identity, :is_special 53 54 def initialize(name, default, sql_type = nil, identity = false, null = true) # TODO: check ok to remove scale_value = 0 52 55 super(name, default, sql_type, null) 53 @identity = i s_identity54 @is_special = sql_type =~ /text|ntext|image/i ? true : false55 @scale = scale_value56 @identity = identity 57 @is_special = sql_type =~ /text|ntext|image/i 58 # TODO: check ok to remove @scale = scale_value 56 59 # SQL Server only supports limits on *char and float types 57 60 @limit = nil unless @type == :float or @type == :string … … 60 63 def simplified_type(field_type) 61 64 case field_type 62 when /int|bigint|smallint|tinyint/i then :integer 63 when /float|double|decimal|money|numeric|real|smallmoney/i then @scale == 0 ? :integer : :float 64 when /datetime|smalldatetime/i then :datetime 65 when /timestamp/i then :timestamp 66 when /time/i then :time 67 when /text|ntext/i then :text 68 when /binary|image|varbinary/i then :binary 69 when /char|nchar|nvarchar|string|varchar/i then :string 70 when /bit/i then :boolean 71 when /uniqueidentifier/i then :string 65 when /money/i then :decimal 66 when /image/i then :binary 67 when /bit/i then :boolean 68 when /uniqueidentifier/i then :string 69 else super 72 70 end 73 71 end … … 76 74 return nil if value.nil? || value =~ /^\s*null\s*$/i 77 75 case type 78 when :string then value79 when :integer then value == true || value == false ? value == true ? 1 : 0 : value.to_i80 when :float then value.to_f81 76 when :datetime then cast_to_datetime(value) 82 77 when :timestamp then cast_to_time(value) … … 84 79 when :date then cast_to_datetime(value) 85 80 when :boolean then value == true or (value =~ /^t(rue)?$/i) == 0 or value.to_s == '1' 86 else value81 else super 87 82 end 88 83 end … … 185 180 :integer => { :name => "int" }, 186 181 :float => { :name => "float", :limit => 8 }, 182 :decimal => { :name => "decimal" }, 187 183 :datetime => { :name => "datetime" }, 188 184 :timestamp => { :name => "datetime" }, 189 185 :time => { :name => "datetime" }, 190 186 :date => { :name => "datetime" }, 191 :binary => { :name => "image" },192 :boolean => { :name => "bit" }187 :binary => { :name => "image" }, 188 :boolean => { :name => "bit" } 193 189 } 194 190 end … … 241 237 table_name = table_name.to_s if table_name.is_a?(Symbol) 242 238 table_name = table_name.split('.')[-1] unless table_name.nil? 243 sql = "SELECT COLUMN_NAME as ColName, COLUMN_DEFAULT as DefaultValue, DATA_TYPE as ColType, IS_NULLABLE As IsNullable, COL_LENGTH('#{table_name}', COLUMN_NAME) as Length, COLUMNPROPERTY(OBJECT_ID('#{table_name}'), COLUMN_NAME, 'IsIdentity') as IsIdentity, NUMERIC_SCALE as Scale FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '#{table_name}'" 239 sql = "SELECT COLUMN_NAME as ColName, 240 COLUMN_DEFAULT as DefaultValue, 241 DATA_TYPE as ColType, 242 IS_NULLABLE As IsNullable, 243 COL_LENGTH('#{table_name}', COLUMN_NAME) as Length, 244 COLUMNPROPERTY(OBJECT_ID('#{table_name}'), COLUMN_NAME, 'IsIdentity') as IsIdentity, 245 NUMERIC_PRECISION as [Precision], 246 NUMERIC_SCALE as Scale 247 FROM INFORMATION_SCHEMA.COLUMNS 248 WHERE TABLE_NAME = '#{table_name}'" 244 249 # Comment out if you want to have the Columns select statment logged. 245 250 # Personally, I think it adds unnecessary bloat to the log. … … 250 255 result.each do |field| 251 256 default = field[:DefaultValue].to_s.gsub!(/[()\']/,"") =~ /null/ ? nil : field[:DefaultValue] 252 type = "#{field[:ColType]}(#{field[:Length]})" 257 if field[:ColType] =~ /numeric|decimal/i 258 type = "#{field[:ColType]}(#{field[:Precision]},#{field[:Scale]})" 259 else 260 type = "#{field[:ColType]}(#{field[:Length]})" 261 end 253 262 is_identity = field[:IsIdentity] == 1 254 263 is_nullable = field[:IsNullable] == 'YES' 255 columns << ColumnWithIdentity.new(field[:ColName], default, type, is_identity, is_nullable, field[:Scale])264 columns << SQLServerColumn.new(field[:ColName], default, type, is_identity, is_nullable) 256 265 end 257 266 columns … … 337 346 338 347 case value 339 when String340 if column && column.type == :binary && column.class.respond_to?(:string_to_binary)341 "'#{quote_string(column.class.string_to_binary(value))}'"342 else343 "'#{quote_string(value)}'"344 end345 when NilClass then "NULL"346 348 when TrueClass then '1' 347 349 when FalseClass then '0' 348 when Float, Fixnum, Bignum then value.to_s349 when Date then "'#{value.to_s}'"350 350 when Time, DateTime then "'#{value.strftime("%Y-%m-%d %H:%M:%S")}'" 351 else "'#{quote_string(value.to_yaml)}'"351 else super 352 352 end 353 353 end … … 460 460 461 461 def change_column(table_name, column_name, type, options = {}) #:nodoc: 462 sql_commands = ["ALTER TABLE #{table_name} ALTER COLUMN #{column_name} #{type_to_sql(type, options[:limit] )}"]462 sql_commands = ["ALTER TABLE #{table_name} ALTER COLUMN #{column_name} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"] 463 463 if options[:default] 464 464 remove_default_constraint(table_name, column_name) … … 484 484 def remove_index(table_name, options = {}) 485 485 execute "DROP INDEX #{table_name}.#{quote_column_name(index_name(table_name, options))}" 486 end487 488 def type_to_sql(type, limit = nil) #:nodoc:489 native = native_database_types[type]490 # if there's no :limit in the default type definition, assume that type doesn't support limits491 limit = limit || native[:limit]492 column_type_sql = native[:name]493 column_type_sql << "(#{limit})" if limit494 column_type_sql495 486 end 496 487 trunk/activerecord/lib/active_record/connection_adapters/sybase_adapter.rb
r4393 r4596 87 87 def simplified_type(field_type) 88 88 case field_type 89 when /int|bigint|smallint|tinyint/i then :integer 90 when /float|double|decimal|money|numeric|real|smallmoney/i then :float 91 when /text|ntext/i then :text 92 when /binary|image|varbinary/i then :binary 93 when /char|nchar|nvarchar|string|varchar/i then :string 94 when /bit/i then :boolean 95 when /datetime|smalldatetime/i then :datetime 96 else super 89 when /int|bigint|smallint|tinyint/i then :integer 90 when /float|double|real/i then :float 91 when /decimal|money|numeric|smallmoney/i then :decimal 92 when /text|ntext/i then :text 93 when /binary|image|varbinary/i then :binary 94 when /char|nchar|nvarchar|string|varchar/i then :string 95 when /bit/i then :boolean 96 when /datetime|smalldatetime/i then :datetime 97 else super 97 98 end 98 99 end … … 138 139 :integer => { :name => "int" }, 139 140 :float => { :name => "float", :limit => 8 }, 141 :decimal => { :name => "decimal" }, 140 142 :datetime => { :name => "datetime" }, 141 143 :timestamp => { :name => "timestamp" }, … … 288 290 when TrueClass then '1' 289 291 when FalseClass then '0' 290 when Float, Fixnum, Bignum 291 force_numeric?(column) ? value.to_s : "'#{value.to_s}'" 292 when Date then "'#{value.to_s}'" 292 when Float, Fixnum, Bignum then force_numeric?(column) ? value.to_s : "'#{value.to_s}'" 293 293 when Time, DateTime then "'#{value.strftime("%Y-%m-%d %H:%M:%S")}'" 294 else "'#{quote_string(value.to_yaml)}'"294 else super 295 295 end 296 296 end … … 299 299 # if column is nil (not specified). 300 300 def force_numeric?(column) 301 (column.nil? || [:integer, :float ].include?(column.type))301 (column.nil? || [:integer, :float, :decimal].include?(column.type)) 302 302 end 303 303 trunk/activerecord/lib/active_record/migration.rb
r4249 r4596 65 65 # * <tt>add_column(table_name, column_name, type, options)</tt>: Adds a new column to the table called +table_name+ 66 66 # named +column_name+ specified to be one of the following types: 67 # :string, :text, :integer, :float, :datetime, :timestamp, :time, :date, :binary, :boolean. A default value can be specified 68 # by passing an +options+ hash like { :default => 11 }. 67 # :string, :text, :integer, :float, :decimal, :datetime, :timestamp, :time, 68 # :date, :binary, :boolean. A default value can be specified by passing an 69 # +optio