activerecord 1.15.6 → 2.0.0

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 (185) hide show
  1. data/CHANGELOG +2454 -34
  2. data/README +1 -1
  3. data/RUNNING_UNIT_TESTS +3 -34
  4. data/Rakefile +98 -77
  5. data/install.rb +1 -1
  6. data/lib/active_record.rb +13 -22
  7. data/lib/active_record/aggregations.rb +38 -49
  8. data/lib/active_record/associations.rb +452 -333
  9. data/lib/active_record/associations/association_collection.rb +66 -20
  10. data/lib/active_record/associations/association_proxy.rb +9 -8
  11. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +46 -51
  12. data/lib/active_record/associations/has_many_association.rb +21 -57
  13. data/lib/active_record/associations/has_many_through_association.rb +38 -18
  14. data/lib/active_record/associations/has_one_association.rb +30 -14
  15. data/lib/active_record/attribute_methods.rb +253 -0
  16. data/lib/active_record/base.rb +719 -494
  17. data/lib/active_record/calculations.rb +62 -63
  18. data/lib/active_record/callbacks.rb +57 -83
  19. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +38 -9
  20. data/lib/active_record/connection_adapters/abstract/database_statements.rb +56 -15
  21. data/lib/active_record/connection_adapters/abstract/query_cache.rb +87 -0
  22. data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -12
  23. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +191 -62
  24. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +37 -34
  25. data/lib/active_record/connection_adapters/abstract_adapter.rb +28 -17
  26. data/lib/active_record/connection_adapters/mysql_adapter.rb +119 -37
  27. data/lib/active_record/connection_adapters/postgresql_adapter.rb +473 -210
  28. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +34 -0
  29. data/lib/active_record/connection_adapters/sqlite_adapter.rb +91 -107
  30. data/lib/active_record/fixtures.rb +503 -113
  31. data/lib/active_record/locking/optimistic.rb +72 -34
  32. data/lib/active_record/migration.rb +80 -57
  33. data/lib/active_record/observer.rb +13 -10
  34. data/lib/active_record/query_cache.rb +16 -57
  35. data/lib/active_record/reflection.rb +35 -38
  36. data/lib/active_record/schema.rb +5 -5
  37. data/lib/active_record/schema_dumper.rb +35 -13
  38. data/lib/active_record/serialization.rb +98 -0
  39. data/lib/active_record/serializers/json_serializer.rb +71 -0
  40. data/lib/active_record/{xml_serialization.rb → serializers/xml_serializer.rb} +90 -83
  41. data/lib/active_record/timestamp.rb +20 -21
  42. data/lib/active_record/transactions.rb +39 -43
  43. data/lib/active_record/validations.rb +256 -107
  44. data/lib/active_record/version.rb +3 -3
  45. data/lib/activerecord.rb +1 -0
  46. data/test/aaa_create_tables_test.rb +15 -2
  47. data/test/abstract_unit.rb +24 -17
  48. data/test/active_schema_test_mysql.rb +20 -8
  49. data/test/adapter_test.rb +23 -5
  50. data/test/adapter_test_sqlserver.rb +15 -1
  51. data/test/aggregations_test.rb +16 -1
  52. data/test/all.sh +2 -2
  53. data/test/associations/ar_joins_test.rb +0 -0
  54. data/test/associations/callbacks_test.rb +51 -30
  55. data/test/associations/cascaded_eager_loading_test.rb +1 -29
  56. data/test/associations/eager_singularization_test.rb +145 -0
  57. data/test/associations/eager_test.rb +42 -6
  58. data/test/associations/extension_test.rb +6 -1
  59. data/test/associations/inner_join_association_test.rb +88 -0
  60. data/test/associations/join_model_test.rb +47 -16
  61. data/test/associations_test.rb +449 -226
  62. data/test/attribute_methods_test.rb +97 -0
  63. data/test/base_test.rb +251 -105
  64. data/test/binary_test.rb +22 -27
  65. data/test/calculations_test.rb +37 -5
  66. data/test/callbacks_test.rb +23 -0
  67. data/test/connection_test_firebird.rb +2 -2
  68. data/test/connection_test_mysql.rb +30 -0
  69. data/test/connections/native_mysql/connection.rb +3 -0
  70. data/test/connections/native_sqlite/connection.rb +5 -14
  71. data/test/connections/native_sqlite3/connection.rb +5 -14
  72. data/test/connections/native_sqlite3/in_memory_connection.rb +1 -1
  73. data/test/{copy_table_sqlite.rb → copy_table_test_sqlite.rb} +8 -3
  74. data/test/datatype_test_postgresql.rb +178 -27
  75. data/test/{empty_date_time_test.rb → date_time_test.rb} +13 -1
  76. data/test/defaults_test.rb +8 -1
  77. data/test/deprecated_finder_test.rb +7 -128
  78. data/test/finder_test.rb +192 -54
  79. data/test/fixtures/all/developers.yml +0 -0
  80. data/test/fixtures/all/people.csv +0 -0
  81. data/test/fixtures/all/tasks.yml +0 -0
  82. data/test/fixtures/author.rb +12 -5
  83. data/test/fixtures/binaries.yml +130 -435
  84. data/test/fixtures/category.rb +6 -0
  85. data/test/fixtures/company.rb +8 -1
  86. data/test/fixtures/computer.rb +1 -0
  87. data/test/fixtures/contact.rb +16 -0
  88. data/test/fixtures/customer.rb +2 -2
  89. data/test/fixtures/db_definitions/db2.drop.sql +1 -0
  90. data/test/fixtures/db_definitions/db2.sql +4 -0
  91. data/test/fixtures/db_definitions/firebird.drop.sql +3 -1
  92. data/test/fixtures/db_definitions/firebird.sql +6 -0
  93. data/test/fixtures/db_definitions/frontbase.drop.sql +1 -0
  94. data/test/fixtures/db_definitions/frontbase.sql +5 -0
  95. data/test/fixtures/db_definitions/openbase.sql +41 -25
  96. data/test/fixtures/db_definitions/oracle.drop.sql +2 -0
  97. data/test/fixtures/db_definitions/oracle.sql +5 -0
  98. data/test/fixtures/db_definitions/postgresql.drop.sql +7 -0
  99. data/test/fixtures/db_definitions/postgresql.sql +87 -58
  100. data/test/fixtures/db_definitions/postgresql2.sql +1 -2
  101. data/test/fixtures/db_definitions/schema.rb +280 -0
  102. data/test/fixtures/db_definitions/schema2.rb +11 -0
  103. data/test/fixtures/db_definitions/sqlite.drop.sql +1 -0
  104. data/test/fixtures/db_definitions/sqlite.sql +4 -0
  105. data/test/fixtures/db_definitions/sybase.drop.sql +1 -0
  106. data/test/fixtures/db_definitions/sybase.sql +4 -0
  107. data/test/fixtures/developer.rb +10 -0
  108. data/test/fixtures/example.log +1 -0
  109. data/test/fixtures/flowers.jpg +0 -0
  110. data/test/fixtures/item.rb +7 -0
  111. data/test/fixtures/items.yml +4 -0
  112. data/test/fixtures/joke.rb +0 -3
  113. data/test/fixtures/matey.rb +4 -0
  114. data/test/fixtures/mateys.yml +4 -0
  115. data/test/fixtures/minimalistic.rb +2 -0
  116. data/test/fixtures/minimalistics.yml +2 -0
  117. data/test/fixtures/mixins.yml +2 -100
  118. data/test/fixtures/parrot.rb +13 -0
  119. data/test/fixtures/parrots.yml +27 -0
  120. data/test/fixtures/parrots_pirates.yml +7 -0
  121. data/test/fixtures/pirate.rb +5 -0
  122. data/test/fixtures/pirates.yml +9 -0
  123. data/test/fixtures/post.rb +1 -0
  124. data/test/fixtures/project.rb +3 -2
  125. data/test/fixtures/reserved_words/distinct.yml +5 -0
  126. data/test/fixtures/reserved_words/distincts_selects.yml +11 -0
  127. data/test/fixtures/reserved_words/group.yml +14 -0
  128. data/test/fixtures/reserved_words/select.yml +8 -0
  129. data/test/fixtures/reserved_words/values.yml +7 -0
  130. data/test/fixtures/ship.rb +3 -0
  131. data/test/fixtures/ships.yml +5 -0
  132. data/test/fixtures/tagging.rb +4 -0
  133. data/test/fixtures/taggings.yml +8 -1
  134. data/test/fixtures/topic.rb +13 -1
  135. data/test/fixtures/treasure.rb +4 -0
  136. data/test/fixtures/treasures.yml +10 -0
  137. data/test/fixtures_test.rb +205 -24
  138. data/test/inheritance_test.rb +7 -1
  139. data/test/json_serialization_test.rb +180 -0
  140. data/test/lifecycle_test.rb +1 -1
  141. data/test/locking_test.rb +85 -2
  142. data/test/migration_test.rb +206 -40
  143. data/test/mixin_test.rb +13 -515
  144. data/test/pk_test.rb +3 -6
  145. data/test/query_cache_test.rb +104 -0
  146. data/test/reflection_test.rb +16 -0
  147. data/test/reserved_word_test_mysql.rb +177 -0
  148. data/test/schema_dumper_test.rb +38 -3
  149. data/test/serialization_test.rb +47 -0
  150. data/test/transactions_test.rb +74 -23
  151. data/test/unconnected_test.rb +1 -1
  152. data/test/validations_test.rb +322 -32
  153. data/test/xml_serialization_test.rb +121 -44
  154. metadata +48 -41
  155. data/examples/associations.rb +0 -87
  156. data/examples/shared_setup.rb +0 -15
  157. data/examples/validation.rb +0 -85
  158. data/lib/active_record/acts/list.rb +0 -256
  159. data/lib/active_record/acts/nested_set.rb +0 -211
  160. data/lib/active_record/acts/tree.rb +0 -96
  161. data/lib/active_record/connection_adapters/db2_adapter.rb +0 -228
  162. data/lib/active_record/connection_adapters/firebird_adapter.rb +0 -728
  163. data/lib/active_record/connection_adapters/frontbase_adapter.rb +0 -861
  164. data/lib/active_record/connection_adapters/openbase_adapter.rb +0 -350
  165. data/lib/active_record/connection_adapters/oracle_adapter.rb +0 -690
  166. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +0 -591
  167. data/lib/active_record/connection_adapters/sybase_adapter.rb +0 -662
  168. data/lib/active_record/deprecated_associations.rb +0 -104
  169. data/lib/active_record/deprecated_finders.rb +0 -44
  170. data/lib/active_record/vendor/simple.rb +0 -693
  171. data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
  172. data/lib/active_record/wrappings.rb +0 -58
  173. data/test/connections/native_sqlserver/connection.rb +0 -23
  174. data/test/connections/native_sqlserver_odbc/connection.rb +0 -25
  175. data/test/deprecated_associations_test.rb +0 -396
  176. data/test/fixtures/db_definitions/mysql.drop.sql +0 -32
  177. data/test/fixtures/db_definitions/mysql.sql +0 -234
  178. data/test/fixtures/db_definitions/mysql2.drop.sql +0 -2
  179. data/test/fixtures/db_definitions/mysql2.sql +0 -5
  180. data/test/fixtures/db_definitions/sqlserver.drop.sql +0 -34
  181. data/test/fixtures/db_definitions/sqlserver.sql +0 -243
  182. data/test/fixtures/db_definitions/sqlserver2.drop.sql +0 -2
  183. data/test/fixtures/db_definitions/sqlserver2.sql +0 -5
  184. data/test/fixtures/mixin.rb +0 -63
  185. data/test/mixin_nested_set_test.rb +0 -196
@@ -1,96 +0,0 @@
1
- module ActiveRecord
2
- module Acts #:nodoc:
3
- module Tree #:nodoc:
4
- def self.included(base)
5
- base.extend(ClassMethods)
6
- end
7
-
8
- # Specify this act if you want to model a tree structure by providing a parent association and a children
9
- # association. This act requires that you have a foreign key column, which by default is called parent_id.
10
- #
11
- # class Category < ActiveRecord::Base
12
- # acts_as_tree :order => "name"
13
- # end
14
- #
15
- # Example:
16
- # root
17
- # \_ child1
18
- # \_ subchild1
19
- # \_ subchild2
20
- #
21
- # root = Category.create("name" => "root")
22
- # child1 = root.children.create("name" => "child1")
23
- # subchild1 = child1.children.create("name" => "subchild1")
24
- #
25
- # root.parent # => nil
26
- # child1.parent # => root
27
- # root.children # => [child1]
28
- # root.children.first.children.first # => subchild1
29
- #
30
- # In addition to the parent and children associations, the following instance methods are added to the class
31
- # after specifying the act:
32
- # * siblings : Returns all the children of the parent, excluding the current node ([ subchild2 ] when called from subchild1)
33
- # * self_and_siblings : Returns all the children of the parent, including the current node ([ subchild1, subchild2 ] when called from subchild1)
34
- # * ancestors : Returns all the ancestors of the current node ([child1, root] when called from subchild2)
35
- # * root : Returns the root of the current node (root when called from subchild2)
36
- module ClassMethods
37
- # Configuration options are:
38
- #
39
- # * <tt>foreign_key</tt> - specifies the column name to use for tracking of the tree (default: parent_id)
40
- # * <tt>order</tt> - makes it possible to sort the children according to this SQL snippet.
41
- # * <tt>counter_cache</tt> - keeps a count in a children_count column if set to true (default: false).
42
- def acts_as_tree(options = {})
43
- configuration = { :foreign_key => "parent_id", :order => nil, :counter_cache => nil }
44
- configuration.update(options) if options.is_a?(Hash)
45
-
46
- belongs_to :parent, :class_name => name, :foreign_key => configuration[:foreign_key], :counter_cache => configuration[:counter_cache]
47
- has_many :children, :class_name => name, :foreign_key => configuration[:foreign_key], :order => configuration[:order], :dependent => :destroy
48
-
49
- class_eval <<-EOV
50
- include ActiveRecord::Acts::Tree::InstanceMethods
51
-
52
- def self.roots
53
- find(:all, :conditions => "#{configuration[:foreign_key]} IS NULL", :order => #{configuration[:order].nil? ? "nil" : %Q{"#{configuration[:order]}"}})
54
- end
55
-
56
- def self.root
57
- find(:first, :conditions => "#{configuration[:foreign_key]} IS NULL", :order => #{configuration[:order].nil? ? "nil" : %Q{"#{configuration[:order]}"}})
58
- end
59
- EOV
60
- end
61
- end
62
-
63
- module InstanceMethods
64
- # Returns list of ancestors, starting from parent until root.
65
- #
66
- # subchild1.ancestors # => [child1, root]
67
- def ancestors
68
- node, nodes = self, []
69
- nodes << node = node.parent while node.parent
70
- nodes
71
- end
72
-
73
- # Returns the root node of the tree.
74
- def root
75
- node = self
76
- node = node.parent while node.parent
77
- node
78
- end
79
-
80
- # Returns all siblings of the current node.
81
- #
82
- # subchild1.siblings # => [subchild2]
83
- def siblings
84
- self_and_siblings - [self]
85
- end
86
-
87
- # Returns all siblings and a reference to the current node.
88
- #
89
- # subchild1.self_and_siblings # => [subchild1, subchild2]
90
- def self_and_siblings
91
- parent ? parent.children : self.class.roots
92
- end
93
- end
94
- end
95
- end
96
- end
@@ -1,228 +0,0 @@
1
- # Author/Maintainer: Maik Schmidt <contact@maik-schmidt.de>
2
-
3
- require 'active_record/connection_adapters/abstract_adapter'
4
-
5
- begin
6
- require 'db2/db2cli' unless self.class.const_defined?(:DB2CLI)
7
- require 'active_record/vendor/db2'
8
-
9
- module ActiveRecord
10
- class Base
11
- # Establishes a connection to the database that's used by
12
- # all Active Record objects
13
- def self.db2_connection(config) # :nodoc:
14
- config = config.symbolize_keys
15
- usr = config[:username]
16
- pwd = config[:password]
17
- schema = config[:schema]
18
-
19
- if config.has_key?(:database)
20
- database = config[:database]
21
- else
22
- raise ArgumentError, 'No database specified. Missing argument: database.'
23
- end
24
-
25
- connection = DB2::Connection.new(DB2::Environment.new)
26
- connection.connect(database, usr, pwd)
27
- ConnectionAdapters::DB2Adapter.new(connection, logger, :schema => schema)
28
- end
29
- end
30
-
31
- module ConnectionAdapters
32
- # The DB2 adapter works with the C-based CLI driver (http://rubyforge.org/projects/ruby-dbi/)
33
- #
34
- # Options:
35
- #
36
- # * <tt>:username</tt> -- Defaults to nothing
37
- # * <tt>:password</tt> -- Defaults to nothing
38
- # * <tt>:database</tt> -- The name of the database. No default, must be provided.
39
- # * <tt>:schema</tt> -- Database schema to be set initially.
40
- class DB2Adapter < AbstractAdapter
41
- def initialize(connection, logger, connection_options)
42
- super(connection, logger)
43
- @connection_options = connection_options
44
- if schema = @connection_options[:schema]
45
- with_statement do |stmt|
46
- stmt.exec_direct("SET SCHEMA=#{schema}")
47
- end
48
- end
49
- end
50
-
51
- def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
52
- execute(sql, name = nil)
53
- id_value || last_insert_id
54
- end
55
-
56
- def execute(sql, name = nil)
57
- rows_affected = 0
58
- with_statement do |stmt|
59
- log(sql, name) do
60
- stmt.exec_direct(sql)
61
- rows_affected = stmt.row_count
62
- end
63
- end
64
- rows_affected
65
- end
66
-
67
- def begin_db_transaction
68
- @connection.set_auto_commit_off
69
- end
70
-
71
- def commit_db_transaction
72
- @connection.commit
73
- @connection.set_auto_commit_on
74
- end
75
-
76
- def rollback_db_transaction
77
- @connection.rollback
78
- @connection.set_auto_commit_on
79
- end
80
-
81
- def quote_column_name(column_name)
82
- column_name
83
- end
84
-
85
- def adapter_name()
86
- 'DB2'
87
- end
88
-
89
- def quote_string(string)
90
- string.gsub(/'/, "''") # ' (for ruby-mode)
91
- end
92
-
93
- def add_limit_offset!(sql, options)
94
- if limit = options[:limit]
95
- offset = options[:offset] || 0
96
- # The following trick was added by andrea+rails@webcom.it.
97
- sql.gsub!(/SELECT/i, 'SELECT B.* FROM (SELECT A.*, row_number() over () AS internal$rownum FROM (SELECT')
98
- sql << ") A ) B WHERE B.internal$rownum > #{offset} AND B.internal$rownum <= #{limit + offset}"
99
- end
100
- end
101
-
102
- def tables(name = nil)
103
- result = []
104
- schema = @connection_options[:schema] || '%'
105
- with_statement do |stmt|
106
- stmt.tables(schema).each { |t| result << t[2].downcase }
107
- end
108
- result
109
- end
110
-
111
- def indexes(table_name, name = nil)
112
- tmp = {}
113
- schema = @connection_options[:schema] || ''
114
- with_statement do |stmt|
115
- stmt.indexes(table_name, schema).each do |t|
116
- next unless t[5]
117
- next if t[4] == 'SYSIBM' # Skip system indexes.
118
- idx_name = t[5].downcase
119
- col_name = t[8].downcase
120
- if tmp.has_key?(idx_name)
121
- tmp[idx_name].columns << col_name
122
- else
123
- is_unique = t[3] == 0
124
- tmp[idx_name] = IndexDefinition.new(table_name, idx_name, is_unique, [col_name])
125
- end
126
- end
127
- end
128
- tmp.values
129
- end
130
-
131
- def columns(table_name, name = nil)
132
- result = []
133
- schema = @connection_options[:schema] || '%'
134
- with_statement do |stmt|
135
- stmt.columns(table_name, schema).each do |c|
136
- c_name = c[3].downcase
137
- c_default = c[12] == 'NULL' ? nil : c[12]
138
- c_default.gsub!(/^'(.*)'$/, '\1') if !c_default.nil?
139
- c_type = c[5].downcase
140
- c_type += "(#{c[6]})" if !c[6].nil? && c[6] != ''
141
- result << Column.new(c_name, c_default, c_type, c[17] == 'YES')
142
- end
143
- end
144
- result
145
- end
146
-
147
- def native_database_types
148
- {
149
- :primary_key => 'int generated by default as identity (start with 42) primary key',
150
- :string => { :name => 'varchar', :limit => 255 },
151
- :text => { :name => 'clob', :limit => 32768 },
152
- :integer => { :name => 'int' },
153
- :float => { :name => 'float' },
154
- :decimal => { :name => 'decimal' },
155
- :datetime => { :name => 'timestamp' },
156
- :timestamp => { :name => 'timestamp' },
157
- :time => { :name => 'time' },
158
- :date => { :name => 'date' },
159
- :binary => { :name => 'blob', :limit => 32768 },
160
- :boolean => { :name => 'decimal', :limit => 1 }
161
- }
162
- end
163
-
164
- def quoted_true
165
- '1'
166
- end
167
-
168
- def quoted_false
169
- '0'
170
- end
171
-
172
- def active?
173
- @connection.select_one 'select 1 from ibm.sysdummy1'
174
- true
175
- rescue Exception
176
- false
177
- end
178
-
179
- def reconnect!
180
- end
181
-
182
- def table_alias_length
183
- 128
184
- end
185
-
186
- private
187
-
188
- def with_statement
189
- stmt = DB2::Statement.new(@connection)
190
- yield stmt
191
- stmt.free
192
- end
193
-
194
- def last_insert_id
195
- row = select_one(<<-GETID.strip)
196
- with temp(id) as (values (identity_val_local())) select * from temp
197
- GETID
198
- row['id'].to_i
199
- end
200
-
201
- def select(sql, name = nil)
202
- rows = []
203
- with_statement do |stmt|
204
- log(sql, name) do
205
- stmt.exec_direct("#{sql.gsub(/=\s*null/i, 'IS NULL')} with ur")
206
- end
207
-
208
- while row = stmt.fetch_as_hash
209
- row.delete('internal$rownum')
210
- rows << row
211
- end
212
- end
213
- rows
214
- end
215
- end
216
- end
217
- end
218
- rescue LoadError
219
- # DB2 driver is unavailable.
220
- module ActiveRecord # :nodoc:
221
- class Base
222
- def self.db2_connection(config) # :nodoc:
223
- # Set up a reasonable error message
224
- raise LoadError, "DB2 Libraries could not be loaded."
225
- end
226
- end
227
- end
228
- end
@@ -1,728 +0,0 @@
1
- # Author: Ken Kunz <kennethkunz@gmail.com>
2
-
3
- require 'active_record/connection_adapters/abstract_adapter'
4
-
5
- module FireRuby # :nodoc: all
6
- NON_EXISTENT_DOMAIN_ERROR = "335544569"
7
- class Database
8
- def self.db_string_for(config)
9
- unless config.has_key?(:database)
10
- raise ArgumentError, "No database specified. Missing argument: database."
11
- end
12
- host_string = config.values_at(:host, :service, :port).compact.first(2).join("/") if config[:host]
13
- [host_string, config[:database]].join(":")
14
- end
15
-
16
- def self.new_from_config(config)
17
- db = new db_string_for(config)
18
- db.character_set = config[:charset]
19
- return db
20
- end
21
- end
22
- end
23
-
24
- module ActiveRecord
25
- class << Base
26
- def firebird_connection(config) # :nodoc:
27
- require_library_or_gem 'fireruby'
28
- unless defined? FireRuby::SQLType
29
- raise AdapterNotFound,
30
- 'The Firebird adapter requires FireRuby version 0.4.0 or greater; you appear ' <<
31
- 'to be running an older version -- please update FireRuby (gem install fireruby).'
32
- end
33
- config.symbolize_keys!
34
- db = FireRuby::Database.new_from_config(config)
35
- connection_params = config.values_at(:username, :password)
36
- connection = db.connect(*connection_params)
37
- ConnectionAdapters::FirebirdAdapter.new(connection, logger, connection_params)
38
- end
39
- end
40
-
41
- module ConnectionAdapters
42
- class FirebirdColumn < Column # :nodoc:
43
- VARCHAR_MAX_LENGTH = 32_765
44
- BLOB_MAX_LENGTH = 32_767
45
-
46
- def initialize(name, domain, type, sub_type, length, precision, scale, default_source, null_flag)
47
- @firebird_type = FireRuby::SQLType.to_base_type(type, sub_type).to_s
48
-
49
- super(name.downcase, nil, @firebird_type, !null_flag)
50
-
51
- @default = parse_default(default_source) if default_source
52
- @limit = decide_limit(length)
53
- @domain, @sub_type, @precision, @scale = domain, sub_type, precision, scale.abs
54
- end
55
-
56
- def type
57
- if @domain =~ /BOOLEAN/
58
- :boolean
59
- elsif @type == :binary and @sub_type == 1
60
- :text
61
- else
62
- @type
63
- end
64
- end
65
-
66
- def default
67
- type_cast(decide_default) if @default
68
- end
69
-
70
- def self.value_to_boolean(value)
71
- %W(#{FirebirdAdapter.boolean_domain[:true]} true t 1).include? value.to_s.downcase
72
- end
73
-
74
- private
75
- def parse_default(default_source)
76
- default_source =~ /^\s*DEFAULT\s+(.*)\s*$/i
77
- return $1 unless $1.upcase == "NULL"
78
- end
79
-
80
- def decide_default
81
- if @default =~ /^'?(\d*\.?\d+)'?$/ or
82
- @default =~ /^'(.*)'$/ && [:text, :string, :binary, :boolean].include?(type)
83
- $1
84
- else
85
- firebird_cast_default
86
- end
87
- end
88
-
89
- # Submits a _CAST_ query to the database, casting the default value to the specified SQL type.
90
- # This enables Firebird to provide an actual value when context variables are used as column
91
- # defaults (such as CURRENT_TIMESTAMP).
92
- def firebird_cast_default
93
- sql = "SELECT CAST(#{@default} AS #{column_def}) FROM RDB$DATABASE"
94
- if connection = Base.active_connections.values.detect { |conn| conn && conn.adapter_name == 'Firebird' }
95
- connection.execute(sql).to_a.first['CAST']
96
- else
97
- raise ConnectionNotEstablished, "No Firebird connections established."
98
- end
99
- end
100
-
101
- def decide_limit(length)
102
- if text? or number?
103
- length
104
- elsif @firebird_type == 'BLOB'
105
- BLOB_MAX_LENGTH
106
- end
107
- end
108
-
109
- def column_def
110
- case @firebird_type
111
- when 'BLOB' then "VARCHAR(#{VARCHAR_MAX_LENGTH})"
112
- when 'CHAR', 'VARCHAR' then "#{@firebird_type}(#{@limit})"
113
- when 'NUMERIC', 'DECIMAL' then "#{@firebird_type}(#{@precision},#{@scale.abs})"
114
- when 'DOUBLE' then "DOUBLE PRECISION"
115
- else @firebird_type
116
- end
117
- end
118
-
119
- def simplified_type(field_type)
120
- if field_type == 'TIMESTAMP'
121
- :datetime
122
- else
123
- super
124
- end
125
- end
126
- end
127
-
128
- # The Firebird adapter relies on the FireRuby[http://rubyforge.org/projects/fireruby/]
129
- # extension, version 0.4.0 or later (available as a gem or from
130
- # RubyForge[http://rubyforge.org/projects/fireruby/]). FireRuby works with
131
- # Firebird 1.5.x on Linux, OS X and Win32 platforms.
132
- #
133
- # == Usage Notes
134
- #
135
- # === Sequence (Generator) Names
136
- # The Firebird adapter supports the same approach adopted for the Oracle
137
- # adapter. See ActiveRecord::Base#set_sequence_name for more details.
138
- #
139
- # Note that in general there is no need to create a <tt>BEFORE INSERT</tt>
140
- # trigger corresponding to a Firebird sequence generator when using
141
- # ActiveRecord. In other words, you don't have to try to make Firebird
142
- # simulate an <tt>AUTO_INCREMENT</tt> or +IDENTITY+ column. When saving a
143
- # new record, ActiveRecord pre-fetches the next sequence value for the table
144
- # and explicitly includes it in the +INSERT+ statement. (Pre-fetching the
145
- # next primary key value is the only reliable method for the Firebird
146
- # adapter to report back the +id+ after a successful insert.)
147
- #
148
- # === BOOLEAN Domain
149
- # Firebird 1.5 does not provide a native +BOOLEAN+ type. But you can easily
150
- # define a +BOOLEAN+ _domain_ for this purpose, e.g.:
151
- #
152
- # CREATE DOMAIN D_BOOLEAN AS SMALLINT CHECK (VALUE IN (0, 1) OR VALUE IS NULL);
153
- #
154
- # When the Firebird adapter encounters a column that is based on a domain
155
- # that includes "BOOLEAN" in the domain name, it will attempt to treat
156
- # the column as a +BOOLEAN+.
157
- #
158
- # By default, the Firebird adapter will assume that the BOOLEAN domain is
159
- # defined as above. This can be modified if needed. For example, if you
160
- # have a legacy schema with the following +BOOLEAN+ domain defined:
161
- #
162
- # CREATE DOMAIN BOOLEAN AS CHAR(1) CHECK (VALUE IN ('T', 'F'));
163
- #
164
- # ...you can add the following line to your <tt>environment.rb</tt> file:
165
- #
166
- # ActiveRecord::ConnectionAdapters::FirebirdAdapter.boolean_domain = { :true => 'T', :false => 'F' }
167
- #
168
- # === BLOB Elements
169
- # The Firebird adapter currently provides only limited support for +BLOB+
170
- # columns. You cannot currently retrieve or insert a +BLOB+ as an IO stream.
171
- # When selecting a +BLOB+, the entire element is converted into a String.
172
- # When inserting or updating a +BLOB+, the entire value is included in-line
173
- # in the SQL statement, limiting you to values <= 32KB in size.
174
- #
175
- # === Column Name Case Semantics
176
- # Firebird and ActiveRecord have somewhat conflicting case semantics for
177
- # column names.
178
- #
179
- # [*Firebird*]
180
- # The standard practice is to use unquoted column names, which can be
181
- # thought of as case-insensitive. (In fact, Firebird converts them to
182
- # uppercase.) Quoted column names (not typically used) are case-sensitive.
183
- # [*ActiveRecord*]
184
- # Attribute accessors corresponding to column names are case-sensitive.
185
- # The defaults for primary key and inheritance columns are lowercase, and
186
- # in general, people use lowercase attribute names.
187
- #
188
- # In order to map between the differing semantics in a way that conforms
189
- # to common usage for both Firebird and ActiveRecord, uppercase column names
190
- # in Firebird are converted to lowercase attribute names in ActiveRecord,
191
- # and vice-versa. Mixed-case column names retain their case in both
192
- # directions. Lowercase (quoted) Firebird column names are not supported.
193
- # This is similar to the solutions adopted by other adapters.
194
- #
195
- # In general, the best approach is to use unqouted (case-insensitive) column
196
- # names in your Firebird DDL (or if you must quote, use uppercase column
197
- # names). These will correspond to lowercase attributes in ActiveRecord.
198
- #
199
- # For example, a Firebird table based on the following DDL:
200
- #
201
- # CREATE TABLE products (
202
- # id BIGINT NOT NULL PRIMARY KEY,
203
- # "TYPE" VARCHAR(50),
204
- # name VARCHAR(255) );
205
- #
206
- # ...will correspond to an ActiveRecord model class called +Product+ with
207
- # the following attributes: +id+, +type+, +name+.
208
- #
209
- # ==== Quoting <tt>"TYPE"</tt> and other Firebird reserved words:
210
- # In ActiveRecord, the default inheritance column name is +type+. The word
211
- # _type_ is a Firebird reserved word, so it must be quoted in any Firebird
212
- # SQL statements. Because of the case mapping described above, you should
213
- # always reference this column using quoted-uppercase syntax
214
- # (<tt>"TYPE"</tt>) within Firebird DDL or other SQL statements (as in the
215
- # example above). This holds true for any other Firebird reserved words used
216
- # as column names as well.
217
- #
218
- # === Migrations
219
- # The Firebird Adapter now supports Migrations.
220
- #
221
- # ==== Create/Drop Table and Sequence Generators
222
- # Creating or dropping a table will automatically create/drop a
223
- # correpsonding sequence generator, using the default naming convension.
224
- # You can specify a different name using the <tt>:sequence</tt> option; no
225
- # generator is created if <tt>:sequence</tt> is set to +false+.
226
- #
227
- # ==== Rename Table
228
- # The Firebird #rename_table Migration should be used with caution.
229
- # Firebird 1.5 lacks built-in support for this feature, so it is
230
- # implemented by making a copy of the original table (including column
231
- # definitions, indexes and data records), and then dropping the original
232
- # table. Constraints and Triggers are _not_ properly copied, so avoid
233
- # this method if your original table includes constraints (other than
234
- # the primary key) or triggers. (Consider manually copying your table
235
- # or using a view instead.)
236
- #
237
- # == Connection Options
238
- # The following options are supported by the Firebird adapter. None of the
239
- # options have default values.
240
- #
241
- # <tt>:database</tt>::
242
- # <i>Required option.</i> Specifies one of: (i) a Firebird database alias;
243
- # (ii) the full path of a database file; _or_ (iii) a full Firebird
244
- # connection string. <i>Do not specify <tt>:host</tt>, <tt>:service</tt>
245
- # or <tt>:port</tt> as separate options when using a full connection
246
- # string.</i>
247
- # <tt>:host</tt>::
248
- # Set to <tt>"remote.host.name"</tt> for remote database connections.
249
- # May be omitted for local connections if a full database path is
250
- # specified for <tt>:database</tt>. Some platforms require a value of
251
- # <tt>"localhost"</tt> for local connections when using a Firebird
252
- # database _alias_.
253
- # <tt>:service</tt>::
254
- # Specifies a service name for the connection. Only used if <tt>:host</tt>
255
- # is provided. Required when connecting to a non-standard service.
256
- # <tt>:port</tt>::
257
- # Specifies the connection port. Only used if <tt>:host</tt> is provided
258
- # and <tt>:service</tt> is not. Required when connecting to a non-standard
259
- # port and <tt>:service</tt> is not defined.
260
- # <tt>:username</tt>::
261
- # Specifies the database user. May be omitted or set to +nil+ (together
262
- # with <tt>:password</tt>) to use the underlying operating system user
263
- # credentials on supported platforms.
264
- # <tt>:password</tt>::
265
- # Specifies the database password. Must be provided if <tt>:username</tt>
266
- # is explicitly specified; should be omitted if OS user credentials are
267
- # are being used.
268
- # <tt>:charset</tt>::
269
- # Specifies the character set to be used by the connection. Refer to
270
- # Firebird documentation for valid options.
271
- class FirebirdAdapter < AbstractAdapter
272
- TEMP_COLUMN_NAME = 'AR$TEMP_COLUMN'
273
-
274
- @@boolean_domain = { :name => "d_boolean", :type => "smallint", :true => 1, :false => 0 }
275
- cattr_accessor :boolean_domain
276
-
277
- def initialize(connection, logger, connection_params = nil)
278
- super(connection, logger)
279
- @connection_params = connection_params
280
- end
281
-
282
- def adapter_name # :nodoc:
283
- 'Firebird'
284
- end
285
-
286
- def supports_migrations? # :nodoc:
287
- true
288
- end
289
-
290
- def native_database_types # :nodoc:
291
- {
292
- :primary_key => "BIGINT NOT NULL PRIMARY KEY",
293
- :string => { :name => "varchar", :limit => 255 },
294
- :text => { :name => "blob sub_type text" },
295
- :integer => { :name => "bigint" },
296
- :decimal => { :name => "decimal" },
297
- :numeric => { :name => "numeric" },
298
- :float => { :name => "float" },
299
- :datetime => { :name => "timestamp" },
300
- :timestamp => { :name => "timestamp" },
301
- :time => { :name => "time" },
302
- :date => { :name => "date" },
303
- :binary => { :name => "blob sub_type 0" },
304
- :boolean => boolean_domain
305
- }
306
- end
307
-
308
- # Returns true for Firebird adapter (since Firebird requires primary key
309
- # values to be pre-fetched before insert). See also #next_sequence_value.
310
- def prefetch_primary_key?(table_name = nil)
311
- true
312
- end
313
-
314
- def default_sequence_name(table_name, primary_key = nil) # :nodoc:
315
- "#{table_name}_seq"
316
- end
317
-
318
-
319
- # QUOTING ==================================================
320
-
321
- def quote(value, column = nil) # :nodoc:
322
- if [Time, DateTime].include?(value.class)
323
- "CAST('#{value.strftime("%Y-%m-%d %H:%M:%S")}' AS TIMESTAMP)"
324
- else
325
- super
326
- end
327
- end
328
-
329
- def quote_string(string) # :nodoc:
330
- string.gsub(/'/, "''")
331
- end
332
-
333
- def quote_column_name(column_name) # :nodoc:
334
- %Q("#{ar_to_fb_case(column_name.to_s)}")
335
- end
336
-
337
- def quoted_true # :nodoc:
338
- quote(boolean_domain[:true])
339
- end
340
-
341
- def quoted_false # :nodoc:
342
- quote(boolean_domain[:false])
343
- end
344
-
345
-
346
- # CONNECTION MANAGEMENT ====================================
347
-
348
- def active? # :nodoc:
349
- not @connection.closed?
350
- end
351
-
352
- def disconnect! # :nodoc:
353
- @connection.close rescue nil
354
- end
355
-
356
- def reconnect! # :nodoc:
357
- disconnect!
358
- @connection = @connection.database.connect(*@connection_params)
359
- end
360
-
361
-
362
- # DATABASE STATEMENTS ======================================
363
-
364
- def select_all(sql, name = nil) # :nodoc:
365
- select(sql, name)
366
- end
367
-
368
- def select_one(sql, name = nil) # :nodoc:
369
- select(sql, name).first
370
- end
371
-
372
- def execute(sql, name = nil, &block) # :nodoc:
373
- log(sql, name) do
374
- if @transaction
375
- @connection.execute(sql, @transaction, &block)
376
- else
377
- @connection.execute_immediate(sql, &block)
378
- end
379
- end
380
- end
381
-
382
- def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) # :nodoc:
383
- execute(sql, name)
384
- id_value
385
- end
386
-
387
- alias_method :update, :execute
388
- alias_method :delete, :execute
389
-
390
- def begin_db_transaction() # :nodoc:
391
- @transaction = @connection.start_transaction
392
- end
393
-
394
- def commit_db_transaction() # :nodoc:
395
- @transaction.commit
396
- ensure
397
- @transaction = nil
398
- end
399
-
400
- def rollback_db_transaction() # :nodoc:
401
- @transaction.rollback
402
- ensure
403
- @transaction = nil
404
- end
405
-
406
- def add_limit_offset!(sql, options) # :nodoc:
407
- if options[:limit]
408
- limit_string = "FIRST #{options[:limit]}"
409
- limit_string << " SKIP #{options[:offset]}" if options[:offset]
410
- sql.sub!(/\A(\s*SELECT\s)/i, '\&' + limit_string + ' ')
411
- end
412
- end
413
-
414
- # Returns the next sequence value from a sequence generator. Not generally
415
- # called directly; used by ActiveRecord to get the next primary key value
416
- # when inserting a new database record (see #prefetch_primary_key?).
417
- def next_sequence_value(sequence_name)
418
- FireRuby::Generator.new(sequence_name, @connection).next(1)
419
- end
420
-
421
-
422
- # SCHEMA STATEMENTS ========================================
423
-
424
- def current_database # :nodoc:
425
- file = @connection.database.file.split(':').last
426
- File.basename(file, '.*')
427
- end
428
-
429
- def recreate_database! # :nodoc:
430
- sql = "SELECT rdb$character_set_name FROM rdb$database"
431
- charset = execute(sql).to_a.first[0].rstrip
432
- disconnect!
433
- @connection.database.drop(*@connection_params)
434
- FireRuby::Database.create(@connection.database.file,
435
- @connection_params[0], @connection_params[1], 4096, charset)
436
- end
437
-
438
- def tables(name = nil) # :nodoc:
439
- sql = "SELECT rdb$relation_name FROM rdb$relations WHERE rdb$system_flag = 0"
440
- execute(sql, name).collect { |row| row[0].rstrip.downcase }
441
- end
442
-
443
- def indexes(table_name, name = nil) # :nodoc:
444
- index_metadata(table_name, false, name).inject([]) do |indexes, row|
445
- if indexes.empty? or indexes.last.name != row[0]
446
- indexes << IndexDefinition.new(table_name, row[0].rstrip.downcase, row[1] == 1, [])
447
- end
448
- indexes.last.columns << row[2].rstrip.downcase
449
- indexes
450
- end
451
- end
452
-
453
- def columns(table_name, name = nil) # :nodoc:
454
- sql = <<-end_sql
455
- SELECT r.rdb$field_name, r.rdb$field_source, f.rdb$field_type, f.rdb$field_sub_type,
456
- f.rdb$field_length, f.rdb$field_precision, f.rdb$field_scale,
457
- COALESCE(r.rdb$default_source, f.rdb$default_source) rdb$default_source,
458
- COALESCE(r.rdb$null_flag, f.rdb$null_flag) rdb$null_flag
459
- FROM rdb$relation_fields r
460
- JOIN rdb$fields f ON r.rdb$field_source = f.rdb$field_name
461
- WHERE r.rdb$relation_name = '#{table_name.to_s.upcase}'
462
- ORDER BY r.rdb$field_position
463
- end_sql
464
- execute(sql, name).collect do |field|
465
- field_values = field.values.collect do |value|
466
- case value
467
- when String then value.rstrip
468
- when FireRuby::Blob then value.to_s
469
- else value
470
- end
471
- end
472
- FirebirdColumn.new(*field_values)
473
- end
474
- end
475
-
476
- def create_table(name, options = {}) # :nodoc:
477
- begin
478
- super
479
- rescue StatementInvalid
480
- raise unless non_existent_domain_error?
481
- create_boolean_domain
482
- super
483
- end
484
- unless options[:id] == false or options[:sequence] == false
485
- sequence_name = options[:sequence] || default_sequence_name(name)
486
- create_sequence(sequence_name)
487
- end
488
- end
489
-
490
- def drop_table(name, options = {}) # :nodoc:
491
- super(name)
492
- unless options[:sequence] == false
493
- sequence_name = options[:sequence] || default_sequence_name(name)
494
- drop_sequence(sequence_name) if sequence_exists?(sequence_name)
495
- end
496
- end
497
-
498
- def add_column(table_name, column_name, type, options = {}) # :nodoc:
499
- super
500
- rescue StatementInvalid
501
- raise unless non_existent_domain_error?
502
- create_boolean_domain
503
- super
504
- end
505
-
506
- def change_column(table_name, column_name, type, options = {}) # :nodoc:
507
- change_column_type(table_name, column_name, type, options)
508
- change_column_position(table_name, column_name, options[:position]) if options.include?(:position)
509
- change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
510
- end
511
-
512
- def change_column_default(table_name, column_name, default) # :nodoc:
513
- table_name = table_name.to_s.upcase
514
- sql = <<-end_sql
515
- UPDATE rdb$relation_fields f1
516
- SET f1.rdb$default_source =
517
- (SELECT f2.rdb$default_source FROM rdb$relation_fields f2
518
- WHERE f2.rdb$relation_name = '#{table_name}'
519
- AND f2.rdb$field_name = '#{TEMP_COLUMN_NAME}'),
520
- f1.rdb$default_value =
521
- (SELECT f2.rdb$default_value FROM rdb$relation_fields f2
522
- WHERE f2.rdb$relation_name = '#{table_name}'
523
- AND f2.rdb$field_name = '#{TEMP_COLUMN_NAME}')
524
- WHERE f1.rdb$relation_name = '#{table_name}'
525
- AND f1.rdb$field_name = '#{ar_to_fb_case(column_name.to_s)}'
526
- end_sql
527
- transaction do
528
- add_column(table_name, TEMP_COLUMN_NAME, :string, :default => default)
529
- execute sql
530
- remove_column(table_name, TEMP_COLUMN_NAME)
531
- end
532
- end
533
-
534
- def rename_column(table_name, column_name, new_column_name) # :nodoc:
535
- execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} TO #{new_column_name}"
536
- end
537
-
538
- def remove_index(table_name, options) #:nodoc:
539
- execute "DROP INDEX #{quote_column_name(index_name(table_name, options))}"
540
- end
541
-
542
- def rename_table(name, new_name) # :nodoc:
543
- if table_has_constraints_or_dependencies?(name)
544
- raise ActiveRecordError,
545
- "Table #{name} includes constraints or dependencies that are not supported by " <<
546
- "the Firebird rename_table migration. Try explicitly removing the constraints/" <<
547
- "dependencies first, or manually renaming the table."
548
- end
549
-
550
- transaction do
551
- copy_table(name, new_name)
552
- copy_table_indexes(name, new_name)
553
- end
554
- begin
555
- copy_table_data(name, new_name)
556
- copy_sequence_value(name, new_name)
557
- rescue
558
- drop_table(new_name)
559
- raise
560
- end
561
- drop_table(name)
562
- end
563
-
564
- def dump_schema_information # :nodoc:
565
- super << ";\n"
566
- end
567
-
568
- def type_to_sql(type, limit = nil, precision = nil, scale = nil) # :nodoc:
569
- case type
570
- when :integer then integer_sql_type(limit)
571
- when :float then float_sql_type(limit)
572
- when :string then super(type, limit, precision, scale)
573
- else super(type, limit, precision, scale)
574
- end
575
- end
576
-
577
- private
578
- def integer_sql_type(limit)
579
- case limit
580
- when (1..2) then 'smallint'
581
- when (3..4) then 'integer'
582
- else 'bigint'
583
- end
584
- end
585
-
586
- def float_sql_type(limit)
587
- limit.to_i <= 4 ? 'float' : 'double precision'
588
- end
589
-
590
- def select(sql, name = nil)
591
- execute(sql, name).collect do |row|
592
- hashed_row = {}
593
- row.each do |column, value|
594
- value = value.to_s if FireRuby::Blob === value
595
- hashed_row[fb_to_ar_case(column)] = value
596
- end
597
- hashed_row
598
- end
599
- end
600
-
601
- def primary_key(table_name)
602
- if pk_row = index_metadata(table_name, true).to_a.first
603
- pk_row[2].rstrip.downcase
604
- end
605
- end
606
-
607
- def index_metadata(table_name, pk, name = nil)
608
- sql = <<-end_sql
609
- SELECT i.rdb$index_name, i.rdb$unique_flag, s.rdb$field_name
610
- FROM rdb$indices i
611
- JOIN rdb$index_segments s ON i.rdb$index_name = s.rdb$index_name
612
- LEFT JOIN rdb$relation_constraints c ON i.rdb$index_name = c.rdb$index_name
613
- WHERE i.rdb$relation_name = '#{table_name.to_s.upcase}'
614
- end_sql
615
- if pk
616
- sql << "AND c.rdb$constraint_type = 'PRIMARY KEY'\n"
617
- else
618
- sql << "AND (c.rdb$constraint_type IS NULL OR c.rdb$constraint_type != 'PRIMARY KEY')\n"
619
- end
620
- sql << "ORDER BY i.rdb$index_name, s.rdb$field_position\n"
621
- execute sql, name
622
- end
623
-
624
- def change_column_type(table_name, column_name, type, options = {})
625
- sql = "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} TYPE #{type_to_sql(type, options[:limit])}"
626
- execute sql
627
- rescue StatementInvalid
628
- raise unless non_existent_domain_error?
629
- create_boolean_domain
630
- execute sql
631
- end
632
-
633
- def change_column_position(table_name, column_name, position)
634
- execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} POSITION #{position}"
635
- end
636
-
637
- def copy_table(from, to)
638
- table_opts = {}
639
- if pk = primary_key(from)
640
- table_opts[:primary_key] = pk
641
- else
642
- table_opts[:id] = false
643
- end
644
- create_table(to, table_opts) do |table|
645
- from_columns = columns(from).reject { |col| col.name == table_opts[:primary_key] }
646
- from_columns.each do |column|
647
- col_opts = [:limit, :default, :null].inject({}) { |opts, opt| opts.merge(opt => column.send(opt)) }
648
- table.column column.name, column.type, col_opts
649
- end
650
- end
651
- end
652
-
653
- def copy_table_indexes(from, to)
654
- indexes(from).each do |index|
655
- unless index.name[from.to_s]
656
- raise ActiveRecordError,
657
- "Cannot rename index #{index.name}, because the index name does not include " <<
658
- "the original table name (#{from}). Try explicitly removing the index on the " <<
659
- "original table and re-adding it on the new (renamed) table."
660
- end
661
- options = {}
662
- options[:name] = index.name.gsub(from.to_s, to.to_s)
663
- options[:unique] = index.unique
664
- add_index(to, index.columns, options)
665
- end
666
- end
667
-
668
- def copy_table_data(from, to)
669
- execute "INSERT INTO #{to} SELECT * FROM #{from}", "Copy #{from} data to #{to}"
670
- end
671
-
672
- def copy_sequence_value(from, to)
673
- sequence_value = FireRuby::Generator.new(default_sequence_name(from), @connection).last
674
- execute "SET GENERATOR #{default_sequence_name(to)} TO #{sequence_value}"
675
- end
676
-
677
- def sequence_exists?(sequence_name)
678
- FireRuby::Generator.exists?(sequence_name, @connection)
679
- end
680
-
681
- def create_sequence(sequence_name)
682
- FireRuby::Generator.create(sequence_name.to_s, @connection)
683
- end
684
-
685
- def drop_sequence(sequence_name)
686
- FireRuby::Generator.new(sequence_name.to_s, @connection).drop
687
- end
688
-
689
- def create_boolean_domain
690
- sql = <<-end_sql
691
- CREATE DOMAIN #{boolean_domain[:name]} AS #{boolean_domain[:type]}
692
- CHECK (VALUE IN (#{quoted_true}, #{quoted_false}) OR VALUE IS NULL)
693
- end_sql
694
- execute sql rescue nil
695
- end
696
-
697
- def table_has_constraints_or_dependencies?(table_name)
698
- table_name = table_name.to_s.upcase
699
- sql = <<-end_sql
700
- SELECT 1 FROM rdb$relation_constraints
701
- WHERE rdb$relation_name = '#{table_name}'
702
- AND rdb$constraint_type IN ('UNIQUE', 'FOREIGN KEY', 'CHECK')
703
- UNION
704
- SELECT 1 FROM rdb$dependencies
705
- WHERE rdb$depended_on_name = '#{table_name}'
706
- AND rdb$depended_on_type = 0
707
- end_sql
708
- !select(sql).empty?
709
- end
710
-
711
- def non_existent_domain_error?
712
- $!.message.include? FireRuby::NON_EXISTENT_DOMAIN_ERROR
713
- end
714
-
715
- # Maps uppercase Firebird column names to lowercase for ActiveRecord;
716
- # mixed-case columns retain their original case.
717
- def fb_to_ar_case(column_name)
718
- column_name =~ /[[:lower:]]/ ? column_name : column_name.downcase
719
- end
720
-
721
- # Maps lowercase ActiveRecord column names to uppercase for Fierbird;
722
- # mixed-case columns retain their original case.
723
- def ar_to_fb_case(column_name)
724
- column_name =~ /[[:upper:]]/ ? column_name : column_name.upcase
725
- end
726
- end
727
- end
728
- end