odbc-rails 1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. data/AUTHORS +16 -0
  2. data/COPYING +21 -0
  3. data/ChangeLog +89 -0
  4. data/LICENSE +5 -0
  5. data/NEWS +12 -0
  6. data/README +282 -0
  7. data/lib/active_record/connection_adapters/odbc_adapter.rb +1792 -0
  8. data/lib/active_record/vendor/odbcext_db2.rb +87 -0
  9. data/lib/active_record/vendor/odbcext_informix.rb +132 -0
  10. data/lib/active_record/vendor/odbcext_informix_col.rb +45 -0
  11. data/lib/active_record/vendor/odbcext_ingres.rb +156 -0
  12. data/lib/active_record/vendor/odbcext_microsoftsqlserver.rb +185 -0
  13. data/lib/active_record/vendor/odbcext_microsoftsqlserver_col.rb +40 -0
  14. data/lib/active_record/vendor/odbcext_mysql.rb +136 -0
  15. data/lib/active_record/vendor/odbcext_oracle.rb +220 -0
  16. data/lib/active_record/vendor/odbcext_postgresql.rb +179 -0
  17. data/lib/active_record/vendor/odbcext_progress.rb +139 -0
  18. data/lib/active_record/vendor/odbcext_progress89.rb +259 -0
  19. data/lib/active_record/vendor/odbcext_sybase.rb +212 -0
  20. data/lib/active_record/vendor/odbcext_sybase_col.rb +49 -0
  21. data/lib/active_record/vendor/odbcext_virtuoso.rb +146 -0
  22. data/lib/odbc_adapter.rb +28 -0
  23. data/support/lib/active_record/connection_adapters/abstract/schema_definitions.rb +259 -0
  24. data/support/odbc_rails.diff +707 -0
  25. data/support/pack_odbc.rb +119 -0
  26. data/support/rake/rails_plugin_package_task.rb +212 -0
  27. data/support/test/base_test.rb +1349 -0
  28. data/support/test/migration_test.rb +566 -0
  29. data/test/connections/native_odbc/connection.rb +95 -0
  30. data/test/fixtures/db_definitions/db2.drop.sql +30 -0
  31. data/test/fixtures/db_definitions/db2.sql +217 -0
  32. data/test/fixtures/db_definitions/db22.drop.sql +2 -0
  33. data/test/fixtures/db_definitions/db22.sql +5 -0
  34. data/test/fixtures/db_definitions/informix.drop.sql +30 -0
  35. data/test/fixtures/db_definitions/informix.sql +205 -0
  36. data/test/fixtures/db_definitions/informix2.drop.sql +2 -0
  37. data/test/fixtures/db_definitions/informix2.sql +5 -0
  38. data/test/fixtures/db_definitions/ingres.drop.sql +62 -0
  39. data/test/fixtures/db_definitions/ingres.sql +232 -0
  40. data/test/fixtures/db_definitions/ingres2.drop.sql +2 -0
  41. data/test/fixtures/db_definitions/ingres2.sql +5 -0
  42. data/test/fixtures/db_definitions/mysql.drop.sql +30 -0
  43. data/test/fixtures/db_definitions/mysql.sql +219 -0
  44. data/test/fixtures/db_definitions/mysql2.drop.sql +2 -0
  45. data/test/fixtures/db_definitions/mysql2.sql +5 -0
  46. data/test/fixtures/db_definitions/oracle_odbc.drop.sql +64 -0
  47. data/test/fixtures/db_definitions/oracle_odbc.sql +257 -0
  48. data/test/fixtures/db_definitions/oracle_odbc2.drop.sql +2 -0
  49. data/test/fixtures/db_definitions/oracle_odbc2.sql +6 -0
  50. data/test/fixtures/db_definitions/progress.drop.sql +61 -0
  51. data/test/fixtures/db_definitions/progress.sql +234 -0
  52. data/test/fixtures/db_definitions/progress2.drop.sql +2 -0
  53. data/test/fixtures/db_definitions/progress2.sql +6 -0
  54. data/test/fixtures/db_definitions/sqlserver.drop.sql +30 -0
  55. data/test/fixtures/db_definitions/sqlserver.sql +203 -0
  56. data/test/fixtures/db_definitions/sqlserver2.drop.sql +2 -0
  57. data/test/fixtures/db_definitions/sqlserver2.sql +5 -0
  58. data/test/fixtures/db_definitions/sybase.drop.sql +31 -0
  59. data/test/fixtures/db_definitions/sybase.sql +204 -0
  60. data/test/fixtures/db_definitions/sybase2.drop.sql +4 -0
  61. data/test/fixtures/db_definitions/sybase2.sql +5 -0
  62. data/test/fixtures/db_definitions/virtuoso.drop.sql +30 -0
  63. data/test/fixtures/db_definitions/virtuoso.sql +200 -0
  64. data/test/fixtures/db_definitions/virtuoso2.drop.sql +2 -0
  65. data/test/fixtures/db_definitions/virtuoso2.sql +5 -0
  66. metadata +149 -0
@@ -0,0 +1,49 @@
1
+ #
2
+ # $Id: odbcext_sybase_col.rb,v 1.1 2006/12/06 14:42:11 source Exp $
3
+ #
4
+ # OpenLink ODBC Adapter for Ruby on Rails
5
+ # Copyright (C) 2006 OpenLink Software
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining
8
+ # a copy of this software and associated documentation files (the
9
+ # "Software"), to deal in the Software without restriction, including
10
+ # without limitation the rights to use, copy, modify, merge, publish,
11
+ # distribute, sublicense, and/or sell copies of the Software, and to
12
+ # permit persons to whom the Software is furnished to do so, subject
13
+ # to the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be
16
+ # included in all copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
22
+ # ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
23
+ # CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
+ #
26
+
27
+ module ODBCColumnExt
28
+
29
+ # Is the column a numeric autoincrementing column?
30
+ def auto_unique?
31
+ @autounique
32
+ end
33
+
34
+ private
35
+
36
+ def autoUnique?
37
+ @nativeType =~ /\bidentity\b/i
38
+ end
39
+
40
+ #private
41
+
42
+ def default_preprocess(nativeType, default)
43
+ return default if default.nil?
44
+ default.replace($2.strip) if default =~ /(DEFAULT +)(.*)/i
45
+ default.replace($1) if default =~ /^'(.*)'$/
46
+ default
47
+ end
48
+
49
+ end # module
@@ -0,0 +1,146 @@
1
+ #
2
+ # $Id: odbcext_virtuoso.rb,v 1.1 2006/12/06 14:42:11 source Exp $
3
+ #
4
+ # OpenLink ODBC Adapter for Ruby on Rails
5
+ # Copyright (C) 2006 OpenLink Software
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining
8
+ # a copy of this software and associated documentation files (the
9
+ # "Software"), to deal in the Software without restriction, including
10
+ # without limitation the rights to use, copy, modify, merge, publish,
11
+ # distribute, sublicense, and/or sell copies of the Software, and to
12
+ # permit persons to whom the Software is furnished to do so, subject
13
+ # to the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be
16
+ # included in all copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
22
+ # ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
23
+ # CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
+ #
26
+
27
+ module ODBCExt
28
+
29
+ # ------------------------------------------------------------------------
30
+ # Mandatory methods
31
+ #
32
+
33
+ # #last_insert_id must be implemented for any database which returns
34
+ # false from #prefetch_primary_key?
35
+
36
+ def last_insert_id(table, sequence_name, stmt = nil)
37
+ @logger.unknown("ODBCAdapter#last_insert_id>") if @trace
38
+ # 1049 (SQL_LASTSERIAL) is an ODBC extension for SQLGetStmtOption
39
+ stmt.get_option(1049)
40
+ end
41
+
42
+
43
+ # ------------------------------------------------------------------------
44
+ # Optional methods
45
+ #
46
+ # These are supplied for a DBMS only if necessary.
47
+ # ODBCAdapter tests for optional methods using Object#respond_to?
48
+
49
+ # Pre action for ODBCAdapter#insert
50
+ # def pre_insert(sql, name, pk, id_value, sequence_name)
51
+ # end
52
+
53
+ # Post action for ODBCAdapter#insert
54
+ # def post_insert(sql, name, pk, id_value, sequence_name)
55
+ # end
56
+
57
+ def set_sequence(table_name, pk)
58
+ begin
59
+ stmt = @connection.run("select max(#{pk}) + 1 from #{table_name}")
60
+ next_pk_val = stmt.fetch
61
+ stmt.drop
62
+ flds = table_name.split('.')
63
+ @connection.do("sequence_set('#{flds[0]}.#{flds[1]}.#{table_name}.#{pk}', #{next_pk_val}, 0)")
64
+ return true
65
+ rescue Exception => e
66
+ @logger.unknown("exception=#{e}") if @trace
67
+ end
68
+ return false
69
+ end
70
+
71
+ # ------------------------------------------------------------------------
72
+ # Method redefinitions
73
+ #
74
+ # DBMS specific methods which override the default implementation
75
+ # provided by the ODBCAdapter core.
76
+
77
+ def quote_string(s)
78
+ s.gsub(/\\/, '\&\&').gsub(/'/, "''")
79
+ end
80
+
81
+ def quoted_date(value)
82
+ @logger.unknown("ODBCAdapter#quoted_date>") if @trace
83
+ @logger.unknown("args=[#{value}]") if @trace
84
+ case value
85
+ # Ruby Time class includes a date component and so must be
86
+ # mapped to a Virtuoso DateTime type, not a Virtuoso Time type.
87
+ when Time, DateTime
88
+ %Q!stringdate('#{value.strftime("%Y-%m-%d %H:%M:%S")}')!
89
+ when Date
90
+ %Q!d('#{value.strftime("%Y-%m-%d")}')!
91
+ end
92
+ end
93
+
94
+ def rename_table(name, new_name)
95
+ @logger.unknown("ODBCAdapter#rename_table>") if @trace
96
+ @logger.unknown("args=[#{name}|#{new_name}]") if @trace
97
+ execute "ALTER TABLE #{name} RENAME #{new_name}"
98
+ rescue Exception => e
99
+ @logger.unknown("exception=#{e}") if @trace
100
+ raise ActiveRecord::ActiveRecordError, e.message
101
+ end
102
+
103
+ def change_column(table_name, column_name, type, options = {})
104
+ @logger.unknown("ODBCAdapter#change_column>") if @trace
105
+ @logger.unknown("args=[#{table_name}|#{column_name}|#{type}]") if @trace
106
+ change_column_sql = "ALTER TABLE #{table_name} MODIFY #{column_name} " +
107
+ "#{type_to_sql(type)}"
108
+
109
+ # Add any :null and :default options
110
+ add_column_options!(change_column_sql, options)
111
+ execute(change_column_sql)
112
+ rescue Exception => e
113
+ @logger.unknown("exception=#{e}") if @trace
114
+ raise ActiveRecord::ActiveRecordError, e.message
115
+ end
116
+
117
+ def change_column_default(table_name, column_name, default)
118
+ @logger.unknown("ODBCAdapter#change_column_default>") if @trace
119
+ @logger.unknown("args=[#{table_name}|#{column_name}]") if @trace
120
+ change_column(table_name, column_name, nil, {:default => default})
121
+ rescue Exception => e
122
+ @logger.unknown("exception=#{e}") if @trace
123
+ raise ActiveRecord::ActiveRecordError, e.message
124
+ end
125
+
126
+ def remove_index(table_name, options = {})
127
+ @logger.unknown("ODBCAdapter#remove_index>") if @trace
128
+ @logger.unknown("args=[#{table_name}]") if @trace
129
+ execute "DROP INDEX #{quote_column_name(index_name(table_name, options))} #{table_name}"
130
+ rescue Exception => e
131
+ @logger.unknown("exception=#{e}") if @trace
132
+ raise ActiveRecord::ActiveRecordError, e.message
133
+ end
134
+
135
+ def indexes(table_name, name = nil)
136
+ # Virtuoso creates a unique index for a table's primary key.
137
+ # Hide any such index. The index name matches the table name.
138
+ #
139
+ # If this isn't done...
140
+ # Rails' 'rake test_units' attempts to create this index explicitly,
141
+ # but Virtuoso rejects this as the index has already been created
142
+ # automatically when the table was defined.
143
+ super(table_name, name).delete_if { |i| i.unique && i.name == table_name }
144
+ end
145
+
146
+ end
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # $Id: odbc_adapter.rb,v 1.1 2006/12/06 16:16:08 source Exp $
4
+ #
5
+ # OpenLink ODBC Adapter for Ruby on Rails
6
+ # Copyright (C) 2006 OpenLink Software
7
+ #
8
+ # Permission is hereby granted, free of charge, to any person obtaining
9
+ # a copy of this software and associated documentation files (the
10
+ # "Software"), to deal in the Software without restriction, including
11
+ # without limitation the rights to use, copy, modify, merge, publish,
12
+ # distribute, sublicense, and/or sell copies of the Software, and to
13
+ # permit persons to whom the Software is furnished to do so, subject
14
+ # to the following conditions:
15
+ #
16
+ # The above copyright notice and this permission notice shall be
17
+ # included in all copies or substantial portions of the Software.
18
+ #
19
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
23
+ # ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
24
+ # CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26
+ #
27
+
28
+ require 'active_record/connection_adapters/odbc_adapter'
@@ -0,0 +1,259 @@
1
+ require 'parsedate'
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters #:nodoc:
5
+ # An abstract definition of a column in a table.
6
+ class Column
7
+ attr_reader :name, :default, :type, :limit, :null, :sql_type
8
+ attr_accessor :primary
9
+
10
+ # Instantiates a new column in the table.
11
+ #
12
+ # +name+ is the column's name, as in <tt><b>supplier_id</b> int(11)</tt>.
13
+ # +default+ is the type-casted default value, such as <tt>sales_stage varchar(20) default <b>'new'</b></tt>.
14
+ # +sql_type+ is only used to extract the column's length, if necessary. For example, <tt>company_name varchar(<b>60</b>)</tt>.
15
+ # +null+ determines if this column allows +NULL+ values.
16
+ def initialize(name, default, sql_type = nil, null = true)
17
+ @name, @type, @null = name, simplified_type(sql_type), null
18
+ @sql_type = sql_type
19
+ # have to do this one separately because type_cast depends on #type
20
+ @default = type_cast(default)
21
+ @limit = extract_limit(sql_type) unless sql_type.nil?
22
+ @primary = nil
23
+ @text = [:string, :text].include? @type
24
+ @number = [:float, :integer].include? @type
25
+ end
26
+
27
+ def text?
28
+ @text
29
+ end
30
+
31
+ def number?
32
+ @number
33
+ end
34
+
35
+ # Returns the Ruby class that corresponds to the abstract data type.
36
+ def klass
37
+ case type
38
+ when :integer then Fixnum
39
+ when :float then Float
40
+ when :datetime then Time
41
+ when :date then Date
42
+ when :timestamp then Time
43
+ when :time then Time
44
+ when :text, :string then String
45
+ when :binary then String
46
+ when :boolean then Object
47
+ end
48
+ end
49
+
50
+ # Casts value (which is a String) to an appropriate instance.
51
+ def type_cast(value)
52
+ return nil if value.nil?
53
+ case type
54
+ when :string then value
55
+ when :text then value
56
+ when :integer then value.to_i rescue value ? 1 : 0
57
+ when :float then value.to_f
58
+ when :datetime then self.class.string_to_time(value)
59
+ when :timestamp then self.class.string_to_time(value)
60
+ when :time then self.class.string_to_dummy_time(value)
61
+ when :date then self.class.string_to_date(value)
62
+ when :binary then self.class.binary_to_string(value)
63
+ when :boolean then self.class.value_to_boolean(value)
64
+ else value
65
+ end
66
+ end
67
+
68
+ def type_cast_code(var_name)
69
+ case type
70
+ when :string then nil
71
+ when :text then nil
72
+ when :integer then "(#{var_name}.to_i rescue #{var_name} ? 1 : 0)"
73
+ when :float then "#{var_name}.to_f"
74
+ when :datetime then "#{self.class.name}.string_to_time(#{var_name})"
75
+ when :timestamp then "#{self.class.name}.string_to_time(#{var_name})"
76
+ when :time then "#{self.class.name}.string_to_dummy_time(#{var_name})"
77
+ when :date then "#{self.class.name}.string_to_date(#{var_name})"
78
+ when :binary then "#{self.class.name}.binary_to_string(#{var_name})"
79
+ when :boolean then "#{self.class.name}.value_to_boolean(#{var_name})"
80
+ else nil
81
+ end
82
+ end
83
+
84
+ # Returns the human name of the column name.
85
+ #
86
+ # ===== Examples
87
+ # Column.new('sales_stage', ...).human_name #=> 'Sales stage'
88
+ def human_name
89
+ Base.human_attribute_name(@name)
90
+ end
91
+
92
+ # Used to convert from Strings to BLOBs
93
+ def self.string_to_binary(value)
94
+ value
95
+ end
96
+
97
+ # Used to convert from BLOBs to Strings
98
+ def self.binary_to_string(value)
99
+ value
100
+ end
101
+
102
+ def self.string_to_date(string)
103
+ return string unless string.is_a?(String)
104
+ date_array = ParseDate.parsedate(string)
105
+ # treat 0000-00-00 as nil
106
+ Date.new(date_array[0], date_array[1], date_array[2]) rescue nil
107
+ end
108
+
109
+ def self.string_to_time(string)
110
+ return string unless string.is_a?(String)
111
+ time_array = ParseDate.parsedate(string)[0..5]
112
+ # treat 0000-00-00 00:00:00 as nil
113
+ Time.send(Base.default_timezone, *time_array) rescue nil
114
+ end
115
+
116
+ def self.string_to_dummy_time(string)
117
+ return string unless string.is_a?(String)
118
+ time_array = ParseDate.parsedate(string)
119
+ # pad the resulting array with dummy date information
120
+ time_array[0] = 2000; time_array[1] = 1; time_array[2] = 1;
121
+ Time.send(Base.default_timezone, *time_array) rescue nil
122
+ end
123
+
124
+ # convert something to a boolean
125
+ def self.value_to_boolean(value)
126
+ return value if value==true || value==false
127
+ case value.to_s.downcase
128
+ when "true", "t", "1" then true
129
+ else false
130
+ end
131
+ end
132
+
133
+ private
134
+ def extract_limit(sql_type)
135
+ $1.to_i if sql_type =~ /\((.*)\)/
136
+ end
137
+
138
+ def simplified_type(field_type)
139
+ case field_type
140
+ when /int/i
141
+ :integer
142
+ when /float|double|decimal|numeric/i
143
+ :float
144
+ when /datetime/i
145
+ :datetime
146
+ when /timestamp/i
147
+ :timestamp
148
+ when /time/i
149
+ :time
150
+ when /date/i
151
+ :date
152
+ when /clob/i, /text/i
153
+ :text
154
+ when /blob/i, /binary/i
155
+ :binary
156
+ when /char/i, /string/i
157
+ :string
158
+ when /boolean/i
159
+ :boolean
160
+ end
161
+ end
162
+ end
163
+
164
+ class IndexDefinition < Struct.new(:table, :name, :unique, :columns) #:nodoc:
165
+ end
166
+
167
+ class ColumnDefinition < Struct.new(:base, :name, :type, :limit, :default, :null) #:nodoc:
168
+ def to_sql
169
+ column_sql = "#{base.quote_column_name(name)} #{type_to_sql(type.to_sym, limit)}"
170
+ add_column_options!(column_sql, :null => null, :default => default)
171
+ column_sql
172
+ end
173
+ alias to_s :to_sql
174
+
175
+ private
176
+ def type_to_sql(name, limit)
177
+ base.type_to_sql(name, limit) rescue name
178
+ end
179
+
180
+ def add_column_options!(sql, options)
181
+ base.add_column_options!(sql, options.merge(:column => self))
182
+ end
183
+ end
184
+
185
+ # Represents a SQL table in an abstract way.
186
+ # Columns are stored as ColumnDefinition in the #columns attribute.
187
+ class TableDefinition
188
+ attr_accessor :columns
189
+
190
+ def initialize(base)
191
+ @columns = []
192
+ @base = base
193
+ end
194
+
195
+ # Appends a primary key definition to the table definition.
196
+ # Can be called multiple times, but this is probably not a good idea.
197
+ def primary_key(name)
198
+ column(name, :primary_key)
199
+ end
200
+
201
+ # Returns a ColumnDefinition for the column with name +name+.
202
+ def [](name)
203
+ @columns.find {|column| column.name.to_s == name.to_s}
204
+ end
205
+
206
+ # Instantiates a new column for the table.
207
+ # The +type+ parameter must be one of the following values:
208
+ # <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
209
+ # <tt>:integer</tt>, <tt>:float</tt>, <tt>:datetime</tt>,
210
+ # <tt>:timestamp</tt>, <tt>:time</tt>, <tt>:date</tt>,
211
+ # <tt>:binary</tt>, <tt>:boolean</tt>.
212
+ #
213
+ # Available options are (none of these exists by default):
214
+ # * <tt>:limit</tt>:
215
+ # Requests a maximum column length (<tt>:string</tt>, <tt>:text</tt>,
216
+ # <tt>:binary</tt> or <tt>:integer</tt> columns only)
217
+ # * <tt>:default</tt>:
218
+ # The column's default value. You cannot explicitely set the default
219
+ # value to +NULL+. Simply leave off this option if you want a +NULL+
220
+ # default value.
221
+ # * <tt>:null</tt>:
222
+ # Allows or disallows +NULL+ values in the column. This option could
223
+ # have been named <tt>:null_allowed</tt>.
224
+ #
225
+ # This method returns <tt>self</tt>.
226
+ #
227
+ # ===== Examples
228
+ # # Assuming def is an instance of TableDefinition
229
+ # def.column(:granted, :boolean)
230
+ # #=> granted BOOLEAN
231
+ #
232
+ # def.column(:picture, :binary, :limit => 2.megabytes)
233
+ # #=> picture BLOB(2097152)
234
+ #
235
+ # def.column(:sales_stage, :string, :limit => 20, :default => 'new', :null => false)
236
+ # #=> sales_stage VARCHAR(20) DEFAULT 'new' NOT NULL
237
+ def column(name, type, options = {})
238
+ column = self[name] || ColumnDefinition.new(@base, name, type)
239
+ column.limit = options[:limit] || native[type.to_sym][:limit] if options[:limit] or native[type.to_sym]
240
+ column.default = options[:default]
241
+ column.null = options[:null]
242
+ @columns << column unless @columns.include? column
243
+ self
244
+ end
245
+
246
+ # Returns a String whose contents are the column definitions
247
+ # concatenated together. This string can then be pre and appended to
248
+ # to generate the final SQL to create the table.
249
+ def to_sql
250
+ @columns * ', '
251
+ end
252
+
253
+ private
254
+ def native
255
+ @base.native_database_types
256
+ end
257
+ end
258
+ end
259
+ end