do_oracle 0.10.1-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,36 @@
1
+ ENV["RC_ARCHS"] = "" if RUBY_PLATFORM =~ /darwin/
2
+
3
+ # Loads mkmf which is used to make makefiles for Ruby extensions
4
+ require 'mkmf'
5
+
6
+ # need to check dynamically for libraries and include files directories
7
+ def config_value(type)
8
+ case type
9
+ when 'libdir'
10
+ '/usr/local/oracle/instantclient_10_2'
11
+ when 'includedir'
12
+ '/usr/local/oracle/instantclient_10_2/sdk/include'
13
+ end
14
+ end
15
+
16
+ def have_build_env
17
+ # have_library('occi') &&
18
+ # have_library('clntsh') &&
19
+ # have_header('oci.h')
20
+ true
21
+ end
22
+
23
+ # dir_config('oracle-client', config_value('includedir'), config_value('libdir'))
24
+
25
+ if have_build_env
26
+
27
+ $CFLAGS << ' -Wall ' unless RUBY_PLATFORM =~ /mswin/
28
+ if RUBY_VERSION < '1.8.6'
29
+ $CFLAGS << ' -DRUBY_LESS_THAN_186'
30
+ end
31
+
32
+ create_makefile("do_oracle/do_oracle")
33
+ else
34
+ puts 'Could not find Oracle build environment (libraries & headers): Makefile not created'
35
+ exit(1)
36
+ end
data/lib/do_oracle.rb ADDED
@@ -0,0 +1,133 @@
1
+ require 'data_objects'
2
+ if RUBY_PLATFORM =~ /java/
3
+ require 'do_jdbc'
4
+ require 'java'
5
+
6
+ else # MRI and Ruby 1.9
7
+ require 'oci8'
8
+ end
9
+
10
+ begin
11
+ require 'do_oracle/do_oracle'
12
+ rescue LoadError
13
+ if RUBY_PLATFORM =~ /mingw|mswin/ then
14
+ RUBY_VERSION =~ /(\d+.\d+)/
15
+ require "do_oracle/#{$1}/do_oracle"
16
+ else
17
+ raise
18
+ end
19
+ end
20
+
21
+ require 'do_oracle/version'
22
+
23
+ if RUBY_PLATFORM =~ /java/
24
+ # Oracle JDBC driver (ojdbc14.jar or ojdbc5.jar) file should be in JRUBY_HOME/lib or should be in Java class path
25
+ # Register Oracle JDBC driver
26
+ begin
27
+ java.sql.DriverManager.registerDriver Java::oracle.jdbc.OracleDriver.new
28
+ rescue NameError => e
29
+ raise ConnectionError, "Cannot load Oracle JDBC driver, put it (ojdbc14.jar or ojdbc5.jar) in JRUBY_HOME/lib or in the java extension directory or include in Java class path or call jruby with the option -J-Djava.ext.dirs=/path/to/directory/with/oracle/jars"
30
+ end
31
+ # JDBC driver has transactions implementation in Java
32
+
33
+ else # MRI and Ruby 1.9
34
+ require 'do_oracle/transaction'
35
+ end
36
+
37
+ if RUBY_PLATFORM !~ /java/
38
+ module DataObjects
39
+ module Oracle
40
+ class Command
41
+ private
42
+
43
+ def execute(*args)
44
+ oci8_conn = @connection.instance_variable_get("@connection")
45
+ raise ConnectionError, "This connection has already been closed." unless oci8_conn
46
+
47
+ sql, bind_variables = replace_argument_placeholders(@text, args)
48
+ execute_internal(oci8_conn, sql, bind_variables)
49
+ rescue OCIError => e
50
+ raise SQLError.new(e.message, e.code, nil, e.sql, @connection.to_s)
51
+ end
52
+
53
+ # Replace ? placeholders with :n argument placeholders in string of SQL
54
+ # as required by OCI8#exec method
55
+ # Compare number of ? placeholders with number of passed arguments
56
+ # and raise exception if different
57
+ def replace_argument_placeholders(sql_string, args)
58
+ sql = sql_string.dup
59
+ args_count = args.length
60
+ bind_variables = []
61
+
62
+ replacements = 0
63
+ mismatch = false
64
+
65
+ sql.gsub!(/'[^']*'|"[^"]*"|(IS |IS NOT )?\?/) do |x|
66
+ next x unless x =~ /\?$/
67
+ arg = args[replacements]
68
+ replacements += 1
69
+ case arg
70
+ when Array
71
+ i = 0
72
+ "(" << arg.map do |a|
73
+ bind_variables << a
74
+ i += 1
75
+ ":a#{replacements}_#{i}"
76
+ end.join(", ") << ")"
77
+ when Range
78
+ bind_variables << arg.first << arg.last
79
+ ":r#{replacements}_1 AND :r#{replacements}_2"
80
+ when Regexp
81
+ regexp_options = arg.options & Regexp::IGNORECASE > 0 ? "i" : ""
82
+ regexp_options << "m" if arg.options & Regexp::MULTILINE > 0
83
+ bind_variables << arg.source << regexp_options
84
+ ":re#{replacements}_1, :re#{replacements}_2"
85
+ when NilClass
86
+ # if "IS ?" or "IS NOT ?" then replace with NULL
87
+ if $1
88
+ "#{$1}NULL"
89
+ # otherwise pass as bind variable
90
+ else
91
+ bind_variables << arg
92
+ ":#{replacements}"
93
+ end
94
+ else
95
+ bind_variables << arg
96
+ ":#{replacements}"
97
+ end
98
+ end
99
+
100
+ if sql =~ /^\s*INSERT.+RETURNING.+INTO :insert_id\s*$/i
101
+ @insert_id_present = true
102
+ end
103
+
104
+ if args_count != replacements
105
+ raise ArgumentError, "Binding mismatch: #{args_count} for #{replacements}"
106
+ else
107
+ [sql, bind_variables]
108
+ end
109
+
110
+ end
111
+ end
112
+
113
+ class Connection
114
+ def self.oci8_new(user, password, connect_string)
115
+ OCI8.new(user, password, connect_string)
116
+ rescue OCIError => e
117
+ raise ConnectionError.new(e.message, e.code, nil, nil, @connection.to_s)
118
+ end
119
+
120
+ # Quote true, false as 1 and 0
121
+ def quote_boolean(value)
122
+ value ? 1 : 0
123
+ end
124
+
125
+ # for getting Ruby current time zone in C extension
126
+ def self.ruby_time_zone
127
+ ENV['TZ']
128
+ end
129
+ end
130
+
131
+ end
132
+ end
133
+ end
Binary file
Binary file
@@ -0,0 +1,36 @@
1
+ module DataObjects
2
+
3
+ module Oracle
4
+
5
+ class Transaction < DataObjects::Transaction
6
+
7
+ def begin
8
+ connection.instance_variable_get("@connection").autocommit = false
9
+ end
10
+
11
+ def commit
12
+ connection.instance_variable_get("@connection").commit
13
+ ensure
14
+ connection.instance_variable_get("@connection").autocommit = true
15
+ end
16
+
17
+ def rollback
18
+ connection.instance_variable_get("@connection").rollback
19
+ ensure
20
+ connection.instance_variable_get("@connection").autocommit = true
21
+ end
22
+
23
+ def rollback_prepared
24
+ # TODO: what should be done differently?
25
+ rollback
26
+ end
27
+
28
+ def prepare
29
+ # TODO: what should be done here?
30
+ end
31
+
32
+ end
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,5 @@
1
+ module DataObjects
2
+ module Oracle
3
+ VERSION = '0.10.1'.freeze
4
+ end
5
+ end
@@ -0,0 +1,53 @@
1
+ # encoding: utf-8
2
+
3
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
4
+ require 'data_objects/spec/command_spec'
5
+
6
+ describe DataObjects::Oracle::Command do
7
+ behaves_like 'a Command'
8
+
9
+ if JRUBY
10
+ behaves_like 'a Command with async'
11
+ else
12
+ describe 'running queries in parallel' do
13
+
14
+ before do
15
+
16
+ threads = []
17
+ connections = []
18
+ 4.times do |i|
19
+ # by default connection is not non_blocking, need to pass parameter
20
+ connections[i] = DataObjects::Connection.new(CONFIG.uri+"?non_blocking=true")
21
+ end
22
+
23
+ @start = Time.now
24
+ 4.times do |i|
25
+ threads << Thread.new do
26
+ command = connections[i].create_command(CONFIG.sleep)
27
+ result = command.execute_non_query
28
+ end
29
+ end
30
+
31
+ threads.each{|t| t.join }
32
+ @finish = Time.now
33
+
34
+ connections.each {|c| c.close}
35
+ end
36
+
37
+ # after do
38
+ # @connection.close
39
+ # end
40
+
41
+ FINISH_IN_SECONDS = RUBY_VERSION > "1.9" ? 2 : 3
42
+
43
+ it "should finish within #{FINISH_IN_SECONDS} seconds" do
44
+ pending_if("Ruby on Windows doesn't support asynchronious operations", WINDOWS) do
45
+ # puts "DEBUG: execution time = #{@finish - @start} seconds"
46
+ (@finish - @start).should < FINISH_IN_SECONDS
47
+ end
48
+ end
49
+
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,21 @@
1
+ # encoding: utf-8
2
+
3
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
4
+ require 'data_objects/spec/connection_spec'
5
+
6
+ describe DataObjects::Oracle::Connection do
7
+
8
+ before do
9
+ @driver = CONFIG.scheme
10
+ @user = CONFIG.user
11
+ @password = CONFIG.pass
12
+ @host = CONFIG.host
13
+ @port = CONFIG.port
14
+ @database = CONFIG.database
15
+ end
16
+
17
+ behaves_like 'a Connection'
18
+ behaves_like 'a Connection with authentication support'
19
+ # FIXME: behaves_like 'a Connection with JDBC URL support' if JRUBY
20
+ behaves_like 'a Connection via JDNI' if JRUBY
21
+ end
@@ -0,0 +1,12 @@
1
+ # encoding: utf-8
2
+
3
+ # require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
4
+ # require 'data_objects/spec/encoding_spec'
5
+ #
6
+ # describe DataObjects::Oracle::Connection do
7
+ # behaves_like 'a driver supporting different encodings'
8
+ # end
9
+
10
+
11
+ # Oracle does not support dynamic changing of client character set
12
+ # Client character set is set using NLS_LANG environment variable (e.g. equal to AMERICAN_AMERICA.UTF8)
@@ -0,0 +1,8 @@
1
+ # encoding: utf-8
2
+
3
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
4
+ require 'data_objects/spec/reader_spec'
5
+
6
+ describe DataObjects::Oracle::Reader do
7
+ behaves_like 'a Reader'
8
+ end
@@ -0,0 +1,95 @@
1
+ # encoding: utf-8
2
+
3
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
4
+ require 'data_objects/spec/result_spec'
5
+
6
+ describe DataObjects::Oracle::Result do
7
+ behaves_like 'a Result'
8
+ end
9
+
10
+ def current_sequence_value(seq_name)
11
+ reader = @connection.create_command("SELECT #{seq_name}.currval FROM dual").execute_reader
12
+ reader.next!
13
+ value = reader.values.first
14
+ reader.close
15
+ value
16
+ end
17
+
18
+ describe DataObjects::Oracle::Result do
19
+
20
+ setup_test_environment(false)
21
+
22
+ describe 'without using RETURNING' do
23
+
24
+ before do
25
+ @connection = DataObjects::Connection.new(CONFIG.uri)
26
+ @users_seq_value = current_sequence_value("users_seq")
27
+ @result = @connection.create_command("INSERT INTO users (name) VALUES (?)").execute_non_query("monkey")
28
+ end
29
+
30
+ after do
31
+ @connection.close
32
+ end
33
+
34
+ it 'should respond to #affected_rows' do @result.should.respond_to(:affected_rows) end
35
+
36
+ describe 'affected_rows' do
37
+
38
+ it 'should return the number of created rows' do
39
+ @result.affected_rows.should == 1
40
+ end
41
+
42
+ end
43
+
44
+ it 'should respond to #insert_id' do @result.should.respond_to(:insert_id) end
45
+
46
+ describe 'insert_id' do
47
+
48
+ it 'should return nil' do
49
+ @result.insert_id.should.be.nil
50
+ end
51
+
52
+ it 'should be retrievable through currval' do
53
+ # This is actually the 2nd record inserted
54
+ current_sequence_value("users_seq").should == @users_seq_value + 1
55
+ end
56
+
57
+ end
58
+
59
+ end
60
+
61
+ describe 'when using RETURNING' do
62
+
63
+ before do
64
+ @connection = DataObjects::Connection.new(CONFIG.uri)
65
+ @users_seq_value = current_sequence_value("users_seq")
66
+ @result = @connection.create_command("INSERT INTO users (name) VALUES (?) RETURNING id INTO :insert_id").execute_non_query("monkey")
67
+ end
68
+
69
+ after do
70
+ @connection.close
71
+ end
72
+
73
+ it 'should respond to #affected_rows' do @result.should.respond_to(:affected_rows) end
74
+
75
+ describe 'affected_rows' do
76
+
77
+ it 'should return the number of created rows' do
78
+ @result.affected_rows.should == 1
79
+ end
80
+
81
+ end
82
+
83
+ it 'should respond to #insert_id' do @result.should.respond_to(:insert_id) end
84
+
85
+ describe 'insert_id' do
86
+
87
+ it 'should return the generated key value' do
88
+ @result.insert_id.should == @users_seq_value + 1
89
+ end
90
+
91
+ end
92
+
93
+ end
94
+
95
+ end
@@ -0,0 +1,190 @@
1
+ $TESTING=true
2
+ JRUBY = RUBY_PLATFORM =~ /java/
3
+
4
+ require 'rubygems'
5
+ require 'date'
6
+ require 'ostruct'
7
+ require 'fileutils'
8
+
9
+ driver_lib = File.expand_path('../../lib', __FILE__)
10
+ $LOAD_PATH.unshift(driver_lib) unless $LOAD_PATH.include?(driver_lib)
11
+
12
+ # Prepend data_objects/do_jdbc in the repository to the load path.
13
+ # DO NOT USE installed gems, except when running the specs from gem.
14
+ repo_root = File.expand_path('../../..', __FILE__)
15
+ (['data_objects'] << ('do_jdbc' if JRUBY)).compact.each do |lib|
16
+ lib_path = "#{repo_root}/#{lib}/lib"
17
+ $LOAD_PATH.unshift(lib_path) if File.directory?(lib_path) && !$LOAD_PATH.include?(lib_path)
18
+ end
19
+
20
+ require 'data_objects'
21
+ require 'data_objects/spec/bacon'
22
+ require 'do_oracle'
23
+
24
+ DataObjects::Oracle.logger = DataObjects::Logger.new(STDOUT, :off)
25
+ at_exit { DataObjects.logger.flush }
26
+
27
+ # Set default time zone in MRI if not set in environment
28
+ # as otherwise wrong time zone is set for database connection
29
+ ENV['TZ'] ||= 'EET' unless JRUBY
30
+
31
+ CONFIG = OpenStruct.new
32
+ CONFIG.scheme = 'oracle'
33
+ CONFIG.user = ENV['DO_ORACLE_USER'] || 'do_test'
34
+ CONFIG.pass = ENV['DO_ORACLE_PASS'] || 'do_test'
35
+ CONFIG.host = ENV['DO_ORACLE_HOST'] || 'localhost'
36
+ CONFIG.port = ENV['DO_ORACLE_PORT'] || '1521'
37
+ CONFIG.database = ENV['DO_ORACLE_DATABASE'] || '/orcl'
38
+
39
+ CONFIG.uri = ENV["DO_ORACLE_SPEC_URI"] ||"#{CONFIG.scheme}://#{CONFIG.user}:#{CONFIG.pass}@#{CONFIG.host}:#{CONFIG.port}#{CONFIG.database}"
40
+ CONFIG.sleep = "BEGIN SYS.DBMS_LOCK.sleep(seconds => 1); END;"
41
+ CONFIG.testsql = "SELECT 1 FROM dual"
42
+
43
+ module DataObjectsSpecHelpers
44
+
45
+ def drop_table_and_seq(conn, table_name)
46
+ begin
47
+ conn.create_command("DROP TABLE #{table_name}").execute_non_query
48
+ rescue StandardError => error
49
+ raise unless error.to_s =~ /ORA-00942/
50
+ end
51
+ begin
52
+ conn.create_command("DROP SEQUENCE #{table_name}_seq").execute_non_query
53
+ rescue StandardError => error
54
+ raise unless error.to_s =~ /ORA-02289/
55
+ end
56
+ end
57
+
58
+ def create_seq_and_trigger(conn, table_name)
59
+ conn.create_command("CREATE SEQUENCE #{table_name}_seq").execute_non_query
60
+ conn.create_command(<<-EOF).execute_non_query
61
+ CREATE OR REPLACE TRIGGER #{table_name}_pkt
62
+ BEFORE INSERT ON #{table_name} FOR EACH ROW
63
+ BEGIN
64
+ IF inserting THEN
65
+ IF :new.id IS NULL THEN
66
+ SELECT #{table_name}_seq.NEXTVAL INTO :new.id FROM dual;
67
+ END IF;
68
+ END IF;
69
+ END;
70
+ EOF
71
+ end
72
+
73
+ def setup_test_environment(force_setup = false)
74
+ # setup test environment just once
75
+ return if $test_environment_setup_done && !force_setup
76
+ puts "Setting up test environment"
77
+
78
+ conn = DataObjects::Connection.new(CONFIG.uri)
79
+
80
+ drop_table_and_seq(conn, "invoices")
81
+ drop_table_and_seq(conn, "users")
82
+ drop_table_and_seq(conn, "widgets")
83
+
84
+ conn.create_command(<<-EOF).execute_non_query
85
+ CREATE TABLE users (
86
+ id NUMBER(38,0) PRIMARY KEY NOT NULL,
87
+ name VARCHAR(200) default 'Billy',
88
+ fired_at timestamp
89
+ )
90
+ EOF
91
+ create_seq_and_trigger(conn, "users")
92
+
93
+ conn.create_command(<<-EOF).execute_non_query
94
+ CREATE TABLE invoices (
95
+ id NUMBER(38,0) PRIMARY KEY NOT NULL,
96
+ invoice_number VARCHAR2(50) NOT NULL
97
+ )
98
+ EOF
99
+ create_seq_and_trigger(conn, "invoices")
100
+
101
+ conn.create_command(<<-EOF).execute_non_query
102
+ CREATE TABLE widgets (
103
+ id NUMBER(38,0) PRIMARY KEY NOT NULL,
104
+ code CHAR(8) DEFAULT 'A14',
105
+ name VARCHAR2(200) DEFAULT 'Super Widget',
106
+ shelf_location VARCHAR2(4000),
107
+ description VARCHAR2(4000),
108
+ image_data BLOB,
109
+ ad_description VARCHAR2(4000),
110
+ ad_image BLOB,
111
+ whitepaper_text CLOB,
112
+ class_name VARCHAR2(4000),
113
+ cad_drawing BLOB,
114
+ flags NUMBER(1) default 0,
115
+ number_in_stock NUMBER(38,0) DEFAULT 500,
116
+ number_sold NUMBER(38,0) DEFAULT 0,
117
+ super_number NUMBER(38,0) DEFAULT 9223372036854775807,
118
+ weight BINARY_FLOAT DEFAULT 1.23,
119
+ cost1 BINARY_DOUBLE DEFAULT 10.23,
120
+ cost2 NUMBER(8,2) DEFAULT 50.23,
121
+ release_date DATE DEFAULT '2008-02-14',
122
+ release_datetime DATE DEFAULT '2008-02-14 00:31:12',
123
+ release_timestamp TIMESTAMP WITH TIME ZONE DEFAULT '2008-02-14 00:31:12 #{"%+03d" % (Time.local(2008,2,14,0,31,12).utc_offset/3600)}:00'
124
+ )
125
+ EOF
126
+ create_seq_and_trigger(conn, "widgets")
127
+
128
+ command = conn.create_command(<<-EOF)
129
+ insert into widgets(code, name, shelf_location, description, image_data,
130
+ ad_description, ad_image, whitepaper_text,
131
+ class_name, cad_drawing, super_number, weight
132
+ ,release_datetime, release_timestamp)
133
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
134
+ ,?, ?)
135
+ EOF
136
+
137
+ 1.upto(16) do |n|
138
+ # conn.create_command(<<-EOF).execute_non_query
139
+ # insert into widgets(code, name, shelf_location, description, image_data, ad_description, ad_image, whitepaper_text, cad_drawing, super_number, weight) VALUES ('W#{n.to_s.rjust(7,"0")}', 'Widget #{n}', 'A14', 'This is a description', 'IMAGE DATA', 'Buy this product now!', 'AD IMAGE DATA', 'String', 'CAD \\001 \\000 DRAWING', 1234, 13.4);
140
+ # EOF
141
+ # conn.create_command(<<-EOF).execute_non_query
142
+ # insert into widgets(code, name, shelf_location, description, ad_description, whitepaper_text, super_number, weight) VALUES ('W#{n.to_s.rjust(7,"0")}', 'Widget #{n}', 'A14', 'This is a description', 'Buy this product now!', 'String', 1234, 13.4)
143
+ # EOF
144
+ command.execute_non_query(
145
+ "W#{n.to_s.rjust(7,"0")}", "Widget #{n}", 'A14', 'This is a description', ::Extlib::ByteArray.new('IMAGE DATA'),
146
+ 'Buy this product now!', ::Extlib::ByteArray.new('AD IMAGE DATA'), '1234567890'*500,
147
+ 'String', ::Extlib::ByteArray.new("CAD \001 \000 DRAWING"), 1234, 13.4,
148
+ Time.local(2008,2,14,0,31,12), Time.local(2008,2,14,0,31,12)
149
+ )
150
+ end
151
+
152
+ conn.create_command(<<-EOF).execute_non_query
153
+ update widgets set flags = 1 where id = 2
154
+ EOF
155
+
156
+ conn.create_command(<<-EOF).execute_non_query
157
+ update widgets set ad_description = NULL where id = 3
158
+ EOF
159
+
160
+ conn.create_command(<<-EOF).execute_non_query
161
+ update widgets set flags = NULL where id = 4
162
+ EOF
163
+
164
+ conn.create_command(<<-EOF).execute_non_query
165
+ update widgets set cost1 = NULL where id = 5
166
+ EOF
167
+
168
+ conn.create_command(<<-EOF).execute_non_query
169
+ update widgets set cost2 = NULL where id = 6
170
+ EOF
171
+
172
+ conn.create_command(<<-EOF).execute_non_query
173
+ update widgets set release_date = NULL where id = 7
174
+ EOF
175
+
176
+ conn.create_command(<<-EOF).execute_non_query
177
+ update widgets set release_datetime = NULL where id = 8
178
+ EOF
179
+
180
+ conn.create_command(<<-EOF).execute_non_query
181
+ update widgets set release_timestamp = NULL where id = 9
182
+ EOF
183
+
184
+ conn.close
185
+ $test_environment_setup_done = true
186
+ end
187
+
188
+ end
189
+
190
+ include DataObjectsSpecHelpers