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 +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
|