sequel 2.12.0 → 3.0.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.
- data/CHANGELOG +62 -0
- data/README.rdoc +3 -3
- data/Rakefile +7 -0
- data/doc/advanced_associations.rdoc +44 -0
- data/doc/release_notes/3.0.0.txt +221 -0
- data/lib/sequel/adapters/amalgalite.rb +208 -0
- data/lib/sequel/adapters/db2.rb +3 -0
- data/lib/sequel/adapters/dbi.rb +9 -0
- data/lib/sequel/adapters/do.rb +0 -4
- data/lib/sequel/adapters/firebird.rb +16 -18
- data/lib/sequel/adapters/informix.rb +5 -3
- data/lib/sequel/adapters/jdbc.rb +24 -20
- data/lib/sequel/adapters/jdbc/h2.rb +15 -4
- data/lib/sequel/adapters/mysql.rb +4 -8
- data/lib/sequel/adapters/odbc.rb +0 -4
- data/lib/sequel/adapters/oracle.rb +0 -4
- data/lib/sequel/adapters/shared/mssql.rb +16 -5
- data/lib/sequel/adapters/shared/mysql.rb +87 -86
- data/lib/sequel/adapters/shared/oracle.rb +92 -3
- data/lib/sequel/adapters/shared/postgres.rb +85 -29
- data/lib/sequel/adapters/shared/progress.rb +8 -3
- data/lib/sequel/adapters/shared/sqlite.rb +53 -23
- data/lib/sequel/adapters/sqlite.rb +4 -7
- data/lib/sequel/adapters/utils/unsupported.rb +3 -3
- data/lib/sequel/connection_pool.rb +18 -25
- data/lib/sequel/core.rb +2 -21
- data/lib/sequel/database.rb +60 -44
- data/lib/sequel/database/schema_generator.rb +26 -31
- data/lib/sequel/database/schema_methods.rb +8 -3
- data/lib/sequel/database/schema_sql.rb +114 -28
- data/lib/sequel/dataset.rb +14 -41
- data/lib/sequel/dataset/convenience.rb +31 -54
- data/lib/sequel/dataset/graph.rb +7 -13
- data/lib/sequel/dataset/sql.rb +43 -54
- data/lib/sequel/extensions/inflector.rb +0 -5
- data/lib/sequel/extensions/schema_dumper.rb +238 -0
- data/lib/sequel/metaprogramming.rb +0 -20
- data/lib/sequel/model.rb +1 -2
- data/lib/sequel/model/base.rb +18 -16
- data/lib/sequel/model/inflections.rb +6 -9
- data/lib/sequel/plugins/caching.rb +0 -6
- data/lib/sequel/plugins/hook_class_methods.rb +1 -1
- data/lib/sequel/sql.rb +2 -0
- data/lib/sequel/version.rb +2 -2
- data/spec/adapters/firebird_spec.rb +35 -8
- data/spec/adapters/mysql_spec.rb +173 -266
- data/spec/adapters/oracle_spec.rb +13 -0
- data/spec/adapters/postgres_spec.rb +127 -227
- data/spec/adapters/sqlite_spec.rb +13 -171
- data/spec/core/connection_pool_spec.rb +15 -4
- data/spec/core/core_sql_spec.rb +14 -170
- data/spec/core/database_spec.rb +50 -132
- data/spec/core/dataset_spec.rb +47 -930
- data/spec/core/expression_filters_spec.rb +12 -0
- data/spec/core/schema_generator_spec.rb +37 -45
- data/spec/core/schema_spec.rb +26 -16
- data/spec/core/spec_helper.rb +0 -25
- data/spec/extensions/inflector_spec.rb +0 -3
- data/spec/extensions/schema_dumper_spec.rb +292 -0
- data/spec/extensions/serialization_spec.rb +9 -0
- data/spec/extensions/single_table_inheritance_spec.rb +6 -1
- data/spec/extensions/spec_helper.rb +1 -3
- data/spec/extensions/validation_helpers_spec.rb +4 -4
- data/spec/integration/database_test.rb +18 -0
- data/spec/integration/dataset_test.rb +112 -1
- data/spec/integration/eager_loader_test.rb +70 -9
- data/spec/integration/prepared_statement_test.rb +2 -2
- data/spec/integration/schema_test.rb +76 -27
- data/spec/integration/spec_helper.rb +0 -14
- data/spec/integration/transaction_test.rb +27 -0
- data/spec/model/associations_spec.rb +0 -36
- data/spec/model/base_spec.rb +18 -123
- data/spec/model/hooks_spec.rb +2 -235
- data/spec/model/inflector_spec.rb +15 -115
- data/spec/model/model_spec.rb +0 -120
- data/spec/model/plugins_spec.rb +0 -70
- data/spec/model/record_spec.rb +35 -93
- data/spec/model/spec_helper.rb +0 -27
- data/spec/model/validations_spec.rb +0 -931
- metadata +9 -14
- data/lib/sequel/deprecated.rb +0 -593
- data/lib/sequel/deprecated_migration.rb +0 -91
- data/lib/sequel/model/deprecated.rb +0 -204
- data/lib/sequel/model/deprecated_hooks.rb +0 -103
- data/lib/sequel/model/deprecated_inflector.rb +0 -335
- data/lib/sequel/model/deprecated_validations.rb +0 -388
- data/spec/core/core_ext_spec.rb +0 -156
- data/spec/core/migration_spec.rb +0 -263
- data/spec/core/pretty_table_spec.rb +0 -58
- data/spec/model/caching_spec.rb +0 -217
- data/spec/model/schema_spec.rb +0 -92
data/CHANGELOG
CHANGED
@@ -1,3 +1,65 @@
|
|
1
|
+
=== 3.0.0 (2009-05-04)
|
2
|
+
|
3
|
+
* Remove dead threads from connection pool if the pool is full and a connection is requested (jeremyevans)
|
4
|
+
|
5
|
+
* Add autoincrementing primary key support in the Oracle adapter, using a sequence and trigger (jeremyevans, Mike Golod)
|
6
|
+
|
7
|
+
* Make Model#save use the same server it uses for saving as for retrieving the saved record (jeremyevans)
|
8
|
+
|
9
|
+
* Add Database#database_type method, for identifying which type of database the object is connecting to (jeremyevans)
|
10
|
+
|
11
|
+
* Add ability to reset primary key sequences in the PostgreSQL adapter (jeremyevans)
|
12
|
+
|
13
|
+
* Fix parsing of non-simple sequence names (that contain uppercase, spaces, etc.) in the PostgreSQL adapter (jeremyevans)
|
14
|
+
|
15
|
+
* Support dumping indexes in the schema_dumper extension (jeremyevans)
|
16
|
+
|
17
|
+
* Add index parsing to PostgreSQL, MySQL, SQLite, and JDBC adapters (jeremyevans)
|
18
|
+
|
19
|
+
* Correctly quote SQL Array references, and handle qualified identifiers with them (e.g. :table__column.sql_subscript(1)) (jeremyevans)
|
20
|
+
|
21
|
+
* Allow dropping an index with a name different than the default name (jeremyevans)
|
22
|
+
|
23
|
+
* Allow Dataset#from to remove existing FROM tables when called without an argument, instead of raising an error later (jeremyevans)
|
24
|
+
|
25
|
+
* Fix string quoting on Oracle so it doesn't double backslashes (jeremyevans)
|
26
|
+
|
27
|
+
* Alias the count function call in Dataset#count, fixes use on MSSQL (akitaonrails, jeremyevans)
|
28
|
+
|
29
|
+
* Allow QualifiedIdentifiers to be qualified, to allow :column.qualify(:table).qualify(:schema) (jeremyevans)
|
30
|
+
|
31
|
+
* Allow :db_type=>'mssql' option to be respected when using the DBI adapter (akitaonrails)
|
32
|
+
|
33
|
+
* Add schema_dumper extension, for dumping schema of tables (jeremyevans)
|
34
|
+
|
35
|
+
* Allow generic database types specified as ruby types to take options (jeremyevans)
|
36
|
+
|
37
|
+
* Change Dataset#exclude to invert given hash argument, not negate it (jeremyevans)
|
38
|
+
|
39
|
+
* Make Dataset#filter and related methods treat multiple arguments more intuitively (jeremyevans)
|
40
|
+
|
41
|
+
* Fix full text searching with multiple search terms on MySQL (jeremyevans)
|
42
|
+
|
43
|
+
* Fix altering a column name, type, default, or NULL/NOT NULL status on MySQL (jeremyevans)
|
44
|
+
|
45
|
+
* Fix index type syntax on MySQL (jeremyevans)
|
46
|
+
|
47
|
+
* Add temporary table support, via :temp option to Database#create_table (EppO, jeremyevans)
|
48
|
+
|
49
|
+
* Add Amalgalite adapter (jeremyevans)
|
50
|
+
|
51
|
+
* Remove Sequel::Metaprogramming#metaattr_accessor and metaattr_reader (jeremyevans)
|
52
|
+
|
53
|
+
* Remove Dataset#irregular_function_sql (jeremyevans)
|
54
|
+
|
55
|
+
* Add Dataset#full_text_sql to the MySQL adapter (dusty)
|
56
|
+
|
57
|
+
* Fix schema type parsing of decimal types on MySQL (jeremyevans)
|
58
|
+
|
59
|
+
* Make Dataset#quote_identifier work with SQL::Identifiers (jeremyevans)
|
60
|
+
|
61
|
+
* Remove methods and features deprecated in 2.12.0 (jeremyevans)
|
62
|
+
|
1
63
|
=== 2.12.0 (2009-04-03)
|
2
64
|
|
3
65
|
* Deprecate Java::JavaSQL::Timestamp#usec (jeremyevans)
|
data/README.rdoc
CHANGED
@@ -11,9 +11,9 @@ Sequel is a lightweight database access toolkit for Ruby.
|
|
11
11
|
configurations, and database sharding.
|
12
12
|
* Sequel makes it easy to deal with multiple records without having
|
13
13
|
to break your teeth on SQL.
|
14
|
-
* Sequel currently has adapters for ADO,
|
15
|
-
Firebird, Informix, JDBC, MySQL, ODBC, OpenBase, Oracle,
|
16
|
-
and SQLite3.
|
14
|
+
* Sequel currently has adapters for ADO, Amalgalite, DataObjects,
|
15
|
+
DB2, DBI, Firebird, Informix, JDBC, MySQL, ODBC, OpenBase, Oracle,
|
16
|
+
PostgreSQL and SQLite3.
|
17
17
|
|
18
18
|
== Resources
|
19
19
|
|
data/Rakefile
CHANGED
@@ -155,6 +155,13 @@ begin
|
|
155
155
|
t.spec_opts = spec_opts.call
|
156
156
|
end
|
157
157
|
|
158
|
+
desc "Run extention/plugin specs with coverage"
|
159
|
+
Spec::Rake::SpecTask.new("spec_plugin_cov") do |t|
|
160
|
+
t.spec_files = Dir["spec/extensions/*_spec.rb"]
|
161
|
+
t.spec_opts = spec_opts.call
|
162
|
+
t.rcov, t.rcov_opts = rcov_opts.call
|
163
|
+
end
|
164
|
+
|
158
165
|
desc "Run integration tests"
|
159
166
|
Spec::Rake::SpecTask.new("integration") do |t|
|
160
167
|
t.spec_files = FileList["spec/integration/*_test.rb"]
|
@@ -603,3 +603,47 @@ name, with no duplicates?
|
|
603
603
|
records.each{|r| r.associations[:songs].uniq!}
|
604
604
|
end)
|
605
605
|
end
|
606
|
+
|
607
|
+
=== Statistics Associations (Sum of Associated Table Column)
|
608
|
+
|
609
|
+
In addition to getting associated records, you can use Sequel's association support
|
610
|
+
to get aggregate information for columns in associated tables (sums, averages, etc.).
|
611
|
+
|
612
|
+
Let's say you have a database with projects and tickets. A project can have many
|
613
|
+
tickets, and each ticket has a number of hours associated with it. You can use the
|
614
|
+
association support to create a Project association that gives the sum of hours for all
|
615
|
+
associated tickets.
|
616
|
+
|
617
|
+
|
618
|
+
class Project < Sequel::Model
|
619
|
+
one_to_many :tickets
|
620
|
+
many_to_one :ticket_hours, :read_only=>true, :key=>:id,
|
621
|
+
:dataset=>proc{Ticket.filter(:project_id=>id).select{sum(hours).as(hours)}},
|
622
|
+
:eager_loader=>(proc do |kh, projects, a|
|
623
|
+
projects.each{|p| p.associations[:ticket_hours] = nil}
|
624
|
+
Ticket.filter(:project_id=>kh[:id].keys).
|
625
|
+
group(:project_id).
|
626
|
+
select{[project_id, sum(hours).as(hours)]}.
|
627
|
+
all do |t|
|
628
|
+
p = kh[:id][t.values.delete(:project_id)].first
|
629
|
+
p.associations[:ticket_hours] = t
|
630
|
+
end
|
631
|
+
end)
|
632
|
+
# The association method returns a Ticket object with a single aggregate
|
633
|
+
# sum-of-hours value, but you want it to return an Integer/Float of just the
|
634
|
+
# sum of hours, so you call super and return just the sum-of-hours value.
|
635
|
+
# This works for both lazy loading and eager loading.
|
636
|
+
def ticket_hours
|
637
|
+
if s = super
|
638
|
+
s[:hours]
|
639
|
+
end
|
640
|
+
end
|
641
|
+
end
|
642
|
+
class Ticket < Sequel::Model
|
643
|
+
many_to_one :project
|
644
|
+
end
|
645
|
+
|
646
|
+
Note that it is often better to use a sum cache instead of this approach. You can implement
|
647
|
+
a sum cache using before or after save and delete hooks, or using a database trigger
|
648
|
+
(the preferred method if you only have to support one database and that database supports
|
649
|
+
triggers).
|
@@ -0,0 +1,221 @@
|
|
1
|
+
Deprecated Methods/Features Removed
|
2
|
+
-----------------------------------
|
3
|
+
|
4
|
+
Methods and features that were deprecated in 2.12.0 have been removed
|
5
|
+
in 3.0.0. Many features were moved into plugins or extensions, so in
|
6
|
+
many cases you just need to require an extension or use Model.plugin
|
7
|
+
and not make any changes to your code. See the 2.12.0 release notes
|
8
|
+
for the list of methods/features deprecated in 2.12.0.
|
9
|
+
|
10
|
+
If you are upgrading from a previous 2.x release, please upgrade to
|
11
|
+
2.12.0 first, fix your code to remove all deprecation warnings, and
|
12
|
+
then upgrade to 3.0.0.
|
13
|
+
|
14
|
+
New Adapter
|
15
|
+
-----------
|
16
|
+
|
17
|
+
* Sequel now has an Amalgalite adapter. Amalgalite is a ruby
|
18
|
+
extension that embeds SQLite without requiring a separate SQLite
|
19
|
+
installation. The adapter is functionality complete but
|
20
|
+
significantly slower than the native SQLite adapter.
|
21
|
+
|
22
|
+
New Features
|
23
|
+
------------
|
24
|
+
|
25
|
+
* The JDBC, PostgreSQL, MySQL, and SQLite adapters all now have a
|
26
|
+
Database#indexes method that returns indexes for a given table:
|
27
|
+
|
28
|
+
DB.indexes(:songs)
|
29
|
+
=> {:songs_name_index=>{:unique=>true, :columns=>[:name]},
|
30
|
+
:songs_lyricid_index=>{:unique=>false, :columns=>[:lyricid]}}
|
31
|
+
|
32
|
+
* A schema_dumper extension was added to Sequel. It supports dumping
|
33
|
+
the schema of a table (including indexes) as a string that can be
|
34
|
+
evaluated in the context of a Database object to create the table.
|
35
|
+
It also supports dumping all tables in the database as a string
|
36
|
+
containing a Migration subclass that will rebuild the database.
|
37
|
+
|
38
|
+
require 'sequel/extensions/schema_dumper'
|
39
|
+
DB.dump_table_schema(:table)
|
40
|
+
DB.dump_schema_migration
|
41
|
+
DB.dump_schema_migration(:same_db=>true)
|
42
|
+
DB.dump_schema_migration(:indexes=>false)
|
43
|
+
DB.dump_indexes_migration
|
44
|
+
|
45
|
+
The :same_db option causes Sequel to not translate column types
|
46
|
+
to generic column types. By default, the migration created will
|
47
|
+
use generic types so it will run on other databases. However, if
|
48
|
+
you only want to support a single database, using the :same_db
|
49
|
+
option will make the migration use the exact database type parsed
|
50
|
+
from the database.
|
51
|
+
|
52
|
+
The :indexes=>false option causes indexes not be included in the
|
53
|
+
migration. The dump_indexes_migration can be used to create a
|
54
|
+
separate migration with the indexes. This can be useful if you
|
55
|
+
plan on loading a lot of data right after creating the tables,
|
56
|
+
since it is faster to add indexes after the data has been added.
|
57
|
+
|
58
|
+
* Using options with the generic database types is now supported to
|
59
|
+
a limited extent. For example, the following code now works:
|
60
|
+
|
61
|
+
DB.create_table(:table) do
|
62
|
+
String :a, :size=>50 # varchar(50)
|
63
|
+
String :b, :text=>true # text
|
64
|
+
String :c, :fixed=>true, :size=>30 # char(30)
|
65
|
+
Time :ts # timestamp
|
66
|
+
Time :t, :only_time=>true # time
|
67
|
+
end
|
68
|
+
|
69
|
+
* Using Dataset#filter and related methods with multiple arguments
|
70
|
+
now works much more intuitively:
|
71
|
+
|
72
|
+
# 2.12.0
|
73
|
+
dataset.filter(:a, :b=>1) # a IS NULL AND (b = 1) IS NULL
|
74
|
+
# 3.0.0
|
75
|
+
dataset.filter(:a, :b=>1) # a AND b = 1
|
76
|
+
|
77
|
+
* You can now create temporary tables by passing the :temp=>true
|
78
|
+
option to Database#create_table.
|
79
|
+
|
80
|
+
* The Oracle shared adapter now supports emulation of
|
81
|
+
autoincrementing primary keys by creating a sequence and a trigger,
|
82
|
+
similar to how the Firebird adapter works.
|
83
|
+
|
84
|
+
* The Database#database_type method was added that returns a symbol
|
85
|
+
specifying the database type being used. This can be different
|
86
|
+
than Database.adapter_scheme if you are using an adapter like
|
87
|
+
JDBC that allows connecting to multiple different types of
|
88
|
+
databases.
|
89
|
+
|
90
|
+
* Database#drop_index and related methods now support an options
|
91
|
+
hash that respects the :name option, so they can now be used to
|
92
|
+
drop an index that doesn't use the default index name.
|
93
|
+
|
94
|
+
* The PostgreSQL shared adapter now supports a
|
95
|
+
Database#reset_primary_key_sequence method to reset the
|
96
|
+
primary key sequence for a given table, based on code from
|
97
|
+
ActiveRecord.
|
98
|
+
|
99
|
+
* SQL::QualifiedIdentifiers can now be qualified, allowing you to do:
|
100
|
+
|
101
|
+
:column.qualify(:table).qualify(:schema)
|
102
|
+
|
103
|
+
* Using the :db_type=>'mssql' option with the DBI adapter will now
|
104
|
+
load the MSSQL support.
|
105
|
+
|
106
|
+
* The MySQL shared adapter now supports Dataset#full_text_sql, which
|
107
|
+
you can use in queries like the following:
|
108
|
+
|
109
|
+
ds.select(:table.*, ds.full_text_sql(:column, 'value').as(:ft))
|
110
|
+
|
111
|
+
Other Improvements
|
112
|
+
------------------
|
113
|
+
|
114
|
+
* Sequel will now release connections from the connection pool
|
115
|
+
automatically if they are held by a dead thread. This can happen
|
116
|
+
if you are using MRI 1.8 and you are heavily multithreaded or
|
117
|
+
you call Thread#exit! or similar method explicitly. Those methods
|
118
|
+
skip the execution of ensure blocks which normally release the
|
119
|
+
connections when the threads exit.
|
120
|
+
|
121
|
+
* Model#save will now always use the same server when refreshing data
|
122
|
+
after an insert. This fixes an issue when Sequel's master/slave
|
123
|
+
database support is used with models.
|
124
|
+
|
125
|
+
* SQL Array references are now quoted correctly, so code like this
|
126
|
+
now works:
|
127
|
+
|
128
|
+
:table__column.sql_subscript(1)
|
129
|
+
|
130
|
+
* The PostgreSQL shared adapter now handles sequences that need to be
|
131
|
+
quoted correctly (previously these were quoted twice).
|
132
|
+
|
133
|
+
* String quoting on Oracle no longer doubles backslashes.
|
134
|
+
|
135
|
+
* Database#count now works correctly when used on MSSQL when using
|
136
|
+
an adapter that doesn't handle unnamed columns.
|
137
|
+
|
138
|
+
* Full text searching in the MySQL adapter now works correctly when
|
139
|
+
multiple search terms are used.
|
140
|
+
|
141
|
+
* Altering a column's name, type, default, or NULL/NOT NULL status
|
142
|
+
on MySQL now keeps other relevent column information. For example,
|
143
|
+
if you alter a column's type, it'll keep an existing default. This
|
144
|
+
functionality isn't complete, there may be other column information
|
145
|
+
that is lost.
|
146
|
+
|
147
|
+
* Fix creation of an index with a given type on MySQL, since MySQL's
|
148
|
+
documentation lies.
|
149
|
+
|
150
|
+
* The schema parser now handles decimal types with size specifiers,
|
151
|
+
fixing use on MySQL.
|
152
|
+
|
153
|
+
* Dataset#quote_identifier now works correctly when given an
|
154
|
+
SQL::Identifier. This allows you to do:
|
155
|
+
|
156
|
+
dataset.select{sum(hours).as(hours)}
|
157
|
+
|
158
|
+
Backwards Compatibility
|
159
|
+
-----------------------
|
160
|
+
|
161
|
+
* Sequel will now use instance_eval on all virtual row blocks without
|
162
|
+
an argument. This can lead to much nicer code:
|
163
|
+
|
164
|
+
dataset.filter{(number > 10) & (name > 'M')}
|
165
|
+
# WHERE number > 10 AND name > 'M'
|
166
|
+
|
167
|
+
2.12.0 raised a deprecation warning if you used a virtual row block
|
168
|
+
without an argument and you hadn't set
|
169
|
+
Sequel.virtual_row_instance_eval = true.
|
170
|
+
|
171
|
+
* Dataset#exclude now inverts the given argument, instead of negating
|
172
|
+
it. This only changes its behavior if it is called with a hash or
|
173
|
+
array of all two pairs that have more than one element.
|
174
|
+
|
175
|
+
# 2.12.0
|
176
|
+
dataset.exclude(:a=>1, :b=>1) # a != 1 AND b != 1
|
177
|
+
# 3.0.0
|
178
|
+
dataset.exclude(:a=>1, :b=>1) # a != 1 OR b != 1
|
179
|
+
|
180
|
+
This was done for consistency, since exclude would only negate a
|
181
|
+
hash if it was given an argument, it would invert the same hash
|
182
|
+
if you used a block:
|
183
|
+
|
184
|
+
# 2.12.0
|
185
|
+
dataset.exclude{{:a=>1, :b=>1}} # a != 1 OR b != 1
|
186
|
+
|
187
|
+
If you want the previous behavior,
|
188
|
+
change the code to the following:
|
189
|
+
|
190
|
+
dataset.filter({:a=>1, :b=>1}.sql_negate)
|
191
|
+
|
192
|
+
* As noted above, the methods/features deprecated in 2.12.0 were
|
193
|
+
removed.
|
194
|
+
|
195
|
+
* The private Dataset#select_*_sql methods now only take a single
|
196
|
+
argument, the SQL string being built.
|
197
|
+
|
198
|
+
* Dataset#from when called without arguments would previously cause an
|
199
|
+
error to be raised when the SQL string is generated. Now it causes
|
200
|
+
no FROM clause to be used, similar to how Dataset#select with no
|
201
|
+
arguments causes SELECT * to be used.
|
202
|
+
|
203
|
+
* The internals of the generic type support and the schema generators
|
204
|
+
were changed significantly, which could have some fallout in terms
|
205
|
+
of old migrations breaking if they used the generic types and were
|
206
|
+
relying on some undocumented behavior (such as using Integer as a
|
207
|
+
type with the :unsigned option).
|
208
|
+
|
209
|
+
* The Firebird adapter no longer translates the text database
|
210
|
+
specific type. Use the following instead:
|
211
|
+
|
212
|
+
String :column, :text=>true
|
213
|
+
|
214
|
+
* The MySQL shared adapter used to use the timestamp type for Time,
|
215
|
+
now it uses datetime. This is because the timestamp type cannot
|
216
|
+
represent everything that the ruby Time class can represent.
|
217
|
+
|
218
|
+
* Metaprogramming#metaattr_accessor and #metaattr_reader methods were
|
219
|
+
removed.
|
220
|
+
|
221
|
+
* Dataset#irregular_function_sql was removed.
|
@@ -0,0 +1,208 @@
|
|
1
|
+
require 'amalgalite'
|
2
|
+
Sequel.require 'adapters/shared/sqlite'
|
3
|
+
|
4
|
+
module Sequel
|
5
|
+
# Top level module for holding all Amalgalite-related modules and classes
|
6
|
+
# for Sequel.
|
7
|
+
module Amalgalite
|
8
|
+
# Type conversion map class for Sequel's use of Amalgamite
|
9
|
+
class SequelTypeMap < ::Amalgalite::TypeMaps::DefaultMap
|
10
|
+
methods_handling_sql_types.delete('string')
|
11
|
+
methods_handling_sql_types.merge!(
|
12
|
+
'datetime' => %w'datetime timestamp',
|
13
|
+
'time' => %w'time',
|
14
|
+
'float' => ['float', 'double', 'real', 'double precision'],
|
15
|
+
'decimal' => %w'numeric decimal money'
|
16
|
+
)
|
17
|
+
|
18
|
+
# Return blobs as instances of Sequel::SQL::Blob instead of
|
19
|
+
# Amalgamite::Blob
|
20
|
+
def blob(s)
|
21
|
+
SQL::Blob.new(s)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Return numeric/decimal types as instances of BigDecimal
|
25
|
+
# instead of Float
|
26
|
+
def decimal(s)
|
27
|
+
BigDecimal.new(s)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Return datetime types as instances of Sequel.datetime_class
|
31
|
+
def datetime(s)
|
32
|
+
Sequel.string_to_datetime(s)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Don't raise an error if the value is a string and the declared
|
36
|
+
# type doesn't match a known type, just return the value.
|
37
|
+
def result_value_of(declared_type, value)
|
38
|
+
if value.is_a?(::Amalgalite::Blob)
|
39
|
+
SQL::Blob.new(value.source)
|
40
|
+
elsif value.is_a?(String) && declared_type
|
41
|
+
(meth = self.class.sql_to_method(declared_type.downcase)) ? send(meth, value) : value
|
42
|
+
else
|
43
|
+
super
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Database class for SQLite databases used with Sequel and the
|
49
|
+
# amalgalite driver.
|
50
|
+
class Database < Sequel::Database
|
51
|
+
include ::Sequel::SQLite::DatabaseMethods
|
52
|
+
|
53
|
+
set_adapter_scheme :amalgalite
|
54
|
+
|
55
|
+
# Mimic the file:// uri, by having 2 preceding slashes specify a relative
|
56
|
+
# path, and 3 preceding slashes specify an absolute path.
|
57
|
+
def self.uri_to_options(uri) # :nodoc:
|
58
|
+
{ :database => (uri.host.nil? && uri.path == '/') ? nil : "#{uri.host}#{uri.path}" }
|
59
|
+
end
|
60
|
+
private_class_method :uri_to_options
|
61
|
+
|
62
|
+
# Connect to the database. Since SQLite is a file based database,
|
63
|
+
# the only options available are :database (to specify the database
|
64
|
+
# name), and :timeout, to specify how long to wait for the database to
|
65
|
+
# be available if it is locked, given in milliseconds (default is 5000).
|
66
|
+
def connect(server)
|
67
|
+
opts = server_opts(server)
|
68
|
+
opts[:database] = ':memory:' if blank_object?(opts[:database])
|
69
|
+
db = ::Amalgalite::Database.new(opts[:database])
|
70
|
+
db.busy_handler(::Amalgalite::BusyTimeout.new(opts.fetch(:timeout, 5000)/50, 50))
|
71
|
+
db.type_map = SequelTypeMap.new
|
72
|
+
db
|
73
|
+
end
|
74
|
+
|
75
|
+
# Amalgalite is just the SQLite database without a separate SQLite installation.
|
76
|
+
def database_type
|
77
|
+
:sqlite
|
78
|
+
end
|
79
|
+
|
80
|
+
# Return instance of Sequel::Amalgalite::Dataset with the given options.
|
81
|
+
def dataset(opts = nil)
|
82
|
+
Amalgalite::Dataset.new(self, opts)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Run the given SQL with the given arguments and reload the schema. Returns nil.
|
86
|
+
def execute_ddl(sql, opts={})
|
87
|
+
_execute(sql, opts){|conn| conn.execute_batch(sql); conn.reload_schema!}
|
88
|
+
nil
|
89
|
+
end
|
90
|
+
|
91
|
+
# Run the given SQL with the given arguments and return the number of changed rows.
|
92
|
+
def execute_dui(sql, opts={})
|
93
|
+
_execute(sql, opts){|conn| conn.execute_batch(sql); conn.row_changes}
|
94
|
+
end
|
95
|
+
|
96
|
+
# Run the given SQL with the given arguments and return the last inserted row id.
|
97
|
+
def execute_insert(sql, opts={})
|
98
|
+
_execute(sql, opts){|conn| conn.execute_batch(sql); conn.last_insert_rowid}
|
99
|
+
end
|
100
|
+
|
101
|
+
# Run the given SQL with the given arguments and yield each row.
|
102
|
+
def execute(sql, opts={})
|
103
|
+
retried = false
|
104
|
+
_execute(sql, opts) do |conn|
|
105
|
+
conn.prepare(sql) do |stmt|
|
106
|
+
begin
|
107
|
+
stmt.result_meta
|
108
|
+
rescue NoMethodError
|
109
|
+
conn.reload_schema!
|
110
|
+
stmt.result_meta
|
111
|
+
end
|
112
|
+
yield stmt
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# Run the given SQL with the given arguments and return the first value of the first row.
|
118
|
+
def single_value(sql, opts={})
|
119
|
+
_execute(sql, opts){|conn| conn.first_value_from(sql)}
|
120
|
+
end
|
121
|
+
|
122
|
+
# Use the native driver transaction method if there isn't already a transaction
|
123
|
+
# in progress on the connection, always yielding a connection inside a transaction
|
124
|
+
# transaction.
|
125
|
+
def transaction(opts={})
|
126
|
+
synchronize(opts[:server]) do |conn|
|
127
|
+
return yield(conn) if conn.in_transaction?
|
128
|
+
begin
|
129
|
+
result = nil
|
130
|
+
log_info('Transaction.begin')
|
131
|
+
conn.transaction{result = yield(conn)}
|
132
|
+
result
|
133
|
+
rescue ::Exception => e
|
134
|
+
log_info('Transaction.rollback')
|
135
|
+
transaction_error(e, ::Amalgalite::Error, ::Amalgalite::SQLite3::Error)
|
136
|
+
ensure
|
137
|
+
log_info('Transaction.commit') unless e
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
private
|
143
|
+
|
144
|
+
# Log the SQL and yield an available connection. Rescue
|
145
|
+
# any Amalgalite::Errors and turn them into DatabaseErrors.
|
146
|
+
def _execute(sql, opts)
|
147
|
+
begin
|
148
|
+
log_info(sql)
|
149
|
+
synchronize(opts[:server]){|conn| yield conn}
|
150
|
+
rescue ::Amalgalite::Error, ::Amalgalite::SQLite3::Error => e
|
151
|
+
raise_error(e)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# The Amagalite adapter does not need the pool to convert exceptions.
|
156
|
+
# Also, force the max connections to 1 if a memory database is being
|
157
|
+
# used, as otherwise each connection gets a separate database.
|
158
|
+
def connection_pool_default_options
|
159
|
+
o = super.merge(:pool_convert_exceptions=>false)
|
160
|
+
# Default to only a single connection if a memory database is used,
|
161
|
+
# because otherwise each connection will get a separate database
|
162
|
+
o[:max_connections] = 1 if @opts[:database] == ':memory:' || blank_object?(@opts[:database])
|
163
|
+
o
|
164
|
+
end
|
165
|
+
|
166
|
+
# Disconnect given connections from the database.
|
167
|
+
def disconnect_connection(c)
|
168
|
+
c.close
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# Dataset class for SQLite datasets that use the amalgalite driver.
|
173
|
+
class Dataset < Sequel::Dataset
|
174
|
+
include ::Sequel::SQLite::DatasetMethods
|
175
|
+
|
176
|
+
EXPLAIN = 'EXPLAIN %s'.freeze
|
177
|
+
|
178
|
+
# Return an array of strings specifying a query explanation for the
|
179
|
+
# current dataset.
|
180
|
+
def explain
|
181
|
+
res = []
|
182
|
+
@db.result_set(EXPLAIN % select_sql(opts), nil) {|r| res << r}
|
183
|
+
res
|
184
|
+
end
|
185
|
+
|
186
|
+
# Yield a hash for each row in the dataset.
|
187
|
+
def fetch_rows(sql)
|
188
|
+
execute(sql) do |stmt|
|
189
|
+
stmt.result_meta
|
190
|
+
@columns = cols = stmt.result_fields.map{|c| output_identifier(c)}
|
191
|
+
col_count = cols.size
|
192
|
+
stmt.each do |result|
|
193
|
+
row = {}
|
194
|
+
col_count.times{|i| row[cols[i]] = result[i]}
|
195
|
+
yield row
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
private
|
201
|
+
|
202
|
+
# Quote the string using the adapter instance method.
|
203
|
+
def literal_string(v)
|
204
|
+
"#{db.synchronize{|c| c.quote(v)}}"
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|