activerecord-oracle_enhanced-adapter 1.1.7 → 1.1.8

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,17 @@
1
+ == 1.1.8 2008-10-10
2
+
3
+ * Bug fixes:
4
+ * Fixed storing of serialized LOB columns
5
+ * Prevent from SQL injection in :limit and :offset
6
+ * Order by LOB columns (by replacing column with function which returns first 100 characters of LOB)
7
+ * Sequence creation for tables with non-default primary key in create_table block
8
+ * Do count distinct workaround only when composite_primary_keys gem is used
9
+ (otherwise count distinct did not work with ActiveRecord 2.1.1)
10
+ * Fixed rake db:test:clone_structure task
11
+ (see http://rsim.lighthouseapp.com/projects/11468/tickets/11-rake-dbtestclone_structure-fails-in-117)
12
+ * Fixed bug when ActiveRecord::Base.allow_concurrency = true
13
+ (see http://dev.rubyonrails.org/ticket/11134)
14
+
1
15
  == 1.1.7 2008-08-20
2
16
 
3
17
  * Bug fixes:
@@ -27,16 +27,16 @@ namespace :db do
27
27
  redefine_task :clone_structure => [ "db:structure:dump", "db:test:purge" ] do
28
28
  abcs = ActiveRecord::Base.configurations
29
29
  ActiveRecord::Base.establish_connection(:test)
30
- IO.readlines("db/#{RAILS_ENV}_structure.sql").join.split(";\n\n").each do |ddl|
31
- ActiveRecord::Base.connection.execute(ddl)
30
+ IO.readlines("db/#{RAILS_ENV}_structure.sql").join.split("\n\n").each do |ddl|
31
+ ActiveRecord::Base.connection.execute(ddl.chop)
32
32
  end
33
33
  end
34
34
 
35
35
  redefine_task :purge => :environment do
36
36
  abcs = ActiveRecord::Base.configurations
37
37
  ActiveRecord::Base.establish_connection(:test)
38
- ActiveRecord::Base.connection.structure_drop.split(";\n\n").each do |ddl|
39
- ActiveRecord::Base.connection.execute(ddl)
38
+ ActiveRecord::Base.connection.structure_drop.split("\n\n").each do |ddl|
39
+ ActiveRecord::Base.connection.execute(ddl.chop)
40
40
  end
41
41
  end
42
42
 
@@ -81,8 +81,32 @@ begin
81
81
  connection.write_lobs(self.class.table_name, self.class, attributes)
82
82
  end
83
83
  end
84
-
85
84
  private :enhanced_write_lobs
85
+
86
+ class << self
87
+ # RSI: patch ORDER BY to work with LOBs
88
+ def add_order_with_lobs!(sql, order, scope = :auto)
89
+ if connection.is_a?(ConnectionAdapters::OracleEnhancedAdapter)
90
+ order = connection.lob_order_by_expression(self, order) if order
91
+
92
+ orig_scope = scope
93
+ scope = scope(:find) if :auto == scope
94
+ if scope
95
+ new_scope_order = connection.lob_order_by_expression(self, scope[:order])
96
+ if new_scope_order != scope[:order]
97
+ scope = scope.merge(:order => new_scope_order)
98
+ else
99
+ scope = orig_scope
100
+ end
101
+ end
102
+ end
103
+ add_order_without_lobs!(sql, order, scope = :auto)
104
+ end
105
+ private :add_order_with_lobs!
106
+ alias_method :add_order_without_lobs!, :add_order!
107
+ alias_method :add_order!, :add_order_with_lobs!
108
+ end
109
+
86
110
  end
87
111
 
88
112
 
@@ -425,9 +449,11 @@ begin
425
449
  end
426
450
 
427
451
  def add_limit_offset!(sql, options) #:nodoc:
428
- offset = options[:offset] || 0
452
+ # RSI: added to_i for limit and offset to protect from SQL injection
453
+ offset = (options[:offset] || 0).to_i
429
454
 
430
455
  if limit = options[:limit]
456
+ limit = limit.to_i
431
457
  sql.replace "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_ where rownum <= #{offset+limit}) where raw_rnum_ > #{offset}"
432
458
  elsif offset > 0
433
459
  sql.replace "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_) where raw_rnum_ > #{offset}"
@@ -460,8 +486,9 @@ begin
460
486
  id = quote(attributes[klass.primary_key])
461
487
  klass.columns.select { |col| col.sql_type =~ /LOB$/i }.each do |col|
462
488
  value = attributes[col.name]
463
- value = value.to_yaml if col.text? && klass.serialized_attributes[col.name]
489
+ # RSI: changed sequence of next two lines - should check if value is nil before converting to yaml
464
490
  next if value.nil? || (value == '')
491
+ value = value.to_yaml if col.text? && klass.serialized_attributes[col.name]
465
492
  uncached do
466
493
  lob = select_one("SELECT #{col.name} FROM #{table_name} WHERE #{klass.primary_key} = #{id} FOR UPDATE",
467
494
  'Writable Large Object')[col.name]
@@ -470,6 +497,22 @@ begin
470
497
  end
471
498
  end
472
499
 
500
+ # RSI: change LOB column for ORDER BY clause
501
+ # just first 100 characters are taken for ordering
502
+ def lob_order_by_expression(klass, order)
503
+ return order if order.nil?
504
+ changed = false
505
+ new_order = order.to_s.strip.split(/, */).map do |order_by_col|
506
+ column_name, asc_desc = order_by_col.split(/ +/)
507
+ if column = klass.columns.detect { |col| col.name == column_name && col.sql_type =~ /LOB$/i}
508
+ changed = true
509
+ "DBMS_LOB.SUBSTR(#{column_name},100,1) #{asc_desc}"
510
+ else
511
+ order_by_col
512
+ end
513
+ end.join(', ')
514
+ changed ? new_order : order
515
+ end
473
516
 
474
517
  # SCHEMA STATEMENTS ========================================
475
518
  #
@@ -590,10 +633,25 @@ begin
590
633
  end
591
634
  end
592
635
 
593
- def create_table(name, options = {}) #:nodoc:
594
- super(name, options)
636
+ def create_table(name, options = {}, &block) #:nodoc:
637
+ create_sequence = options[:id] != false
638
+ if create_sequence
639
+ super(name, options, &block)
640
+ else
641
+ super(name, options) do |t|
642
+ class <<t
643
+ attr_accessor :create_sequence
644
+ def primary_key(*args)
645
+ self.create_sequence = true
646
+ super(*args)
647
+ end
648
+ end
649
+ result = block.call(t)
650
+ create_sequence = t.create_sequence
651
+ end
652
+ end
595
653
  seq_name = options[:sequence_name] || "#{name}_seq"
596
- execute "CREATE SEQUENCE #{seq_name} START WITH 10000" unless options[:id] == false
654
+ execute "CREATE SEQUENCE #{seq_name} START WITH 10000" if create_sequence
597
655
  end
598
656
 
599
657
  def rename_table(name, new_name) #:nodoc:
@@ -836,7 +894,7 @@ begin
836
894
  def describe(name)
837
895
  @desc ||= @@env.alloc(OCIDescribe)
838
896
  @desc.attrSet(OCI_ATTR_DESC_PUBLIC, -1) if VERSION >= '0.1.14'
839
- @desc.describeAny(@svc, name.to_s, OCI_PTYPE_UNK) rescue raise %Q{"DESC #{name}" failed; does it exist?}
897
+ do_ocicall(@ctx) { @desc.describeAny(@svc, name.to_s, OCI_PTYPE_UNK) } rescue raise %Q{"DESC #{name}" failed; does it exist?}
840
898
  info = @desc.attrGet(OCI_ATTR_PARAM)
841
899
 
842
900
  case info.attrGet(OCI_ATTR_PTYPE)
@@ -3,8 +3,9 @@ module ActiveRecord #:nodoc:
3
3
  module OracleEnhancedCpk #:nodoc:
4
4
 
5
5
  # This mightn't be in Core, but count(distinct x,y) doesn't work for me
6
+ # RSI: return that not supported if composite_primary_keys gem is required
6
7
  def supports_count_distinct? #:nodoc:
7
- false
8
+ @supports_count_distinct ||= ! defined?(CompositePrimaryKeys)
8
9
  end
9
10
 
10
11
  def concat(*columns)
@@ -3,7 +3,7 @@ module ActiveRecord #:nodoc:
3
3
  module OracleEnhancedVersion #:nodoc:
4
4
  MAJOR = 1
5
5
  MINOR = 1
6
- TINY = 7
6
+ TINY = 8
7
7
 
8
8
  STRING = [MAJOR, MINOR, TINY].join('.')
9
9
  end
@@ -976,3 +976,124 @@ describe "OracleEnhancedAdapter assign string to :date and :datetime columns" do
976
976
  end
977
977
 
978
978
  end
979
+
980
+ describe "OracleEnhancedAdapter handling of CLOB columns" do
981
+ before(:all) do
982
+ ActiveRecord::Base.establish_connection(:adapter => "oracle_enhanced",
983
+ :database => "xe",
984
+ :username => "hr",
985
+ :password => "hr")
986
+ @conn = ActiveRecord::Base.connection
987
+ @conn.execute <<-SQL
988
+ CREATE TABLE test_employees (
989
+ employee_id NUMBER(6,0),
990
+ first_name VARCHAR2(20),
991
+ last_name VARCHAR2(25),
992
+ comments CLOB
993
+ )
994
+ SQL
995
+ @conn.execute <<-SQL
996
+ CREATE SEQUENCE test_employees_seq MINVALUE 1
997
+ INCREMENT BY 1 CACHE 20 NOORDER NOCYCLE
998
+ SQL
999
+ class TestEmployee < ActiveRecord::Base
1000
+ set_primary_key :employee_id
1001
+ end
1002
+ end
1003
+
1004
+ after(:all) do
1005
+ Object.send(:remove_const, "TestEmployee")
1006
+ @conn.execute "DROP TABLE test_employees"
1007
+ @conn.execute "DROP SEQUENCE test_employees_seq"
1008
+ end
1009
+
1010
+ before(:each) do
1011
+ end
1012
+
1013
+ it "should create record without CLOB data when attribute is serialized" do
1014
+ TestEmployee.serialize :comments
1015
+ @employee = TestEmployee.create!(
1016
+ :first_name => "First",
1017
+ :last_name => "Last"
1018
+ )
1019
+ @employee.should be_valid
1020
+ end
1021
+
1022
+ it "should order by CLOB column" do
1023
+ @employee = TestEmployee.create!(
1024
+ :first_name => "First",
1025
+ :last_name => "Last",
1026
+ :comments => "comments"
1027
+ )
1028
+ TestEmployee.find(:all, :order => "comments ASC").should_not be_empty
1029
+ TestEmployee.find(:all, :order => " comments ASC ").should_not be_empty
1030
+ TestEmployee.find(:all, :order => "comments").should_not be_empty
1031
+ TestEmployee.find(:all, :order => " comments ").should_not be_empty
1032
+ TestEmployee.find(:all, :order => :comments).should_not be_empty
1033
+ TestEmployee.find(:all, :order => " first_name DESC, last_name ASC ").should_not be_empty
1034
+ end
1035
+
1036
+ end
1037
+
1038
+ describe "OracleEnhancedAdapter table and sequence creation with non-default primary key" do
1039
+ before(:all) do
1040
+ ActiveRecord::Base.establish_connection(:adapter => "oracle_enhanced",
1041
+ :database => "xe",
1042
+ :username => "hr",
1043
+ :password => "hr")
1044
+ ActiveRecord::Schema.define do
1045
+ create_table :keyboards, :force => true, :id => false do |t|
1046
+ t.primary_key :key_number
1047
+ t.string :name
1048
+ end
1049
+ create_table :id_keyboards, :force => true do |t|
1050
+ t.string :name
1051
+ end
1052
+ end
1053
+ class Keyboard < ActiveRecord::Base
1054
+ set_primary_key :key_number
1055
+ end
1056
+ class IdKeyboard < ActiveRecord::Base
1057
+ end
1058
+ end
1059
+
1060
+ after(:all) do
1061
+ ActiveRecord::Schema.define do
1062
+ drop_table :keyboards
1063
+ drop_table :id_keyboards
1064
+ end
1065
+ Object.send(:remove_const, "Keyboard")
1066
+ Object.send(:remove_const, "IdKeyboard")
1067
+ end
1068
+
1069
+ it "should create sequence for non-default primary key" do
1070
+ ActiveRecord::Base.connection.next_sequence_value(Keyboard.sequence_name).should_not be_nil
1071
+ end
1072
+
1073
+ it "should create sequence for default primary key" do
1074
+ ActiveRecord::Base.connection.next_sequence_value(IdKeyboard.sequence_name).should_not be_nil
1075
+ end
1076
+ end
1077
+
1078
+ describe "OracleEnhancedAdapter without composite_primary_keys" do
1079
+
1080
+ before(:all) do
1081
+ ActiveRecord::Base.establish_connection(:adapter => "oracle_enhanced",
1082
+ :database => "xe",
1083
+ :username => "hr",
1084
+ :password => "hr")
1085
+ Object.send(:remove_const, 'CompositePrimaryKeys') if defined?(CompositePrimaryKeys)
1086
+ class Employee < ActiveRecord::Base
1087
+ set_primary_key :employee_id
1088
+ end
1089
+ end
1090
+
1091
+ it "should tell ActiveRecord that count distinct is supported" do
1092
+ ActiveRecord::Base.connection.supports_count_distinct?.should be_true
1093
+ end
1094
+
1095
+ it "should execute correct SQL COUNT DISTINCT statement" do
1096
+ lambda { Employee.count(:employee_id, :distinct => true) }.should_not raise_error
1097
+ end
1098
+
1099
+ end
@@ -3,6 +3,30 @@ require "composite_primary_keys"
3
3
 
4
4
  describe "OracleEnhancedAdapter composite_primary_keys support" do
5
5
 
6
- # Currently testing was done based on composite_primary_keys tests
6
+ before(:all) do
7
+ ActiveRecord::Base.establish_connection(:adapter => "oracle_enhanced",
8
+ :database => "xe",
9
+ :username => "hr",
10
+ :password => "hr")
11
+ class JobHistory < ActiveRecord::Base
12
+ set_table_name "job_history"
13
+ set_primary_keys :employee_id, :start_date
14
+ end
15
+ end
16
+
17
+ after(:all) do
18
+ Object.send(:remove_const, 'CompositePrimaryKeys') if defined?(CompositePrimaryKeys)
19
+ Object.send(:remove_const, 'JobHistory') if defined?(JobHistory)
20
+ end
21
+
22
+ it "should tell ActiveRecord that count distinct is not supported" do
23
+ ActiveRecord::Base.connection.supports_count_distinct?.should be_false
24
+ end
25
+
26
+ it "should execute correct SQL COUNT DISTINCT statement on table with composite primary keys" do
27
+ lambda { JobHistory.count(:distinct => true) }.should_not raise_error
28
+ end
29
+
30
+ # Other testing was done based on composite_primary_keys tests
7
31
 
8
32
  end
data/spec/spec_helper.rb CHANGED
@@ -6,9 +6,9 @@ $:.unshift(File.dirname(__FILE__) + '/../lib')
6
6
  # gem 'activerecord', '=2.0.2'
7
7
  # gem 'actionpack', '=2.0.2'
8
8
  # gem 'activesupport', '=2.0.2'
9
- gem 'activerecord', '=2.1.0'
10
- gem 'actionpack', '=2.1.0'
11
- gem 'activesupport', '=2.1.0'
9
+ gem 'activerecord', '=2.1.1'
10
+ gem 'actionpack', '=2.1.1'
11
+ gem 'activesupport', '=2.1.1'
12
12
  require 'activerecord'
13
13
  require 'actionpack'
14
14
  require 'action_controller/session/active_record_store'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-oracle_enhanced-adapter
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.7
4
+ version: 1.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Raimonds Simanovskis
@@ -9,10 +9,19 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-08-21 00:00:00 +03:00
12
+ date: 2008-10-10 00:00:00 +03:00
13
13
  default_executable:
14
- dependencies: []
15
-
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: hoe
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.7.0
24
+ version:
16
25
  description: Oracle enhaced adapter for Active Record
17
26
  email:
18
27
  - raymonds72@gmail.com