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.
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