sequel 3.21.0 → 3.32.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 +539 -3
- data/MIT-LICENSE +1 -1
- data/README.rdoc +20 -6
- data/Rakefile +37 -19
- data/bin/sequel +1 -5
- data/doc/advanced_associations.rdoc +17 -0
- data/doc/association_basics.rdoc +293 -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/release_notes/3.32.0.txt +202 -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 +52 -0
- data/lib/sequel/adapters/jdbc/derby.rb +260 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
- data/lib/sequel/adapters/jdbc/h2.rb +66 -30
- data/lib/sequel/adapters/jdbc/hsqldb.rb +177 -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 +88 -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 +339 -0
- data/lib/sequel/adapters/mysql.rb +85 -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 +355 -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 +328 -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 +278 -107
- data/lib/sequel/adapters/shared/mysql.rb +203 -65
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
- data/lib/sequel/adapters/shared/oracle.rb +237 -46
- data/lib/sequel/adapters/shared/postgres.rb +214 -82
- data/lib/sequel/adapters/shared/progress.rb +5 -8
- data/lib/sequel/adapters/shared/sqlite.rb +200 -50
- 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 +68 -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 +163 -59
- data/lib/sequel/database/schema_generator.rb +17 -7
- data/lib/sequel/database/schema_methods.rb +59 -29
- data/lib/sequel/dataset/actions.rb +240 -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 +12 -7
- data/lib/sequel/dataset/prepared_statements.rb +59 -27
- data/lib/sequel/dataset/query.rb +236 -43
- data/lib/sequel/dataset/sql.rb +676 -267
- 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/query.rb +1 -1
- 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 +1081 -344
- data/lib/sequel/model/base.rb +420 -133
- data/lib/sequel/model/errors.rb +1 -1
- data/lib/sequel/model/exceptions.rb +5 -1
- data/lib/sequel/model.rb +20 -10
- data/lib/sequel/plugins/active_model.rb +13 -1
- data/lib/sequel/plugins/association_pks.rb +22 -4
- data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
- data/lib/sequel/plugins/composition.rb +43 -10
- 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 +68 -7
- data/lib/sequel/plugins/nested_attributes.rb +65 -10
- 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 +98 -49
- data/lib/sequel/plugins/serialization_modification_detection.rb +63 -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 +135 -30
- data/spec/adapters/mysql_spec.rb +244 -212
- data/spec/adapters/oracle_spec.rb +67 -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 +789 -512
- data/spec/core/dataset_spec.rb +1176 -1061
- 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/active_model_spec.rb +13 -0
- 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/query_spec.rb +6 -0
- 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 +72 -0
- data/spec/extensions/serialization_spec.rb +81 -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 +661 -21
- data/spec/integration/database_test.rb +4 -2
- data/spec/integration/dataset_test.rb +500 -67
- data/spec/integration/eager_loader_test.rb +19 -21
- data/spec/integration/model_test.rb +80 -1
- data/spec/integration/plugin_test.rb +367 -119
- data/spec/integration/prepared_statement_test.rb +222 -125
- data/spec/integration/schema_test.rb +217 -39
- data/spec/integration/spec_helper.rb +39 -30
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +182 -5
- data/spec/integration/type_test.rb +17 -3
- data/spec/model/association_reflection_spec.rb +201 -7
- data/spec/model/associations_spec.rb +955 -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 +349 -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 +117 -73
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
= Database Transactions
|
|
2
|
+
|
|
3
|
+
Sequel uses autocommit mode by default for all of its database adapters, so in general in Sequel if you want to use database transactions, you need to be explicit about it. There are a few cases where transactions are used implicitly by default:
|
|
4
|
+
|
|
5
|
+
* Dataset#import to insert many records at once
|
|
6
|
+
* Model#save
|
|
7
|
+
* Model#destroy
|
|
8
|
+
* Migrations
|
|
9
|
+
* A few model plugins
|
|
10
|
+
|
|
11
|
+
Everywhere else, it is up to use to use a database transaction if you want to.
|
|
12
|
+
|
|
13
|
+
== Basic Transaction Usage
|
|
14
|
+
|
|
15
|
+
In Sequel, the <tt>Database#transaction</tt> method should be called if you want to use a database transaction. This method must be called with a block. If the block does not raise an exception, the transaction is committed:
|
|
16
|
+
|
|
17
|
+
DB.transaction do # BEGIN
|
|
18
|
+
DB[:foo].insert(1) # INSERT
|
|
19
|
+
end # COMMIT
|
|
20
|
+
|
|
21
|
+
If the block raises a Sequel::Rollback exception, the transaction is rolled back, but no exception is raised outside the block:
|
|
22
|
+
|
|
23
|
+
DB.transaction do # BEGIN
|
|
24
|
+
raise Sequel::Rollback
|
|
25
|
+
end # ROLLBACK
|
|
26
|
+
# no exception raised
|
|
27
|
+
|
|
28
|
+
If any other exception is raised, the transaction is rolled back, and the exception is raised outside the block:
|
|
29
|
+
|
|
30
|
+
# ArgumentError raised
|
|
31
|
+
DB.transaction do # BEGIN
|
|
32
|
+
raise ArgumentError
|
|
33
|
+
end # ROLLBACK
|
|
34
|
+
# ArgumentError raised
|
|
35
|
+
|
|
36
|
+
If you want Sequel::Rollback exceptions to be reraised, use the <tt>:rollback => :reraise</tt> option:
|
|
37
|
+
|
|
38
|
+
DB.transaction(:rollback => :reraise) do # BEGIN
|
|
39
|
+
raise Sequel::Rollback
|
|
40
|
+
end # ROLLBACK
|
|
41
|
+
# Sequel::Rollback raised
|
|
42
|
+
|
|
43
|
+
If you always want to rollback (useful for testing), use the <tt>:rollback => :always</tt> option:
|
|
44
|
+
|
|
45
|
+
DB.transaction(:rollback => :always) do # BEGIN
|
|
46
|
+
DB[:foo].insert(1) # INSERT
|
|
47
|
+
end # ROLLBACK
|
|
48
|
+
|
|
49
|
+
If you want to check whether you are currently in a transaction, use the Database#in_transaction? method:
|
|
50
|
+
|
|
51
|
+
DB.in_transaction? # false
|
|
52
|
+
DB.transaction do
|
|
53
|
+
DB.in_transaction? # true
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
== Transaction Hooks
|
|
57
|
+
|
|
58
|
+
You can add hooks to an in progress transaction that are called after the transaction commits or rolls back:
|
|
59
|
+
|
|
60
|
+
x = nil
|
|
61
|
+
DB.transaction do
|
|
62
|
+
DB.after_commit{x = 1}
|
|
63
|
+
DB.after_rollback{x = 2}
|
|
64
|
+
x # nil
|
|
65
|
+
end
|
|
66
|
+
x # 1
|
|
67
|
+
|
|
68
|
+
x = nil
|
|
69
|
+
DB.transaction do
|
|
70
|
+
DB.after_commit{x = 1}
|
|
71
|
+
DB.after_rollback{x = 2}
|
|
72
|
+
raise Sequel::Rollback
|
|
73
|
+
end
|
|
74
|
+
x # 2
|
|
75
|
+
|
|
76
|
+
== Nested Transaction Calls / Savepoints
|
|
77
|
+
|
|
78
|
+
You can nest calls to transaction, which by default just reuses the existing transaction:
|
|
79
|
+
|
|
80
|
+
DB.transaction do # BEGIN
|
|
81
|
+
DB.transaction do
|
|
82
|
+
DB[:foo].insert(1) # INSERT
|
|
83
|
+
end
|
|
84
|
+
end # COMMIT
|
|
85
|
+
|
|
86
|
+
You can use the <tt>:savepoint => true</tt> option in the inner transaction to explicitly use a savepoint (if the database supports it):
|
|
87
|
+
|
|
88
|
+
DB.transaction do # BEGIN
|
|
89
|
+
DB.transaction(:savepoint => true) do # SAVEPOINT
|
|
90
|
+
DB[:foo].insert(1) # INSERT
|
|
91
|
+
end # RELEASE SAVEPOINT
|
|
92
|
+
end # COMMIT
|
|
93
|
+
|
|
94
|
+
If a Sequel::Rollback exception is raised inside the savepoint block, it will only rollback to the savepoint:
|
|
95
|
+
|
|
96
|
+
DB.transaction do # BEGIN
|
|
97
|
+
DB.transaction(:savepoint => true) do # SAVEPOINT
|
|
98
|
+
raise Sequel::Rollback
|
|
99
|
+
end # ROLLBACK TO SAVEPOINT
|
|
100
|
+
# no exception raised
|
|
101
|
+
end # COMMIT
|
|
102
|
+
|
|
103
|
+
Other exceptions, unless rescued inside the outer transaction block, will rollback the savepoint and the outer transactions, since they are reraised by the transaction code:
|
|
104
|
+
|
|
105
|
+
DB.transaction do # BEGIN
|
|
106
|
+
DB.transaction(:savepoint => true) do # SAVEPOINT
|
|
107
|
+
raise ArgumentError
|
|
108
|
+
end # ROLLBACK TO SAVEPOINT
|
|
109
|
+
end # ROLLBACK
|
|
110
|
+
# ArgumentError raised
|
|
111
|
+
|
|
112
|
+
== Prepared Transactions / Two-Phase Commit
|
|
113
|
+
|
|
114
|
+
Sequel supports database prepared transactions on PostgreSQL, MySQL, and H2. With prepared transactions, at the end of the transaction, the transaction is not immediately committed (it acts like a rollback). Later, you can call +commit_prepared_transaction+ to commit the transaction or +rollback_prepared_transaction+ to roll the transaction back. Prepared transactions are usually used with distributed databases to make sure all databases commit the same transaction or none of them do.
|
|
115
|
+
|
|
116
|
+
To use prepared transactions in Sequel, you provide a string as the value of the :prepare option:
|
|
117
|
+
|
|
118
|
+
DB.transaction(:prepare => 'foo') do # BEGIN
|
|
119
|
+
DB[:foo].insert(1) # INSERT
|
|
120
|
+
end # PREPARE TRANSACTION 'foo'
|
|
121
|
+
|
|
122
|
+
Later, you can commit the prepared transaction:
|
|
123
|
+
|
|
124
|
+
DB.commit_prepared_transaction('foo')
|
|
125
|
+
|
|
126
|
+
or roll the prepared transaction back:
|
|
127
|
+
|
|
128
|
+
DB.rollback_prepared_transaction('foo')
|
|
129
|
+
|
|
130
|
+
== Transaction Isolation Levels
|
|
131
|
+
|
|
132
|
+
The SQL standard supports 4 isolation levels: READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, and SERIALIZABLE. Not all databases implement the levels as specified in the standard (or implement the levels at all), but on PostgreSQL, MySQL, and Microsoft SQL Server, you can specify which transaction isolation level you want to use via the :isolation option to <tt>Database#transaction</tt>. The isolation level is specified as one of the following symbols: :uncommitted, :committed, :repeatable, and :serializable. Using this option make Sequel use the correct transaction isolation syntax for your database:
|
|
133
|
+
|
|
134
|
+
DB.transaction(:isolation => :serializable) do # BEGIN
|
|
135
|
+
# SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
|
|
136
|
+
DB[:foo].insert(1) # INSERT
|
|
137
|
+
end # COMMIT
|
data/doc/virtual_rows.rdoc
CHANGED
|
@@ -40,7 +40,7 @@ available. For example, you are no longer able to do:
|
|
|
40
40
|
# WHERE a AND (b OR NOT c)
|
|
41
41
|
|
|
42
42
|
Because Symbol#&, Symbol#| and Symbol#~ are not defined when the core
|
|
43
|
-
extensions are turned off. However,
|
|
43
|
+
extensions are turned off. However, virtual rows allow almost the same
|
|
44
44
|
syntax even without the core extensions:
|
|
45
45
|
|
|
46
46
|
dataset.filter{a & (b | ~c)}
|
|
@@ -87,7 +87,7 @@ and second is the difference between the the use of "a". In the regular proc,
|
|
|
87
87
|
you couldn't call c without an explicit receiver in the proc, unless the self of the
|
|
88
88
|
surrounding scope responded to it. For a, note how ruby calls the method on
|
|
89
89
|
the receiver of the surrounding scope in the regular proc, which returns an integer,
|
|
90
|
-
and does the
|
|
90
|
+
and does the substitution before Sequel gets access to it. In the instance evaled
|
|
91
91
|
proc, calling a without a receiver calls the a method on the VirtualRow instance.
|
|
92
92
|
For b, note that it operates the same in both cases, as it is a local variable.
|
|
93
93
|
|
|
@@ -11,11 +11,6 @@ module Sequel
|
|
|
11
11
|
# delete query.
|
|
12
12
|
ROWS_AFFECTED = "SELECT @@ROWCOUNT AS AffectedRows"
|
|
13
13
|
|
|
14
|
-
# Return instance of Sequel::ADO::MSSQL::Dataset with the given opts.
|
|
15
|
-
def dataset(opts=nil)
|
|
16
|
-
Sequel::ADO::MSSQL::Dataset.new(self, opts)
|
|
17
|
-
end
|
|
18
|
-
|
|
19
14
|
# Just execute so it doesn't attempt to return the number of rows modified.
|
|
20
15
|
def execute_ddl(sql, opts={})
|
|
21
16
|
execute(sql, opts)
|
|
@@ -37,11 +32,28 @@ module Sequel
|
|
|
37
32
|
end
|
|
38
33
|
end
|
|
39
34
|
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
# The ADO adapter's default provider doesn't support transactions, since it
|
|
39
|
+
# creates a new native connection for each query. So Sequel only attempts
|
|
40
|
+
# to use transactions if an explicit :provider is given.
|
|
41
|
+
def begin_transaction(conn, opts={})
|
|
42
|
+
super if @opts[:provider]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def commit_transaction(conn, opts={})
|
|
46
|
+
super if @opts[:provider]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def rollback_transaction(conn, opts={})
|
|
50
|
+
super if @opts[:provider]
|
|
51
|
+
end
|
|
40
52
|
end
|
|
41
53
|
|
|
42
54
|
class Dataset < ADO::Dataset
|
|
43
55
|
include Sequel::MSSQL::DatasetMethods
|
|
44
|
-
|
|
56
|
+
|
|
45
57
|
# Use a nasty hack of multiple SQL statements in the same call and
|
|
46
58
|
# having the last one return the most recently inserted id. This
|
|
47
59
|
# is necessary as ADO's default :provider uses a separate native
|
data/lib/sequel/adapters/ado.rb
CHANGED
|
@@ -4,6 +4,8 @@ module Sequel
|
|
|
4
4
|
# The ADO adapter provides connectivity to ADO databases in Windows.
|
|
5
5
|
module ADO
|
|
6
6
|
class Database < Sequel::Database
|
|
7
|
+
DISCONNECT_ERROR_RE = /Communication link failure/
|
|
8
|
+
|
|
7
9
|
set_adapter_scheme :ado
|
|
8
10
|
|
|
9
11
|
def initialize(opts)
|
|
@@ -12,12 +14,15 @@ module Sequel
|
|
|
12
14
|
when /Microsoft\.(Jet|ACE)\.OLEDB/io
|
|
13
15
|
Sequel.ts_require 'adapters/shared/access'
|
|
14
16
|
extend Sequel::Access::DatabaseMethods
|
|
15
|
-
|
|
17
|
+
extend_datasets(Sequel::Access::DatasetMethods)
|
|
18
|
+
else
|
|
16
19
|
@opts[:driver] ||= 'SQL Server'
|
|
17
20
|
case @opts[:driver]
|
|
18
21
|
when 'SQL Server'
|
|
19
22
|
Sequel.ts_require 'adapters/ado/mssql'
|
|
20
23
|
extend Sequel::ADO::MSSQL::DatabaseMethods
|
|
24
|
+
@dataset_class = ADO::MSSQL::Dataset
|
|
25
|
+
set_mssql_unicode_strings
|
|
21
26
|
end
|
|
22
27
|
end
|
|
23
28
|
end
|
|
@@ -52,10 +57,6 @@ module Sequel
|
|
|
52
57
|
handle
|
|
53
58
|
end
|
|
54
59
|
|
|
55
|
-
def dataset(opts = nil)
|
|
56
|
-
ADO::Dataset.new(self, opts)
|
|
57
|
-
end
|
|
58
|
-
|
|
59
60
|
def execute(sql, opts={})
|
|
60
61
|
synchronize(opts[:server]) do |conn|
|
|
61
62
|
begin
|
|
@@ -74,28 +75,49 @@ module Sequel
|
|
|
74
75
|
# The ADO adapter's default provider doesn't support transactions, since it
|
|
75
76
|
# creates a new native connection for each query. So Sequel only attempts
|
|
76
77
|
# to use transactions if an explicit :provider is given.
|
|
77
|
-
def
|
|
78
|
-
|
|
79
|
-
th = Thread.current
|
|
80
|
-
begin
|
|
81
|
-
@transactions << th
|
|
82
|
-
yield conn
|
|
83
|
-
rescue Sequel::Rollback
|
|
84
|
-
ensure
|
|
85
|
-
@transactions.delete(th)
|
|
86
|
-
end
|
|
78
|
+
def begin_transaction(conn, opts={})
|
|
79
|
+
super if @opts[:provider]
|
|
87
80
|
end
|
|
88
|
-
|
|
81
|
+
|
|
82
|
+
def commit_transaction(conn, opts={})
|
|
83
|
+
super if @opts[:provider]
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def database_error_classes
|
|
87
|
+
[::WIN32OLERuntimeError]
|
|
88
|
+
end
|
|
89
|
+
|
|
89
90
|
def disconnect_connection(conn)
|
|
90
91
|
conn.Close
|
|
91
92
|
end
|
|
93
|
+
|
|
94
|
+
def disconnect_error?(e, opts)
|
|
95
|
+
super || (e.is_a?(::WIN32OLERuntimeError) && e.message =~ DISCONNECT_ERROR_RE)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def rollback_transaction(conn, opts={})
|
|
99
|
+
super if @opts[:provider]
|
|
100
|
+
end
|
|
92
101
|
end
|
|
93
102
|
|
|
94
103
|
class Dataset < Sequel::Dataset
|
|
104
|
+
Database::DatasetClass = self
|
|
105
|
+
|
|
95
106
|
def fetch_rows(sql)
|
|
96
107
|
execute(sql) do |s|
|
|
97
|
-
|
|
98
|
-
|
|
108
|
+
columns = cols = s.Fields.extend(Enumerable).map{|column| output_identifier(column.Name)}
|
|
109
|
+
if opts[:offset] && offset_returns_row_number_column?
|
|
110
|
+
rn = row_number_column
|
|
111
|
+
columns = columns.dup
|
|
112
|
+
columns.delete(rn)
|
|
113
|
+
end
|
|
114
|
+
@columns = columns
|
|
115
|
+
s.getRows.transpose.each do |r|
|
|
116
|
+
row = {}
|
|
117
|
+
cols.each{|c| row[c] = r.shift}
|
|
118
|
+
row.delete(rn) if rn
|
|
119
|
+
yield row
|
|
120
|
+
end unless s.eof
|
|
99
121
|
end
|
|
100
122
|
end
|
|
101
123
|
|
|
@@ -14,6 +14,12 @@ module Sequel
|
|
|
14
14
|
'float' => ['float', 'double', 'real', 'double precision'],
|
|
15
15
|
'decimal' => %w'numeric decimal money'
|
|
16
16
|
)
|
|
17
|
+
|
|
18
|
+
# Store the related database object, in order to be able to correctly
|
|
19
|
+
# handle the database timezone.
|
|
20
|
+
def initialize(db)
|
|
21
|
+
@db = db
|
|
22
|
+
end
|
|
17
23
|
|
|
18
24
|
# Return blobs as instances of Sequel::SQL::Blob instead of
|
|
19
25
|
# Amalgamite::Blob
|
|
@@ -29,7 +35,11 @@ module Sequel
|
|
|
29
35
|
|
|
30
36
|
# Return datetime types as instances of Sequel.datetime_class
|
|
31
37
|
def datetime(s)
|
|
32
|
-
|
|
38
|
+
@db.to_application_timestamp(s)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def time(s)
|
|
42
|
+
Sequel.string_to_time(s)
|
|
33
43
|
end
|
|
34
44
|
|
|
35
45
|
# Don't raise an error if the value is a string and the declared
|
|
@@ -68,7 +78,8 @@ module Sequel
|
|
|
68
78
|
opts[:database] = ':memory:' if blank_object?(opts[:database])
|
|
69
79
|
db = ::Amalgalite::Database.new(opts[:database])
|
|
70
80
|
db.busy_handler(::Amalgalite::BusyTimeout.new(opts.fetch(:timeout, 5000)/50, 50))
|
|
71
|
-
db.type_map = SequelTypeMap.new
|
|
81
|
+
db.type_map = SequelTypeMap.new(self)
|
|
82
|
+
connection_pragmas.each{|s| log_yield(s){db.execute_batch(s)}}
|
|
72
83
|
db
|
|
73
84
|
end
|
|
74
85
|
|
|
@@ -77,11 +88,6 @@ module Sequel
|
|
|
77
88
|
:sqlite
|
|
78
89
|
end
|
|
79
90
|
|
|
80
|
-
# Return instance of Sequel::Amalgalite::Dataset with the given options.
|
|
81
|
-
def dataset(opts = nil)
|
|
82
|
-
Amalgalite::Dataset.new(self, opts)
|
|
83
|
-
end
|
|
84
|
-
|
|
85
91
|
# Run the given SQL with the given arguments. Returns nil.
|
|
86
92
|
def execute_ddl(sql, opts={})
|
|
87
93
|
_execute(sql, opts){|conn| log_yield(sql){conn.execute_batch(sql)}}
|
|
@@ -151,6 +157,8 @@ module Sequel
|
|
|
151
157
|
# Dataset class for SQLite datasets that use the amalgalite driver.
|
|
152
158
|
class Dataset < Sequel::Dataset
|
|
153
159
|
include ::Sequel::SQLite::DatasetMethods
|
|
160
|
+
|
|
161
|
+
Database::DatasetClass = self
|
|
154
162
|
|
|
155
163
|
# Yield a hash for each row in the dataset.
|
|
156
164
|
def fetch_rows(sql)
|
|
@@ -168,8 +176,8 @@ module Sequel
|
|
|
168
176
|
private
|
|
169
177
|
|
|
170
178
|
# Quote the string using the adapter instance method.
|
|
171
|
-
def
|
|
172
|
-
db.synchronize{|c| c.quote(v)}
|
|
179
|
+
def literal_string_append(sql, v)
|
|
180
|
+
db.synchronize{|c| sql << c.quote(v)}
|
|
173
181
|
end
|
|
174
182
|
end
|
|
175
183
|
end
|
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
|