simpleOracleJDBC 0.2.0 → 0.3.0

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.
@@ -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
+ /