sequel 3.45.0 → 3.46.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG +34 -0
- data/README.rdoc +6 -0
- data/Rakefile +46 -33
- data/doc/release_notes/3.46.0.txt +122 -0
- data/doc/schema_modification.rdoc +42 -6
- data/doc/security.rdoc +379 -0
- data/doc/transactions.rdoc +1 -1
- data/lib/sequel/adapters/jdbc/as400.rb +1 -0
- data/lib/sequel/adapters/jdbc/h2.rb +11 -0
- data/lib/sequel/adapters/mysql2.rb +3 -9
- data/lib/sequel/adapters/postgres.rb +34 -2
- data/lib/sequel/adapters/shared/cubrid.rb +5 -0
- data/lib/sequel/adapters/shared/mssql.rb +27 -3
- data/lib/sequel/adapters/shared/mysql.rb +25 -4
- data/lib/sequel/adapters/shared/sqlite.rb +12 -1
- data/lib/sequel/connection_pool.rb +3 -3
- data/lib/sequel/connection_pool/sharded_threaded.rb +7 -8
- data/lib/sequel/connection_pool/threaded.rb +7 -8
- data/lib/sequel/core.rb +5 -2
- data/lib/sequel/database.rb +1 -1
- data/lib/sequel/database/connecting.rb +7 -7
- data/lib/sequel/database/features.rb +88 -0
- data/lib/sequel/database/misc.rb +14 -64
- data/lib/sequel/database/query.rb +0 -332
- data/lib/sequel/database/schema_generator.rb +36 -3
- data/lib/sequel/database/schema_methods.rb +48 -12
- data/lib/sequel/database/transactions.rb +344 -0
- data/lib/sequel/dataset/actions.rb +24 -9
- data/lib/sequel/dataset/mutation.rb +20 -0
- data/lib/sequel/dataset/query.rb +0 -17
- data/lib/sequel/dataset/sql.rb +7 -0
- data/lib/sequel/exceptions.rb +10 -6
- data/lib/sequel/extensions/_pretty_table.rb +2 -2
- data/lib/sequel/extensions/looser_typecasting.rb +10 -0
- data/lib/sequel/extensions/migration.rb +5 -2
- data/lib/sequel/model.rb +1 -1
- data/lib/sequel/model/associations.rb +16 -14
- data/lib/sequel/model/base.rb +14 -2
- data/lib/sequel/plugins/composition.rb +3 -3
- data/lib/sequel/plugins/dirty.rb +6 -6
- data/lib/sequel/plugins/hook_class_methods.rb +3 -0
- data/lib/sequel/plugins/serialization.rb +7 -17
- data/lib/sequel/plugins/string_stripper.rb +2 -1
- data/lib/sequel/plugins/validation_helpers.rb +1 -1
- data/lib/sequel/sql.rb +3 -0
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +21 -0
- data/spec/adapters/postgres_spec.rb +35 -8
- data/spec/core/database_spec.rb +4 -0
- data/spec/core/dataset_spec.rb +48 -2
- data/spec/core/schema_generator_spec.rb +10 -1
- data/spec/core/schema_spec.rb +69 -0
- data/spec/extensions/composition_spec.rb +21 -2
- data/spec/extensions/dirty_spec.rb +17 -10
- data/spec/extensions/eager_each_spec.rb +4 -1
- data/spec/extensions/looser_typecasting_spec.rb +16 -19
- data/spec/extensions/migration_spec.rb +7 -1
- data/spec/extensions/serialization_spec.rb +22 -0
- data/spec/extensions/single_table_inheritance_spec.rb +3 -2
- data/spec/extensions/validation_helpers_spec.rb +6 -0
- data/spec/integration/dataset_test.rb +5 -0
- data/spec/integration/schema_test.rb +16 -0
- data/spec/model/associations_spec.rb +40 -0
- data/spec/model/base_spec.rb +21 -1
- data/spec/model/record_spec.rb +3 -0
- metadata +14 -10
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f41a65970b01417999c117602accf3a2eb0acb64
|
4
|
+
data.tar.gz: 70f3a0b14da5779276cea3fc752b06d69b95fdee
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ac4bbb6968ce5890ec6e3ba61cafc982d5508fc7aa4cab016f15fb67a237145f7461b82ad2aa6bb1cd5f90bb5327d8c616823a61fccc0d40457886cdccd3dfb6
|
7
|
+
data.tar.gz: f3c47bbc24033387b7cc225c58f73646bf19a477f69963f053eace7e8d8fb02b97fc78043a42d5a9d23d49b7ff1d1363de37fdeef396d177925f6f30face34b4
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,37 @@
|
|
1
|
+
=== 3.46.0 (2013-04-02)
|
2
|
+
|
3
|
+
* Add Dataset#cross_apply and Dataset#outer_apply on Microsoft SQL Server (jeremyevans)
|
4
|
+
|
5
|
+
* Speed up threaded connection pools when :connection_handling=>:queue is used (jeremyevans)
|
6
|
+
|
7
|
+
* Allow external connection pool classes to be loaded automatically (jeremyevans)
|
8
|
+
|
9
|
+
* Add Dataset#with_pk! for model datasets, like #with_pk, but raising instead of returning nil (jeremyevans)
|
10
|
+
|
11
|
+
* Add Dataset#first!, like #first, but raising a Sequel::NoMatchingRow exception instead of returning nil (jeremyevans)
|
12
|
+
|
13
|
+
* Dataset #select_map, #select_order_map, and #get no longer support a plain string inside an array of arguments (jeremyevans)
|
14
|
+
|
15
|
+
* Escape ] characters in identifiers on Microsoft SQL Server (jeremyevans)
|
16
|
+
|
17
|
+
* Add security guide (jeremyevans)
|
18
|
+
|
19
|
+
* Make validates_type handle false values correctly (jeremyevans) (#636)
|
20
|
+
|
21
|
+
* Have associations, composition, serialization, and dirty plugins clear caches in some additional cases (jeremyevans) (#635)
|
22
|
+
|
23
|
+
* Add alter_table drop_foreign_key method for dropping foreign keys by column names (raxoft, jeremyevans) (#627)
|
24
|
+
|
25
|
+
* Allow creation named column constraints via :*_constraint_name column options (jeremyevans)
|
26
|
+
|
27
|
+
* Handle drop_constraint :type=>:primary_key on H2 (jeremyevans)
|
28
|
+
|
29
|
+
* Handle infinite dates in the postgres adapter using Database#convert_infinite_timestamps (jeremyevans)
|
30
|
+
|
31
|
+
* Make the looser_typecasting extension use looser typecasting for decimal columns as well as integers and floats (jeremyevans)
|
32
|
+
|
33
|
+
* Do strict typecasting of decimal columns by default, similar to integer/float typecasting (jeremyevans)
|
34
|
+
|
1
35
|
=== 3.45.0 (2013-03-01)
|
2
36
|
|
3
37
|
* Remove bad model typecasting of money type on PostgreSQL (jeremyevans) (#624)
|
data/README.rdoc
CHANGED
@@ -244,6 +244,12 @@ After filtering, you can retrieve the matching records by using any of the retri
|
|
244
244
|
|
245
245
|
See the {Dataset Filtering}[link:files/doc/dataset_filtering_rdoc.html] file for more details.
|
246
246
|
|
247
|
+
=== Security
|
248
|
+
|
249
|
+
Designing apps with security in mind is a best practice.
|
250
|
+
Please read the {Security Guide}[link:files/doc/security_rdoc.html] for details on security
|
251
|
+
issues that you should be aware of when using Sequel.
|
252
|
+
|
247
253
|
=== Summarizing Records
|
248
254
|
|
249
255
|
Counting records is easy using +count+:
|
data/Rakefile
CHANGED
@@ -11,25 +11,24 @@ SUDO = ENV['SUDO'] || 'sudo'
|
|
11
11
|
|
12
12
|
# Gem Packaging and Release
|
13
13
|
|
14
|
-
desc "
|
14
|
+
desc "Build sequel gem"
|
15
15
|
task :package=>[:clean] do |p|
|
16
|
-
|
17
|
-
Gem::Builder.new(SEQUEL_GEMSPEC).build
|
16
|
+
sh %{#{FileUtils::RUBY} -S gem build sequel.gemspec}
|
18
17
|
end
|
19
18
|
|
20
19
|
desc "Install sequel gem"
|
21
20
|
task :install=>[:package] do
|
22
|
-
sh %{#{SUDO} gem install ./#{NAME}-#{VERS.call} --local}
|
21
|
+
sh %{#{SUDO} #{FileUtils::RUBY} -S gem install ./#{NAME}-#{VERS.call} --local}
|
23
22
|
end
|
24
23
|
|
25
24
|
desc "Uninstall sequel gem"
|
26
25
|
task :uninstall=>[:clean] do
|
27
|
-
sh %{#{SUDO} gem uninstall #{NAME}}
|
26
|
+
sh %{#{SUDO} #{FileUtils::RUBY} -S gem uninstall #{NAME}}
|
28
27
|
end
|
29
28
|
|
30
|
-
desc "
|
29
|
+
desc "Publish sequel gem to rubygems.org"
|
31
30
|
task :release=>[:package] do
|
32
|
-
sh %{gem push ./#{NAME}-#{VERS.call}.gem}
|
31
|
+
sh %{#{FileUtils::RUBY} -S gem push ./#{NAME}-#{VERS.call}.gem}
|
33
32
|
end
|
34
33
|
|
35
34
|
### Website
|
@@ -46,11 +45,23 @@ end
|
|
46
45
|
|
47
46
|
### RDoc
|
48
47
|
|
49
|
-
RDOC_DEFAULT_OPTS = ["--
|
48
|
+
RDOC_DEFAULT_OPTS = ["--line-numbers", "--inline-source", '--title', 'Sequel: The Database Toolkit for Ruby']
|
49
|
+
|
50
|
+
allow_website_rdoc = begin
|
51
|
+
# Sequel uses hanna-nouveau for the website RDoc.
|
52
|
+
# Due to bugs in older versions of RDoc, and the
|
53
|
+
# fact that hanna-nouveau does not support RDoc 4,
|
54
|
+
# a specific version of rdoc is required.
|
55
|
+
gem 'rdoc', '= 3.12.2'
|
56
|
+
gem 'hanna-nouveau'
|
57
|
+
RDOC_DEFAULT_OPTS.concat(['-f', 'hanna'])
|
58
|
+
true
|
59
|
+
rescue Gem::LoadError
|
60
|
+
false
|
61
|
+
end
|
50
62
|
|
51
63
|
rdoc_task_class = begin
|
52
64
|
require "rdoc/task"
|
53
|
-
RDOC_DEFAULT_OPTS.concat(['-f', 'hanna'])
|
54
65
|
RDoc::Task
|
55
66
|
rescue LoadError
|
56
67
|
begin
|
@@ -67,32 +78,34 @@ if rdoc_task_class
|
|
67
78
|
rdoc.rdoc_dir = "rdoc"
|
68
79
|
rdoc.options += RDOC_OPTS
|
69
80
|
rdoc.rdoc_files.add %w"README.rdoc CHANGELOG MIT-LICENSE lib/**/*.rb doc/*.rdoc doc/release_notes/*.txt"
|
70
|
-
end if rdoc_task_class
|
71
|
-
|
72
|
-
desc "Make rdoc for website"
|
73
|
-
task :website_rdoc=>[:website_rdoc_main, :website_rdoc_adapters, :website_rdoc_plugins]
|
74
|
-
|
75
|
-
rdoc_task_class.new(:website_rdoc_main) do |rdoc|
|
76
|
-
rdoc.rdoc_dir = "www/public/rdoc"
|
77
|
-
rdoc.options += RDOC_OPTS
|
78
|
-
rdoc.rdoc_files.add %w"README.rdoc CHANGELOG MIT-LICENSE lib/*.rb lib/sequel/*.rb lib/sequel/{connection_pool,dataset,database,model}/*.rb doc/*.rdoc doc/release_notes/*.txt lib/sequel/extensions/migration.rb"
|
79
81
|
end
|
80
82
|
|
81
|
-
|
82
|
-
rdoc
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
83
|
+
if allow_website_rdoc
|
84
|
+
desc "Make rdoc for website"
|
85
|
+
task :website_rdoc=>[:website_rdoc_main, :website_rdoc_adapters, :website_rdoc_plugins]
|
86
|
+
|
87
|
+
rdoc_task_class.new(:website_rdoc_main) do |rdoc|
|
88
|
+
rdoc.rdoc_dir = "www/public/rdoc"
|
89
|
+
rdoc.options += RDOC_OPTS + %w'--no-ignore-invalid'
|
90
|
+
rdoc.rdoc_files.add %w"README.rdoc CHANGELOG MIT-LICENSE lib/*.rb lib/sequel/*.rb lib/sequel/{connection_pool,dataset,database,model}/*.rb doc/*.rdoc doc/release_notes/*.txt lib/sequel/extensions/migration.rb lib/sequel/extensions/core_extensions.rb"
|
91
|
+
end
|
92
|
+
|
93
|
+
rdoc_task_class.new(:website_rdoc_adapters) do |rdoc|
|
94
|
+
rdoc.rdoc_dir = "www/public/rdoc-adapters"
|
95
|
+
rdoc.options += RDOC_DEFAULT_OPTS + %w'--main Sequel --no-ignore-invalid'
|
96
|
+
rdoc.rdoc_files.add %w"lib/sequel/adapters/**/*.rb"
|
97
|
+
end
|
98
|
+
|
99
|
+
rdoc_task_class.new(:website_rdoc_plugins) do |rdoc|
|
100
|
+
rdoc.rdoc_dir = "www/public/rdoc-plugins"
|
101
|
+
rdoc.options += RDOC_DEFAULT_OPTS + %w'--main Sequel --no-ignore-invalid'
|
102
|
+
rdoc.rdoc_files.add %w"lib/sequel/{extensions,plugins}/**/*.rb"
|
103
|
+
end
|
104
|
+
|
105
|
+
desc "Update sequel.rubyforge.org"
|
106
|
+
task :website_rf=>[:website, :website_rdoc] do
|
107
|
+
sh %{rsync -rvt www/public/* rubyforge.org:/var/www/gforge-projects/sequel/}
|
108
|
+
end
|
96
109
|
end
|
97
110
|
end
|
98
111
|
|
@@ -0,0 +1,122 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* Dataset#first! has been added. This is identical to #first,
|
4
|
+
except where #first would return nil due to no row matching,
|
5
|
+
#first! raises a Sequel::NoMatchingRow exception. The main
|
6
|
+
benefit here is that a standard exception class is now used,
|
7
|
+
so external libraries can deal with these exceptions appropriately
|
8
|
+
(such as web applications returning a 404 error).
|
9
|
+
|
10
|
+
* Dataset#with_pk! has been added to model datasets. Similar to
|
11
|
+
#first!, this raises a Sequel::NoMatchingRow exception instead of
|
12
|
+
returning nil if there is no matching row.
|
13
|
+
|
14
|
+
* A drop_foreign_key method has been added to the alter_table
|
15
|
+
generator:
|
16
|
+
|
17
|
+
alter_table(:tab){drop_foreign_key :col}
|
18
|
+
|
19
|
+
This relies on foreign_key_list working and including the name
|
20
|
+
of the foreign key. Previously, you'd have to drop the foreign key
|
21
|
+
constraint before dropping the column in some cases.
|
22
|
+
|
23
|
+
* Column constraints can now be named using :*_constraint_name
|
24
|
+
options:
|
25
|
+
|
26
|
+
create_table(:tab) do
|
27
|
+
primary_key :id, :primary_key_constraint_name=>:pk_name
|
28
|
+
foriegn_key :t_id, :t, :foreign_key_constraint_name=>:fk_name,
|
29
|
+
:unique=>true, :unique_constraint_name=>:uk_name
|
30
|
+
end
|
31
|
+
|
32
|
+
This makes it easier to name constraints, which has always been
|
33
|
+
recommended as it makes it easier to drop such constraints in the
|
34
|
+
future.
|
35
|
+
|
36
|
+
* On Microsoft SQL Server, Dataset#cross_apply and #outer_apply have
|
37
|
+
been added to use CROSS/OUTER APPLY. These are useful if you
|
38
|
+
want to join a table to the output of a function that takes the
|
39
|
+
table as an argument.
|
40
|
+
|
41
|
+
= Other Improvements
|
42
|
+
|
43
|
+
* The connection pools are now faster when using the
|
44
|
+
:connection_handling=>:queue option.
|
45
|
+
|
46
|
+
* External connection pool classes can now be loaded automatically by
|
47
|
+
the :pool_class option.
|
48
|
+
|
49
|
+
* Database#each_server now raises if not given a block. Previously,
|
50
|
+
it just leaked Database references.
|
51
|
+
|
52
|
+
* On Microsoft SQL Server, ] characters are now escaped correctly in
|
53
|
+
identifiers.
|
54
|
+
|
55
|
+
* On PostgreSQL, infinite dates are also handled when using
|
56
|
+
Database#convert_infinite_timestamps. Previously, infinite dates
|
57
|
+
were incorrectly converted to 0000-01-01.
|
58
|
+
|
59
|
+
* The associations, composition, serialization, and dirty plugins
|
60
|
+
now clear caches stored in the instance in some additional cases,
|
61
|
+
such as when saving model instances when the dataset supports
|
62
|
+
insert_select.
|
63
|
+
|
64
|
+
* Model#validates_type in the validation_helpers plugin now handles
|
65
|
+
false values correctly.
|
66
|
+
|
67
|
+
* The string_stripper plugin has been fixed to not change the result
|
68
|
+
of Model.set_dataset.
|
69
|
+
|
70
|
+
* You can now drop primary key constraints on H2, using:
|
71
|
+
|
72
|
+
alter_table(:tab){drop_constraint :foo, :type=>:primary_key}
|
73
|
+
|
74
|
+
* The jdbc/as400 adapter has been fixed, it was broken starting in
|
75
|
+
Sequel 3.44.0.
|
76
|
+
|
77
|
+
* A Security guide has been added explaining various security issues
|
78
|
+
to think about when using Sequel.
|
79
|
+
|
80
|
+
= Backwards Compatibility
|
81
|
+
|
82
|
+
* The change to make associations, composition, serialization, and
|
83
|
+
dirty now clear caches after saving when the dataset supports
|
84
|
+
insert_select can break code that expected the previous behavior.
|
85
|
+
For example:
|
86
|
+
|
87
|
+
artist = Artist[1]
|
88
|
+
artist.has_albums # => false
|
89
|
+
|
90
|
+
album = Album.new(:artist=>artist)
|
91
|
+
def album.after_create
|
92
|
+
super
|
93
|
+
artist.update(:has_albums=>true)
|
94
|
+
end
|
95
|
+
album.save
|
96
|
+
|
97
|
+
artist.has_albums # => false
|
98
|
+
|
99
|
+
Such code should either refresh the artist after saving the album,
|
100
|
+
or use album.artist.has_albums. You already had to do that if
|
101
|
+
the dataset did not support insert_select; the impetus for this
|
102
|
+
change was to make the behavior consistent.
|
103
|
+
|
104
|
+
* Decimal/numeric columns are now strictly typecast by default,
|
105
|
+
similar to integer and real/double precision columns. If you want
|
106
|
+
the previous loose typecasting to for decimal/numeric columns,
|
107
|
+
use the looser_typecasting extension.
|
108
|
+
|
109
|
+
* External adapters that called Database.set_adapter_scheme with a
|
110
|
+
string should change to using a symbol.
|
111
|
+
|
112
|
+
* Dataset#select_map, #select_order_map, and #get now raise an
|
113
|
+
exception if they are passed a plain string inside an array.
|
114
|
+
If you do want to use a plain string, you now need to alias it:
|
115
|
+
|
116
|
+
dataset.get([Sequel.as('string', :some_alias)])
|
117
|
+
|
118
|
+
= Sequel 4 Implementation Planning
|
119
|
+
|
120
|
+
* Sequel 4 implementation planning has begun. If you want to view
|
121
|
+
and/or provide feedback on the implementation plan, see
|
122
|
+
https://github.com/jeremyevans/sequel-4-plans
|
@@ -85,8 +85,12 @@ method, the fourth argument is the options hash. The following options are supp
|
|
85
85
|
:null :: Mark the column as allowing NULL values (if true),
|
86
86
|
or not allowing NULL values (if false). If unspecified, will default
|
87
87
|
to whatever the database default is.
|
88
|
+
:primary_key :: Mark this column as the primary key. This is used instead of the
|
89
|
+
primary key method if you want a non-autoincrementing primary key.
|
90
|
+
:primary_key_constraint_name :: The name to give the primary key constraint.
|
88
91
|
:unique :: Mark the column as unique, generally has the same effect as
|
89
92
|
creating a unique index on the column.
|
93
|
+
:unique_constraint_name :: The name to give the unique key constraint.
|
90
94
|
|
91
95
|
=== Other methods
|
92
96
|
|
@@ -110,12 +114,13 @@ method:
|
|
110
114
|
create_table(:a2){String :name, :primary_key=>true} # varchar(255) primary key
|
111
115
|
|
112
116
|
If you want to create a composite primary key, you should call the +primary_key+ method with an
|
113
|
-
array of column symbols
|
117
|
+
array of column symbols. You can provide a specific name to use for the primary key constraint
|
118
|
+
via the :name option:
|
114
119
|
|
115
120
|
create_table(:items) do
|
116
121
|
Integer :group_id
|
117
122
|
Integer :position
|
118
|
-
primary_key [:group_id, :position]
|
123
|
+
primary_key [:group_id, :position], :name=>:items_pk
|
119
124
|
end
|
120
125
|
|
121
126
|
If provided with an array, +primary_key+ does not create a column, it just sets up the primary key constraint.
|
@@ -136,6 +141,7 @@ as it's third argument. A simple example is:
|
|
136
141
|
|
137
142
|
:deferrable :: Makes the foreign key constraint checks deferrable, so they aren't checked
|
138
143
|
until the end of the transaction.
|
144
|
+
:foreign_key_constraint_name :: The name to give the foreign key constraint.
|
139
145
|
:key :: For foreign key columns, the column in the associated table
|
140
146
|
that this column references. Unnecessary if this column
|
141
147
|
references the primary key of the associated table, at least
|
@@ -271,7 +277,7 @@ to <tt>Dataset#where</tt>:
|
|
271
277
|
==== +check+
|
272
278
|
|
273
279
|
+check+ operates just like +constraint+, except that it doesn't take a name
|
274
|
-
and it creates an unnamed constraint
|
280
|
+
and it creates an unnamed constraint:
|
275
281
|
|
276
282
|
create_table(:artists) do
|
277
283
|
primary_key :id
|
@@ -279,6 +285,9 @@ and it creates an unnamed constraint
|
|
279
285
|
check{char_length(name) > 2}
|
280
286
|
end
|
281
287
|
|
288
|
+
It's recommended that you use the +constraint+ method and provide a name for the
|
289
|
+
constraint, as that makes it easier to drop the constraint later if necessary.
|
290
|
+
|
282
291
|
== +create_join_table+
|
283
292
|
|
284
293
|
+create_join_table+ is a shortcut that you can use to create simple many-to-many join tables:
|
@@ -371,19 +380,46 @@ creates a new column:
|
|
371
380
|
end
|
372
381
|
|
373
382
|
If you want to add a new foreign key constraint to an existing column, you provide an
|
374
|
-
array with a single element
|
383
|
+
array with a single element. It's encouraged to provide a name when adding the constraint,
|
384
|
+
via the :name option:
|
375
385
|
|
376
386
|
alter_table(:albums) do
|
377
|
-
add_foreign_key [:artist_id], :artists
|
387
|
+
add_foreign_key [:artist_id], :artists, :name=>:albums_artist_id_fkey
|
378
388
|
end
|
379
389
|
|
380
390
|
To set up a multiple column foreign key constraint, use an array with multiple column
|
381
391
|
symbols:
|
382
392
|
|
383
393
|
alter_table(:albums) do
|
384
|
-
add_foreign_key [:artist_name, :artist_location], :artists
|
394
|
+
add_foreign_key [:artist_name, :artist_location], :artists, :name=>:albums_artist_name_location_fkey
|
395
|
+
end
|
396
|
+
|
397
|
+
=== +drop_foreign_key+
|
398
|
+
|
399
|
+
+drop_foreign_key+ is used to drop foreign keys from tables. If you provide a symbol as
|
400
|
+
the first argument, it drops both the foreign key constraint and the column:
|
401
|
+
|
402
|
+
alter_table(:albums) do
|
403
|
+
drop_foreign_key :artist_id
|
385
404
|
end
|
386
405
|
|
406
|
+
If you want to just drop the foreign key constraint without dropping the column, use
|
407
|
+
an array. It's encouraged to use the :name option to provide the constraint name to
|
408
|
+
drop, though on some databases Sequel may be able to find the name through introspection:
|
409
|
+
|
410
|
+
alter_table(:albums) do
|
411
|
+
drop_foreign_key [:artist_id], :name=>:albums_artist_id_fkey
|
412
|
+
end
|
413
|
+
|
414
|
+
An array is also used to drop a composite foreign key constraint:
|
415
|
+
|
416
|
+
alter_table(:albums) do
|
417
|
+
drop_foreign_key [:artist_name, :artist_location], :name=>:albums_artist_name_location_fkey
|
418
|
+
end
|
419
|
+
|
420
|
+
If you do not provide a :name option and Sequel is not able to determine the name
|
421
|
+
to use, it will probably raise a Sequel::Error exception.
|
422
|
+
|
387
423
|
=== +add_index+
|
388
424
|
|
389
425
|
+add_index+ works just like +create_table+'s +index+ method, creating a new index on
|
data/doc/security.rdoc
ADDED
@@ -0,0 +1,379 @@
|
|
1
|
+
= Security Considerations with Sequel
|
2
|
+
|
3
|
+
When using Sequel, there are some security areas you should be aware of:
|
4
|
+
|
5
|
+
* Code Execution
|
6
|
+
* SQL Injection
|
7
|
+
* Denial of Service
|
8
|
+
* Mass Assignment
|
9
|
+
* General Parameter Handling
|
10
|
+
|
11
|
+
== Code Execution
|
12
|
+
|
13
|
+
The most serious security vulnerability you can have in any library is
|
14
|
+
a code execution vulnerability. Sequel should not be vulnerable to this,
|
15
|
+
as it never calls eval on a string that is derived from user input.
|
16
|
+
However, some Sequel methods used for creating methods via metaprogramming
|
17
|
+
could conceivably be abused to do so:
|
18
|
+
|
19
|
+
* Sequel::Schema::CreateTableGenerator.add_type_method
|
20
|
+
* Sequel::Dataset.def_mutation_method
|
21
|
+
* Sequel::Dataset.def_append_methods
|
22
|
+
* Sequel.def_adapter_method (private)
|
23
|
+
* Sequel::Model::InstanceMethods.class_attr_overridable (private)
|
24
|
+
* Sequel::Model::InstanceMethods.class_attr_reader (private)
|
25
|
+
* Sequel::SQL::Expression.to_s_method (private)
|
26
|
+
* Sequel::Plugins::HookClassMethods::ClassMethods#add_hook_type
|
27
|
+
|
28
|
+
As long as you don't call those with user input, you should not be
|
29
|
+
vulnerable to code execution.
|
30
|
+
|
31
|
+
== SQL Injection
|
32
|
+
|
33
|
+
The primary security concern in SQL database libraries is SQL injection.
|
34
|
+
Because Sequel mostly promotes using ruby objects for SQL concepts instead
|
35
|
+
of raw SQL, it is less likely to be vulnerable to SQL injection.
|
36
|
+
However, because Sequel still makes it easy to use raw SQL, misuse of the
|
37
|
+
library can result in SQL injection in your application.
|
38
|
+
|
39
|
+
There are basically two kinds of possible SQL injections in Sequel:
|
40
|
+
|
41
|
+
* SQL code injections
|
42
|
+
* SQL identifier injections
|
43
|
+
|
44
|
+
=== SQL Code Injections
|
45
|
+
|
46
|
+
==== Full SQL Strings
|
47
|
+
|
48
|
+
Some Sequel methods are designed to execute raw SQL, including:
|
49
|
+
|
50
|
+
* Sequel::Database#execute
|
51
|
+
* Sequel::Database#run
|
52
|
+
* Sequel::Database#<<
|
53
|
+
* Sequel::Database#[]
|
54
|
+
* Sequel::Database#fetch
|
55
|
+
* Sequel::Dataset#with_sql
|
56
|
+
|
57
|
+
Here are some examples of use:
|
58
|
+
|
59
|
+
DB.run 'SQL'
|
60
|
+
DB << 'SQL'
|
61
|
+
DB.execute 'SQL'
|
62
|
+
DB['SQL'].all
|
63
|
+
DB.fetch('SQL').all
|
64
|
+
DB.dataset.with_sql('SQL').all
|
65
|
+
|
66
|
+
If you pass a string to these methods that is derived from user input, you open
|
67
|
+
yourself up to SQL injection. The Sequel::Database#run, Sequel::Database#<<, and
|
68
|
+
Sequel::Database#execute methods are not designed to work at all with user input.
|
69
|
+
If you must use them with user input, you should escape the user input manually
|
70
|
+
via Sequel::Database#literal. Example:
|
71
|
+
|
72
|
+
DB.run "SOME SQL #{DB.literal(params[:user].to_s)}"
|
73
|
+
|
74
|
+
With Sequel::Database#[], Sequel::Database#fetch and Sequel::Dataset#with_sql, you should use placeholders,
|
75
|
+
in which case Sequel automatically literalizes the input:
|
76
|
+
|
77
|
+
DB['SELECT * FROM foo WHERE bar = ?', params[:user].to_s]
|
78
|
+
|
79
|
+
==== Manually Created Literal Strings
|
80
|
+
|
81
|
+
Sequel generally treats ruby strings as SQL strings (escaping them correctly), and
|
82
|
+
not as raw SQL. However, you can convert a ruby string to a literal string, and
|
83
|
+
Sequel will then treat it as raw SQL. This is typically done through String#lit
|
84
|
+
if the {core_extensions}[link:files/doc/core_extensions_rdoc.html] are in use,
|
85
|
+
or Sequel.lit[rdoc-ref:Sequel::SQL::Builders#lit] if they are not in use.
|
86
|
+
|
87
|
+
'a'.lit
|
88
|
+
Sequel.lit('a')
|
89
|
+
|
90
|
+
Using String#lit or Sequel.lit[rdoc-ref:Sequel::SQL::Builders#lit] to turn a ruby string into a literal string results
|
91
|
+
in SQL injection if the string is derived from user input. With both of these
|
92
|
+
methods, the strings can contain placeholders, which you can use to safely include
|
93
|
+
user input inside a literal string:
|
94
|
+
|
95
|
+
'a = ?'.lit(params[:user_id].to_s)
|
96
|
+
Sequel.lit('a = ?', params[:user_id].to_s)
|
97
|
+
|
98
|
+
Even though they have similar names, note that Sequel::Database#literal operates very differently from
|
99
|
+
String#lit or Sequel.lit[rdoc-ref:Sequel::SQL::Builders#lit].
|
100
|
+
Sequel::Database#literal is for taking any supported object,
|
101
|
+
and getting an SQL representation of that object, while
|
102
|
+
String#lit or Sequel.lit[rdoc-ref:Sequel::SQL::Builders#lit] are for treating
|
103
|
+
a ruby string as raw SQL. For example:
|
104
|
+
|
105
|
+
DB.literal(Date.today) # "'2013-03-22'"
|
106
|
+
DB.literal('a') # "'a'"
|
107
|
+
DB.literal(Sequel.lit('a')) # "a"
|
108
|
+
DB.literal(:a => 'a') # "(\"a\" = 'a')"
|
109
|
+
DB.literal(:a => Sequel.lit('a')) # "(\"a\" = a)"
|
110
|
+
|
111
|
+
==== SQL Filter Fragments
|
112
|
+
|
113
|
+
The most common way to use raw SQL with Sequel is in filters:
|
114
|
+
|
115
|
+
DB[:table].where("name > 'M'")
|
116
|
+
|
117
|
+
If a filter method is passed a string as the first argument, it treats the rest of
|
118
|
+
the arguments (if any) as placeholders to the string. So you should never do:
|
119
|
+
|
120
|
+
DB[:table].where("name > #{params[:id].to_s}") # SQL Injection!
|
121
|
+
|
122
|
+
Instead, you should use a placeholder:
|
123
|
+
|
124
|
+
DB[:table].where("name > ?", params[:id].to_s) # Safe
|
125
|
+
|
126
|
+
Note that for that type of query, Sequel generally encourages the following form:
|
127
|
+
|
128
|
+
DB[:table].where{|o| o.name > params[:id].to_s} # Safe
|
129
|
+
|
130
|
+
Sequel's DSL supports a wide variety of SQL concepts, so it's possible to
|
131
|
+
code most applications without every using raw SQL.
|
132
|
+
|
133
|
+
A large number of dataset methods ultimately pass down their arguments to a filter
|
134
|
+
method, even some you may not expect, so you should be careful. At least the
|
135
|
+
following methods pass their arguments to a filter method:
|
136
|
+
|
137
|
+
* Sequel::Dataset#where
|
138
|
+
* Sequel::Dataset#having
|
139
|
+
* Sequel::Dataset#filter
|
140
|
+
* Sequel::Dataset#exclude
|
141
|
+
* Sequel::Dataset#exclude_where
|
142
|
+
* Sequel::Dataset#exclude_having
|
143
|
+
* Sequel::Dataset#and
|
144
|
+
* Sequel::Dataset#or
|
145
|
+
* Sequel::Dataset#first
|
146
|
+
* Sequel::Dataset#last
|
147
|
+
* Sequel::Dataset#[]
|
148
|
+
* Sequel::Dataset#[]=
|
149
|
+
|
150
|
+
The Model.find[rdoc-ref:Sequel::Model::ClassMethods#find] and Model.find_or_create[rdoc-ref:Sequel::Model::ClassMethods#find_or_create]
|
151
|
+
class methods also call down to the filter methods.
|
152
|
+
|
153
|
+
==== SQL Fragment passed to Dataset#update
|
154
|
+
|
155
|
+
Similar to the filter methods, Sequel::Dataset#update (and alias Sequel::Dataset#set) also treat a
|
156
|
+
string argument as raw SQL:
|
157
|
+
|
158
|
+
DB[:table].update("column = 1")
|
159
|
+
|
160
|
+
So you should not do:
|
161
|
+
|
162
|
+
DB[:table].update("column = #{params[:value].to_s}") # SQL Injection!
|
163
|
+
|
164
|
+
Instead, you should do:
|
165
|
+
|
166
|
+
DB[:table].update(:column => params[:value].to_s) # Safe
|
167
|
+
|
168
|
+
=== SQL Identifier Injections
|
169
|
+
|
170
|
+
Usually, Sequel treats ruby symbols as SQL identifiers, and ruby
|
171
|
+
strings as SQL strings. However, there are some parts of Sequel
|
172
|
+
that treat ruby strings as SQL identifiers if an SQL string would
|
173
|
+
not make sense in the same context.
|
174
|
+
|
175
|
+
For example, Sequel::Database#from and Sequel::Dataset#from will treat a string as
|
176
|
+
a table name:
|
177
|
+
|
178
|
+
DB.from('t') # SELECT * FROM "t"
|
179
|
+
|
180
|
+
Another place where Sequel treats ruby strings as identifiers are
|
181
|
+
the Sequel::Dataset#insert and Sequel::Dataset#update methods:
|
182
|
+
|
183
|
+
DB[:t].update('b'=>1) # UPDATE "t" SET "b" = 1
|
184
|
+
DB[:t].insert('b'=>1) # INSERT INTO "t" ("b") VALUES (1)
|
185
|
+
|
186
|
+
Note how the identifier is still quoted in these cases. Sequel quotes identifiers by default
|
187
|
+
on most databases. However, it does not quote identifiers by default on DB2 and Informix.
|
188
|
+
On those databases using an identifier derived from user input can lead to SQL injection.
|
189
|
+
Similarly, if you turn off identifier quoting manually on other databases, you open yourself
|
190
|
+
up to SQL injection if you use identifiers derived from user input.
|
191
|
+
|
192
|
+
When Sequel quotes identifiers, using an identifier derived from user input does not lead to
|
193
|
+
SQL injection, since the identifiers are also escaped when quoting.
|
194
|
+
Exceptions to this are Oracle (can't escape <tt>"</tt>) and Microsoft Access
|
195
|
+
(can't escape <tt>]</tt>).
|
196
|
+
|
197
|
+
In general, even if doesn't lead to SQL Injection, you should avoid using identifiers
|
198
|
+
derived from user input unless absolutely necessary.
|
199
|
+
|
200
|
+
Sequel also allows you to create identifiers using
|
201
|
+
Sequel.identifier[rdoc-ref:Sequel::SQL::Builders#identifier] for plain identifiers,
|
202
|
+
Sequel.qualify[rdoc-ref:Sequel::SQL::Builders#qualify] for qualified identifiers, and
|
203
|
+
Sequel.as[rdoc-ref:Sequel::SQL::Builders#as] for aliased expressions. So if you
|
204
|
+
pass any of those values derived from user input, you are dealing with the same scenario.
|
205
|
+
|
206
|
+
Note that the issues with SQL identifiers do not just apply to places where
|
207
|
+
strings are used as identifiers, they also apply to all places where Sequel
|
208
|
+
uses symbols as identifiers. However, if you are creating symbols from user input,
|
209
|
+
you at least have a denial of service vulnerability, and possibly a more serious
|
210
|
+
vulnerability.
|
211
|
+
|
212
|
+
== Denial of Service
|
213
|
+
|
214
|
+
Sequel converts some strings to symbols. Because symbols in ruby are not
|
215
|
+
garbage collected, if the strings that are converted to symbols are
|
216
|
+
derived from user input, you have a denial of service vulnerability due to
|
217
|
+
memory exhaustion.
|
218
|
+
|
219
|
+
The strings that Sequel converts to symbols are generally not derived
|
220
|
+
from user input, so Sequel in general is not vulnerable to this. However,
|
221
|
+
users should be aware of the cases in which Sequel creates symbols, so
|
222
|
+
they do not introduce a vulnerability into their application.
|
223
|
+
|
224
|
+
=== Column Names/Aliases
|
225
|
+
|
226
|
+
Sequel returns SQL result sets as an array of hashes with symbol keys. The
|
227
|
+
keys are derived from the name that the database server gives the column. These
|
228
|
+
names are generally static. For example:
|
229
|
+
|
230
|
+
SELECT column FROM table
|
231
|
+
|
232
|
+
The database will generally use "column" as the name in the result set.
|
233
|
+
|
234
|
+
If you use an alias:
|
235
|
+
|
236
|
+
SELECT column AS alias FROM table
|
237
|
+
|
238
|
+
The database will generally use "alias" as the name in the result set. So
|
239
|
+
if you allow the user to control the alias name:
|
240
|
+
|
241
|
+
DB[:table].select(:column.as(params[:alias]))
|
242
|
+
|
243
|
+
Then you have a denial of service vulnerability. In general, such a vulnerability
|
244
|
+
is unlikely, because you are probably indexing into the returned hash(es) by name,
|
245
|
+
and if an alias was used and you didn't expect it, your application wouldn't work.
|
246
|
+
|
247
|
+
The more insidious cases are those where an explicit alias is not used at all, but
|
248
|
+
an unaliased expression is used and the database chooses which alias to use. For
|
249
|
+
example, on SQLite, the following types of queries are vulnerable to denial of service:
|
250
|
+
|
251
|
+
DB[:table].get(params[:a].to_s)
|
252
|
+
DB[:table].select_map(params[:b].to_s)
|
253
|
+
DB[:table].select_order_map(params[:c].to_s)
|
254
|
+
|
255
|
+
In these cases, the queries will work correctly, but an unused symbol will be created.
|
256
|
+
To protect against the denial of service, use an explicit alias:
|
257
|
+
|
258
|
+
DB[:table].get(Sequel.as(params[:a].to_s, :a))
|
259
|
+
DB[:table].select_map(Sequel.as(params[:b].to_s, :a))
|
260
|
+
DB[:table].select_order_map(Sequel.as(params[:c].to_s, :a))
|
261
|
+
|
262
|
+
While the above code is unlikely to be used in practice, variants that use expressions
|
263
|
+
could be. For example, if you want to select all values in a specific column, with
|
264
|
+
a suffix provided by the user:
|
265
|
+
|
266
|
+
DB[:table].select_map(Sequel.join(:column, params[:suffix].to_s))
|
267
|
+
|
268
|
+
As above, you should use an explicit alias to protect against denial of service:
|
269
|
+
|
270
|
+
DB[:table].select_map(Sequel.join(:column, params[:suffix].to_s).as(:a))
|
271
|
+
|
272
|
+
=== Database Connection Options
|
273
|
+
|
274
|
+
All database connection options are converted to symbols. For a
|
275
|
+
connection URL, the keys are generally fixed, but the scheme is turned
|
276
|
+
into a symbol and the query option keys are used as connection option
|
277
|
+
keys, so they are converted to symbols as well. For example:
|
278
|
+
|
279
|
+
postgres://host/database?option1=foo&option2=bar
|
280
|
+
|
281
|
+
Will result in :postgres, :option1, and :option2 symbols being created.
|
282
|
+
|
283
|
+
Certain option values are also converted to symbols. In the general case,
|
284
|
+
the sql_log_level option value is, but some adapters treat additional
|
285
|
+
options similarly.
|
286
|
+
|
287
|
+
This is not generally a risk unless you are allowing the user to control
|
288
|
+
the connection URLs or are connecting to arbitrary databases at runtime.
|
289
|
+
|
290
|
+
== Mass Assignment
|
291
|
+
|
292
|
+
Mass assignment is the practice of passing a hash of columns and values
|
293
|
+
to a single method, and having multiple column values for a given object set
|
294
|
+
based on the content of the hash.
|
295
|
+
The security issue here is that mass assignment may allow the user to
|
296
|
+
set columns that you didn't intend to allow.
|
297
|
+
|
298
|
+
The Model#set[rdoc-ref:Sequel::Model::InstanceMethods#set] and Model#update[rdoc-ref:Sequel::Model::InstanceMethods#update] methods do mass
|
299
|
+
assignment. The default configuration of Sequel::Model allows all model
|
300
|
+
columns except for the primary key column(s) to be set via mass assignment.
|
301
|
+
|
302
|
+
Example:
|
303
|
+
|
304
|
+
album = Album.new
|
305
|
+
album.set(params[:album]) # Mass Assignment
|
306
|
+
|
307
|
+
Both Model.new[rdoc-ref:Sequel::Model::InstanceMethods::new] and Model.create[rdoc-ref:Sequel::Model::ClassMethods#create]
|
308
|
+
call Model#set[rdoc-ref:Sequel::Model::InstanceMethods#set] internally, so
|
309
|
+
they also allow mass assignment:
|
310
|
+
|
311
|
+
Album.new(params[:album]) # Mass Assignment
|
312
|
+
Album.create(params[:album]) # Mass Assignment
|
313
|
+
|
314
|
+
Instead of these methods, it is encouraged to either use the
|
315
|
+
Model#set_only[rdoc-ref:Sequel::Model::InstanceMethods#set_only],
|
316
|
+
Model#update_only[rdoc-ref:Sequel::Model::InstanceMethods#update_only],
|
317
|
+
Model#set_fields[rdoc-ref:Sequel::Model::InstanceMethods#set_fields], or
|
318
|
+
Model#update_fields[rdoc-ref:Sequel::Model::InstanceMethods#update_fields]
|
319
|
+
methods, which allow you to specify which fields
|
320
|
+
to allow on a per-call basis. This pretty much eliminates the chance that the
|
321
|
+
user will be able to set a column you did not intend to allow:
|
322
|
+
|
323
|
+
album.set_only(params[:album], [:name, :copies_sold])
|
324
|
+
album.set_fields(params[:album], [:name, :copies_sold])
|
325
|
+
|
326
|
+
You can override the columns to allow by default during mass assignment via
|
327
|
+
the Model.set_allowed_columns[rdoc-ref:Sequel::Model::ClassMethods#set_allowed_columns] class method. This is a good
|
328
|
+
practice, though being explicit on a per-call basis is still recommended:
|
329
|
+
|
330
|
+
Album.set_allowed_columns(:name, :copies_sold)
|
331
|
+
Album.create(params[:album]) # Only name and copies_sold set
|
332
|
+
|
333
|
+
For more details on the mass assignment methods, see the {Mass Assignment Guide}[link:files/doc/mass_assignment_rdoc.html].
|
334
|
+
|
335
|
+
== General Parameter Handling
|
336
|
+
|
337
|
+
This issue isn't necessarily specific to Sequel, but it is a good general practice.
|
338
|
+
If you are using values derived from user input, it is best to be explicit about
|
339
|
+
their type. For example:
|
340
|
+
|
341
|
+
Album.where(:id=>params[:id])
|
342
|
+
|
343
|
+
is probably a bad idea. Assuming you are using a web framework, params\[:id\] could
|
344
|
+
be a string, an array, a hash, or nil.
|
345
|
+
|
346
|
+
Assuming that +id+ is an integer field, you probably want to do:
|
347
|
+
|
348
|
+
Album.where(:id=>params[:id].to_i)
|
349
|
+
|
350
|
+
If you are looking something up by name, you should try to enforce the value to be
|
351
|
+
a string:
|
352
|
+
|
353
|
+
Album.where(:name=>params[:name].to_s)
|
354
|
+
|
355
|
+
If you are trying to use an IN clause with a list of id values based on input provided
|
356
|
+
on a web form:
|
357
|
+
|
358
|
+
Album.where(:id=>params[:ids].to_a.map{|i| i.to_i})
|
359
|
+
|
360
|
+
Basically, be as explicit as possible. While there aren't any known security issues
|
361
|
+
in Sequel when you do:
|
362
|
+
|
363
|
+
Album.where(:id=>params[:id])
|
364
|
+
|
365
|
+
It allows the attacker to choose to do any of the following queries:
|
366
|
+
|
367
|
+
id IS NULL # nil
|
368
|
+
id = '1' # '1'
|
369
|
+
id IN ('1', '2', '3') # ['1', '2', '3']
|
370
|
+
id = ('a' = 'b') # {'a'=>'b'}
|
371
|
+
id = ('a' IN ('a', 'b') AND 'c' = '') # {'a'=>['a', 'b'], 'c'=>''}
|
372
|
+
|
373
|
+
While none of those allow for SQL injection, it's possible that they
|
374
|
+
might have an issue in your application. For example, a long array
|
375
|
+
or deeply nested hash might cause the database to have to do a lot of
|
376
|
+
work that could be avoided.
|
377
|
+
|
378
|
+
In general, it's best to let the attacker control as little as possible,
|
379
|
+
and explicitly specifying types helps a great deal there.
|