sequel 3.21.0 → 3.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +485 -3
- data/README.rdoc +20 -6
- data/Rakefile +37 -19
- data/bin/sequel +1 -5
- data/doc/association_basics.rdoc +249 -43
- data/doc/dataset_basics.rdoc +4 -4
- data/doc/dataset_filtering.rdoc +8 -0
- data/doc/mass_assignment.rdoc +54 -0
- data/doc/migration.rdoc +15 -538
- data/doc/model_hooks.rdoc +72 -27
- data/doc/opening_databases.rdoc +89 -50
- data/doc/prepared_statements.rdoc +40 -13
- data/doc/reflection.rdoc +8 -2
- data/doc/release_notes/3.22.0.txt +39 -0
- data/doc/release_notes/3.23.0.txt +172 -0
- data/doc/release_notes/3.24.0.txt +420 -0
- data/doc/release_notes/3.25.0.txt +88 -0
- data/doc/release_notes/3.26.0.txt +88 -0
- data/doc/release_notes/3.27.0.txt +82 -0
- data/doc/release_notes/3.28.0.txt +304 -0
- data/doc/release_notes/3.29.0.txt +459 -0
- data/doc/release_notes/3.30.0.txt +135 -0
- data/doc/release_notes/3.31.0.txt +146 -0
- data/doc/schema_modification.rdoc +547 -0
- data/doc/sharding.rdoc +7 -1
- data/doc/testing.rdoc +129 -0
- data/doc/transactions.rdoc +137 -0
- data/doc/virtual_rows.rdoc +2 -2
- data/lib/sequel/adapters/ado/mssql.rb +18 -6
- data/lib/sequel/adapters/ado.rb +40 -18
- data/lib/sequel/adapters/amalgalite.rb +17 -9
- data/lib/sequel/adapters/db2.rb +175 -86
- data/lib/sequel/adapters/dbi.rb +15 -15
- data/lib/sequel/adapters/do/mysql.rb +5 -7
- data/lib/sequel/adapters/do/postgres.rb +0 -5
- data/lib/sequel/adapters/do/sqlite.rb +0 -5
- data/lib/sequel/adapters/do.rb +17 -36
- data/lib/sequel/adapters/firebird.rb +27 -208
- data/lib/sequel/adapters/ibmdb.rb +448 -0
- data/lib/sequel/adapters/informix.rb +6 -23
- data/lib/sequel/adapters/jdbc/as400.rb +16 -34
- data/lib/sequel/adapters/jdbc/db2.rb +45 -0
- data/lib/sequel/adapters/jdbc/derby.rb +247 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
- data/lib/sequel/adapters/jdbc/h2.rb +56 -29
- data/lib/sequel/adapters/jdbc/hsqldb.rb +170 -0
- data/lib/sequel/adapters/jdbc/informix.rb +26 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
- data/lib/sequel/adapters/jdbc/mysql.rb +11 -11
- data/lib/sequel/adapters/jdbc/oracle.rb +81 -25
- data/lib/sequel/adapters/jdbc/postgresql.rb +9 -13
- data/lib/sequel/adapters/jdbc/sqlite.rb +2 -6
- data/lib/sequel/adapters/jdbc/sqlserver.rb +71 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
- data/lib/sequel/adapters/jdbc.rb +231 -78
- data/lib/sequel/adapters/mock.rb +335 -0
- data/lib/sequel/adapters/mysql.rb +82 -184
- data/lib/sequel/adapters/mysql2.rb +26 -25
- data/lib/sequel/adapters/odbc/db2.rb +17 -0
- data/lib/sequel/adapters/odbc/mssql.rb +1 -6
- data/lib/sequel/adapters/odbc.rb +28 -9
- data/lib/sequel/adapters/openbase.rb +3 -5
- data/lib/sequel/adapters/oracle.rb +351 -53
- data/lib/sequel/adapters/postgres.rb +194 -33
- data/lib/sequel/adapters/shared/access.rb +32 -9
- data/lib/sequel/adapters/shared/db2.rb +321 -0
- data/lib/sequel/adapters/shared/firebird.rb +221 -0
- data/lib/sequel/adapters/shared/informix.rb +53 -0
- data/lib/sequel/adapters/shared/mssql.rb +231 -106
- data/lib/sequel/adapters/shared/mysql.rb +181 -63
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
- data/lib/sequel/adapters/shared/oracle.rb +231 -41
- data/lib/sequel/adapters/shared/postgres.rb +214 -82
- data/lib/sequel/adapters/shared/progress.rb +4 -7
- data/lib/sequel/adapters/shared/sqlite.rb +201 -57
- data/lib/sequel/adapters/sqlite.rb +29 -21
- data/lib/sequel/adapters/swift/mysql.rb +12 -7
- data/lib/sequel/adapters/swift/postgres.rb +0 -5
- data/lib/sequel/adapters/swift/sqlite.rb +8 -6
- data/lib/sequel/adapters/swift.rb +5 -5
- data/lib/sequel/adapters/tinytds.rb +160 -19
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
- data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
- data/lib/sequel/ast_transformer.rb +190 -0
- data/lib/sequel/connection_pool/threaded.rb +3 -2
- data/lib/sequel/connection_pool.rb +1 -1
- data/lib/sequel/core.rb +46 -5
- data/lib/sequel/database/connecting.rb +5 -6
- data/lib/sequel/database/dataset.rb +4 -4
- data/lib/sequel/database/dataset_defaults.rb +59 -1
- data/lib/sequel/database/logging.rb +1 -1
- data/lib/sequel/database/misc.rb +104 -19
- data/lib/sequel/database/query.rb +153 -58
- data/lib/sequel/database/schema_generator.rb +17 -7
- data/lib/sequel/database/schema_methods.rb +59 -29
- data/lib/sequel/dataset/actions.rb +231 -76
- data/lib/sequel/dataset/features.rb +91 -8
- data/lib/sequel/dataset/graph.rb +38 -11
- data/lib/sequel/dataset/misc.rb +50 -23
- data/lib/sequel/dataset/mutation.rb +5 -6
- data/lib/sequel/dataset/prepared_statements.rb +48 -27
- data/lib/sequel/dataset/query.rb +236 -43
- data/lib/sequel/dataset/sql.rb +665 -253
- data/lib/sequel/dataset.rb +8 -0
- data/lib/sequel/exceptions.rb +4 -0
- data/lib/sequel/extensions/columns_introspection.rb +61 -0
- data/lib/sequel/extensions/migration.rb +6 -4
- data/lib/sequel/extensions/named_timezones.rb +5 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
- data/lib/sequel/extensions/to_dot.rb +95 -83
- data/lib/sequel/model/associations.rb +1020 -337
- data/lib/sequel/model/base.rb +414 -132
- data/lib/sequel/model/errors.rb +1 -1
- data/lib/sequel/model/exceptions.rb +5 -1
- data/lib/sequel/model.rb +15 -8
- data/lib/sequel/plugins/association_pks.rb +22 -4
- data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
- data/lib/sequel/plugins/dataset_associations.rb +100 -0
- data/lib/sequel/plugins/defaults_setter.rb +58 -0
- data/lib/sequel/plugins/force_encoding.rb +6 -6
- data/lib/sequel/plugins/identity_map.rb +114 -7
- data/lib/sequel/plugins/many_through_many.rb +65 -7
- data/lib/sequel/plugins/prepared_statements.rb +151 -0
- data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
- data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
- data/lib/sequel/plugins/rcte_tree.rb +29 -15
- data/lib/sequel/plugins/serialization.rb +88 -44
- data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
- data/lib/sequel/plugins/sharding.rb +21 -54
- data/lib/sequel/plugins/single_table_inheritance.rb +3 -1
- data/lib/sequel/plugins/typecast_on_load.rb +9 -12
- data/lib/sequel/plugins/update_primary_key.rb +2 -2
- data/lib/sequel/plugins/xml_serializer.rb +3 -3
- data/lib/sequel/sql.rb +109 -52
- data/lib/sequel/timezones.rb +57 -38
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/db2_spec.rb +146 -0
- data/spec/adapters/mssql_spec.rb +111 -29
- data/spec/adapters/mysql_spec.rb +135 -123
- data/spec/adapters/oracle_spec.rb +49 -77
- data/spec/adapters/postgres_spec.rb +267 -78
- data/spec/adapters/spec_helper.rb +6 -5
- data/spec/adapters/sqlite_spec.rb +35 -21
- data/spec/core/connection_pool_spec.rb +71 -92
- data/spec/core/core_sql_spec.rb +21 -30
- data/spec/core/database_spec.rb +788 -512
- data/spec/core/dataset_spec.rb +1073 -1053
- data/spec/core/expression_filters_spec.rb +159 -42
- data/spec/core/mock_adapter_spec.rb +419 -0
- data/spec/core/object_graph_spec.rb +86 -114
- data/spec/core/schema_generator_spec.rb +3 -3
- data/spec/core/schema_spec.rb +298 -38
- data/spec/core/spec_helper.rb +6 -48
- data/spec/extensions/association_autoreloading_spec.rb +1 -10
- data/spec/extensions/association_dependencies_spec.rb +2 -12
- data/spec/extensions/association_pks_spec.rb +66 -32
- data/spec/extensions/caching_spec.rb +23 -50
- data/spec/extensions/class_table_inheritance_spec.rb +31 -83
- data/spec/extensions/columns_introspection_spec.rb +91 -0
- data/spec/extensions/composition_spec.rb +18 -13
- data/spec/extensions/dataset_associations_spec.rb +199 -0
- data/spec/extensions/defaults_setter_spec.rb +64 -0
- data/spec/extensions/hook_class_methods_spec.rb +65 -91
- data/spec/extensions/identity_map_spec.rb +114 -22
- data/spec/extensions/instance_filters_spec.rb +11 -21
- data/spec/extensions/instance_hooks_spec.rb +53 -0
- data/spec/extensions/json_serializer_spec.rb +4 -5
- data/spec/extensions/lazy_attributes_spec.rb +16 -20
- data/spec/extensions/list_spec.rb +17 -39
- data/spec/extensions/many_through_many_spec.rb +283 -250
- data/spec/extensions/migration_spec.rb +35 -32
- data/spec/extensions/named_timezones_spec.rb +23 -3
- data/spec/extensions/nested_attributes_spec.rb +97 -88
- data/spec/extensions/optimistic_locking_spec.rb +9 -20
- data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
- data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
- data/spec/extensions/prepared_statements_spec.rb +53 -0
- data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
- data/spec/extensions/pretty_table_spec.rb +1 -6
- data/spec/extensions/rcte_tree_spec.rb +41 -43
- data/spec/extensions/schema_dumper_spec.rb +5 -8
- data/spec/extensions/schema_spec.rb +13 -21
- data/spec/extensions/serialization_modification_detection_spec.rb +37 -0
- data/spec/extensions/serialization_spec.rb +72 -43
- data/spec/extensions/sharding_spec.rb +66 -140
- data/spec/extensions/single_table_inheritance_spec.rb +18 -29
- data/spec/extensions/spec_helper.rb +13 -61
- data/spec/extensions/sql_expr_spec.rb +20 -60
- data/spec/extensions/tactical_eager_loading_spec.rb +9 -19
- data/spec/extensions/thread_local_timezones_spec.rb +22 -2
- data/spec/extensions/timestamps_spec.rb +6 -6
- data/spec/extensions/to_dot_spec.rb +4 -7
- data/spec/extensions/touch_spec.rb +13 -14
- data/spec/extensions/tree_spec.rb +11 -26
- data/spec/extensions/typecast_on_load_spec.rb +1 -6
- data/spec/extensions/update_primary_key_spec.rb +30 -24
- data/spec/extensions/validation_class_methods_spec.rb +30 -51
- data/spec/extensions/validation_helpers_spec.rb +16 -35
- data/spec/extensions/xml_serializer_spec.rb +16 -4
- data/spec/integration/associations_test.rb +537 -21
- data/spec/integration/database_test.rb +4 -2
- data/spec/integration/dataset_test.rb +461 -65
- data/spec/integration/eager_loader_test.rb +19 -21
- data/spec/integration/model_test.rb +80 -1
- data/spec/integration/plugin_test.rb +304 -116
- data/spec/integration/prepared_statement_test.rb +212 -122
- data/spec/integration/schema_test.rb +161 -30
- data/spec/integration/spec_helper.rb +39 -30
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +172 -5
- data/spec/integration/type_test.rb +17 -3
- data/spec/model/association_reflection_spec.rb +201 -7
- data/spec/model/associations_spec.rb +865 -662
- data/spec/model/base_spec.rb +275 -95
- data/spec/model/dataset_methods_spec.rb +7 -27
- data/spec/model/eager_loading_spec.rb +677 -676
- data/spec/model/hooks_spec.rb +309 -67
- data/spec/model/model_spec.rb +222 -168
- data/spec/model/plugins_spec.rb +24 -13
- data/spec/model/record_spec.rb +381 -218
- data/spec/model/spec_helper.rb +13 -71
- data/spec/model/validations_spec.rb +11 -0
- metadata +115 -73
|
@@ -13,6 +13,18 @@ module Sequel
|
|
|
13
13
|
# ORA-03114: not connected to ORACLE
|
|
14
14
|
CONNECTION_ERROR_CODES = [ 28, 1012, 3113, 3114 ]
|
|
15
15
|
|
|
16
|
+
ORACLE_TYPES = {:blob=>lambda{|b| Sequel::SQL::Blob.new(b.read)}}
|
|
17
|
+
|
|
18
|
+
# Hash of conversion procs for this database.
|
|
19
|
+
attr_reader :conversion_procs
|
|
20
|
+
|
|
21
|
+
def initialize(opts={})
|
|
22
|
+
super
|
|
23
|
+
@autosequence = opts[:autosequence]
|
|
24
|
+
@primary_key_sequences = {}
|
|
25
|
+
@conversion_procs = ORACLE_TYPES.dup
|
|
26
|
+
end
|
|
27
|
+
|
|
16
28
|
def connect(server)
|
|
17
29
|
opts = server_opts(server)
|
|
18
30
|
if opts[:database]
|
|
@@ -22,61 +34,144 @@ module Sequel
|
|
|
22
34
|
dbname = opts[:host]
|
|
23
35
|
end
|
|
24
36
|
conn = OCI8.new(opts[:user], opts[:password], dbname, opts[:privilege])
|
|
37
|
+
conn.prefetch_rows = typecast_value_integer(opts[:prefetch_rows]) if opts[:prefetch_rows]
|
|
25
38
|
conn.autocommit = true
|
|
26
39
|
conn.non_blocking = true
|
|
40
|
+
|
|
41
|
+
# The ruby-oci8 gem which retrieves oracle columns with a type of
|
|
42
|
+
# DATE, TIMESTAMP, TIMESTAMP WITH TIME ZONE is complex based on the
|
|
43
|
+
# ruby version (1.9.2 or later) and Oracle version (9 or later)
|
|
44
|
+
# In the now standard case of 1.9.2 and Oracle 9 or later, the timezone
|
|
45
|
+
# is determined by the Oracle session timezone. Thus if the user
|
|
46
|
+
# requests Sequel provide UTC timezone to the application,
|
|
47
|
+
# we need to alter the session timezone to be UTC
|
|
48
|
+
if Sequel.application_timezone == :utc
|
|
49
|
+
conn.exec("ALTER SESSION SET TIME_ZONE='-00:00'")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
class << conn
|
|
53
|
+
attr_reader :prepared_statements
|
|
54
|
+
end
|
|
55
|
+
conn.instance_variable_set(:@prepared_statements, {})
|
|
56
|
+
|
|
27
57
|
conn
|
|
28
58
|
end
|
|
29
|
-
|
|
30
|
-
def
|
|
31
|
-
|
|
59
|
+
|
|
60
|
+
def execute(sql, opts={}, &block)
|
|
61
|
+
_execute(nil, sql, opts, &block)
|
|
32
62
|
end
|
|
63
|
+
alias do execute
|
|
33
64
|
|
|
34
|
-
def
|
|
35
|
-
|
|
36
|
-
ds.identifier_output_method = :downcase
|
|
37
|
-
schema_and_table = "#{"#{quote_identifier(opts[:schema])}." if opts[:schema]}#{quote_identifier(table)}"
|
|
38
|
-
table_schema = []
|
|
39
|
-
metadata = transaction(opts){|conn| conn.describe_table(schema_and_table)}
|
|
40
|
-
metadata.columns.each do |column|
|
|
41
|
-
table_schema << [
|
|
42
|
-
column.name.downcase.to_sym,
|
|
43
|
-
{
|
|
44
|
-
:type => column.data_type,
|
|
45
|
-
:db_type => column.type_string.split(' ')[0],
|
|
46
|
-
:type_string => column.type_string,
|
|
47
|
-
:charset_form => column.charset_form,
|
|
48
|
-
:char_used => column.char_used?,
|
|
49
|
-
:char_size => column.char_size,
|
|
50
|
-
:data_size => column.data_size,
|
|
51
|
-
:precision => column.precision,
|
|
52
|
-
:scale => column.scale,
|
|
53
|
-
:fsprecision => column.fsprecision,
|
|
54
|
-
:lfprecision => column.lfprecision,
|
|
55
|
-
:allow_null => column.nullable?
|
|
56
|
-
}
|
|
57
|
-
]
|
|
58
|
-
end
|
|
59
|
-
table_schema
|
|
65
|
+
def execute_insert(sql, opts={})
|
|
66
|
+
_execute(:insert, sql, opts)
|
|
60
67
|
end
|
|
61
68
|
|
|
62
|
-
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
def _execute(type, sql, opts={}, &block)
|
|
63
72
|
synchronize(opts[:server]) do |conn|
|
|
64
73
|
begin
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
74
|
+
return execute_prepared_statement(conn, type, sql, opts, &block) if sql.is_a?(Symbol)
|
|
75
|
+
if args = opts[:arguments]
|
|
76
|
+
r = conn.parse(sql)
|
|
77
|
+
args = cursor_bind_params(conn, r, args)
|
|
78
|
+
nr = log_yield(sql, args){r.exec}
|
|
79
|
+
r = nr unless block_given?
|
|
80
|
+
else
|
|
81
|
+
r = log_yield(sql){conn.exec(sql)}
|
|
82
|
+
end
|
|
83
|
+
if block_given?
|
|
84
|
+
begin
|
|
85
|
+
yield(r)
|
|
86
|
+
ensure
|
|
87
|
+
r.close
|
|
88
|
+
end
|
|
89
|
+
elsif type == :insert
|
|
90
|
+
last_insert_id(conn, opts)
|
|
91
|
+
else
|
|
92
|
+
r
|
|
93
|
+
end
|
|
94
|
+
rescue OCIException, RuntimeError => e
|
|
95
|
+
# ruby-oci8 is naughty and raises strings in some places
|
|
96
|
+
raise_error(e)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
PS_TYPES = {'string'.freeze=>String, 'integer'.freeze=>Integer, 'float'.freeze=>Float,
|
|
102
|
+
'decimal'.freeze=>Float, 'date'.freeze=>Time, 'datetime'.freeze=>Time,
|
|
103
|
+
'time'.freeze=>Time, 'boolean'.freeze=>String, 'blob'.freeze=>OCI8::BLOB}
|
|
104
|
+
def cursor_bind_params(conn, cursor, args)
|
|
105
|
+
cursor
|
|
106
|
+
i = 0
|
|
107
|
+
args.map do |arg, type|
|
|
108
|
+
i += 1
|
|
109
|
+
case arg
|
|
110
|
+
when true
|
|
111
|
+
arg = 'Y'
|
|
112
|
+
when false
|
|
113
|
+
arg = 'N'
|
|
114
|
+
when BigDecimal
|
|
115
|
+
arg = arg.to_f
|
|
116
|
+
when ::Sequel::SQL::Blob
|
|
117
|
+
raise Error, "Sequel's oracle adapter does not currently support using a blob in a bound variable"
|
|
118
|
+
end
|
|
119
|
+
if t = PS_TYPES[type]
|
|
120
|
+
cursor.bind_param(i, arg, t)
|
|
121
|
+
else
|
|
122
|
+
cursor.bind_param(i, arg, arg.class)
|
|
123
|
+
end
|
|
124
|
+
arg
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def execute_prepared_statement(conn, type, name, opts)
|
|
129
|
+
ps = prepared_statements[name]
|
|
130
|
+
sql = ps.prepared_sql
|
|
131
|
+
if cursora = conn.prepared_statements[name]
|
|
132
|
+
cursor, cursor_sql = cursora
|
|
133
|
+
if cursor_sql != sql
|
|
134
|
+
cursor.close
|
|
135
|
+
cursor = nil
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
unless cursor
|
|
139
|
+
cursor = log_yield("Preparing #{name}: #{sql}"){conn.parse(sql)}
|
|
140
|
+
conn.prepared_statements[name] = [cursor, sql]
|
|
141
|
+
end
|
|
142
|
+
args = cursor_bind_params(conn, cursor, opts[:arguments])
|
|
143
|
+
r = log_yield("Executing #{name}", args){cursor.exec}
|
|
144
|
+
if block_given?
|
|
145
|
+
yield(cursor)
|
|
146
|
+
elsif type == :insert
|
|
147
|
+
last_insert_id(conn, opts)
|
|
148
|
+
else
|
|
149
|
+
r
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def last_insert_id(conn, opts)
|
|
154
|
+
unless sequence = opts[:sequence]
|
|
155
|
+
if t = opts[:table]
|
|
156
|
+
sequence = sequence_for_table(t)
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
if sequence
|
|
160
|
+
sql = "SELECT #{literal(sequence)}.currval FROM dual"
|
|
161
|
+
begin
|
|
162
|
+
cursor = log_yield(sql){conn.exec(sql)}
|
|
163
|
+
row = cursor.fetch
|
|
164
|
+
row.each{|v| return (v.to_i if v)}
|
|
165
|
+
rescue OCIError
|
|
166
|
+
nil
|
|
167
|
+
ensure
|
|
168
|
+
cursor.close if cursor
|
|
70
169
|
end
|
|
71
170
|
end
|
|
72
171
|
end
|
|
73
|
-
alias_method :do, :execute
|
|
74
172
|
|
|
75
|
-
private
|
|
76
|
-
|
|
77
173
|
def begin_transaction(conn, opts={})
|
|
78
174
|
log_yield(TRANSACTION_BEGIN){conn.autocommit = false}
|
|
79
|
-
conn
|
|
80
175
|
end
|
|
81
176
|
|
|
82
177
|
def commit_transaction(conn, opts={})
|
|
@@ -88,49 +183,252 @@ module Sequel
|
|
|
88
183
|
rescue OCIInvalidHandle
|
|
89
184
|
nil
|
|
90
185
|
end
|
|
186
|
+
|
|
187
|
+
def disconnect_error?(e, opts)
|
|
188
|
+
super || (e.is_a?(::OCIError) && CONNECTION_ERROR_CODES.include?(e.code))
|
|
189
|
+
end
|
|
91
190
|
|
|
92
|
-
def
|
|
93
|
-
|
|
191
|
+
def oracle_column_type(h)
|
|
192
|
+
case h[:oci8_type]
|
|
193
|
+
when :number
|
|
194
|
+
case h[:scale]
|
|
195
|
+
when 0
|
|
196
|
+
:integer
|
|
197
|
+
when -127
|
|
198
|
+
:float
|
|
199
|
+
else
|
|
200
|
+
:decimal
|
|
201
|
+
end
|
|
202
|
+
when :date
|
|
203
|
+
:datetime
|
|
204
|
+
else
|
|
205
|
+
schema_column_type(h[:db_type])
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def remove_transaction(conn, committed)
|
|
210
|
+
conn.autocommit = true
|
|
211
|
+
ensure
|
|
94
212
|
super
|
|
95
213
|
end
|
|
96
214
|
|
|
97
215
|
def rollback_transaction(conn, opts={})
|
|
98
216
|
log_yield(TRANSACTION_ROLLBACK){conn.rollback}
|
|
99
217
|
end
|
|
218
|
+
|
|
219
|
+
def schema_parse_table(table, opts={})
|
|
220
|
+
schema, table = schema_and_table(table)
|
|
221
|
+
schema ||= opts[:schema]
|
|
222
|
+
schema_and_table = if ds = opts[:dataset]
|
|
223
|
+
ds.literal(schema ? SQL::QualifiedIdentifier.new(schema, table) : SQL::Identifier.new(table))
|
|
224
|
+
else
|
|
225
|
+
"#{"#{quote_identifier(schema)}." if schema}#{quote_identifier(table)}"
|
|
226
|
+
end
|
|
227
|
+
table_schema = []
|
|
228
|
+
m = output_identifier_meth(ds)
|
|
229
|
+
im = input_identifier_meth(ds)
|
|
230
|
+
|
|
231
|
+
# Primary Keys
|
|
232
|
+
ds = metadata_dataset.from(:all_constraints___cons, :all_cons_columns___cols).
|
|
233
|
+
where(:cols__table_name=>im.call(table), :cons__constraint_type=>'P',
|
|
234
|
+
:cons__constraint_name=>:cols__constraint_name, :cons__owner=>:cols__owner)
|
|
235
|
+
ds = ds.where(:cons__owner=>im.call(schema)) if schema
|
|
236
|
+
pks = ds.select_map(:cols__column_name)
|
|
237
|
+
|
|
238
|
+
# Default values
|
|
239
|
+
defaults = metadata_dataset.from(:dba_tab_cols).
|
|
240
|
+
where(:table_name=>im.call(table)).
|
|
241
|
+
to_hash(:column_name, :data_default)
|
|
242
|
+
|
|
243
|
+
metadata = synchronize(opts[:server]) do |conn|
|
|
244
|
+
begin
|
|
245
|
+
log_yield("Connection.describe_table"){conn.describe_table(schema_and_table)}
|
|
246
|
+
rescue OCIError => e
|
|
247
|
+
raise_error(e)
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
metadata.columns.each do |column|
|
|
251
|
+
h = {
|
|
252
|
+
:primary_key => pks.include?(column.name),
|
|
253
|
+
:default => defaults[column.name],
|
|
254
|
+
:oci8_type => column.data_type,
|
|
255
|
+
:db_type => column.type_string.split(' ')[0],
|
|
256
|
+
:type_string => column.type_string,
|
|
257
|
+
:charset_form => column.charset_form,
|
|
258
|
+
:char_used => column.char_used?,
|
|
259
|
+
:char_size => column.char_size,
|
|
260
|
+
:data_size => column.data_size,
|
|
261
|
+
:precision => column.precision,
|
|
262
|
+
:scale => column.scale,
|
|
263
|
+
:fsprecision => column.fsprecision,
|
|
264
|
+
:lfprecision => column.lfprecision,
|
|
265
|
+
:allow_null => column.nullable?
|
|
266
|
+
}
|
|
267
|
+
h[:type] = oracle_column_type(h)
|
|
268
|
+
table_schema << [m.call(column.name), h]
|
|
269
|
+
end
|
|
270
|
+
table_schema
|
|
271
|
+
end
|
|
100
272
|
end
|
|
101
273
|
|
|
102
274
|
class Dataset < Sequel::Dataset
|
|
103
275
|
include DatasetMethods
|
|
104
276
|
|
|
277
|
+
Database::DatasetClass = self
|
|
278
|
+
|
|
279
|
+
PREPARED_ARG_PLACEHOLDER = ':'.freeze
|
|
280
|
+
|
|
281
|
+
# Oracle already supports named bind arguments, so use directly.
|
|
282
|
+
module ArgumentMapper
|
|
283
|
+
include Sequel::Dataset::ArgumentMapper
|
|
284
|
+
|
|
285
|
+
protected
|
|
286
|
+
|
|
287
|
+
# Return a hash with the same values as the given hash,
|
|
288
|
+
# but with the keys converted to strings.
|
|
289
|
+
def map_to_prepared_args(bind_vars)
|
|
290
|
+
prepared_args.map{|v, t| [bind_vars[v], t]}
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
private
|
|
294
|
+
|
|
295
|
+
# Oracle uses a : before the name of the argument for named
|
|
296
|
+
# arguments.
|
|
297
|
+
def prepared_arg(k)
|
|
298
|
+
y, type = k.to_s.split("__", 2)
|
|
299
|
+
prepared_args << [y.to_sym, type]
|
|
300
|
+
i = prepared_args.length
|
|
301
|
+
LiteralString.new(":#{i}")
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
# Always assume a prepared argument.
|
|
305
|
+
def prepared_arg?(k)
|
|
306
|
+
true
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
# Oracle prepared statement uses a new prepared statement each time
|
|
311
|
+
# it is called, but it does use the bind arguments.
|
|
312
|
+
module BindArgumentMethods
|
|
313
|
+
include ArgumentMapper
|
|
314
|
+
|
|
315
|
+
private
|
|
316
|
+
|
|
317
|
+
# Run execute_select on the database with the given SQL and the stored
|
|
318
|
+
# bind arguments.
|
|
319
|
+
def execute(sql, opts={}, &block)
|
|
320
|
+
super(prepared_sql, {:arguments=>bind_arguments}.merge(opts), &block)
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
# Same as execute, explicit due to intricacies of alias and super.
|
|
324
|
+
def execute_dui(sql, opts={}, &block)
|
|
325
|
+
super(prepared_sql, {:arguments=>bind_arguments}.merge(opts), &block)
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
# Same as execute, explicit due to intricacies of alias and super.
|
|
329
|
+
def execute_insert(sql, opts={}, &block)
|
|
330
|
+
super(prepared_sql, {:arguments=>bind_arguments}.merge(opts), &block)
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
module PreparedStatementMethods
|
|
335
|
+
include BindArgumentMethods
|
|
336
|
+
|
|
337
|
+
private
|
|
338
|
+
|
|
339
|
+
# Execute the stored prepared statement name and the stored bind
|
|
340
|
+
# arguments instead of the SQL given.
|
|
341
|
+
def execute(sql, opts={}, &block)
|
|
342
|
+
super(prepared_statement_name, opts, &block)
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
# Same as execute, explicit due to intricacies of alias and super.
|
|
346
|
+
def execute_dui(sql, opts={}, &block)
|
|
347
|
+
super(prepared_statement_name, opts, &block)
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
# Same as execute, explicit due to intricacies of alias and super.
|
|
351
|
+
def execute_insert(sql, opts={}, &block)
|
|
352
|
+
super(prepared_statement_name, opts, &block)
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
# Execute the given type of statement with the hash of values.
|
|
357
|
+
def call(type, bind_vars={}, *values, &block)
|
|
358
|
+
ps = to_prepared_statement(type, values)
|
|
359
|
+
ps.extend(BindArgumentMethods)
|
|
360
|
+
ps.call(bind_vars, &block)
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
# Prepare the given type of query with the given name and store
|
|
364
|
+
# it in the database. Note that a new native prepared statement is
|
|
365
|
+
# created on each call to this prepared statement.
|
|
366
|
+
def prepare(type, name=nil, *values)
|
|
367
|
+
ps = to_prepared_statement(type, values)
|
|
368
|
+
ps.extend(PreparedStatementMethods)
|
|
369
|
+
if name
|
|
370
|
+
ps.prepared_statement_name = name
|
|
371
|
+
db.prepared_statements[name] = ps
|
|
372
|
+
end
|
|
373
|
+
ps
|
|
374
|
+
end
|
|
375
|
+
|
|
105
376
|
def fetch_rows(sql)
|
|
106
377
|
execute(sql) do |cursor|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
378
|
+
offset = @opts[:offset]
|
|
379
|
+
rn = row_number_column
|
|
380
|
+
cps = db.conversion_procs
|
|
381
|
+
cols = columns = cursor.get_col_names.map{|c| output_identifier(c)}
|
|
382
|
+
metadata = cursor.column_metadata
|
|
383
|
+
cm = cols.zip(metadata).map{|c, m| [c, cps[m.data_type]]}
|
|
384
|
+
columns = cols.reject{|x| x == rn} if offset
|
|
385
|
+
@columns = columns
|
|
386
|
+
while r = cursor.fetch
|
|
387
|
+
row = {}
|
|
388
|
+
r.zip(cm).each{|v, (c, cp)| row[c] = ((v && cp) ? cp.call(v) : v)}
|
|
389
|
+
row.delete(rn) if offset
|
|
390
|
+
yield row
|
|
116
391
|
end
|
|
117
392
|
end
|
|
118
393
|
self
|
|
119
394
|
end
|
|
120
395
|
|
|
396
|
+
# Create a named prepared statement that is stored in the
|
|
397
|
+
# database (and connection) for reuse.
|
|
398
|
+
def prepare(type, name=nil, *values)
|
|
399
|
+
ps = to_prepared_statement(type, values)
|
|
400
|
+
ps.extend(PreparedStatementMethods)
|
|
401
|
+
if name
|
|
402
|
+
ps.prepared_statement_name = name
|
|
403
|
+
db.prepared_statements[name] = ps
|
|
404
|
+
end
|
|
405
|
+
ps
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
# Oracle requires type specifiers for placeholders, at least
|
|
409
|
+
# if you ever want to use a nil/NULL value as the value for
|
|
410
|
+
# the placeholder.
|
|
411
|
+
def requires_placeholder_type_specifiers?
|
|
412
|
+
true
|
|
413
|
+
end
|
|
414
|
+
|
|
121
415
|
private
|
|
122
416
|
|
|
123
|
-
def
|
|
417
|
+
def literal_other_append(sql, v)
|
|
124
418
|
case v
|
|
125
419
|
when OraDate
|
|
126
|
-
|
|
420
|
+
literal_append(sql, db.to_application_timestamp(v))
|
|
127
421
|
when OCI8::CLOB
|
|
128
422
|
v.rewind
|
|
129
|
-
|
|
423
|
+
literal_append(sql, v.read)
|
|
130
424
|
else
|
|
131
425
|
super
|
|
132
426
|
end
|
|
133
427
|
end
|
|
428
|
+
|
|
429
|
+
def prepared_arg_placeholder
|
|
430
|
+
PREPARED_ARG_PLACEHOLDER
|
|
431
|
+
end
|
|
134
432
|
end
|
|
135
433
|
end
|
|
136
434
|
end
|