spiderfw 0.5.11 → 0.5.12

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/CHANGELOG CHANGED
@@ -1,3 +1,7 @@
1
+ = 0.5.12
2
+ == 17 June, 2010
3
+ * JRuby support, Oracle JDBC connector
4
+
1
5
  = 0.5.11
2
6
  === 15 June, 2010
3
7
  * Many bugfixes
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.11
1
+ 0.5.12
@@ -61,7 +61,6 @@ module Spider; module CommandLine
61
61
  end
62
62
  end
63
63
  cmd_name ||= 'help'
64
- require 'ruby-debug'
65
64
  if !@cmd.main_command.commands[cmd_name]
66
65
  require 'spiderfw'
67
66
  if Spider.apps_by_short_name[cmd_name] && Spider.apps_by_short_name[cmd_name].const_defined?(:Cmd)
@@ -1,9 +1,8 @@
1
1
  require 'spiderfw/model/storage/db/db_storage'
2
- require 'oci8'
3
2
 
4
3
  module Spider; module Model; module Storage; module Db
5
4
 
6
- class OCI8 < DbStorage
5
+ class Oracle < DbStorage
7
6
  @capabilities = {
8
7
  :autoincrement => false,
9
8
  :sequences => true,
@@ -22,36 +21,7 @@ module Spider; module Model; module Storage; module Db
22
21
  super << Spider::DataTypes::Binary
23
22
  end
24
23
 
25
- def self.new_connection(user, pass, dbname, role)
26
- conn ||= ::OCI8.new(user, pass, dbname, role)
27
- conn.autocommit = true
28
- conn.non_blocking = true
29
- return conn
30
- end
31
-
32
- def self.disconnect(conn)
33
- conn.logoff
34
- end
35
-
36
- def self.connection_alive?(conn)
37
- # TODO: move to ping method when ruby-oci8 2.x is stable
38
- begin
39
- conn.autocommit?
40
- return true
41
- rescue
42
- return false
43
- end
44
- end
45
-
46
- def release
47
- begin
48
- curr[:conn].autocommit = true if curr[:conn]
49
- super
50
- rescue
51
- self.class.remove_connection(curr[:conn], @connection_params)
52
- curr[:conn] = nil
53
- end
54
- end
24
+
55
25
 
56
26
  def parse_url(url)
57
27
  # db:oracle://<username:password>:connect_role@<database>
@@ -64,46 +34,12 @@ module Spider; module Model; module Storage; module Db
64
34
  @role = $3
65
35
  @dbname = $4
66
36
  else
67
- raise ArgumentError, "OCI8 url '#{url}' is invalid"
37
+ raise ArgumentError, "Oracle url '#{url}' is invalid"
68
38
  end
69
39
  @connection_params = [@user, @pass, @dbname, @role]
70
40
  end
71
41
 
72
42
 
73
- def do_start_transaction
74
- return unless transactions_enabled?
75
- connection.autocommit = false
76
- end
77
-
78
- def in_transaction?
79
- return false unless transactions_enabled?
80
- return curr[:conn] && !curr[:conn].autocommit?
81
- end
82
-
83
- def do_commit
84
- return release unless transactions_enabled?
85
- curr[:conn].commit if curr[:conn]
86
- release
87
- end
88
-
89
- def do_rollback
90
- return release unless transactions_enabled?
91
- curr[:conn].rollback
92
- release
93
- end
94
-
95
- def prepare_value(type, value)
96
- value = super
97
- if (type < Spider::Model::BaseModel)
98
- type = type.primary_keys[0].type
99
- end
100
- return OCI8NilValue.new(Spider::Model.ruby_type(type)) if (value == nil)
101
- case type.name
102
- when 'Spider::DataTypes::Binary'
103
- return OCI8::BLOB.new(curr[:conn], value)
104
- end
105
- return value
106
- end
107
43
 
108
44
  def value_for_condition(type, value)
109
45
  return value if value.nil?
@@ -125,83 +61,7 @@ module Spider; module Model; module Storage; module Db
125
61
  return super(type, value)
126
62
  end
127
63
 
128
- def execute(sql, *bind_vars)
129
- begin
130
- if (bind_vars && bind_vars.length > 0)
131
- debug_vars = bind_vars.map{|var| var = var.to_s; var && var.length > 50 ? var[0..50]+"...(#{var.length-50} chars more)" : var}
132
- end
133
- curr[:last_executed] = [sql, bind_vars]
134
- if (Spider.conf.get('storage.db.replace_debug_vars'))
135
- debug("oci8 #{connection} executing: "+sql.gsub(/:(\d+)/){
136
- i = $1.to_i
137
- v = bind_vars[i-1]
138
- dv = debug_vars[i-1]
139
- v.is_a?(String) ? "'#{dv}'" : dv
140
- })
141
- else
142
- debug_vars_str = debug_vars ? debug_vars.join(', ') : ''
143
- debug("oci8 #{connection} executing:\n#{sql}\n[#{debug_vars_str}]")
144
- end
145
- cursor = connection.parse(sql)
146
- return cursor if (!cursor || cursor.is_a?(Fixnum))
147
- bind_vars.each_index do |i|
148
- var = bind_vars[i]
149
- if (var.is_a?(OCI8NilValue))
150
- cursor.bind_param(i+1, nil, var.type, 0)
151
- else
152
- cursor.bind_param(i+1, var)
153
- end
154
- end
155
- res = cursor.exec
156
- have_result = (cursor.type == ::OCI8::STMT_SELECT)
157
- # @cursor = connection.exec(sql, *bind_vars)
158
- if (have_result)
159
- result = []
160
- while (h = cursor.fetch_hash)
161
- h.each do |key, val|
162
- if val.respond_to?(:read)
163
- h[key] = val.read
164
- end
165
- end
166
- if block_given?
167
- yield h
168
- else
169
- result << h
170
- end
171
- end
172
- end
173
- if (have_result)
174
- unless block_given?
175
- result.extend(StorageResult)
176
- curr[:last_result] = result
177
- return result
178
- end
179
- else
180
- return res
181
- end
182
- cursor.close
183
-
184
- rescue => exc
185
- curr[:conn].break if curr[:conn]
186
- rollback! if in_transaction?
187
- #curr[:conn].logoff
188
- release
189
- raise
190
- ensure
191
- cursor.close if cursor
192
- release if curr[:conn] && !in_transaction?
193
- end
194
- end
195
-
196
-
197
- def prepare(sql)
198
- debug("oci8 preparing: #{sql}")
199
- return connection.parse(sql)
200
- end
201
64
 
202
- def execute_statement(stmt, *bind_vars)
203
- stmt.exec(bind_vars)
204
- end
205
65
 
206
66
  def total_rows
207
67
  return nil unless curr[:last_executed]
@@ -251,9 +111,8 @@ module Spider; module Model; module Storage; module Db
251
111
  # Spider::Logger.debug("SQL SELECT:")
252
112
  # Spider::Logger.debug(query)
253
113
  bind_vars = query[:bind_vars] || []
254
- order_on_different_table = false
114
+ query[:order_replacements] ||= {}
255
115
  if query[:limit] # Oracle is so braindead
256
- replaced_fields = {}
257
116
  replace_cnt = 0
258
117
  # add first field to order if none is found; order is needed for limit
259
118
  query[:order] << [query[:keys][0], 'desc'] if query[:order].length < 1
@@ -265,15 +124,18 @@ module Spider; module Model; module Storage; module Db
265
124
  # i = query[:keys].length < 1
266
125
  # end
267
126
  transformed = "O#{replace_cnt += 1}"
268
- replaced_fields[field.to_s] = transformed
269
- order_on_different_table = true if field.is_a?(Spider::Model::Storage::Db::Field) && !query[:tables].include?(field.table)
127
+ query[:order_replacements][field.to_s] = transformed
128
+ if field.is_a?(Spider::Model::Storage::Db::Field) && !query[:tables].include?(field.table)
129
+ query[:order_on_different_table] = true
130
+ end
270
131
  if field.is_a?(FieldFunction)
271
- order_on_different_table = true if field.joins.length > 0
132
+ query[:order_on_different_table] = true if field.joins.length > 0
272
133
  end
273
134
  if (field.is_a?(Spider::Model::Storage::Db::Field) && field.type == 'CLOB')
274
135
  field = "CAST(#{field} as varchar2(100))"
275
136
  end
276
- query[:keys] << "#{field} AS #{transformed}"
137
+
138
+ query[:keys] << Db::FieldExpression.new(field.table, transformed, field.type, :expression => "#{field}")
277
139
  end
278
140
  end
279
141
  keys = sql_keys(query)
@@ -284,7 +146,7 @@ module Spider; module Model; module Storage; module Db
284
146
  where, vals = sql_condition(query)
285
147
  bind_vars += vals
286
148
  sql += "WHERE #{where} " if where && !where.empty?
287
- order = sql_order(query, replaced_fields)
149
+ order = sql_order(query, query[:order_replacements])
288
150
  if (query[:limit] || query[:query_type] == :count)
289
151
  limit = nil
290
152
  if (query[:offset])
@@ -296,7 +158,7 @@ module Spider; module Model; module Storage; module Db
296
158
  bind_vars << query[:limit] + 1
297
159
  end
298
160
  if (!query[:joins].empty?)
299
- data_tables_sql = order_on_different_table ? tables_sql : query[:tables].join(', ')
161
+ data_tables_sql = query[:order_on_different_table] ? tables_sql : query[:tables].join(', ')
300
162
  pk_sql = query[:primary_keys].join(', ')
301
163
  distinct_sql = "SELECT DISTINCT #{pk_sql} FROM #{tables_sql}"
302
164
  distinct_sql += " WHERE #{where}" if where && !where.empty?
@@ -406,31 +268,20 @@ module Spider; module Model; module Storage; module Db
406
268
  end
407
269
 
408
270
  def describe_table(table)
409
- columns = {}
410
271
  primary_keys = []
411
272
  o_foreign_keys = {}
273
+ columns = {}
412
274
  connection do |conn|
413
- t = conn.describe_table(table)
414
- t.columns.each do |c|
415
- col = {
416
- :type => c.data_type.to_s.upcase,
417
- :length => c.data_size,
418
- :precision => c.precision,
419
- :scale => c.scale,
420
- :null => c.nullable?
421
- }
422
- col.delete(:length) if (col[:precision])
423
- columns[c.name] = col
424
- end
425
- res = conn.exec("SELECT cols.table_name, cols.COLUMN_NAME, cols.position, cons.status, cons.owner
275
+ columns = do_describe_table(conn, table)
276
+ res = execute("SELECT cols.table_name, cols.COLUMN_NAME, cols.position, cons.status, cons.owner
426
277
  FROM user_constraints cons, user_cons_columns cols
427
278
  WHERE cons.constraint_type = 'P'
428
279
  AND cons.constraint_name = cols.constraint_name
429
280
  AND cols.table_name = '#{table}'")
430
- while h = res.fetch_hash
281
+ res.each do |h|
431
282
  primary_keys << h['COLUMN_NAME']
432
283
  end
433
- res = conn.exec("SELECT cons.constraint_name as CONSTRAINT_NAME, cols.column_name as REFERENCED_COLUMN,
284
+ res = execute("SELECT cons.constraint_name as CONSTRAINT_NAME, cols.column_name as REFERENCED_COLUMN,
434
285
  cols.table_name as REFERENCED_TABLE, cons.column_name as COLUMN_NAME
435
286
  FROM user_tab_columns col
436
287
  join user_cons_columns cons
@@ -443,7 +294,7 @@ module Spider; module Model; module Storage; module Db
443
294
  and cons.position = cols.position
444
295
  WHERE cc.constraint_type = 'R'
445
296
  AND cons.table_name = '#{table}'")
446
- while h = res.fetch_hash
297
+ res.each do |h|
447
298
  fk_name = h['CONSTRAINT_NAME']
448
299
  o_foreign_keys[fk_name] ||= {:table => h['REFERENCED_TABLE'], :columns => {}}
449
300
  o_foreign_keys[fk_name][:columns][h['COLUMN_NAME']] = h['REFERENCED_COLUMN']
@@ -457,17 +308,6 @@ module Spider; module Model; module Storage; module Db
457
308
 
458
309
  end
459
310
 
460
- def table_exists?(table)
461
- begin
462
- connection do |c|
463
- c.describe_table(table)
464
- end
465
- Spider.logger.debug("TABLE EXISTS #{table}")
466
- return true
467
- rescue OCIError
468
- return false
469
- end
470
- end
471
311
 
472
312
  # Schema methods
473
313
 
@@ -535,28 +375,32 @@ module Spider; module Model; module Storage; module Db
535
375
  return true
536
376
  end
537
377
 
378
+ class OracleNilValue
379
+ attr_accessor :type
380
+
381
+ def initialize(type)
382
+ @type = type
383
+ @type = Fixnum if @type == TrueClass || @type == FalseClass
384
+ end
385
+
386
+ def to_s
387
+ 'NULL'
388
+ end
389
+
390
+ end
391
+
538
392
 
539
393
  end
540
394
 
541
- class OCI8NilValue
542
- attr_accessor :type
543
-
544
- def initialize(type)
545
- @type = type
546
- @type = Fixnum if @type == TrueClass || @type == FalseClass
547
- end
548
-
549
- def to_s
550
- 'NULL'
551
- end
552
-
553
- end
395
+
396
+
397
+
554
398
 
555
399
  ###############################
556
400
  # Exceptions #
557
401
  ###############################
558
402
 
559
- class OCI8Exception < RuntimeError
403
+ class OracleException < RuntimeError
560
404
  end
561
405
 
562
406
  end; end; end; end
@@ -0,0 +1,32 @@
1
+ module Spider; module Model; module Storage; module Db; module Connectors
2
+
3
+ module JDBC
4
+ Mutex = java.lang.Object.new
5
+ DriverManager = java.sql.DriverManager
6
+ Statement = java.sql.Statement
7
+ Types = java.sql.Types
8
+
9
+
10
+ def self.driver_class(name)
11
+ driver_class ||= begin
12
+ driver_class_const = (name[0...1].capitalize + name[1..name.length]).gsub(/\./, '_')
13
+ JDBC::Mutex.synchronized do
14
+ unless JDBC.const_defined?(driver_class_const)
15
+ driver_class_name = name
16
+ JDBC.module_eval do
17
+ include_class(driver_class_name) { driver_class_const }
18
+ end
19
+ end
20
+ end
21
+ JDBC.const_get(driver_class_const)
22
+ end
23
+ JDBC::DriverManager.registerDriver(driver_class)
24
+ @driver_classes ||= {}
25
+ @driver_classes[name] = driver_class
26
+ driver_class
27
+ end
28
+
29
+ end
30
+
31
+
32
+ end; end; end; end; end
@@ -0,0 +1,347 @@
1
+ require 'spiderfw/model/storage/db/connectors/jdbc'
2
+
3
+ module Spider; module Model; module Storage; module Db; module Connectors
4
+
5
+ module JDBCOracle
6
+ include Connectors::JDBC
7
+
8
+ def self.included(klass)
9
+ klass.extend(ClassMethods)
10
+ end
11
+
12
+ RUBY_CLASS_TO_SQL_TYPE = {
13
+ Fixnum => java.sql.Types::INTEGER,
14
+ Bignum => java.sql.Types::INTEGER,
15
+ Integer => java.sql.Types::INTEGER,
16
+ Float => java.sql.Types::FLOAT,
17
+ BigDecimal => java.sql.Types::NUMERIC,
18
+ String => java.sql.Types::VARCHAR,
19
+ Java::OracleSql::CLOB => Java::oracle.jdbc.OracleTypes::CLOB,
20
+ Java::OracleSql::BLOB => Java::oracle.jdbc.OracleTypes::BLOB,
21
+ Date => java.sql.Types::DATE,
22
+ Time => java.sql.Types::TIMESTAMP,
23
+ DateTime => java.sql.Types::DATE,
24
+ Java::OracleSql::ARRAY => Java::oracle.jdbc.OracleTypes::ARRAY,
25
+ Array => Java::oracle.jdbc.OracleTypes::ARRAY,
26
+ Java::OracleSql::STRUCT => Java::oracle.jdbc.OracleTypes::STRUCT,
27
+ Hash => Java::oracle.jdbc.OracleTypes::STRUCT,
28
+ java.sql.ResultSet => Java::oracle.jdbc.OracleTypes::CURSOR,
29
+ }
30
+
31
+ module ClassMethods
32
+
33
+ def new_connection(user, pass, dbname, role)
34
+ driver = Connectors::JDBC.driver_class('oracle.jdbc.driver.OracleDriver')
35
+ host = nil; port = nil; sid = nil
36
+ if dbname =~ /(.+)(?::(\d+))?\/(.+)/
37
+ host = $1
38
+ port = $2
39
+ sid = $3
40
+ else
41
+ raise ArgumentError, "Oracle db name must be in the host:port/SID form"
42
+ end
43
+ port ||= 1521
44
+ url = "jdbc:oracle:thin:@#{host}:#{port}:#{sid}"
45
+ conn = begin
46
+ Jdbc::DriverManager.getConnection(url, user, pass)
47
+ rescue => exc
48
+ # bypass DriverManager to get around problem with dynamically loaded jdbc drivers
49
+ props = java.util.Properties.new
50
+ props.setProperty("user", user)
51
+ props.setProperty("password", pass)
52
+ driver.new.connect(url, props)
53
+ end
54
+ conn.setAutoCommit(true)
55
+ return conn
56
+ end
57
+
58
+ def connection_alive?(conn)
59
+ conn.pingDatabase()
60
+ end
61
+
62
+ end
63
+
64
+ def release
65
+ begin
66
+ curr[:conn].setAutoCommit(true) if curr[:conn]
67
+ super
68
+ rescue
69
+ self.class.remove_connection(curr[:conn], @connection_params)
70
+ curr[:conn] = nil
71
+ end
72
+ end
73
+
74
+ def do_start_transaction
75
+ return unless transactions_enabled?
76
+ connection.setAutoCommit(false)
77
+ end
78
+
79
+ def in_transaction?
80
+ return false unless transactions_enabled?
81
+ return curr[:conn] && !curr[:conn].getAutoCommit()
82
+ end
83
+
84
+ def do_commit
85
+ return release unless transactions_enabled?
86
+ curr[:conn].commit if curr[:conn]
87
+ release
88
+ end
89
+
90
+ def do_rollback
91
+ return release unless transactions_enabled?
92
+ curr[:conn].rollback
93
+ release
94
+ end
95
+
96
+ def value_to_mapper(type, value)
97
+ return value if value.nil?
98
+ case type.name
99
+ when 'Time', 'Date', 'DateTime'
100
+ return nil unless value
101
+ d = value.dateValue
102
+ t = value.timeValue
103
+ value = Time.local(d.year + 1900, d.month + 1, d.date, t.hours, t.minutes, t.seconds)
104
+ return value.to_datetime if type == DateTime
105
+ return value.to_date
106
+ when 'Spider::DataTypes::Text'
107
+ if value.isEmptyLob
108
+ value = nil
109
+ else
110
+ value = value.getSubString(1, value.length)
111
+ end
112
+ when 'Spider::DataTypes::Binary'
113
+ if value.isEmptyLob
114
+ nil
115
+ else
116
+ String.from_java_bytes(value.getBytes(1, value.length))
117
+ end
118
+ when 'Spider::DataTypes::Decimal', 'BigDecimal'
119
+ value = value.to_s
120
+ end
121
+ return super(type, value)
122
+ end
123
+
124
+ def prepare_value(type, value)
125
+ return Oracle::OracleNilValue.new(Spider::Model.ruby_type(type)) if (value == nil)
126
+ case type.name
127
+ when 'Spider::DataTypes::Decimal', 'BigDecimal'
128
+ java_bigdecimal(value)
129
+ when 'Spider::DataTypes::Bool'
130
+ value ? 1 : 0
131
+ when 'Date', 'DateTime'
132
+ java_date(value)
133
+ when 'Time'
134
+ java_timestamp(value)
135
+ when 'Spider::DataTypes::Text'
136
+ if value
137
+ clob = Java::OracleSql::CLOB.createTemporary(connection, false, Java::OracleSql::CLOB::DURATION_SESSION)
138
+ clob.setString(1, value)
139
+ clob
140
+ else
141
+ Java::OracleSql::CLOB.getEmptyCLOB
142
+ end
143
+ when 'Spider::DataTypes::Binary'
144
+ if value
145
+ blob = Java::OracleSql::BLOB.createTemporary(connection, false, Java::OracleSql::BLOB::DURATION_SESSION)
146
+ blob.setBytes(1, value.to_java_bytes)
147
+ blob
148
+ else
149
+ Java::OracleSql::BLOB.getEmptyBLOB
150
+ end
151
+ else
152
+ super(type, value)
153
+ end
154
+ end
155
+
156
+
157
+ def set_bind_variable(stmt, i, val)
158
+ method = nil
159
+ if val.is_a?(Oracle::OracleNilValue)
160
+ type = RUBY_CLASS_TO_SQL_TYPE[val.type] || java.sql.Types::VARCHAR
161
+ return stmt.setNull(i, type)
162
+ else
163
+ method = case val.class.name
164
+ when 'Fixnum', 'Float'
165
+ :setInt
166
+ when 'Java::JavaMath::BigDecimal'
167
+ :setBigDecimal
168
+ when 'String'
169
+ :setString
170
+ when 'Java::OracleSql::CLOB'
171
+ :setClob
172
+ when 'Java::OracleSql::BLOB'
173
+ :setBlob
174
+ when 'Java::OracleSql::DATE'
175
+ :setDATE
176
+ when 'Java::OracleSql::Timestamp'
177
+ :setTimestamp
178
+ end
179
+ end
180
+ raise "Can't find how to bind variable #{val}" unless method
181
+ stmt.send(method, i, val)
182
+ end
183
+
184
+ def value_from_resultset(res, i, type)
185
+ method = case type
186
+ when :INTEGER, :SMALLINT, :TINYING, :BIGINTEGER, :NUMBER, :NUMERIC
187
+ :getInt
188
+ when :FLOAT, :REAL
189
+ :getFloat
190
+ when :DECIMAL
191
+ :getBigDecimal
192
+ when :VARCHAR, :VARCHAR2, :LONGVARCHAR, :NCHAR, :CHAR
193
+ :getString
194
+ when :CLOB
195
+ :getClob
196
+ when :DATE, :TIME
197
+ :getDATE
198
+ when :TIMESTAMP
199
+ :getTimestamp
200
+ else
201
+ raise "Don't know how to convert Oracle value of type #{type}"
202
+ end
203
+ res.send(method, i)
204
+ end
205
+
206
+
207
+ def execute(sql, *bind_vars)
208
+ begin
209
+ if (bind_vars && bind_vars.length > 0)
210
+ debug_vars = bind_vars.map{|var| var = var.to_s; var && var.length > 50 ? var[0..50]+"...(#{var.length-50} chars more)" : var}
211
+ end
212
+ curr[:last_executed] = [sql, bind_vars]
213
+ if (Spider.conf.get('storage.db.replace_debug_vars'))
214
+ debug("oci8 #{connection} executing: "+sql.gsub(/:(\d+)/){
215
+ i = $1.to_i
216
+ v = bind_vars[i-1]
217
+ dv = debug_vars[i-1]
218
+ v.is_a?(String) ? "'#{dv}'" : dv
219
+ })
220
+ else
221
+ debug_vars_str = debug_vars ? debug_vars.join(', ') : ''
222
+ debug("oci8 #{connection} executing:\n#{sql}\n[#{debug_vars_str}]")
223
+ end
224
+ query = curr[:last_query]
225
+ stmt = connection.prepareStatement(sql)
226
+ return unless stmt
227
+ bind_vars.each_index do |i|
228
+ set_bind_variable(stmt, i+1, bind_vars[i])
229
+ end
230
+ res = nil
231
+ if stmt.execute() # false means this is an update query
232
+ res = stmt.getResultSet()
233
+ end
234
+ if (res)
235
+ result = []
236
+ metadata = res.getMetaData
237
+ column_count = metadata.getColumnCount
238
+ column_names = []
239
+ column_types = []
240
+ 1.upto(column_count) do |i|
241
+ column_names[i] = metadata.getColumnName(i)
242
+ column_types[i] = metadata.getColumnTypeName(i).to_sym
243
+ end
244
+
245
+ while res.next()
246
+ h = {}
247
+ 1.upto(column_count) do |i|
248
+ h[column_names[i]] = value_from_resultset(res, i, column_types[i])
249
+ end
250
+ if block_given?
251
+ yield h
252
+ else
253
+ result << h
254
+ end
255
+ end
256
+ res.close
257
+ end
258
+ if (res)
259
+ unless block_given?
260
+ result.extend(StorageResult)
261
+ curr[:last_result] = result
262
+ return result
263
+ end
264
+ else
265
+ return res
266
+ end
267
+ stmt.close
268
+ rescue => exc
269
+ stmt.cancel if stmt
270
+ # curr[:conn].break if curr[:conn]
271
+ rollback! if in_transaction?
272
+ #curr[:conn].logoff
273
+ release
274
+ raise
275
+ ensure
276
+ stmt.close if stmt
277
+ release if curr[:conn] && !in_transaction?
278
+ end
279
+ end
280
+
281
+
282
+ def prepare(sql)
283
+ debug("oci8 preparing: #{sql}")
284
+ return connection.prepareStatement(sql)
285
+ end
286
+
287
+ def execute_statement(stmt, *bind_vars)
288
+ bind_vars.each_index do |i|
289
+ set_bind_variable(stmt, i+1, bind_vars[i])
290
+ end
291
+ stmt.execute()
292
+ end
293
+
294
+
295
+ def table_exists?(table)
296
+ connection do |c|
297
+ res = get_table_metadata(c, table)
298
+ while res.next()
299
+ return true
300
+ end
301
+ return false
302
+ end
303
+ end
304
+
305
+ def do_describe_table(conn, table)
306
+ md = get_db_metadata(conn)
307
+ res = md.getColumns(nil, @user.upcase, table, nil)
308
+ columns = {}
309
+ while res.next()
310
+ col_name = res.getString("COLUMN_NAME")
311
+ col = {
312
+ :type => res.getString("TYPE_NAME"),
313
+ :length => res.getInt("COLUMN_SIZE"),
314
+ :precision => res.getInt("DECIMAL_DIGITS"),
315
+ }
316
+ col.delete(:length) if (col[:precision])
317
+ columns[col_name] = col
318
+ end
319
+ columns
320
+ end
321
+
322
+
323
+ def get_db_metadata(conn)
324
+ @db_metadata ||= conn.getMetaData()
325
+ end
326
+
327
+ def get_table_metadata(conn, table)
328
+ get_db_metadata(conn).getTables(nil, @user.upcase, table, nil)
329
+ end
330
+
331
+ def java_date(value)
332
+ value && Java::oracle.sql.DATE.new(value.strftime("%Y-%m-%d %H:%M:%S"))
333
+ end
334
+
335
+ def java_timestamp(value)
336
+ value && Java::java.sql.Timestamp.new(value.year-1900, value.month-1, value.day, value.hour, value.min, value.sec, value.usec * 1000)
337
+ end
338
+
339
+ def java_bigdecimal(value)
340
+ value && java.math.BigDecimal.new(value.to_s)
341
+ end
342
+
343
+
344
+ end
345
+
346
+
347
+ end; end; end; end; end
@@ -0,0 +1,193 @@
1
+ require 'oci8'
2
+
3
+ module Spider; module Model; module Storage; module Db; module Connectors
4
+
5
+ module OCI8
6
+
7
+ def self.included(klass)
8
+ klass.extend(ClassMethods)
9
+ end
10
+
11
+ module ClassMethods
12
+
13
+ def new_connection(user, pass, dbname, role)
14
+ conn ||= ::OCI8.new(user, pass, dbname, role)
15
+ conn.autocommit = true
16
+ conn.non_blocking = true
17
+ return conn
18
+ end
19
+
20
+ def disconnect(conn)
21
+ conn.logoff
22
+ end
23
+
24
+ def connection_alive?(conn)
25
+ begin
26
+ conn.autocommit?
27
+ return true
28
+ rescue
29
+ return false
30
+ end
31
+ end
32
+
33
+ end
34
+
35
+ def release
36
+ begin
37
+ curr[:conn].autocommit = true if curr[:conn]
38
+ super
39
+ rescue
40
+ self.class.remove_connection(curr[:conn], @connection_params)
41
+ curr[:conn] = nil
42
+ end
43
+ end
44
+
45
+ def do_start_transaction
46
+ return unless transactions_enabled?
47
+ connection.autocommit = false
48
+ end
49
+
50
+ def in_transaction?
51
+ return false unless transactions_enabled?
52
+ return curr[:conn] && !curr[:conn].autocommit?
53
+ end
54
+
55
+ def do_commit
56
+ return release unless transactions_enabled?
57
+ curr[:conn].commit if curr[:conn]
58
+ release
59
+ end
60
+
61
+ def do_rollback
62
+ return release unless transactions_enabled?
63
+ curr[:conn].rollback
64
+ release
65
+ end
66
+
67
+ def prepare_value(type, value)
68
+ value = super
69
+ if (type < Spider::Model::BaseModel)
70
+ type = type.primary_keys[0].type
71
+ end
72
+ return Oracle::OracleNilValue.new(Spider::Model.ruby_type(type)) if (value == nil)
73
+ case type.name
74
+ when 'Spider::DataTypes::Binary'
75
+ return OCI8::BLOB.new(curr[:conn], value)
76
+ end
77
+ return value
78
+ end
79
+
80
+
81
+ def execute(sql, *bind_vars)
82
+ begin
83
+ if (bind_vars && bind_vars.length > 0)
84
+ debug_vars = bind_vars.map{|var| var = var.to_s; var && var.length > 50 ? var[0..50]+"...(#{var.length-50} chars more)" : var}
85
+ end
86
+ curr[:last_executed] = [sql, bind_vars]
87
+ if (Spider.conf.get('storage.db.replace_debug_vars'))
88
+ debug("oci8 #{connection} executing: "+sql.gsub(/:(\d+)/){
89
+ i = $1.to_i
90
+ v = bind_vars[i-1]
91
+ dv = debug_vars[i-1]
92
+ v.is_a?(String) ? "'#{dv}'" : dv
93
+ })
94
+ else
95
+ debug_vars_str = debug_vars ? debug_vars.join(', ') : ''
96
+ debug("oci8 #{connection} executing:\n#{sql}\n[#{debug_vars_str}]")
97
+ end
98
+ cursor = connection.parse(sql)
99
+ return cursor if (!cursor || cursor.is_a?(Fixnum))
100
+ bind_vars.each_index do |i|
101
+ var = bind_vars[i]
102
+ if (var.is_a?(Oracle::OracleNilValue))
103
+ cursor.bind_param(i+1, nil, var.type, 0)
104
+ else
105
+ cursor.bind_param(i+1, var)
106
+ end
107
+ end
108
+ res = cursor.exec
109
+ have_result = (cursor.type == ::OCI8::STMT_SELECT)
110
+ # @cursor = connection.exec(sql, *bind_vars)
111
+ if (have_result)
112
+ result = []
113
+ while (h = cursor.fetch_hash)
114
+ h.each do |key, val|
115
+ if val.respond_to?(:read)
116
+ h[key] = val.read
117
+ end
118
+ end
119
+ if block_given?
120
+ yield h
121
+ else
122
+ result << h
123
+ end
124
+ end
125
+ end
126
+ if (have_result)
127
+ unless block_given?
128
+ result.extend(StorageResult)
129
+ curr[:last_result] = result
130
+ return result
131
+ end
132
+ else
133
+ return res
134
+ end
135
+ cursor.close
136
+
137
+ rescue => exc
138
+ curr[:conn].break if curr[:conn]
139
+ rollback! if in_transaction?
140
+ #curr[:conn].logoff
141
+ release
142
+ raise
143
+ ensure
144
+ cursor.close if cursor
145
+ release if curr[:conn] && !in_transaction?
146
+ end
147
+ end
148
+
149
+
150
+ def prepare(sql)
151
+ debug("oci8 preparing: #{sql}")
152
+ return connection.parse(sql)
153
+ end
154
+
155
+ def execute_statement(stmt, *bind_vars)
156
+ stmt.exec(bind_vars)
157
+ end
158
+
159
+
160
+ def table_exists?(table)
161
+ begin
162
+ connection do |c|
163
+ c.describe_table(table)
164
+ end
165
+ Spider.logger.debug("TABLE EXISTS #{table}")
166
+ return true
167
+ rescue OCIError
168
+ return false
169
+ end
170
+ end
171
+
172
+ def do_describe_table(conn, table)
173
+ columns = {}
174
+ t = conn.describe_table(table)
175
+ t.columns.each do |c|
176
+ col = {
177
+ :type => c.data_type.to_s.upcase,
178
+ :length => c.data_size,
179
+ :precision => c.precision,
180
+ :scale => c.scale,
181
+ :null => c.nullable?
182
+ }
183
+ col.delete(:length) if (col[:precision])
184
+ columns[c.name] = col
185
+ end
186
+ columns
187
+ end
188
+
189
+
190
+ end
191
+
192
+
193
+ end; end; end; end; end
@@ -1,13 +1,18 @@
1
1
  module Spider; module Model; module Storage
2
2
 
3
3
  module Db
4
+ module Connectors
5
+ end
4
6
 
5
7
  end
6
8
 
7
9
  Db.autoload(:DbSchema, 'spiderfw/model/storage/db/db_schema')
8
10
  Db.autoload(:SQLite, 'spiderfw/model/storage/db/adapters/sqlite')
9
- Db.autoload(:OCI8, 'spiderfw/model/storage/db/adapters/oci8')
11
+ Db.autoload(:Oracle, 'spiderfw/model/storage/db/adapters/oracle')
10
12
  Db.autoload(:Mysql, 'spiderfw/model/storage/db/adapters/mysql')
11
13
  Db.autoload(:MSSQL, 'spiderfw/model/storage/db/adapters/mssql')
14
+ Db::Connectors.autoload(:ODBC, 'spiderfw/model/storage/db/connectors/odbc')
15
+ Db::Connectors.autoload(:OCI8, 'spiderfw/model/storage/db/connectors/oci8')
16
+ Db::Connectors.autoload(:JDBCOracle, 'spiderfw/model/storage/db/connectors/jdbc_oracle')
12
17
 
13
18
  end; end; end
@@ -77,6 +77,13 @@ module Spider; module Model; module Storage; module Db
77
77
  raise "Virtual"
78
78
  end
79
79
 
80
+ def inherited(subclass)
81
+ subclass.instance_variable_set("@reserved_keywords", @reserved_keywords)
82
+ subclass.instance_variable_set("@type_synonyms", @type_synonyms)
83
+ subclass.instance_variable_set("@safe_conversions", @safe_conversions)
84
+ subclass.instance_variable_set("@capabilities", @capabilities)
85
+ end
86
+
80
87
  end
81
88
 
82
89
  def curr
@@ -19,28 +19,39 @@ module Spider; module Model
19
19
  adapter = $2
20
20
  url = "#{adapter}://#{rest}"
21
21
  end
22
- case adapter
22
+ class_name = case adapter
23
23
  when 'sqlite'
24
- class_name = :SQLite
25
- when 'oci8'
26
- class_name = :OCI8
24
+ :SQLite
25
+ when 'oci8', 'oracle'
26
+ :Oracle
27
27
  when 'mysql'
28
- class_name = :Mysql
28
+ :Mysql
29
29
  when 'mssql'
30
- class_name = :MSSQL
30
+ :MSSQL
31
31
  end
32
32
  klass = Db.const_get(class_name)
33
+ unless connector
34
+ connector = case adapter
35
+ when 'oci8', 'oracle'
36
+ RUBY_PLATFORM =~ /java/ ? 'jdbc' : 'oci8'
37
+ end
38
+ end
33
39
  if (connector)
34
- case connector
40
+ conn_mod_name = case connector
35
41
  when 'odbc'
36
- conn_mod = :ODBC
42
+ :ODBC
43
+ when 'jdbc'
44
+ :JDBC
45
+ when 'oci8'
46
+ :OCI8
37
47
  end
38
- conn_class = "#{conn_mod}#{class_name}"
39
- if Db.const_defined?(conn_class)
40
- klass = Db.const_get(conn_class)
48
+ full_name = "#{conn_mod_name}#{class_name}"
49
+ if Db.const_defined?(full_name)
50
+ klass = Db.const_get(full_name)
41
51
  else
42
- klass = Db.const_set(conn_class, Class.new(klass))
43
- klass.instance_eval{ include Db::Connectors.const_get(conn_mod)}
52
+ conn_mod = Db::Connectors.const_defined?(full_name) ? Db::Connectors.const_get(full_name) : Db::Connectors.const_get(conn_mod_name)
53
+ klass = Db.const_set(full_name, Class.new(klass))
54
+ klass.instance_eval{ include conn_mod }
44
55
  end
45
56
  end
46
57
  Thread.current[:storages][type][url] = klass.new(url)
@@ -31,7 +31,7 @@ class Date
31
31
  private
32
32
  def to_time(dest, method)
33
33
  #Convert a fraction of a day to a number of microseconds
34
- usec = (dest.sec_fraction * 60 * 60 * 24 * (10**6)).to_i
34
+ usec = (dest.send(:sec_fraction) * 60 * 60 * 24 * (10**6)).to_i
35
35
  Time.send(method, dest.year, dest.month, dest.day, dest.hour, dest.min,
36
36
  dest.sec, usec)
37
37
  end
data/lib/spiderfw.rb CHANGED
@@ -484,7 +484,7 @@ module Spider
484
484
  Debugger.post_mortem
485
485
  end
486
486
  require 'ruby-prof'
487
- rescue LoadError; end
487
+ rescue LoadError, RuntimeError; end
488
488
  end
489
489
  end
490
490
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spiderfw
3
3
  version: !ruby/object:Gem::Version
4
- hash: 29
4
+ hash: 19
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 5
9
- - 11
10
- version: 0.5.11
9
+ - 12
10
+ version: 0.5.12
11
11
  platform: ruby
12
12
  authors:
13
13
  - Ivan Pirlik
@@ -840,8 +840,11 @@ files:
840
840
  - lib/spiderfw/model/storage/base_storage.rb
841
841
  - lib/spiderfw/model/storage/db/adapters/mssql.rb
842
842
  - lib/spiderfw/model/storage/db/adapters/mysql.rb
843
- - lib/spiderfw/model/storage/db/adapters/oci8.rb
843
+ - lib/spiderfw/model/storage/db/adapters/oracle.rb
844
844
  - lib/spiderfw/model/storage/db/adapters/sqlite.rb
845
+ - lib/spiderfw/model/storage/db/connectors/jdbc.rb
846
+ - lib/spiderfw/model/storage/db/connectors/jdbc_oracle.rb
847
+ - lib/spiderfw/model/storage/db/connectors/oci8.rb
845
848
  - lib/spiderfw/model/storage/db/connectors/odbc.rb
846
849
  - lib/spiderfw/model/storage/db/db.rb
847
850
  - lib/spiderfw/model/storage/db/db_connection_pool.rb