simpleOracleJDBC 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,139 @@
1
+ module SimpleOracleJDBC
2
+
3
+ class OraRecord
4
+ include TypeMap
5
+
6
+ attr_reader :ora_type
7
+
8
+ # This must be initialized with the name of the Oracle record type as defined
9
+ # on the database, ie something like:
10
+ #
11
+ # create or replace type t_record as object (
12
+ # p_varchar varchar2(10),
13
+ # p_integer integer
14
+ # );
15
+ #
16
+ # Values must be an array of Ruby objects, or nil. The values in the array
17
+ # will be cast into the appropriate Oracle type depending on the definition
18
+ # of the record defined in Oracle.
19
+ def initialize(ora_type, values)
20
+ @ora_type = ora_type.upcase
21
+ self.values = values
22
+ @descriptor = nil
23
+ @type_attributes = nil
24
+ end
25
+
26
+ # Values must be a Ruby array of objects or nil.
27
+ #
28
+ # While the values can be set in upon object initialization, this method
29
+ # allows them to be changed. The one advantage is that it allows the
30
+ # object descriptor to be reused across many database calls. As this must
31
+ # be queried from the database, it requires 1 database round trip for each new object,
32
+ # but is cached inside the object once it is initialized.
33
+ def values=(value_array)
34
+ if value_array and !value_array.is_a? Array
35
+ raise "The values must be a Ruby array, not #{value_array.class}"
36
+ end
37
+ @values = value_array || Array.new
38
+ end
39
+
40
+ # Given a database connection, a prepared statement and a bind index,
41
+ # this method will bind the array of values (set at object initialization time
42
+ # or by the values= method) to the statement.
43
+ def bind_to_call(conn, stmt, index)
44
+ set_descriptor(conn)
45
+
46
+ # Avoid binding an empty record for OUT parameters
47
+ if @values.length == 0
48
+ return
49
+ end
50
+
51
+ ora_array = convert_to_oracle_struct(conn, @values)
52
+ stmt.set_object(index, ora_array)
53
+ end
54
+
55
+ # Given a ruby array, convert it into the Oracle STRUCT required
56
+ # when binding it to a procedure call
57
+ def convert_to_oracle_struct(conn, input)
58
+ set_descriptor(conn)
59
+
60
+ if @type_attributes.length != input.length
61
+ raise "Not enough values (#{input.length}) for Oracle record (#{@type_attributes.length})"
62
+ end
63
+
64
+ temp_array = Array.new
65
+ @type_attributes.each_with_index do |t, i|
66
+ if t == 'VARCHAR' or t == 'CHAR'
67
+ temp_array.push input[i]
68
+ elsif t == 'RAW'
69
+ temp_array.push ruby_raw_string_as_jdbc_raw(input[i])
70
+ elsif t == 'NUMBER' or t == 'INTEGER'
71
+ temp_array.push ruby_number_as_jdbc_number(input[i])
72
+ elsif t == 'DATE' or t == 'TIMESTAMP'
73
+ temp_array.push ruby_any_date_as_jdbc_date(input[i])
74
+ else
75
+ raise "#{base_type}: Unimplemented Record Type"
76
+ end
77
+ end
78
+ ora_array = STRUCT.new(@descriptor, conn, temp_array.to_java)
79
+ end
80
+
81
+ # Given a database connection, a prepared statement and a bind index,
82
+ # register the bind at that index as an out or inout parameter.
83
+ def register_as_out_parameter(conn, stmt, index)
84
+ set_descriptor(conn)
85
+ stmt.register_out_parameter(index, OracleTypes::STRUCT, @ora_type)
86
+ end
87
+
88
+ # After executing a statement, retrieve the resultant array from Oracle
89
+ # returning a Ruby array of Ruby objects.
90
+ def retrieve_out_value(conn, stmt, index)
91
+ set_descriptor(conn)
92
+ convert_struct_to_ruby(conn, stmt.get_struct(index))
93
+ end
94
+
95
+ def convert_struct_to_ruby(conn, struct)
96
+ set_descriptor(conn)
97
+ final_array = Array.new
98
+
99
+ ora_array = struct.get_attributes.to_a
100
+ ora_array.each_with_index do |v,i|
101
+ base_type = @type_attributes[i]
102
+ if base_type == 'VARCHAR' or base_type == 'CHAR'
103
+ final_array.push v
104
+ elsif base_type == 'RAW'
105
+ # RAW is a bit different as the default returned type is a byte array
106
+ # By extracting the results as 'oracle_attributes' you get a to_string
107
+ # method on the RAW type to turn it into a hex string.
108
+ temp_array = struct.get_oracle_attributes.to_a
109
+ final_array.push oracle_raw_as_string(temp_array[i])
110
+ elsif base_type == 'NUMBER' or base_type == 'INTEGER'
111
+ final_array.push java_number_as_float(v)
112
+ elsif base_type == 'DATE' or base_type == 'TIMESTAMP'
113
+ final_array.push java_date_as_time(v)
114
+ else
115
+ raise "#{base_type}: Unimplemented Record Type"
116
+ end
117
+ end
118
+ final_array
119
+ end
120
+
121
+ private
122
+
123
+ def set_descriptor(conn)
124
+ @descriptor ||= StructDescriptor.createDescriptor(@ora_type, conn);
125
+ set_type_attributes
126
+ end
127
+
128
+ def set_type_attributes
129
+ unless @type_attributes
130
+ @type_attributes = []
131
+ meta = @descriptor.get_meta_data
132
+ 1.upto(meta.get_column_count) do |i|
133
+ @type_attributes.push(meta.get_column_type_name(i))
134
+ end
135
+ end
136
+ end
137
+
138
+ end
139
+ end
@@ -24,29 +24,30 @@ module SimpleOracleJDBC
24
24
  include Binding
25
25
  include ResultSet
26
26
 
27
- attr_reader :statement, :sql, :result_set
27
+ attr_reader :connection, :statement, :sql, :result_set
28
28
 
29
29
  attr_writer :result_set
30
30
 
31
31
  # Creates a new instance of this class. Not intended to be used directly. Use the factory
32
32
  # class methods prepare or execute instead.
33
- def initialize
33
+ def initialize(connection)
34
+ @connection = connection
34
35
  @auto_statement_close = true
35
36
  end
36
37
 
37
38
  # Takes a JDBC connection object and an SQL statement and returns a SimpleOracleJDBC::Sql
38
39
  # object with the prepared statement.
39
40
  def self.prepare(connection, sql)
40
- sql_object = self.new
41
+ sql_object = self.new(connection)
41
42
  sql_object.disable_auto_statement_close
42
- sql_object.prepare(connection,sql)
43
+ sql_object.prepare(sql)
43
44
  end
44
45
 
45
46
  # Takes a JDBC connection object, an SQL statement and an optional list of bind variables and
46
47
  # will prepare and execute the sql statement, returning an SimpleOracle::Sql object.
47
48
  def self.execute(connection, sql, *binds)
48
- sql_object = self.new
49
- sql_object.prepare(connection, sql)
49
+ sql_object = self.new(connection)
50
+ sql_object.prepare(sql)
50
51
  sql_object.execute(*binds)
51
52
  end
52
53
 
@@ -54,9 +55,9 @@ module SimpleOracleJDBC
54
55
  # and a JDBC prepared statement will be stored in @statement.
55
56
  #
56
57
  # This method returns self to allow calls to be chained.
57
- def prepare(connection, sql)
58
+ def prepare(sql)
58
59
  @sql = sql
59
- @statement = connection.prepare_statement(@sql)
60
+ @statement = @connection.prepare_statement(@sql)
60
61
  self
61
62
  end
62
63
 
@@ -0,0 +1,99 @@
1
+ module SimpleOracleJDBC
2
+ module TypeMap
3
+
4
+ def java_date_as_date(v)
5
+ if v
6
+ Date.new(v.get_year+1900, v.get_month+1, v.get_date)
7
+ else
8
+ nil
9
+ end
10
+ end
11
+
12
+ def java_date_as_time(v)
13
+ if v
14
+ Time.at(v.get_time.to_f / 1000)
15
+ else
16
+ nil
17
+ end
18
+ end
19
+
20
+ def java_number_as_float(v)
21
+ if v
22
+ v.double_value
23
+ else
24
+ nil
25
+ end
26
+ end
27
+
28
+ def java_integer_as_integer(v)
29
+ # JRuby automatically converts INT to INT
30
+ v
31
+ end
32
+
33
+ def java_string_as_string(v)
34
+ # JRubyt automatically converts to a Ruby string
35
+ v
36
+ end
37
+
38
+ def oracle_raw_as_string(v)
39
+ if v
40
+ v.string_value
41
+ else
42
+ nil
43
+ end
44
+ end
45
+
46
+ def ruby_date_as_jdbc_date(v)
47
+ if v
48
+ jdbc_date = Java::JavaSql::Date.new(v.strftime("%s").to_f * 1000)
49
+ else
50
+ nil
51
+ end
52
+ end
53
+
54
+ def ruby_time_as_jdbc_timestamp(v)
55
+ if v
56
+ TIMESTAMP.new(Java::JavaSql::Timestamp.new(v.to_f * 1000))
57
+ else
58
+ nil
59
+ end
60
+ end
61
+
62
+ def ruby_any_date_as_jdbc_date(v)
63
+ if v
64
+ if v.is_a? Date
65
+ ruby_date_as_jdbc_date(v)
66
+ elsif v.is_a? Time
67
+ ruby_time_as_jdbc_timestamp(v)
68
+ else
69
+ raise "#{v.class}: unimplemented Ruby date type for arrays. Use Date or Time"
70
+ end
71
+ else
72
+ nil
73
+ end
74
+ end
75
+
76
+ def ruby_number_as_jdbc_number(v)
77
+ if v
78
+ # Avoid warning that appeared in JRuby 1.7.3. There are many signatures of
79
+ # Java::OracleSql::NUMBER and it has to pick one. This causes a warning. This
80
+ # technique works around the warning and forces it to the the signiture with a
81
+ # double input - see https://github.com/jruby/jruby/wiki/CallingJavaFromJRuby
82
+ # under the Constructors section.
83
+ construct = Java::OracleSql::NUMBER.java_class.constructor(Java::double)
84
+ construct.new_instance(v)
85
+ else
86
+ nil
87
+ end
88
+ end
89
+
90
+ def ruby_raw_string_as_jdbc_raw(v)
91
+ if v
92
+ Java::OracleSql::RAW.new(v)
93
+ else
94
+ v
95
+ end
96
+ end
97
+
98
+ end
99
+ end
data/test/README ADDED
@@ -0,0 +1,165 @@
1
+ For the test suit to run the Oracle objects below must be created upfront.
2
+
3
+ ** The test harness will not create them - maybe in the future ... **
4
+
5
+ Right now, these objects are only needed to test the array functionality.
6
+
7
+
8
+ create or replace type t_varchar2_tab is table of varchar2(100);
9
+ /
10
+
11
+ create or replace type t_char_tab is table of char(100);
12
+ /
13
+
14
+ create or replace type t_integer_tab is table of integer;
15
+ /
16
+
17
+ create or replace type t_number_tab is table of number;
18
+ /
19
+
20
+ create or replace type t_date_tab is table of date;
21
+ /
22
+
23
+ create or replace type t_timestamp_tab is table of timestamp;
24
+ /
25
+
26
+ create or replace type t_raw_tab is table of raw(100);
27
+ /
28
+
29
+ create or replace type t_record as object (
30
+ p_varchar varchar2(10),
31
+ p_integer integer,
32
+ p_number number,
33
+ p_char char(10),
34
+ p_date date,
35
+ p_timestamp timestamp,
36
+ p_raw raw(10)
37
+ );
38
+ /
39
+
40
+ create or replace type t_record_tab as table of t_record;
41
+ /
42
+
43
+ create or replace function test_record(i_record t_record)
44
+ return t_record
45
+ is
46
+ begin
47
+ return i_record;
48
+ end;
49
+ /
50
+
51
+ create or replace function test_array_of_records(i_array t_record_tab)
52
+ return t_record_tab
53
+ is
54
+ v_return_value t_record_tab;
55
+ begin
56
+ v_return_value := t_record_tab();
57
+ for i in 1..i_array.count loop
58
+ v_return_value.extend(1);
59
+ v_return_value(v_return_value.count) := i_array(i);
60
+ end loop;
61
+ return v_return_value;
62
+ end;
63
+ /
64
+
65
+
66
+ create or replace function test_array_varchar(i_array t_varchar2_tab)
67
+ return t_varchar2_tab
68
+ is
69
+ v_return_value t_varchar2_tab;
70
+ begin
71
+ v_return_value := t_varchar2_tab();
72
+ for i in 1..i_array.count loop
73
+ v_return_value.extend(1);
74
+ v_return_value(v_return_value.count) := i_array(i);
75
+ end loop;
76
+ return v_return_value;
77
+ end;
78
+ /
79
+
80
+ create or replace function test_array_char(i_array t_char_tab)
81
+ return t_char_tab
82
+ is
83
+ v_return_value t_char_tab;
84
+ begin
85
+ v_return_value := t_char_tab();
86
+ for i in 1..i_array.count loop
87
+ v_return_value.extend(1);
88
+ v_return_value(v_return_value.count) := i_array(i);
89
+ end loop;
90
+ return v_return_value;
91
+ end;
92
+ /
93
+
94
+
95
+ create or replace function test_array_integer(i_array t_integer_tab)
96
+ return t_integer_tab
97
+ is
98
+ v_return_value t_integer_tab;
99
+ begin
100
+ v_return_value := t_integer_tab();
101
+ for i in 1..i_array.count loop
102
+ v_return_value.extend(1);
103
+ v_return_value(v_return_value.count) := i_array(i);
104
+ end loop;
105
+ return v_return_value;
106
+ end;
107
+ /
108
+
109
+
110
+ create or replace function test_array_number(i_array t_number_tab)
111
+ return t_number_tab
112
+ is
113
+ v_return_value t_number_tab;
114
+ begin
115
+ v_return_value := t_number_tab();
116
+ for i in 1..i_array.count loop
117
+ v_return_value.extend(1);
118
+ v_return_value(v_return_value.count) := i_array(i);
119
+ end loop;
120
+ return v_return_value;
121
+ end;
122
+ /
123
+
124
+
125
+ create or replace function test_array_date(i_array t_date_tab)
126
+ return t_date_tab
127
+ is
128
+ v_return_value t_date_tab;
129
+ begin
130
+ v_return_value := t_date_tab();
131
+ for i in 1..i_array.count loop
132
+ v_return_value.extend(1);
133
+ v_return_value(v_return_value.count) := i_array(i);
134
+ end loop;
135
+ return v_return_value;
136
+ end;
137
+ /
138
+
139
+ create or replace function test_array_timestamp(i_array t_timestamp_tab)
140
+ return t_timestamp_tab
141
+ is
142
+ v_return_value t_timestamp_tab;
143
+ begin
144
+ v_return_value := t_timestamp_tab();
145
+ for i in 1..i_array.count loop
146
+ v_return_value.extend(1);
147
+ v_return_value(v_return_value.count) := i_array(i);
148
+ end loop;
149
+ return v_return_value;
150
+ end;
151
+ /
152
+
153
+ create or replace function test_array_raw(i_array t_raw_tab)
154
+ return t_raw_tab
155
+ is
156
+ v_return_value t_raw_tab;
157
+ begin
158
+ v_return_value := t_raw_tab();
159
+ for i in 1..i_array.count loop
160
+ v_return_value.extend(1);
161
+ v_return_value(v_return_value.count) := i_array(i);
162
+ end loop;
163
+ return v_return_value;
164
+ end;
165
+ /