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.
Files changed (234) hide show
  1. data/CHANGELOG +539 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +20 -6
  4. data/Rakefile +37 -19
  5. data/bin/sequel +1 -5
  6. data/doc/advanced_associations.rdoc +17 -0
  7. data/doc/association_basics.rdoc +293 -43
  8. data/doc/dataset_basics.rdoc +4 -4
  9. data/doc/dataset_filtering.rdoc +8 -0
  10. data/doc/mass_assignment.rdoc +54 -0
  11. data/doc/migration.rdoc +15 -538
  12. data/doc/model_hooks.rdoc +72 -27
  13. data/doc/opening_databases.rdoc +89 -50
  14. data/doc/prepared_statements.rdoc +40 -13
  15. data/doc/reflection.rdoc +8 -2
  16. data/doc/release_notes/3.22.0.txt +39 -0
  17. data/doc/release_notes/3.23.0.txt +172 -0
  18. data/doc/release_notes/3.24.0.txt +420 -0
  19. data/doc/release_notes/3.25.0.txt +88 -0
  20. data/doc/release_notes/3.26.0.txt +88 -0
  21. data/doc/release_notes/3.27.0.txt +82 -0
  22. data/doc/release_notes/3.28.0.txt +304 -0
  23. data/doc/release_notes/3.29.0.txt +459 -0
  24. data/doc/release_notes/3.30.0.txt +135 -0
  25. data/doc/release_notes/3.31.0.txt +146 -0
  26. data/doc/release_notes/3.32.0.txt +202 -0
  27. data/doc/schema_modification.rdoc +547 -0
  28. data/doc/sharding.rdoc +7 -1
  29. data/doc/testing.rdoc +129 -0
  30. data/doc/transactions.rdoc +137 -0
  31. data/doc/virtual_rows.rdoc +2 -2
  32. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  33. data/lib/sequel/adapters/ado.rb +40 -18
  34. data/lib/sequel/adapters/amalgalite.rb +17 -9
  35. data/lib/sequel/adapters/db2.rb +175 -86
  36. data/lib/sequel/adapters/dbi.rb +15 -15
  37. data/lib/sequel/adapters/do/mysql.rb +5 -7
  38. data/lib/sequel/adapters/do/postgres.rb +0 -5
  39. data/lib/sequel/adapters/do/sqlite.rb +0 -5
  40. data/lib/sequel/adapters/do.rb +17 -36
  41. data/lib/sequel/adapters/firebird.rb +27 -208
  42. data/lib/sequel/adapters/ibmdb.rb +448 -0
  43. data/lib/sequel/adapters/informix.rb +6 -23
  44. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  45. data/lib/sequel/adapters/jdbc/db2.rb +52 -0
  46. data/lib/sequel/adapters/jdbc/derby.rb +260 -0
  47. data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
  48. data/lib/sequel/adapters/jdbc/h2.rb +66 -30
  49. data/lib/sequel/adapters/jdbc/hsqldb.rb +177 -0
  50. data/lib/sequel/adapters/jdbc/informix.rb +26 -0
  51. data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
  52. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  53. data/lib/sequel/adapters/jdbc/mysql.rb +11 -11
  54. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  55. data/lib/sequel/adapters/jdbc/postgresql.rb +9 -13
  56. data/lib/sequel/adapters/jdbc/sqlite.rb +2 -6
  57. data/lib/sequel/adapters/jdbc/sqlserver.rb +71 -0
  58. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  59. data/lib/sequel/adapters/jdbc.rb +231 -78
  60. data/lib/sequel/adapters/mock.rb +339 -0
  61. data/lib/sequel/adapters/mysql.rb +85 -184
  62. data/lib/sequel/adapters/mysql2.rb +26 -25
  63. data/lib/sequel/adapters/odbc/db2.rb +17 -0
  64. data/lib/sequel/adapters/odbc/mssql.rb +1 -6
  65. data/lib/sequel/adapters/odbc.rb +28 -9
  66. data/lib/sequel/adapters/openbase.rb +3 -5
  67. data/lib/sequel/adapters/oracle.rb +355 -53
  68. data/lib/sequel/adapters/postgres.rb +194 -33
  69. data/lib/sequel/adapters/shared/access.rb +32 -9
  70. data/lib/sequel/adapters/shared/db2.rb +328 -0
  71. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  72. data/lib/sequel/adapters/shared/informix.rb +53 -0
  73. data/lib/sequel/adapters/shared/mssql.rb +278 -107
  74. data/lib/sequel/adapters/shared/mysql.rb +203 -65
  75. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
  76. data/lib/sequel/adapters/shared/oracle.rb +237 -46
  77. data/lib/sequel/adapters/shared/postgres.rb +214 -82
  78. data/lib/sequel/adapters/shared/progress.rb +5 -8
  79. data/lib/sequel/adapters/shared/sqlite.rb +200 -50
  80. data/lib/sequel/adapters/sqlite.rb +29 -21
  81. data/lib/sequel/adapters/swift/mysql.rb +12 -7
  82. data/lib/sequel/adapters/swift/postgres.rb +0 -5
  83. data/lib/sequel/adapters/swift/sqlite.rb +8 -6
  84. data/lib/sequel/adapters/swift.rb +5 -5
  85. data/lib/sequel/adapters/tinytds.rb +160 -19
  86. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
  87. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  88. data/lib/sequel/ast_transformer.rb +190 -0
  89. data/lib/sequel/connection_pool/threaded.rb +3 -2
  90. data/lib/sequel/connection_pool.rb +1 -1
  91. data/lib/sequel/core.rb +68 -5
  92. data/lib/sequel/database/connecting.rb +5 -6
  93. data/lib/sequel/database/dataset.rb +4 -4
  94. data/lib/sequel/database/dataset_defaults.rb +59 -1
  95. data/lib/sequel/database/logging.rb +1 -1
  96. data/lib/sequel/database/misc.rb +104 -19
  97. data/lib/sequel/database/query.rb +163 -59
  98. data/lib/sequel/database/schema_generator.rb +17 -7
  99. data/lib/sequel/database/schema_methods.rb +59 -29
  100. data/lib/sequel/dataset/actions.rb +240 -76
  101. data/lib/sequel/dataset/features.rb +91 -8
  102. data/lib/sequel/dataset/graph.rb +38 -11
  103. data/lib/sequel/dataset/misc.rb +50 -23
  104. data/lib/sequel/dataset/mutation.rb +12 -7
  105. data/lib/sequel/dataset/prepared_statements.rb +59 -27
  106. data/lib/sequel/dataset/query.rb +236 -43
  107. data/lib/sequel/dataset/sql.rb +676 -267
  108. data/lib/sequel/dataset.rb +8 -0
  109. data/lib/sequel/exceptions.rb +4 -0
  110. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  111. data/lib/sequel/extensions/migration.rb +6 -4
  112. data/lib/sequel/extensions/named_timezones.rb +5 -0
  113. data/lib/sequel/extensions/query.rb +1 -1
  114. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  115. data/lib/sequel/extensions/to_dot.rb +95 -83
  116. data/lib/sequel/model/associations.rb +1081 -344
  117. data/lib/sequel/model/base.rb +420 -133
  118. data/lib/sequel/model/errors.rb +1 -1
  119. data/lib/sequel/model/exceptions.rb +5 -1
  120. data/lib/sequel/model.rb +20 -10
  121. data/lib/sequel/plugins/active_model.rb +13 -1
  122. data/lib/sequel/plugins/association_pks.rb +22 -4
  123. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  124. data/lib/sequel/plugins/composition.rb +43 -10
  125. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  126. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  127. data/lib/sequel/plugins/force_encoding.rb +6 -6
  128. data/lib/sequel/plugins/identity_map.rb +114 -7
  129. data/lib/sequel/plugins/many_through_many.rb +68 -7
  130. data/lib/sequel/plugins/nested_attributes.rb +65 -10
  131. data/lib/sequel/plugins/prepared_statements.rb +151 -0
  132. data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
  133. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  134. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  135. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  136. data/lib/sequel/plugins/serialization.rb +98 -49
  137. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  138. data/lib/sequel/plugins/sharding.rb +21 -54
  139. data/lib/sequel/plugins/single_table_inheritance.rb +3 -1
  140. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  141. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  142. data/lib/sequel/plugins/xml_serializer.rb +3 -3
  143. data/lib/sequel/sql.rb +109 -52
  144. data/lib/sequel/timezones.rb +57 -38
  145. data/lib/sequel/version.rb +1 -1
  146. data/spec/adapters/db2_spec.rb +146 -0
  147. data/spec/adapters/mssql_spec.rb +135 -30
  148. data/spec/adapters/mysql_spec.rb +244 -212
  149. data/spec/adapters/oracle_spec.rb +67 -77
  150. data/spec/adapters/postgres_spec.rb +267 -78
  151. data/spec/adapters/spec_helper.rb +6 -5
  152. data/spec/adapters/sqlite_spec.rb +35 -21
  153. data/spec/core/connection_pool_spec.rb +71 -92
  154. data/spec/core/core_sql_spec.rb +21 -30
  155. data/spec/core/database_spec.rb +789 -512
  156. data/spec/core/dataset_spec.rb +1176 -1061
  157. data/spec/core/expression_filters_spec.rb +159 -42
  158. data/spec/core/mock_adapter_spec.rb +419 -0
  159. data/spec/core/object_graph_spec.rb +86 -114
  160. data/spec/core/schema_generator_spec.rb +3 -3
  161. data/spec/core/schema_spec.rb +298 -38
  162. data/spec/core/spec_helper.rb +6 -48
  163. data/spec/extensions/active_model_spec.rb +13 -0
  164. data/spec/extensions/association_autoreloading_spec.rb +1 -10
  165. data/spec/extensions/association_dependencies_spec.rb +2 -12
  166. data/spec/extensions/association_pks_spec.rb +66 -32
  167. data/spec/extensions/caching_spec.rb +23 -50
  168. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  169. data/spec/extensions/columns_introspection_spec.rb +91 -0
  170. data/spec/extensions/composition_spec.rb +18 -13
  171. data/spec/extensions/dataset_associations_spec.rb +199 -0
  172. data/spec/extensions/defaults_setter_spec.rb +64 -0
  173. data/spec/extensions/hook_class_methods_spec.rb +65 -91
  174. data/spec/extensions/identity_map_spec.rb +114 -22
  175. data/spec/extensions/instance_filters_spec.rb +11 -21
  176. data/spec/extensions/instance_hooks_spec.rb +53 -0
  177. data/spec/extensions/json_serializer_spec.rb +4 -5
  178. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  179. data/spec/extensions/list_spec.rb +17 -39
  180. data/spec/extensions/many_through_many_spec.rb +283 -250
  181. data/spec/extensions/migration_spec.rb +35 -32
  182. data/spec/extensions/named_timezones_spec.rb +23 -3
  183. data/spec/extensions/nested_attributes_spec.rb +97 -88
  184. data/spec/extensions/optimistic_locking_spec.rb +9 -20
  185. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  186. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  187. data/spec/extensions/prepared_statements_spec.rb +53 -0
  188. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  189. data/spec/extensions/pretty_table_spec.rb +1 -6
  190. data/spec/extensions/query_spec.rb +6 -0
  191. data/spec/extensions/rcte_tree_spec.rb +41 -43
  192. data/spec/extensions/schema_dumper_spec.rb +5 -8
  193. data/spec/extensions/schema_spec.rb +13 -21
  194. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  195. data/spec/extensions/serialization_spec.rb +81 -43
  196. data/spec/extensions/sharding_spec.rb +66 -140
  197. data/spec/extensions/single_table_inheritance_spec.rb +18 -29
  198. data/spec/extensions/spec_helper.rb +13 -61
  199. data/spec/extensions/sql_expr_spec.rb +20 -60
  200. data/spec/extensions/tactical_eager_loading_spec.rb +9 -19
  201. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  202. data/spec/extensions/timestamps_spec.rb +6 -6
  203. data/spec/extensions/to_dot_spec.rb +4 -7
  204. data/spec/extensions/touch_spec.rb +13 -14
  205. data/spec/extensions/tree_spec.rb +11 -26
  206. data/spec/extensions/typecast_on_load_spec.rb +1 -6
  207. data/spec/extensions/update_primary_key_spec.rb +30 -24
  208. data/spec/extensions/validation_class_methods_spec.rb +30 -51
  209. data/spec/extensions/validation_helpers_spec.rb +16 -35
  210. data/spec/extensions/xml_serializer_spec.rb +16 -4
  211. data/spec/integration/associations_test.rb +661 -21
  212. data/spec/integration/database_test.rb +4 -2
  213. data/spec/integration/dataset_test.rb +500 -67
  214. data/spec/integration/eager_loader_test.rb +19 -21
  215. data/spec/integration/model_test.rb +80 -1
  216. data/spec/integration/plugin_test.rb +367 -119
  217. data/spec/integration/prepared_statement_test.rb +222 -125
  218. data/spec/integration/schema_test.rb +217 -39
  219. data/spec/integration/spec_helper.rb +39 -30
  220. data/spec/integration/timezone_test.rb +38 -12
  221. data/spec/integration/transaction_test.rb +182 -5
  222. data/spec/integration/type_test.rb +17 -3
  223. data/spec/model/association_reflection_spec.rb +201 -7
  224. data/spec/model/associations_spec.rb +955 -662
  225. data/spec/model/base_spec.rb +275 -95
  226. data/spec/model/dataset_methods_spec.rb +7 -27
  227. data/spec/model/eager_loading_spec.rb +677 -676
  228. data/spec/model/hooks_spec.rb +349 -67
  229. data/spec/model/model_spec.rb +222 -168
  230. data/spec/model/plugins_spec.rb +24 -13
  231. data/spec/model/record_spec.rb +381 -218
  232. data/spec/model/spec_helper.rb +13 -71
  233. data/spec/model/validations_spec.rb +11 -0
  234. metadata +117 -73
@@ -1,9 +1,13 @@
1
+ Sequel.require 'adapters/utils/emulate_offset_with_row_number'
2
+
1
3
  module Sequel
2
4
  module Oracle
3
5
  module DatabaseMethods
4
6
  TEMPORARY = 'GLOBAL TEMPORARY '.freeze
5
7
  AUTOINCREMENT = ''.freeze
6
8
 
9
+ attr_accessor :autosequence
10
+
7
11
  def create_sequence(name, opts={})
8
12
  self << create_sequence_sql(name, opts)
9
13
  end
@@ -12,6 +16,10 @@ module Sequel
12
16
  self << create_trigger_sql(*args)
13
17
  end
14
18
 
19
+ def current_user
20
+ @current_user ||= metadata_dataset.get{sys_context('USERENV', 'CURRENT_USER')}
21
+ end
22
+
15
23
  def drop_sequence(name)
16
24
  self << drop_sequence_sql(name)
17
25
  end
@@ -22,32 +30,53 @@ module Sequel
22
30
  end
23
31
 
24
32
  def tables(opts={})
25
- ds = from(:tab).server(opts[:server]).select(:tname).filter(:tabtype => 'TABLE')
26
- ds.map{|r| ds.send(:output_identifier, r[:tname])}
27
- end
28
-
29
- def table_exists?(name)
30
- from(:tab).filter(:tname =>dataset.send(:input_identifier, name), :tabtype => 'TABLE').count > 0
33
+ m = output_identifier_meth
34
+ metadata_dataset.from(:tab).server(opts[:server]).select(:tname).filter(:tabtype => 'TABLE').map{|r| m.call(r[:tname])}
31
35
  end
32
36
 
33
37
  def views(opts={})
34
- ds = from(:tab).server(opts[:server]).select(:tname).filter(:tabtype => 'VIEW')
35
- ds.map{|r| ds.send(:output_identifier, r[:tname])}
38
+ m = output_identifier_meth
39
+ metadata_dataset.from(:tab).server(opts[:server]).select(:tname).filter(:tabtype => 'VIEW').map{|r| m.call(r[:tname])}
36
40
  end
37
41
 
38
42
  def view_exists?(name)
39
- from(:tab).filter(:tname =>dataset.send(:input_identifier, name), :tabtype => 'VIEW').count > 0
43
+ m = input_identifier_meth
44
+ metadata_dataset.from(:tab).filter(:tname =>m.call(name), :tabtype => 'VIEW').count > 0
40
45
  end
41
46
 
42
47
  private
43
48
 
44
- def auto_increment_sql
45
- AUTOINCREMENT
49
+ # Handle Oracle specific ALTER TABLE SQL
50
+ def alter_table_sql(table, op)
51
+ case op[:op]
52
+ when :add_column
53
+ if op[:primary_key]
54
+ sqls = []
55
+ sqls << alter_table_sql(table, op.merge(:primary_key=>nil))
56
+ if op[:auto_increment]
57
+ seq_name = default_sequence_name(table, op[:name])
58
+ sqls << drop_sequence_sql(seq_name)
59
+ sqls << create_sequence_sql(seq_name, op)
60
+ sqls << "UPDATE #{quote_schema_table(table)} SET #{quote_identifier(op[:name])} = #{seq_name}.nextval"
61
+ end
62
+ sqls << "ALTER TABLE #{quote_schema_table(table)} ADD PRIMARY KEY (#{quote_identifier(op[:name])})"
63
+ sqls
64
+ else
65
+ "ALTER TABLE #{quote_schema_table(table)} ADD #{column_definition_sql(op)}"
66
+ end
67
+ when :set_column_null
68
+ "ALTER TABLE #{quote_schema_table(table)} MODIFY #{quote_identifier(op[:name])} #{op[:null] ? 'NULL' : 'NOT NULL'}"
69
+ when :set_column_type
70
+ "ALTER TABLE #{quote_schema_table(table)} MODIFY #{quote_identifier(op[:name])} #{type_literal(op)}"
71
+ when :set_column_default
72
+ "ALTER TABLE #{quote_schema_table(table)} MODIFY #{quote_identifier(op[:name])} DEFAULT #{literal(op[:default])}"
73
+ else
74
+ super(table, op)
75
+ end
46
76
  end
47
77
 
48
- # SQL fragment for showing a table is temporary
49
- def temporary_table_sql
50
- TEMPORARY
78
+ def auto_increment_sql
79
+ AUTOINCREMENT
51
80
  end
52
81
 
53
82
  def create_sequence_sql(name, opts={})
@@ -65,7 +94,7 @@ module Sequel
65
94
  drop_seq_statement = nil
66
95
  generator.columns.each do |c|
67
96
  if c[:auto_increment]
68
- c[:sequence_name] ||= "seq_#{name}_#{c[:name]}"
97
+ c[:sequence_name] ||= default_sequence_name(name, c[:name])
69
98
  unless c[:create_sequence] == false
70
99
  drop_seq_statement = drop_sequence_sql(c[:sequence_name])
71
100
  statements << create_sequence_sql(c[:sequence_name], c)
@@ -97,13 +126,120 @@ module Sequel
97
126
  sql
98
127
  end
99
128
 
129
+ def default_sequence_name(table, column)
130
+ "seq_#{table}_#{column}"
131
+ end
132
+
100
133
  def drop_sequence_sql(name)
101
134
  "DROP SEQUENCE #{quote_identifier(name)}"
102
135
  end
136
+
137
+ def remove_cached_schema(table)
138
+ @primary_key_sequences.delete(table)
139
+ super
140
+ end
141
+
142
+ def sequence_for_table(table)
143
+ return nil unless autosequence
144
+ @primary_key_sequences.fetch(table) do |key|
145
+ pk = schema(table).select{|k, v| v[:primary_key]}
146
+ @primary_key_sequences[table] = if pk.length == 1
147
+ seq = "seq_#{table}_#{pk.first.first}"
148
+ seq.to_sym unless from(:user_sequences).filter(:sequence_name=>input_identifier_meth.call(seq)).empty?
149
+ end
150
+ end
151
+ end
152
+
153
+ # Oracle's integer/:number type handles larger values than
154
+ # most other databases's bigint types, so it should be
155
+ # safe to use for Bignum.
156
+ def type_literal_generic_bignum(column)
157
+ :integer
158
+ end
159
+
160
+ # Oracle doesn't have a time type, so use timestamp for all
161
+ # time columns.
162
+ def type_literal_generic_time(column)
163
+ :timestamp
164
+ end
165
+
166
+ # Oracle doesn't have a boolean type or even a reasonable
167
+ # facsimile. Using a char(1) seems to be the recommended way.
168
+ def type_literal_generic_trueclass(column)
169
+ :'char(1)'
170
+ end
171
+
172
+ # SQL fragment for showing a table is temporary
173
+ def temporary_table_sql
174
+ TEMPORARY
175
+ end
103
176
  end
104
177
 
105
178
  module DatasetMethods
106
- SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'with distinct columns from join where group having compounds order limit lock')
179
+ include EmulateOffsetWithRowNumber
180
+
181
+ SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'with select distinct columns from join where group having compounds order lock')
182
+ ROW_NUMBER_EXPRESSION = 'ROWNUM'.lit.freeze
183
+ SPACE = Dataset::SPACE
184
+ APOS = Dataset::APOS
185
+ APOS_RE = Dataset::APOS_RE
186
+ DOUBLE_APOS = Dataset::DOUBLE_APOS
187
+ FROM = Dataset::FROM
188
+ BITCOMP_OPEN = "((0 - ".freeze
189
+ BITCOMP_CLOSE = ") - 1)".freeze
190
+ ILIKE_0 = "(UPPER(".freeze
191
+ ILIKE_1 = ") ".freeze
192
+ ILIKE_2 = ' UPPER('.freeze
193
+ ILIKE_3 = "))".freeze
194
+ LIKE = 'LIKE'.freeze
195
+ NOT_LIKE = 'NOT LIKE'.freeze
196
+ TIMESTAMP_FORMAT = "TIMESTAMP '%Y-%m-%d %H:%M:%S%N %z'".freeze
197
+ TIMESTAMP_OFFSET_FORMAT = "%+03i:%02i".freeze
198
+ BOOL_FALSE = "'N'".freeze
199
+ BOOL_TRUE = "'Y'".freeze
200
+ HSTAR = "H*".freeze
201
+ DUAL = ['DUAL'.freeze].freeze
202
+
203
+ # Oracle needs to emulate bitwise operators and ILIKE/NOT ILIKE operators.
204
+ def complex_expression_sql_append(sql, op, args)
205
+ case op
206
+ when :&
207
+ sql << complex_expression_arg_pairs(args){|a, b| "CAST(BITAND(#{literal(a)}, #{literal(b)}) AS INTEGER)"}
208
+ when :|
209
+ sql << complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} - #{complex_expression_sql(:&, [a, b])} + #{literal(b)})"}
210
+ when :^
211
+ sql << complex_expression_arg_pairs(args){|*x| "(#{complex_expression_sql(:|, x)} - #{complex_expression_sql(:&, x)})"}
212
+ when :'B~'
213
+ sql << BITCOMP_OPEN
214
+ literal_append(sql, args.at(0))
215
+ sql << BITCOMP_CLOSE
216
+ when :<<
217
+ sql << complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} * power(2, #{literal b}))"}
218
+ when :>>
219
+ sql << complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} / power(2, #{literal b}))"}
220
+ when :ILIKE, :'NOT ILIKE'
221
+ sql << ILIKE_0
222
+ literal_append(sql, args.at(0))
223
+ sql << ILIKE_1
224
+ sql << (op == :ILIKE ? LIKE : NOT_LIKE)
225
+ sql<< ILIKE_2
226
+ literal_append(sql, args.at(1))
227
+ sql << ILIKE_3
228
+ else
229
+ super
230
+ end
231
+ end
232
+
233
+ # Oracle doesn't support CURRENT_TIME, as it doesn't have
234
+ # a type for storing just time values without a date, so
235
+ # use CURRENT_TIMESTAMP in its place.
236
+ def constant_sql_append(sql, c)
237
+ if c == :CURRENT_TIME
238
+ super(sql, :CURRENT_TIMESTAMP)
239
+ else
240
+ super
241
+ end
242
+ end
107
243
 
108
244
  # Oracle uses MINUS instead of EXCEPT, and doesn't support EXCEPT ALL
109
245
  def except(dataset, opts={})
@@ -112,19 +248,10 @@ module Sequel
112
248
  compound_clone(:minus, dataset, opts)
113
249
  end
114
250
 
251
+ # Use a custom expression with EXISTS to determine whether a dataset
252
+ # is empty.
115
253
  def empty?
116
- db[:dual].where(exists).get(1) == nil
117
- end
118
-
119
- # If this dataset is associated with a sequence, return the most recently
120
- # inserted sequence value.
121
- def insert(*args)
122
- r = super
123
- if s = opts[:sequence]
124
- with_sql("SELECT #{literal(s)}.currval FROM dual").single_value.to_i
125
- else
126
- r
127
- end
254
+ db[:dual].where(@opts[:offset] ? exists : unordered.exists).get(1) == nil
128
255
  end
129
256
 
130
257
  # Oracle requires SQL standard datetimes
@@ -139,16 +266,61 @@ module Sequel
139
266
  clone(:sequence=>s)
140
267
  end
141
268
 
269
+ # Handle LIMIT by using a unlimited subselect filtered with ROWNUM.
270
+ def select_sql
271
+ if (limit = @opts[:limit]) && !@opts[:sql]
272
+ ds = clone(:limit=>nil)
273
+ # Lock doesn't work in subselects, so don't use a subselect when locking.
274
+ # Don't use a subselect if custom SQL is used, as it breaks somethings.
275
+ ds = ds.from_self unless @opts[:lock]
276
+ sql = @opts[:append_sql] || ''
277
+ subselect_sql_append(sql, ds.where(SQL::ComplexExpression.new(:<=, ROW_NUMBER_EXPRESSION, limit)))
278
+ sql
279
+ else
280
+ super
281
+ end
282
+ end
283
+
284
+ # Oracle requires recursive CTEs to have column aliases.
285
+ def recursive_cte_requires_column_aliases?
286
+ true
287
+ end
288
+
289
+ # Oracle supports GROUP BY CUBE
290
+ def supports_group_cube?
291
+ true
292
+ end
293
+
294
+ # Oracle supports GROUP BY ROLLUP
295
+ def supports_group_rollup?
296
+ true
297
+ end
298
+
142
299
  # Oracle does not support INTERSECT ALL or EXCEPT ALL
143
300
  def supports_intersect_except_all?
144
301
  false
145
302
  end
303
+
304
+ # Oracle does not support IS TRUE.
305
+ def supports_is_true?
306
+ false
307
+ end
308
+
309
+ # Oracle does not support SELECT *, column
310
+ def supports_select_all_and_column?
311
+ false
312
+ end
146
313
 
147
314
  # Oracle supports timezones in literal timestamps.
148
315
  def supports_timestamp_timezones?
149
316
  true
150
317
  end
151
318
 
319
+ # Oracle does not support WHERE 'Y' for WHERE TRUE.
320
+ def supports_where_true?
321
+ false
322
+ end
323
+
152
324
  # Oracle supports window functions
153
325
  def supports_window_functions?
154
326
  true
@@ -158,25 +330,54 @@ module Sequel
158
330
 
159
331
  # Oracle doesn't support the use of AS when aliasing a dataset. It doesn't require
160
332
  # the use of AS anywhere, so this disables it in all cases.
161
- def as_sql(expression, aliaz)
162
- "#{expression} #{quote_identifier(aliaz)}"
333
+ def as_sql_append(sql, aliaz)
334
+ sql << SPACE
335
+ quote_identifier_append(sql, aliaz)
163
336
  end
164
337
 
165
338
  # The strftime format to use when literalizing the time.
166
339
  def default_timestamp_format
167
- "TIMESTAMP '%Y-%m-%d %H:%M:%S%N %z'".freeze
340
+ TIMESTAMP_FORMAT
341
+ end
342
+
343
+ # If this dataset is associated with a sequence, return the most recently
344
+ # inserted sequence value.
345
+ def execute_insert(sql, opts={})
346
+ f = @opts[:from]
347
+ super(sql, {:table=>(f.first if f), :sequence=>@opts[:sequence]}.merge(opts))
168
348
  end
169
349
 
170
350
  # Use a colon for the timestamp offset, since Oracle appears to require it.
171
351
  def format_timestamp_offset(hour, minute)
172
- sprintf("%+03i:%02i", hour, minute)
352
+ sprintf(TIMESTAMP_OFFSET_FORMAT, hour, minute)
353
+ end
354
+
355
+ # Oracle doesn't support empty values when inserting.
356
+ def insert_supports_empty_values?
357
+ false
358
+ end
359
+
360
+ # Use string in hex format for blob data.
361
+ def literal_blob_append(sql, v)
362
+ sql << APOS << v.unpack(HSTAR).first << APOS
363
+ end
364
+
365
+ # Oracle uses 'N' for false values.
366
+ def literal_false
367
+ BOOL_FALSE
173
368
  end
174
369
 
175
370
  # Oracle uses the SQL standard of only doubling ' inside strings.
176
- def literal_string(v)
177
- "'#{v.gsub("'", "''")}'"
371
+ def literal_string_append(sql, v)
372
+ sql << APOS << v.gsub(APOS_RE, DOUBLE_APOS) << APOS
373
+ end
374
+
375
+ # Oracle uses 'Y' for true values.
376
+ def literal_true
377
+ BOOL_TRUE
178
378
  end
179
379
 
380
+ # Use the Oracle-specific SQL clauses (no limit, since it is emulated).
180
381
  def select_clause_methods
181
382
  SELECT_CLAUSE_METHODS
182
383
  end
@@ -186,18 +387,8 @@ module Sequel
186
387
  # so add the dummy DUAL table if the dataset doesn't select
187
388
  # from a table.
188
389
  def select_from_sql(sql)
189
- sql << " FROM #{source_list(@opts[:from] || ['DUAL'])}"
190
- end
191
-
192
- # Oracle requires a subselect to do limit and offset
193
- def select_limit_sql(sql)
194
- if limit = @opts[:limit]
195
- if (offset = @opts[:offset]) && (offset > 0)
196
- sql.replace("SELECT * FROM (SELECT raw_sql_.*, ROWNUM raw_rnum_ FROM(#{sql}) raw_sql_ WHERE ROWNUM <= #{limit + offset}) WHERE raw_rnum_ > #{offset}")
197
- else
198
- sql.replace("SELECT * FROM (#{sql}) WHERE ROWNUM <= #{limit}")
199
- end
200
- end
390
+ sql << FROM
391
+ source_list_append(sql, @opts[:from] || DUAL)
201
392
  end
202
393
  end
203
394
  end