activerecord 1.11.1 → 1.12.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (102) hide show
  1. data/CHANGELOG +198 -0
  2. data/lib/active_record.rb +19 -14
  3. data/lib/active_record/acts/list.rb +8 -6
  4. data/lib/active_record/acts/tree.rb +33 -10
  5. data/lib/active_record/aggregations.rb +1 -7
  6. data/lib/active_record/associations.rb +151 -82
  7. data/lib/active_record/associations/association_collection.rb +25 -0
  8. data/lib/active_record/associations/association_proxy.rb +9 -8
  9. data/lib/active_record/associations/belongs_to_association.rb +19 -5
  10. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +44 -69
  11. data/lib/active_record/associations/has_many_association.rb +6 -14
  12. data/lib/active_record/associations/has_one_association.rb +5 -3
  13. data/lib/active_record/base.rb +344 -130
  14. data/lib/active_record/callbacks.rb +2 -2
  15. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +128 -0
  16. data/lib/active_record/connection_adapters/abstract/database_statements.rb +104 -0
  17. data/lib/active_record/connection_adapters/abstract/quoting.rb +51 -0
  18. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +249 -0
  19. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +245 -0
  20. data/lib/active_record/connection_adapters/abstract_adapter.rb +29 -464
  21. data/lib/active_record/connection_adapters/db2_adapter.rb +40 -10
  22. data/lib/active_record/connection_adapters/mysql_adapter.rb +131 -60
  23. data/lib/active_record/connection_adapters/oci_adapter.rb +106 -26
  24. data/lib/active_record/connection_adapters/postgresql_adapter.rb +211 -62
  25. data/lib/active_record/connection_adapters/sqlite_adapter.rb +193 -44
  26. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +24 -15
  27. data/lib/active_record/fixtures.rb +47 -24
  28. data/lib/active_record/migration.rb +34 -5
  29. data/lib/active_record/observer.rb +32 -2
  30. data/lib/active_record/query_cache.rb +12 -11
  31. data/lib/active_record/schema.rb +58 -0
  32. data/lib/active_record/schema_dumper.rb +84 -0
  33. data/lib/active_record/transactions.rb +1 -3
  34. data/lib/active_record/validations.rb +40 -26
  35. data/lib/active_record/vendor/mysql.rb +6 -0
  36. data/lib/active_record/version.rb +9 -0
  37. data/rakefile +5 -16
  38. data/test/abstract_unit.rb +6 -11
  39. data/test/adapter_test.rb +58 -0
  40. data/test/ar_schema_test.rb +33 -0
  41. data/test/association_callbacks_test.rb +14 -0
  42. data/test/associations_go_eager_test.rb +56 -14
  43. data/test/associations_test.rb +245 -25
  44. data/test/base_test.rb +205 -34
  45. data/test/binary_test.rb +25 -42
  46. data/test/callbacks_test.rb +75 -0
  47. data/test/conditions_scoping_test.rb +136 -0
  48. data/test/connections/native_mysql/connection.rb +0 -4
  49. data/test/connections/native_sqlite3/in_memory_connection.rb +17 -0
  50. data/test/copy_table_sqlite.rb +64 -0
  51. data/test/deprecated_associations_test.rb +7 -6
  52. data/test/deprecated_finder_test.rb +3 -3
  53. data/test/finder_test.rb +33 -3
  54. data/test/fixtures/accounts.yml +5 -0
  55. data/test/fixtures/categories_ordered.yml +7 -0
  56. data/test/fixtures/category.rb +11 -1
  57. data/test/fixtures/comment.rb +22 -2
  58. data/test/fixtures/comments.yml +6 -0
  59. data/test/fixtures/companies.yml +15 -0
  60. data/test/fixtures/company.rb +24 -1
  61. data/test/fixtures/db_definitions/db2.drop.sql +5 -1
  62. data/test/fixtures/db_definitions/db2.sql +15 -1
  63. data/test/fixtures/db_definitions/mysql.drop.sql +2 -0
  64. data/test/fixtures/db_definitions/mysql.sql +17 -2
  65. data/test/fixtures/db_definitions/oci.drop.sql +37 -5
  66. data/test/fixtures/db_definitions/oci.sql +47 -4
  67. data/test/fixtures/db_definitions/oci2.drop.sql +1 -1
  68. data/test/fixtures/db_definitions/oci2.sql +2 -2
  69. data/test/fixtures/db_definitions/postgresql.drop.sql +4 -0
  70. data/test/fixtures/db_definitions/postgresql.sql +33 -4
  71. data/test/fixtures/db_definitions/sqlite.drop.sql +2 -0
  72. data/test/fixtures/db_definitions/sqlite.sql +16 -2
  73. data/test/fixtures/db_definitions/sqlserver.drop.sql +2 -0
  74. data/test/fixtures/db_definitions/sqlserver.sql +16 -2
  75. data/test/fixtures/developer.rb +1 -1
  76. data/test/fixtures/flowers.jpg +0 -0
  77. data/test/fixtures/keyboard.rb +3 -0
  78. data/test/fixtures/mixins.yml +11 -1
  79. data/test/fixtures/order.rb +4 -0
  80. data/test/fixtures/post.rb +4 -0
  81. data/test/fixtures/posts.yml +7 -0
  82. data/test/fixtures/project.rb +1 -0
  83. data/test/fixtures/subject.rb +4 -0
  84. data/test/fixtures/subscriber.rb +2 -4
  85. data/test/fixtures/topics.yml +2 -2
  86. data/test/fixtures_test.rb +79 -7
  87. data/test/inheritance_test.rb +2 -2
  88. data/test/lifecycle_test.rb +14 -6
  89. data/test/migration_test.rb +164 -6
  90. data/test/mixin_test.rb +78 -2
  91. data/test/pk_test.rb +25 -1
  92. data/test/readonly_test.rb +31 -0
  93. data/test/reflection_test.rb +4 -1
  94. data/test/schema_dumper_test.rb +19 -0
  95. data/test/schema_test_postgresql.rb +3 -2
  96. data/test/synonym_test_oci.rb +17 -0
  97. data/test/threaded_connections_test.rb +2 -1
  98. data/test/transactions_test.rb +109 -10
  99. data/test/validations_test.rb +70 -42
  100. metadata +25 -5
  101. data/test/fixtures/associations.png +0 -0
  102. data/test/thread_safety_test.rb +0 -36
@@ -0,0 +1,245 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters # :nodoc:
3
+ module SchemaStatements
4
+ # Returns a Hash of mappings from the abstract data types to the native
5
+ # database types. See TableDefinition#column for details on the recognized
6
+ # abstract data types.
7
+ def native_database_types
8
+ {}
9
+ end
10
+
11
+ # def tables(name = nil) end
12
+
13
+ # Returns an array of indexes for the given table.
14
+ # def indexes(table_name, name = nil) end
15
+
16
+ # Returns an array of Column objects for the table specified by +table_name+.
17
+ # See the concrete implementation for details on the expected parameter values.
18
+ def columns(table_name, name = nil) end
19
+
20
+ # Creates a new table
21
+ # There are two ways to work with #create_table. You can use the block
22
+ # form or the regular form, like this:
23
+ #
24
+ # === Block form
25
+ # # create_table() yields a TableDefinition instance
26
+ # create_table(:suppliers) do |t|
27
+ # t.column :name, :string, :limit => 60
28
+ # # Other fields here
29
+ # end
30
+ #
31
+ # === Regular form
32
+ # create_table(:suppliers)
33
+ # add_column(:suppliers, :name, :string, {:limit => 60})
34
+ #
35
+ # The +options+ hash can include the following keys:
36
+ # [<tt>:id</tt>]
37
+ # Set to true or false to add/not add a primary key column
38
+ # automatically. Defaults to true.
39
+ # [<tt>:primary_key</tt>]
40
+ # The name of the primary key, if one is to be added automatically.
41
+ # Defaults to +id+.
42
+ # [<tt>:options</tt>]
43
+ # Any extra options you want appended to the table definition.
44
+ # [<tt>:temporary</tt>]
45
+ # Make a temporary table.
46
+ #
47
+ # ===== Examples
48
+ # ====== Add a backend specific option to the generated SQL (MySQL)
49
+ # create_table(:suppliers, :options => 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
50
+ # generates:
51
+ # CREATE TABLE suppliers (
52
+ # id int(11) DEFAULT NULL auto_increment PRIMARY KEY
53
+ # ) ENGINE=InnoDB DEFAULT CHARSET=utf8
54
+ #
55
+ # ====== Rename the primary key column
56
+ # create_table(:objects, :primary_key => 'guid') do |t|
57
+ # t.column :name, :string, :limit => 80
58
+ # end
59
+ # generates:
60
+ # CREATE TABLE objects (
61
+ # guid int(11) DEFAULT NULL auto_increment PRIMARY KEY,
62
+ # name varchar(80)
63
+ # )
64
+ #
65
+ # ====== Do not add a primary key column
66
+ # create_table(:categories_suppliers, :id => false) do |t|
67
+ # t.column :category_id, :integer
68
+ # t.column :supplier_id, :integer
69
+ # end
70
+ # generates:
71
+ # CREATE TABLE categories_suppliers_join (
72
+ # category_id int,
73
+ # supplier_id int
74
+ # )
75
+ #
76
+ # See also TableDefinition#column for details on how to create columns.
77
+ def create_table(name, options = {})
78
+ table_definition = TableDefinition.new(self)
79
+ table_definition.primary_key(options[:primary_key] || "id") unless options[:id] == false
80
+
81
+ yield table_definition
82
+
83
+ if options[:force]
84
+ drop_table(name) rescue nil
85
+ end
86
+
87
+ create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE "
88
+ create_sql << "#{name} ("
89
+ create_sql << table_definition.to_sql
90
+ create_sql << ") #{options[:options]}"
91
+ execute create_sql
92
+ end
93
+
94
+ # Renames a table.
95
+ # ===== Example
96
+ # rename_table('octopuses', 'octopi')
97
+ def rename_table(name, new_name)
98
+ raise NotImplementedError, "rename_table is not implemented"
99
+ end
100
+
101
+ # Drops a table from the database.
102
+ def drop_table(name)
103
+ execute "DROP TABLE #{name}"
104
+ end
105
+
106
+ # Adds a new column to the named table.
107
+ # See TableDefinition#column for details of the options you can use.
108
+ def add_column(table_name, column_name, type, options = {})
109
+ add_column_sql = "ALTER TABLE #{table_name} ADD #{column_name} #{type_to_sql(type, options[:limit])}"
110
+ add_column_options!(add_column_sql, options)
111
+ execute(add_column_sql)
112
+ end
113
+
114
+ # Removes the column from the table definition.
115
+ # ===== Examples
116
+ # remove_column(:suppliers, :qualification)
117
+ def remove_column(table_name, column_name)
118
+ execute "ALTER TABLE #{table_name} DROP #{column_name}"
119
+ end
120
+
121
+ # Changes the column's definition according to the new options.
122
+ # See TableDefinition#column for details of the options you can use.
123
+ # ===== Examples
124
+ # change_column(:suppliers, :name, :string, :limit => 80)
125
+ # change_column(:accounts, :description, :text)
126
+ def change_column(table_name, column_name, type, options = {})
127
+ raise NotImplementedError, "change_column is not implemented"
128
+ end
129
+
130
+ # Sets a new default value for a column. If you want to set the default
131
+ # value to +NULL+, you are out of luck. You need to
132
+ # DatabaseStatements#execute the apppropriate SQL statement yourself.
133
+ # ===== Examples
134
+ # change_column_default(:suppliers, :qualification, 'new')
135
+ # change_column_default(:accounts, :authorized, 1)
136
+ def change_column_default(table_name, column_name, default)
137
+ raise NotImplementedError, "change_column_default is not implemented"
138
+ end
139
+
140
+ # Renames a column.
141
+ # ===== Example
142
+ # rename_column(:suppliers, :description, :name)
143
+ def rename_column(table_name, column_name, new_column_name)
144
+ raise NotImplementedError, "rename_column is not implemented"
145
+ end
146
+
147
+ # Adds a new index to the table. +column_name+ can be a single Symbol, or
148
+ # an Array of Symbols.
149
+ #
150
+ # The index will be named after the table and the first column names,
151
+ # unless you pass +:name+ as an option.
152
+ #
153
+ # ===== Examples
154
+ # ====== Creating a simple index
155
+ # add_index(:suppliers, :name)
156
+ # generates
157
+ # CREATE INDEX suppliers_name_index ON suppliers(name)
158
+ # ====== Creating a unique index
159
+ # add_index(:accounts, [:branch_id, :party_id], :unique => true)
160
+ # generates
161
+ # CREATE UNIQUE INDEX accounts_branch_id_index ON accounts(branch_id, party_id)
162
+ # ====== Creating a named index
163
+ # add_index(:accounts, [:branch_id, :party_id], :unique => true, :name => 'by_branch_party')
164
+ # generates
165
+ # CREATE UNIQUE INDEX by_branch_party ON accounts(branch_id, party_id)
166
+ def add_index(table_name, column_name, options = {})
167
+ index_name = "#{table_name}_#{Array(column_name).first}_index"
168
+
169
+ if Hash === options # legacy support, since this param was a string
170
+ index_type = options[:unique] ? "UNIQUE" : ""
171
+ index_name = options[:name] || index_name
172
+ else
173
+ index_type = options
174
+ end
175
+
176
+ execute "CREATE #{index_type} INDEX #{index_name} ON #{table_name} (#{Array(column_name).join(", ")})"
177
+ end
178
+
179
+ # Remove the given index from the table.
180
+ #
181
+ # Remove the suppliers_name_index in the suppliers table (legacy support, use the second or third forms).
182
+ # remove_index :suppliers, :name
183
+ # Remove the index named accounts_branch_id in the accounts table.
184
+ # remove_index :accounts, :column => :branch_id
185
+ # Remove the index named by_branch_party in the accounts table.
186
+ # remove_index :accounts, :name => :by_branch_party
187
+ def remove_index(table_name, options = {})
188
+ if Hash === options # legacy support
189
+ if options[:column]
190
+ index_name = "#{table_name}_#{options[:column]}_index"
191
+ elsif options[:name]
192
+ index_name = options[:name]
193
+ else
194
+ raise ArgumentError, "You must specify the index name"
195
+ end
196
+ else
197
+ index_name = "#{table_name}_#{options}_index"
198
+ end
199
+
200
+ execute "DROP INDEX #{index_name} ON #{table_name}"
201
+ end
202
+
203
+
204
+ # Returns a string of <tt>CREATE TABLE</tt> SQL statement(s) for recreating the
205
+ # entire structure of the database.
206
+ def structure_dump
207
+ end
208
+
209
+ # Should not be called normally, but this operation is non-destructive.
210
+ # The migrations module handles this automatically.
211
+ def initialize_schema_information
212
+ begin
213
+ execute "CREATE TABLE #{ActiveRecord::Migrator.schema_info_table_name} (version #{type_to_sql(:integer)})"
214
+ execute "INSERT INTO #{ActiveRecord::Migrator.schema_info_table_name} (version) VALUES(0)"
215
+ rescue ActiveRecord::StatementInvalid
216
+ # Schema has been intialized
217
+ end
218
+ end
219
+
220
+ def dump_schema_information #:nodoc:
221
+ begin
222
+ if (current_schema = ActiveRecord::Migrator.current_version) > 0
223
+ return "INSERT INTO #{ActiveRecord::Migrator.schema_info_table_name} (version) VALUES (#{current_schema});"
224
+ end
225
+ rescue ActiveRecord::StatementInvalid
226
+ # No Schema Info
227
+ end
228
+ end
229
+
230
+
231
+ def type_to_sql(type, limit = nil) #:nodoc:
232
+ native = native_database_types[type]
233
+ limit ||= native[:limit]
234
+ column_type_sql = native[:name]
235
+ column_type_sql << "(#{limit})" if limit
236
+ column_type_sql
237
+ end
238
+
239
+ def add_column_options!(sql, options) #:nodoc:
240
+ sql << " NOT NULL" if options[:null] == false
241
+ sql << " DEFAULT #{quote(options[:default], options[:column])}" unless options[:default].nil?
242
+ end
243
+ end
244
+ end
245
+ end
@@ -1,444 +1,49 @@
1
1
  require 'benchmark'
2
2
  require 'date'
3
3
 
4
- # Method that requires a library, ensuring that rubygems is loaded
5
- # This is used in the database adaptors to require DB drivers. Reasons:
6
- # (1) database drivers are the only third-party library that Rails depend upon
7
- # (2) they are often installed as gems
8
- def require_library_or_gem(library_name)
9
- begin
10
- require library_name
11
- rescue LoadError => cannot_require
12
- # 1. Requiring the module is unsuccessful, maybe it's a gem and nobody required rubygems yet. Try.
13
- begin
14
- require 'rubygems'
15
- rescue LoadError => rubygems_not_installed
16
- raise cannot_require
17
- end
18
- # 2. Rubygems is installed and loaded. Try to load the library again
19
- begin
20
- require library_name
21
- rescue LoadError => gem_not_installed
22
- raise cannot_require
23
- end
24
- end
25
- end
4
+ require 'active_record/connection_adapters/abstract/schema_definitions'
5
+ require 'active_record/connection_adapters/abstract/schema_statements'
6
+ require 'active_record/connection_adapters/abstract/database_statements'
7
+ require 'active_record/connection_adapters/abstract/quoting'
8
+ require 'active_record/connection_adapters/abstract/connection_specification'
26
9
 
27
10
  module ActiveRecord
28
- class Base
29
- class ConnectionSpecification #:nodoc:
30
- attr_reader :config, :adapter_method
31
- def initialize (config, adapter_method)
32
- @config, @adapter_method = config, adapter_method
33
- end
34
- end
35
-
36
- # The class -> [adapter_method, config] map
37
- @@defined_connections = {}
38
-
39
- # Establishes the connection to the database. Accepts a hash as input where
40
- # the :adapter key must be specified with the name of a database adapter (in lower-case)
41
- # example for regular databases (MySQL, Postgresql, etc):
42
- #
43
- # ActiveRecord::Base.establish_connection(
44
- # :adapter => "mysql",
45
- # :host => "localhost",
46
- # :username => "myuser",
47
- # :password => "mypass",
48
- # :database => "somedatabase"
49
- # )
50
- #
51
- # Example for SQLite database:
52
- #
53
- # ActiveRecord::Base.establish_connection(
54
- # :adapter => "sqlite",
55
- # :dbfile => "path/to/dbfile"
56
- # )
57
- #
58
- # Also accepts keys as strings (for parsing from yaml for example):
59
- # ActiveRecord::Base.establish_connection(
60
- # "adapter" => "sqlite",
61
- # "dbfile" => "path/to/dbfile"
62
- # )
63
- #
64
- # The exceptions AdapterNotSpecified, AdapterNotFound and ArgumentError
65
- # may be returned on an error.
66
- def self.establish_connection(spec = nil)
67
- case spec
68
- when nil
69
- raise AdapterNotSpecified unless defined? RAILS_ENV
70
- establish_connection(RAILS_ENV)
71
- when ConnectionSpecification
72
- @@defined_connections[self] = spec
73
- when Symbol, String
74
- if configuration = configurations[spec.to_s]
75
- establish_connection(configuration)
76
- else
77
- raise AdapterNotSpecified, "#{spec} database is not configured"
78
- end
79
- else
80
- spec = spec.symbolize_keys
81
- unless spec.key?(:adapter) then raise AdapterNotSpecified, "database configuration does not specify adapter" end
82
- adapter_method = "#{spec[:adapter]}_connection"
83
- unless respond_to?(adapter_method) then raise AdapterNotFound, "database configuration specifies nonexistent #{spec[:adapter]} adapter" end
84
- remove_connection
85
- establish_connection(ConnectionSpecification.new(spec, adapter_method))
86
- end
87
- end
88
-
89
- def self.active_connections #:nodoc:
90
- if threaded_connections
91
- Thread.current['active_connections'] ||= {}
92
- else
93
- @@active_connections ||= {}
94
- end
95
- end
96
-
97
- # Locate the connection of the nearest super class. This can be an
98
- # active or defined connections: if it is the latter, it will be
99
- # opened and set as the active connection for the class it was defined
100
- # for (not necessarily the current class).
101
- def self.retrieve_connection #:nodoc:
102
- klass = self
103
- ar_super = ActiveRecord::Base.superclass
104
- until klass == ar_super
105
- if conn = active_connections[klass]
106
- return conn
107
- elsif conn = @@defined_connections[klass]
108
- klass.connection = conn
109
- return self.connection
110
- end
111
- klass = klass.superclass
112
- end
113
- raise ConnectionNotEstablished
114
- end
115
-
116
- # Returns true if a connection that's accessible to this class have already been opened.
117
- def self.connected?
118
- klass = self
119
- until klass == ActiveRecord::Base.superclass
120
- if active_connections[klass]
121
- return true
122
- else
123
- klass = klass.superclass
124
- end
125
- end
126
- return false
127
- end
128
-
129
- # Remove the connection for this class. This will close the active
130
- # connection and the defined connection (if they exist). The result
131
- # can be used as argument for establish_connection, for easy
132
- # re-establishing of the connection.
133
- def self.remove_connection(klass=self)
134
- conn = @@defined_connections[klass]
135
- @@defined_connections.delete(klass)
136
- active_connections[klass] = nil
137
- conn.config if conn
138
- end
139
-
140
- # Set the connection for the class.
141
- def self.connection=(spec)
142
- raise ConnectionNotEstablished unless spec
143
- conn = self.send(spec.adapter_method, spec.config)
144
- active_connections[self] = conn
145
- end
146
-
147
- # Converts all strings in a hash to symbols.
148
- def self.symbolize_strings_in_hash(hash) #:nodoc:
149
- hash.symbolize_keys
150
- end
151
- end
152
-
153
11
  module ConnectionAdapters # :nodoc:
154
- class Column # :nodoc:
155
- attr_reader :name, :default, :type, :limit
156
- # The name should contain the name of the column, such as "name" in "name varchar(250)"
157
- # The default should contain the type-casted default of the column, such as 1 in "count int(11) DEFAULT 1"
158
- # The type parameter should either contain :integer, :float, :datetime, :date, :text, or :string
159
- # The sql_type is just used for extracting the limit, such as 10 in "varchar(10)"
160
- def initialize(name, default, sql_type = nil)
161
- @name, @default, @type = name, type_cast(default), simplified_type(sql_type)
162
- @limit = extract_limit(sql_type) unless sql_type.nil?
163
- end
164
-
165
- def klass
166
- case type
167
- when :integer then Fixnum
168
- when :float then Float
169
- when :datetime then Time
170
- when :date then Date
171
- when :timestamp then Time
172
- when :time then Time
173
- when :text, :string then String
174
- when :binary then String
175
- when :boolean then Object
176
- end
177
- end
178
-
179
- def type_cast(value)
180
- if value.nil? then return nil end
181
- case type
182
- when :string then value
183
- when :text then value
184
- when :integer then value.to_i rescue value ? 1 : 0
185
- when :float then value.to_f
186
- when :datetime then string_to_time(value)
187
- when :timestamp then string_to_time(value)
188
- when :time then string_to_dummy_time(value)
189
- when :date then string_to_date(value)
190
- when :binary then binary_to_string(value)
191
- when :boolean then value == true or (value =~ /^t(rue)?$/i) == 0 or value.to_s == '1'
192
- else value
193
- end
194
- end
195
-
196
- def human_name
197
- Base.human_attribute_name(@name)
198
- end
199
-
200
- def string_to_binary(value)
201
- value
202
- end
203
-
204
- def binary_to_string(value)
205
- value
206
- end
207
-
208
- private
209
- def string_to_date(string)
210
- return string unless string.is_a?(String)
211
- date_array = ParseDate.parsedate(string.to_s)
212
- # treat 0000-00-00 as nil
213
- Date.new(date_array[0], date_array[1], date_array[2]) rescue nil
214
- end
215
-
216
- def string_to_time(string)
217
- return string unless string.is_a?(String)
218
- time_array = ParseDate.parsedate(string.to_s).compact
219
- # treat 0000-00-00 00:00:00 as nil
220
- Time.send(Base.default_timezone, *time_array) rescue nil
221
- end
222
-
223
- def string_to_dummy_time(string)
224
- return string unless string.is_a?(String)
225
- time_array = ParseDate.parsedate(string.to_s)
226
- # pad the resulting array with dummy date information
227
- time_array[0] = 2000; time_array[1] = 1; time_array[2] = 1;
228
- Time.send(Base.default_timezone, *time_array) rescue nil
229
- end
230
-
231
- def extract_limit(sql_type)
232
- $1.to_i if sql_type =~ /\((.*)\)/
233
- end
234
-
235
- def simplified_type(field_type)
236
- case field_type
237
- when /int/i
238
- :integer
239
- when /float|double|decimal|numeric/i
240
- :float
241
- when /datetime/i
242
- :datetime
243
- when /timestamp/i
244
- :timestamp
245
- when /time/i
246
- :time
247
- when /date/i
248
- :date
249
- when /clob/i, /text/i
250
- :text
251
- when /blob/i, /binary/i
252
- :binary
253
- when /char/i, /string/i
254
- :string
255
- when /boolean/i
256
- :boolean
257
- end
258
- end
259
- end
260
-
261
12
  # All the concrete database adapters follow the interface laid down in this class.
262
13
  # You can use this interface directly by borrowing the database connection from the Base with
263
14
  # Base.connection.
15
+ #
16
+ # Most of the methods in the adapter are useful during migrations. Most
17
+ # notably, SchemaStatements#create_table, SchemaStatements#drop_table,
18
+ # SchemaStatements#add_index, SchemaStatements#remove_index,
19
+ # SchemaStatements#add_column, SchemaStatements#change_column and
20
+ # SchemaStatements#remove_column are very useful.
264
21
  class AbstractAdapter
22
+ include Quoting, DatabaseStatements, SchemaStatements
265
23
  @@row_even = true
266
24
 
267
- def initialize(connection, logger = nil) # :nodoc:
25
+ def initialize(connection, logger = nil) #:nodoc:
268
26
  @connection, @logger = connection, logger
269
27
  @runtime = 0
270
28
  end
271
29
 
272
- # Returns an array of record hashes with the column names as a keys and fields as values.
273
- def select_all(sql, name = nil) end
274
-
275
- # Returns a record hash with the column names as a keys and fields as values.
276
- def select_one(sql, name = nil) end
277
-
278
- # Returns an array of column objects for the table specified by +table_name+.
279
- def columns(table_name, name = nil) end
280
-
281
- # Returns the last auto-generated ID from the affected table.
282
- def insert(sql, name = nil, pk = nil, id_value = nil) end
283
-
284
- # Executes the update statement and returns the number of rows affected.
285
- def update(sql, name = nil) end
286
-
287
- # Executes the delete statement and returns the number of rows affected.
288
- def delete(sql, name = nil) end
289
-
290
- def reset_runtime # :nodoc:
291
- rt = @runtime
292
- @runtime = 0
293
- return rt
294
- end
295
-
296
- # Wrap a block in a transaction. Returns result of block.
297
- def transaction(start_db_transaction = true)
298
- begin
299
- if block_given?
300
- begin_db_transaction if start_db_transaction
301
- result = yield
302
- commit_db_transaction if start_db_transaction
303
- result
304
- end
305
- rescue Exception => database_transaction_rollback
306
- rollback_db_transaction if start_db_transaction
307
- raise
308
- end
309
- end
310
-
311
- # Begins the transaction (and turns off auto-committing).
312
- def begin_db_transaction() end
313
-
314
- # Commits the transaction (and turns on auto-committing).
315
- def commit_db_transaction() end
316
-
317
- # Rolls back the transaction (and turns on auto-committing). Must be done if the transaction block
318
- # raises an exception or returns false.
319
- def rollback_db_transaction() end
320
-
321
- def quote(value, column = nil)
322
- case value
323
- when String
324
- if column && column.type == :binary
325
- "'#{quote_string(column.string_to_binary(value))}'" # ' (for ruby-mode)
326
- else
327
- "'#{quote_string(value)}'" # ' (for ruby-mode)
328
- end
329
- when NilClass then "NULL"
330
- when TrueClass then (column && column.type == :boolean ? "'t'" : "1")
331
- when FalseClass then (column && column.type == :boolean ? "'f'" : "0")
332
- when Float, Fixnum, Bignum then value.to_s
333
- when Date then "'#{value.to_s}'"
334
- when Time, DateTime then "'#{value.strftime("%Y-%m-%d %H:%M:%S")}'"
335
- else "'#{quote_string(value.to_yaml)}'"
336
- end
337
- end
338
-
339
- def quote_string(s)
340
- s.gsub(/\\/, '\&\&').gsub(/'/, "''") # ' (for ruby-mode)
341
- end
342
-
343
- def quote_column_name(name)
344
- name
345
- end
346
-
347
- # Returns the human-readable name of the adapter. Use mixed case - one can always use downcase if needed.
348
- def adapter_name()
30
+ # Returns the human-readable name of the adapter. Use mixed case - one
31
+ # can always use downcase if needed.
32
+ def adapter_name
349
33
  'Abstract'
350
34
  end
351
-
352
- # Returns a string of the CREATE TABLE SQL statements for recreating the entire structure of the database.
353
- def structure_dump() end
354
-
355
- def add_limit!(sql, options)
356
- return unless options
357
- add_limit_offset!(sql, options)
358
- end
359
-
360
- def add_limit_offset!(sql, options)
361
- return if options[:limit].nil?
362
- sql << " LIMIT #{options[:limit]}"
363
- sql << " OFFSET #{options[:offset]}" if options.has_key?(:offset) and !options[:offset].nil?
364
- end
365
-
366
-
367
- def initialize_schema_information
368
- begin
369
- execute "CREATE TABLE schema_info (version #{type_to_sql(:integer)})"
370
- execute "INSERT INTO schema_info (version) VALUES(0)"
371
- rescue ActiveRecord::StatementInvalid
372
- # Schema has been intialized
373
- end
374
- end
375
-
376
- def create_table(name, options = {})
377
- table_definition = TableDefinition.new(self)
378
- table_definition.primary_key(options[:primary_key] || "id") unless options[:id] == false
379
-
380
- yield table_definition
381
- create_sql = "CREATE TABLE #{name} ("
382
- create_sql << table_definition.to_sql
383
- create_sql << ") #{options[:options]}"
384
-
385
- execute create_sql
386
- end
387
-
388
- def drop_table(name)
389
- execute "DROP TABLE #{name}"
390
- end
391
-
392
- def add_column(table_name, column_name, type, options = {})
393
- native_type = native_database_types[type]
394
- add_column_sql = "ALTER TABLE #{table_name} ADD #{column_name} #{type_to_sql(type, options[:limit])}"
395
- add_column_options!(add_column_sql, options)
396
- execute(add_column_sql)
397
- end
398
-
399
- def remove_column(table_name, column_name)
400
- execute "ALTER TABLE #{table_name} DROP #{column_name}"
401
- end
402
-
403
- def change_column(table_name, column_name, type, options = {})
404
- raise NotImplementedError, "change_column is not implemented"
405
- end
406
-
407
- def change_column_default(table_name, column_name, default)
408
- raise NotImplementedError, "change_column_default is not implemented"
409
- end
410
35
 
36
+ # Does this adapter support migrations ? Backend specific, as the
37
+ # abstract adapter always returns +false+.
411
38
  def supports_migrations?
412
39
  false
413
- end
414
-
415
- def rename_column(table_name, column_name, new_column_name)
416
- raise NotImplementedError, "rename_column is not implemented"
417
- end
418
-
419
- def add_index(table_name, column_name, index_type = '')
420
- execute "CREATE #{index_type} INDEX #{table_name}_#{column_name.to_a.first}_index ON #{table_name} (#{column_name.to_a.join(", ")})"
421
40
  end
422
41
 
423
- def remove_index(table_name, column_name)
424
- execute "DROP INDEX #{table_name}_#{column_name}_index ON #{table_name}"
42
+ def reset_runtime #:nodoc:
43
+ rt = @runtime
44
+ @runtime = 0
45
+ return rt
425
46
  end
426
-
427
- def supports_migrations?
428
- false
429
- end
430
-
431
- def native_database_types
432
- {}
433
- end
434
-
435
- def type_to_sql(type, limit = nil)
436
- native = native_database_types[type]
437
- limit ||= native[:limit]
438
- column_type_sql = native[:name]
439
- column_type_sql << "(#{limit})" if limit
440
- column_type_sql
441
- end
442
47
 
443
48
  protected
444
49
  def log(sql, name)
@@ -476,61 +81,21 @@ module ActiveRecord
476
81
 
477
82
  def format_log_entry(message, dump = nil)
478
83
  if ActiveRecord::Base.colorize_logging
479
- if @@row_even then
480
- @@row_even = false; caller_color = "1;32"; message_color = "4;33"; dump_color = "1;37"
84
+ if @@row_even
85
+ @@row_even = false
86
+ message_color, dump_color = "4;36;1", "0;1"
481
87
  else
482
- @@row_even = true; caller_color = "1;36"; message_color = "4;35"; dump_color = "0;37"
88
+ @@row_even = true
89
+ message_color, dump_color = "4;35;1", "0"
483
90
  end
484
91
 
485
- log_entry = " \e[#{message_color}m#{message}\e[m"
486
- log_entry << " \e[#{dump_color}m%s\e[m" % dump if dump.kind_of?(String) && !dump.nil?
487
- log_entry << " \e[#{dump_color}m%p\e[m" % dump if !dump.kind_of?(String) && !dump.nil?
92
+ log_entry = " \e[#{message_color}m#{message}\e[0m "
93
+ log_entry << "\e[#{dump_color}m%#{String === dump ? 's' : 'p'}\e[0m" % dump if dump
488
94
  log_entry
489
95
  else
490
96
  "%s %s" % [message, dump]
491
97
  end
492
98
  end
493
-
494
- def add_column_options!(sql, options)
495
- sql << " DEFAULT '#{options[:default]}'" unless options[:default].nil?
496
- end
497
- end
498
-
499
- class TableDefinition
500
- attr_accessor :columns
501
-
502
- def initialize(base)
503
- @columns = []
504
- @base = base
505
- end
506
-
507
- def primary_key(name)
508
- @columns << "#{name} #{native[:primary_key]}"
509
- self
510
- end
511
-
512
- def column(name, type, options = {})
513
- limit = options[:limit] || native[type.to_sym][:limit]
514
-
515
- column_sql = "#{name} #{type_to_sql(type.to_sym, options[:limit])}"
516
- column_sql << " DEFAULT '#{options[:default]}'" if options[:default]
517
- @columns << column_sql
518
- self
519
- end
520
-
521
- def to_sql
522
- @columns.join(", ")
523
- end
524
-
525
- private
526
-
527
- def type_to_sql(name, limit)
528
- @base.type_to_sql(name, limit)
529
- end
530
-
531
- def native
532
- @base.native_database_types
533
- end
534
99
  end
535
100
  end
536
101
  end