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
data/lib/sequel/adapters/db2.rb
CHANGED
|
@@ -1,139 +1,228 @@
|
|
|
1
1
|
require 'db2/db2cli'
|
|
2
|
+
Sequel.require %w'shared/db2', 'adapters'
|
|
2
3
|
|
|
3
4
|
module Sequel
|
|
4
5
|
module DB2
|
|
6
|
+
|
|
7
|
+
@convert_smallint_to_bool = true
|
|
8
|
+
|
|
9
|
+
# Underlying error raised by Sequel, since ruby-db2 doesn't
|
|
10
|
+
# use exceptions.
|
|
11
|
+
class DB2Error < StandardError
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class << self
|
|
15
|
+
# Whether to convert smallint values to bool, true by default.
|
|
16
|
+
# Can also be overridden per dataset.
|
|
17
|
+
attr_accessor :convert_smallint_to_bool
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
tt = Class.new do
|
|
21
|
+
def boolean(s) !s.to_i.zero? end
|
|
22
|
+
def date(s) Date.new(s.year, s.month, s.day) end
|
|
23
|
+
def time(s) Sequel::SQLTime.create(s.hour, s.minute, s.second) end
|
|
24
|
+
end.new
|
|
25
|
+
|
|
26
|
+
# Hash holding type translation methods, used by Dataset#fetch_rows.
|
|
27
|
+
DB2_TYPES = {
|
|
28
|
+
:boolean => tt.method(:boolean),
|
|
29
|
+
DB2CLI::SQL_BLOB => ::Sequel::SQL::Blob.method(:new),
|
|
30
|
+
DB2CLI::SQL_TYPE_DATE => tt.method(:date),
|
|
31
|
+
DB2CLI::SQL_TYPE_TIME => tt.method(:time),
|
|
32
|
+
DB2CLI::SQL_DECIMAL => ::BigDecimal.method(:new)
|
|
33
|
+
}
|
|
34
|
+
DB2_TYPES[DB2CLI::SQL_CLOB] = DB2_TYPES[DB2CLI::SQL_BLOB]
|
|
35
|
+
|
|
5
36
|
class Database < Sequel::Database
|
|
6
|
-
|
|
37
|
+
include DatabaseMethods
|
|
7
38
|
|
|
8
|
-
|
|
39
|
+
set_adapter_scheme :db2
|
|
9
40
|
|
|
10
41
|
TEMPORARY = 'GLOBAL TEMPORARY '.freeze
|
|
42
|
+
rc, NullHandle = DB2CLI.SQLAllocHandle(DB2CLI::SQL_HANDLE_ENV, DB2CLI::SQL_NULL_HANDLE)
|
|
11
43
|
|
|
12
|
-
|
|
13
|
-
|
|
44
|
+
# Hash of connection procs for converting
|
|
45
|
+
attr_reader :conversion_procs
|
|
46
|
+
|
|
47
|
+
def initialize(opts={})
|
|
48
|
+
super
|
|
49
|
+
@conversion_procs = DB2_TYPES.dup
|
|
50
|
+
@conversion_procs[DB2CLI::SQL_TYPE_TIMESTAMP] = method(:to_application_timestamp_db2)
|
|
51
|
+
end
|
|
14
52
|
|
|
15
53
|
def connect(server)
|
|
16
54
|
opts = server_opts(server)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
rc = SQLConnect(dbc, opts[:database], opts[:user], opts[:password])
|
|
21
|
-
check_error(rc, "Could not connect to database")
|
|
22
|
-
|
|
55
|
+
dbc = checked_error("Could not allocate database connection"){DB2CLI.SQLAllocHandle(DB2CLI::SQL_HANDLE_DBC, NullHandle)}
|
|
56
|
+
checked_error("Could not connect to database"){DB2CLI.SQLConnect(dbc, opts[:database], opts[:user], opts[:password])}
|
|
23
57
|
dbc
|
|
24
58
|
end
|
|
25
59
|
|
|
26
|
-
def
|
|
27
|
-
synchronize(server){|conn|}
|
|
28
|
-
true
|
|
60
|
+
def execute(sql, opts={}, &block)
|
|
61
|
+
synchronize(opts[:server]){|conn| log_connection_execute(conn, sql, &block)}
|
|
29
62
|
end
|
|
63
|
+
alias do execute
|
|
30
64
|
|
|
31
|
-
def
|
|
32
|
-
DB2::Dataset.new(self, opts)
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def execute(sql, opts={})
|
|
65
|
+
def execute_insert(sql, opts={})
|
|
36
66
|
synchronize(opts[:server]) do |conn|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
ensure
|
|
50
|
-
rc = SQLFreeHandle(SQL_HANDLE_STMT, sth)
|
|
51
|
-
check_error(rc, "Could not free statement")
|
|
67
|
+
log_connection_execute(conn, sql)
|
|
68
|
+
sql = "SELECT IDENTITY_VAL_LOCAL() FROM SYSIBM.SYSDUMMY1"
|
|
69
|
+
log_connection_execute(conn, sql) do |sth|
|
|
70
|
+
name, buflen, datatype, size, digits, nullable = checked_error("Could not describe column"){DB2CLI.SQLDescribeCol(sth, 1, 256)}
|
|
71
|
+
if DB2CLI.SQLFetch(sth) != DB2CLI::SQL_NO_DATA_FOUND
|
|
72
|
+
v, _ = checked_error("Could not get data"){DB2CLI.SQLGetData(sth, 1, datatype, size)}
|
|
73
|
+
if v.is_a?(String)
|
|
74
|
+
return v.to_i
|
|
75
|
+
else
|
|
76
|
+
return nil
|
|
77
|
+
end
|
|
78
|
+
end
|
|
52
79
|
end
|
|
53
80
|
end
|
|
54
81
|
end
|
|
55
|
-
alias_method :do, :execute
|
|
56
|
-
|
|
57
|
-
private
|
|
58
82
|
|
|
83
|
+
ERROR_MAP = {}
|
|
84
|
+
%w'SQL_INVALID_HANDLE SQL_STILL_EXECUTING SQL_ERROR'.each do |s|
|
|
85
|
+
ERROR_MAP[DB2CLI.const_get(s)] = s
|
|
86
|
+
end
|
|
59
87
|
def check_error(rc, msg)
|
|
60
88
|
case rc
|
|
61
|
-
when SQL_SUCCESS, SQL_SUCCESS_WITH_INFO
|
|
89
|
+
when DB2CLI::SQL_SUCCESS, DB2CLI::SQL_SUCCESS_WITH_INFO, DB2CLI::SQL_NO_DATA_FOUND
|
|
62
90
|
nil
|
|
91
|
+
when DB2CLI::SQL_INVALID_HANDLE, DB2CLI::SQL_STILL_EXECUTING
|
|
92
|
+
e = DB2Error.new("#{ERROR_MAP[rc]}: #{msg}")
|
|
93
|
+
e.set_backtrace(caller)
|
|
94
|
+
raise_error(e, :disconnect=>true)
|
|
63
95
|
else
|
|
64
|
-
|
|
96
|
+
e = DB2Error.new("#{ERROR_MAP[rc] || "Error code #{rc}"}: #{msg}")
|
|
97
|
+
e.set_backtrace(caller)
|
|
98
|
+
raise_error(e, :disconnect=>true)
|
|
65
99
|
end
|
|
66
100
|
end
|
|
67
101
|
|
|
68
|
-
def
|
|
69
|
-
rc =
|
|
70
|
-
check_error(rc,
|
|
102
|
+
def checked_error(msg)
|
|
103
|
+
rc, *ary= yield
|
|
104
|
+
check_error(rc, msg)
|
|
105
|
+
ary.length <= 1 ? ary.first : ary
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def to_application_timestamp_db2(v)
|
|
109
|
+
to_application_timestamp(v.to_s)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
private
|
|
113
|
+
|
|
114
|
+
def begin_transaction(conn, opts={})
|
|
115
|
+
log_yield(TRANSACTION_BEGIN){DB2CLI.SQLSetConnectAttr(conn, DB2CLI::SQL_ATTR_AUTOCOMMIT, DB2CLI::SQL_AUTOCOMMIT_OFF)}
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def remove_transaction(conn, committed)
|
|
119
|
+
DB2CLI.SQLSetConnectAttr(conn, DB2CLI::SQL_ATTR_AUTOCOMMIT, DB2CLI::SQL_AUTOCOMMIT_ON)
|
|
120
|
+
ensure
|
|
121
|
+
super
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def rollback_transaction(conn, opts={})
|
|
125
|
+
log_yield(TRANSACTION_ROLLBACK){DB2CLI.SQLEndTran(DB2CLI::SQL_HANDLE_DBC, conn, DB2CLI::SQL_ROLLBACK)}
|
|
126
|
+
end
|
|
71
127
|
|
|
72
|
-
|
|
73
|
-
|
|
128
|
+
def commit_transaction(conn, opts={})
|
|
129
|
+
log_yield(TRANSACTION_COMMIT){DB2CLI.SQLEndTran(DB2CLI::SQL_HANDLE_DBC, conn, DB2CLI::SQL_COMMIT)}
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def log_connection_execute(conn, sql)
|
|
133
|
+
sth = checked_error("Could not allocate statement"){DB2CLI.SQLAllocHandle(DB2CLI::SQL_HANDLE_STMT, conn)}
|
|
134
|
+
|
|
135
|
+
begin
|
|
136
|
+
checked_error("Could not execute statement: #{sql}"){log_yield(sql){DB2CLI.SQLExecDirect(sth, sql)}}
|
|
137
|
+
|
|
138
|
+
if block_given?
|
|
139
|
+
yield(sth)
|
|
140
|
+
else
|
|
141
|
+
checked_error("Could not get RPC"){DB2CLI.SQLRowCount(sth)}
|
|
142
|
+
end
|
|
143
|
+
ensure
|
|
144
|
+
checked_error("Could not free statement"){DB2CLI.SQLFreeHandle(DB2CLI::SQL_HANDLE_STMT, sth)}
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Convert smallint type to boolean if convert_smallint_to_bool is true
|
|
149
|
+
def schema_column_type(db_type)
|
|
150
|
+
if DB2.convert_smallint_to_bool && db_type =~ /smallint/i
|
|
151
|
+
:boolean
|
|
152
|
+
else
|
|
153
|
+
super
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def disconnect_connection(conn)
|
|
158
|
+
checked_error("Could not disconnect from database"){DB2CLI.SQLDisconnect(conn)}
|
|
159
|
+
checked_error("Could not free Database handle"){DB2CLI.SQLFreeHandle(DB2CLI::SQL_HANDLE_DBC, conn)}
|
|
74
160
|
end
|
|
75
161
|
end
|
|
76
162
|
|
|
77
163
|
class Dataset < Sequel::Dataset
|
|
164
|
+
include DatasetMethods
|
|
165
|
+
|
|
166
|
+
Database::DatasetClass = self
|
|
78
167
|
MAX_COL_SIZE = 256
|
|
79
168
|
|
|
169
|
+
# Whether to convert smallint to boolean arguments for this dataset.
|
|
170
|
+
# Defaults to the DB2 module setting.
|
|
171
|
+
def convert_smallint_to_bool
|
|
172
|
+
defined?(@convert_smallint_to_bool) ? @convert_smallint_to_bool : (@convert_smallint_to_bool = DB2.convert_smallint_to_bool)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Override the default DB2.convert_smallint_to_bool setting for this dataset.
|
|
176
|
+
attr_writer :convert_smallint_to_bool
|
|
177
|
+
|
|
80
178
|
def fetch_rows(sql)
|
|
81
179
|
execute(sql) do |sth|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
180
|
+
offset = @opts[:offset]
|
|
181
|
+
db = @db
|
|
182
|
+
i = 1
|
|
183
|
+
column_info = get_column_info(sth)
|
|
184
|
+
cols = column_info.map{|c| c.at(1)}
|
|
185
|
+
cols.delete(row_number_column) if offset
|
|
186
|
+
@columns = cols
|
|
187
|
+
errors = [DB2CLI::SQL_NO_DATA_FOUND, DB2CLI::SQL_ERROR]
|
|
188
|
+
until errors.include?(rc = DB2CLI.SQLFetch(sth))
|
|
189
|
+
db.check_error(rc, "Could not fetch row")
|
|
190
|
+
row = {}
|
|
191
|
+
column_info.each do |i, c, t, s, pr|
|
|
192
|
+
v, _ = db.checked_error("Could not get data"){DB2CLI.SQLGetData(sth, i, t, s)}
|
|
193
|
+
row[c] = if v == DB2CLI::Null
|
|
194
|
+
nil
|
|
195
|
+
elsif pr
|
|
196
|
+
pr.call(v)
|
|
197
|
+
else
|
|
198
|
+
v
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
row.delete(row_number_column) if offset
|
|
202
|
+
yield row
|
|
87
203
|
end
|
|
88
204
|
end
|
|
89
205
|
self
|
|
90
206
|
end
|
|
91
207
|
|
|
92
|
-
# DB2 supports window functions
|
|
93
|
-
def supports_window_functions?
|
|
94
|
-
true
|
|
95
|
-
end
|
|
96
|
-
|
|
97
208
|
private
|
|
98
209
|
|
|
99
210
|
def get_column_info(sth)
|
|
100
|
-
|
|
101
|
-
|
|
211
|
+
db = @db
|
|
212
|
+
column_count = db.checked_error("Could not get number of result columns"){DB2CLI.SQLNumResultCols(sth)}
|
|
213
|
+
convert = convert_smallint_to_bool
|
|
214
|
+
cps = db.conversion_procs
|
|
102
215
|
|
|
103
216
|
(1..column_count).map do |i|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
217
|
+
name, buflen, datatype, size, digits, nullable = db.checked_error("Could not describe column"){DB2CLI.SQLDescribeCol(sth, i, MAX_COL_SIZE)}
|
|
218
|
+
pr = if datatype == DB2CLI::SQL_SMALLINT && convert && size <= 5 && digits <= 1
|
|
219
|
+
cps[:boolean]
|
|
220
|
+
else
|
|
221
|
+
cps[datatype]
|
|
222
|
+
end
|
|
223
|
+
[i, output_identifier(name), datatype, size, pr]
|
|
108
224
|
end
|
|
109
225
|
end
|
|
110
|
-
|
|
111
|
-
def hash_row(sth)
|
|
112
|
-
row = {}
|
|
113
|
-
@column_info.each_with_index do |c, i|
|
|
114
|
-
rc, v = SQLGetData(sth, i+1, c[:db2_type], c[:precision])
|
|
115
|
-
@db.check_error(rc, "Could not get data")
|
|
116
|
-
|
|
117
|
-
row[output_identifier(c[:name])] = convert_type(v)
|
|
118
|
-
end
|
|
119
|
-
row
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
def convert_type(v)
|
|
123
|
-
case v
|
|
124
|
-
when DB2CLI::Date
|
|
125
|
-
DBI::Date.new(v.year, v.month, v.day)
|
|
126
|
-
when DB2CLI::Time
|
|
127
|
-
DBI::Time.new(v.hour, v.minute, v.second)
|
|
128
|
-
when DB2CLI::Timestamp
|
|
129
|
-
DBI::Timestamp.new(v.year, v.month, v.day,
|
|
130
|
-
v.hour, v.minute, v.second, v.fraction)
|
|
131
|
-
when DB2CLI::Null
|
|
132
|
-
nil
|
|
133
|
-
else
|
|
134
|
-
v
|
|
135
|
-
end
|
|
136
|
-
end
|
|
137
226
|
end
|
|
138
227
|
end
|
|
139
228
|
end
|
data/lib/sequel/adapters/dbi.rb
CHANGED
|
@@ -63,10 +63,6 @@ module Sequel
|
|
|
63
63
|
::DBI.connect(dbname, opts[:user], opts[:password])
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
-
def dataset(opts = nil)
|
|
67
|
-
DBI::Dataset.new(self, opts)
|
|
68
|
-
end
|
|
69
|
-
|
|
70
66
|
def execute(sql, opts={})
|
|
71
67
|
synchronize(opts[:server]) do |conn|
|
|
72
68
|
r = log_yield(sql){conn.execute(sql)}
|
|
@@ -88,26 +84,30 @@ module Sequel
|
|
|
88
84
|
end
|
|
89
85
|
|
|
90
86
|
class Dataset < Sequel::Dataset
|
|
87
|
+
Database::DatasetClass = self
|
|
88
|
+
|
|
91
89
|
def fetch_rows(sql)
|
|
92
90
|
execute(sql) do |s|
|
|
93
91
|
begin
|
|
94
|
-
|
|
95
|
-
|
|
92
|
+
columns = cols = s.column_names.map{|c| output_identifier(c)}
|
|
93
|
+
if opts[:offset] && offset_returns_row_number_column?
|
|
94
|
+
rn = row_number_column
|
|
95
|
+
columns = columns.dup
|
|
96
|
+
columns.delete(rn)
|
|
97
|
+
end
|
|
98
|
+
@columns = columns
|
|
99
|
+
s.fetch do |r|
|
|
100
|
+
row = {}
|
|
101
|
+
cols.each{|c| row[c] = r.shift}
|
|
102
|
+
row.delete(rn) if rn
|
|
103
|
+
yield row
|
|
104
|
+
end
|
|
96
105
|
ensure
|
|
97
106
|
s.finish rescue nil
|
|
98
107
|
end
|
|
99
108
|
end
|
|
100
109
|
self
|
|
101
110
|
end
|
|
102
|
-
|
|
103
|
-
private
|
|
104
|
-
|
|
105
|
-
def hash_row(stmt, row)
|
|
106
|
-
@columns.inject({}) do |m, c|
|
|
107
|
-
m[c] = row.shift
|
|
108
|
-
m
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
111
|
end
|
|
112
112
|
end
|
|
113
113
|
end
|
|
@@ -9,11 +9,6 @@ module Sequel
|
|
|
9
9
|
module DatabaseMethods
|
|
10
10
|
include Sequel::MySQL::DatabaseMethods
|
|
11
11
|
|
|
12
|
-
# Return instance of Sequel::DataObjects::MySQL::Dataset with the given opts.
|
|
13
|
-
def dataset(opts=nil)
|
|
14
|
-
Sequel::DataObjects::MySQL::Dataset.new(self, opts)
|
|
15
|
-
end
|
|
16
|
-
|
|
17
12
|
private
|
|
18
13
|
|
|
19
14
|
# The database name for the given database. Need to parse it out
|
|
@@ -31,6 +26,9 @@ module Sequel
|
|
|
31
26
|
# Dataset class for MySQL datasets accessed via DataObjects.
|
|
32
27
|
class Dataset < DataObjects::Dataset
|
|
33
28
|
include Sequel::MySQL::DatasetMethods
|
|
29
|
+
APOS = Dataset::APOS
|
|
30
|
+
APOS_RE = Dataset::APOS_RE
|
|
31
|
+
DOUBLE_APOS = Dataset::DOUBLE_APOS
|
|
34
32
|
|
|
35
33
|
# Use execute_insert to execute the replace_sql.
|
|
36
34
|
def replace(*args)
|
|
@@ -40,8 +38,8 @@ module Sequel
|
|
|
40
38
|
private
|
|
41
39
|
|
|
42
40
|
# do_mysql sets NO_BACKSLASH_ESCAPES, so use standard SQL string escaping
|
|
43
|
-
def
|
|
44
|
-
|
|
41
|
+
def literal_string_append(sql, s)
|
|
42
|
+
sql << APOS << s.gsub(APOS_RE, DOUBLE_APOS) << APOS
|
|
45
43
|
end
|
|
46
44
|
end
|
|
47
45
|
end
|
|
@@ -55,11 +55,6 @@ module Sequel
|
|
|
55
55
|
end
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
-
# Return instance of Sequel::DataObjects::Postgres::Dataset with the given opts.
|
|
59
|
-
def dataset(opts=nil)
|
|
60
|
-
Sequel::DataObjects::Postgres::Dataset.new(self, opts)
|
|
61
|
-
end
|
|
62
|
-
|
|
63
58
|
# Run the INSERT sql on the database and return the primary key
|
|
64
59
|
# for the record.
|
|
65
60
|
def execute_insert(sql, opts={})
|
|
@@ -8,11 +8,6 @@ module Sequel
|
|
|
8
8
|
module DatabaseMethods
|
|
9
9
|
include Sequel::SQLite::DatabaseMethods
|
|
10
10
|
|
|
11
|
-
# Return Sequel::DataObjects::SQLite::Dataset object with the given opts.
|
|
12
|
-
def dataset(opts=nil)
|
|
13
|
-
Sequel::DataObjects::SQLite::Dataset.new(self, opts)
|
|
14
|
-
end
|
|
15
|
-
|
|
16
11
|
private
|
|
17
12
|
|
|
18
13
|
# Default to a single connection for a memory database.
|
data/lib/sequel/adapters/do.rb
CHANGED
|
@@ -17,16 +17,19 @@ module Sequel
|
|
|
17
17
|
Sequel.tsk_require 'do_postgres'
|
|
18
18
|
Sequel.ts_require 'adapters/do/postgres'
|
|
19
19
|
db.extend(Sequel::DataObjects::Postgres::DatabaseMethods)
|
|
20
|
+
db.dataset_class = Sequel::DataObjects::Postgres::Dataset
|
|
20
21
|
end,
|
|
21
22
|
:mysql=>proc do |db|
|
|
22
23
|
Sequel.tsk_require 'do_mysql'
|
|
23
24
|
Sequel.ts_require 'adapters/do/mysql'
|
|
24
25
|
db.extend(Sequel::DataObjects::MySQL::DatabaseMethods)
|
|
26
|
+
db.dataset_class = Sequel::DataObjects::MySQL::Dataset
|
|
25
27
|
end,
|
|
26
28
|
:sqlite3=>proc do |db|
|
|
27
29
|
Sequel.tsk_require 'do_sqlite3'
|
|
28
30
|
Sequel.ts_require 'adapters/do/sqlite'
|
|
29
31
|
db.extend(Sequel::DataObjects::SQLite::DatabaseMethods)
|
|
32
|
+
db.dataset_class = Sequel::DataObjects::SQLite::Dataset
|
|
30
33
|
end
|
|
31
34
|
}
|
|
32
35
|
|
|
@@ -36,6 +39,8 @@ module Sequel
|
|
|
36
39
|
# Sequel::DataObjects::Database object, or hack DataObjects (or Extlib) to
|
|
37
40
|
# use a pool size at least as large as the pool size being used by Sequel.
|
|
38
41
|
class Database < Sequel::Database
|
|
42
|
+
DISCONNECT_ERROR_RE = /terminating connection due to administrator command/
|
|
43
|
+
|
|
39
44
|
set_adapter_scheme :do
|
|
40
45
|
|
|
41
46
|
# Call the DATABASE_SETUP proc directly after initialization,
|
|
@@ -55,11 +60,6 @@ module Sequel
|
|
|
55
60
|
setup_connection(::DataObjects::Connection.new(uri(server_opts(server))))
|
|
56
61
|
end
|
|
57
62
|
|
|
58
|
-
# Return a Sequel::DataObjects::Dataset object for this database.
|
|
59
|
-
def dataset(opts = nil)
|
|
60
|
-
DataObjects::Dataset.new(self, opts)
|
|
61
|
-
end
|
|
62
|
-
|
|
63
63
|
# Execute the given SQL. If a block is given, the DataObjects::Reader
|
|
64
64
|
# created is yielded to it. A block should not be provided unless a
|
|
65
65
|
# a SELECT statement is being used (or something else that returns rows).
|
|
@@ -114,53 +114,32 @@ module Sequel
|
|
|
114
114
|
|
|
115
115
|
private
|
|
116
116
|
|
|
117
|
-
# DataObjects uses a special transaction object to keep track of
|
|
118
|
-
# transactions. Unfortunately, it tries to create a new connection
|
|
119
|
-
# to do a transaction. So we close the connection created and
|
|
120
|
-
# substitute our own.
|
|
121
|
-
def begin_transaction(conn, opts={})
|
|
122
|
-
return super if supports_savepoints?
|
|
123
|
-
log_yield(TRANSACTION_BEGIN) do
|
|
124
|
-
t = ::DataObjects::Transaction.create_for_uri(uri)
|
|
125
|
-
t.instance_variable_get(:@connection).close
|
|
126
|
-
t.instance_variable_set(:@connection, conn)
|
|
127
|
-
t.begin
|
|
128
|
-
t
|
|
129
|
-
end
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
# DataObjects requires transactions be prepared before being
|
|
133
|
-
# committed, so we do that.
|
|
134
|
-
def commit_transaction(t, opts={})
|
|
135
|
-
return super if supports_savepoints?
|
|
136
|
-
log_yield(TRANSACTION_ROLLBACK) do
|
|
137
|
-
t.prepare
|
|
138
|
-
t.commit
|
|
139
|
-
end
|
|
140
|
-
end
|
|
141
|
-
|
|
142
117
|
# Method to call on a statement object to execute SQL that does
|
|
143
118
|
# not return any rows.
|
|
144
119
|
def connection_execute_method
|
|
145
120
|
:execute_non_query
|
|
146
121
|
end
|
|
147
122
|
|
|
123
|
+
# dataobjects uses the DataObjects::Error class as the main error class.
|
|
124
|
+
def database_error_classes
|
|
125
|
+
[::DataObjects::Error]
|
|
126
|
+
end
|
|
127
|
+
|
|
148
128
|
# Close the given database connection.
|
|
149
129
|
def disconnect_connection(c)
|
|
150
130
|
c.close
|
|
151
131
|
end
|
|
132
|
+
|
|
133
|
+
# Recognize DataObjects::ConnectionError instances as disconnect errors.
|
|
134
|
+
def disconnect_error?(e, opts)
|
|
135
|
+
super || (e.is_a?(::DataObjects::Error) && (e.is_a?(::DataObjects::ConnectionError) || e.message =~ DISCONNECT_ERROR_RE))
|
|
136
|
+
end
|
|
152
137
|
|
|
153
138
|
# Execute SQL on the connection by creating a command first
|
|
154
139
|
def log_connection_execute(conn, sql)
|
|
155
140
|
log_yield(sql){conn.create_command(sql).execute_non_query}
|
|
156
141
|
end
|
|
157
142
|
|
|
158
|
-
# We use the transactions rollback method to rollback.
|
|
159
|
-
def rollback_transaction(t, opts={})
|
|
160
|
-
return super if supports_savepoints?
|
|
161
|
-
log_yield(TRANSACTION_COMMIT){t.rollback}
|
|
162
|
-
end
|
|
163
|
-
|
|
164
143
|
# Allow extending the given connection when it is first created.
|
|
165
144
|
# By default, just returns the connection.
|
|
166
145
|
def setup_connection(conn)
|
|
@@ -170,6 +149,8 @@ module Sequel
|
|
|
170
149
|
|
|
171
150
|
# Dataset class for Sequel::DataObjects::Database objects.
|
|
172
151
|
class Dataset < Sequel::Dataset
|
|
152
|
+
Database::DatasetClass = self
|
|
153
|
+
|
|
173
154
|
# Execute the SQL on the database and yield the rows as hashes
|
|
174
155
|
# with symbol keys.
|
|
175
156
|
def fetch_rows(sql)
|