viking-sequel 3.10.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 +3134 -0
- data/COPYING +19 -0
- data/README.rdoc +723 -0
- data/Rakefile +193 -0
- data/bin/sequel +196 -0
- data/doc/advanced_associations.rdoc +644 -0
- data/doc/cheat_sheet.rdoc +218 -0
- data/doc/dataset_basics.rdoc +106 -0
- data/doc/dataset_filtering.rdoc +158 -0
- data/doc/opening_databases.rdoc +296 -0
- data/doc/prepared_statements.rdoc +104 -0
- data/doc/reflection.rdoc +84 -0
- data/doc/release_notes/1.0.txt +38 -0
- data/doc/release_notes/1.1.txt +143 -0
- data/doc/release_notes/1.3.txt +101 -0
- data/doc/release_notes/1.4.0.txt +53 -0
- data/doc/release_notes/1.5.0.txt +155 -0
- data/doc/release_notes/2.0.0.txt +298 -0
- data/doc/release_notes/2.1.0.txt +271 -0
- data/doc/release_notes/2.10.0.txt +328 -0
- data/doc/release_notes/2.11.0.txt +215 -0
- data/doc/release_notes/2.12.0.txt +534 -0
- data/doc/release_notes/2.2.0.txt +253 -0
- data/doc/release_notes/2.3.0.txt +88 -0
- data/doc/release_notes/2.4.0.txt +106 -0
- data/doc/release_notes/2.5.0.txt +137 -0
- data/doc/release_notes/2.6.0.txt +157 -0
- data/doc/release_notes/2.7.0.txt +166 -0
- data/doc/release_notes/2.8.0.txt +171 -0
- data/doc/release_notes/2.9.0.txt +97 -0
- data/doc/release_notes/3.0.0.txt +221 -0
- data/doc/release_notes/3.1.0.txt +406 -0
- data/doc/release_notes/3.10.0.txt +286 -0
- data/doc/release_notes/3.2.0.txt +268 -0
- data/doc/release_notes/3.3.0.txt +192 -0
- data/doc/release_notes/3.4.0.txt +325 -0
- data/doc/release_notes/3.5.0.txt +510 -0
- data/doc/release_notes/3.6.0.txt +366 -0
- data/doc/release_notes/3.7.0.txt +179 -0
- data/doc/release_notes/3.8.0.txt +151 -0
- data/doc/release_notes/3.9.0.txt +233 -0
- data/doc/schema.rdoc +36 -0
- data/doc/sharding.rdoc +113 -0
- data/doc/virtual_rows.rdoc +205 -0
- data/lib/sequel.rb +1 -0
- data/lib/sequel/adapters/ado.rb +90 -0
- data/lib/sequel/adapters/ado/mssql.rb +30 -0
- data/lib/sequel/adapters/amalgalite.rb +176 -0
- data/lib/sequel/adapters/db2.rb +139 -0
- data/lib/sequel/adapters/dbi.rb +113 -0
- data/lib/sequel/adapters/do.rb +188 -0
- data/lib/sequel/adapters/do/mysql.rb +49 -0
- data/lib/sequel/adapters/do/postgres.rb +91 -0
- data/lib/sequel/adapters/do/sqlite.rb +40 -0
- data/lib/sequel/adapters/firebird.rb +283 -0
- data/lib/sequel/adapters/informix.rb +77 -0
- data/lib/sequel/adapters/jdbc.rb +587 -0
- data/lib/sequel/adapters/jdbc/as400.rb +58 -0
- data/lib/sequel/adapters/jdbc/h2.rb +133 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +57 -0
- data/lib/sequel/adapters/jdbc/mysql.rb +78 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +50 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +108 -0
- data/lib/sequel/adapters/jdbc/sqlite.rb +55 -0
- data/lib/sequel/adapters/mysql.rb +421 -0
- data/lib/sequel/adapters/odbc.rb +143 -0
- data/lib/sequel/adapters/odbc/mssql.rb +42 -0
- data/lib/sequel/adapters/openbase.rb +64 -0
- data/lib/sequel/adapters/oracle.rb +131 -0
- data/lib/sequel/adapters/postgres.rb +504 -0
- data/lib/sequel/adapters/shared/mssql.rb +490 -0
- data/lib/sequel/adapters/shared/mysql.rb +498 -0
- data/lib/sequel/adapters/shared/oracle.rb +195 -0
- data/lib/sequel/adapters/shared/postgres.rb +830 -0
- data/lib/sequel/adapters/shared/progress.rb +44 -0
- data/lib/sequel/adapters/shared/sqlite.rb +389 -0
- data/lib/sequel/adapters/sqlite.rb +224 -0
- data/lib/sequel/adapters/utils/stored_procedures.rb +84 -0
- data/lib/sequel/connection_pool.rb +99 -0
- data/lib/sequel/connection_pool/sharded_single.rb +84 -0
- data/lib/sequel/connection_pool/sharded_threaded.rb +211 -0
- data/lib/sequel/connection_pool/single.rb +29 -0
- data/lib/sequel/connection_pool/threaded.rb +150 -0
- data/lib/sequel/core.rb +293 -0
- data/lib/sequel/core_sql.rb +241 -0
- data/lib/sequel/database.rb +1079 -0
- data/lib/sequel/database/schema_generator.rb +327 -0
- data/lib/sequel/database/schema_methods.rb +203 -0
- data/lib/sequel/database/schema_sql.rb +320 -0
- data/lib/sequel/dataset.rb +32 -0
- data/lib/sequel/dataset/actions.rb +441 -0
- data/lib/sequel/dataset/features.rb +86 -0
- data/lib/sequel/dataset/graph.rb +254 -0
- data/lib/sequel/dataset/misc.rb +119 -0
- data/lib/sequel/dataset/mutation.rb +64 -0
- data/lib/sequel/dataset/prepared_statements.rb +227 -0
- data/lib/sequel/dataset/query.rb +709 -0
- data/lib/sequel/dataset/sql.rb +996 -0
- data/lib/sequel/exceptions.rb +51 -0
- data/lib/sequel/extensions/blank.rb +43 -0
- data/lib/sequel/extensions/inflector.rb +242 -0
- data/lib/sequel/extensions/looser_typecasting.rb +21 -0
- data/lib/sequel/extensions/migration.rb +239 -0
- data/lib/sequel/extensions/named_timezones.rb +61 -0
- data/lib/sequel/extensions/pagination.rb +100 -0
- data/lib/sequel/extensions/pretty_table.rb +82 -0
- data/lib/sequel/extensions/query.rb +52 -0
- data/lib/sequel/extensions/schema_dumper.rb +271 -0
- data/lib/sequel/extensions/sql_expr.rb +122 -0
- data/lib/sequel/extensions/string_date_time.rb +46 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +48 -0
- data/lib/sequel/metaprogramming.rb +9 -0
- data/lib/sequel/model.rb +120 -0
- data/lib/sequel/model/associations.rb +1514 -0
- data/lib/sequel/model/base.rb +1069 -0
- data/lib/sequel/model/default_inflections.rb +45 -0
- data/lib/sequel/model/errors.rb +39 -0
- data/lib/sequel/model/exceptions.rb +21 -0
- data/lib/sequel/model/inflections.rb +162 -0
- data/lib/sequel/model/plugins.rb +70 -0
- data/lib/sequel/plugins/active_model.rb +59 -0
- data/lib/sequel/plugins/association_dependencies.rb +103 -0
- data/lib/sequel/plugins/association_proxies.rb +41 -0
- data/lib/sequel/plugins/boolean_readers.rb +53 -0
- data/lib/sequel/plugins/caching.rb +141 -0
- data/lib/sequel/plugins/class_table_inheritance.rb +214 -0
- data/lib/sequel/plugins/composition.rb +138 -0
- data/lib/sequel/plugins/force_encoding.rb +72 -0
- data/lib/sequel/plugins/hook_class_methods.rb +126 -0
- data/lib/sequel/plugins/identity_map.rb +116 -0
- data/lib/sequel/plugins/instance_filters.rb +98 -0
- data/lib/sequel/plugins/instance_hooks.rb +57 -0
- data/lib/sequel/plugins/lazy_attributes.rb +77 -0
- data/lib/sequel/plugins/many_through_many.rb +208 -0
- data/lib/sequel/plugins/nested_attributes.rb +206 -0
- data/lib/sequel/plugins/optimistic_locking.rb +81 -0
- data/lib/sequel/plugins/rcte_tree.rb +281 -0
- data/lib/sequel/plugins/schema.rb +66 -0
- data/lib/sequel/plugins/serialization.rb +166 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +74 -0
- data/lib/sequel/plugins/subclasses.rb +45 -0
- data/lib/sequel/plugins/tactical_eager_loading.rb +61 -0
- data/lib/sequel/plugins/timestamps.rb +87 -0
- data/lib/sequel/plugins/touch.rb +118 -0
- data/lib/sequel/plugins/typecast_on_load.rb +72 -0
- data/lib/sequel/plugins/validation_class_methods.rb +405 -0
- data/lib/sequel/plugins/validation_helpers.rb +223 -0
- data/lib/sequel/sql.rb +1020 -0
- data/lib/sequel/timezones.rb +161 -0
- data/lib/sequel/version.rb +12 -0
- data/lib/sequel_core.rb +1 -0
- data/lib/sequel_model.rb +1 -0
- data/spec/adapters/firebird_spec.rb +407 -0
- data/spec/adapters/informix_spec.rb +97 -0
- data/spec/adapters/mssql_spec.rb +403 -0
- data/spec/adapters/mysql_spec.rb +1019 -0
- data/spec/adapters/oracle_spec.rb +286 -0
- data/spec/adapters/postgres_spec.rb +969 -0
- data/spec/adapters/spec_helper.rb +51 -0
- data/spec/adapters/sqlite_spec.rb +432 -0
- data/spec/core/connection_pool_spec.rb +808 -0
- data/spec/core/core_sql_spec.rb +417 -0
- data/spec/core/database_spec.rb +1662 -0
- data/spec/core/dataset_spec.rb +3827 -0
- data/spec/core/expression_filters_spec.rb +595 -0
- data/spec/core/object_graph_spec.rb +296 -0
- data/spec/core/schema_generator_spec.rb +159 -0
- data/spec/core/schema_spec.rb +830 -0
- data/spec/core/spec_helper.rb +56 -0
- data/spec/core/version_spec.rb +7 -0
- data/spec/extensions/active_model_spec.rb +76 -0
- data/spec/extensions/association_dependencies_spec.rb +127 -0
- data/spec/extensions/association_proxies_spec.rb +50 -0
- data/spec/extensions/blank_spec.rb +67 -0
- data/spec/extensions/boolean_readers_spec.rb +92 -0
- data/spec/extensions/caching_spec.rb +250 -0
- data/spec/extensions/class_table_inheritance_spec.rb +252 -0
- data/spec/extensions/composition_spec.rb +194 -0
- data/spec/extensions/force_encoding_spec.rb +117 -0
- data/spec/extensions/hook_class_methods_spec.rb +470 -0
- data/spec/extensions/identity_map_spec.rb +202 -0
- data/spec/extensions/inflector_spec.rb +181 -0
- data/spec/extensions/instance_filters_spec.rb +55 -0
- data/spec/extensions/instance_hooks_spec.rb +133 -0
- data/spec/extensions/lazy_attributes_spec.rb +153 -0
- data/spec/extensions/looser_typecasting_spec.rb +39 -0
- data/spec/extensions/many_through_many_spec.rb +884 -0
- data/spec/extensions/migration_spec.rb +332 -0
- data/spec/extensions/named_timezones_spec.rb +72 -0
- data/spec/extensions/nested_attributes_spec.rb +396 -0
- data/spec/extensions/optimistic_locking_spec.rb +100 -0
- data/spec/extensions/pagination_spec.rb +99 -0
- data/spec/extensions/pretty_table_spec.rb +91 -0
- data/spec/extensions/query_spec.rb +85 -0
- data/spec/extensions/rcte_tree_spec.rb +205 -0
- data/spec/extensions/schema_dumper_spec.rb +357 -0
- data/spec/extensions/schema_spec.rb +127 -0
- data/spec/extensions/serialization_spec.rb +209 -0
- data/spec/extensions/single_table_inheritance_spec.rb +96 -0
- data/spec/extensions/spec_helper.rb +91 -0
- data/spec/extensions/sql_expr_spec.rb +89 -0
- data/spec/extensions/string_date_time_spec.rb +93 -0
- data/spec/extensions/subclasses_spec.rb +52 -0
- data/spec/extensions/tactical_eager_loading_spec.rb +65 -0
- data/spec/extensions/thread_local_timezones_spec.rb +45 -0
- data/spec/extensions/timestamps_spec.rb +150 -0
- data/spec/extensions/touch_spec.rb +155 -0
- data/spec/extensions/typecast_on_load_spec.rb +69 -0
- data/spec/extensions/validation_class_methods_spec.rb +984 -0
- data/spec/extensions/validation_helpers_spec.rb +438 -0
- data/spec/integration/associations_test.rb +281 -0
- data/spec/integration/database_test.rb +26 -0
- data/spec/integration/dataset_test.rb +963 -0
- data/spec/integration/eager_loader_test.rb +734 -0
- data/spec/integration/model_test.rb +130 -0
- data/spec/integration/plugin_test.rb +814 -0
- data/spec/integration/prepared_statement_test.rb +213 -0
- data/spec/integration/schema_test.rb +361 -0
- data/spec/integration/spec_helper.rb +73 -0
- data/spec/integration/timezone_test.rb +55 -0
- data/spec/integration/transaction_test.rb +122 -0
- data/spec/integration/type_test.rb +96 -0
- data/spec/model/association_reflection_spec.rb +175 -0
- data/spec/model/associations_spec.rb +2633 -0
- data/spec/model/base_spec.rb +418 -0
- data/spec/model/dataset_methods_spec.rb +78 -0
- data/spec/model/eager_loading_spec.rb +1391 -0
- data/spec/model/hooks_spec.rb +240 -0
- data/spec/model/inflector_spec.rb +26 -0
- data/spec/model/model_spec.rb +593 -0
- data/spec/model/plugins_spec.rb +236 -0
- data/spec/model/record_spec.rb +1500 -0
- data/spec/model/spec_helper.rb +97 -0
- data/spec/model/validations_spec.rb +153 -0
- data/spec/rcov.opts +6 -0
- data/spec/spec_config.rb.example +10 -0
- metadata +346 -0
@@ -0,0 +1,205 @@
|
|
1
|
+
= Virtual Row Blocks
|
2
|
+
|
3
|
+
Dataset methods filter, order, and select all take blocks that are referred to as
|
4
|
+
virtual row blocks. Many other dataset methods pass the blocks
|
5
|
+
they are given into one of those three methods, so there are actually
|
6
|
+
many Sequel::Dataset methods that take virtual row blocks.
|
7
|
+
|
8
|
+
== Regular Proc vs InstanceEvaled Procs
|
9
|
+
|
10
|
+
Virtual row blocks behave differently depending on whether the block accepts
|
11
|
+
an argument. If the block accepts an argument, it is called with an instance
|
12
|
+
of Sequel::SQL::VirtualRow. If it does not accept an argument, it is
|
13
|
+
evaluated in the context of an instance of Sequel::SQL::VirtualRow.
|
14
|
+
|
15
|
+
ds = DB[:items]
|
16
|
+
# Regular proc
|
17
|
+
ds.filter{|o| o.column > 1}
|
18
|
+
# WHERE column > 1
|
19
|
+
|
20
|
+
# Instance-evaled proc
|
21
|
+
ds.filter{column > 1}
|
22
|
+
# WHERE column > 1
|
23
|
+
|
24
|
+
If you aren't familiar with the difference between regular blocks and instance
|
25
|
+
evaled blocks, you should probably consult a general ruby reference, but briefly,
|
26
|
+
with regular procs, methods called without an explicit receiver inside the
|
27
|
+
proc call the method on the receiver in the surrounding scope, while instance
|
28
|
+
evaled procs call the method on the receiver of the instance_eval call. However,
|
29
|
+
in both cases, local variables available in the surrounding scope will be available
|
30
|
+
inside the proc. If that doesn't make sense, maybe this example will help:
|
31
|
+
|
32
|
+
def self.a
|
33
|
+
42
|
34
|
+
end
|
35
|
+
b = 32
|
36
|
+
|
37
|
+
# Regular proc
|
38
|
+
ds.filter{|o| o.c > a - b}
|
39
|
+
# WHERE c > 10
|
40
|
+
|
41
|
+
# Instance-evaled proc
|
42
|
+
ds.filter{c > a - b}
|
43
|
+
# WHERE c > (a - 32)
|
44
|
+
|
45
|
+
There are two related differences here. First is the usage of "o.c" vs "c",
|
46
|
+
and second is the difference between the the use of "a". In the regular proc,
|
47
|
+
you couldn't call c without an explicit receiver in the proc, unless the self of the
|
48
|
+
surrounding scope responded to it. For a, note how ruby calls the method on
|
49
|
+
the receiver of the surrounding scope in the regular proc, which returns an integer,
|
50
|
+
and does the substraction before Sequel gets access to it. In the instance evaled
|
51
|
+
proc, calling a without a receiver calls the a method on the VirtualRow instance.
|
52
|
+
For b, note that it operates the same in both cases, as it is a local variable.
|
53
|
+
|
54
|
+
Basically, the choice for whether to use a regular proc or an instance evaled proc is
|
55
|
+
completely up to you. The same things can be accomplished with both.
|
56
|
+
Instance evaled procs tend to produce shorter code, but by modifying the scope
|
57
|
+
can be more difficult for a new user to understand. That being said, I usually
|
58
|
+
use instance evaled procs unless I need to call methods on the receiver of the
|
59
|
+
surrounding scope inside the proc.
|
60
|
+
|
61
|
+
== Local Variables vs Method Calls
|
62
|
+
|
63
|
+
If you have a method that accepts 0 arguments and has the same name as a local
|
64
|
+
variable, you can call it with () to differentiate the method call from the
|
65
|
+
local variable access. This is mostly useful in instance_evaled procs:
|
66
|
+
|
67
|
+
b = 32
|
68
|
+
ds.filter{b() > b}
|
69
|
+
# WHERE b > 32
|
70
|
+
|
71
|
+
== VirtualRow Methods
|
72
|
+
|
73
|
+
VirtualRow is a class that returns SQL::Identifiers, SQL::QualifiedIdentifiers,
|
74
|
+
SQL::Functions, or SQL::WindowFunctions depending on how it is called.
|
75
|
+
|
76
|
+
== SQL::Identifiers - Regular columns
|
77
|
+
|
78
|
+
SQL::Identifiers can be thought of as regular column references in SQL,
|
79
|
+
not qualified by any table. You get an SQL::Identifier if the method is called
|
80
|
+
without a block or arguments, and doesn't have a double underscore in the method
|
81
|
+
name:
|
82
|
+
|
83
|
+
ds.filter{|o| o.column > 1}
|
84
|
+
ds.filter{column > 1}
|
85
|
+
# WHERE column > 1
|
86
|
+
|
87
|
+
== SQL::QualifiedIdentifiers - Qualified columns
|
88
|
+
|
89
|
+
SQL::QualifiedIdentifiers can be thought of as column references in SQL that
|
90
|
+
are qualified to a specific table. You get an SQL::QualifiedIdentifier if
|
91
|
+
the method is called without a block or arguments, and has a double underscore
|
92
|
+
in the method name:
|
93
|
+
|
94
|
+
ds.filter{|o| o.table__column > 1}
|
95
|
+
ds.filter{table__column > 1}
|
96
|
+
# WHERE table.column > 1
|
97
|
+
|
98
|
+
Using the double underscore for SQL::QualifiedIdentifiers was done to make
|
99
|
+
usage very similar to using symbols, which also translate the double underscore
|
100
|
+
into a qualified column.
|
101
|
+
|
102
|
+
== SQL::Functions - SQL function calls
|
103
|
+
|
104
|
+
SQL::Functions can be thought of as function calls in SQL. You get a simple
|
105
|
+
function call if you call a method with arguments and without a block:
|
106
|
+
|
107
|
+
ds.filter{|o| o.function(1) > 1}
|
108
|
+
ds.filter{function(1) > 1}
|
109
|
+
# WHERE function(1) > 1
|
110
|
+
|
111
|
+
To call a SQL function with multiple arguments, just use those arguments in
|
112
|
+
your function call:
|
113
|
+
|
114
|
+
ds.filter{|o| o.function(1, o.a) > 1}
|
115
|
+
ds.filter{function(1, a) > 1}
|
116
|
+
# WHERE function(1, a) > 1
|
117
|
+
|
118
|
+
If the SQL function does not accept any arguments, you need to provide an empty
|
119
|
+
block to the method to distinguish it from a call that will produce an
|
120
|
+
SQL::Identifier:
|
121
|
+
|
122
|
+
ds.select{|o| o.version{}}
|
123
|
+
ds.select{version{}}
|
124
|
+
# SELECT version()
|
125
|
+
|
126
|
+
To use the SQL wildcard (*) as the sole argument in a function call (most often
|
127
|
+
used with the count function), you should provide :* as the sole argument to
|
128
|
+
the method, and provide an empty block to the method:
|
129
|
+
|
130
|
+
ds.select{|o| o.count(:*){}}
|
131
|
+
ds.select{count(:*){}}
|
132
|
+
# SELECT count(*)
|
133
|
+
|
134
|
+
To append the DISTINCT keyword before the method arguments, you need to make
|
135
|
+
:distinct the first argument of the method call, and provide an empty block to
|
136
|
+
the method:
|
137
|
+
|
138
|
+
ds.select{|o| o.count(:distinct, o.col1){}}
|
139
|
+
ds.select{count(:distinct, col1){}}
|
140
|
+
# SELECT count(DISTINCT col1)
|
141
|
+
|
142
|
+
To use multiple columns with the DISTINCT keyword, use multiple arguments in
|
143
|
+
the method call:
|
144
|
+
|
145
|
+
ds.select{|o| o.count(:distinct, o.col1, o.col2){}}
|
146
|
+
ds.select{count(:distinct, col1, col2){}}
|
147
|
+
# SELECT count(DISTINCT col1, col2)
|
148
|
+
|
149
|
+
== SQL::WindowFunctions - SQL window function calls
|
150
|
+
|
151
|
+
SQL::WindowFunctions can be thought of as calls to SQL window functions. Not
|
152
|
+
all databases support them, but they are very helpful for certain types of
|
153
|
+
queries. To use them, you need to make :over the first argument of the method
|
154
|
+
call, with an optional hash as the second argument: Here are some examples of use:
|
155
|
+
|
156
|
+
ds.select{|o| o.rank(:over){}}
|
157
|
+
ds.select{rank(:over){}}
|
158
|
+
# SELECT rank() OVER ()
|
159
|
+
|
160
|
+
ds.select{|o| o.count(:over, :*=>true){}}
|
161
|
+
ds.select{count(:over, :*=>true){}}
|
162
|
+
# SELECT count(*) OVER ()
|
163
|
+
|
164
|
+
ds.select{|o| o.sum(:over, :args=>o.col1, :partition=>o.col2, :order=>o.col3){}}
|
165
|
+
ds.select{sum(:over, :args=>col1, :partition=>col2, :order=>col3){}}
|
166
|
+
# SELECT sum(col1) OVER (PARTITION BY col2 ORDER BY col3)
|
167
|
+
|
168
|
+
== Returning multiple values
|
169
|
+
|
170
|
+
It's common when using select and order virtual row blocks to want to
|
171
|
+
return multiple values. If you want to do that, you just need to return a single
|
172
|
+
array:
|
173
|
+
|
174
|
+
ds.select{|o| [o.column1, o.sum(o.column2).as(o.sum)]}
|
175
|
+
ds.select{[column1, sum(column2).as(sum)]}
|
176
|
+
# SELECT column1, sum(column2) AS sum
|
177
|
+
|
178
|
+
Note that if you forget the array brackets, you'll end up with a syntax error:
|
179
|
+
|
180
|
+
# Invalid ruby syntax
|
181
|
+
ds.select{|o| o.column1, o.sum(o.column2).as(o.sum)}
|
182
|
+
ds.select{column1, sum(column2).as(sum)}
|
183
|
+
|
184
|
+
== Alternative Description of the VirtualRow method call rules
|
185
|
+
|
186
|
+
* If a block is given:
|
187
|
+
* The block is currently not called. This may change in a future version.
|
188
|
+
* If there are no arguments, an SQL::Function with the name of
|
189
|
+
method used, and no arguments.
|
190
|
+
* If the first argument is :*, an SQL::Function is created with a single
|
191
|
+
wildcard argument (*).
|
192
|
+
* If the first argument is :distinct, an SQL::Function is created with
|
193
|
+
the keyword DISTINCT prefacing all remaining arguments.
|
194
|
+
* If the first argument is :over, the second argument if provided should
|
195
|
+
be a hash of options to pass to SQL::Window. The options hash can also
|
196
|
+
contain :*=>true to use a wildcard argument as the function argument, or
|
197
|
+
:args=>... to specify an array of arguments to use as the function arguments.
|
198
|
+
* If a block is not given:
|
199
|
+
* If there are arguments, an SQL::Function is returned with the
|
200
|
+
name of the method used and the arguments given.
|
201
|
+
* If there are no arguments and the method contains a double
|
202
|
+
underscore, split on the double underscore and return an
|
203
|
+
SQL::QualifiedIdentifier with the table and column.
|
204
|
+
* Otherwise, create an SQL::Identifier with the name of the
|
205
|
+
method.
|
data/lib/sequel.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'sequel/model'
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'win32ole'
|
2
|
+
|
3
|
+
module Sequel
|
4
|
+
# The ADO adapter provides connectivity to ADO databases in Windows.
|
5
|
+
module ADO
|
6
|
+
class Database < Sequel::Database
|
7
|
+
set_adapter_scheme :ado
|
8
|
+
|
9
|
+
def initialize(opts)
|
10
|
+
super
|
11
|
+
@opts[:driver] ||= 'SQL Server'
|
12
|
+
case @opts[:driver]
|
13
|
+
when 'SQL Server'
|
14
|
+
Sequel.ts_require 'adapters/ado/mssql'
|
15
|
+
extend Sequel::ADO::MSSQL::DatabaseMethods
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Connect to the database. In addition to the usual database options,
|
20
|
+
# the following options have an effect:
|
21
|
+
#
|
22
|
+
# * :command_timeout - Sets the time in seconds to wait while attempting
|
23
|
+
# to execute a command before cancelling the attempt and generating
|
24
|
+
# an error. Specifically, it sets the ADO CommandTimeout property.
|
25
|
+
# If this property is not set, the default of 30 seconds is used.
|
26
|
+
# * :conn_string - The full ADO connection string. If this is provided,
|
27
|
+
# the usual options are ignored.
|
28
|
+
# * :provider - Sets the Provider of this ADO connection (for example, "SQLOLEDB")
|
29
|
+
def connect(server)
|
30
|
+
opts = server_opts(server)
|
31
|
+
s = opts[:conn_string] || "driver=#{opts[:driver]};server=#{opts[:host]};database=#{opts[:database]}#{";uid=#{opts[:user]};pwd=#{opts[:password]}" if opts[:user]}"
|
32
|
+
handle = WIN32OLE.new('ADODB.Connection')
|
33
|
+
handle.CommandTimeout = opts[:command_timeout] if opts[:command_timeout]
|
34
|
+
handle.Provider = opts[:provider] if opts[:provider]
|
35
|
+
handle.Open(s)
|
36
|
+
handle
|
37
|
+
end
|
38
|
+
|
39
|
+
def dataset(opts = nil)
|
40
|
+
ADO::Dataset.new(self, opts)
|
41
|
+
end
|
42
|
+
|
43
|
+
def execute(sql, opts={})
|
44
|
+
synchronize(opts[:server]) do |conn|
|
45
|
+
begin
|
46
|
+
r = log_yield(sql){conn.Execute(sql)}
|
47
|
+
yield(r) if block_given?
|
48
|
+
rescue ::WIN32OLERuntimeError => e
|
49
|
+
raise_error(e)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
nil
|
53
|
+
end
|
54
|
+
alias do execute
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
# The ADO adapter doesn't support transactions, since it appears not to
|
59
|
+
# use a single native connection for each connection in the pool
|
60
|
+
def _transaction(conn)
|
61
|
+
th = Thread.current
|
62
|
+
begin
|
63
|
+
@transactions << th
|
64
|
+
yield conn
|
65
|
+
rescue Sequel::Rollback
|
66
|
+
ensure
|
67
|
+
@transactions.delete(th)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def disconnect_connection(conn)
|
72
|
+
conn.Close
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
class Dataset < Sequel::Dataset
|
77
|
+
def fetch_rows(sql)
|
78
|
+
execute(sql) do |s|
|
79
|
+
@columns = cols = s.Fields.extend(Enumerable).map{|column| output_identifier(column.Name)}
|
80
|
+
s.getRows.transpose.each{|r| yield cols.inject({}){|m,c| m[c] = r.shift; m}} unless s.eof
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# ADO returns nil for all for delete and update statements.
|
85
|
+
def provides_accurate_rows_matched?
|
86
|
+
false
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
Sequel.require 'adapters/shared/mssql'
|
2
|
+
|
3
|
+
module Sequel
|
4
|
+
module ADO
|
5
|
+
# Database and Dataset instance methods for MSSQL specific
|
6
|
+
# support via ADO.
|
7
|
+
module MSSQL
|
8
|
+
module DatabaseMethods
|
9
|
+
include Sequel::MSSQL::DatabaseMethods
|
10
|
+
|
11
|
+
# Return instance of Sequel::ADO::MSSQL::Dataset with the given opts.
|
12
|
+
def dataset(opts=nil)
|
13
|
+
Sequel::ADO::MSSQL::Dataset.new(self, opts)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class Dataset < ADO::Dataset
|
18
|
+
include Sequel::MSSQL::DatasetMethods
|
19
|
+
|
20
|
+
# Use a nasty hack of multiple SQL statements in the same call and
|
21
|
+
# having the last one return the most recently inserted id. This
|
22
|
+
# is necessary as ADO doesn't provide a consistent native connection.
|
23
|
+
def insert(*values)
|
24
|
+
return super if @opts[:sql]
|
25
|
+
with_sql("SET NOCOUNT ON; #{insert_sql(*values)}; SELECT CAST(SCOPE_IDENTITY() AS INTEGER)").single_value
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,176 @@
|
|
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.database_to_application_timestamp(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. Returns nil.
|
86
|
+
def execute_ddl(sql, opts={})
|
87
|
+
_execute(sql, opts){|conn| log_yield(sql){conn.execute_batch(sql)}}
|
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| log_yield(sql){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| log_yield(sql){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
|
+
_execute(sql, opts) do |conn|
|
104
|
+
begin
|
105
|
+
yield(stmt = log_yield(sql){conn.prepare(sql)})
|
106
|
+
ensure
|
107
|
+
stmt.close if stmt
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Run the given SQL with the given arguments and return the first value of the first row.
|
113
|
+
def single_value(sql, opts={})
|
114
|
+
_execute(sql, opts){|conn| log_yield(sql){conn.first_value_from(sql)}}
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
# Yield an available connection. Rescue
|
120
|
+
# any Amalgalite::Errors and turn them into DatabaseErrors.
|
121
|
+
def _execute(sql, opts)
|
122
|
+
begin
|
123
|
+
synchronize(opts[:server]){|conn| yield conn}
|
124
|
+
rescue ::Amalgalite::Error, ::Amalgalite::SQLite3::Error => e
|
125
|
+
raise_error(e)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# The Amagalite adapter does not need the pool to convert exceptions.
|
130
|
+
# Also, force the max connections to 1 if a memory database is being
|
131
|
+
# used, as otherwise each connection gets a separate database.
|
132
|
+
def connection_pool_default_options
|
133
|
+
o = super.dup
|
134
|
+
# Default to only a single connection if a memory database is used,
|
135
|
+
# because otherwise each connection will get a separate database
|
136
|
+
o[:max_connections] = 1 if @opts[:database] == ':memory:' || blank_object?(@opts[:database])
|
137
|
+
o
|
138
|
+
end
|
139
|
+
|
140
|
+
# Both main error classes that Amalgalite raises
|
141
|
+
def database_error_classes
|
142
|
+
[::Amalgalite::Error, ::Amalgalite::SQLite3::Error]
|
143
|
+
end
|
144
|
+
|
145
|
+
# Disconnect given connections from the database.
|
146
|
+
def disconnect_connection(c)
|
147
|
+
c.close
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# Dataset class for SQLite datasets that use the amalgalite driver.
|
152
|
+
class Dataset < Sequel::Dataset
|
153
|
+
include ::Sequel::SQLite::DatasetMethods
|
154
|
+
|
155
|
+
# Yield a hash for each row in the dataset.
|
156
|
+
def fetch_rows(sql)
|
157
|
+
execute(sql) do |stmt|
|
158
|
+
@columns = cols = stmt.result_fields.map{|c| output_identifier(c)}
|
159
|
+
col_count = cols.size
|
160
|
+
stmt.each do |result|
|
161
|
+
row = {}
|
162
|
+
col_count.times{|i| row[cols[i]] = result[i]}
|
163
|
+
yield row
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
private
|
169
|
+
|
170
|
+
# Quote the string using the adapter instance method.
|
171
|
+
def literal_string(v)
|
172
|
+
db.synchronize{|c| c.quote(v)}
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|