sequel 4.31.0 → 4.32.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.
- checksums.yaml +4 -4
- data/CHANGELOG +24 -0
- data/Rakefile +17 -15
- data/doc/association_basics.rdoc +7 -3
- data/doc/opening_databases.rdoc +7 -0
- data/doc/release_notes/4.32.0.txt +132 -0
- data/doc/schema_modification.rdoc +1 -1
- data/doc/security.rdoc +70 -26
- data/doc/testing.rdoc +1 -0
- data/lib/sequel/adapters/jdbc.rb +2 -1
- data/lib/sequel/adapters/postgres.rb +3 -4
- data/lib/sequel/adapters/shared/mysql.rb +14 -1
- data/lib/sequel/adapters/shared/sqlite.rb +2 -2
- data/lib/sequel/extensions/_pretty_table.rb +2 -0
- data/lib/sequel/extensions/arbitrary_servers.rb +2 -0
- data/lib/sequel/extensions/columns_introspection.rb +2 -0
- data/lib/sequel/extensions/connection_validator.rb +2 -0
- data/lib/sequel/extensions/constraint_validations.rb +2 -0
- data/lib/sequel/extensions/core_extensions.rb +1 -5
- data/lib/sequel/extensions/current_datetime_timestamp.rb +2 -0
- data/lib/sequel/extensions/dataset_source_alias.rb +2 -0
- data/lib/sequel/extensions/date_arithmetic.rb +2 -0
- data/lib/sequel/extensions/empty_array_consider_nulls.rb +3 -1
- data/lib/sequel/extensions/error_sql.rb +2 -0
- data/lib/sequel/extensions/eval_inspect.rb +2 -0
- data/lib/sequel/extensions/filter_having.rb +2 -0
- data/lib/sequel/extensions/from_block.rb +2 -0
- data/lib/sequel/extensions/graph_each.rb +2 -0
- data/lib/sequel/extensions/hash_aliases.rb +2 -0
- data/lib/sequel/extensions/inflector.rb +2 -0
- data/lib/sequel/extensions/looser_typecasting.rb +3 -1
- data/lib/sequel/extensions/meta_def.rb +2 -0
- data/lib/sequel/extensions/migration.rb +4 -0
- data/lib/sequel/extensions/mssql_emulate_lateral_with_apply.rb +2 -0
- data/lib/sequel/extensions/named_timezones.rb +2 -0
- data/lib/sequel/extensions/no_auto_literal_strings.rb +84 -0
- data/lib/sequel/extensions/null_dataset.rb +2 -0
- data/lib/sequel/extensions/pagination.rb +2 -0
- data/lib/sequel/extensions/pg_array.rb +2 -4
- data/lib/sequel/extensions/pg_array_ops.rb +2 -0
- data/lib/sequel/extensions/pg_enum.rb +2 -0
- data/lib/sequel/extensions/pg_hstore.rb +2 -4
- data/lib/sequel/extensions/pg_hstore_ops.rb +2 -0
- data/lib/sequel/extensions/pg_inet.rb +2 -4
- data/lib/sequel/extensions/pg_inet_ops.rb +2 -0
- data/lib/sequel/extensions/pg_interval.rb +2 -4
- data/lib/sequel/extensions/pg_json.rb +4 -4
- data/lib/sequel/extensions/pg_json_ops.rb +3 -0
- data/lib/sequel/extensions/pg_loose_count.rb +2 -0
- data/lib/sequel/extensions/pg_range.rb +2 -4
- data/lib/sequel/extensions/pg_range_ops.rb +2 -0
- data/lib/sequel/extensions/pg_row.rb +2 -4
- data/lib/sequel/extensions/pg_row_ops.rb +2 -0
- data/lib/sequel/extensions/pg_static_cache_updater.rb +2 -0
- data/lib/sequel/extensions/pretty_table.rb +2 -0
- data/lib/sequel/extensions/query.rb +3 -0
- data/lib/sequel/extensions/query_literals.rb +7 -5
- data/lib/sequel/extensions/round_timestamps.rb +4 -3
- data/lib/sequel/extensions/schema_caching.rb +2 -0
- data/lib/sequel/extensions/schema_dumper.rb +2 -0
- data/lib/sequel/extensions/select_remove.rb +2 -0
- data/lib/sequel/extensions/sequel_3_dataset_methods.rb +2 -0
- data/lib/sequel/extensions/server_block.rb +3 -0
- data/lib/sequel/extensions/set_overrides.rb +2 -0
- data/lib/sequel/extensions/split_array_nil.rb +2 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +2 -0
- data/lib/sequel/extensions/to_dot.rb +2 -0
- data/lib/sequel/model/associations.rb +95 -55
- data/lib/sequel/plugins/association_pks.rb +58 -33
- data/lib/sequel/plugins/eager_each.rb +22 -0
- data/lib/sequel/plugins/pg_typecast_on_load.rb +3 -2
- data/lib/sequel/plugins/tactical_eager_loading.rb +44 -3
- data/lib/sequel/version.rb +2 -2
- data/spec/adapters/mysql_spec.rb +34 -6
- data/spec/adapters/oracle_spec.rb +1 -1
- data/spec/bin_spec.rb +2 -2
- data/spec/core/dataset_spec.rb +7 -0
- data/spec/extensions/association_pks_spec.rb +38 -0
- data/spec/extensions/class_table_inheritance_spec.rb +24 -0
- data/spec/extensions/eager_each_spec.rb +25 -1
- data/spec/extensions/no_auto_literal_strings_spec.rb +65 -0
- data/spec/extensions/pg_range_spec.rb +1 -0
- data/spec/extensions/spec_helper.rb +5 -5
- data/spec/extensions/tactical_eager_loading_spec.rb +71 -17
- data/spec/integration/associations_test.rb +77 -62
- data/spec/integration/dataset_test.rb +3 -3
- data/spec/integration/plugin_test.rb +22 -0
- data/spec/integration/prepared_statement_test.rb +8 -8
- data/spec/integration/spec_helper.rb +4 -0
- data/spec/model/association_reflection_spec.rb +30 -0
- data/spec/model/associations_spec.rb +177 -16
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d56404078cfe75cf79b2fe4b804e205d8a2e48d3
|
4
|
+
data.tar.gz: 587618022698a1b6f7076571e01361b080f3ea77
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 97590c69f6bef7d22b856c9e331256357cdb4bc5804a65747242c14e3e9c40149f134503f303ce410941181e7574253205e73482f5fe5501ce2d6bebaa94c6cc
|
7
|
+
data.tar.gz: cb19067f82ec892008af6e1064bfb107edce01df096e377606adade354dbfa59dad90aa55dd2a33dc1d7ede4b9c42f16e5f5850c864979e85dfbb63adbfceca6
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,27 @@
|
|
1
|
+
=== 4.32.0 (2016-03-01)
|
2
|
+
|
3
|
+
* Use mutex for synchronizing access to association reflection cache on MRI (jeremyevans)
|
4
|
+
|
5
|
+
* Add Dataset#delete_from on MySQL, allowing deletions from multiple tables in a single query (jeremyevans) (#1146)
|
6
|
+
|
7
|
+
* Add no_auto_literal_strings extension, which makes SQL injection vulnerabilities less likely (jeremyevans)
|
8
|
+
|
9
|
+
* Add Model.default_association_options, for setting option defaults for all future associations (jeremyevans)
|
10
|
+
|
11
|
+
* Support :association_pks_nil association option in association_pks setter for determining how to handle nil (jeremyevans)
|
12
|
+
|
13
|
+
* Make association_pks setter handle empty array correctly when :delay_pks is set (jeremyevans)
|
14
|
+
|
15
|
+
* Add a setter method for one_through_one associations (jeremyevans)
|
16
|
+
|
17
|
+
* Include :remarks entry in JDBC schema parsing output, containing comments on the column (olleolleolle) (#1143)
|
18
|
+
|
19
|
+
* Support :eager_reload and :eager options to associations in tactical_eager_loading plugin (jeremyevans)
|
20
|
+
|
21
|
+
* Make tactical_eager_loading not eager load if passing proc or block to association method (jeremyevans)
|
22
|
+
|
23
|
+
* Make eager_each plugin handle eager loading for Dataset#first and similar methods (jeremyevans)
|
24
|
+
|
1
25
|
=== 4.31.0 (2016-02-01)
|
2
26
|
|
3
27
|
* Convert types in association_pks setters before saving them, instead of just before running queries (jeremyevans)
|
data/Rakefile
CHANGED
@@ -93,7 +93,7 @@ run_spec = proc do |patterns|
|
|
93
93
|
ENV['RUBYLIB'] = rubylib
|
94
94
|
end
|
95
95
|
|
96
|
-
spec_task = proc do |description, name, files|
|
96
|
+
spec_task = proc do |description, name, files, coverage|
|
97
97
|
desc description
|
98
98
|
task name do
|
99
99
|
run_spec.call(files)
|
@@ -106,11 +106,13 @@ spec_task = proc do |description, name, files|
|
|
106
106
|
sh "#{rake} #{name} 2>&1 | egrep -v \"(: warning: instance variable @.* not initialized|: warning: method redefined; discarding old|: warning: previous definition of)\""
|
107
107
|
end
|
108
108
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
109
|
+
if coverage
|
110
|
+
desc "#{description} with coverage"
|
111
|
+
task :"#{name}_cov" do
|
112
|
+
ENV['COVERAGE'] = '1'
|
113
|
+
run_spec.call(files)
|
114
|
+
ENV.delete('COVERAGE')
|
115
|
+
end
|
114
116
|
end
|
115
117
|
end
|
116
118
|
|
@@ -119,19 +121,19 @@ task :default => :spec
|
|
119
121
|
desc "Run the core, model, and extension/plugin specs"
|
120
122
|
task :spec => [:spec_core, :spec_model, :spec_plugin]
|
121
123
|
|
122
|
-
spec_task.call("Run core and model specs together", :spec_core_model, './spec/core/*_spec.rb ./spec/model/*_spec.rb')
|
123
|
-
spec_task.call("Run core specs", :spec_core, './spec/core/*_spec.rb')
|
124
|
-
spec_task.call("Run model specs", :spec_model, './spec/model/*_spec.rb')
|
125
|
-
spec_task.call("Run plugin/extension specs", :spec_plugin, './spec/extensions/*_spec.rb')
|
126
|
-
spec_task.call("Run bin/sequel specs", :spec_bin, './spec/bin_spec.rb')
|
127
|
-
spec_task.call("Run core extensions specs", :spec_core_ext, './spec/core_extensions_spec.rb')
|
128
|
-
spec_task.call("Run integration tests", :spec_integration, './spec/integration/*_test.rb')
|
124
|
+
spec_task.call("Run core and model specs together", :spec_core_model, './spec/core/*_spec.rb ./spec/model/*_spec.rb', true)
|
125
|
+
spec_task.call("Run core specs", :spec_core, './spec/core/*_spec.rb', false)
|
126
|
+
spec_task.call("Run model specs", :spec_model, './spec/model/*_spec.rb', false)
|
127
|
+
spec_task.call("Run plugin/extension specs", :spec_plugin, './spec/extensions/*_spec.rb', true)
|
128
|
+
spec_task.call("Run bin/sequel specs", :spec_bin, './spec/bin_spec.rb', false)
|
129
|
+
spec_task.call("Run core extensions specs", :spec_core_ext, './spec/core_extensions_spec.rb', true)
|
130
|
+
spec_task.call("Run integration tests", :spec_integration, './spec/integration/*_test.rb', true)
|
129
131
|
|
130
132
|
%w'postgres sqlite mysql informix oracle firebird mssql db2 sqlanywhere'.each do |adapter|
|
131
|
-
spec_task.call("Run #{adapter} tests", :"spec_#{adapter}", "./spec/adapters/#{adapter}_spec.rb ./spec/integration/*_test.rb")
|
133
|
+
spec_task.call("Run #{adapter} tests", :"spec_#{adapter}", "./spec/adapters/#{adapter}_spec.rb ./spec/integration/*_test.rb", true)
|
132
134
|
end
|
133
135
|
|
134
|
-
spec_task.call("Run model specs without the associations code", :_spec_model_no_assoc, Dir["./spec/model/*_spec.rb"].delete_if{|f| f =~ /association|eager_loading/}.join(' '))
|
136
|
+
spec_task.call("Run model specs without the associations code", :_spec_model_no_assoc, Dir["./spec/model/*_spec.rb"].delete_if{|f| f =~ /association|eager_loading/}.join(' '), false)
|
135
137
|
desc "Run model specs without the associations code"
|
136
138
|
task :spec_model_no_assoc do
|
137
139
|
ENV['SEQUEL_NO_ASSOCIATIONS'] = '1'
|
data/doc/association_basics.rdoc
CHANGED
@@ -302,8 +302,6 @@ Examples:
|
|
302
302
|
@artist.remove_album(@album)
|
303
303
|
@artist.remove_all_albums
|
304
304
|
|
305
|
-
one_through_one associations do not have any modification methods added.
|
306
|
-
|
307
305
|
Note that the remove_all_* method does not call remove hooks defined on
|
308
306
|
the association, it just issues a single query to the database. If you
|
309
307
|
want to remove all associated objects and call remove hooks, iterate
|
@@ -867,7 +865,13 @@ you also wanted to handle the Artist#remove_all_albums method:
|
|
867
865
|
== Association Options
|
868
866
|
|
869
867
|
Sequel's associations mostly share the same options. For ease of understanding,
|
870
|
-
they are grouped here by section
|
868
|
+
they are grouped here by section.
|
869
|
+
|
870
|
+
The defaults for any of these options can be set at the class level using
|
871
|
+
<tt>Sequel::Model.default_association_options</tt>. Many of these options
|
872
|
+
are specific to particular associations and probably should not be set as
|
873
|
+
defaults, but there certainly are options where a class level default makes
|
874
|
+
sense.
|
871
875
|
|
872
876
|
=== Association Dataset Modification Options
|
873
877
|
|
data/doc/opening_databases.rdoc
CHANGED
@@ -62,6 +62,13 @@ For example:
|
|
62
62
|
|
63
63
|
Sequel.connect('sqlite://blog.db'){|db| puts db[:users].count}
|
64
64
|
|
65
|
+
Note that if you do not pass a block to Sequel.connect, Sequel will automatically retain a
|
66
|
+
reference to the object in the <tt>Sequel::DATABASES</tt> array. So calling +Sequel.connect+
|
67
|
+
multiple times (say once per request), can result in a memory leak. For any application where
|
68
|
+
database access is needed for a long period of time, it's best to store the result of
|
69
|
+
Sequel.connection in a constant, as recommended above.
|
70
|
+
|
71
|
+
== Using the Sequel.connect method
|
65
72
|
== General connection options
|
66
73
|
|
67
74
|
These options are shared by all adapters unless otherwise noted.
|
@@ -0,0 +1,132 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* A no_auto_string_literals plugin has been added, which removes the
|
4
|
+
automatic usage of strings in filter arguments as literal SQL code.
|
5
|
+
By default, if you do:
|
6
|
+
|
7
|
+
DB[:albums].where("name > 'N'")
|
8
|
+
|
9
|
+
By default Sequel will treat "name > 'N'" as SQL code. However,
|
10
|
+
this makes it much easier to introduce SQL injection:
|
11
|
+
|
12
|
+
# SQL Injection vulnerability in default Sequel
|
13
|
+
DB[:albums].where("name > 'params[:letter]'")
|
14
|
+
|
15
|
+
Sequel does support using placeholders when using literal strings:
|
16
|
+
|
17
|
+
# Safe in default Sequel
|
18
|
+
DB[:albums].where("name > ?", params[:letter])
|
19
|
+
|
20
|
+
However, if you forget to use placeholders, you can end up with SQL
|
21
|
+
injection. Accidental usage of filter strings derived from user
|
22
|
+
input as literal SQL code is probably the most common SQL injection
|
23
|
+
vector in applications using Sequel.
|
24
|
+
|
25
|
+
With the no_auto_string_literals extension, passing a plain string
|
26
|
+
as the first or only argument to a filter method raises an
|
27
|
+
exception. If you want to use literal SQL code, you have to do so
|
28
|
+
explicitly:
|
29
|
+
|
30
|
+
DB[:albums].where(Sequel.lit("name > 'N'"))
|
31
|
+
|
32
|
+
You can also specify placeholders when using Sequel.lit:
|
33
|
+
|
34
|
+
DB[:albums].where(Sequel.lit("name > ?", params[:letter]))
|
35
|
+
|
36
|
+
Note that in many cases, you can avoid using literal SQL strings
|
37
|
+
completely:
|
38
|
+
|
39
|
+
DB[:albums].where{|v| v.name > params[:letter]}
|
40
|
+
|
41
|
+
* one_through_one associations now support a setter method:
|
42
|
+
|
43
|
+
Foo.one_through_one :bar
|
44
|
+
|
45
|
+
foo = Foo[1]
|
46
|
+
foo.bar = Bar[2]
|
47
|
+
foo.bar = nil
|
48
|
+
|
49
|
+
This will check the current entry in the join table, and based on
|
50
|
+
the argument and the current entry, run a DELETE, INSERT, or UPDATE
|
51
|
+
query, or take no action if the join table is already in the
|
52
|
+
correct state.
|
53
|
+
|
54
|
+
* Model.default_association_options has been added, which supports
|
55
|
+
default options for all future associations. You can use this to
|
56
|
+
do:
|
57
|
+
|
58
|
+
Model.default_association_options = {:read_only=>true}
|
59
|
+
|
60
|
+
Which makes associations not create modification methods by default.
|
61
|
+
You could still create the modification methods by passing
|
62
|
+
:read_only=>true when creating association.
|
63
|
+
|
64
|
+
* The tactical_eager_loading plugin now supports two additional
|
65
|
+
options when calling an association method: :eager and
|
66
|
+
:eager_reload. Example:
|
67
|
+
|
68
|
+
artist = Artist.all.first
|
69
|
+
|
70
|
+
# Loads all albums for all of the artists,
|
71
|
+
# and all tracks for all of those albums
|
72
|
+
artist.albums(:eager=>:tracks)
|
73
|
+
|
74
|
+
# Reload the artists association for all artists
|
75
|
+
artist.albums(:eager_reload=>true)
|
76
|
+
|
77
|
+
You can also use the :eager option for an eager loading callback:
|
78
|
+
|
79
|
+
# Eagerly load the albums with names starting with A-M
|
80
|
+
artist.albums(:eager=>proc{|ds| ds.where(:name > 'N')})
|
81
|
+
|
82
|
+
* The association_pks plugin now supports an :association_pks_nil
|
83
|
+
association option in the association_pks setter, for determining
|
84
|
+
how nil values should be handled.
|
85
|
+
|
86
|
+
In Sequel <4.31.0, if you provided nil, it would either raise an
|
87
|
+
exception immediately if :delay_pks was not set, or on saving if
|
88
|
+
:delay_pks was set.
|
89
|
+
|
90
|
+
In Sequel 4.31.0, if :delay_pks was not set, it would remove all
|
91
|
+
associated rows. If :delay_pks was set, it would do nothing.
|
92
|
+
|
93
|
+
You can now set :association_pks_nil=>:remove to remove all
|
94
|
+
associated values on nil, or :association_pks_nil=>:ignore to ignore
|
95
|
+
a nil value passed to the method. Without :association_pks_nil set,
|
96
|
+
an exception will be raised.
|
97
|
+
|
98
|
+
* Dataset#delete_from has been added on MySQL, allowing deletions from
|
99
|
+
multiple tables in a single query:
|
100
|
+
|
101
|
+
DB[:a].join(:b, :a_id=>:id).delete_from(:a, :b).delete
|
102
|
+
# DELETE a, b FROM a INNER JOIN b ON (b.a_id = a.id)
|
103
|
+
|
104
|
+
* The JDBC schema parser now includes a :remarks entry for each
|
105
|
+
column, which contains comments on the column.
|
106
|
+
|
107
|
+
= Other Improvements
|
108
|
+
|
109
|
+
* The setter method added by the association_pks plugin now handles
|
110
|
+
the empty array correctly when :delay_pks is set. Previously, if
|
111
|
+
the empty array was passed, Sequel made no modifications in this
|
112
|
+
case. Sequel now correctly removes all associated values if an
|
113
|
+
empty array is passed.
|
114
|
+
|
115
|
+
* The eager_each plugin now handles eager loading when using
|
116
|
+
Dataset#first and related methods. Previously, the behavior was
|
117
|
+
unspecified. In Sequel <4.27.0 Dataset#first did eager loading
|
118
|
+
correctly in the eager case, but incorrectly in the eager_graph
|
119
|
+
case. In Sequel 4.27.0-4.31.0, it did not do eager loading in
|
120
|
+
either case.
|
121
|
+
|
122
|
+
* The tactical_eager_loading plugin will not automatically eager load
|
123
|
+
if passing a proc or block to an association method, since the proc
|
124
|
+
or block could be specific to the receiver.
|
125
|
+
|
126
|
+
* Sequel now uses a mutex to synchronize access to the association
|
127
|
+
cache on MRI, as it does on other ruby implementations.
|
128
|
+
|
129
|
+
= Backwards Compatibility
|
130
|
+
|
131
|
+
* See above for changes in eager_each and association_pks plugin
|
132
|
+
behavior.
|
@@ -132,7 +132,7 @@ If provided with an array, +primary_key+ does not create a column, it just sets
|
|
132
132
|
|
133
133
|
+foreign_key+ is used to create a foreign key column that references a column in another table (or the same table).
|
134
134
|
It takes the column name as the first argument, the table it references as the second argument, and an options hash
|
135
|
-
as
|
135
|
+
as its third argument. A simple example is:
|
136
136
|
|
137
137
|
create_table(:albums) do
|
138
138
|
primary_key :id
|
data/doc/security.rdoc
CHANGED
@@ -44,34 +44,56 @@ There are basically two kinds of possible SQL injections in Sequel:
|
|
44
44
|
|
45
45
|
==== Full SQL Strings
|
46
46
|
|
47
|
-
Some Sequel methods are designed to execute raw SQL, including:
|
47
|
+
Some Sequel methods are designed to execute raw SQL strings, including:
|
48
48
|
|
49
49
|
* Sequel::Database#execute
|
50
50
|
* Sequel::Database#run
|
51
51
|
* Sequel::Database#<<
|
52
|
-
* Sequel::
|
53
|
-
* Sequel::
|
54
|
-
* Sequel::Dataset#
|
52
|
+
* Sequel::Dataset#fetch_rows
|
53
|
+
* Sequel::Dataset#with_sql_all
|
54
|
+
* Sequel::Dataset#with_sql_delete
|
55
|
+
* Sequel::Dataset#with_sql_each
|
56
|
+
* Sequel::Dataset#with_sql_first
|
57
|
+
* Sequel::Dataset#with_sql_insert
|
58
|
+
* Sequel::Dataset#with_sql_single_value
|
59
|
+
* Sequel::Dataset#with_sql_update
|
55
60
|
|
56
61
|
Here are some examples of use:
|
57
62
|
|
58
63
|
DB.run 'SQL'
|
59
64
|
DB << 'SQL'
|
60
65
|
DB.execute 'SQL'
|
61
|
-
DB
|
62
|
-
DB.
|
63
|
-
DB.dataset.
|
66
|
+
DB.fetch_rows('SQL'){|row| }
|
67
|
+
DB.dataset.with_sql_all('SQL')
|
68
|
+
DB.dataset.with_sql_delete('SQL')
|
69
|
+
DB.dataset.with_sql_each('SQL'){|row| }
|
70
|
+
DB.dataset.with_sql_first('SQL')
|
71
|
+
DB.dataset.with_sql_insert('SQL')
|
72
|
+
DB.dataset.with_sql_single_value('SQL')
|
73
|
+
DB.dataset.with_sql_update('SQL')
|
64
74
|
|
65
75
|
If you pass a string to these methods that is derived from user input, you open
|
66
|
-
yourself up to SQL injection.
|
67
|
-
|
68
|
-
|
69
|
-
via Sequel::Database#literal. Example:
|
76
|
+
yourself up to SQL injection. These methods are not designed to work at all
|
77
|
+
with user input. If you must call them with user input, you should escape the
|
78
|
+
user input manually via Sequel::Database#literal. Example:
|
70
79
|
|
71
80
|
DB.run "SOME SQL #{DB.literal(params[:user].to_s)}"
|
72
81
|
|
73
|
-
|
74
|
-
|
82
|
+
==== Full SQL Strings, With Possible Placeholders
|
83
|
+
|
84
|
+
Other Sequel methods are designed to support execution of raw SQL strings that may contain placeholders:
|
85
|
+
|
86
|
+
* Sequel::Database#[]
|
87
|
+
* Sequel::Database#fetch
|
88
|
+
* Sequel::Dataset#with_sql
|
89
|
+
|
90
|
+
Here are some examples of use:
|
91
|
+
|
92
|
+
DB['SQL'].all
|
93
|
+
DB.fetch('SQL').all
|
94
|
+
DB.dataset.with_sql('SQL').all
|
95
|
+
|
96
|
+
With these methods you should use placeholders, in which case Sequel automatically escapes the input:
|
75
97
|
|
76
98
|
DB['SELECT * FROM foo WHERE bar = ?', params[:user].to_s]
|
77
99
|
|
@@ -144,11 +166,13 @@ following methods pass their arguments to a filter method:
|
|
144
166
|
* Sequel::Dataset#first
|
145
167
|
* Sequel::Dataset#last
|
146
168
|
* Sequel::Dataset#[]
|
147
|
-
* Sequel::Dataset#[]=
|
148
169
|
|
149
170
|
The Model.find[rdoc-ref:Sequel::Model::ClassMethods#find] and Model.find_or_create[rdoc-ref:Sequel::Model::ClassMethods#find_or_create]
|
150
171
|
class methods also call down to the filter methods.
|
151
172
|
|
173
|
+
The no_auto_string_literals extension can be used to remove the default support
|
174
|
+
for plain strings as literal strings in filter methods.
|
175
|
+
|
152
176
|
==== SQL Fragment passed to Dataset#update
|
153
177
|
|
154
178
|
Similar to the filter methods, Sequel::Dataset#update also treats a
|
@@ -164,6 +188,9 @@ Instead, you should do:
|
|
164
188
|
|
165
189
|
DB[:table].update(:column => params[:value].to_s) # Safe
|
166
190
|
|
191
|
+
The no_auto_string_literals extension can also be used to remove the default support
|
192
|
+
for plain strings as literal strings in update methods.
|
193
|
+
|
167
194
|
==== SQL Fragment passed to Dataset#lock_style
|
168
195
|
|
169
196
|
The Sequel::Dataset#lock_style method also treats an input string
|
@@ -222,12 +249,12 @@ pass any of those values derived from user input, you are dealing with the same
|
|
222
249
|
Note that the issues with SQL identifiers do not just apply to places where
|
223
250
|
strings are used as identifiers, they also apply to all places where Sequel
|
224
251
|
uses symbols as identifiers. However, if you are creating symbols from user input,
|
225
|
-
you at least have a denial of service vulnerability, and possibly a
|
226
|
-
vulnerability.
|
252
|
+
you at least have a denial of service vulnerability in ruby <2.2, and possibly a
|
253
|
+
more serious vulnerability.
|
227
254
|
|
228
255
|
== Denial of Service
|
229
256
|
|
230
|
-
Sequel converts some strings to symbols. Because symbols in ruby are not
|
257
|
+
Sequel converts some strings to symbols. Because symbols in ruby <2.2 are not
|
231
258
|
garbage collected, if the strings that are converted to symbols are
|
232
259
|
derived from user input, you have a denial of service vulnerability due to
|
233
260
|
memory exhaustion.
|
@@ -302,17 +329,34 @@ they also allow mass assignment:
|
|
302
329
|
Album.new(params[:album]) # Mass Assignment
|
303
330
|
Album.create(params[:album]) # Mass Assignment
|
304
331
|
|
305
|
-
|
306
|
-
Model#
|
332
|
+
When the argument is derived from user input, instead of these methods, it is encouraged to either use
|
333
|
+
Model#set_fields[rdoc-ref:Sequel::Model::InstanceMethods#set_fields] or
|
334
|
+
Model#update_fields[rdoc-ref:Sequel::Model::InstanceMethods#update_fields],
|
335
|
+
which allow you to specify which fields to allow on a per-call basis. This
|
336
|
+
pretty much eliminates the chance that the user will be able to set a column
|
337
|
+
you did not intend to allow:
|
338
|
+
|
339
|
+
album.set_fields(params[:album], [:name, :copies_sold])
|
340
|
+
album.update_fields(params[:album], [:name, :copies_sold])
|
341
|
+
|
342
|
+
These two methods iterate over the second argument (+:name+ and +:copies_sold+ in
|
343
|
+
this example) instead of iterating over the entries in the first argument
|
344
|
+
(<tt>params[:album]</tt> in this example).
|
345
|
+
|
346
|
+
In addition to these two methods, you can also use
|
347
|
+
Model#set_only[rdoc-ref:Sequel::Model::InstanceMethods#set_only] or
|
307
348
|
Model#update_only[rdoc-ref:Sequel::Model::InstanceMethods#update_only],
|
308
|
-
|
309
|
-
|
310
|
-
methods, which allow you to specify which fields
|
311
|
-
to allow on a per-call basis. This pretty much eliminates the chance that the
|
312
|
-
user will be able to set a column you did not intend to allow:
|
349
|
+
which are similar but iterate over the entries in the first argument, checking
|
350
|
+
the second argument to see if setting the entries is allowed.
|
313
351
|
|
314
352
|
album.set_only(params[:album], [:name, :copies_sold])
|
315
|
-
album.
|
353
|
+
album.update_only(params[:album], [:name, :copies_sold])
|
354
|
+
|
355
|
+
If you expect all entries in the second argument to be present in the first
|
356
|
+
argument, use +set_fields+ or +update_fields+. If you are not sure if all
|
357
|
+
arguments in the second argument will be present in the first argument, but
|
358
|
+
do not want to allow setting any column other than the ones listed in the
|
359
|
+
second argument, use +set_only+ or +update_only+.
|
316
360
|
|
317
361
|
You can override the columns to allow by default during mass assignment via
|
318
362
|
the Model.set_allowed_columns[rdoc-ref:Sequel::Model::ClassMethods#set_allowed_columns] class method. This is a good
|
@@ -331,7 +375,7 @@ their type. For example:
|
|
331
375
|
|
332
376
|
Album.where(:id=>params[:id])
|
333
377
|
|
334
|
-
is probably a bad idea. Assuming you are using a web framework, params
|
378
|
+
is probably a bad idea. Assuming you are using a web framework, <tt>params[:id]</tt> could
|
335
379
|
be a string, an array, a hash, or nil.
|
336
380
|
|
337
381
|
Assuming that +id+ is an integer field, you probably want to do:
|
data/doc/testing.rdoc
CHANGED
@@ -155,6 +155,7 @@ The SEQUEL_INTEGRATION_URL environment variable specifies the Database connectio
|
|
155
155
|
SEQUEL_COLUMNS_INTROSPECTION :: Use the columns_introspection extension when running the specs
|
156
156
|
SEQUEL_CONNECTION_VALIDATOR :: Use the connection validator extension when running the specs
|
157
157
|
SEQUEL_ERROR_SQL :: Use the error_sql extension when running the specs
|
158
|
+
SEQUEL_NO_AUTO_LITERAL_STRINGS :: Use the no_auto_string_literals extension when running the specs
|
158
159
|
SEQUEL_NO_CACHE_ASSOCIATIONS :: Don't cache association metadata when running the specs
|
159
160
|
SEQUEL_NO_CHECK_SQLS :: Don't check for specific SQL syntax when running the specs
|
160
161
|
SEQUEL_NO_PENDING :: Don't skip any specs, try running all specs (note, can cause lockups for some adapters)
|