Upgraded Rails and RSpec
[monkeycharger.git] / vendor / rails / activerecord / lib / active_record / connection_adapters / abstract / schema_definitions.rb
blobc6cdc776d6c948eb9ff41e3de2daf19815d9c2f5
1 require 'date'
2 require 'bigdecimal'
3 require 'bigdecimal/util'
5 module ActiveRecord
6   module ConnectionAdapters #:nodoc:
7     # An abstract definition of a column in a table.
8     class Column
9       module Format
10         ISO_DATE = /\A(\d{4})-(\d\d)-(\d\d)\z/
11         ISO_DATETIME = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/
12       end
14       attr_reader :name, :default, :type, :limit, :null, :sql_type, :precision, :scale
15       attr_accessor :primary
17       # Instantiates a new column in the table.
18       #
19       # +name+ is the column's name, as in <tt><b>supplier_id</b> int(11)</tt>.
20       # +default+ is the type-casted default value, such as <tt>sales_stage varchar(20) default <b>'new'</b></tt>.
21       # +sql_type+ is only used to extract the column's length, if necessary.  For example, <tt>company_name varchar(<b>60</b>)</tt>.
22       # +null+ determines if this column allows +NULL+ values.
23       def initialize(name, default, sql_type = nil, null = true)
24         @name, @sql_type, @null = name, sql_type, null
25         @limit, @precision, @scale  = extract_limit(sql_type), extract_precision(sql_type), extract_scale(sql_type) 
26         @type = simplified_type(sql_type)
27         @default = extract_default(default)
29         @primary = nil
30       end
32       def text?
33         [:string, :text].include? type
34       end
36       def number?
37         [:float, :integer, :decimal].include? type
38       end
40       # Returns the Ruby class that corresponds to the abstract data type.
41       def klass
42         case type
43           when :integer       then Fixnum
44           when :float         then Float
45           when :decimal       then BigDecimal
46           when :datetime      then Time
47           when :date          then Date
48           when :timestamp     then Time
49           when :time          then Time
50           when :text, :string then String
51           when :binary        then String
52           when :boolean       then Object
53         end
54       end
56       # Casts value (which is a String) to an appropriate instance.
57       def type_cast(value)
58         return nil if value.nil?
59         case type
60           when :string    then value
61           when :text      then value
62           when :integer   then value.to_i rescue value ? 1 : 0
63           when :float     then value.to_f
64           when :decimal   then self.class.value_to_decimal(value)
65           when :datetime  then self.class.string_to_time(value)
66           when :timestamp then self.class.string_to_time(value)
67           when :time      then self.class.string_to_dummy_time(value)
68           when :date      then self.class.string_to_date(value)
69           when :binary    then self.class.binary_to_string(value)
70           when :boolean   then self.class.value_to_boolean(value)
71           else value
72         end
73       end
75       def type_cast_code(var_name)
76         case type
77           when :string    then nil
78           when :text      then nil
79           when :integer   then "(#{var_name}.to_i rescue #{var_name} ? 1 : 0)"
80           when :float     then "#{var_name}.to_f"
81           when :decimal   then "#{self.class.name}.value_to_decimal(#{var_name})"
82           when :datetime  then "#{self.class.name}.string_to_time(#{var_name})"
83           when :timestamp then "#{self.class.name}.string_to_time(#{var_name})"
84           when :time      then "#{self.class.name}.string_to_dummy_time(#{var_name})"
85           when :date      then "#{self.class.name}.string_to_date(#{var_name})"
86           when :binary    then "#{self.class.name}.binary_to_string(#{var_name})"
87           when :boolean   then "#{self.class.name}.value_to_boolean(#{var_name})"
88           else nil
89         end
90       end
92       # Returns the human name of the column name.
93       #
94       # ===== Examples
95       #  Column.new('sales_stage', ...).human_name #=> 'Sales stage'
96       def human_name
97         Base.human_attribute_name(@name)
98       end
100       def extract_default(default)
101         type_cast(default)
102       end
104       class << self
105         # Used to convert from Strings to BLOBs
106         def string_to_binary(value)
107           value
108         end
110         # Used to convert from BLOBs to Strings
111         def binary_to_string(value)
112           value
113         end
115         def string_to_date(string)
116           return string unless string.is_a?(String)
117           return nil if string.empty?
119           fast_string_to_date(string) || fallback_string_to_date(string)
120         end
122         def string_to_time(string)
123           return string unless string.is_a?(String)
124           return nil if string.empty?
126           fast_string_to_time(string) || fallback_string_to_time(string)
127         end
129         def string_to_dummy_time(string)
130           return string unless string.is_a?(String)
131           return nil if string.empty?
133           string_to_time "2000-01-01 #{string}"
134         end
136         # convert something to a boolean
137         def value_to_boolean(value)
138           if value == true || value == false
139             value
140           else
141             %w(true t 1).include?(value.to_s.downcase)
142           end
143         end
145         # convert something to a BigDecimal
146         def value_to_decimal(value)
147           if value.is_a?(BigDecimal)
148             value
149           elsif value.respond_to?(:to_d)
150             value.to_d
151           else
152             value.to_s.to_d
153           end
154         end
156         protected
157           # '0.123456' -> 123456
158           # '1.123456' -> 123456
159           def microseconds(time)
160             ((time[:sec_fraction].to_f % 1) * 1_000_000).to_i
161           end
163           def new_date(year, mon, mday)
164             if year && year != 0
165               Date.new(year, mon, mday) rescue nil
166             end
167           end
169           def new_time(year, mon, mday, hour, min, sec, microsec)
170             # Treat 0000-00-00 00:00:00 as nil.
171             return nil if year.nil? || year == 0
173             Time.send(Base.default_timezone, year, mon, mday, hour, min, sec, microsec)
174           # Over/underflow to DateTime
175           rescue ArgumentError, TypeError
176             zone_offset = Base.default_timezone == :local ? DateTime.local_offset : 0
177             # Append zero calendar reform start to account for dates skipped by calendar reform
178             DateTime.new(year, mon, mday, hour, min, sec, zone_offset, 0) rescue nil
179           end
181           def fast_string_to_date(string)
182             if string =~ Format::ISO_DATE
183               new_date $1.to_i, $2.to_i, $3.to_i
184             end
185           end
187           # Doesn't handle time zones.
188           def fast_string_to_time(string)
189             if string =~ Format::ISO_DATETIME
190               microsec = ($7.to_f * 1_000_000).to_i
191               new_time $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, microsec
192             end
193           end
195           def fallback_string_to_date(string)
196             new_date *ParseDate.parsedate(string)[0..2]
197           end
199           def fallback_string_to_time(string)
200             time_hash = Date._parse(string)
201             time_hash[:sec_fraction] = microseconds(time_hash)
203             new_time *time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction)
204           end
205       end
207       private
208         def extract_limit(sql_type)
209           $1.to_i if sql_type =~ /\((.*)\)/
210         end
212         def extract_precision(sql_type)
213           $2.to_i if sql_type =~ /^(numeric|decimal|number)\((\d+)(,\d+)?\)/i
214         end
216         def extract_scale(sql_type)
217           case sql_type
218             when /^(numeric|decimal|number)\((\d+)\)/i then 0
219             when /^(numeric|decimal|number)\((\d+)(,(\d+))\)/i then $4.to_i
220           end
221         end
223         def simplified_type(field_type)
224           case field_type
225             when /int/i
226               :integer
227             when /float|double/i
228               :float
229             when /decimal|numeric|number/i
230               extract_scale(field_type) == 0 ? :integer : :decimal
231             when /datetime/i
232               :datetime
233             when /timestamp/i
234               :timestamp
235             when /time/i
236               :time
237             when /date/i
238               :date
239             when /clob/i, /text/i
240               :text
241             when /blob/i, /binary/i
242               :binary
243             when /char/i, /string/i
244               :string
245             when /boolean/i
246               :boolean
247           end
248         end
249     end
251     class IndexDefinition < Struct.new(:table, :name, :unique, :columns) #:nodoc:
252     end
254     class ColumnDefinition < Struct.new(:base, :name, :type, :limit, :precision, :scale, :default, :null) #:nodoc:
255       
256       def sql_type
257         base.type_to_sql(type.to_sym, limit, precision, scale) rescue type
258       end
259       
260       def to_sql
261         column_sql = "#{base.quote_column_name(name)} #{sql_type}"
262         add_column_options!(column_sql, :null => null, :default => default) unless type.to_sym == :primary_key
263         column_sql
264       end
265       alias to_s :to_sql
267       private
269         def add_column_options!(sql, options)
270           base.add_column_options!(sql, options.merge(:column => self))
271         end
272     end
274     # Represents a SQL table in an abstract way.
275     # Columns are stored as a ColumnDefinition in the #columns attribute.
276     class TableDefinition
277       attr_accessor :columns
279       def initialize(base)
280         @columns = []
281         @base = base
282       end
284       # Appends a primary key definition to the table definition.
285       # Can be called multiple times, but this is probably not a good idea.
286       def primary_key(name)
287         column(name, :primary_key)
288       end
290       # Returns a ColumnDefinition for the column with name +name+.
291       def [](name)
292         @columns.find {|column| column.name.to_s == name.to_s}
293       end
295       # Instantiates a new column for the table.
296       # The +type+ parameter must be one of the following values:
297       # <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
298       # <tt>:integer</tt>, <tt>:float</tt>, <tt>:decimal</tt>,
299       # <tt>:datetime</tt>, <tt>:timestamp</tt>, <tt>:time</tt>,
300       # <tt>:date</tt>, <tt>:binary</tt>, <tt>:boolean</tt>.
301       #
302       # Available options are (none of these exists by default):
303       # * <tt>:limit</tt> -
304       #   Requests a maximum column length (<tt>:string</tt>, <tt>:text</tt>,
305       #   <tt>:binary</tt> or <tt>:integer</tt> columns only)
306       # * <tt>:default</tt> -
307       #   The column's default value. Use nil for NULL.
308       # * <tt>:null</tt> -
309       #   Allows or disallows +NULL+ values in the column.  This option could
310       #   have been named <tt>:null_allowed</tt>.
311       # * <tt>:precision</tt> -
312       #   Specifies the precision for a <tt>:decimal</tt> column. 
313       # * <tt>:scale</tt> -
314       #   Specifies the scale for a <tt>:decimal</tt> column. 
315       #
316       # Please be aware of different RDBMS implementations behavior with
317       # <tt>:decimal</tt> columns:
318       # * The SQL standard says the default scale should be 0, <tt>:scale</tt> <=
319       #   <tt>:precision</tt>, and makes no comments about the requirements of
320       #   <tt>:precision</tt>.
321       # * MySQL: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..30]. 
322       #   Default is (10,0).
323       # * PostgreSQL: <tt>:precision</tt> [1..infinity], 
324       #   <tt>:scale</tt> [0..infinity]. No default.
325       # * SQLite2: Any <tt>:precision</tt> and <tt>:scale</tt> may be used. 
326       #   Internal storage as strings. No default.
327       # * SQLite3: No restrictions on <tt>:precision</tt> and <tt>:scale</tt>,
328       #   but the maximum supported <tt>:precision</tt> is 16. No default.
329       # * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127]. 
330       #   Default is (38,0).
331       # * DB2: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..62]. 
332       #   Default unknown.
333       # * Firebird: <tt>:precision</tt> [1..18], <tt>:scale</tt> [0..18]. 
334       #   Default (9,0). Internal types NUMERIC and DECIMAL have different
335       #   storage rules, decimal being better.
336       # * FrontBase?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38]. 
337       #   Default (38,0). WARNING Max <tt>:precision</tt>/<tt>:scale</tt> for
338       #   NUMERIC is 19, and DECIMAL is 38.
339       # * SqlServer?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38]. 
340       #   Default (38,0).
341       # * Sybase: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38]. 
342       #   Default (38,0).
343       # * OpenBase?: Documentation unclear. Claims storage in <tt>double</tt>.
344       #
345       # This method returns <tt>self</tt>.
346       #
347       # == Examples
348       #  # Assuming td is an instance of TableDefinition
349       #  td.column(:granted, :boolean)
350       #    #=> granted BOOLEAN
351       #
352       #  td.column(:picture, :binary, :limit => 2.megabytes)
353       #    #=> picture BLOB(2097152)
354       #
355       #  td.column(:sales_stage, :string, :limit => 20, :default => 'new', :null => false)
356       #    #=> sales_stage VARCHAR(20) DEFAULT 'new' NOT NULL
357       #
358       #  def.column(:bill_gates_money, :decimal, :precision => 15, :scale => 2)
359       #    #=> bill_gates_money DECIMAL(15,2)
360       #
361       #  def.column(:sensor_reading, :decimal, :precision => 30, :scale => 20)
362       #    #=> sensor_reading DECIMAL(30,20)
363       #
364       #  # While <tt>:scale</tt> defaults to zero on most databases, it
365       #  # probably wouldn't hurt to include it.
366       #  def.column(:huge_integer, :decimal, :precision => 30)
367       #    #=> huge_integer DECIMAL(30)
368       #
369       # == Short-hand examples
370       #
371       # Instead of calling column directly, you can also work with the short-hand definitions for the default types.
372       # They use the type as the method name instead of as a parameter and allow for multiple columns to be defined
373       # in a single statement.
374       #
375       # What can be written like this with the regular calls to column:
376       #
377       #   create_table "products", :force => true do |t|
378       #     t.column "shop_id",    :integer
379       #     t.column "creator_id", :integer
380       #     t.column "name",       :string,   :default => "Untitled"
381       #     t.column "value",      :string,   :default => "Untitled"
382       #     t.column "created_at", :datetime
383       #     t.column "updated_at", :datetime
384       #   end
385       #
386       # Can also be written as follows using the short-hand:
387       #
388       #   create_table :products do |t|
389       #     t.integer :shop_id, :creator_id
390       #     t.string  :name, :value, :default => "Untitled"
391       #     t.timestamps
392       #   end
393       #
394       # There's a short-hand method for each of the type values declared at the top. And then there's 
395       # TableDefinition#timestamps that'll add created_at and updated_at as datetimes.
396       #
397       # TableDefinition#references will add an appropriately-named _id column, plus a corresponding _type
398       # column if the :polymorphic option is supplied. If :polymorphic is a hash of options, these will be
399       # used when creating the _type column. So what can be written like this:
400       #
401       #   create_table :taggings do |t|
402       #     t.integer :tag_id, :tagger_id, :taggable_id
403       #     t.string  :tagger_type
404       #     t.string  :taggable_type, :default => 'Photo'
405       #   end
406       #
407       # Can also be written as follows using references:
408       #
409       #   create_table :taggings do |t|
410       #     t.references :tag
411       #     t.references :tagger, :polymorphic => true
412       #     t.references :taggable, :polymorphic => { :default => 'Photo' }
413       #   end
414       def column(name, type, options = {})
415         column = self[name] || ColumnDefinition.new(@base, name, type)
416         column.limit = options[:limit] || native[type.to_sym][:limit] if options[:limit] or native[type.to_sym]
417         column.precision = options[:precision]
418         column.scale = options[:scale]
419         column.default = options[:default]
420         column.null = options[:null]
421         @columns << column unless @columns.include? column
422         self
423       end
425       %w( string text integer float decimal datetime timestamp time date binary boolean ).each do |column_type|
426         class_eval <<-EOV
427           def #{column_type}(*args)
428             options = args.extract_options!
429             column_names = args
430             
431             column_names.each { |name| column(name, '#{column_type}', options) }
432           end
433         EOV
434       end
435       
436       def timestamps
437         column(:created_at, :datetime)
438         column(:updated_at, :datetime)
439       end
441       def references(*args)
442         options = args.extract_options!
443         polymorphic = options.delete(:polymorphic)
444         args.each do |col|
445           column("#{col}_id", :integer, options)
446           unless polymorphic.nil?
447             column("#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : {})
448           end
449         end
450       end
451       alias :belongs_to :references
453       # Returns a String whose contents are the column definitions
454       # concatenated together.  This string can then be prepended and appended to
455       # to generate the final SQL to create the table.
456       def to_sql
457         @columns * ', '
458       end
460       private
461         def native
462           @base.native_database_types
463         end
464     end
465   end