sequel 3.28.0 → 3.29.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 +119 -3
- data/Rakefile +5 -3
- data/bin/sequel +1 -5
- data/doc/model_hooks.rdoc +9 -1
- data/doc/opening_databases.rdoc +49 -40
- data/doc/prepared_statements.rdoc +27 -6
- data/doc/release_notes/3.28.0.txt +2 -2
- data/doc/release_notes/3.29.0.txt +459 -0
- data/doc/sharding.rdoc +7 -1
- data/doc/testing.rdoc +18 -9
- data/doc/transactions.rdoc +41 -1
- data/lib/sequel/adapters/ado.rb +28 -17
- data/lib/sequel/adapters/ado/mssql.rb +18 -6
- data/lib/sequel/adapters/amalgalite.rb +11 -7
- data/lib/sequel/adapters/db2.rb +122 -70
- data/lib/sequel/adapters/dbi.rb +15 -15
- data/lib/sequel/adapters/do.rb +5 -36
- data/lib/sequel/adapters/do/mysql.rb +0 -5
- data/lib/sequel/adapters/do/postgres.rb +0 -5
- data/lib/sequel/adapters/do/sqlite.rb +0 -5
- data/lib/sequel/adapters/firebird.rb +3 -6
- data/lib/sequel/adapters/ibmdb.rb +24 -16
- data/lib/sequel/adapters/informix.rb +2 -4
- data/lib/sequel/adapters/jdbc.rb +47 -11
- data/lib/sequel/adapters/jdbc/as400.rb +5 -24
- data/lib/sequel/adapters/jdbc/db2.rb +0 -5
- data/lib/sequel/adapters/jdbc/derby.rb +217 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +0 -5
- data/lib/sequel/adapters/jdbc/h2.rb +10 -12
- data/lib/sequel/adapters/jdbc/hsqldb.rb +166 -0
- data/lib/sequel/adapters/jdbc/informix.rb +0 -5
- data/lib/sequel/adapters/jdbc/jtds.rb +0 -5
- data/lib/sequel/adapters/jdbc/mysql.rb +0 -10
- data/lib/sequel/adapters/jdbc/oracle.rb +70 -3
- data/lib/sequel/adapters/jdbc/postgresql.rb +0 -11
- data/lib/sequel/adapters/jdbc/sqlite.rb +0 -5
- data/lib/sequel/adapters/jdbc/sqlserver.rb +0 -5
- data/lib/sequel/adapters/jdbc/transactions.rb +56 -7
- data/lib/sequel/adapters/mock.rb +315 -0
- data/lib/sequel/adapters/mysql.rb +64 -51
- data/lib/sequel/adapters/mysql2.rb +15 -9
- data/lib/sequel/adapters/odbc.rb +13 -6
- data/lib/sequel/adapters/odbc/db2.rb +0 -4
- data/lib/sequel/adapters/odbc/mssql.rb +0 -5
- data/lib/sequel/adapters/openbase.rb +2 -4
- data/lib/sequel/adapters/oracle.rb +333 -51
- data/lib/sequel/adapters/postgres.rb +80 -27
- data/lib/sequel/adapters/shared/access.rb +0 -6
- data/lib/sequel/adapters/shared/db2.rb +13 -15
- data/lib/sequel/adapters/shared/firebird.rb +6 -6
- data/lib/sequel/adapters/shared/mssql.rb +23 -18
- data/lib/sequel/adapters/shared/mysql.rb +6 -6
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +6 -0
- data/lib/sequel/adapters/shared/oracle.rb +185 -30
- data/lib/sequel/adapters/shared/postgres.rb +35 -18
- data/lib/sequel/adapters/shared/progress.rb +0 -6
- data/lib/sequel/adapters/shared/sqlite.rb +116 -37
- data/lib/sequel/adapters/sqlite.rb +16 -8
- data/lib/sequel/adapters/swift.rb +5 -5
- data/lib/sequel/adapters/swift/mysql.rb +0 -5
- data/lib/sequel/adapters/swift/postgres.rb +0 -5
- data/lib/sequel/adapters/swift/sqlite.rb +6 -4
- data/lib/sequel/adapters/tinytds.rb +13 -10
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +8 -0
- data/lib/sequel/core.rb +40 -0
- data/lib/sequel/database/connecting.rb +1 -2
- data/lib/sequel/database/dataset.rb +3 -3
- data/lib/sequel/database/dataset_defaults.rb +58 -0
- data/lib/sequel/database/misc.rb +62 -2
- data/lib/sequel/database/query.rb +113 -49
- data/lib/sequel/database/schema_methods.rb +7 -2
- data/lib/sequel/dataset/actions.rb +37 -19
- data/lib/sequel/dataset/features.rb +24 -0
- data/lib/sequel/dataset/graph.rb +7 -6
- data/lib/sequel/dataset/misc.rb +11 -3
- data/lib/sequel/dataset/mutation.rb +2 -3
- data/lib/sequel/dataset/prepared_statements.rb +6 -4
- data/lib/sequel/dataset/query.rb +46 -15
- data/lib/sequel/dataset/sql.rb +28 -4
- data/lib/sequel/extensions/named_timezones.rb +5 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
- data/lib/sequel/model.rb +2 -1
- data/lib/sequel/model/associations.rb +115 -33
- data/lib/sequel/model/base.rb +91 -31
- data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
- data/lib/sequel/plugins/dataset_associations.rb +100 -0
- data/lib/sequel/plugins/force_encoding.rb +6 -6
- data/lib/sequel/plugins/identity_map.rb +1 -1
- data/lib/sequel/plugins/many_through_many.rb +6 -10
- data/lib/sequel/plugins/prepared_statements.rb +12 -1
- data/lib/sequel/plugins/prepared_statements_associations.rb +1 -1
- data/lib/sequel/plugins/rcte_tree.rb +29 -15
- data/lib/sequel/plugins/serialization.rb +6 -1
- data/lib/sequel/plugins/sharding.rb +0 -5
- data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
- data/lib/sequel/plugins/typecast_on_load.rb +9 -12
- data/lib/sequel/plugins/update_primary_key.rb +1 -1
- data/lib/sequel/timezones.rb +42 -42
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +29 -29
- data/spec/adapters/mysql_spec.rb +86 -104
- data/spec/adapters/oracle_spec.rb +48 -76
- data/spec/adapters/postgres_spec.rb +98 -33
- data/spec/adapters/spec_helper.rb +0 -5
- data/spec/adapters/sqlite_spec.rb +24 -21
- data/spec/core/connection_pool_spec.rb +9 -15
- data/spec/core/core_sql_spec.rb +20 -31
- data/spec/core/database_spec.rb +491 -227
- data/spec/core/dataset_spec.rb +638 -1051
- data/spec/core/expression_filters_spec.rb +0 -1
- data/spec/core/mock_adapter_spec.rb +378 -0
- data/spec/core/object_graph_spec.rb +48 -114
- data/spec/core/schema_generator_spec.rb +3 -3
- data/spec/core/schema_spec.rb +51 -114
- data/spec/core/spec_helper.rb +3 -90
- data/spec/extensions/class_table_inheritance_spec.rb +1 -1
- data/spec/extensions/dataset_associations_spec.rb +199 -0
- data/spec/extensions/instance_hooks_spec.rb +71 -0
- data/spec/extensions/named_timezones_spec.rb +22 -2
- data/spec/extensions/nested_attributes_spec.rb +3 -0
- data/spec/extensions/schema_spec.rb +1 -1
- data/spec/extensions/serialization_modification_detection_spec.rb +1 -0
- data/spec/extensions/serialization_spec.rb +5 -8
- data/spec/extensions/spec_helper.rb +4 -0
- data/spec/extensions/thread_local_timezones_spec.rb +22 -2
- data/spec/extensions/typecast_on_load_spec.rb +1 -6
- data/spec/integration/associations_test.rb +123 -12
- data/spec/integration/dataset_test.rb +140 -47
- data/spec/integration/eager_loader_test.rb +19 -21
- data/spec/integration/model_test.rb +80 -1
- data/spec/integration/plugin_test.rb +179 -128
- data/spec/integration/prepared_statement_test.rb +92 -91
- data/spec/integration/schema_test.rb +42 -23
- data/spec/integration/spec_helper.rb +25 -31
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +161 -34
- data/spec/integration/type_test.rb +3 -3
- data/spec/model/association_reflection_spec.rb +83 -7
- data/spec/model/associations_spec.rb +393 -676
- data/spec/model/base_spec.rb +186 -116
- data/spec/model/dataset_methods_spec.rb +7 -27
- data/spec/model/eager_loading_spec.rb +343 -867
- data/spec/model/hooks_spec.rb +160 -79
- data/spec/model/model_spec.rb +118 -165
- data/spec/model/plugins_spec.rb +7 -13
- data/spec/model/record_spec.rb +138 -207
- data/spec/model/spec_helper.rb +10 -73
- metadata +14 -8
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
Sequel.require 'adapters/jdbc/transactions'
|
|
2
|
+
|
|
1
3
|
module Sequel
|
|
2
4
|
module JDBC
|
|
3
5
|
# Database and Dataset support for AS400 databases accessed via JDBC.
|
|
4
6
|
module AS400
|
|
5
7
|
# Instance methods for AS400 Database objects accessed via JDBC.
|
|
6
8
|
module DatabaseMethods
|
|
9
|
+
include Sequel::JDBC::Transactions
|
|
10
|
+
|
|
7
11
|
TRANSACTION_BEGIN = 'Transaction.begin'.freeze
|
|
8
12
|
TRANSACTION_COMMIT = 'Transaction.commit'.freeze
|
|
9
13
|
TRANSACTION_ROLLBACK = 'Transaction.rollback'.freeze
|
|
@@ -13,11 +17,6 @@ module Sequel
|
|
|
13
17
|
:as400
|
|
14
18
|
end
|
|
15
19
|
|
|
16
|
-
# Return Sequel::JDBC::AS400::Dataset object with the given opts.
|
|
17
|
-
def dataset(opts=nil)
|
|
18
|
-
Sequel::JDBC::AS400::Dataset.new(self, opts)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
20
|
# TODO: Fix for AS400
|
|
22
21
|
def last_insert_id(conn, opts={})
|
|
23
22
|
nil
|
|
@@ -33,25 +32,7 @@ module Sequel
|
|
|
33
32
|
# Use JDBC connection's setAutoCommit to false to start transactions
|
|
34
33
|
def begin_transaction(conn, opts={})
|
|
35
34
|
set_transaction_isolation(conn, opts)
|
|
36
|
-
|
|
37
|
-
conn
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
# Use JDBC connection's commit method to commit transactions
|
|
41
|
-
def commit_transaction(conn, opts={})
|
|
42
|
-
log_yield(TRANSACTION_COMMIT){conn.commit}
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
# Use JDBC connection's setAutoCommit to true to enable default
|
|
46
|
-
# auto-commit mode
|
|
47
|
-
def remove_transaction(conn)
|
|
48
|
-
conn.setAutoCommit(true) if conn
|
|
49
|
-
@transactions.delete(Thread.current)
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
# Use JDBC connection's rollback method to rollback transactions
|
|
53
|
-
def rollback_transaction(conn, opts={})
|
|
54
|
-
log_yield(TRANSACTION_ROLLBACK){conn.rollback}
|
|
35
|
+
super
|
|
55
36
|
end
|
|
56
37
|
end
|
|
57
38
|
|
|
@@ -19,11 +19,6 @@ module Sequel
|
|
|
19
19
|
include Sequel::DB2::DatabaseMethods
|
|
20
20
|
include Sequel::JDBC::Transactions
|
|
21
21
|
|
|
22
|
-
# Return instance of Sequel::JDBC::DB2::Dataset with the given opts.
|
|
23
|
-
def dataset(opts=nil)
|
|
24
|
-
Sequel::JDBC::DB2::Dataset.new(self, opts)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
22
|
%w'schema_parse_table tables views indexes'.each do |s|
|
|
28
23
|
class_eval("def #{s}(*a) jdbc_#{s}(*a) end", __FILE__, __LINE__)
|
|
29
24
|
end
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
Sequel.require 'adapters/jdbc/transactions'
|
|
2
|
+
|
|
3
|
+
module Sequel
|
|
4
|
+
module JDBC
|
|
5
|
+
# Database and Dataset support for Derby databases accessed via JDBC.
|
|
6
|
+
module Derby
|
|
7
|
+
# Instance methods for Derby Database objects accessed via JDBC.
|
|
8
|
+
module DatabaseMethods
|
|
9
|
+
include ::Sequel::JDBC::Transactions
|
|
10
|
+
|
|
11
|
+
# Derby doesn't support casting integer to varchar, only integer to char,
|
|
12
|
+
# and char(254) appears to have the widest support (with char(255) failing).
|
|
13
|
+
# This does add a bunch of extra spaces at the end, but those will be trimmed
|
|
14
|
+
# elsewhere.
|
|
15
|
+
def cast_type_literal(type)
|
|
16
|
+
(type == String) ? 'CHAR(254)' : super
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Derby uses the :derby database type.
|
|
20
|
+
def database_type
|
|
21
|
+
:derby
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Derby uses an IDENTITY sequence for autoincrementing columns.
|
|
25
|
+
def serial_primary_key_options
|
|
26
|
+
{:primary_key => true, :type => :integer, :identity=>true, :start_with=>1}
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# The SVN version of the database.
|
|
30
|
+
def svn_version
|
|
31
|
+
@svn_version ||= begin
|
|
32
|
+
v = synchronize{|c| c.get_meta_data.get_database_product_version}
|
|
33
|
+
v =~ /\((\d+)\)\z/
|
|
34
|
+
$1.to_i
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
# Derby-specific syntax for renaming columns and changing a columns type/nullity.
|
|
41
|
+
def alter_table_sql(table, op)
|
|
42
|
+
case op[:op]
|
|
43
|
+
when :rename_column
|
|
44
|
+
"RENAME COLUMN #{quote_schema_table(table)}.#{quote_identifier(op[:name])} TO #{quote_identifier(op[:new_name])}"
|
|
45
|
+
when :set_column_type
|
|
46
|
+
# Derby is very limited in changing a columns type, so adding a new column and then dropping the existing column is
|
|
47
|
+
# the best approach, as mentioned in the Derby documentation.
|
|
48
|
+
temp_name = :x_sequel_temp_column_x
|
|
49
|
+
[alter_table_sql(table, op.merge(:op=>:add_column, :name=>temp_name)),
|
|
50
|
+
from(table).update_sql(temp_name=>::Sequel::SQL::Cast.new(op[:name], op[:type])),
|
|
51
|
+
alter_table_sql(table, op.merge(:op=>:drop_column)),
|
|
52
|
+
alter_table_sql(table, op.merge(:op=>:rename_column, :name=>temp_name, :new_name=>op[:name]))]
|
|
53
|
+
when :set_column_null
|
|
54
|
+
"ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(op[:name])} #{op[:null] ? 'NULL' : 'NOT NULL'}"
|
|
55
|
+
else
|
|
56
|
+
super
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Derby doesn't allow specifying NULL for columns, only NOT NULL.
|
|
61
|
+
def column_definition_null_sql(sql, column)
|
|
62
|
+
sql << " NOT NULL" if column.fetch(:null, column[:allow_null]) == false
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Temporary table creation on Derby use DECLARE instead of CREATE.
|
|
66
|
+
def create_table_sql(name, generator, options)
|
|
67
|
+
if options[:temp]
|
|
68
|
+
"DECLARE GLOBAL TEMPORARY TABLE #{quote_identifier(name)} (#{column_list_sql(generator)}) NOT LOGGED"
|
|
69
|
+
else
|
|
70
|
+
super
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Use IDENTITY_VAL_LOCAL() to get the last inserted id.
|
|
75
|
+
def last_insert_id(conn, opts={})
|
|
76
|
+
statement(conn) do |stmt|
|
|
77
|
+
sql = 'SELECT IDENTITY_VAL_LOCAL() FROM sysibm.sysdummy1'
|
|
78
|
+
rs = log_yield(sql){stmt.executeQuery(sql)}
|
|
79
|
+
rs.next
|
|
80
|
+
rs.getInt(1)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Derby uses RENAME TABLE syntax to rename tables.
|
|
85
|
+
def rename_table_sql(name, new_name)
|
|
86
|
+
"RENAME TABLE #{quote_schema_table(name)} TO #{quote_schema_table(new_name)}"
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# If an :identity option is present in the column, add the necessary IDENTITY SQL.
|
|
90
|
+
def type_literal(column)
|
|
91
|
+
if column[:identity]
|
|
92
|
+
sql = "#{super} GENERATED BY DEFAULT AS IDENTITY"
|
|
93
|
+
if sw = column[:start_with]
|
|
94
|
+
sql << " (START WITH #{sw.to_i}"
|
|
95
|
+
sql << " INCREMENT BY #{column[:increment_by].to_i}" if column[:increment_by]
|
|
96
|
+
sql << ")"
|
|
97
|
+
end
|
|
98
|
+
sql
|
|
99
|
+
else
|
|
100
|
+
super
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Dataset class for Derby datasets accessed via JDBC.
|
|
106
|
+
class Dataset < JDBC::Dataset
|
|
107
|
+
BOOL_TRUE = '(1 = 1)'.freeze
|
|
108
|
+
BOOL_FALSE = '(1 = 0)'.freeze
|
|
109
|
+
SELECT_CLAUSE_METHODS = clause_methods(:select, %w'distinct columns from join where group having compounds order limit lock')
|
|
110
|
+
|
|
111
|
+
# Derby doesn't support an expression between CASE and WHEN,
|
|
112
|
+
# so emulate it by using an equality statement for all of the
|
|
113
|
+
# conditions.
|
|
114
|
+
def case_expression_sql(ce)
|
|
115
|
+
if ce.expression?
|
|
116
|
+
e = ce.expression
|
|
117
|
+
literal(::Sequel::SQL::CaseExpression.new(ce.conditions.map{|c, r| [::Sequel::SQL::BooleanExpression.new(:'=', e, c), r]}, ce.default))
|
|
118
|
+
else
|
|
119
|
+
super
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# If the type is String, trim the extra spaces since CHAR is used instead
|
|
124
|
+
# of varchar. This can cause problems if you are casting a char/varchar to
|
|
125
|
+
# a string and the ending whitespace is important.
|
|
126
|
+
def cast_sql(expr, type)
|
|
127
|
+
if type == String
|
|
128
|
+
"RTRIM(#{super})"
|
|
129
|
+
else
|
|
130
|
+
super
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Handle Derby specific LIKE, extract, and some bitwise compliment support.
|
|
135
|
+
def complex_expression_sql(op, args)
|
|
136
|
+
case op
|
|
137
|
+
when :ILIKE
|
|
138
|
+
super(:LIKE, [SQL::Function.new(:upper, args.at(0)), SQL::Function.new(:upper, args.at(1)) ])
|
|
139
|
+
when :"NOT ILIKE"
|
|
140
|
+
super(:"NOT LIKE", [SQL::Function.new(:upper, args.at(0)), SQL::Function.new(:upper, args.at(1)) ])
|
|
141
|
+
when :&, :|, :^, :<<, :>>
|
|
142
|
+
raise Error, "Derby doesn't support the #{op} operator"
|
|
143
|
+
when :'B~'
|
|
144
|
+
"((0 - #{literal(args.at(0))}) - 1)"
|
|
145
|
+
when :extract
|
|
146
|
+
"#{args.at(0)}(#{literal(args.at(1))})"
|
|
147
|
+
else
|
|
148
|
+
super
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Derby does not support IS TRUE.
|
|
153
|
+
def supports_is_true?
|
|
154
|
+
false
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Derby does not support IN/NOT IN with multiple columns
|
|
158
|
+
def supports_multiple_column_in?
|
|
159
|
+
false
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
private
|
|
163
|
+
|
|
164
|
+
# Derby needs a hex string casted to BLOB for blobs.
|
|
165
|
+
def literal_blob(v)
|
|
166
|
+
blob = "CAST(X'"
|
|
167
|
+
v.each_byte{|x| blob << sprintf('%02x', x)}
|
|
168
|
+
blob << "' AS BLOB)"
|
|
169
|
+
blob
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Derby needs the standard workaround to insert all default values into
|
|
173
|
+
# a table with more than one column.
|
|
174
|
+
def insert_supports_empty_values?
|
|
175
|
+
false
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Derby uses an expression yielding false for false values.
|
|
179
|
+
# Newer versions can use the FALSE literal, but the latest gem version cannot.
|
|
180
|
+
def literal_false
|
|
181
|
+
BOOL_FALSE
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Derby handles fractional seconds in timestamps, but not in times
|
|
185
|
+
def literal_sqltime(v)
|
|
186
|
+
v.strftime("'%H:%M:%S'")
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# Derby uses an expression yielding true for true values.
|
|
190
|
+
# Newer versions can use the TRUE literal, but the latest gem version cannot.
|
|
191
|
+
def literal_true
|
|
192
|
+
BOOL_TRUE
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Derby doesn't support common table expressions.
|
|
196
|
+
def select_clause_methods
|
|
197
|
+
SELECT_CLAUSE_METHODS
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Use a default FROM table if the dataset does not contain a FROM table.
|
|
201
|
+
def select_from_sql(sql)
|
|
202
|
+
if @opts[:from]
|
|
203
|
+
super
|
|
204
|
+
else
|
|
205
|
+
sql << " FROM sysibm.sysdummy1"
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Offset comes before limit in Derby
|
|
210
|
+
def select_limit_sql(sql)
|
|
211
|
+
sql << " OFFSET #{literal(@opts[:offset])} ROWS" if @opts[:offset]
|
|
212
|
+
sql << " FETCH FIRST #{literal(@opts[:limit])} ROWS ONLY" if @opts[:limit]
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|
|
@@ -18,11 +18,6 @@ module Sequel
|
|
|
18
18
|
@primary_keys = {}
|
|
19
19
|
end
|
|
20
20
|
end
|
|
21
|
-
|
|
22
|
-
# Return instance of Sequel::JDBC::Firebird::Dataset with the given opts.
|
|
23
|
-
def dataset(opts=nil)
|
|
24
|
-
Sequel::JDBC::Firebird::Dataset.new(self, opts)
|
|
25
|
-
end
|
|
26
21
|
end
|
|
27
22
|
|
|
28
23
|
# Dataset class for Firebird datasets accessed via JDBC.
|
|
@@ -17,11 +17,6 @@ module Sequel
|
|
|
17
17
|
:h2
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
# Return Sequel::JDBC::H2::Dataset object with the given opts.
|
|
21
|
-
def dataset(opts=nil)
|
|
22
|
-
Sequel::JDBC::H2::Dataset.new(self, opts)
|
|
23
|
-
end
|
|
24
|
-
|
|
25
20
|
# Rollback an existing prepared transaction with the given transaction
|
|
26
21
|
# identifier string.
|
|
27
22
|
def rollback_prepared_transaction(transaction_id)
|
|
@@ -53,8 +48,8 @@ module Sequel
|
|
|
53
48
|
# If the :prepare option is given and we aren't in a savepoint,
|
|
54
49
|
# prepare the transaction for a two-phase commit.
|
|
55
50
|
def commit_transaction(conn, opts={})
|
|
56
|
-
if opts[:prepare] &&
|
|
57
|
-
log_connection_execute(conn, "PREPARE COMMIT #{
|
|
51
|
+
if (s = opts[:prepare]) && @transactions[conn][:savepoint_level] <= 1
|
|
52
|
+
log_connection_execute(conn, "PREPARE COMMIT #{s}")
|
|
58
53
|
else
|
|
59
54
|
super
|
|
60
55
|
end
|
|
@@ -95,7 +90,8 @@ module Sequel
|
|
|
95
90
|
# Use IDENTITY() to get the last inserted id.
|
|
96
91
|
def last_insert_id(conn, opts={})
|
|
97
92
|
statement(conn) do |stmt|
|
|
98
|
-
|
|
93
|
+
sql = 'SELECT IDENTITY();'
|
|
94
|
+
rs = log_yield(sql){stmt.executeQuery(sql)}
|
|
99
95
|
rs.next
|
|
100
96
|
rs.getInt(1)
|
|
101
97
|
end
|
|
@@ -111,7 +107,7 @@ module Sequel
|
|
|
111
107
|
SELECT_CLAUSE_METHODS = clause_methods(:select, %w'distinct columns from join where group having compounds order limit')
|
|
112
108
|
BITWISE_METHOD_MAP = {:& =>:BITAND, :| => :BITOR, :^ => :BITXOR}
|
|
113
109
|
|
|
114
|
-
#
|
|
110
|
+
# Emulate the case insensitive LIKE operator and the bitwise operators.
|
|
115
111
|
def complex_expression_sql(op, args)
|
|
116
112
|
case op
|
|
117
113
|
when :ILIKE
|
|
@@ -119,11 +115,13 @@ module Sequel
|
|
|
119
115
|
when :"NOT ILIKE"
|
|
120
116
|
super(:"NOT LIKE", [SQL::PlaceholderLiteralString.new("CAST(? AS VARCHAR_IGNORECASE)", [args.at(0)]), args.at(1)])
|
|
121
117
|
when :&, :|, :^
|
|
122
|
-
literal(SQL::Function.new(BITWISE_METHOD_MAP[op],
|
|
118
|
+
complex_expression_arg_pairs(args){|a, b| literal(SQL::Function.new(BITWISE_METHOD_MAP[op], a, b))}
|
|
123
119
|
when :<<
|
|
124
|
-
"(#{literal(
|
|
120
|
+
complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} * POWER(2, #{literal(b)}))"}
|
|
125
121
|
when :>>
|
|
126
|
-
"(#{literal(
|
|
122
|
+
complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} / POWER(2, #{literal(b)}))"}
|
|
123
|
+
when :'B~'
|
|
124
|
+
"((0 - #{literal(args.at(0))}) - 1)"
|
|
127
125
|
else
|
|
128
126
|
super(op, args)
|
|
129
127
|
end
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
Sequel.require 'adapters/jdbc/transactions'
|
|
2
|
+
|
|
3
|
+
module Sequel
|
|
4
|
+
module JDBC
|
|
5
|
+
# Database and Dataset support for HSQLDB databases accessed via JDBC.
|
|
6
|
+
module HSQLDB
|
|
7
|
+
# Instance methods for HSQLDB Database objects accessed via JDBC.
|
|
8
|
+
module DatabaseMethods
|
|
9
|
+
include ::Sequel::JDBC::Transactions
|
|
10
|
+
|
|
11
|
+
# HSQLDB uses the :hsqldb database type.
|
|
12
|
+
def database_type
|
|
13
|
+
:hsqldb
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# HSQLDB uses an IDENTITY sequence as the default value for primary
|
|
17
|
+
# key columns.
|
|
18
|
+
def serial_primary_key_options
|
|
19
|
+
{:primary_key => true, :type => :integer, :identity=>true, :start_with=>1}
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# The version of the database, as an integer (e.g 2.2.5 -> 20205)
|
|
23
|
+
def db_version
|
|
24
|
+
@db_version ||= begin
|
|
25
|
+
v = get{DATABASE_VERSION(){}}
|
|
26
|
+
if v =~ /(\d+)\.(\d+)\.(\d+)/
|
|
27
|
+
$1.to_i * 10000 + $2.to_i * 100 + $3.to_i
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
# HSQLDB specific SQL for renaming columns, and changing column types and/or nullity.
|
|
35
|
+
def alter_table_sql(table, op)
|
|
36
|
+
case op[:op]
|
|
37
|
+
when :rename_column
|
|
38
|
+
"ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(op[:name])} RENAME TO #{quote_identifier(op[:new_name])}"
|
|
39
|
+
when :set_column_type
|
|
40
|
+
"ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(op[:name])} SET DATA TYPE #{type_literal(op)}"
|
|
41
|
+
when :set_column_null
|
|
42
|
+
"ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(op[:name])} SET #{op[:null] ? 'NULL' : 'NOT NULL'}"
|
|
43
|
+
else
|
|
44
|
+
super
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Use IDENTITY() to get the last inserted id.
|
|
49
|
+
def last_insert_id(conn, opts={})
|
|
50
|
+
statement(conn) do |stmt|
|
|
51
|
+
sql = 'CALL IDENTITY()'
|
|
52
|
+
rs = log_yield(sql){stmt.executeQuery(sql)}
|
|
53
|
+
rs.next
|
|
54
|
+
rs.getInt(1)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# If an :identity option is present in the column, add the necessary IDENTITY SQL.
|
|
59
|
+
# It's possible to use an IDENTITY type, but that defaults the sequence to start
|
|
60
|
+
# at 0 instead of 1, and we don't want that.
|
|
61
|
+
def type_literal(column)
|
|
62
|
+
if column[:identity]
|
|
63
|
+
sql = "#{super} GENERATED BY DEFAULT AS IDENTITY"
|
|
64
|
+
if sw = column[:start_with]
|
|
65
|
+
sql << " (START WITH #{sw.to_i}"
|
|
66
|
+
sql << " INCREMENT BY #{column[:increment_by].to_i}" if column[:increment_by]
|
|
67
|
+
sql << ")"
|
|
68
|
+
end
|
|
69
|
+
sql
|
|
70
|
+
else
|
|
71
|
+
super
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Dataset class for HSQLDB datasets accessed via JDBC.
|
|
77
|
+
class Dataset < JDBC::Dataset
|
|
78
|
+
BITWISE_METHOD_MAP = {:& =>:BITAND, :| => :BITOR, :^ => :BITXOR}
|
|
79
|
+
BOOL_TRUE = 'TRUE'.freeze
|
|
80
|
+
BOOL_FALSE = 'FALSE'.freeze
|
|
81
|
+
# HSQLDB does support common table expressions, but the support is broken.
|
|
82
|
+
# CTEs operate more like temprorary tables or views, lasting longer than the duration of the expression.
|
|
83
|
+
# CTEs in earlier queries might take precedence over CTEs with the same name in later queries.
|
|
84
|
+
# Also, if any CTE is recursive, all CTEs must be recursive.
|
|
85
|
+
# If you want to use CTEs with HSQLDB, you'll have to manually modify the dataset to allow it.
|
|
86
|
+
SELECT_CLAUSE_METHODS = clause_methods(:select, %w'distinct columns from join where group having compounds order limit lock')
|
|
87
|
+
SQL_WITH_RECURSIVE = "WITH RECURSIVE ".freeze
|
|
88
|
+
|
|
89
|
+
# Handle HSQLDB specific case insensitive LIKE and bitwise operator support.
|
|
90
|
+
def complex_expression_sql(op, args)
|
|
91
|
+
case op
|
|
92
|
+
when :ILIKE
|
|
93
|
+
super(:LIKE, [SQL::Function.new(:ucase, args.at(0)), SQL::Function.new(:ucase, args.at(1)) ])
|
|
94
|
+
when :"NOT ILIKE"
|
|
95
|
+
super(:"NOT LIKE", [SQL::Function.new(:ucase, args.at(0)), SQL::Function.new(:ucase, args.at(1)) ])
|
|
96
|
+
when :&, :|, :^
|
|
97
|
+
op = BITWISE_METHOD_MAP[op]
|
|
98
|
+
complex_expression_arg_pairs(args){|a, b| literal(SQL::Function.new(op, a, b))}
|
|
99
|
+
when :<<
|
|
100
|
+
complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} * POWER(2, #{literal(b)}))"}
|
|
101
|
+
when :>>
|
|
102
|
+
complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} / POWER(2, #{literal(b)}))"}
|
|
103
|
+
when :'B~'
|
|
104
|
+
"((0 - #{literal(args.at(0))}) - 1)"
|
|
105
|
+
else
|
|
106
|
+
super
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# HSQLDB requires recursive CTEs to have column aliases.
|
|
111
|
+
def recursive_cte_requires_column_aliases?
|
|
112
|
+
true
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# HSQLDB does not support IS TRUE.
|
|
116
|
+
def supports_is_true?
|
|
117
|
+
false
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
private
|
|
121
|
+
|
|
122
|
+
# Use string in hex format for blob data.
|
|
123
|
+
def literal_blob(v)
|
|
124
|
+
blob = "X'"
|
|
125
|
+
v.each_byte{|x| blob << sprintf('%02x', x)}
|
|
126
|
+
blob << "'"
|
|
127
|
+
blob
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# HSQLDB uses FALSE for false values.
|
|
131
|
+
def literal_false
|
|
132
|
+
BOOL_FALSE
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# HSQLDB handles fractional seconds in timestamps, but not in times
|
|
136
|
+
def literal_sqltime(v)
|
|
137
|
+
v.strftime("'%H:%M:%S'")
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# HSQLDB uses TRUE for true values.
|
|
141
|
+
def literal_true
|
|
142
|
+
BOOL_TRUE
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# HSQLDB does not support CTEs well enough for Sequel to enable support for them.
|
|
146
|
+
def select_clause_methods
|
|
147
|
+
SELECT_CLAUSE_METHODS
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Use a default FROM table if the dataset does not contain a FROM table.
|
|
151
|
+
def select_from_sql(sql)
|
|
152
|
+
if @opts[:from]
|
|
153
|
+
super
|
|
154
|
+
else
|
|
155
|
+
sql << " FROM (VALUES (0))"
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Use WITH RECURSIVE instead of WITH if any of the CTEs is recursive
|
|
160
|
+
def select_with_sql_base
|
|
161
|
+
opts[:with].any?{|w| w[:recursive]} ? SQL_WITH_RECURSIVE : super
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|