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.
- data/CHANGELOG +64 -0
- data/doc/association_basics.rdoc +43 -5
- data/doc/model_hooks.rdoc +64 -27
- data/doc/prepared_statements.rdoc +8 -4
- data/doc/reflection.rdoc +8 -2
- data/doc/release_notes/3.23.0.txt +1 -1
- data/doc/release_notes/3.24.0.txt +420 -0
- data/lib/sequel/adapters/db2.rb +8 -1
- data/lib/sequel/adapters/firebird.rb +25 -9
- data/lib/sequel/adapters/informix.rb +4 -19
- data/lib/sequel/adapters/jdbc.rb +34 -17
- data/lib/sequel/adapters/jdbc/h2.rb +5 -0
- data/lib/sequel/adapters/jdbc/informix.rb +31 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
- data/lib/sequel/adapters/jdbc/mysql.rb +9 -0
- data/lib/sequel/adapters/jdbc/sqlserver.rb +46 -0
- data/lib/sequel/adapters/postgres.rb +30 -1
- data/lib/sequel/adapters/shared/access.rb +10 -0
- data/lib/sequel/adapters/shared/informix.rb +45 -0
- data/lib/sequel/adapters/shared/mssql.rb +82 -8
- data/lib/sequel/adapters/shared/mysql.rb +25 -7
- data/lib/sequel/adapters/shared/postgres.rb +39 -6
- data/lib/sequel/adapters/shared/sqlite.rb +57 -5
- data/lib/sequel/adapters/sqlite.rb +8 -3
- data/lib/sequel/adapters/swift/mysql.rb +9 -0
- data/lib/sequel/ast_transformer.rb +190 -0
- data/lib/sequel/core.rb +1 -1
- data/lib/sequel/database/misc.rb +6 -0
- data/lib/sequel/database/query.rb +33 -3
- data/lib/sequel/database/schema_methods.rb +6 -2
- data/lib/sequel/dataset/features.rb +6 -0
- data/lib/sequel/dataset/prepared_statements.rb +17 -2
- data/lib/sequel/dataset/query.rb +17 -0
- data/lib/sequel/dataset/sql.rb +2 -53
- data/lib/sequel/exceptions.rb +4 -0
- data/lib/sequel/extensions/to_dot.rb +95 -83
- data/lib/sequel/model.rb +5 -0
- data/lib/sequel/model/associations.rb +80 -14
- data/lib/sequel/model/base.rb +182 -55
- data/lib/sequel/model/exceptions.rb +3 -1
- data/lib/sequel/plugins/association_pks.rb +6 -4
- data/lib/sequel/plugins/defaults_setter.rb +58 -0
- data/lib/sequel/plugins/many_through_many.rb +8 -3
- data/lib/sequel/plugins/prepared_statements.rb +140 -0
- data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
- data/lib/sequel/plugins/prepared_statements_safe.rb +72 -0
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
- data/lib/sequel/sql.rb +8 -0
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +43 -18
- data/spec/core/connection_pool_spec.rb +56 -77
- data/spec/core/database_spec.rb +25 -0
- data/spec/core/dataset_spec.rb +127 -16
- data/spec/core/expression_filters_spec.rb +13 -0
- data/spec/core/schema_spec.rb +6 -1
- data/spec/extensions/association_pks_spec.rb +7 -0
- data/spec/extensions/defaults_setter_spec.rb +64 -0
- data/spec/extensions/many_through_many_spec.rb +60 -4
- data/spec/extensions/nested_attributes_spec.rb +1 -0
- data/spec/extensions/prepared_statements_associations_spec.rb +126 -0
- data/spec/extensions/prepared_statements_safe_spec.rb +69 -0
- data/spec/extensions/prepared_statements_spec.rb +72 -0
- data/spec/extensions/prepared_statements_with_pk_spec.rb +38 -0
- data/spec/extensions/to_dot_spec.rb +3 -5
- data/spec/integration/associations_test.rb +155 -1
- data/spec/integration/dataset_test.rb +8 -1
- data/spec/integration/plugin_test.rb +119 -0
- data/spec/integration/prepared_statement_test.rb +72 -1
- data/spec/integration/schema_test.rb +66 -8
- data/spec/integration/transaction_test.rb +40 -0
- data/spec/model/associations_spec.rb +349 -8
- data/spec/model/base_spec.rb +59 -0
- data/spec/model/hooks_spec.rb +161 -0
- data/spec/model/record_spec.rb +24 -0
- 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
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
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
|
281
|
-
naked.clone(default_server_opts(:sql=>output(nil, [:inserted
|
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
|
-
#
|
87
|
-
|
88
|
-
|
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
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
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
|
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
|
-
|
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
|