sequel 4.43.0 → 4.44.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +40 -0
- data/doc/active_record.rdoc +2 -2
- data/doc/code_order.rdoc +15 -0
- data/doc/dataset_filtering.rdoc +1 -1
- data/doc/model_dataset_method_design.rdoc +132 -0
- data/doc/opening_databases.rdoc +2 -2
- data/doc/release_notes/4.44.0.txt +125 -0
- data/lib/sequel/adapters/jdbc.rb +5 -1
- data/lib/sequel/adapters/jdbc/as400.rb +1 -1
- data/lib/sequel/adapters/postgres.rb +23 -14
- data/lib/sequel/core.rb +1 -1
- data/lib/sequel/database/schema_generator.rb +27 -0
- data/lib/sequel/dataset/actions.rb +1 -1
- data/lib/sequel/dataset/query.rb +5 -1
- data/lib/sequel/extensions/eval_inspect.rb +4 -4
- data/lib/sequel/extensions/implicit_subquery.rb +48 -0
- data/lib/sequel/extensions/to_dot.rb +1 -1
- data/lib/sequel/model.rb +3 -5
- data/lib/sequel/model/associations.rb +107 -4
- data/lib/sequel/model/base.rb +98 -12
- data/lib/sequel/model/dataset_module.rb +1 -1
- data/lib/sequel/plugins/active_model.rb +11 -3
- data/lib/sequel/plugins/association_dependencies.rb +7 -0
- data/lib/sequel/plugins/auto_validations.rb +10 -0
- data/lib/sequel/plugins/blacklist_security.rb +13 -4
- data/lib/sequel/plugins/class_table_inheritance.rb +11 -0
- data/lib/sequel/plugins/column_conflicts.rb +8 -0
- data/lib/sequel/plugins/composition.rb +12 -2
- data/lib/sequel/plugins/constraint_validations.rb +12 -0
- data/lib/sequel/plugins/csv_serializer.rb +9 -0
- data/lib/sequel/plugins/defaults_setter.rb +6 -0
- data/lib/sequel/plugins/force_encoding.rb +4 -3
- data/lib/sequel/plugins/hook_class_methods.rb +6 -0
- data/lib/sequel/plugins/input_transformer.rb +9 -0
- data/lib/sequel/plugins/insert_returning_select.rb +8 -0
- data/lib/sequel/plugins/instance_hooks.rb +4 -3
- data/lib/sequel/plugins/json_serializer.rb +9 -0
- data/lib/sequel/plugins/lazy_attributes.rb +7 -0
- data/lib/sequel/plugins/many_through_many.rb +13 -2
- data/lib/sequel/plugins/nested_attributes.rb +7 -0
- data/lib/sequel/plugins/pg_array_associations.rb +18 -2
- data/lib/sequel/plugins/pg_row.rb +3 -3
- data/lib/sequel/plugins/pg_typecast_on_load.rb +7 -0
- data/lib/sequel/plugins/prepared_statements.rb +2 -2
- data/lib/sequel/plugins/prepared_statements_safe.rb +7 -0
- data/lib/sequel/plugins/serialization.rb +9 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +13 -1
- data/lib/sequel/plugins/subclasses.rb +15 -1
- data/lib/sequel/plugins/touch.rb +7 -0
- data/lib/sequel/plugins/tree.rb +7 -0
- data/lib/sequel/plugins/typecast_on_load.rb +7 -0
- data/lib/sequel/plugins/update_refresh.rb +24 -13
- data/lib/sequel/plugins/validation_class_methods.rb +13 -0
- data/lib/sequel/sql.rb +28 -0
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +18 -15
- data/spec/core/dataset_spec.rb +5 -0
- data/spec/core/expression_filters_spec.rb +33 -0
- data/spec/extensions/active_model_spec.rb +15 -1
- data/spec/extensions/association_dependencies_spec.rb +8 -0
- data/spec/extensions/auto_validations_spec.rb +8 -0
- data/spec/extensions/blacklist_security_spec.rb +6 -0
- data/spec/extensions/class_table_inheritance_spec.rb +9 -0
- data/spec/extensions/column_conflicts_spec.rb +6 -0
- data/spec/extensions/composition_spec.rb +8 -0
- data/spec/extensions/constraint_validations_plugin_spec.rb +12 -0
- data/spec/extensions/csv_serializer_spec.rb +7 -0
- data/spec/extensions/defaults_setter_spec.rb +7 -0
- data/spec/extensions/force_encoding_spec.rb +14 -0
- data/spec/extensions/hook_class_methods_spec.rb +10 -0
- data/spec/extensions/implicit_subquery_spec.rb +60 -0
- data/spec/extensions/input_transformer_spec.rb +10 -0
- data/spec/extensions/insert_returning_select_spec.rb +6 -0
- data/spec/extensions/json_serializer_spec.rb +7 -0
- data/spec/extensions/lazy_attributes_spec.rb +6 -0
- data/spec/extensions/many_through_many_spec.rb +44 -0
- data/spec/extensions/nested_attributes_spec.rb +5 -0
- data/spec/extensions/pg_array_associations_spec.rb +46 -0
- data/spec/extensions/pg_typecast_on_load_spec.rb +5 -0
- data/spec/extensions/prepared_statements_safe_spec.rb +5 -0
- data/spec/extensions/serialization_spec.rb +7 -0
- data/spec/extensions/single_table_inheritance_spec.rb +19 -2
- data/spec/extensions/subclasses_spec.rb +13 -0
- data/spec/extensions/to_dot_spec.rb +1 -1
- data/spec/extensions/touch_spec.rb +6 -0
- data/spec/extensions/tree_spec.rb +6 -0
- data/spec/extensions/typecast_on_load_spec.rb +6 -0
- data/spec/extensions/update_refresh_spec.rb +7 -1
- data/spec/extensions/validation_class_methods_spec.rb +13 -0
- data/spec/model/association_reflection_spec.rb +177 -0
- data/spec/model/associations_spec.rb +16 -0
- data/spec/model/dataset_methods_spec.rb +59 -0
- data/spec/model/model_spec.rb +59 -0
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f527b0eacda71627a6bfed400fce411509a1ce77
|
4
|
+
data.tar.gz: ca7bf3f62b76c4618d59b825d270cca436d54d54
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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)
|
data/doc/active_record.rdoc
CHANGED
@@ -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
|
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
|
|
data/doc/code_order.rdoc
CHANGED
@@ -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
|
data/doc/dataset_filtering.rdoc
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
= Dataset Filtering
|
2
2
|
|
3
|
-
Sequel is very
|
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.
|
data/doc/opening_databases.rdoc
CHANGED
@@ -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
|
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.
|
data/lib/sequel/adapters/jdbc.rb
CHANGED
@@ -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 =~ /\
|
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
|
-
|
11
|
+
Sequel::Postgres::USES_PG = true
|
12
12
|
rescue LoadError => e
|
13
|
-
|
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
|
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
|
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
|
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
|
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
|
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
|
864
|
+
if Sequel::Postgres::USES_PG && !ENV['NO_SEQUEL_PG']
|
858
865
|
begin
|
859
866
|
require 'sequel_pg'
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
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
|