Changeset 1912
- Timestamp:
- 07/24/05 14:01:35 (3 years ago)
- Files:
-
- trunk/activerecord/CHANGELOG (modified) (1 diff)
- trunk/activerecord/lib/active_record/base.rb (modified) (3 diffs)
- trunk/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb (modified) (1 diff)
- trunk/activerecord/lib/active_record/connection_adapters/db2_adapter.rb (modified) (1 diff)
- trunk/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb (modified) (1 diff)
- trunk/activerecord/lib/active_record/connection_adapters/oci_adapter.rb (modified) (11 diffs)
- trunk/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb (modified) (1 diff)
- trunk/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb (modified) (1 diff)
- trunk/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb (modified) (1 diff)
- trunk/activerecord/Rakefile (modified) (1 diff)
- trunk/activerecord/test/base_test.rb (modified) (1 diff)
- trunk/activerecord/test/fixtures/company.rb (modified) (1 diff)
- trunk/activerecord/test/fixtures/db_definitions/oci.drop.sql (modified) (2 diffs)
- trunk/activerecord/test/fixtures/db_definitions/oci.sql (modified) (24 diffs)
- trunk/activerecord/test/fixtures/db_definitions/oci2.drop.sql (modified) (1 diff)
- trunk/activerecord/test/fixtures/db_definitions/oci2.sql (modified) (1 diff)
- trunk/activerecord/test/fixtures/subject.rb (added)
- trunk/activerecord/test/synonym_test_oci.rb (added)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/activerecord/CHANGELOG
r1897 r1912 1 1 *SVN* 2 3 * Default sequence names for Oracle changed to #{table_name}_seq, which is the most commonly used standard. In addition, a new method ActiveRecord::Base#set_sequence_name allows the developer to set the sequence name per model. This is a non-backwards-compatible change -- anyone using the old-style "rails_sequence" will need to either create new sequences, or set: ActiveRecord::Base.set_sequence_name = "rails_sequence" #1798 4 5 * OCIAdapter now properly handles synonyms, which are commonly used to separate out the schema owner from the application user #1798 6 7 * Fixed the handling of camelCase columns names in Oracle #1798 8 9 * Implemented for OCI the Rakefile tasks of :clone_structure_to_test, :db_structure_dump, and :purge_test_database, which enable Oracle folks to enjoy all the agile goodness of Rails for testing. Note that the current implementation is fairly limited -- only tables and sequences are cloned, not constraints or indexes. A full clone in Oracle generally requires some manual effort, and is version-specific. Post 9i, Oracle recommends the use of the DBMS_METADATA package, though that approach requires editing of the physical characteristics generated #1798 10 11 * Fixed the handling of multiple blob columns in Oracle if one or more of them are null #1798 2 12 3 13 * Added support for calling constrained class methods on has_many and has_and_belongs_to_many collections #1764 [Tobias Luetke] trunk/activerecord/lib/active_record/base.rb
r1894 r1912 570 570 end 571 571 572 # Defines the sequence_name (for Oracle) -- can be overridden in subclasses. 573 def sequence_name 574 "#{table_name}_seq" 575 end 576 572 577 # Sets the table name to use to the given value, or (if the value 573 578 # is nil or false) to the value returned by the given block. … … 612 617 end 613 618 alias :inheritance_column= :set_inheritance_column 619 620 # Sets the name of the sequence to use when generating ids to the given 621 # value, or (if the value is nil or false) to the value returned by the 622 # given block. Currently useful only when using Oracle, which requires 623 # explicit sequences. 624 # 625 # Setting the sequence name when using other dbs will have no effect. 626 # If a sequence name is not explicitly set when using Oracle, it will 627 # default to the commonly used pattern of: #{table_name}_seq 628 # 629 # Example: 630 # 631 # class Project < ActiveRecord::Base 632 # set_sequence_name "projectseq" # default would have been "project_seq" 633 # end 634 def set_sequence_name( value=nil, &block ) 635 define_attr_method :sequence_name, value, &block 636 end 637 alias :sequence_name= :set_sequence_name 614 638 615 639 # Turns the +table_name+ back into a class name following the reverse rules of +table_name+. … … 1194 1218 "VALUES(#{attributes_with_quotes.values.join(', ')})", 1195 1219 "#{self.class.name} Create", 1196 self.class.primary_key, self.id 1220 self.class.primary_key, self.id, self.class.sequence_name 1197 1221 ) 1198 1222 trunk/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
r1893 r1912 280 280 281 281 # Returns the last auto-generated ID from the affected table. 282 def insert(sql, name = nil, pk = nil, id_value = nil ) end282 def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) end 283 283 284 284 # Executes the update statement and returns the number of rows affected. trunk/activerecord/lib/active_record/connection_adapters/db2_adapter.rb
r1850 r1912 45 45 end 46 46 47 def insert(sql, name = nil, pk = nil, id_value = nil )47 def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) 48 48 execute(sql, name = nil) 49 49 id_value || last_insert_id trunk/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
r1774 r1912 110 110 end 111 111 112 def insert(sql, name = nil, pk = nil, id_value = nil )112 def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) 113 113 execute(sql, name = nil) 114 114 id_value || @connection.insert_id trunk/activerecord/lib/active_record/connection_adapters/oci_adapter.rb
r1629 r1912 16 16 # remains then please acknowledge my contribution. 17 17 # Copyright 2005 Graham Jenkins 18 # $Revision: 1.2 $ 18 19 19 require 'active_record/connection_adapters/abstract_adapter' 20 20 … … 78 78 # 79 79 # Usage notes: 80 # * Key generation uses a sequence "rails_sequence" for all tables. (I couldn't find a simple81 # and safe way of passing table-specific sequence information to the adapter.)80 # * Key generation assumes a "${table_name}_seq" sequence is available for all tables; the 81 # sequence name can be changed using ActiveRecord::Base.set_sequence_name 82 82 # * Oracle uses DATE or TIMESTAMP datatypes for both dates and times. Consequently I have had to 83 83 # resort to some hacks to get data converted to Date or Time in Ruby. … … 107 107 if column and column.type == :binary then %Q{empty_#{ column.sql_type }()} 108 108 else case value 109 when String then %Q{'#{quote_string(value)}'}109 when String then %Q{'#{quote_string(value)}'} 110 110 when NilClass then 'null' 111 111 when TrueClass then '1' 112 112 when FalseClass then '0' 113 when Numeric then value.to_s113 when Numeric then value.to_s 114 114 when Date, Time then %Q{'#{value.strftime("%Y-%m-%d %H:%M:%S")}'} 115 else %Q{'#{quote_string(value.to_yaml)}'}115 else %Q{'#{quote_string(value.to_yaml)}'} 116 116 end 117 end 118 end 119 120 # camelCase column names need to be quoted; not that anyone using Oracle 121 # would really do this, but handling this case means we pass the test... 122 def quote_column_name(name) 123 name =~ /[A-Z]/ ? "\"#{name}\"" : name 124 end 125 126 def structure_dump 127 s = select_all("select sequence_name from user_sequences").inject("") do |structure, seq| 128 structure << "create sequence #{seq.to_a.first.last};\n\n" 129 end 130 131 select_all("select table_name from user_tables").inject(s) do |structure, table| 132 ddl = "create table #{table.to_a.first.last} (\n " 133 cols = select_all(%Q{ 134 select column_name, data_type, data_length, data_precision, data_scale, data_default, nullable 135 from user_tab_columns 136 where table_name = '#{table.to_a.first.last}' 137 order by column_id 138 }).map do |row| 139 col = "#{row['column_name'].downcase} #{row['data_type'].downcase}" 140 if row['data_type'] =='NUMBER' and !row['data_precision'].nil? 141 col << "(#{row['data_precision'].to_i}" 142 col << ",#{row['data_scale'].to_i}" if !row['data_scale'].nil? 143 col << ')' 144 elsif row['data_type'].include?('CHAR') 145 col << "(#{row['data_length'].to_i})" 146 end 147 col << " default #{row['data_default']}" if !row['data_default'].nil? 148 col << ' not null' if row['nullable'] == 'N' 149 col 150 end 151 ddl << cols.join(",\n ") 152 ddl << ");\n\n" 153 structure << ddl 154 end 155 end 156 157 def structure_drop 158 s = select_all("select sequence_name from user_sequences").inject("") do |drop, seq| 159 drop << "drop sequence #{seq.to_a.first.last};\n\n" 160 end 161 162 select_all("select table_name from user_tables").inject(s) do |drop, table| 163 drop << "drop table #{table.to_a.first.last} cascade constraints;\n\n" 117 164 end 118 165 end … … 121 168 offset = sql =~ /OFFSET (\d+)$/ ? $1.to_i : 0 122 169 sql, limit = $1, $2.to_i if sql =~ /(.*)(?: LIMIT[= ](\d+))(\s*OFFSET \d+)?$/ 170 123 171 if limit 124 172 sql = "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_ where rownum <= #{offset+limit}) where raw_rnum_ > #{offset}" … … 126 174 sql = "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_) where raw_rnum_ > #{offset}" 127 175 end 176 128 177 cursor = log(sql, name) { @connection.exec sql } 129 cols = cursor.get_col_names.map { |x| x.downcase}178 cols = cursor.get_col_names.map { |x| oci_downcase(x) } 130 179 rows = [] 180 131 181 while row = cursor.fetch 132 182 hash = Hash.new 133 cols.each_with_index { |col, i| 183 184 cols.each_with_index do |col, i| 134 185 hash[col] = case row[i] 135 186 when OCI8::LOB … … 140 191 else row[i] 141 192 end unless col == 'raw_rnum_' 142 } 193 end 194 143 195 rows << hash 144 196 end 197 145 198 rows 146 199 ensure … … 154 207 155 208 def columns(table_name, name = nil) 156 cols =select_all(%Q{209 select_all(%Q{ 157 210 select column_name, data_type, data_default, data_length, data_scale 158 from user_tab_columns where table_name = '#{table_name.upcase}'} 159 ).map { |row| 160 OCIColumn.new row['column_name'].downcase, row['data_default'], 161 row['data_length'], row['data_type'], row['data_scale'] 162 } 163 cols 164 end 165 166 def insert(sql, name = nil, pk = nil, id_value = nil) 211 from user_catalog cat, user_synonyms syn, all_tab_columns col 212 where cat.table_name = '#{table_name.upcase}' 213 and syn.synonym_name (+)= cat.table_name 214 and col.owner = nvl(syn.table_owner, user) 215 and col.table_name = nvl(syn.table_name, cat.table_name)} 216 ).map do |row| 217 OCIColumn.new( 218 oci_downcase(row['column_name']), 219 row['data_default'], 220 row['data_length'], 221 row['data_type'], 222 row['data_scale'] 223 ) 224 end 225 end 226 227 def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) 167 228 if pk.nil? # Who called us? What does the sql look like? No idea! 168 229 execute sql, name … … 170 231 log(sql, name) { @connection.exec sql } 171 232 else # Assume the sql contains a bind-variable for the id 172 id_value = select_one("select rails_sequence.nextval id from dual")['id']233 id_value = select_one("select #{sequence_name}.nextval id from dual")['id'] 173 234 log(sql, name) { @connection.exec sql, id_value } 174 235 end 236 175 237 id_value 176 238 end … … 202 264 'OCI' 203 265 end 266 267 private 268 # Oracle column names by default are case-insensitive, but treated as upcase; 269 # for neatness, we'll downcase within Rails. EXCEPT that folks CAN quote 270 # their column names when creating Oracle tables, which makes then case-sensitive. 271 # I don't know anybody who does this, but we'll handle the theoretical case of a 272 # camelCase column name. I imagine other dbs handle this different, since there's a 273 # unit test that's currently failing test_oci. 274 def oci_downcase(column_name) 275 column_name =~ /[a-z]/ ? column_name : column_name.downcase 276 end 204 277 end 205 278 end … … 208 281 module ActiveRecord 209 282 class Base 210 def self.oci_connection(config) #:nodoc: 211 conn = OCI8.new config[:username], config[:password], config[:host] 212 conn.exec %q{alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS'} 213 conn.exec %q{alter session set nls_timestamp_format = 'YYYY-MM-DD HH24:MI:SS'} 214 conn.autocommit = true 215 ConnectionAdapters::OCIAdapter.new conn, logger 283 class << self 284 def oci_connection(config) #:nodoc: 285 conn = OCI8.new config[:username], config[:password], config[:host] 286 conn.exec %q{alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS'} 287 conn.exec %q{alter session set nls_timestamp_format = 'YYYY-MM-DD HH24:MI:SS'} 288 conn.autocommit = true 289 ConnectionAdapters::OCIAdapter.new conn, logger 290 end 216 291 end 217 292 … … 232 307 # After setting large objects to empty, select the OCI8::LOB and write back the data 233 308 def write_lobs() #:nodoc: 234 if connection. class == ConnectionAdapters::OCIAdapter309 if connection.is_a?(ConnectionAdapters::OCIAdapter) 235 310 self.class.columns.select { |c| c.type == :binary }.each { |c| 236 break unless value = self[c.name] 311 value = self[c.name] 312 next if value.nil? || (value == '') 237 313 lob = connection.select_one( 238 314 "select #{ c.name} from #{ self.class.table_name } WHERE #{ self.class.primary_key} = #{quote(id)}", trunk/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
r1703 r1912 96 96 end 97 97 98 def insert(sql, name = nil, pk = nil, id_value = nil )98 def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) 99 99 execute(sql, name) 100 100 table = sql.split(" ", 4)[2] trunk/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
r1893 r1912 124 124 end 125 125 126 def insert(sql, name = nil, pk = nil, id_value = nil )126 def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) 127 127 execute(sql, name = nil) 128 128 id_value || @connection.last_insert_row_id trunk/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb
r1784 r1912 205 205 end 206 206 207 def insert(sql, name = nil, pk = nil, id_value = nil )207 def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) 208 208 begin 209 209 table_name = get_table_name(sql) trunk/activerecord/Rakefile
r1814 r1912 23 23 24 24 desc "Default Task" 25 task :default => [ :test_ ruby_mysql, :test_mysql_ruby, :test_sqlite, :test_sqlite3, :test_postgresql ]25 task :default => [ :test_mysql, :test_sqlite, :test_postgresql ] 26 26 27 27 # Run the unit tests 28 28 29 Rake::TestTask.new("test_ruby_mysql") { |t| 30 t.libs << "test" << "test/connections/native_mysql" 31 t.pattern = 'test/*_test{,_mysql}.rb' 32 t.verbose = true 33 } 34 35 Rake::TestTask.new("test_mysql_ruby") { |t| 36 t.libs << "test" << "test/connections/native_mysql" 37 t.pattern = 'test/*_test{,_mysql}.rb' 38 t.verbose = true 39 } 40 41 for adapter in %w( postgresql sqlite sqlite3 sqlserver sqlserver_odbc db2 oci ) 29 for adapter in %w( mysql postgresql sqlite sqlite3 sqlserver sqlserver_odbc db2 oci ) 42 30 Rake::TestTask.new("test_#{adapter}") { |t| 43 31 t.libs << "test" << "test/connections/native_#{adapter}" trunk/activerecord/test/base_test.rb
r1850 r1912 572 572 def test_multiparameter_mass_assignment_protector 573 573 task = Task.new 574 time = Time.mktime( 0)574 time = Time.mktime(2000, 1, 1, 1) 575 575 task.starting = time 576 576 attributes = { "starting(1i)" => "2004", "starting(2i)" => "6", "starting(3i)" => "24" } trunk/activerecord/test/fixtures/company.rb
r1398 r1912 1 1 class Company < ActiveRecord::Base 2 2 attr_protected :rating 3 set_sequence_name :companies_nonstd_seq 3 4 4 5 validates_presence_of :name trunk/activerecord/test/fixtures/db_definitions/oci.drop.sql
r1132 r1912 2 2 drop table companies; 3 3 drop table topics; 4 drop synonym subjects; 5 drop table developers_projects; 6 drop table computers; 4 7 drop table developers; 5 8 drop table projects; 6 drop table developers_projects;7 9 drop table customers; 8 10 drop table movies; … … 15 17 drop table people; 16 18 drop table binaries; 17 drop table posts;18 19 drop table comments; 19 20 drop table authors; 20 drop table computers; 21 drop table tasks; 22 drop table categories_posts; 21 23 drop table categories; 22 drop table categories_posts; 23 drop sequence rails_sequence; 24 drop table posts; 25 drop table fk_test_has_pk; 26 drop table fk_test_has_fk; 27 drop sequence accounts_seq; 28 drop sequence companies_nonstd_seq; 29 drop sequence topics_seq; 30 drop sequence developers_seq; 31 drop sequence projects_seq; 32 drop sequence developers_projects_seq; 33 drop sequence customers_seq; 34 drop sequence movies_seq; 35 drop sequence subscribers_seq; 36 drop sequence booleantests_seq; 37 drop sequence auto_id_tests_seq; 38 drop sequence entrants_seq; 39 drop sequence colnametests_seq; 40 drop sequence mixins_seq; 41 drop sequence people_seq; 42 drop sequence binaries_seq; 43 drop sequence posts_seq; 44 drop sequence comments_seq; 45 drop sequence authors_seq; 46 drop sequence tasks_seq; 47 drop sequence computers_seq; 48 drop sequence categories_seq; 49 drop sequence categories_posts_seq; 50 drop sequence fk_test_has_pk_seq; 51 drop sequence fk_test_has_fk_seq; trunk/activerecord/test/fixtures/db_definitions/oci.sql
r1860 r1912 1 create sequence rails_sequence minvalue 10000;2 3 1 create table companies ( 4 2 id integer not null, … … 13 11 ); 14 12 13 -- non-standard sequence name used to test set_sequence_name 14 -- 15 create sequence companies_nonstd_seq minvalue 10000; 16 15 17 create table accounts ( 16 18 id integer not null, … … 19 21 primary key (id) 20 22 ); 23 create sequence accounts_seq minvalue 10000; 21 24 22 25 create table topics ( … … 51 54 primary key (id) 52 55 ); 56 create sequence topics_seq minvalue 10000; 57 58 create synonym subjects for topics; 53 59 54 60 create table developers ( … … 60 66 primary key (id) 61 67 ); 68 create sequence developers_seq minvalue 10000; 62 69 63 70 create table projects ( … … 67 74 primary key (id) 68 75 ); 76 create sequence projects_seq minvalue 10000; 69 77 70 78 create table developers_projects ( … … 80 88 joined_on date default null 81 89 ); 90 create sequence developers_projects_seq minvalue 10000; 82 91 83 92 create table customers ( … … 91 100 primary key (id) 92 101 ); 102 create sequence customers_seq minvalue 10000; 93 103 94 104 create table movies ( … … 97 107 primary key (movieid) 98 108 ); 109 create sequence movies_seq minvalue 10000; 99 110 100 111 create table subscribers ( … … 103 114 primary key (nick) 104 115 ); 116 create sequence subscribers_seq minvalue 10000; 105 117 106 118 create table booleantests ( … … 109 121 primary key (id) 110 122 ); 123 create sequence booleantests_seq minvalue 10000; 111 124 112 125 create table auto_id_tests ( … … 115 128 primary key (auto_id) 116 129 ); 130 create sequence auto_id_tests_seq minvalue 10000; 117 131 118 132 create table entrants ( … … 121 135 course_id integer not null 122 136 ); 137 create sequence entrants_seq minvalue 10000; 123 138 124 139 create table colnametests ( … … 127 142 primary key (id) 128 143 ); 144 create sequence colnametests_seq minvalue 10000; 129 145 130 146 create table mixins ( … … 153 169 primary key (id) 154 170 ); 171 create sequence mixins_seq minvalue 10000; 155 172 156 173 create table people ( … … 160 177 primary key (id) 161 178 ); 179 create sequence people_seq minvalue 10000; 162 180 163 181 create table binaries ( … … 166 184 primary key (id) 167 185 ); 186 create sequence binaries_seq minvalue 10000; 168 187 169 188 create table computers ( 170 189 id integer not null primary key, 171 190 developer integer not null references developers initially deferred disable, 172 extendedWarranty integer not null 173 ); 191 "extendedWarranty" integer not null 192 ); 193 create sequence computers_seq minvalue 10000; 174 194 175 195 create table posts ( … … 180 200 body varchar(3000) default null 181 201 ); 202 create sequence posts_seq minvalue 10000; 182 203 183 204 create table comments ( … … 187 208 body varchar(3000) default null 188 209 ); 210 create sequence comments_seq minvalue 10000; 189 211 190 212 create table authors ( … … 192 214 name varchar(255) default null 193 215 ); 216 create sequence authors_seq minvalue 10000; 194 217 195 218 create table tasks ( … … 198 221 ending date default null 199 222 ); 223 create sequence tasks_seq minvalue 10000; 200 224 201 225 create table categories ( … … 204 228 type varchar(255) default null 205 229 ); 230 create sequence categories_seq minvalue 10000; 206 231 207 232 create table categories_posts ( … … 209 234 post_id integer not null references posts initially deferred disable 210 235 ); 236 create sequence categories_posts_seq minvalue 10000; 211 237 212 238 create table fk_test_has_pk ( 213 239 id integer not null primary key 214 240 ); 241 create sequence fk_test_has_pk_seq minvalue 10000; 215 242 216 243 create table fk_test_has_fk ( … … 218 245 fk_id integer not null references fk_test_has_fk initially deferred disable 219 246 ); 247 create sequence fk_test_has_fk_seq minvalue 10000; trunk/activerecord/test/fixtures/db_definitions/oci2.drop.sql
r761 r1912 1 1 drop table courses; 2 drop sequence rails_sequence;2 drop sequence courses_seq; trunk/activerecord/test/fixtures/db_definitions/oci2.sql
r761 r1912 1 create sequence rails_sequence minvalue 10000;2 3 1 create table courses ( 4 2 id int not null primary key, 5 3 name varchar(255) not null 6 4 ); 5 6 create sequence courses_seq minvalue 10000;