sequel 3.21.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.
Files changed (200) hide show
  1. data/CHANGELOG +413 -3
  2. data/README.rdoc +20 -6
  3. data/Rakefile +23 -16
  4. data/bin/sequel +1 -5
  5. data/doc/association_basics.rdoc +210 -43
  6. data/doc/dataset_basics.rdoc +4 -4
  7. data/doc/mass_assignment.rdoc +54 -0
  8. data/doc/migration.rdoc +15 -538
  9. data/doc/model_hooks.rdoc +72 -27
  10. data/doc/opening_databases.rdoc +86 -50
  11. data/doc/prepared_statements.rdoc +40 -13
  12. data/doc/reflection.rdoc +8 -2
  13. data/doc/release_notes/3.22.0.txt +39 -0
  14. data/doc/release_notes/3.23.0.txt +172 -0
  15. data/doc/release_notes/3.24.0.txt +420 -0
  16. data/doc/release_notes/3.25.0.txt +88 -0
  17. data/doc/release_notes/3.26.0.txt +88 -0
  18. data/doc/release_notes/3.27.0.txt +82 -0
  19. data/doc/release_notes/3.28.0.txt +304 -0
  20. data/doc/release_notes/3.29.0.txt +459 -0
  21. data/doc/schema_modification.rdoc +547 -0
  22. data/doc/sharding.rdoc +7 -1
  23. data/doc/testing.rdoc +115 -0
  24. data/doc/transactions.rdoc +137 -0
  25. data/doc/virtual_rows.rdoc +2 -2
  26. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  27. data/lib/sequel/adapters/ado.rb +40 -18
  28. data/lib/sequel/adapters/amalgalite.rb +15 -7
  29. data/lib/sequel/adapters/db2.rb +175 -86
  30. data/lib/sequel/adapters/dbi.rb +15 -15
  31. data/lib/sequel/adapters/do/mysql.rb +0 -5
  32. data/lib/sequel/adapters/do/postgres.rb +0 -5
  33. data/lib/sequel/adapters/do/sqlite.rb +0 -5
  34. data/lib/sequel/adapters/do.rb +17 -36
  35. data/lib/sequel/adapters/firebird.rb +27 -208
  36. data/lib/sequel/adapters/ibmdb.rb +448 -0
  37. data/lib/sequel/adapters/informix.rb +6 -23
  38. data/lib/sequel/adapters/jdbc/as400.rb +5 -31
  39. data/lib/sequel/adapters/jdbc/db2.rb +44 -0
  40. data/lib/sequel/adapters/jdbc/derby.rb +217 -0
  41. data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
  42. data/lib/sequel/adapters/jdbc/h2.rb +26 -17
  43. data/lib/sequel/adapters/jdbc/hsqldb.rb +166 -0
  44. data/lib/sequel/adapters/jdbc/informix.rb +26 -0
  45. data/lib/sequel/adapters/jdbc/jtds.rb +29 -0
  46. data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
  47. data/lib/sequel/adapters/jdbc/mysql.rb +9 -10
  48. data/lib/sequel/adapters/jdbc/oracle.rb +67 -25
  49. data/lib/sequel/adapters/jdbc/postgresql.rb +6 -11
  50. data/lib/sequel/adapters/jdbc/sqlite.rb +0 -5
  51. data/lib/sequel/adapters/jdbc/sqlserver.rb +41 -0
  52. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  53. data/lib/sequel/adapters/jdbc.rb +107 -38
  54. data/lib/sequel/adapters/mock.rb +315 -0
  55. data/lib/sequel/adapters/mysql.rb +78 -182
  56. data/lib/sequel/adapters/mysql2.rb +24 -23
  57. data/lib/sequel/adapters/odbc/db2.rb +17 -0
  58. data/lib/sequel/adapters/odbc/mssql.rb +0 -5
  59. data/lib/sequel/adapters/odbc.rb +28 -9
  60. data/lib/sequel/adapters/openbase.rb +2 -4
  61. data/lib/sequel/adapters/oracle.rb +349 -51
  62. data/lib/sequel/adapters/postgres.rb +169 -21
  63. data/lib/sequel/adapters/shared/access.rb +21 -6
  64. data/lib/sequel/adapters/shared/db2.rb +288 -0
  65. data/lib/sequel/adapters/shared/firebird.rb +214 -0
  66. data/lib/sequel/adapters/shared/informix.rb +45 -0
  67. data/lib/sequel/adapters/shared/mssql.rb +108 -65
  68. data/lib/sequel/adapters/shared/mysql.rb +56 -13
  69. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
  70. data/lib/sequel/adapters/shared/oracle.rb +185 -34
  71. data/lib/sequel/adapters/shared/postgres.rb +107 -58
  72. data/lib/sequel/adapters/shared/progress.rb +0 -6
  73. data/lib/sequel/adapters/shared/sqlite.rb +168 -41
  74. data/lib/sequel/adapters/sqlite.rb +27 -18
  75. data/lib/sequel/adapters/swift/mysql.rb +9 -5
  76. data/lib/sequel/adapters/swift/postgres.rb +0 -5
  77. data/lib/sequel/adapters/swift/sqlite.rb +6 -4
  78. data/lib/sequel/adapters/swift.rb +5 -5
  79. data/lib/sequel/adapters/tinytds.rb +152 -17
  80. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +63 -0
  81. data/lib/sequel/ast_transformer.rb +190 -0
  82. data/lib/sequel/connection_pool/threaded.rb +3 -2
  83. data/lib/sequel/connection_pool.rb +1 -1
  84. data/lib/sequel/core.rb +46 -5
  85. data/lib/sequel/database/connecting.rb +5 -6
  86. data/lib/sequel/database/dataset.rb +4 -4
  87. data/lib/sequel/database/dataset_defaults.rb +59 -1
  88. data/lib/sequel/database/logging.rb +1 -1
  89. data/lib/sequel/database/misc.rb +100 -19
  90. data/lib/sequel/database/query.rb +153 -58
  91. data/lib/sequel/database/schema_generator.rb +8 -5
  92. data/lib/sequel/database/schema_methods.rb +59 -29
  93. data/lib/sequel/dataset/actions.rb +194 -57
  94. data/lib/sequel/dataset/features.rb +81 -8
  95. data/lib/sequel/dataset/graph.rb +8 -7
  96. data/lib/sequel/dataset/misc.rb +50 -23
  97. data/lib/sequel/dataset/mutation.rb +5 -6
  98. data/lib/sequel/dataset/prepared_statements.rb +32 -15
  99. data/lib/sequel/dataset/query.rb +223 -42
  100. data/lib/sequel/dataset/sql.rb +58 -61
  101. data/lib/sequel/dataset.rb +8 -0
  102. data/lib/sequel/exceptions.rb +4 -0
  103. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  104. data/lib/sequel/extensions/migration.rb +6 -4
  105. data/lib/sequel/extensions/named_timezones.rb +5 -0
  106. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  107. data/lib/sequel/extensions/to_dot.rb +95 -83
  108. data/lib/sequel/model/associations.rb +997 -331
  109. data/lib/sequel/model/base.rb +386 -129
  110. data/lib/sequel/model/errors.rb +1 -1
  111. data/lib/sequel/model/exceptions.rb +5 -1
  112. data/lib/sequel/model.rb +15 -8
  113. data/lib/sequel/plugins/association_pks.rb +22 -4
  114. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  115. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  116. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  117. data/lib/sequel/plugins/force_encoding.rb +6 -6
  118. data/lib/sequel/plugins/identity_map.rb +114 -7
  119. data/lib/sequel/plugins/many_through_many.rb +65 -7
  120. data/lib/sequel/plugins/prepared_statements.rb +151 -0
  121. data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
  122. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  123. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  124. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  125. data/lib/sequel/plugins/serialization.rb +6 -1
  126. data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
  127. data/lib/sequel/plugins/sharding.rb +12 -25
  128. data/lib/sequel/plugins/single_table_inheritance.rb +3 -1
  129. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  130. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  131. data/lib/sequel/plugins/xml_serializer.rb +3 -3
  132. data/lib/sequel/sql.rb +107 -51
  133. data/lib/sequel/timezones.rb +44 -35
  134. data/lib/sequel/version.rb +1 -1
  135. data/spec/adapters/db2_spec.rb +146 -0
  136. data/spec/adapters/mssql_spec.rb +65 -29
  137. data/spec/adapters/mysql_spec.rb +122 -123
  138. data/spec/adapters/oracle_spec.rb +48 -76
  139. data/spec/adapters/postgres_spec.rb +213 -61
  140. data/spec/adapters/spec_helper.rb +6 -5
  141. data/spec/adapters/sqlite_spec.rb +35 -21
  142. data/spec/core/connection_pool_spec.rb +71 -92
  143. data/spec/core/core_sql_spec.rb +20 -31
  144. data/spec/core/database_spec.rb +729 -508
  145. data/spec/core/dataset_spec.rb +980 -1044
  146. data/spec/core/expression_filters_spec.rb +159 -42
  147. data/spec/core/mock_adapter_spec.rb +378 -0
  148. data/spec/core/object_graph_spec.rb +48 -114
  149. data/spec/core/schema_generator_spec.rb +3 -3
  150. data/spec/core/schema_spec.rb +298 -38
  151. data/spec/core/spec_helper.rb +6 -48
  152. data/spec/extensions/association_pks_spec.rb +38 -0
  153. data/spec/extensions/class_table_inheritance_spec.rb +1 -1
  154. data/spec/extensions/columns_introspection_spec.rb +91 -0
  155. data/spec/extensions/dataset_associations_spec.rb +199 -0
  156. data/spec/extensions/defaults_setter_spec.rb +64 -0
  157. data/spec/extensions/identity_map_spec.rb +162 -0
  158. data/spec/extensions/instance_hooks_spec.rb +71 -0
  159. data/spec/extensions/many_through_many_spec.rb +195 -20
  160. data/spec/extensions/migration_spec.rb +17 -17
  161. data/spec/extensions/named_timezones_spec.rb +22 -2
  162. data/spec/extensions/nested_attributes_spec.rb +4 -0
  163. data/spec/extensions/prepared_statements_associations_spec.rb +126 -0
  164. data/spec/extensions/prepared_statements_safe_spec.rb +79 -0
  165. data/spec/extensions/prepared_statements_spec.rb +72 -0
  166. data/spec/extensions/prepared_statements_with_pk_spec.rb +38 -0
  167. data/spec/extensions/schema_dumper_spec.rb +2 -2
  168. data/spec/extensions/schema_spec.rb +13 -21
  169. data/spec/extensions/serialization_modification_detection_spec.rb +37 -0
  170. data/spec/extensions/serialization_spec.rb +5 -8
  171. data/spec/extensions/single_table_inheritance_spec.rb +11 -0
  172. data/spec/extensions/spec_helper.rb +7 -1
  173. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  174. data/spec/extensions/to_dot_spec.rb +3 -5
  175. data/spec/extensions/typecast_on_load_spec.rb +1 -6
  176. data/spec/extensions/xml_serializer_spec.rb +16 -4
  177. data/spec/integration/associations_test.rb +522 -21
  178. data/spec/integration/database_test.rb +4 -2
  179. data/spec/integration/dataset_test.rb +375 -62
  180. data/spec/integration/eager_loader_test.rb +19 -21
  181. data/spec/integration/model_test.rb +80 -1
  182. data/spec/integration/plugin_test.rb +304 -116
  183. data/spec/integration/prepared_statement_test.rb +200 -120
  184. data/spec/integration/schema_test.rb +161 -30
  185. data/spec/integration/spec_helper.rb +39 -30
  186. data/spec/integration/timezone_test.rb +38 -12
  187. data/spec/integration/transaction_test.rb +172 -5
  188. data/spec/integration/type_test.rb +17 -3
  189. data/spec/model/association_reflection_spec.rb +174 -7
  190. data/spec/model/associations_spec.rb +849 -661
  191. data/spec/model/base_spec.rb +255 -95
  192. data/spec/model/dataset_methods_spec.rb +7 -27
  193. data/spec/model/eager_loading_spec.rb +640 -676
  194. data/spec/model/hooks_spec.rb +309 -67
  195. data/spec/model/model_spec.rb +207 -167
  196. data/spec/model/plugins_spec.rb +24 -13
  197. data/spec/model/record_spec.rb +321 -218
  198. data/spec/model/spec_helper.rb +13 -71
  199. data/spec/model/validations_spec.rb +11 -0
  200. metadata +95 -38
@@ -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
@@ -0,0 +1,29 @@
1
+ Sequel.require 'adapters/shared/firebird'
2
+ Sequel.require 'adapters/jdbc/transactions'
3
+
4
+ module Sequel
5
+ module JDBC
6
+ # Database and Dataset instance methods for Firebird specific
7
+ # support via JDBC.
8
+ module Firebird
9
+ # Database instance methods for Firebird databases accessed via JDBC.
10
+ module DatabaseMethods
11
+ include Sequel::Firebird::DatabaseMethods
12
+ include Sequel::JDBC::Transactions
13
+
14
+ # Add the primary_keys and primary_key_sequences instance variables,
15
+ # so we can get the correct return values for inserted rows.
16
+ def self.extended(db)
17
+ db.instance_eval do
18
+ @primary_keys = {}
19
+ end
20
+ end
21
+ end
22
+
23
+ # Dataset class for Firebird datasets accessed via JDBC.
24
+ class Dataset < JDBC::Dataset
25
+ include Sequel::Firebird::DatasetMethods
26
+ end
27
+ end
28
+ end
29
+ end
@@ -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)
@@ -33,6 +28,11 @@ module Sequel
33
28
  {:primary_key => true, :type => :identity}
34
29
  end
35
30
 
31
+ # H2 supports CREATE TABLE IF NOT EXISTS syntax.
32
+ def supports_create_table_if_not_exists?
33
+ true
34
+ end
35
+
36
36
  # H2 supports prepared transactions
37
37
  def supports_prepared_transactions?
38
38
  true
@@ -48,8 +48,8 @@ module Sequel
48
48
  # If the :prepare option is given and we aren't in a savepoint,
49
49
  # prepare the transaction for a two-phase commit.
50
50
  def commit_transaction(conn, opts={})
51
- if opts[:prepare] && Thread.current[:sequel_transaction_depth] <= 1
52
- log_connection_execute(conn, "PREPARE COMMIT #{opts[:prepare]}")
51
+ if (s = opts[:prepare]) && @transactions[conn][:savepoint_level] <= 1
52
+ log_connection_execute(conn, "PREPARE COMMIT #{s}")
53
53
  else
54
54
  super
55
55
  end
@@ -90,7 +90,8 @@ module Sequel
90
90
  # Use IDENTITY() to get the last inserted id.
91
91
  def last_insert_id(conn, opts={})
92
92
  statement(conn) do |stmt|
93
- rs = stmt.executeQuery('SELECT IDENTITY();')
93
+ sql = 'SELECT IDENTITY();'
94
+ rs = log_yield(sql){stmt.executeQuery(sql)}
94
95
  rs.next
95
96
  rs.getInt(1)
96
97
  end
@@ -106,7 +107,7 @@ module Sequel
106
107
  SELECT_CLAUSE_METHODS = clause_methods(:select, %w'distinct columns from join where group having compounds order limit')
107
108
  BITWISE_METHOD_MAP = {:& =>:BITAND, :| => :BITOR, :^ => :BITXOR}
108
109
 
109
- # Work around H2's lack of a case insensitive LIKE operator
110
+ # Emulate the case insensitive LIKE operator and the bitwise operators.
110
111
  def complex_expression_sql(op, args)
111
112
  case op
112
113
  when :ILIKE
@@ -114,11 +115,13 @@ module Sequel
114
115
  when :"NOT ILIKE"
115
116
  super(:"NOT LIKE", [SQL::PlaceholderLiteralString.new("CAST(? AS VARCHAR_IGNORECASE)", [args.at(0)]), args.at(1)])
116
117
  when :&, :|, :^
117
- literal(SQL::Function.new(BITWISE_METHOD_MAP[op], *args))
118
+ complex_expression_arg_pairs(args){|a, b| literal(SQL::Function.new(BITWISE_METHOD_MAP[op], a, b))}
118
119
  when :<<
119
- "(#{literal(args[0])} * POWER(2, #{literal(args[1])}))"
120
+ complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} * POWER(2, #{literal(b)}))"}
120
121
  when :>>
121
- "(#{literal(args[0])} / POWER(2, #{literal(args[1])}))"
122
+ complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} / POWER(2, #{literal(b)}))"}
123
+ when :'B~'
124
+ "((0 - #{literal(args.at(0))}) - 1)"
122
125
  else
123
126
  super(op, args)
124
127
  end
@@ -146,11 +149,7 @@ module Sequel
146
149
 
147
150
  private
148
151
 
149
- # H2 expects hexadecimal strings for blob values
150
- def literal_blob(v)
151
- literal_string v.unpack("H*").first
152
- end
153
-
152
+ # Handle H2 specific clobs as strings.
154
153
  def convert_type(v)
155
154
  case v
156
155
  when Java::OrgH2Jdbc::JdbcClob
@@ -160,6 +159,16 @@ module Sequel
160
159
  end
161
160
  end
162
161
 
162
+ # H2 expects hexadecimal strings for blob values
163
+ def literal_blob(v)
164
+ literal_string v.unpack("H*").first
165
+ end
166
+
167
+ # H2 handles fractional seconds in timestamps, but not in times
168
+ def literal_sqltime(v)
169
+ v.strftime("'%H:%M:%S'")
170
+ end
171
+
163
172
  def select_clause_methods
164
173
  SELECT_CLAUSE_METHODS
165
174
  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
@@ -0,0 +1,26 @@
1
+ Sequel.require 'adapters/shared/informix'
2
+
3
+ module Sequel
4
+ module JDBC
5
+ # Database and Dataset instance methods for Informix specific
6
+ # support via JDBC.
7
+ module Informix
8
+ # Database instance methods for Informix databases accessed via JDBC.
9
+ module DatabaseMethods
10
+ include Sequel::Informix::DatabaseMethods
11
+
12
+ private
13
+
14
+ # TODO: implement
15
+ def last_insert_id(conn, opts={})
16
+ nil
17
+ end
18
+ end
19
+
20
+ # Dataset class for Informix datasets accessed via JDBC.
21
+ class Dataset < JDBC::Dataset
22
+ include Sequel::Informix::DatasetMethods
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,29 @@
1
+ Sequel.require 'adapters/jdbc/mssql'
2
+
3
+ module Sequel
4
+ module JDBC
5
+ # Database and Dataset instance methods for JTDS specific
6
+ # support via JDBC.
7
+ module JTDS
8
+ # Database instance methods for JTDS databases accessed via JDBC.
9
+ module DatabaseMethods
10
+ include Sequel::JDBC::MSSQL::DatabaseMethods
11
+ end
12
+
13
+ # Dataset class for JTDS datasets accessed via JDBC.
14
+ class Dataset < JDBC::Dataset
15
+ include Sequel::MSSQL::DatasetMethods
16
+
17
+ # Handle CLOB types retrieved via JTDS.
18
+ def convert_type(v)
19
+ case v
20
+ when Java::NetSourceforgeJtdsJdbc::ClobImpl
21
+ convert_type(v.getSubString(1, v.length))
22
+ else
23
+ super
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -16,11 +16,6 @@ module Sequel
16
16
 
17
17
  include Sequel::MSSQL::DatabaseMethods
18
18
 
19
- # Return instance of Sequel::JDBC::MSSQL::Dataset with the given opts.
20
- def dataset(opts=nil)
21
- Sequel::JDBC::MSSQL::Dataset.new(self, opts)
22
- end
23
-
24
19
  private
25
20
 
26
21
  # Get the last inserted id using SCOPE_IDENTITY().
@@ -43,33 +38,6 @@ module Sequel
43
38
  def primary_key_index_re
44
39
  PRIMARY_KEY_INDEX_RE
45
40
  end
46
-
47
- def metadata_dataset
48
- ds = super
49
- # Work around a bug in SQL Server JDBC Driver 3.0, where the metadata
50
- # for the getColumns result set specifies an incorrect type for the
51
- # IS_AUTOINCREMENT column. The column is a string, but the type is
52
- # specified as a short. This causes getObject() to throw a
53
- # com.microsoft.sqlserver.jdbc.SQLServerException: "The conversion
54
- # from char to SMALLINT is unsupported." Using getString() rather
55
- # than getObject() for this column avoids the problem.
56
- # Reference: http://social.msdn.microsoft.com/Forums/en/sqldataaccess/thread/20df12f3-d1bf-4526-9daa-239a83a8e435
57
- def ds.result_set_object_getter
58
- lambda do |result, n, i|
59
- if n == :is_autoincrement
60
- @convert_types ? convert_type(result.getString(i)) : result.getString(i)
61
- else
62
- @convert_types ? convert_type(result.getObject(i)) : result.getObject(i)
63
- end
64
- end
65
- end
66
- ds
67
- end
68
- end
69
-
70
- # Dataset class for MSSQL datasets accessed via JDBC.
71
- class Dataset < JDBC::Dataset
72
- include Sequel::MSSQL::DatasetMethods
73
41
  end
74
42
  end
75
43
  end
@@ -9,11 +9,6 @@ module Sequel
9
9
  module DatabaseMethods
10
10
  include Sequel::MySQL::DatabaseMethods
11
11
 
12
- # Return instance of Sequel::JDBC::MySQL::Dataset with the given opts.
13
- def dataset(opts=nil)
14
- Sequel::JDBC::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
@@ -56,17 +51,21 @@ module Sequel
56
51
  def schema_column_type(db_type)
57
52
  db_type == 'tinyint(1)' ? :boolean : super
58
53
  end
54
+
55
+ # By default, MySQL 'where id is null' selects the last inserted id.
56
+ # Turn that off unless explicitly enabled.
57
+ def setup_connection(conn)
58
+ super
59
+ sql = "SET SQL_AUTO_IS_NULL=0"
60
+ statement(conn){|s| log_yield(sql){s.execute(sql)}} unless opts[:auto_is_null]
61
+ conn
62
+ end
59
63
  end
60
64
 
61
65
  # Dataset class for MySQL datasets accessed via JDBC.
62
66
  class Dataset < JDBC::Dataset
63
67
  include Sequel::MySQL::DatasetMethods
64
68
 
65
- # Use execute_insert to execute the insert_sql.
66
- def insert(*values)
67
- execute_insert(insert_sql(*values))
68
- end
69
-
70
69
  # Use execute_insert to execute the replace_sql.
71
70
  def replace(*args)
72
71
  execute_insert(replace_sql(*args))