sequel 2.12.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|