sequel 4.31.0 → 4.32.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +24 -0
  3. data/Rakefile +17 -15
  4. data/doc/association_basics.rdoc +7 -3
  5. data/doc/opening_databases.rdoc +7 -0
  6. data/doc/release_notes/4.32.0.txt +132 -0
  7. data/doc/schema_modification.rdoc +1 -1
  8. data/doc/security.rdoc +70 -26
  9. data/doc/testing.rdoc +1 -0
  10. data/lib/sequel/adapters/jdbc.rb +2 -1
  11. data/lib/sequel/adapters/postgres.rb +3 -4
  12. data/lib/sequel/adapters/shared/mysql.rb +14 -1
  13. data/lib/sequel/adapters/shared/sqlite.rb +2 -2
  14. data/lib/sequel/extensions/_pretty_table.rb +2 -0
  15. data/lib/sequel/extensions/arbitrary_servers.rb +2 -0
  16. data/lib/sequel/extensions/columns_introspection.rb +2 -0
  17. data/lib/sequel/extensions/connection_validator.rb +2 -0
  18. data/lib/sequel/extensions/constraint_validations.rb +2 -0
  19. data/lib/sequel/extensions/core_extensions.rb +1 -5
  20. data/lib/sequel/extensions/current_datetime_timestamp.rb +2 -0
  21. data/lib/sequel/extensions/dataset_source_alias.rb +2 -0
  22. data/lib/sequel/extensions/date_arithmetic.rb +2 -0
  23. data/lib/sequel/extensions/empty_array_consider_nulls.rb +3 -1
  24. data/lib/sequel/extensions/error_sql.rb +2 -0
  25. data/lib/sequel/extensions/eval_inspect.rb +2 -0
  26. data/lib/sequel/extensions/filter_having.rb +2 -0
  27. data/lib/sequel/extensions/from_block.rb +2 -0
  28. data/lib/sequel/extensions/graph_each.rb +2 -0
  29. data/lib/sequel/extensions/hash_aliases.rb +2 -0
  30. data/lib/sequel/extensions/inflector.rb +2 -0
  31. data/lib/sequel/extensions/looser_typecasting.rb +3 -1
  32. data/lib/sequel/extensions/meta_def.rb +2 -0
  33. data/lib/sequel/extensions/migration.rb +4 -0
  34. data/lib/sequel/extensions/mssql_emulate_lateral_with_apply.rb +2 -0
  35. data/lib/sequel/extensions/named_timezones.rb +2 -0
  36. data/lib/sequel/extensions/no_auto_literal_strings.rb +84 -0
  37. data/lib/sequel/extensions/null_dataset.rb +2 -0
  38. data/lib/sequel/extensions/pagination.rb +2 -0
  39. data/lib/sequel/extensions/pg_array.rb +2 -4
  40. data/lib/sequel/extensions/pg_array_ops.rb +2 -0
  41. data/lib/sequel/extensions/pg_enum.rb +2 -0
  42. data/lib/sequel/extensions/pg_hstore.rb +2 -4
  43. data/lib/sequel/extensions/pg_hstore_ops.rb +2 -0
  44. data/lib/sequel/extensions/pg_inet.rb +2 -4
  45. data/lib/sequel/extensions/pg_inet_ops.rb +2 -0
  46. data/lib/sequel/extensions/pg_interval.rb +2 -4
  47. data/lib/sequel/extensions/pg_json.rb +4 -4
  48. data/lib/sequel/extensions/pg_json_ops.rb +3 -0
  49. data/lib/sequel/extensions/pg_loose_count.rb +2 -0
  50. data/lib/sequel/extensions/pg_range.rb +2 -4
  51. data/lib/sequel/extensions/pg_range_ops.rb +2 -0
  52. data/lib/sequel/extensions/pg_row.rb +2 -4
  53. data/lib/sequel/extensions/pg_row_ops.rb +2 -0
  54. data/lib/sequel/extensions/pg_static_cache_updater.rb +2 -0
  55. data/lib/sequel/extensions/pretty_table.rb +2 -0
  56. data/lib/sequel/extensions/query.rb +3 -0
  57. data/lib/sequel/extensions/query_literals.rb +7 -5
  58. data/lib/sequel/extensions/round_timestamps.rb +4 -3
  59. data/lib/sequel/extensions/schema_caching.rb +2 -0
  60. data/lib/sequel/extensions/schema_dumper.rb +2 -0
  61. data/lib/sequel/extensions/select_remove.rb +2 -0
  62. data/lib/sequel/extensions/sequel_3_dataset_methods.rb +2 -0
  63. data/lib/sequel/extensions/server_block.rb +3 -0
  64. data/lib/sequel/extensions/set_overrides.rb +2 -0
  65. data/lib/sequel/extensions/split_array_nil.rb +2 -0
  66. data/lib/sequel/extensions/thread_local_timezones.rb +2 -0
  67. data/lib/sequel/extensions/to_dot.rb +2 -0
  68. data/lib/sequel/model/associations.rb +95 -55
  69. data/lib/sequel/plugins/association_pks.rb +58 -33
  70. data/lib/sequel/plugins/eager_each.rb +22 -0
  71. data/lib/sequel/plugins/pg_typecast_on_load.rb +3 -2
  72. data/lib/sequel/plugins/tactical_eager_loading.rb +44 -3
  73. data/lib/sequel/version.rb +2 -2
  74. data/spec/adapters/mysql_spec.rb +34 -6
  75. data/spec/adapters/oracle_spec.rb +1 -1
  76. data/spec/bin_spec.rb +2 -2
  77. data/spec/core/dataset_spec.rb +7 -0
  78. data/spec/extensions/association_pks_spec.rb +38 -0
  79. data/spec/extensions/class_table_inheritance_spec.rb +24 -0
  80. data/spec/extensions/eager_each_spec.rb +25 -1
  81. data/spec/extensions/no_auto_literal_strings_spec.rb +65 -0
  82. data/spec/extensions/pg_range_spec.rb +1 -0
  83. data/spec/extensions/spec_helper.rb +5 -5
  84. data/spec/extensions/tactical_eager_loading_spec.rb +71 -17
  85. data/spec/integration/associations_test.rb +77 -62
  86. data/spec/integration/dataset_test.rb +3 -3
  87. data/spec/integration/plugin_test.rb +22 -0
  88. data/spec/integration/prepared_statement_test.rb +8 -8
  89. data/spec/integration/spec_helper.rb +4 -0
  90. data/spec/model/association_reflection_spec.rb +30 -0
  91. data/spec/model/associations_spec.rb +177 -16
  92. metadata +6 -2
@@ -632,7 +632,8 @@ module Sequel
632
632
  :allow_null=>(h[:nullable] != 0),
633
633
  :primary_key=>pks.include?(h[:column_name]),
634
634
  :column_size=>h[:column_size],
635
- :scale=>h[:decimal_digits]
635
+ :scale=>h[:decimal_digits],
636
+ :remarks=>h[:remarks]
636
637
  }
637
638
  if s[:primary_key]
638
639
  s[:auto_increment] = h[:is_autoincrement] == "YES"
@@ -306,10 +306,9 @@ module Sequel
306
306
 
307
307
  # Disconnect given connection
308
308
  def disconnect_connection(conn)
309
- begin
310
- conn.finish
311
- rescue PGError, IOError
312
- end
309
+ conn.finish
310
+ rescue PGError, IOError
311
+ nil
313
312
  end
314
313
 
315
314
  if SEQUEL_POSTGRES_USES_PG && Object.const_defined?(:PG) && ::PG.const_defined?(:Constants) && ::PG::Constants.const_defined?(:PG_DIAG_SCHEMA_NAME)
@@ -682,6 +682,18 @@ module Sequel
682
682
  def calc_found_rows
683
683
  clone(:calc_found_rows => true)
684
684
  end
685
+
686
+ # Sets up the select methods to delete from if deleting from a
687
+ # joined dataset:
688
+ #
689
+ # DB[:a].join(:b, :a_id=>:id).delete
690
+ # # DELETE a FROM a INNER JOIN b ON (b.a_id = a.id)
691
+ #
692
+ # DB[:a].join(:b, :a_id=>:id).delete_from(:a, :b).delete
693
+ # # DELETE a, b FROM a INNER JOIN b ON (b.a_id = a.id)
694
+ def delete_from(*tables)
695
+ clone(:delete_from=>tables)
696
+ end
685
697
 
686
698
  # Return the results of an EXPLAIN query as a string. Options:
687
699
  # :extended :: Use EXPLAIN EXPTENDED instead of EXPLAIN if true.
@@ -847,7 +859,8 @@ module Sequel
847
859
  def delete_from_sql(sql)
848
860
  if joined_dataset?
849
861
  sql << SPACE
850
- source_list_append(sql, @opts[:from][0..0])
862
+ tables = @opts[:delete_from] || @opts[:from][0..0]
863
+ source_list_append(sql, tables)
851
864
  sql << FROM
852
865
  source_list_append(sql, @opts[:from])
853
866
  select_join_sql(sql)
@@ -13,9 +13,9 @@ module Sequel
13
13
  AUTO_VACUUM = [:none, :full, :incremental].freeze
14
14
  PRIMARY_KEY_INDEX_RE = /\Asqlite_autoindex_/.freeze
15
15
  SYNCHRONOUS = [:off, :normal, :full].freeze
16
- TABLES_FILTER = "type = 'table' AND NOT name = 'sqlite_sequence'".freeze
16
+ TABLES_FILTER = Sequel.~(:name=>'sqlite_sequence'.freeze) & {:type => 'table'.freeze}
17
17
  TEMP_STORE = [:default, :file, :memory].freeze
18
- VIEWS_FILTER = "type = 'view'".freeze
18
+ VIEWS_FILTER = {:type => 'view'.freeze}.freeze
19
19
  TRANSACTION_MODE = {
20
20
  :deferred => "BEGIN DEFERRED TRANSACTION".freeze,
21
21
  :immediate => "BEGIN IMMEDIATE TRANSACTION".freeze,
@@ -7,6 +7,8 @@
7
7
  # To load the extension:
8
8
  #
9
9
  # Sequel.extension :_pretty_table
10
+ #
11
+ # Related module: Sequel::PrettyTable
10
12
 
11
13
  #
12
14
  module Sequel
@@ -54,6 +54,8 @@
54
54
  # pool. If you are using the sharded single connection pool, you need
55
55
  # to switch to the sharded threaded connection pool before using this
56
56
  # extension.
57
+ #
58
+ # Related module: Sequel::ArbitraryServers
57
59
 
58
60
  #
59
61
  module Sequel
@@ -16,6 +16,8 @@
16
16
  # To attempt to introspect columns for all datasets on a single database:
17
17
  #
18
18
  # DB.extension(:columns_introspection)
19
+ #
20
+ # Related module: Sequel::ColumnsIntrospection
19
21
 
20
22
  #
21
23
  module Sequel
@@ -44,6 +44,8 @@
44
44
  # threaded pools work fine even in single threaded code, so if
45
45
  # you are currently using a single threaded pool and want to
46
46
  # use this extension, switch to using a threaded pool.
47
+ #
48
+ # Related module: Sequel::ConnectionValidator
47
49
 
48
50
  #
49
51
  module Sequel
@@ -123,6 +123,8 @@
123
123
  # <tt>drop_constraint_validations_for(:table=>'table')</tt>, and then
124
124
  # readd all constraints you want to use inside the alter table block,
125
125
  # making no other changes inside the alter_table block.
126
+ #
127
+ # Related module: Sequel::ConstraintValidations
126
128
 
127
129
  #
128
130
  module Sequel
@@ -3,11 +3,7 @@
3
3
  # These are extensions to core classes that Sequel enables by default.
4
4
  # They make using Sequel's DSL easier by adding methods to Array,
5
5
  # Hash, String, and Symbol to add methods that return Sequel
6
- # expression objects.
7
- #
8
- # This extension is currently loaded by default, but that will no
9
- # longer be true in Sequel 4. Starting in Sequel 4, you will
10
- # need to load it manually via:
6
+ # expression objects. To load the extension:
11
7
  #
12
8
  # Sequel.extension :core_extensions
13
9
 
@@ -22,6 +22,8 @@
22
22
  # datasets of a given database.
23
23
  #
24
24
  # DB.extension(:current_datetime_timestamp)
25
+ #
26
+ # Related module: Sequel::CurrentDateTimeTimestamp
25
27
 
26
28
  #
27
29
  module Sequel
@@ -37,6 +37,8 @@
37
37
  # is probably the desired behavior if you are using this extension:
38
38
  #
39
39
  # DB.extension(:dataset_source_alias)
40
+ #
41
+ # Related module: Sequel::Dataset::DatasetSourceAlias
40
42
 
41
43
  #
42
44
  module Sequel
@@ -25,6 +25,8 @@
25
25
  # Sequel expressions are allowed:
26
26
  #
27
27
  # DB[:table].select(add.as(:d)).where(sub > Sequel::CURRENT_TIMESTAMP)
28
+ #
29
+ # Related module: Sequel::SQL::DateAdd
28
30
 
29
31
  #
30
32
  module Sequel
@@ -21,11 +21,13 @@
21
21
  # is probably the desired behavior if you are using this extension:
22
22
  #
23
23
  # DB.extension(:empty_array_consider_nulls)
24
+ #
25
+ # Related module: Sequel::EmptyArrayConsiderNulls
24
26
 
25
27
  #
26
28
  module Sequel
27
29
  module EmptyArrayConsiderNulls
28
- # Use a simple expression that is always true or false, never NULL.
30
+ # Use an expression that returns NULL if the column value is NULL.
29
31
  def empty_array_value(op, cols)
30
32
  c = Array(cols)
31
33
  SQL::BooleanExpression.from_value_pairs(c.zip(c), :AND, op == :IN)
@@ -29,6 +29,8 @@
29
29
  # To load the extension into the database:
30
30
  #
31
31
  # DB.extension :error_sql
32
+ #
33
+ # Related module: Sequel::ErrorSQL
32
34
 
33
35
  #
34
36
  module Sequel
@@ -13,6 +13,8 @@
13
13
  # To load the extension:
14
14
  #
15
15
  # Sequel.extension :eval_inspect
16
+ #
17
+ # Related module: Sequel::EvalInspect
16
18
 
17
19
  #
18
20
  module Sequel
@@ -15,6 +15,8 @@
15
15
  # is probably the desired behavior if you are using this extension:
16
16
  #
17
17
  # DB.extension(:filter_having)
18
+ #
19
+ # Related module: Sequel::FilterHaving
18
20
 
19
21
  #
20
22
  module Sequel
@@ -13,6 +13,8 @@
13
13
  # To load the extension into the database:
14
14
  #
15
15
  # DB.extension :from_block
16
+ #
17
+ # Related module: Sequel::Database::FromBlock
16
18
 
17
19
  #
18
20
  module Sequel
@@ -16,6 +16,8 @@
16
16
  # is probably the desired behavior if you are using this extension:
17
17
  #
18
18
  # DB.extension(:graph_each)
19
+ #
20
+ # Related module: Sequel::GraphEach
19
21
 
20
22
  #
21
23
  module Sequel
@@ -15,6 +15,8 @@
15
15
  # is probably the desired behavior if you are using this extension:
16
16
  #
17
17
  # DB.extension(:hash_aliases)
18
+ #
19
+ # Related module: Sequel::HashAliases
18
20
 
19
21
  #
20
22
  module Sequel
@@ -8,6 +8,8 @@
8
8
  # To load the extension:
9
9
  #
10
10
  # Sequel.extension :inflector
11
+ #
12
+ # Related module: String::Inflections
11
13
 
12
14
  class String
13
15
  # This module acts as a singleton returned/yielded by String.inflections,
@@ -11,6 +11,8 @@
11
11
  # To load the extension into the database:
12
12
  #
13
13
  # DB.extension :looser_typecasting
14
+ #
15
+ # Related module: Sequel::LooserTypecasting
14
16
 
15
17
  #
16
18
  module Sequel
@@ -25,7 +27,7 @@ module Sequel
25
27
  value.to_i
26
28
  end
27
29
 
28
- # Typecast the value to an Integer using to_i instead of Kernel.Integer
30
+ # Typecast the value to an String using to_s instead of Kernel.String
29
31
  def typecast_value_string(value)
30
32
  value.to_s
31
33
  end
@@ -6,6 +6,8 @@
6
6
  # not recommended for usage in new code. To load this extension:
7
7
  #
8
8
  # Sequel.extension :meta_def
9
+ #
10
+ # Related module: Sequel::Metaprogramming
9
11
 
10
12
  #
11
13
  module Sequel
@@ -7,6 +7,10 @@
7
7
  # To load the extension:
8
8
  #
9
9
  # Sequel.extension :migration
10
+ #
11
+ # Related modules: Sequel::Migration, Sequel::SimpleMigration,
12
+ # Sequel::MigrationDSL, Sequel::MigrationReverser, Sequel::MigrationAlterTableReverser,
13
+ # Sequel::Migrator, Sequel::IntegerMigrator, Sequel::TimestampMigrator
10
14
 
11
15
  #
12
16
  module Sequel
@@ -23,6 +23,8 @@
23
23
  # Or you can load it into all of a database's datasets:
24
24
  #
25
25
  # DB.extension(:mssql_emulate_lateral_with_apply)
26
+ #
27
+ # Related module: Sequel::MSSQL::EmulateLateralWithApply
26
28
 
27
29
  #
28
30
  module Sequel
@@ -38,6 +38,8 @@
38
38
  # timezone when fetching rows is dependent on the database adapter,
39
39
  # and only works on adapters where Sequel itself does the conversion.
40
40
  # It should work on mysql, postgres, sqlite, ibmdb, and jdbc.
41
+ #
42
+ # Related module: Sequel::NamedTimezones
41
43
 
42
44
  require 'tzinfo'
43
45
 
@@ -0,0 +1,84 @@
1
+ # frozen-string-literal: true
2
+ #
3
+ # The no_auto_literal_strings extension removes Sequel's automatic conversion
4
+ # of strings to literal strings in the dataset filter methods. By default,
5
+ # Sequel considers a string passed to a filter method as a literal string:
6
+ #
7
+ # DB[:table].where("name > 'A'")
8
+ #
9
+ # This is fine, except when the string is derived from user input:
10
+ #
11
+ # DB[:table].where("name > '#{params[:user]}'") # SQL Injection!
12
+ #
13
+ # Sequel does support using placeholders for such strings:
14
+ #
15
+ # DB[:table].where("name > ?", params[:user].to_s) # Safe
16
+ #
17
+ # However, if you forget to user placeholders, and pass a string to a filter
18
+ # method that is derived from user input, you open yourself up to SQL injection.
19
+ # With this extension, using a plain string in a filter method will result
20
+ # in an exception being raised. You either need to explicitly use a literal
21
+ # string:
22
+ #
23
+ # DB[:table].where(Sequel.lit("name > ?", params[:user].to_s))
24
+ #
25
+ # or you need to construct the same SQL using a non-string based approach:
26
+ #
27
+ # DB[:table].where{|o| o.name > params[:user].to_s}
28
+ #
29
+ # Note that as listed in Sequel's security guide, a large number of dataset
30
+ # methods call down to a filtering method, and this protects all of those
31
+ # cases.
32
+ #
33
+ # This extension also protects the use of a plain string passed to Dataset#update:
34
+ #
35
+ # DB[:table].update("column = column + 1")
36
+ #
37
+ # Again, you either need to explicitly use a literal string:
38
+ #
39
+ # DB[:table].update(Sequel.lit("column = column + 1"))
40
+ #
41
+ # or construct the same SQL using a non-string based approach:
42
+ #
43
+ # DB[:table].update(:column => Sequel.expr(:column) + 1)
44
+ #
45
+ # Related module: Sequel::Dataset::NoAutoLiteralStrings
46
+
47
+ #
48
+ module Sequel
49
+ class Dataset
50
+ module NoAutoLiteralStrings
51
+ # Raise an error if passing a plain string or an array whose first
52
+ # entry is a plain string.
53
+ def filter_expr(expr = nil)
54
+ case expr
55
+ when LiteralString
56
+ super
57
+ when String
58
+ raise Error, "plain string passed to a dataset filtering method"
59
+ when Array
60
+ if expr.first.is_a?(String) && !expr.first.is_a?(LiteralString)
61
+ raise Error, "plain string passed to a dataset filtering method"
62
+ end
63
+ super
64
+ else
65
+ super
66
+ end
67
+ end
68
+
69
+ # Raise an error if passing a plain string.
70
+ def update_sql(values=OPTS)
71
+ case values
72
+ when LiteralString
73
+ super
74
+ when String
75
+ raise Error, "plain string passed to a dataset filtering method"
76
+ else
77
+ super
78
+ end
79
+ end
80
+ end
81
+
82
+ register_extension(:no_auto_literal_strings, NoAutoLiteralStrings)
83
+ end
84
+ end
@@ -32,6 +32,8 @@
32
32
  # To add the nullify method to all datasets on a single database:
33
33
  #
34
34
  # DB.extension(:null_dataset)
35
+ #
36
+ # Related modules: Sequel::Dataset::Nullifiable, Sequel::Dataset::NullDataset
35
37
 
36
38
  #
37
39
  module Sequel
@@ -27,6 +27,8 @@
27
27
  # is probably the desired behavior if you are using this extension:
28
28
  #
29
29
  # DB.extension(:pagination)
30
+ #
31
+ # Related modules: Sequel::DatasetPagination, Sequel::Dataset::Pagination
30
32
 
31
33
  #
32
34
  module Sequel
@@ -41,10 +41,6 @@
41
41
  # See the {schema modification guide}[rdoc-ref:doc/schema_modification.rdoc]
42
42
  # for details on using postgres array columns in CREATE/ALTER TABLE statements.
43
43
  #
44
- # If you are not using the native postgres or jdbc/postgresql adapter and are using array
45
- # types as model column values you probably should use the the pg_typecast_on_load plugin
46
- # if the column values are returned as a string.
47
- #
48
44
  # This extension by default includes handlers for array types for
49
45
  # all scalar types that the native postgres adapter handles. It
50
46
  # also makes it easy to add support for other array types. In
@@ -84,6 +80,8 @@
84
80
  # operators, look into the pg_array_ops extension.
85
81
  #
86
82
  # This extension requires the strscan and delegate libraries.
83
+ #
84
+ # Related module: Sequel::Postgres::PGArray
87
85
 
88
86
  require 'delegate'
89
87
  require 'strscan'
@@ -66,6 +66,8 @@
66
66
  #
67
67
  # In order for #hstore to automatically wrap the returned value correctly in
68
68
  # an HStoreOp, you need to load the pg_hstore_ops extension.
69
+ #
70
+ # Related module: Sequel::Postgres::ArrayOp
69
71
 
70
72
  #
71
73
  module Sequel