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.
Files changed (148) hide show
  1. data/CHANGELOG +119 -3
  2. data/Rakefile +5 -3
  3. data/bin/sequel +1 -5
  4. data/doc/model_hooks.rdoc +9 -1
  5. data/doc/opening_databases.rdoc +49 -40
  6. data/doc/prepared_statements.rdoc +27 -6
  7. data/doc/release_notes/3.28.0.txt +2 -2
  8. data/doc/release_notes/3.29.0.txt +459 -0
  9. data/doc/sharding.rdoc +7 -1
  10. data/doc/testing.rdoc +18 -9
  11. data/doc/transactions.rdoc +41 -1
  12. data/lib/sequel/adapters/ado.rb +28 -17
  13. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  14. data/lib/sequel/adapters/amalgalite.rb +11 -7
  15. data/lib/sequel/adapters/db2.rb +122 -70
  16. data/lib/sequel/adapters/dbi.rb +15 -15
  17. data/lib/sequel/adapters/do.rb +5 -36
  18. data/lib/sequel/adapters/do/mysql.rb +0 -5
  19. data/lib/sequel/adapters/do/postgres.rb +0 -5
  20. data/lib/sequel/adapters/do/sqlite.rb +0 -5
  21. data/lib/sequel/adapters/firebird.rb +3 -6
  22. data/lib/sequel/adapters/ibmdb.rb +24 -16
  23. data/lib/sequel/adapters/informix.rb +2 -4
  24. data/lib/sequel/adapters/jdbc.rb +47 -11
  25. data/lib/sequel/adapters/jdbc/as400.rb +5 -24
  26. data/lib/sequel/adapters/jdbc/db2.rb +0 -5
  27. data/lib/sequel/adapters/jdbc/derby.rb +217 -0
  28. data/lib/sequel/adapters/jdbc/firebird.rb +0 -5
  29. data/lib/sequel/adapters/jdbc/h2.rb +10 -12
  30. data/lib/sequel/adapters/jdbc/hsqldb.rb +166 -0
  31. data/lib/sequel/adapters/jdbc/informix.rb +0 -5
  32. data/lib/sequel/adapters/jdbc/jtds.rb +0 -5
  33. data/lib/sequel/adapters/jdbc/mysql.rb +0 -10
  34. data/lib/sequel/adapters/jdbc/oracle.rb +70 -3
  35. data/lib/sequel/adapters/jdbc/postgresql.rb +0 -11
  36. data/lib/sequel/adapters/jdbc/sqlite.rb +0 -5
  37. data/lib/sequel/adapters/jdbc/sqlserver.rb +0 -5
  38. data/lib/sequel/adapters/jdbc/transactions.rb +56 -7
  39. data/lib/sequel/adapters/mock.rb +315 -0
  40. data/lib/sequel/adapters/mysql.rb +64 -51
  41. data/lib/sequel/adapters/mysql2.rb +15 -9
  42. data/lib/sequel/adapters/odbc.rb +13 -6
  43. data/lib/sequel/adapters/odbc/db2.rb +0 -4
  44. data/lib/sequel/adapters/odbc/mssql.rb +0 -5
  45. data/lib/sequel/adapters/openbase.rb +2 -4
  46. data/lib/sequel/adapters/oracle.rb +333 -51
  47. data/lib/sequel/adapters/postgres.rb +80 -27
  48. data/lib/sequel/adapters/shared/access.rb +0 -6
  49. data/lib/sequel/adapters/shared/db2.rb +13 -15
  50. data/lib/sequel/adapters/shared/firebird.rb +6 -6
  51. data/lib/sequel/adapters/shared/mssql.rb +23 -18
  52. data/lib/sequel/adapters/shared/mysql.rb +6 -6
  53. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +6 -0
  54. data/lib/sequel/adapters/shared/oracle.rb +185 -30
  55. data/lib/sequel/adapters/shared/postgres.rb +35 -18
  56. data/lib/sequel/adapters/shared/progress.rb +0 -6
  57. data/lib/sequel/adapters/shared/sqlite.rb +116 -37
  58. data/lib/sequel/adapters/sqlite.rb +16 -8
  59. data/lib/sequel/adapters/swift.rb +5 -5
  60. data/lib/sequel/adapters/swift/mysql.rb +0 -5
  61. data/lib/sequel/adapters/swift/postgres.rb +0 -5
  62. data/lib/sequel/adapters/swift/sqlite.rb +6 -4
  63. data/lib/sequel/adapters/tinytds.rb +13 -10
  64. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +8 -0
  65. data/lib/sequel/core.rb +40 -0
  66. data/lib/sequel/database/connecting.rb +1 -2
  67. data/lib/sequel/database/dataset.rb +3 -3
  68. data/lib/sequel/database/dataset_defaults.rb +58 -0
  69. data/lib/sequel/database/misc.rb +62 -2
  70. data/lib/sequel/database/query.rb +113 -49
  71. data/lib/sequel/database/schema_methods.rb +7 -2
  72. data/lib/sequel/dataset/actions.rb +37 -19
  73. data/lib/sequel/dataset/features.rb +24 -0
  74. data/lib/sequel/dataset/graph.rb +7 -6
  75. data/lib/sequel/dataset/misc.rb +11 -3
  76. data/lib/sequel/dataset/mutation.rb +2 -3
  77. data/lib/sequel/dataset/prepared_statements.rb +6 -4
  78. data/lib/sequel/dataset/query.rb +46 -15
  79. data/lib/sequel/dataset/sql.rb +28 -4
  80. data/lib/sequel/extensions/named_timezones.rb +5 -0
  81. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  82. data/lib/sequel/model.rb +2 -1
  83. data/lib/sequel/model/associations.rb +115 -33
  84. data/lib/sequel/model/base.rb +91 -31
  85. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  86. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  87. data/lib/sequel/plugins/force_encoding.rb +6 -6
  88. data/lib/sequel/plugins/identity_map.rb +1 -1
  89. data/lib/sequel/plugins/many_through_many.rb +6 -10
  90. data/lib/sequel/plugins/prepared_statements.rb +12 -1
  91. data/lib/sequel/plugins/prepared_statements_associations.rb +1 -1
  92. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  93. data/lib/sequel/plugins/serialization.rb +6 -1
  94. data/lib/sequel/plugins/sharding.rb +0 -5
  95. data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
  96. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  97. data/lib/sequel/plugins/update_primary_key.rb +1 -1
  98. data/lib/sequel/timezones.rb +42 -42
  99. data/lib/sequel/version.rb +1 -1
  100. data/spec/adapters/mssql_spec.rb +29 -29
  101. data/spec/adapters/mysql_spec.rb +86 -104
  102. data/spec/adapters/oracle_spec.rb +48 -76
  103. data/spec/adapters/postgres_spec.rb +98 -33
  104. data/spec/adapters/spec_helper.rb +0 -5
  105. data/spec/adapters/sqlite_spec.rb +24 -21
  106. data/spec/core/connection_pool_spec.rb +9 -15
  107. data/spec/core/core_sql_spec.rb +20 -31
  108. data/spec/core/database_spec.rb +491 -227
  109. data/spec/core/dataset_spec.rb +638 -1051
  110. data/spec/core/expression_filters_spec.rb +0 -1
  111. data/spec/core/mock_adapter_spec.rb +378 -0
  112. data/spec/core/object_graph_spec.rb +48 -114
  113. data/spec/core/schema_generator_spec.rb +3 -3
  114. data/spec/core/schema_spec.rb +51 -114
  115. data/spec/core/spec_helper.rb +3 -90
  116. data/spec/extensions/class_table_inheritance_spec.rb +1 -1
  117. data/spec/extensions/dataset_associations_spec.rb +199 -0
  118. data/spec/extensions/instance_hooks_spec.rb +71 -0
  119. data/spec/extensions/named_timezones_spec.rb +22 -2
  120. data/spec/extensions/nested_attributes_spec.rb +3 -0
  121. data/spec/extensions/schema_spec.rb +1 -1
  122. data/spec/extensions/serialization_modification_detection_spec.rb +1 -0
  123. data/spec/extensions/serialization_spec.rb +5 -8
  124. data/spec/extensions/spec_helper.rb +4 -0
  125. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  126. data/spec/extensions/typecast_on_load_spec.rb +1 -6
  127. data/spec/integration/associations_test.rb +123 -12
  128. data/spec/integration/dataset_test.rb +140 -47
  129. data/spec/integration/eager_loader_test.rb +19 -21
  130. data/spec/integration/model_test.rb +80 -1
  131. data/spec/integration/plugin_test.rb +179 -128
  132. data/spec/integration/prepared_statement_test.rb +92 -91
  133. data/spec/integration/schema_test.rb +42 -23
  134. data/spec/integration/spec_helper.rb +25 -31
  135. data/spec/integration/timezone_test.rb +38 -12
  136. data/spec/integration/transaction_test.rb +161 -34
  137. data/spec/integration/type_test.rb +3 -3
  138. data/spec/model/association_reflection_spec.rb +83 -7
  139. data/spec/model/associations_spec.rb +393 -676
  140. data/spec/model/base_spec.rb +186 -116
  141. data/spec/model/dataset_methods_spec.rb +7 -27
  142. data/spec/model/eager_loading_spec.rb +343 -867
  143. data/spec/model/hooks_spec.rb +160 -79
  144. data/spec/model/model_spec.rb +118 -165
  145. data/spec/model/plugins_spec.rb +7 -13
  146. data/spec/model/record_spec.rb +138 -207
  147. data/spec/model/spec_helper.rb +10 -73
  148. metadata +14 -8
@@ -241,16 +241,6 @@ module Sequel
241
241
  self << drop_language_sql(name, opts)
242
242
  end
243
243
 
244
- # Remove the cached entries for primary keys and sequences when dropping a table.
245
- def drop_table(*names)
246
- names.each do |name|
247
- name = quote_schema_table(name)
248
- @primary_keys.delete(name)
249
- @primary_key_sequences.delete(name)
250
- end
251
- super
252
- end
253
-
254
244
  # Drops a trigger from the database. Arguments:
255
245
  # * table : table from which to drop the trigger
256
246
  # * name : name of the trigger to drop
@@ -294,6 +284,16 @@ module Sequel
294
284
  dataset.from(:pg_class).join(:pg_locks, :relation=>:relfilenode).select(:pg_class__relname, Sequel::SQL::ColumnAll.new(:pg_locks))
295
285
  end
296
286
 
287
+ # Notifies the given channel. See the PostgreSQL NOTIFY documentation. Options:
288
+ #
289
+ # :payload :: The payload string to use for the NOTIFY statement. Only supported
290
+ # in PostgreSQL 9.0+.
291
+ # :server :: The server to which to send the NOTIFY statement, if the sharding support
292
+ # is being used.
293
+ def notify(channel, opts={})
294
+ execute_ddl("NOTIFY #{channel}#{", #{literal(opts[:payload].to_s)}" if opts[:payload]}", opts)
295
+ end
296
+
297
297
  # Return primary key for the given table.
298
298
  def primary_key(table, opts={})
299
299
  quoted_table = quote_schema_table(table)
@@ -398,8 +398,8 @@ module Sequel
398
398
  # If the :prepare option is given and we aren't in a savepoint,
399
399
  # prepare the transaction for a two-phase commit.
400
400
  def commit_transaction(conn, opts={})
401
- if opts[:prepare] && Thread.current[:sequel_transaction_depth] <= 1
402
- log_connection_execute(conn, "PREPARE TRANSACTION #{literal(opts[:prepare])}")
401
+ if (s = opts[:prepare]) && @transactions[conn][:savepoint_level] <= 1
402
+ log_connection_execute(conn, "PREPARE TRANSACTION #{literal(s)}")
403
403
  else
404
404
  super
405
405
  end
@@ -548,6 +548,15 @@ module Sequel
548
548
  PREPARED_ARG_PLACEHOLDER
549
549
  end
550
550
 
551
+ # Remove the cached entries for primary keys and sequences when a table is
552
+ # changed.
553
+ def remove_cached_schema(table)
554
+ tab = quote_schema_table(table)
555
+ @primary_keys.delete(tab)
556
+ @primary_key_sequences.delete(tab)
557
+ super
558
+ end
559
+
551
560
  # SQL DDL statement for renaming a table. PostgreSQL doesn't allow you to change a table's schema in
552
561
  # a rename table operation, so speciying a new schema in new_name will not have an effect.
553
562
  def rename_table_sql(name, new_name)
@@ -562,8 +571,8 @@ module Sequel
562
571
 
563
572
  # The dataset used for parsing table schemas, using the pg_* system catalogs.
564
573
  def schema_parse_table(table_name, opts)
565
- m = output_identifier_meth
566
- m2 = input_identifier_meth
574
+ m = output_identifier_meth(opts[:dataset])
575
+ m2 = input_identifier_meth(opts[:dataset])
567
576
  ds = metadata_dataset.select(:pg_attribute__attname___name,
568
577
  SQL::Function.new(:format_type, :pg_type__oid, :pg_attribute__atttypmod).as(:db_type),
569
578
  SQL::Function.new(:pg_get_expr, :pg_attrdef__adbin, :pg_class__oid).as(:default),
@@ -692,7 +701,7 @@ module Sequel
692
701
  def complex_expression_sql(op, args)
693
702
  case op
694
703
  when :^
695
- "(#{literal(args.at(0))} # #{literal(args.at(1))})"
704
+ "(#{args.collect{|a| literal(a)}.join(" # ")})"
696
705
  else
697
706
  super
698
707
  end
@@ -722,12 +731,14 @@ module Sequel
722
731
 
723
732
  # Insert given values into the database.
724
733
  def insert(*values, &block)
725
- if @opts[:sql] || @opts[:returning]
734
+ if @opts[:returning]
726
735
  super
727
- elsif supports_insert_select?
736
+ elsif !@opts[:sql] && supports_insert_select?
728
737
  returning(insert_pk).insert(*values){|r| return r.values.first}
738
+ elsif (f = opts[:from]) && !f.empty?
739
+ execute_insert(insert_sql(*values), :table=>f.first, :values=>values.size == 1 ? values.first : values)
729
740
  else
730
- execute_insert(insert_sql(*values), :table=>opts[:from].first, :values=>values.size == 1 ? values.first : values)
741
+ super
731
742
  end
732
743
  end
733
744
 
@@ -868,6 +879,12 @@ module Sequel
868
879
  server_version >= 80400 ? SELECT_CLAUSE_METHODS_84 : SELECT_CLAUSE_METHODS
869
880
  end
870
881
 
882
+ # PostgreSQL requires parentheses around compound datasets if they use
883
+ # CTEs, and using them in other places doesn't hurt.
884
+ def compound_dataset_sql(ds)
885
+ "(#{super})"
886
+ end
887
+
871
888
  # Support FOR SHARE locking when using the :share lock style.
872
889
  def select_lock_sql(sql)
873
890
  @opts[:lock] == :share ? (sql << FOR_SHARE) : super
@@ -6,12 +6,6 @@ module Sequel
6
6
  def database_type
7
7
  :progress
8
8
  end
9
-
10
- def dataset(opts = nil)
11
- ds = super
12
- ds.extend(DatasetMethods)
13
- ds
14
- end
15
9
  end
16
10
 
17
11
  module DatasetMethods
@@ -11,14 +11,6 @@ module Sequel
11
11
  TEMP_STORE = [:default, :file, :memory].freeze
12
12
  VIEWS_FILTER = "type = 'view'".freeze
13
13
 
14
- # Run all alter_table commands in a transaction. This is technically only
15
- # needed for drop column.
16
- def alter_table(name, generator=nil, &block)
17
- remove_cached_schema(name)
18
- generator ||= Schema::AlterTableGenerator.new(self, &block)
19
- transaction{generator.operations.each{|op| alter_table_sql_list(name, [op]).flatten.each{|sql| execute_ddl(sql)}}}
20
- end
21
-
22
14
  # A symbol signifying the value of the auto_vacuum PRAGMA.
23
15
  def auto_vacuum
24
16
  AUTO_VACUUM[pragma_get(:auto_vacuum).to_i]
@@ -31,7 +23,20 @@ module Sequel
31
23
  value = AUTO_VACUUM.index(value) || (raise Error, "Invalid value for auto_vacuum option. Please specify one of :none, :full, :incremental.")
32
24
  pragma_set(:auto_vacuum, value)
33
25
  end
26
+
27
+ # Boolean signifying the value of the case_sensitive_likePRAGMA, or nil
28
+ # if not using SQLite 3.2.3+.
29
+ def case_sensitive_like
30
+ pragma_get(:case_sensitive_like).to_i == 1 if sqlite_version >= 30203
31
+ end
34
32
 
33
+ # Set the case_sensitive_like PRAGMA using the given boolean value, if using
34
+ # SQLite 3.2.3+. If not using 3.2.3+, no error is raised. See pragma_set.
35
+ # Consider using the :case_sensitive_like Database option instead.
36
+ def case_sensitive_like=(value)
37
+ pragma_set(:case_sensitive_like, !!value ? 'on' : 'off') if sqlite_version >= 30203
38
+ end
39
+
35
40
  # SQLite uses the :sqlite database type.
36
41
  def database_type
37
42
  :sqlite
@@ -162,6 +167,22 @@ module Sequel
162
167
 
163
168
  private
164
169
 
170
+ # Run all alter_table commands in a transaction. This is technically only
171
+ # needed for drop column.
172
+ def apply_alter_table(table, ops)
173
+ transaction do
174
+ if ops.length > 1 && ops.all?{|op| op[:op] == :add_constraint}
175
+ # If you are just doing constraints, apply all of them at the same time,
176
+ # as otherwise all but the last one get lost.
177
+ alter_table_sql_list(table, [{:op=>:add_constraints, :ops=>ops}]).flatten.each{|sql| execute_ddl(sql)}
178
+ else
179
+ # Run each operation separately, as later operations may depend on the
180
+ # results of earlier operations.
181
+ ops.each{|op| alter_table_sql_list(table, [op]).flatten.each{|sql| execute_ddl(sql)}}
182
+ end
183
+ end
184
+ end
185
+
165
186
  # SQLite supports limited table modification. You can add a column
166
187
  # or an index. Dropping columns is supported by copying the table into
167
188
  # a temporary table, dropping the table, and creating a new table without
@@ -188,11 +209,26 @@ module Sequel
188
209
  duplicate_table(table){|columns| columns.each{|s| s[:null] = op[:null] if s[:name].to_s == op[:name].to_s}}
189
210
  when :set_column_type
190
211
  duplicate_table(table){|columns| columns.each{|s| s[:type] = op[:type] if s[:name].to_s == op[:name].to_s}}
212
+ when :drop_constraint
213
+ case op[:type]
214
+ when :primary_key
215
+ duplicate_table(table){|columns| columns.each{|s| s[:primary_key] = nil}}
216
+ when :foreign_key
217
+ duplicate_table(table){|columns| columns.each{|s| s[:table] = nil}}
218
+ when :unique
219
+ duplicate_table(table)
220
+ else
221
+ raise Error, "Unsupported :type option for drop_constraint: #{op[:type].inspect}"
222
+ end
223
+ when :add_constraint
224
+ duplicate_table(table, :constraints=>[op])
225
+ when :add_constraints
226
+ duplicate_table(table, :constraints=>op[:ops])
191
227
  else
192
- raise Error, "Unsupported ALTER TABLE operation"
228
+ raise Error, "Unsupported ALTER TABLE operation: #{op[:op].inspect}"
193
229
  end
194
230
  end
195
-
231
+
196
232
  # The array of column symbols in the table, except for ones given in opts[:except]
197
233
  def backup_table_name(table, opts={})
198
234
  table = table.gsub('`', '')
@@ -202,17 +238,14 @@ module Sequel
202
238
  end
203
239
  end
204
240
 
205
- # Allow use without a generator, needed for the alter table hackery that Sequel allows.
206
- def column_list_sql(generator)
207
- generator.is_a?(Schema::Generator) ? super : generator.map{|c| column_definition_sql(c)}.join(', ')
208
- end
209
-
210
241
  # Array of PRAGMA SQL statements based on the Database options that should be applied to
211
242
  # new connections.
212
243
  def connection_pragmas
213
244
  ps = []
214
245
  v = typecast_value_boolean(opts.fetch(:foreign_keys, 1))
215
246
  ps << "PRAGMA foreign_keys = #{v ? 1 : 0}"
247
+ v = typecast_value_boolean(opts.fetch(:case_sensitive_like, 1))
248
+ ps << "PRAGMA case_sensitive_like = #{v ? 1 : 0}"
216
249
  [[:auto_vacuum, AUTO_VACUUM], [:synchronous, SYNCHRONOUS], [:temp_store, TEMP_STORE]].each do |prag, con|
217
250
  if v = opts[prag]
218
251
  raise(Error, "Value for PRAGMA #{prag} not supported, should be one of #{con.join(', ')}") unless v = con.index(v.to_sym)
@@ -234,15 +267,20 @@ module Sequel
234
267
  cols.reject!{|c| nono.include? c[:name] }
235
268
  end
236
269
 
237
- if foreign_keys
238
- metadata_dataset.with_sql("PRAGMA foreign_key_list(?)", input_identifier_meth.call(table)).each do |row|
239
- c = cols.find {|co| co[:name] == row[:from] } or next
240
- c[:table] = row[:table]
241
- c[:key] = row[:to]
242
- c[:on_update] = on_delete_sql_to_sym(row[:on_update])
243
- c[:on_delete] = on_delete_sql_to_sym(row[:on_delete])
244
- # is there any way to get deferrable status?
270
+ begin
271
+ if foreign_keys
272
+ metadata_dataset.with_sql("PRAGMA foreign_key_list(?)", input_identifier_meth.call(table)).each do |row|
273
+ c = cols.find {|co| co[:name] == row[:from] } or next
274
+ c[:table] = row[:table]
275
+ c[:key] = row[:to]
276
+ c[:on_update] = on_delete_sql_to_sym(row[:on_update])
277
+ c[:on_delete] = on_delete_sql_to_sym(row[:on_delete])
278
+ # is there any way to get deferrable status?
279
+ end
245
280
  end
281
+ rescue Sequel::DatabaseError
282
+ # Doesn't work correctly on some versions of JDBC SQLite,
283
+ # giving a "query does not return ResultSet" error.
246
284
  end
247
285
  cols
248
286
  end
@@ -257,17 +295,26 @@ module Sequel
257
295
  opts[:old_columns_proc].call(old_columns) if opts[:old_columns_proc]
258
296
 
259
297
  yield def_columns if block_given?
260
- def_columns_str = column_list_sql(def_columns)
298
+
299
+ constraints = (opts[:constraints] || []).dup
300
+ pks = []
301
+ def_columns.each{|c| pks << c[:name] if c[:primary_key]}
302
+ if pks.length > 1
303
+ constraints << {:type=>:primary_key, :columns=>pks}
304
+ def_columns.each{|c| c[:primary_key] = false if c[:primary_key]}
305
+ end
306
+
307
+ def_columns_str = (def_columns.map{|c| column_definition_sql(c)} + constraints.map{|c| constraint_definition_sql(c)}).join(', ')
261
308
  new_columns = old_columns.dup
262
309
  opts[:new_columns_proc].call(new_columns) if opts[:new_columns_proc]
263
310
 
264
311
  qt = quote_schema_table(table)
265
312
  bt = quote_identifier(backup_table_name(qt))
266
313
  a = [
267
- "CREATE TABLE #{bt}(#{def_columns_str})",
268
- "INSERT INTO #{bt}(#{dataset.send(:identifier_list, new_columns)}) SELECT #{dataset.send(:identifier_list, old_columns)} FROM #{qt}",
269
- "DROP TABLE #{qt}",
270
- "ALTER TABLE #{bt} RENAME TO #{qt}"
314
+ "ALTER TABLE #{qt} RENAME TO #{bt}",
315
+ "CREATE TABLE #{qt}(#{def_columns_str})",
316
+ "INSERT INTO #{qt}(#{dataset.send(:identifier_list, new_columns)}) SELECT #{dataset.send(:identifier_list, old_columns)} FROM #{bt}",
317
+ "DROP TABLE #{bt}"
271
318
  ]
272
319
  indexes(table).each do |name, h|
273
320
  if (h[:columns].map{|x| x.to_s} - new_columns).empty?
@@ -306,7 +353,7 @@ module Sequel
306
353
 
307
354
  # Parse the output of the table_info pragma
308
355
  def parse_pragma(table_name, opts)
309
- metadata_dataset.with_sql("PRAGMA table_info(?)", input_identifier_meth.call(table_name)).map do |row|
356
+ metadata_dataset.with_sql("PRAGMA table_info(?)", input_identifier_meth(opts[:dataset]).call(table_name)).map do |row|
310
357
  row.delete(:cid)
311
358
  row[:allow_null] = row.delete(:notnull).to_i == 0
312
359
  row[:default] = row.delete(:dflt_value)
@@ -326,7 +373,7 @@ module Sequel
326
373
  # SQLite supports schema parsing using the table_info PRAGMA, so
327
374
  # parse the output of that into the format Sequel expects.
328
375
  def schema_parse_table(table_name, opts)
329
- m = output_identifier_meth
376
+ m = output_identifier_meth(opts[:dataset])
330
377
  parse_pragma(table_name, opts).map do |row|
331
378
  [m.call(row.delete(:name)), row]
332
379
  end
@@ -361,13 +408,16 @@ module Sequel
361
408
  case op
362
409
  when :~, :'!~', :'~*', :'!~*'
363
410
  raise Error, "SQLite does not support pattern matching via regular expressions"
364
- when :LIKE, :'NOT LIKE', :ILIKE, :'NOT ILIKE'
365
- # SQLite is case insensitive for ASCII, and non case sensitive for other character sets
366
- "#{'NOT ' if [:'NOT LIKE', :'NOT ILIKE'].include?(op)}(#{literal(args.at(0))} LIKE #{literal(args.at(1))})"
411
+ when :ILIKE
412
+ super(:LIKE, args.map{|a| SQL::Function.new(:upper, a)})
413
+ when :"NOT LIKE", :"NOT ILIKE"
414
+ "NOT #{complex_expression_sql((op == :"NOT ILIKE" ? :ILIKE : :LIKE), args)}"
367
415
  when :^
368
- a = literal(args.at(0))
369
- b = literal(args.at(1))
370
- "((~(#{a} & #{b})) & (#{a} | #{b}))"
416
+ complex_expression_arg_pairs(args) do |a, b|
417
+ a = literal(a)
418
+ b = literal(b)
419
+ "((~(#{a} & #{b})) & (#{a} | #{b}))"
420
+ end
371
421
  when :extract
372
422
  part = args.at(0)
373
423
  raise(Sequel::Error, "unsupported extract argument: #{part.inspect}") unless format = EXTRACT_MAP[part]
@@ -408,6 +458,18 @@ module Sequel
408
458
  "`#{c}`"
409
459
  end
410
460
 
461
+ # When a qualified column is selected on SQLite and the qualifier
462
+ # is a subselect, the column name used is the full qualified name
463
+ # (including the qualifier) instead of just the column name. To
464
+ # get correct column names, you must use an alias.
465
+ def select(*cols)
466
+ if ((f = @opts[:from]) && f.any?{|t| t.is_a?(Dataset) || (t.is_a?(SQL::AliasedExpression) && t.expression.is_a?(Dataset))}) || ((j = @opts[:join]) && j.any?{|t| t.table.is_a?(Dataset)})
467
+ super(*cols.map{|c| alias_qualified_column(c)})
468
+ else
469
+ super
470
+ end
471
+ end
472
+
411
473
  # SQLite does not support INTERSECT ALL or EXCEPT ALL
412
474
  def supports_intersect_except_all?
413
475
  false
@@ -442,6 +504,23 @@ module Sequel
442
504
  aliaz = aliaz.value if aliaz.is_a?(SQL::Identifier)
443
505
  "#{expression} AS #{literal(aliaz.to_s)}"
444
506
  end
507
+
508
+ # If col is a qualified column, alias it to the same as the column name
509
+ def alias_qualified_column(col)
510
+ case col
511
+ when Symbol
512
+ t, c, a = split_symbol(col)
513
+ if t && !a
514
+ alias_qualified_column(SQL::QualifiedIdentifier.new(t, c))
515
+ else
516
+ col
517
+ end
518
+ when SQL::QualifiedIdentifier
519
+ SQL::AliasedExpression.new(col, col.column)
520
+ else
521
+ col
522
+ end
523
+ end
445
524
 
446
525
  # SQL fragment specifying a list of identifiers
447
526
  def identifier_list(columns)
@@ -464,7 +543,7 @@ module Sequel
464
543
  def select_lock_sql(sql)
465
544
  super unless @opts[:lock] == :update
466
545
  end
467
-
546
+
468
547
  # SQLite treats a DELETE with no WHERE clause as a TRUNCATE
469
548
  def _truncate_sql(table)
470
549
  "DELETE FROM #{table}"
@@ -21,7 +21,6 @@ module Sequel
21
21
  # Hash with string keys and callable values for converting SQLite types.
22
22
  SQLITE_TYPES = {}
23
23
  {
24
- %w'timestamp datetime' => ::Sequel.method(:database_to_application_timestamp),
25
24
  %w'date' => ::Sequel.method(:string_to_date),
26
25
  %w'time' => ::Sequel.method(:string_to_time),
27
26
  %w'bit bool boolean' => tt.method(:boolean),
@@ -47,6 +46,16 @@ module Sequel
47
46
  end
48
47
 
49
48
  private_class_method :uri_to_options
49
+
50
+ # The conversion procs to use for this database
51
+ attr_reader :conversion_procs
52
+
53
+ def initialize(opts={})
54
+ super
55
+ @conversion_procs = SQLITE_TYPES.dup
56
+ @conversion_procs['timestamp'] = method(:to_application_timestamp)
57
+ @conversion_procs['datetime'] = method(:to_application_timestamp)
58
+ end
50
59
 
51
60
  # Connect to the database. Since SQLite is a file based database,
52
61
  # the only options available are :database (to specify the database
@@ -68,11 +77,6 @@ module Sequel
68
77
  db
69
78
  end
70
79
 
71
- # Return instance of Sequel::SQLite::Dataset with the given options.
72
- def dataset(opts = nil)
73
- SQLite::Dataset.new(self, opts)
74
- end
75
-
76
80
  # Run the given SQL with the given arguments and yield each row.
77
81
  def execute(sql, opts={}, &block)
78
82
  _execute(:select, sql, opts, &block)
@@ -113,7 +117,8 @@ module Sequel
113
117
  synchronize(opts[:server]) do |conn|
114
118
  return execute_prepared_statement(conn, type, sql, opts, &block) if sql.is_a?(Symbol)
115
119
  log_args = opts[:arguments]
116
- args = opts.fetch(:arguments, [])
120
+ args = {}
121
+ opts.fetch(:arguments, {}).each{|k, v| args[k] = prepared_statement_argument(v)}
117
122
  case type
118
123
  when :select
119
124
  log_yield(sql, log_args){conn.query(sql, args, &block)}
@@ -200,6 +205,8 @@ module Sequel
200
205
  # Dataset class for SQLite datasets that use the ruby-sqlite3 driver.
201
206
  class Dataset < Sequel::Dataset
202
207
  include ::Sequel::SQLite::DatasetMethods
208
+
209
+ Database::DatasetClass = self
203
210
 
204
211
  PREPARED_ARG_PLACEHOLDER = ':'.freeze
205
212
 
@@ -288,7 +295,8 @@ module Sequel
288
295
  def fetch_rows(sql)
289
296
  execute(sql) do |result|
290
297
  i = -1
291
- type_procs = result.types.map{|t| SQLITE_TYPES[base_type_name(t)]}
298
+ cps = db.conversion_procs
299
+ type_procs = result.types.map{|t| cps[base_type_name(t)]}
292
300
  cols = result.columns.map{|c| i+=1; [output_identifier(c), i, type_procs[i]]}
293
301
  @columns = cols.map{|c| c.first}
294
302
  result.each do |values|
@@ -15,16 +15,19 @@ module Sequel
15
15
  DATABASE_SETUP = {:postgres=>proc do |db|
16
16
  Sequel.ts_require 'adapters/swift/postgres'
17
17
  db.extend(Sequel::Swift::Postgres::DatabaseMethods)
18
+ db.dataset_class = Sequel::Swift::Postgres::Dataset
18
19
  db.swift_class = ::Swift::DB::Postgres
19
20
  end,
20
21
  :mysql=>proc do |db|
21
22
  Sequel.ts_require 'adapters/swift/mysql'
22
23
  db.extend(Sequel::Swift::MySQL::DatabaseMethods)
24
+ db.dataset_class = Sequel::Swift::MySQL::Dataset
23
25
  db.swift_class = ::Swift::DB::Mysql
24
26
  end,
25
27
  :sqlite=>proc do |db|
26
28
  Sequel.ts_require 'adapters/swift/sqlite'
27
29
  db.extend(Sequel::Swift::SQLite::DatabaseMethods)
30
+ db.dataset_class = Sequel::Swift::SQLite::Dataset
28
31
  db.swift_class = ::Swift::DB::Sqlite3
29
32
  end,
30
33
  }
@@ -59,11 +62,6 @@ module Sequel
59
62
  setup_connection(swift_class.new(server_opts(server)))
60
63
  end
61
64
 
62
- # Return a Sequel::Swift::Dataset object for this database.
63
- def dataset(opts = nil)
64
- Swift::Dataset.new(self, opts)
65
- end
66
-
67
65
  # Execute the given SQL, yielding a Swift::Result if a block is given.
68
66
  def execute(sql, opts={})
69
67
  synchronize(opts[:server]) do |conn|
@@ -134,6 +132,8 @@ module Sequel
134
132
  end
135
133
 
136
134
  class Dataset < Sequel::Dataset
135
+ Database::DatasetClass = self
136
+
137
137
  # Set the columns and yield the hashes to the block.
138
138
  def fetch_rows(sql, &block)
139
139
  execute(sql) do |res|