spiderfw 0.5.11 → 0.5.12

Sign up to get free protection for your applications and to get access to all the features.
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