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 +4 -0
- data/VERSION +1 -1
- data/lib/spiderfw/cmd/cmd.rb +0 -1
- data/lib/spiderfw/model/storage/db/adapters/{oci8.rb → oracle.rb} +37 -193
- data/lib/spiderfw/model/storage/db/connectors/jdbc.rb +32 -0
- data/lib/spiderfw/model/storage/db/connectors/jdbc_oracle.rb +347 -0
- data/lib/spiderfw/model/storage/db/connectors/oci8.rb +193 -0
- data/lib/spiderfw/model/storage/db/db.rb +6 -1
- data/lib/spiderfw/model/storage/db/db_storage.rb +7 -0
- data/lib/spiderfw/model/storage.rb +24 -13
- data/lib/spiderfw/utils/monkey/date_time.rb +1 -1
- data/lib/spiderfw.rb +1 -1
- metadata +7 -4
data/CHANGELOG
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.5.
|
1
|
+
0.5.12
|
data/lib/spiderfw/cmd/cmd.rb
CHANGED
@@ -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
|
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
|
-
|
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, "
|
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
|
-
|
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
|
-
|
269
|
-
|
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
|
-
|
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,
|
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
|
-
|
414
|
-
|
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
|
-
|
281
|
+
res.each do |h|
|
431
282
|
primary_keys << h['COLUMN_NAME']
|
432
283
|
end
|
433
|
-
res =
|
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
|
-
|
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
|
-
|
542
|
-
|
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
|
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(:
|
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
|
-
|
25
|
-
when 'oci8'
|
26
|
-
|
24
|
+
:SQLite
|
25
|
+
when 'oci8', 'oracle'
|
26
|
+
:Oracle
|
27
27
|
when 'mysql'
|
28
|
-
|
28
|
+
:Mysql
|
29
29
|
when 'mssql'
|
30
|
-
|
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
|
-
|
42
|
+
:ODBC
|
43
|
+
when 'jdbc'
|
44
|
+
:JDBC
|
45
|
+
when 'oci8'
|
46
|
+
:OCI8
|
37
47
|
end
|
38
|
-
|
39
|
-
if Db.const_defined?(
|
40
|
-
klass = Db.const_get(
|
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
|
-
|
43
|
-
klass
|
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
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:
|
4
|
+
hash: 19
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 5
|
9
|
-
-
|
10
|
-
version: 0.5.
|
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/
|
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
|