sequel 3.23.0 → 3.24.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 (76) hide show
  1. data/CHANGELOG +64 -0
  2. data/doc/association_basics.rdoc +43 -5
  3. data/doc/model_hooks.rdoc +64 -27
  4. data/doc/prepared_statements.rdoc +8 -4
  5. data/doc/reflection.rdoc +8 -2
  6. data/doc/release_notes/3.23.0.txt +1 -1
  7. data/doc/release_notes/3.24.0.txt +420 -0
  8. data/lib/sequel/adapters/db2.rb +8 -1
  9. data/lib/sequel/adapters/firebird.rb +25 -9
  10. data/lib/sequel/adapters/informix.rb +4 -19
  11. data/lib/sequel/adapters/jdbc.rb +34 -17
  12. data/lib/sequel/adapters/jdbc/h2.rb +5 -0
  13. data/lib/sequel/adapters/jdbc/informix.rb +31 -0
  14. data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
  15. data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
  16. data/lib/sequel/adapters/jdbc/mysql.rb +9 -0
  17. data/lib/sequel/adapters/jdbc/sqlserver.rb +46 -0
  18. data/lib/sequel/adapters/postgres.rb +30 -1
  19. data/lib/sequel/adapters/shared/access.rb +10 -0
  20. data/lib/sequel/adapters/shared/informix.rb +45 -0
  21. data/lib/sequel/adapters/shared/mssql.rb +82 -8
  22. data/lib/sequel/adapters/shared/mysql.rb +25 -7
  23. data/lib/sequel/adapters/shared/postgres.rb +39 -6
  24. data/lib/sequel/adapters/shared/sqlite.rb +57 -5
  25. data/lib/sequel/adapters/sqlite.rb +8 -3
  26. data/lib/sequel/adapters/swift/mysql.rb +9 -0
  27. data/lib/sequel/ast_transformer.rb +190 -0
  28. data/lib/sequel/core.rb +1 -1
  29. data/lib/sequel/database/misc.rb +6 -0
  30. data/lib/sequel/database/query.rb +33 -3
  31. data/lib/sequel/database/schema_methods.rb +6 -2
  32. data/lib/sequel/dataset/features.rb +6 -0
  33. data/lib/sequel/dataset/prepared_statements.rb +17 -2
  34. data/lib/sequel/dataset/query.rb +17 -0
  35. data/lib/sequel/dataset/sql.rb +2 -53
  36. data/lib/sequel/exceptions.rb +4 -0
  37. data/lib/sequel/extensions/to_dot.rb +95 -83
  38. data/lib/sequel/model.rb +5 -0
  39. data/lib/sequel/model/associations.rb +80 -14
  40. data/lib/sequel/model/base.rb +182 -55
  41. data/lib/sequel/model/exceptions.rb +3 -1
  42. data/lib/sequel/plugins/association_pks.rb +6 -4
  43. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  44. data/lib/sequel/plugins/many_through_many.rb +8 -3
  45. data/lib/sequel/plugins/prepared_statements.rb +140 -0
  46. data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
  47. data/lib/sequel/plugins/prepared_statements_safe.rb +72 -0
  48. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  49. data/lib/sequel/sql.rb +8 -0
  50. data/lib/sequel/version.rb +1 -1
  51. data/spec/adapters/postgres_spec.rb +43 -18
  52. data/spec/core/connection_pool_spec.rb +56 -77
  53. data/spec/core/database_spec.rb +25 -0
  54. data/spec/core/dataset_spec.rb +127 -16
  55. data/spec/core/expression_filters_spec.rb +13 -0
  56. data/spec/core/schema_spec.rb +6 -1
  57. data/spec/extensions/association_pks_spec.rb +7 -0
  58. data/spec/extensions/defaults_setter_spec.rb +64 -0
  59. data/spec/extensions/many_through_many_spec.rb +60 -4
  60. data/spec/extensions/nested_attributes_spec.rb +1 -0
  61. data/spec/extensions/prepared_statements_associations_spec.rb +126 -0
  62. data/spec/extensions/prepared_statements_safe_spec.rb +69 -0
  63. data/spec/extensions/prepared_statements_spec.rb +72 -0
  64. data/spec/extensions/prepared_statements_with_pk_spec.rb +38 -0
  65. data/spec/extensions/to_dot_spec.rb +3 -5
  66. data/spec/integration/associations_test.rb +155 -1
  67. data/spec/integration/dataset_test.rb +8 -1
  68. data/spec/integration/plugin_test.rb +119 -0
  69. data/spec/integration/prepared_statement_test.rb +72 -1
  70. data/spec/integration/schema_test.rb +66 -8
  71. data/spec/integration/transaction_test.rb +40 -0
  72. data/spec/model/associations_spec.rb +349 -8
  73. data/spec/model/base_spec.rb +59 -0
  74. data/spec/model/hooks_spec.rb +161 -0
  75. data/spec/model/record_spec.rb +24 -0
  76. metadata +21 -4
@@ -1,6 +1,7 @@
1
1
  module Sequel
2
2
  module Access
3
3
  module DatabaseMethods
4
+ # Access uses type :access as the database_type
4
5
  def database_type
5
6
  :access
6
7
  end
@@ -16,6 +17,7 @@ module Sequel
16
17
  from(:MSysObjects).filter(:Type=>1, :Flags=>0).select_map(:Name).map{|x| x.to_sym}
17
18
  end
18
19
 
20
+ # Access uses type Counter for an autoincrementing keys
19
21
  def serial_primary_key_options
20
22
  {:primary_key => true, :type=>:Counter}
21
23
  end
@@ -34,16 +36,24 @@ module Sequel
34
36
  module DatasetMethods
35
37
  SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'limit distinct columns from join where group order having compounds')
36
38
 
39
+ # Access doesn't support INTERSECT or EXCEPT
37
40
  def supports_intersect_except?
38
41
  false
39
42
  end
40
43
 
41
44
  private
42
45
 
46
+ # Access uses TOP for limits
47
+ def select_limit_sql(sql)
48
+ sql << " TOP #{@opts[:limit]}" if @opts[:limit]
49
+ end
50
+
51
+ # Access uses [] for quoting identifiers
43
52
  def quoted_identifier(v)
44
53
  "[#{v}]"
45
54
  end
46
55
 
56
+ # Access requires the limit clause come before other clauses
47
57
  def select_clause_methods
48
58
  SELECT_CLAUSE_METHODS
49
59
  end
@@ -0,0 +1,45 @@
1
+ module Sequel
2
+ module Informix
3
+ module DatabaseMethods
4
+ TEMPORARY = 'TEMP '.freeze
5
+
6
+ # Informix uses the :informix database type
7
+ def database_type
8
+ :informix
9
+ end
10
+
11
+ private
12
+
13
+ # Informix has issues with quoted identifiers, so
14
+ # turn off database quoting by default.
15
+ def quote_identifiers_default
16
+ false
17
+ end
18
+
19
+ # SQL fragment for showing a table is temporary
20
+ def temporary_table_sql
21
+ TEMPORARY
22
+ end
23
+ end
24
+
25
+ module DatasetMethods
26
+ SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'limit distinct columns from join where having group compounds order')
27
+
28
+ private
29
+
30
+ # Informix does not support INTERSECT or EXCEPT
31
+ def supports_intersect_except?
32
+ false
33
+ end
34
+
35
+ def select_clause_methods
36
+ SELECT_CLAUSE_METHODS
37
+ end
38
+
39
+ def select_limit_sql(sql)
40
+ sql << " SKIP #{@opts[:offset]}" if @opts[:offset]
41
+ sql << " FIRST #{@opts[:limit]}" if @opts[:limit]
42
+ end
43
+ end
44
+ end
45
+ end
@@ -53,11 +53,13 @@ module Sequel
53
53
  # Microsoft SQL Server supports using the INFORMATION_SCHEMA to get
54
54
  # information on tables.
55
55
  def tables(opts={})
56
- m = output_identifier_meth
57
- metadata_dataset.from(:information_schema__tables___t).
58
- select(:table_name).
59
- filter(:table_type=>'BASE TABLE', :table_schema=>(opts[:schema]||default_schema||'dbo').to_s).
60
- map{|x| m.call(x[:table_name])}
56
+ information_schema_tables('BASE TABLE', opts)
57
+ end
58
+
59
+ # Microsoft SQL Server supports using the INFORMATION_SCHEMA to get
60
+ # information on views.
61
+ def views(opts={})
62
+ information_schema_tables('VIEW', opts)
61
63
  end
62
64
 
63
65
  private
@@ -123,6 +125,15 @@ module Sequel
123
125
  "DROP INDEX #{quote_identifier(op[:name] || default_index_name(table, op[:columns]))} ON #{quote_schema_table(table)}"
124
126
  end
125
127
 
128
+ # Backbone of the tables and views support.
129
+ def information_schema_tables(type, opts)
130
+ m = output_identifier_meth
131
+ metadata_dataset.from(:information_schema__tables___t).
132
+ select(:table_name).
133
+ filter(:table_type=>type, :table_schema=>(opts[:schema]||default_schema||'dbo').to_s).
134
+ map{|x| m.call(x[:table_name])}
135
+ end
136
+
126
137
  # Always quote identifiers in the metadata_dataset, so schema parsing works.
127
138
  def metadata_dataset
128
139
  ds = super
@@ -145,6 +156,16 @@ module Sequel
145
156
  SQL_ROLLBACK
146
157
  end
147
158
 
159
+ # The closest MSSQL equivalent of a boolean datatype is the bit type.
160
+ def schema_column_type(db_type)
161
+ case db_type
162
+ when /\A(bit)\z/io
163
+ :boolean
164
+ else
165
+ super
166
+ end
167
+ end
168
+
148
169
  # MSSQL uses the INFORMATION_SCHEMA to hold column information. This method does
149
170
  # not support the parsing of primary key information.
150
171
  def schema_parse_table(table_name, opts)
@@ -231,6 +252,29 @@ module Sequel
231
252
  @mssql_unicode_strings = db.mssql_unicode_strings
232
253
  end
233
254
 
255
+ # Ugly hack. While MSSQL supports TRUE and FALSE values, you can't
256
+ # actually specify them directly in SQL. Unfortunately, you also cannot
257
+ # use an integer value when a boolean is required. Also unforunately, you
258
+ # cannot use an expression that yields a boolean type in cases where in an
259
+ # integer type is needed, such as inserting into a bit field (the closest thing
260
+ # MSSQL has to a boolean).
261
+ #
262
+ # In filters, SQL::BooleanConstants are used more, while in other places
263
+ # the ruby true/false values are used more, so use expressions that return booleans
264
+ # for SQL::BooleanConstants, and 1/0 for other places.
265
+ # The correct fix for this would require separate literalization paths for
266
+ # filters compared to other values, but that's more work than I want to do right now.
267
+ def boolean_constant_sql(constant)
268
+ case constant
269
+ when true
270
+ '(1 = 1)'
271
+ when false
272
+ '(1 = 0)'
273
+ else
274
+ super
275
+ end
276
+ end
277
+
234
278
  # MSSQL uses + for string concatenation, and LIKE is case insensitive by default.
235
279
  def complex_expression_sql(op, args)
236
280
  case op
@@ -277,8 +321,8 @@ module Sequel
277
321
 
278
322
  # Use the OUTPUT clause to get the value of all columns for the newly inserted record.
279
323
  def insert_select(*values)
280
- return unless supports_output_clause?
281
- naked.clone(default_server_opts(:sql=>output(nil, [:inserted.*]).insert_sql(*values))).single_record unless opts[:disable_insert_output]
324
+ return unless supports_insert_select?
325
+ naked.clone(default_server_opts(:sql=>output(nil, [SQL::ColumnAll.new(:inserted)]).insert_sql(*values))).single_record
282
326
  end
283
327
 
284
328
  # Specify a table for a SELECT ... INTO query.
@@ -367,6 +411,11 @@ module Sequel
367
411
  db.server_version(@opts[:server])
368
412
  end
369
413
 
414
+ # MSSQL supports insert_select via the OUTPUT clause.
415
+ def supports_insert_select?
416
+ supports_output_clause? && !opts[:disable_insert_output]
417
+ end
418
+
370
419
  # MSSQL 2005+ supports INTERSECT and EXCEPT
371
420
  def supports_intersect_except?
372
421
  is_2005_or_later?
@@ -441,6 +490,22 @@ module Sequel
441
490
  alias insert_with_sql delete_with_sql
442
491
  alias update_with_sql delete_with_sql
443
492
 
493
+ # Special case when true or false is provided directly to filter.
494
+ def filter_expr(expr)
495
+ if block_given?
496
+ super
497
+ else
498
+ case expr
499
+ when true
500
+ Sequel::TRUE
501
+ when false
502
+ Sequel::FALSE
503
+ else
504
+ super
505
+ end
506
+ end
507
+ end
508
+
444
509
  # MSSQL raises an error if you try to provide more than 3 decimal places
445
510
  # for a fractional timestamp. This probably doesn't work for smalldatetime
446
511
  # fields.
@@ -454,6 +519,16 @@ module Sequel
454
519
  INSERT_CLAUSE_METHODS
455
520
  end
456
521
 
522
+ # Use OUTPUT INSERTED.* to return all columns of the inserted row,
523
+ # for use with the prepared statement code.
524
+ def insert_output_sql(sql)
525
+ if @opts.has_key?(:returning)
526
+ sql << " OUTPUT INSERTED.*"
527
+ else
528
+ output_sql(sql)
529
+ end
530
+ end
531
+
457
532
  # MSSQL uses a literal hexidecimal number for blob strings
458
533
  def literal_blob(v)
459
534
  blob = '0x'
@@ -529,7 +604,6 @@ module Sequel
529
604
  end
530
605
  alias delete_output_sql output_sql
531
606
  alias update_output_sql output_sql
532
- alias insert_output_sql output_sql
533
607
 
534
608
  # MSSQL supports the OUTPUT clause for UPDATE statements.
535
609
  # It also allows prepending a WITH clause.
@@ -83,13 +83,9 @@ module Sequel
83
83
  @server_version ||= (m[1].to_i * 10000) + (m[2].to_i * 100) + m[3].to_i
84
84
  end
85
85
 
86
- # Return an array of symbols specifying table names in the current database.
87
- #
88
- # Options:
89
- # * :server - Set the server to use
90
- def tables(opts={})
91
- m = output_identifier_meth
92
- metadata_dataset.with_sql('SHOW TABLES').server(opts[:server]).map{|r| m.call(r.values.first)}
86
+ # MySQL supports CREATE TABLE IF NOT EXISTS syntax.
87
+ def supports_create_table_if_not_exists?
88
+ true
93
89
  end
94
90
 
95
91
  # MySQL supports prepared transactions (two-phase commit) using XA
@@ -107,6 +103,14 @@ module Sequel
107
103
  true
108
104
  end
109
105
 
106
+ # Return an array of symbols specifying table names in the current database.
107
+ #
108
+ # Options:
109
+ # * :server - Set the server to use
110
+ def tables(opts={})
111
+ full_tables('BASE TABLE', opts)
112
+ end
113
+
110
114
  # Changes the database in use by issuing a USE statement. I would be
111
115
  # very careful if I used this.
112
116
  def use(db_name)
@@ -116,6 +120,14 @@ module Sequel
116
120
  self
117
121
  end
118
122
 
123
+ # Return an array of symbols specifying view names in the current database.
124
+ #
125
+ # Options:
126
+ # * :server - Set the server to use
127
+ def views(opts={})
128
+ full_tables('VIEW', opts)
129
+ end
130
+
119
131
  private
120
132
 
121
133
  # Use MySQL specific syntax for rename column, set column type, and
@@ -205,6 +217,12 @@ module Sequel
205
217
  "#{super}#{" ENGINE=#{engine}" if engine}#{" DEFAULT CHARSET=#{charset}" if charset}#{" DEFAULT COLLATE=#{collate}" if collate}"
206
218
  end
207
219
 
220
+ # Backbone of the tables and views support using SHOW FULL TABLES.
221
+ def full_tables(type, opts)
222
+ m = output_identifier_meth
223
+ metadata_dataset.with_sql('SHOW FULL TABLES').server(opts[:server]).map{|r| m.call(r.values.first) if r.delete(:Table_type) == type}.compact
224
+ end
225
+
208
226
  # MySQL folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on input.
209
227
  def identifier_input_method_default
210
228
  nil
@@ -387,11 +387,17 @@ module Sequel
387
387
  # Options:
388
388
  # * :schema - The schema to search (default_schema by default)
389
389
  # * :server - The server to use
390
- def tables(opts={})
391
- ds = metadata_dataset.from(:pg_class).filter(:relkind=>'r').select(:relname).exclude(SQL::StringExpression.like(:relname, SYSTEM_TABLE_REGEXP)).server(opts[:server]).join(:pg_namespace, :oid=>:relnamespace)
392
- ds = filter_schema(ds, opts)
393
- m = output_identifier_meth
394
- block_given? ? yield(ds) : ds.map{|r| m.call(r[:relname])}
390
+ def tables(opts={}, &block)
391
+ pg_class_relname('r', opts, &block)
392
+ end
393
+
394
+ # Array of symbols specifying view names in the current database.
395
+ #
396
+ # Options:
397
+ # * :schema - The schema to search (default_schema by default)
398
+ # * :server - The server to use
399
+ def views(opts={})
400
+ pg_class_relname('v', opts)
395
401
  end
396
402
 
397
403
  private
@@ -540,6 +546,14 @@ module Sequel
540
546
  conn.execute(sql)
541
547
  end
542
548
 
549
+ # Backbone of the tables and views support.
550
+ def pg_class_relname(type, opts)
551
+ ds = metadata_dataset.from(:pg_class).filter(:relkind=>type).select(:relname).exclude(SQL::StringExpression.like(:relname, SYSTEM_TABLE_REGEXP)).server(opts[:server]).join(:pg_namespace, :oid=>:relnamespace)
552
+ ds = filter_schema(ds, opts)
553
+ m = output_identifier_meth
554
+ block_given? ? yield(ds) : ds.map{|r| m.call(r[:relname])}
555
+ end
556
+
543
557
  # Use a dollar sign instead of question mark for the argument
544
558
  # placeholder.
545
559
  def prepared_arg_placeholder
@@ -632,6 +646,7 @@ module Sequel
632
646
  EXPLAIN = 'EXPLAIN '.freeze
633
647
  EXPLAIN_ANALYZE = 'EXPLAIN ANALYZE '.freeze
634
648
  FOR_SHARE = ' FOR SHARE'.freeze
649
+ INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'into columns values returning_select')
635
650
  LOCK = 'LOCK TABLE %s IN %s MODE'.freeze
636
651
  NULL = LiteralString.new('NULL').freeze
637
652
  PG_TIMESTAMP_FORMAT = "TIMESTAMP '%Y-%m-%d %H:%M:%S".freeze
@@ -726,7 +741,7 @@ module Sequel
726
741
 
727
742
  # Insert a record returning the record inserted
728
743
  def insert_select(*values)
729
- return if opts[:disable_insert_returning] || server_version < 80200
744
+ return unless supports_insert_select?
730
745
  naked.clone(default_server_opts(:sql=>insert_returning_sql(nil, *values))).single_record
731
746
  end
732
747
 
@@ -757,6 +772,11 @@ module Sequel
757
772
  true
758
773
  end
759
774
 
775
+ # PostgreSQL support insert_select using the RETURNING clause.
776
+ def supports_insert_select?
777
+ server_version >= 80200 && !opts[:disable_insert_returning]
778
+ end
779
+
760
780
  # PostgreSQL supports modifying joined datasets
761
781
  def supports_modifying_joins?
762
782
  true
@@ -794,11 +814,24 @@ module Sequel
794
814
  join_from_sql(:USING, sql)
795
815
  end
796
816
 
817
+ # PostgreSQL allows a RETURNING clause.
818
+ def insert_clause_methods
819
+ INSERT_CLAUSE_METHODS
820
+ end
821
+
797
822
  # Use the RETURNING clause to return the primary key of the inserted record, if it exists
798
823
  def insert_returning_pk_sql(*values)
799
824
  pk = db.primary_key(opts[:from].first) if opts[:from] && !opts[:from].empty?
800
825
  insert_returning_sql(pk ? Sequel::SQL::Identifier.new(pk) : NULL, *values)
801
826
  end
827
+
828
+ # Add a RETURNING clause if it is set and the database supports it and
829
+ # this dataset hasn't disabled it.
830
+ def insert_returning_select_sql(sql)
831
+ if supports_insert_select? && opts.has_key?(:returning)
832
+ sql << " RETURNING #{column_list(Array(opts[:returning]))}"
833
+ end
834
+ end
802
835
 
803
836
  # For multiple table support, PostgreSQL requires at least
804
837
  # two from tables, with joins allowed.
@@ -7,8 +7,9 @@ module Sequel
7
7
  AUTO_VACUUM = [:none, :full, :incremental].freeze
8
8
  PRIMARY_KEY_INDEX_RE = /\Asqlite_autoindex_/.freeze
9
9
  SYNCHRONOUS = [:off, :normal, :full].freeze
10
- TABLES_FILTER = "type = 'table' AND NOT name = 'sqlite_sequence'"
10
+ TABLES_FILTER = "type = 'table' AND NOT name = 'sqlite_sequence'".freeze
11
11
  TEMP_STORE = [:default, :file, :memory].freeze
12
+ VIEWS_FILTER = "type = 'view'".freeze
12
13
 
13
14
  # Run all alter_table commands in a transaction. This is technically only
14
15
  # needed for drop column.
@@ -96,6 +97,11 @@ module Sequel
96
97
  end
97
98
  end
98
99
 
100
+ # SQLite supports CREATE TABLE IF NOT EXISTS syntax since 3.3.0.
101
+ def supports_create_table_if_not_exists?
102
+ sqlite_version >= 30300
103
+ end
104
+
99
105
  # SQLite 3.6.8+ supports savepoints.
100
106
  def supports_savepoints?
101
107
  sqlite_version >= 30608
@@ -118,8 +124,7 @@ module Sequel
118
124
  # Options:
119
125
  # * :server - Set the server to use.
120
126
  def tables(opts={})
121
- m = output_identifier_meth
122
- metadata_dataset.from(:sqlite_master).server(opts[:server]).filter(TABLES_FILTER).map{|r| m.call(r[:name])}
127
+ tables_and_views(TABLES_FILTER, opts)
123
128
  end
124
129
 
125
130
  # A symbol signifying the value of the temp_store PRAGMA.
@@ -134,6 +139,14 @@ module Sequel
134
139
  pragma_set(:temp_store, value)
135
140
  end
136
141
 
142
+ # Array of symbols specifying the view names in the current database.
143
+ #
144
+ # Options:
145
+ # * :server - Set the server to use.
146
+ def views(opts={})
147
+ tables_and_views(VIEWS_FILTER, opts)
148
+ end
149
+
137
150
  private
138
151
 
139
152
  # SQLite supports limited table modification. You can add a column
@@ -306,6 +319,12 @@ module Sequel
306
319
  end
307
320
  end
308
321
 
322
+ # Backbone of the tables and views support.
323
+ def tables_and_views(filter, opts)
324
+ m = output_identifier_meth
325
+ metadata_dataset.from(:sqlite_master).server(opts[:server]).filter(filter).map{|r| m.call(r[:name])}
326
+ end
327
+
309
328
  # SQLite uses the integer data type even for bignums. This is because they
310
329
  # are both stored internally as text, and converted when returned from
311
330
  # the database. Using an integer type instead of bigint makes it more likely
@@ -320,7 +339,24 @@ module Sequel
320
339
  SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'distinct columns from join where group having compounds order limit')
321
340
  COMMA_SEPARATOR = ', '.freeze
322
341
  CONSTANT_MAP = {:CURRENT_DATE=>"date(CURRENT_TIMESTAMP, 'localtime')".freeze, :CURRENT_TIMESTAMP=>"datetime(CURRENT_TIMESTAMP, 'localtime')".freeze, :CURRENT_TIME=>"time(CURRENT_TIMESTAMP, 'localtime')".freeze}
323
-
342
+
343
+ # Ugly hack. Really, SQLite uses 0 for false and 1 for true
344
+ # but then you can't differentiate between integers and booleans.
345
+ # In filters, SQL::BooleanConstants are used more, while in other places
346
+ # the ruby true/false values are used more, so use 1/0 for SQL::BooleanConstants.
347
+ # The correct fix for this would require separate literalization paths for
348
+ # filters compared to other values, but that's more work than I want to do right now.
349
+ def boolean_constant_sql(constant)
350
+ case constant
351
+ when true
352
+ '1'
353
+ when false
354
+ '0'
355
+ else
356
+ super
357
+ end
358
+ end
359
+
324
360
  # SQLite does not support pattern matching via regular expressions.
325
361
  # SQLite is case insensitive (depending on pragma), so use LIKE for
326
362
  # ILIKE.
@@ -395,6 +431,22 @@ module Sequel
395
431
  "#{expression} AS #{literal(aliaz.to_s)}"
396
432
  end
397
433
 
434
+ # Special case when true or false is provided directly to filter.
435
+ def filter_expr(expr)
436
+ if block_given?
437
+ super
438
+ else
439
+ case expr
440
+ when true
441
+ 1
442
+ when false
443
+ 0
444
+ else
445
+ super
446
+ end
447
+ end
448
+ end
449
+
398
450
  # SQL fragment specifying a list of identifiers
399
451
  def identifier_list(columns)
400
452
  columns.map{|i| quote_identifier(i)}.join(COMMA_SEPARATOR)
@@ -406,7 +458,7 @@ module Sequel
406
458
  v.each_byte{|x| blob << sprintf('%02x', x)}
407
459
  "X'#{blob}'"
408
460
  end
409
-
461
+
410
462
  # SQLite does not support the SQL WITH clause
411
463
  def select_clause_methods
412
464
  SELECT_CLAUSE_METHODS