activerecord-oracle_enhanced-adapter 1.1.7 → 1.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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