sequel 4.43.0 → 4.44.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +40 -0
  3. data/doc/active_record.rdoc +2 -2
  4. data/doc/code_order.rdoc +15 -0
  5. data/doc/dataset_filtering.rdoc +1 -1
  6. data/doc/model_dataset_method_design.rdoc +132 -0
  7. data/doc/opening_databases.rdoc +2 -2
  8. data/doc/release_notes/4.44.0.txt +125 -0
  9. data/lib/sequel/adapters/jdbc.rb +5 -1
  10. data/lib/sequel/adapters/jdbc/as400.rb +1 -1
  11. data/lib/sequel/adapters/postgres.rb +23 -14
  12. data/lib/sequel/core.rb +1 -1
  13. data/lib/sequel/database/schema_generator.rb +27 -0
  14. data/lib/sequel/dataset/actions.rb +1 -1
  15. data/lib/sequel/dataset/query.rb +5 -1
  16. data/lib/sequel/extensions/eval_inspect.rb +4 -4
  17. data/lib/sequel/extensions/implicit_subquery.rb +48 -0
  18. data/lib/sequel/extensions/to_dot.rb +1 -1
  19. data/lib/sequel/model.rb +3 -5
  20. data/lib/sequel/model/associations.rb +107 -4
  21. data/lib/sequel/model/base.rb +98 -12
  22. data/lib/sequel/model/dataset_module.rb +1 -1
  23. data/lib/sequel/plugins/active_model.rb +11 -3
  24. data/lib/sequel/plugins/association_dependencies.rb +7 -0
  25. data/lib/sequel/plugins/auto_validations.rb +10 -0
  26. data/lib/sequel/plugins/blacklist_security.rb +13 -4
  27. data/lib/sequel/plugins/class_table_inheritance.rb +11 -0
  28. data/lib/sequel/plugins/column_conflicts.rb +8 -0
  29. data/lib/sequel/plugins/composition.rb +12 -2
  30. data/lib/sequel/plugins/constraint_validations.rb +12 -0
  31. data/lib/sequel/plugins/csv_serializer.rb +9 -0
  32. data/lib/sequel/plugins/defaults_setter.rb +6 -0
  33. data/lib/sequel/plugins/force_encoding.rb +4 -3
  34. data/lib/sequel/plugins/hook_class_methods.rb +6 -0
  35. data/lib/sequel/plugins/input_transformer.rb +9 -0
  36. data/lib/sequel/plugins/insert_returning_select.rb +8 -0
  37. data/lib/sequel/plugins/instance_hooks.rb +4 -3
  38. data/lib/sequel/plugins/json_serializer.rb +9 -0
  39. data/lib/sequel/plugins/lazy_attributes.rb +7 -0
  40. data/lib/sequel/plugins/many_through_many.rb +13 -2
  41. data/lib/sequel/plugins/nested_attributes.rb +7 -0
  42. data/lib/sequel/plugins/pg_array_associations.rb +18 -2
  43. data/lib/sequel/plugins/pg_row.rb +3 -3
  44. data/lib/sequel/plugins/pg_typecast_on_load.rb +7 -0
  45. data/lib/sequel/plugins/prepared_statements.rb +2 -2
  46. data/lib/sequel/plugins/prepared_statements_safe.rb +7 -0
  47. data/lib/sequel/plugins/serialization.rb +9 -0
  48. data/lib/sequel/plugins/single_table_inheritance.rb +13 -1
  49. data/lib/sequel/plugins/subclasses.rb +15 -1
  50. data/lib/sequel/plugins/touch.rb +7 -0
  51. data/lib/sequel/plugins/tree.rb +7 -0
  52. data/lib/sequel/plugins/typecast_on_load.rb +7 -0
  53. data/lib/sequel/plugins/update_refresh.rb +24 -13
  54. data/lib/sequel/plugins/validation_class_methods.rb +13 -0
  55. data/lib/sequel/sql.rb +28 -0
  56. data/lib/sequel/version.rb +1 -1
  57. data/spec/adapters/postgres_spec.rb +18 -15
  58. data/spec/core/dataset_spec.rb +5 -0
  59. data/spec/core/expression_filters_spec.rb +33 -0
  60. data/spec/extensions/active_model_spec.rb +15 -1
  61. data/spec/extensions/association_dependencies_spec.rb +8 -0
  62. data/spec/extensions/auto_validations_spec.rb +8 -0
  63. data/spec/extensions/blacklist_security_spec.rb +6 -0
  64. data/spec/extensions/class_table_inheritance_spec.rb +9 -0
  65. data/spec/extensions/column_conflicts_spec.rb +6 -0
  66. data/spec/extensions/composition_spec.rb +8 -0
  67. data/spec/extensions/constraint_validations_plugin_spec.rb +12 -0
  68. data/spec/extensions/csv_serializer_spec.rb +7 -0
  69. data/spec/extensions/defaults_setter_spec.rb +7 -0
  70. data/spec/extensions/force_encoding_spec.rb +14 -0
  71. data/spec/extensions/hook_class_methods_spec.rb +10 -0
  72. data/spec/extensions/implicit_subquery_spec.rb +60 -0
  73. data/spec/extensions/input_transformer_spec.rb +10 -0
  74. data/spec/extensions/insert_returning_select_spec.rb +6 -0
  75. data/spec/extensions/json_serializer_spec.rb +7 -0
  76. data/spec/extensions/lazy_attributes_spec.rb +6 -0
  77. data/spec/extensions/many_through_many_spec.rb +44 -0
  78. data/spec/extensions/nested_attributes_spec.rb +5 -0
  79. data/spec/extensions/pg_array_associations_spec.rb +46 -0
  80. data/spec/extensions/pg_typecast_on_load_spec.rb +5 -0
  81. data/spec/extensions/prepared_statements_safe_spec.rb +5 -0
  82. data/spec/extensions/serialization_spec.rb +7 -0
  83. data/spec/extensions/single_table_inheritance_spec.rb +19 -2
  84. data/spec/extensions/subclasses_spec.rb +13 -0
  85. data/spec/extensions/to_dot_spec.rb +1 -1
  86. data/spec/extensions/touch_spec.rb +6 -0
  87. data/spec/extensions/tree_spec.rb +6 -0
  88. data/spec/extensions/typecast_on_load_spec.rb +6 -0
  89. data/spec/extensions/update_refresh_spec.rb +7 -1
  90. data/spec/extensions/validation_class_methods_spec.rb +13 -0
  91. data/spec/model/association_reflection_spec.rb +177 -0
  92. data/spec/model/associations_spec.rb +16 -0
  93. data/spec/model/dataset_methods_spec.rb +59 -0
  94. data/spec/model/model_spec.rb +59 -0
  95. metadata +8 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f5f04eb2ea922da521a4e67f1cf8fd4346d034df
4
- data.tar.gz: 39ea6ce91593a98832c7e53c03c4ec1ea5607fb8
3
+ metadata.gz: f527b0eacda71627a6bfed400fce411509a1ce77
4
+ data.tar.gz: ca7bf3f62b76c4618d59b825d270cca436d54d54
5
5
  SHA512:
6
- metadata.gz: e449fabbb5ec97c98df590e9afcd6378e392513509dfcb95fbc503cf668029bf31a3e120adf906af3040c695e7998f8ab24d7883409965379ef242498b988b3f
7
- data.tar.gz: 2829985707e14c79044a296d34fa62300053e1a279f232da1aa08cd93217eb001ccb78117c049a287b6c370091b8dfb1b5e3a7424d18fdff5ff289081fa2ec58
6
+ metadata.gz: fb226f39b19f50d82d6eb712c685bb614f77e68e266c898c049bbb1c6345b6347cd661b06a1986be7f1c22add658036dc4f3960966bc2cea30e26e57e0554a36
7
+ data.tar.gz: 4cb5d97a25593e77119726bba294145c8b5f3ee0503f12e52389c4fbe6f5a4cd4f2b6d7c2f5d592f5a58d3cc827e58824e4f03c282dff0146c2760142401163b
data/CHANGELOG CHANGED
@@ -1,3 +1,43 @@
1
+ === 4.44.0 (2017-03-01)
2
+
3
+ * Add where_all, where_each, where_single_value model dataset methods, optimized for frozen datasets (jeremyevans)
4
+
5
+ * Add eager method to dataset_module (jeremyevans)
6
+
7
+ * Add implicit_subquery extension, for implicitly using a subquery for datasets using raw SQL when calling dataset methods that modify SQL (jeremyevans)
8
+
9
+ * Make Dataset#from_self keep the columns from the current dataset if present (jeremyevans)
10
+
11
+ * Add implicit_subquery extension, implicitly using subqueries for dataset methods if the current dataset uses raw SQL (jeremyevans)
12
+
13
+ * Make SQL::ValueList#inspect show that it is a value list (jeremyevans)
14
+
15
+ * Make LiteralString#inspect show that it is a literal string (jeremyevans)
16
+
17
+ * Make Model::Associations::AssociationReflection#inspect show reflection class and guess at association definition line (jeremyevans)
18
+
19
+ * Make SQLTime#inspect show it is an SQLTime instance, and only the time component (jeremyevans)
20
+
21
+ * Make SQL::Blob#inspect show that it is a blob, the number of bytes, and some or all of the content (jeremyevans)
22
+
23
+ * Make plugins not modify the constant namespace for the model class that uses them (jeremyevans)
24
+
25
+ * Do not modify encoding of SQL::Blob instances in force_encoding plugin (jeremyevans)
26
+
27
+ * Add Model.freeze_descendents to subclasses plugin, for easier finalizing associations/freezing of descendent classes (jeremyevans)
28
+
29
+ * Add Model.finalize_associations method for finalizing associations, speeding up some association reflections methods almost 10x (jeremyevans)
30
+
31
+ * Implement Model.freeze such that it can be used in production (jeremyevans)
32
+
33
+ * Recognize another disconnect error in the jdbc/as400 adapter (perlun) (#1300)
34
+
35
+ * Correctly handle conversion of false values when typecasting PostgreSQL arrays (mistoo) (#1299)
36
+
37
+ * Raise error if the postgres adapter attempts to load an incompatible version of sequel_pg (mahlonsmith) (#1298)
38
+
39
+ * Fix jdbc adapter so basic_type_convertor_map is not shared between instances, work with Database#freeze (jeremyevans)
40
+
1
41
  === 4.43.0 (2017-02-01)
2
42
 
3
43
  * Make jdbc/postgresql adapter work if pg_hstore extension is loaded first (jeremyevans) (#1296)
@@ -297,7 +297,7 @@ Sequel doesn't have a <tt>has_many :through</tt> association, instead you can us
297
297
 
298
298
  Sequel doesn't come with support for polymorphic associations. Using polymorphic associations is generally bad from a database design perspective, as it violates referential integrity. If you have an old database and must have polymorphic associations, there is an external +sequel_polymorphic+ plugin that can handle them, just by using the standard association options provided by Sequel.
299
299
 
300
- Sequel doesn't directly support creating a bunch of associated objects and delaying saving them to the database until the main object is saved, like you can with the <tt>association.build</tt> methods in ActiveRecord. You can use +before_save or +after_save+ hooks, or the +nested_attributes+ or +instance_hooks+ plugins to get similar support.
300
+ Sequel doesn't directly support creating a bunch of associated objects and delaying saving them to the database until the main object is saved, like you can with the <tt>association.build</tt> methods in ActiveRecord. You can use +before_save+ or +after_save+ hooks, or the +nested_attributes+ or +instance_hooks+ plugins to get similar support.
301
301
 
302
302
  Sequel supports the same basic association hooks/callbacks as ActiveRecord. It also supports <tt>:after_load</tt>, which is run after the associated objects are loaded. For <tt>*_to_one</tt> associations, it supports +before_set+ and +after_set+ hooks, since a setter method is used instead of an add/remove method pair.
303
303
 
@@ -316,7 +316,7 @@ Association datasets are just like any other Sequel dataset, in that you can fil
316
316
 
317
317
  gold_albums = artist.albums_dataset.where{copies_sold > 500000}.order(:name).all
318
318
 
319
- Sequel caches associated objects similarly to ActiveRecord, and you can skip the cache by passing +true+ to the association method, just like ActiveRecord.
319
+ Sequel caches associated objects similarly to ActiveRecord, and you can skip the cache by passing +:reload=>true+ to the association method.
320
320
 
321
321
  === Eager Loading
322
322
 
@@ -83,6 +83,21 @@ unless the model classes are very simple. Example:
83
83
 
84
84
  Dir['./models/*.rb'].each{|f| require f}
85
85
 
86
+ == Finalize Associations and Freeze Model Classes and Database
87
+
88
+ After all the models have been setup, you can finalize the associations.
89
+ This can speed up association reflection methods by doing a lookup in
90
+ advance to find the associated class, and cache related association
91
+ information in the association itself.
92
+
93
+ Additionally, in production and testing, you should freeze the
94
+ model classes and Database instance, so that you can detect
95
+ unsafe runtime modification of the configuration:
96
+
97
+ model_classes.each(&:finalize_associations)
98
+ model_classes.each(&:freeze)
99
+ DB.freeze
100
+
86
101
  == Disconnect If Using Forking Webserver with Code Preloading
87
102
 
88
103
  If you are using a forking webserver such as unicorn or passenger, with
@@ -1,6 +1,6 @@
1
1
  = Dataset Filtering
2
2
 
3
- Sequel is very flexibile when it comes to filtering records. You can specify your conditions as a custom string, as a string with parameters, as a hash of values to compare against, or as ruby code that Sequel translates into SQL expressions.
3
+ Sequel is very flexible when it comes to filtering records. You can specify your conditions as a custom string, as a string with parameters, as a hash of values to compare against, or as ruby code that Sequel translates into SQL expressions.
4
4
 
5
5
  == Filtering using a custom filter string
6
6
 
@@ -0,0 +1,132 @@
1
+ = Model Dataset Method Design Guide
2
+
3
+ How you design your model dataset methods can significantly affect the flexibility of your API for your model classes, as well as the performance. The goal of this guide is to provide an example of how to design your model dataset methods for maximum flexibility and performance.
4
+
5
+ == Flexibility: Use Single Method Per Task
6
+
7
+ In general, it is recommended that you have a single method per task for maximum flexibilty. For example, let's say you need to retrieve all albums released in a given year, ordered by number of units sold descending, and only care about the id, name and number of units sold. One way to do this is in your application code (outside the model), you can
8
+ call the dataset methods directly:
9
+
10
+ Album.
11
+ select(:id, :name, :copies_sold).
12
+ where(:release_year=>params[:year].to_i).
13
+ order(Sequel.desc(:copies_sold)).
14
+ all
15
+
16
+ One issue with this design is that it ties you to your current database schema, and will make it necessary to change your application code if your schema changes. In general, it is better to encapsulate your code into a dataset method (or a class method, but a dataset method is more flexible):
17
+
18
+ class Album < Sequel::Model
19
+ dataset_module do
20
+ def all_albums_released_in_year(year)
21
+ select(:id, :name, :copies_sold).
22
+ where(:release_year=>year).
23
+ order(Sequel.desc(:copies_sold)).
24
+ all
25
+ end
26
+ end
27
+ end
28
+
29
+ Then your application code just needs to call your dataset method:
30
+
31
+ Album.all_albums_released_in_year(params[:year].to_i)
32
+
33
+ The advantage of this approach is that you can change your schema at any point in the future, and you should only need to change your model code, you should never need to change your application code.
34
+
35
+ == Performance
36
+
37
+ After designing your dataset methods for flexibility, stop. Don't worry about performance until you need to worry about performance. However, assuming you have profiled your application and profiling shows you can benefit from optimizing the above method, you can then consider the performance impact of future design choices.
38
+
39
+ First, considering that the root cause of the performance issue may not be at the Sequel level, it may be at the database itself. Use +EXPLAIN+ or the equivalent to analyze the query plan for the query in use, and see if there is something you can do to optimize it, such as adding an appropriate index.
40
+
41
+ Second, assuming the performance issue is at the Sequel level, you need to understand that one of the best ways to improve performance in most ruby code is to reduce the number of objects allocated. Here is the above code with comments showing datasets allocated:
42
+
43
+ def all_albums_released_in_year(year)
44
+ select(:id, :name, :copies_sold). # new dataset allocated
45
+ where(:release_year=>year). # new dataset allocated
46
+ order(Sequel.desc(:copies_sold)). # new dataset allocated
47
+ all
48
+ end
49
+
50
+ Third, you need to understand that Sequel has optimizations specifically designed to reduce the number of objects allocated, by caching intermediate data structures. However, these optimizations apply only to frozen datasets, and can only provide a benefit if related methods are called multiple times on the same dataset. One easy way to do this is to use frozen model datasets is to use the +freeze_datasets+ Database extension before creating your model class:
51
+
52
+ DB.extension :freeze_datasets
53
+
54
+ Note that freezing datasets is not enough to enable caching in this case. The reason for this is that +select+, +where+, and +order+ can potentially receive arbitrary arguments, and enabling caching for them could easily lead to unbounded cache size (denial of service due to memory exhaustion). You need to signal to Sequel that particular arguments to these methods should be cached, and you can do that by calling methods inside +dataset_module+ blocks such as +select+ and +order+. These methods will add dataset methods to the model that can use caching to optimize performance. Here is an example using these methods:
55
+
56
+ class Album < Sequel::Model
57
+ dataset_module do
58
+ select :with_name_and_units, :id, :name, :copies_sold
59
+ order :by_units_sold, Sequel.desc(:copies_sold)
60
+
61
+ def all_albums_released_in_year(year)
62
+ with_name_and_units.
63
+ by_units_sold.
64
+ where(:release_year=>year).
65
+ all
66
+ end
67
+ end
68
+ end
69
+
70
+ Performance aside, this does provide a slightly nicer and more readable internal API, though naming such methods can be problematic.
71
+
72
+ By calling +select+ and +order+ here, Sequel expects that the created dataset methods may be called more than once on the same dataset, and it knows that the arguments to the underlying +select+ and +order+ methods are fixed, so it can cache the resulting datasets. Let's comment the above example with dataset allocations:
73
+
74
+ def all_albums_released_in_year(year)
75
+ with_name_and_units. # cached dataset returned
76
+ by_units_sold. # cached dataset returned
77
+ where(:release_year=>year). # new dataset allocated
78
+ all
79
+ end
80
+
81
+ Note that the order of methods here is important. If you instead change the method chain to filter the dataset first, then no caching happens:
82
+
83
+ def all_albums_released_in_year(year)
84
+ where(:release_year=>year). # new dataset allocated
85
+ with_name_and_units. # new dataset allocated
86
+ by_units_sold. # new dataset allocated
87
+ all
88
+ end
89
+
90
+ This is because any time a new, uncached dataset is returned by a dataset method, all subsequent methods in the method chain cannot benefit from caching.
91
+
92
+ Usually, when you are designing methods to process data based on user input, the user input affects the rows selected, and not the columns selected or the order in which the rows are returned. Sequel is aware of this and has model dataset methods that specifically take user input (arguments), interpret them as a filter condition and either:
93
+
94
+ * Return all matching rows in an array (+where_all+)
95
+ * Iterate over all matching rows (+where_each+)
96
+ * Return first matching row (+first+)
97
+ * Return first column in first matching row, assumes only a single column is selected (+where_single_value+)
98
+
99
+ After calling these methods on a cached dataset a number of times (currently 3), Sequel will automatically build an optimized loader, cache it, and use it for future loads. So the above example changes to:
100
+
101
+ def all_albums_released_in_year(year)
102
+ with_name_and_units. # cached dataset returned
103
+ by_units_sold. # cached dataset returned
104
+ where_all(:release_year=>year) # cached loader used
105
+ end
106
+
107
+ This can significantly improve performance, up to 3x for complex method chains that only return a few rows.
108
+
109
+ So the general advice on designing datasets for performance is:
110
+
111
+ * Use frozen datasets
112
+ * Use +dataset_module+ methods to create named dataset methods that return cached datasets
113
+ * If any filtering is to be done, have it done last using +where_all+, +where_each+, +first+, or +where_single_value+.
114
+
115
+ By following this advice, you can significantly increase the performance of your model dataset code.
116
+
117
+ === Further Increasing Performance
118
+
119
+ The best way to further increase performance at the Sequel level is to switch to using prepared statements. This does require more significant changes to the API. Here's an example using prepared statements:
120
+
121
+ class Album < Sequel::Model
122
+ ALBUMS_RELEASED_IN_YEAR = select(:id, :name, :copies_sold).
123
+ where(:release_year=>:$year).
124
+ order(Sequel.desc(:copies_sold)).
125
+ prepare(:all, :all_albums_released_in_year)
126
+
127
+ def self.all_albums_released_in_year(year)
128
+ ALBUMS_RELEASED_IN_YEAR.call(:year=>year)
129
+ end
130
+ end
131
+
132
+ Note that when using prepared statements, you need to use a class method instead of a dataset method, as the SQL for the prepared statement must be fixed for the class. This limits the flexibility of the method, since you can no longer call it on arbitrary datasets on the class.
@@ -195,8 +195,8 @@ Houses Sequel's JDBC support when running on JRuby.
195
195
  Support for individual database types is done using subadapters.
196
196
  There are currently subadapters for PostgreSQL, MySQL, SQLite, H2, HSQLDB, Derby,
197
197
  Oracle, MSSQL, JTDS, AS400, Progress, Firebird, Informix, and DB2.
198
- For PostgreSQL, MySQL, SQLite, H2, HSQLDB, Derby, and JTDS,
199
- this can use the jdbc-* gem, for the others you need to have the .jar in your CLASSPATH
198
+ For PostgreSQL, MySQL, SQLite, H2, HSQLDB, Derby, AS400 and JTDS,
199
+ this can use the `jdbc-*` gem, for the others you need to have the `.jar` in your CLASSPATH
200
200
  or load the Java class manually before calling Sequel.connect.
201
201
 
202
202
  You just use the JDBC connection string directly, which can be specified
@@ -0,0 +1,125 @@
1
+ = New Features
2
+
3
+ * Model.freeze is now supported and recommended in production and
4
+ during testing. It freezes all class-level metadata, preventing
5
+ possible thread-safety issues at runtime.
6
+
7
+ * Model.finalize_associations has been added, speeding up some
8
+ association reflection methods by about 10x. This method
9
+ should be called after all associated models have been loaded.
10
+ This can speed up the retrieval of associated objects for small
11
+ datasets by 5-10%.
12
+
13
+ One advantage of using this is it will raise an exception if it
14
+ recognizes that any of your associations are not defined
15
+ correctly, such as referencing an associated class that doesn't
16
+ exist.
17
+
18
+ * Model.freeze_descendents has been added to the subclasses plugin.
19
+ This method finalizes associations for all descendent classes,
20
+ then freezes the descendent class. It's designed to make it
21
+ easy to freeze all model classes in use:
22
+
23
+ Sequel::Model.plugin :subclasses
24
+ Dir['./models/*.rb'].each{|f| require f}
25
+ Sequel::Model.freeze_descendents
26
+
27
+ * An implicit_subquery dataset extension has been added, which
28
+ implicitly uses a subquery if you have a dataset with raw SQL and
29
+ you call a method that would modify the SQL used:
30
+
31
+ DB['SELECT * FROM foo'].where(:bar=>1)
32
+ # SELECT * FROM foo
33
+
34
+ DB.extension :implicit_subquery
35
+ DB['SELECT * FROM foo'].where(:bar=>1)
36
+ # SELECT * FROM (SELECT * FROM foo) AS t1 WHERE (bar = 1)
37
+
38
+ * Model datasets now have where_all, where_each, and
39
+ where_single_value methods for returning data:
40
+
41
+ class Album < Sequel::Model; end
42
+ Album.where_all(:id=>[1,2,3])
43
+ # => [Album[1], Album[3], Album[2]]
44
+
45
+ Album.where_each(:id=>[1,2,3]) do |album|
46
+ # ...
47
+ end
48
+
49
+ Album.select(:name).where_single_value(:id=>1)
50
+ # "Album's Name"
51
+
52
+ These methods are designed for use by other dataset methods you
53
+ define, and are optimized for frozen datasets if the methods will
54
+ be called multiple times on the same dataset. where_all and
55
+ where_each can increase performance by up to 40% for small datasets
56
+ compared to where.all and where.each. where_single_value can be up
57
+ to twice as fast as where.single_value.
58
+
59
+ * Model.dataset_module now supports an eager method for eager loading:
60
+
61
+ class Album < Sequel::Model
62
+ many_to_one :artist
63
+
64
+ dataset_module do
65
+ eager :with_artist, :artist
66
+ end
67
+ end
68
+
69
+ Album.with_artist.all # eagerly loads artist association
70
+
71
+ = Other Improvements
72
+
73
+ * The jdbc adapter now supports Database#freeze. Possible
74
+ thread-safety issues when initializing multiple jdbc Database
75
+ instances in separate threads at the same time have been fixed.
76
+
77
+ * The postgres adapter now raises an exception if it recognizes that
78
+ the loaded version of sequel_pg is incompatible.
79
+
80
+ * Sequel classes that are subclasses of core classes now define
81
+ custom #inspect methods so instances can easily be differentiated from
82
+ core class instances. For example:
83
+
84
+ Sequel::SQL::Blob.new('a')
85
+ # => #<Sequel::SQL::Blob:0xa6f3a3c3710 bytes=1 content="a">
86
+ Sequel::SQLTime.now
87
+ # => #<Sequel::SQLTime 10:03:06>
88
+ Sequel::LiteralString.new("foo")
89
+ # => #<Sequel::LiteralString "foo">
90
+ class Album < Sequel::Model; end
91
+ Album.many_to_one :artist
92
+ # => #<Sequel::Model::Associations::ManyToOneAssociationReflection Album.many_to_one :artist>
93
+ Sequel::SQL::ValueList.new([[1,2]])
94
+ # => #<Sequel::SQL::ValueList [[1, 2]]>
95
+
96
+ * Dataset#from_self now copies the columns from the current dataset
97
+ if they are present, since wrapping a dataset in a subquery should
98
+ not change the columns returned.
99
+
100
+ * On PostgreSQL, array type conversion now correctly handles false
101
+ values.
102
+
103
+ * Another disconnect error is now recognized by the jdbc/as400
104
+ adapter.
105
+
106
+ * Modifications to Sequel::Model::Associations::ASSOCIATION_TYPES
107
+ are now thread safe, fixing issues if separate threads attempt
108
+ to load separate model plugins that modify this hash.
109
+
110
+ * The force_encoding plugin no longer modifies the encoding of
111
+ Sequel::SQL::Blob instances.
112
+
113
+ * Many plugins were updated so they no longer add constants to the
114
+ namespace of the model that loads them.
115
+
116
+ = Backwards Compatibility
117
+
118
+ * Maintainers of external model plugins should update their
119
+ code to support Model.freeze.
120
+
121
+ = Upcoming Deprecation
122
+
123
+ * Starting in Sequel 4.45.0, Sequel will be adding deprecation
124
+ warnings for features that will be removed or where behavior will
125
+ change in Sequel 5.
@@ -137,6 +137,10 @@ module Sequel
137
137
  BASIC_MAP[types.const_get(type)] = o.method(meth)
138
138
  MAP[types.const_get(type)] = o.method(:"Ruby#{meth}")
139
139
  end
140
+
141
+ MAP.freeze
142
+ BASIC_MAP.freeze
143
+ INSTANCE.freeze
140
144
  end
141
145
 
142
146
  # JDBC Databases offer a fairly uniform interface that does not change
@@ -672,7 +676,7 @@ module Sequel
672
676
  # that runs queries works correctly. This cannot be overriding in subadapters,
673
677
  def setup_type_convertor_map_early
674
678
  @type_convertor_map = TypeConvertor::MAP.merge(Java::JavaSQL::Types::TIMESTAMP=>timestamp_convertor)
675
- @basic_type_convertor_map = TypeConvertor::BASIC_MAP
679
+ @basic_type_convertor_map = TypeConvertor::BASIC_MAP.dup
676
680
  end
677
681
 
678
682
  # Yield a new statement object, and ensure that it is closed before returning.
@@ -42,7 +42,7 @@ module Sequel
42
42
  private
43
43
 
44
44
  def disconnect_error?(exception, opts)
45
- super || exception.message =~ /\AThe connection does not exist\./
45
+ super || exception.message =~ /\A(The connection does not exist|Communication link failure)\./
46
46
  end
47
47
 
48
48
  # Use JDBC connection's setAutoCommit to false to start transactions
@@ -8,9 +8,9 @@ begin
8
8
  # Work around postgres-pr 0.7.0+ which ships with a pg.rb file
9
9
  raise LoadError unless defined?(PGconn::CONNECTION_OK)
10
10
 
11
- SEQUEL_POSTGRES_USES_PG = true
11
+ Sequel::Postgres::USES_PG = true
12
12
  rescue LoadError => e
13
- SEQUEL_POSTGRES_USES_PG = false
13
+ Sequel::Postgres::USES_PG = false
14
14
  begin
15
15
  require 'postgres'
16
16
  # Attempt to get uniform behavior for the PGconn object no matter
@@ -101,6 +101,13 @@ module Sequel
101
101
  def bytea(s) ::Sequel::SQL::Blob.new(Adapter.unescape_bytea(s)) end
102
102
  end.new.method(:bytea)
103
103
 
104
+ if Sequel::Postgres::USES_PG
105
+ # Whether the given sequel_pg version integer is supported.
106
+ def self.sequel_pg_version_supported?(version)
107
+ version >= 10617
108
+ end
109
+ end
110
+
104
111
  @use_iso_date_format = true
105
112
 
106
113
  class << self
@@ -139,7 +146,7 @@ module Sequel
139
146
  # Hash of prepared statements for this connection. Keys are
140
147
  # string names of the server side prepared statement, and values
141
148
  # are SQL strings.
142
- attr_reader(:prepared_statements) if SEQUEL_POSTGRES_USES_PG
149
+ attr_reader(:prepared_statements) if USES_PG
143
150
 
144
151
  # Raise a Sequel::DatabaseDisconnectError if a one of the disconnect
145
152
  # error classes is raised, or a PGError is raised and the connection
@@ -230,7 +237,7 @@ module Sequel
230
237
  # are only supported if the pg driver is used.
231
238
  def connect(server)
232
239
  opts = server_opts(server)
233
- if SEQUEL_POSTGRES_USES_PG
240
+ if USES_PG
234
241
  connection_params = {
235
242
  :host => opts[:host],
236
243
  :port => opts[:port] || 5432,
@@ -312,7 +319,7 @@ module Sequel
312
319
  nil
313
320
  end
314
321
 
315
- if SEQUEL_POSTGRES_USES_PG && Object.const_defined?(:PG) && ::PG.const_defined?(:Constants) && ::PG::Constants.const_defined?(:PG_DIAG_SCHEMA_NAME)
322
+ if USES_PG && Object.const_defined?(:PG) && ::PG.const_defined?(:Constants) && ::PG::Constants.const_defined?(:PG_DIAG_SCHEMA_NAME)
316
323
  # Return a hash of information about the related PGError (or Sequel::DatabaseError that
317
324
  # wraps a PGError), with the following entries:
318
325
  #
@@ -342,7 +349,7 @@ module Sequel
342
349
  synchronize(opts[:server]){|conn| check_database_errors{_execute(conn, sql, opts, &block)}}
343
350
  end
344
351
 
345
- if SEQUEL_POSTGRES_USES_PG
352
+ if USES_PG
346
353
  # +copy_table+ uses PostgreSQL's +COPY TO STDOUT+ SQL statement to return formatted
347
354
  # results directly to the caller. This method is only supported if pg is the
348
355
  # underlying ruby driver. This method should only be called if you want
@@ -719,7 +726,7 @@ module Sequel
719
726
  clone(:where=>Sequel.lit(['CURRENT OF '], Sequel.identifier(cursor_name)))
720
727
  end
721
728
 
722
- if SEQUEL_POSTGRES_USES_PG
729
+ if USES_PG
723
730
 
724
731
  PREPARED_ARG_PLACEHOLDER = LiteralString.new('$').freeze
725
732
 
@@ -854,15 +861,17 @@ module Sequel
854
861
  end
855
862
  end
856
863
 
857
- if SEQUEL_POSTGRES_USES_PG && !ENV['NO_SEQUEL_PG']
864
+ if Sequel::Postgres::USES_PG && !ENV['NO_SEQUEL_PG']
858
865
  begin
859
866
  require 'sequel_pg'
860
- rescue LoadError
861
- if RUBY_PLATFORM =~ /mingw|mswin/
862
- begin
863
- require "#{RUBY_VERSION[0...3]}/sequel_pg"
864
- rescue LoadError
865
- end
867
+ if defined?(Gem) &&
868
+ (sequel_pg_spec = Gem.loaded_specs['sequel_pg'] rescue nil) &&
869
+ (sequel_pg_spec.version < Gem::Version.new('1.6.17'))
870
+ raise Sequel::Error, "the installed sequel_pg is too old, please update to at least sequel_pg-1.6.17"
866
871
  end
872
+ rescue LoadError
867
873
  end
868
874
  end
875
+
876
+ # SEQUEL5: Remove
877
+ SEQUEL_POSTGRES_USES_PG = Sequel::Postgres::USES_PG