sequel 3.1.0 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/CHANGELOG +76 -0
  2. data/Rakefile +2 -2
  3. data/bin/sequel +9 -4
  4. data/doc/opening_databases.rdoc +279 -0
  5. data/doc/release_notes/3.2.0.txt +268 -0
  6. data/doc/virtual_rows.rdoc +42 -51
  7. data/lib/sequel/adapters/ado.rb +2 -5
  8. data/lib/sequel/adapters/db2.rb +5 -0
  9. data/lib/sequel/adapters/do.rb +3 -0
  10. data/lib/sequel/adapters/firebird.rb +6 -4
  11. data/lib/sequel/adapters/informix.rb +5 -3
  12. data/lib/sequel/adapters/jdbc.rb +10 -8
  13. data/lib/sequel/adapters/jdbc/h2.rb +17 -4
  14. data/lib/sequel/adapters/mysql.rb +6 -19
  15. data/lib/sequel/adapters/odbc.rb +14 -18
  16. data/lib/sequel/adapters/openbase.rb +8 -0
  17. data/lib/sequel/adapters/shared/mssql.rb +14 -8
  18. data/lib/sequel/adapters/shared/mysql.rb +53 -28
  19. data/lib/sequel/adapters/shared/oracle.rb +21 -12
  20. data/lib/sequel/adapters/shared/postgres.rb +46 -26
  21. data/lib/sequel/adapters/shared/progress.rb +10 -5
  22. data/lib/sequel/adapters/shared/sqlite.rb +28 -12
  23. data/lib/sequel/adapters/sqlite.rb +4 -3
  24. data/lib/sequel/adapters/utils/stored_procedures.rb +18 -9
  25. data/lib/sequel/connection_pool.rb +4 -3
  26. data/lib/sequel/database.rb +110 -10
  27. data/lib/sequel/database/schema_sql.rb +12 -3
  28. data/lib/sequel/dataset.rb +40 -3
  29. data/lib/sequel/dataset/convenience.rb +0 -11
  30. data/lib/sequel/dataset/graph.rb +25 -11
  31. data/lib/sequel/dataset/sql.rb +176 -68
  32. data/lib/sequel/extensions/migration.rb +37 -21
  33. data/lib/sequel/extensions/schema_dumper.rb +8 -61
  34. data/lib/sequel/model.rb +3 -3
  35. data/lib/sequel/model/associations.rb +9 -1
  36. data/lib/sequel/model/base.rb +8 -1
  37. data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
  38. data/lib/sequel/sql.rb +125 -18
  39. data/lib/sequel/version.rb +1 -1
  40. data/spec/adapters/ado_spec.rb +1 -0
  41. data/spec/adapters/firebird_spec.rb +1 -0
  42. data/spec/adapters/informix_spec.rb +1 -0
  43. data/spec/adapters/mysql_spec.rb +23 -8
  44. data/spec/adapters/oracle_spec.rb +1 -0
  45. data/spec/adapters/postgres_spec.rb +52 -4
  46. data/spec/adapters/spec_helper.rb +2 -2
  47. data/spec/adapters/sqlite_spec.rb +2 -1
  48. data/spec/core/connection_pool_spec.rb +16 -0
  49. data/spec/core/database_spec.rb +174 -0
  50. data/spec/core/dataset_spec.rb +121 -26
  51. data/spec/core/expression_filters_spec.rb +156 -0
  52. data/spec/core/object_graph_spec.rb +20 -1
  53. data/spec/core/schema_spec.rb +5 -5
  54. data/spec/extensions/migration_spec.rb +140 -74
  55. data/spec/extensions/schema_dumper_spec.rb +3 -69
  56. data/spec/extensions/single_table_inheritance_spec.rb +6 -0
  57. data/spec/integration/dataset_test.rb +84 -2
  58. data/spec/integration/schema_test.rb +24 -5
  59. data/spec/integration/spec_helper.rb +8 -6
  60. data/spec/model/eager_loading_spec.rb +9 -0
  61. data/spec/model/record_spec.rb +35 -8
  62. metadata +8 -7
  63. data/lib/sequel/adapters/utils/date_format.rb +0 -21
  64. data/lib/sequel/adapters/utils/savepoint_transactions.rb +0 -80
  65. data/lib/sequel/adapters/utils/unsupported.rb +0 -50
@@ -1,10 +1,4 @@
1
- Sequel.require %w'unsupported savepoint_transactions', 'adapters/utils'
2
-
3
1
  module Sequel
4
- class Database
5
- # Keep default column_references_sql for add_foreign_key support
6
- alias default_column_references_sql column_references_sql
7
- end
8
2
  module MySQL
9
3
  class << self
10
4
  # Set the default options used for CREATE TABLE
@@ -14,8 +8,6 @@ module Sequel
14
8
  # Methods shared by Database instances that connect to MySQL,
15
9
  # currently supported by the native and JDBC adapters.
16
10
  module DatabaseMethods
17
- include Sequel::Database::SavepointTransactions
18
-
19
11
  AUTO_INCREMENT = 'AUTO_INCREMENT'.freeze
20
12
  CAST_TYPES = {String=>:CHAR, Integer=>:SIGNED, Time=>:DATETIME, DateTime=>:DATETIME, Numeric=>:DECIMAL, BigDecimal=>:DECIMAL, File=>:BINARY}
21
13
  PRIMARY = 'PRIMARY'.freeze
@@ -68,6 +60,11 @@ module Sequel
68
60
  metadata_dataset.with_sql('SHOW TABLES').server(opts[:server]).map{|r| m.call(r.values.first)}
69
61
  end
70
62
 
63
+ # MySQL supports savepoints
64
+ def supports_savepoints?
65
+ true
66
+ end
67
+
71
68
  # Changes the database in use by issuing a USE statement. I would be
72
69
  # very careful if I used this.
73
70
  def use(db_name)
@@ -87,7 +84,7 @@ module Sequel
87
84
  if related = op.delete(:table)
88
85
  sql = super(table, op)
89
86
  op[:table] = related
90
- [sql, "ALTER TABLE #{quote_schema_table(table)} ADD FOREIGN KEY (#{quote_identifier(op[:name])})#{default_column_references_sql(op)}"]
87
+ [sql, "ALTER TABLE #{quote_schema_table(table)} ADD FOREIGN KEY (#{quote_identifier(op[:name])})#{column_references_sql(op)}"]
91
88
  else
92
89
  super(table, op)
93
90
  end
@@ -98,8 +95,8 @@ module Sequel
98
95
  name = o == :rename_column ? op[:new_name] : op[:name]
99
96
  type = o == :set_column_type ? op[:type] : old_opts[:db_type]
100
97
  null = o == :set_column_null ? op[:null] : old_opts[:allow_null]
101
- default = o == :set_column_default ? op[:default] : (old_opts[:default].lit if old_opts[:default])
102
- "ALTER TABLE #{quote_schema_table(table)} CHANGE COLUMN #{quote_identifier(op[:name])} #{column_definition_sql(:name=>name, :type=>type, :null=>null, :default=>default)}"
98
+ default = o == :set_column_default ? op[:default] : old_opts[:ruby_default]
99
+ "ALTER TABLE #{quote_schema_table(table)} CHANGE COLUMN #{quote_identifier(op[:name])} #{column_definition_sql(op.merge(:name=>name, :type=>type, :null=>null, :default=>default))}"
103
100
  when :drop_index
104
101
  "#{drop_index_sql(table, op)} ON #{quote_schema_table(table)}"
105
102
  else
@@ -119,9 +116,9 @@ module Sequel
119
116
  super
120
117
  end
121
118
 
122
- # Handle MySQL specific syntax for column references
123
- def column_references_sql(column)
124
- "#{", FOREIGN KEY (#{quote_identifier(column[:name])})" unless column[:type] == :check}#{super(column)}"
119
+ # MySQL doesn't handle references as column constraints, it must use a separate table constraint
120
+ def column_references_column_constraint_sql(column)
121
+ "#{", FOREIGN KEY (#{quote_identifier(column[:name])})" unless column[:type] == :check}#{column_references_sql(column)}"
125
122
  end
126
123
 
127
124
  # Use MySQL specific syntax for engine type and character encoding
@@ -199,12 +196,11 @@ module Sequel
199
196
 
200
197
  # Dataset methods shared by datasets that use MySQL databases.
201
198
  module DatasetMethods
202
- include Dataset::UnsupportedIntersectExcept
203
-
204
199
  BOOL_TRUE = '1'.freeze
205
200
  BOOL_FALSE = '0'.freeze
206
201
  TIMESTAMP_FORMAT = "'%Y-%m-%d %H:%M:%S'".freeze
207
202
  COMMA_SEPARATOR = ', '.freeze
203
+ SELECT_CLAUSE_ORDER = %w'distinct columns from join where group having compounds order limit'.freeze
208
204
 
209
205
  # MySQL specific syntax for LIKE/REGEXP searches, as well as
210
206
  # string concatenation.
@@ -231,12 +227,6 @@ module Sequel
231
227
  sql
232
228
  end
233
229
 
234
- # MySQL doesn't support DISTINCT ON
235
- def distinct(*columns)
236
- raise(Error, "DISTINCT ON not supported by MySQL") unless columns.empty?
237
- super
238
- end
239
-
240
230
  # Adds full text filter
241
231
  def full_text_search(cols, terms, opts = {})
242
232
  filter(full_text_sql(cols, terms, opts))
@@ -313,15 +303,11 @@ module Sequel
313
303
  def on_duplicate_key_update(*args)
314
304
  clone(:on_duplicate_key_update => args)
315
305
  end
316
-
306
+
317
307
  # MySQL specific syntax for inserting multiple values at once.
318
308
  def multi_insert_sql(columns, values)
319
- if update_cols = opts[:on_duplicate_key_update]
320
- update_cols = columns if update_cols.empty?
321
- update_string = update_cols.map{|c| "#{quote_identifier(c)}=VALUES(#{quote_identifier(c)})"}.join(COMMA_SEPARATOR)
322
- end
323
309
  values = values.map {|r| literal(Array(r))}.join(COMMA_SEPARATOR)
324
- ["#{insert_sql_base}#{source_list(@opts[:from])} (#{identifier_list(columns)}) VALUES #{values}#{" ON DUPLICATE KEY UPDATE #{update_string}" if update_string}"]
310
+ ["#{insert_sql_base}#{source_list(@opts[:from])} (#{identifier_list(columns)}) VALUES #{values}#{insert_sql_suffix}"]
325
311
  end
326
312
 
327
313
  # MySQL uses the nonstandard ` (backtick) for quoting identifiers.
@@ -365,6 +351,16 @@ module Sequel
365
351
  end
366
352
  end
367
353
 
354
+ # does not support DISTINCT ON
355
+ def supports_distinct_on?
356
+ false
357
+ end
358
+
359
+ # MySQL does not support INTERSECT or EXCEPT
360
+ def supports_intersect_except?
361
+ false
362
+ end
363
+
368
364
  # MySQL supports ORDER and LIMIT clauses in UPDATE statements.
369
365
  def update_sql(values)
370
366
  sql = super
@@ -380,6 +376,11 @@ module Sequel
380
376
  "INSERT #{'IGNORE ' if opts[:insert_ignore]}INTO "
381
377
  end
382
378
 
379
+ # MySQL supports INSERT ... ON DUPLICATE KEY UPDATE
380
+ def insert_sql_suffix
381
+ on_duplicate_key_update_sql if opts[:on_duplicate_key_update]
382
+ end
383
+
383
384
  # MySQL doesn't use the SQL standard DEFAULT VALUES.
384
385
  def insert_default_values_sql
385
386
  "#{insert_sql_base}#{source_list(@opts[:from])} () VALUES ()"
@@ -404,6 +405,30 @@ module Sequel
404
405
  def literal_true
405
406
  BOOL_TRUE
406
407
  end
408
+
409
+ # MySQL specific syntax for ON DUPLICATE KEY UPDATE
410
+ def on_duplicate_key_update_sql
411
+ if update_cols = opts[:on_duplicate_key_update]
412
+ update_vals = nil
413
+
414
+ if update_cols.empty?
415
+ update_cols = columns
416
+ elsif update_cols.last.is_a?(Hash)
417
+ update_vals = update_cols.last
418
+ update_cols = update_cols[0..-2]
419
+ end
420
+
421
+ updating = update_cols.map{|c| "#{quote_identifier(c)}=VALUES(#{quote_identifier(c)})" }
422
+ updating += update_vals.map{|c,v| "#{quote_identifier(c)}=#{literal(v)}" } if update_vals
423
+
424
+ " ON DUPLICATE KEY UPDATE #{updating.join(COMMA_SEPARATOR)}"
425
+ end
426
+ end
427
+
428
+ # MySQL does not support the SQL WITH clause
429
+ def select_clause_order
430
+ SELECT_CLAUSE_ORDER
431
+ end
407
432
  end
408
433
  end
409
434
  end
@@ -1,5 +1,3 @@
1
- Sequel.require %w'date_format unsupported', 'adapters/utils'
2
-
3
1
  module Sequel
4
2
  module Oracle
5
3
  module DatabaseMethods
@@ -96,16 +94,7 @@ module Sequel
96
94
  end
97
95
 
98
96
  module DatasetMethods
99
- include Dataset::UnsupportedIntersectExceptAll
100
- include Dataset::SQLStandardDateFormat
101
-
102
- SELECT_CLAUSE_ORDER = %w'distinct columns from join where group having compounds order limit'.freeze
103
-
104
- # Oracle doesn't support DISTINCT ON
105
- def distinct(*columns)
106
- raise(Error, "DISTINCT ON not supported by Oracle") unless columns.empty?
107
- super
108
- end
97
+ SELECT_CLAUSE_ORDER = %w'with distinct columns from join where group having compounds order limit'.freeze
109
98
 
110
99
  # Oracle uses MINUS instead of EXCEPT, and doesn't support EXCEPT ALL
111
100
  def except(dataset, all = false)
@@ -117,6 +106,26 @@ module Sequel
117
106
  db[:dual].where(exists).get(1) == nil
118
107
  end
119
108
 
109
+ # Oracle requires SQL standard datetimes
110
+ def requires_sql_standard_datetimes?
111
+ true
112
+ end
113
+
114
+ # Oracle does not support DISTINCT ON
115
+ def supports_distinct_on?
116
+ false
117
+ end
118
+
119
+ # Oracle does not support INTERSECT ALL or EXCEPT ALL
120
+ def supports_intersect_except_all?
121
+ false
122
+ end
123
+
124
+ # Oracle supports window functions
125
+ def supports_window_functions?
126
+ true
127
+ end
128
+
120
129
  private
121
130
 
122
131
  # Oracle doesn't support the use of AS when aliasing a dataset. It doesn't require
@@ -1,5 +1,3 @@
1
- Sequel.require 'adapters/utils/savepoint_transactions'
2
-
3
1
  module Sequel
4
2
  # Top level module for holding all PostgreSQL-related modules and classes
5
3
  # for Sequel. There are a few module level accessors that are added via
@@ -165,13 +163,8 @@ module Sequel
165
163
 
166
164
  # Methods shared by Database instances that connect to PostgreSQL.
167
165
  module DatabaseMethods
168
- include Sequel::Database::SavepointTransactions
169
-
170
166
  PREPARED_ARG_PLACEHOLDER = LiteralString.new('$').freeze
171
167
  RE_CURRVAL_ERROR = /currval of sequence "(.*)" is not yet defined in this session|relation "(.*)" does not exist/.freeze
172
- SQL_BEGIN = 'BEGIN'.freeze
173
- SQL_COMMIT = 'COMMIT'.freeze
174
- SQL_ROLLBACK = 'ROLLBACK'.freeze
175
168
  SYSTEM_TABLE_REGEXP = /^pg|sql/.freeze
176
169
 
177
170
  # Creates the function in the database. Arguments:
@@ -277,16 +270,20 @@ module Sequel
277
270
  m = output_identifier_meth
278
271
  im = input_identifier_meth
279
272
  schema, table = schema_and_table(table)
273
+ range = 0...32
274
+ attnums = server_version >= 80100 ? SQL::Function.new(:ANY, :ind__indkey) : range.map{|x| SQL::Subscript.new(:ind__indkey, [x])}
280
275
  ds = metadata_dataset.
281
276
  from(:pg_class___tab).
282
277
  join(:pg_index___ind, :indrelid=>:oid, im.call(table)=>:relname).
283
278
  join(:pg_class___indc, :oid=>:indexrelid).
284
- join(:pg_attribute___att, :attrelid=>:tab__oid, :attnum=>SQL::Function.new(:ANY, :ind__indkey)).
285
- filter(:indc__relkind=>'i', :ind__indisprimary=>false, :indexprs=>nil, :indpred=>nil, :indisvalid=>true, :indisready=>true, :indcheckxmin=>false).
286
- order(:indc__relname, (0...32).map{|x| [SQL::Subscript.new(:ind__indkey, [x]), x]}.case(32, :att__attnum)).
279
+ join(:pg_attribute___att, :attrelid=>:tab__oid, :attnum=>attnums).
280
+ filter(:indc__relkind=>'i', :ind__indisprimary=>false, :indexprs=>nil, :indpred=>nil).
281
+ order(:indc__relname, range.map{|x| [SQL::Subscript.new(:ind__indkey, [x]), x]}.case(32, :att__attnum)).
287
282
  select(:indc__relname___name, :ind__indisunique___unique, :att__attname___column)
288
283
 
289
- ds.join!(:pg_namespace___nsp, :oid=>:tab__relnamespace, :nspname=>schema) if schema
284
+ ds.join!(:pg_namespace___nsp, :oid=>:tab__relnamespace, :nspname=>schema.to_s) if schema
285
+ ds.filter!(:indisvalid=>true) if server_version >= 80200
286
+ ds.filter!(:indisready=>true, :indcheckxmin=>false) if server_version >= 80300
290
287
 
291
288
  indexes = {}
292
289
  ds.each do |r|
@@ -346,12 +343,17 @@ module Sequel
346
343
  (conn.server_version rescue nil) if conn.respond_to?(:server_version)
347
344
  end
348
345
  unless @server_version
349
- m = /PostgreSQL (\d+)\.(\d+)\.(\d+)/.match(get(SQL::Function.new(:version)))
346
+ m = /PostgreSQL (\d+)\.(\d+)(?:(?:rc\d+)|\.(\d+))?/.match(fetch('SELECT version()').single_value)
350
347
  @server_version = (m[1].to_i * 10000) + (m[2].to_i * 100) + m[3].to_i
351
348
  end
352
349
  @server_version
353
350
  end
354
351
 
352
+ # PostgreSQL supports savepoints
353
+ def supports_savepoints?
354
+ true
355
+ end
356
+
355
357
  # Whether the given table exists in the database
356
358
  #
357
359
  # Options:
@@ -451,7 +453,11 @@ module Sequel
451
453
  def index_definition_sql(table_name, index)
452
454
  cols = index[:columns]
453
455
  index_name = index[:name] || default_index_name(table_name, cols)
454
- expr = literal(Array(cols))
456
+ expr = if o = index[:opclass]
457
+ "(#{Array(cols).map{|c| "#{literal(c)} #{o}"}.join(', ')})"
458
+ else
459
+ literal(Array(cols))
460
+ end
455
461
  unique = "UNIQUE " if index[:unique]
456
462
  index_type = index[:type]
457
463
  filter = index[:where] || index[:filter]
@@ -582,9 +588,11 @@ module Sequel
582
588
  ROW_EXCLUSIVE = 'ROW EXCLUSIVE'.freeze
583
589
  ROW_SHARE = 'ROW SHARE'.freeze
584
590
  SELECT_CLAUSE_ORDER = %w'distinct columns from join where group having compounds order limit lock'.freeze
591
+ SELECT_CLAUSE_ORDER_84 = %w'with distinct columns from join where group having window compounds order limit lock'.freeze
585
592
  SHARE = 'SHARE'.freeze
586
593
  SHARE_ROW_EXCLUSIVE = 'SHARE ROW EXCLUSIVE'.freeze
587
594
  SHARE_UPDATE_EXCLUSIVE = 'SHARE UPDATE EXCLUSIVE'.freeze
595
+ SQL_WITH_RECURSIVE = "WITH RECURSIVE ".freeze
588
596
 
589
597
  # Shared methods for prepared statements when used with PostgreSQL databases.
590
598
  module PreparedStatementMethods
@@ -611,12 +619,8 @@ module Sequel
611
619
  end
612
620
 
613
621
  # Return the results of an ANALYZE query as a string
614
- def analyze(opts = nil)
615
- analysis = []
616
- fetch_rows(EXPLAIN_ANALYZE + select_sql(opts)) do |r|
617
- analysis << r[QUERY_PLAN]
618
- end
619
- analysis.join("\r\n")
622
+ def analyze
623
+ explain(:analyze=>true)
620
624
  end
621
625
 
622
626
  # Disable the use of INSERT RETURNING, even if the server supports it
@@ -625,12 +629,8 @@ module Sequel
625
629
  end
626
630
 
627
631
  # Return the results of an EXPLAIN query as a string
628
- def explain(opts = nil)
629
- analysis = []
630
- fetch_rows(EXPLAIN + select_sql(opts)) do |r|
631
- analysis << r[QUERY_PLAN]
632
- end
633
- analysis.join("\r\n")
632
+ def explain(opts={})
633
+ with_sql((opts[:analyze] ? EXPLAIN_ANALYZE : EXPLAIN) + select_sql).map(QUERY_PLAN).join("\r\n")
634
634
  end
635
635
 
636
636
  # Return a cloned dataset with a :share lock type.
@@ -693,6 +693,16 @@ module Sequel
693
693
  ["#{insert_sql_base}#{source_list(@opts[:from])} (#{identifier_list(columns)}) VALUES #{values}"]
694
694
  end
695
695
 
696
+ # PostgreSQL 8.4+ supports window functions
697
+ def supports_window_functions?
698
+ server_version >= 80400
699
+ end
700
+
701
+ # Return a clone of the dataset with an addition named window that can be referenced in window functions.
702
+ def window(name, opts)
703
+ clone(:window=>(@opts[:windows]||[]) + [[name, SQL::Window.new(opts)]])
704
+ end
705
+
696
706
  private
697
707
 
698
708
  # Use the RETURNING clause to return the primary key of the inserted record, if it exists
@@ -733,7 +743,12 @@ module Sequel
733
743
 
734
744
  # The order of clauses in the SELECT SQL statement
735
745
  def select_clause_order
736
- SELECT_CLAUSE_ORDER
746
+ server_version >= 80400 ? SELECT_CLAUSE_ORDER_84 : SELECT_CLAUSE_ORDER
747
+ end
748
+
749
+ # SQL fragment for named window specifications
750
+ def select_window_sql(sql)
751
+ sql << " WINDOW #{@opts[:window].map{|name, window| "#{literal(name)} AS #{literal(window)}"}.join(', ')}" if @opts[:window]
737
752
  end
738
753
 
739
754
  # Support lock mode, allowing FOR SHARE and FOR UPDATE queries.
@@ -746,6 +761,11 @@ module Sequel
746
761
  end
747
762
  end
748
763
 
764
+ # Use WITH RECURSIVE instead of WITH if any of the CTEs is recursive
765
+ def select_with_sql_base
766
+ opts[:with].any?{|w| w[:recursive]} ? SQL_WITH_RECURSIVE : super
767
+ end
768
+
749
769
  # The version of the database server
750
770
  def server_version
751
771
  db.server_version(@opts[:server])
@@ -1,5 +1,3 @@
1
- Sequel.require %w'date_format unsupported', 'adapters/utils'
2
-
3
1
  module Sequel
4
2
  module Progress
5
3
  module DatabaseMethods
@@ -17,11 +15,18 @@ module Sequel
17
15
  end
18
16
 
19
17
  module DatasetMethods
20
- include Dataset::UnsupportedIntersectExcept
21
- include Dataset::SQLStandardDateFormat
22
-
23
18
  SELECT_CLAUSE_ORDER = %w'limit distinct columns from join where group order having compounds'.freeze
24
19
 
20
+ # Progress requires SQL standard datetimes
21
+ def requires_sql_standard_datetimes?
22
+ true
23
+ end
24
+
25
+ # Progress does not support INTERSECT or EXCEPT
26
+ def supports_intersect_except?
27
+ false
28
+ end
29
+
25
30
  private
26
31
 
27
32
  def select_clause_order
@@ -1,10 +1,6 @@
1
- Sequel.require %w'savepoint_transactions unsupported', 'adapters/utils'
2
-
3
1
  module Sequel
4
2
  module SQLite
5
3
  module DatabaseMethods
6
- include Sequel::Database::SavepointTransactions
7
-
8
4
  AUTO_VACUUM = [:none, :full, :incremental].freeze
9
5
  PRIMARY_KEY_INDEX_RE = /\Asqlite_autoindex_/.freeze
10
6
  SYNCHRONOUS = [:off, :normal, :full].freeze
@@ -67,6 +63,11 @@ module Sequel
67
63
  execute_ddl("PRAGMA #{name} = #{value}")
68
64
  end
69
65
 
66
+ # SQLite supports savepoints
67
+ def supports_savepoints?
68
+ true
69
+ end
70
+
70
71
  # A symbol signifying the value of the synchronous PRAGMA.
71
72
  def synchronous
72
73
  SYNCHRONOUS[pragma_get(:synchronous).to_i]
@@ -233,9 +234,8 @@ module Sequel
233
234
 
234
235
  # Instance methods for datasets that connect to an SQLite database
235
236
  module DatasetMethods
236
- include Dataset::UnsupportedIntersectExceptAll
237
- include Dataset::UnsupportedIsTrue
238
-
237
+ SELECT_CLAUSE_ORDER = %w'distinct columns from join where group having compounds order limit'.freeze
238
+
239
239
  # SQLite does not support pattern matching via regular expressions.
240
240
  # SQLite is case insensitive (depending on pragma), so use LIKE for
241
241
  # ILIKE.
@@ -277,19 +277,35 @@ module Sequel
277
277
  "`#{c}`"
278
278
  end
279
279
 
280
- private
280
+ # SQLite does not support INTERSECT ALL or EXCEPT ALL
281
+ def supports_intersect_except_all?
282
+ false
283
+ end
281
284
 
282
- def literal_blob(v)
283
- blob = ''
284
- v.each_byte{|x| blob << sprintf('%02x', x)}
285
- "X'#{blob}'"
285
+ # SQLite does not support IS TRUE
286
+ def supports_is_true?
287
+ false
286
288
  end
289
+
290
+ private
287
291
 
288
292
  # SQLite uses string literals instead of identifiers in AS clauses.
289
293
  def as_sql(expression, aliaz)
290
294
  aliaz = aliaz.value if aliaz.is_a?(SQL::Identifier)
291
295
  "#{expression} AS #{literal(aliaz.to_s)}"
292
296
  end
297
+
298
+ # SQLite uses a preceding X for hex escaping strings
299
+ def literal_blob(v)
300
+ blob = ''
301
+ v.each_byte{|x| blob << sprintf('%02x', x)}
302
+ "X'#{blob}'"
303
+ end
304
+
305
+ # SQLite does not support the SQL WITH clause
306
+ def select_clause_order
307
+ SELECT_CLAUSE_ORDER
308
+ end
293
309
  end
294
310
  end
295
311
  end