sequel 2.6.0 → 2.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +64 -0
- data/Rakefile +1 -1
- data/lib/sequel_core/adapters/jdbc.rb +6 -2
- data/lib/sequel_core/adapters/jdbc/oracle.rb +23 -0
- data/lib/sequel_core/adapters/oracle.rb +4 -77
- data/lib/sequel_core/adapters/postgres.rb +39 -26
- data/lib/sequel_core/adapters/shared/mssql.rb +0 -1
- data/lib/sequel_core/adapters/shared/mysql.rb +1 -1
- data/lib/sequel_core/adapters/shared/oracle.rb +82 -0
- data/lib/sequel_core/adapters/shared/postgres.rb +65 -46
- data/lib/sequel_core/core_ext.rb +10 -0
- data/lib/sequel_core/core_sql.rb +7 -0
- data/lib/sequel_core/database.rb +22 -0
- data/lib/sequel_core/database/schema.rb +1 -1
- data/lib/sequel_core/dataset.rb +29 -11
- data/lib/sequel_core/dataset/sql.rb +27 -7
- data/lib/sequel_core/migration.rb +20 -2
- data/lib/sequel_core/object_graph.rb +24 -10
- data/lib/sequel_core/schema/generator.rb +22 -9
- data/lib/sequel_core/schema/sql.rb +13 -9
- data/lib/sequel_core/sql.rb +27 -2
- data/lib/sequel_model/association_reflection.rb +251 -141
- data/lib/sequel_model/associations.rb +114 -61
- data/lib/sequel_model/base.rb +25 -21
- data/lib/sequel_model/eager_loading.rb +17 -40
- data/lib/sequel_model/hooks.rb +25 -24
- data/lib/sequel_model/record.rb +29 -51
- data/lib/sequel_model/schema.rb +1 -1
- data/lib/sequel_model/validations.rb +13 -3
- data/spec/adapters/postgres_spec.rb +104 -18
- data/spec/adapters/spec_helper.rb +4 -1
- data/spec/integration/eager_loader_test.rb +5 -4
- data/spec/integration/spec_helper.rb +4 -1
- data/spec/sequel_core/connection_pool_spec.rb +24 -24
- data/spec/sequel_core/core_sql_spec.rb +12 -0
- data/spec/sequel_core/dataset_spec.rb +77 -2
- data/spec/sequel_core/expression_filters_spec.rb +6 -0
- data/spec/sequel_core/object_graph_spec.rb +40 -2
- data/spec/sequel_core/schema_spec.rb +13 -0
- data/spec/sequel_model/association_reflection_spec.rb +8 -8
- data/spec/sequel_model/associations_spec.rb +164 -3
- data/spec/sequel_model/caching_spec.rb +2 -1
- data/spec/sequel_model/eager_loading_spec.rb +107 -3
- data/spec/sequel_model/hooks_spec.rb +38 -22
- data/spec/sequel_model/model_spec.rb +11 -35
- data/spec/sequel_model/plugins_spec.rb +4 -2
- data/spec/sequel_model/record_spec.rb +8 -5
- data/spec/sequel_model/validations_spec.rb +25 -0
- data/spec/spec_config.rb +4 -3
- metadata +21 -19
data/CHANGELOG
CHANGED
@@ -1,3 +1,67 @@
|
|
1
|
+
=== 2.7.0 (2008-11-03)
|
2
|
+
|
3
|
+
* Transform AssociationReflection from a single class to a class hierarchy (jeremyevans)
|
4
|
+
|
5
|
+
* Optimize Date object creation in PostgreSQL adapter (jeremyevans)
|
6
|
+
|
7
|
+
* Allow easier creation of custom association types, though support for them may still be suboptimal (jeremyevans)
|
8
|
+
|
9
|
+
* Add :eager_grapher option to associations, which the user can use to override the default eager_graph code (jeremyevans)
|
10
|
+
|
11
|
+
* Associations are now inherited when a model class is subclassed (jeremyevans)
|
12
|
+
|
13
|
+
* Instance methods added by associations are now added to an anonymous module the class includes, allowing you to override them and use super (jeremyevans)
|
14
|
+
|
15
|
+
* Add #add_graph_aliases (select_more for graphs), and allow use of arbitrary expressions when graphing (jeremyevans)
|
16
|
+
|
17
|
+
* Fix a corner case where the wrong table name is used in eager_graph (jeremyevans)
|
18
|
+
|
19
|
+
* Make Dataset#join_table take an option hash instead of a table_alias argument, add support for :implicit_qualifier option (jeremyevans)
|
20
|
+
|
21
|
+
* Add :left_primary_key and :right_primary_key options to many_to_many associations (jeremyevans)
|
22
|
+
|
23
|
+
* Add :primary_key option to one_to_many and many_to_one associations (jeremyevans)
|
24
|
+
|
25
|
+
* Make after_load association callbacks take effect when eager loading via eager (jeremyevans)
|
26
|
+
|
27
|
+
* Add a :uniq association option to many_to_many associations (jeremyevans)
|
28
|
+
|
29
|
+
* Support using any expression as the argument to Symbol#like (jeremyevans)
|
30
|
+
|
31
|
+
* Much better support for multiple schemas in PostgreSQL (jeremyevans) (#243)
|
32
|
+
|
33
|
+
* The first argument to Model#initalize can no longer be nil, it must be a hash if it is given (jeremyevans)
|
34
|
+
|
35
|
+
* Remove Sequel::Model.lazy_load_schema= setting (jeremyevans)
|
36
|
+
|
37
|
+
* Lazily load model instance options such as raise_on_save_failure, for better performance (jeremyevans)
|
38
|
+
|
39
|
+
* Make Model::Validiation::Errors more Rails-compatible (jeremyevans)
|
40
|
+
|
41
|
+
* Refactor model hooks for performance (jeremyevans)
|
42
|
+
|
43
|
+
* Major performance enhancement when fetching rows using PostgreSQL (jeremyevans)
|
44
|
+
|
45
|
+
* Don't typecast serialized columns in models (jeremyevans)
|
46
|
+
|
47
|
+
* Add Array#sql_array to handle ruby arrays of all two pairs as SQL arrays (jeremyevans) (#245)
|
48
|
+
|
49
|
+
* Add ComplexExpression#== and #eql?, for checking equality (rubymage) (#244)
|
50
|
+
|
51
|
+
* Allow full text search on PostgreSQL to include rows where a search column is NULL (jeremyevans)
|
52
|
+
|
53
|
+
* PostgreSQL full text search queries with multiple columns are joined with space to prevent joining border words to one (michalbugno)
|
54
|
+
|
55
|
+
* Don't modify a dataset's cached column information if calling #each with an option that modifies the columns (jeremyevans)
|
56
|
+
|
57
|
+
* The PostgreSQL adapter will now generally default to using a unix socket in /tmp if no host is specified, instead of a tcp socket to localhost (jeremyevans)
|
58
|
+
|
59
|
+
* Make Dataset#sql call Dataset#select_sql instead of being an alias, to allow for easier subclassing (jeremyevans)
|
60
|
+
|
61
|
+
* Split Oracle adapter into shared and unshared parts, so Oracle is better supported when using JDBC (jeremyevans)
|
62
|
+
|
63
|
+
* Fix automatic loading of Oracle driver when using JDBC adapter (bburton333) (#242)
|
64
|
+
|
1
65
|
=== 2.6.0 (2008-10-11)
|
2
66
|
|
3
67
|
* Make the sqlite adapter respect the Sequel.datetime_class setting, for timestamp and datetime types (jeremyevans)
|
data/Rakefile
CHANGED
@@ -12,7 +12,7 @@ require "fileutils"
|
|
12
12
|
include FileUtils
|
13
13
|
|
14
14
|
NAME = 'sequel'
|
15
|
-
VERS = '2.
|
15
|
+
VERS = '2.7.0'
|
16
16
|
CLEAN.include ["**/.*.sw?", "pkg", ".config", "rdoc", "coverage", "www/public/*.html"]
|
17
17
|
RDOC_OPTS = ["--quiet", "--line-numbers", "--inline-source", '--title', \
|
18
18
|
'Sequel: The Database Toolkit for Ruby', '--main', 'README']
|
@@ -3,7 +3,7 @@ require 'java'
|
|
3
3
|
module Sequel
|
4
4
|
# Houses Sequel's JDBC support when running on JRuby.
|
5
5
|
# Support for individual database types is done using sub adapters.
|
6
|
-
# PostgreSQL, MySQL, SQLite, and MSSQL all have relatively good support,
|
6
|
+
# PostgreSQL, MySQL, SQLite, Oracle, and MSSQL all have relatively good support,
|
7
7
|
# close the the level supported by the native adapter.
|
8
8
|
# PostgreSQL, MySQL, SQLite can load necessary support using
|
9
9
|
# the jdbc-* gem, if it is installed, though they will work if you
|
@@ -51,7 +51,11 @@ module Sequel
|
|
51
51
|
JDBC.load_gem('sqlite3')
|
52
52
|
org.sqlite.JDBC
|
53
53
|
end,
|
54
|
-
:oracle=>proc
|
54
|
+
:oracle=>proc do |db|
|
55
|
+
require 'sequel_core/adapters/jdbc/oracle'
|
56
|
+
db.extend(Sequel::JDBC::Oracle::DatabaseMethods)
|
57
|
+
Java::oracle.jdbc.driver.OracleDriver
|
58
|
+
end,
|
55
59
|
:sqlserver=>proc do |db|
|
56
60
|
require 'sequel_core/adapters/shared/mssql'
|
57
61
|
db.extend(Sequel::MSSQL::DatabaseMethods)
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'sequel_core/adapters/shared/oracle'
|
2
|
+
|
3
|
+
module Sequel
|
4
|
+
module JDBC
|
5
|
+
# Database and Dataset support for Oracle databases accessed via JDBC.
|
6
|
+
module Oracle
|
7
|
+
# Instance methods for Oracle Database objects accessed via JDBC.
|
8
|
+
module DatabaseMethods
|
9
|
+
include Sequel::Oracle::DatabaseMethods
|
10
|
+
|
11
|
+
# Return Sequel::JDBC::Oracle::Dataset object with the given opts.
|
12
|
+
def dataset(opts=nil)
|
13
|
+
Sequel::JDBC::Oracle::Dataset.new(self, opts)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Dataset class for Oracle datasets accessed via JDBC.
|
18
|
+
class Dataset < JDBC::Dataset
|
19
|
+
include Sequel::Oracle::DatasetMethods
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -1,8 +1,10 @@
|
|
1
1
|
require 'oci8'
|
2
|
+
require 'sequel_core/adapters/shared/oracle'
|
2
3
|
|
3
4
|
module Sequel
|
4
5
|
module Oracle
|
5
6
|
class Database < Sequel::Database
|
7
|
+
include DatabaseMethods
|
6
8
|
set_adapter_scheme :oracle
|
7
9
|
|
8
10
|
def connect(server)
|
@@ -37,16 +39,6 @@ module Sequel
|
|
37
39
|
end
|
38
40
|
alias_method :do, :execute
|
39
41
|
|
40
|
-
def tables
|
41
|
-
from(:tab).select(:tname).filter(:tabtype => 'TABLE').map do |r|
|
42
|
-
r[:tname].downcase.to_sym
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
def table_exists?(name)
|
47
|
-
from(:tab).filter(:tname => name.to_s.upcase, :tabtype => 'TABLE').count > 0
|
48
|
-
end
|
49
|
-
|
50
42
|
def transaction(server=nil)
|
51
43
|
synchronize(server) do |conn|
|
52
44
|
return yield(conn) if @transactions.include?(Thread.current)
|
@@ -68,6 +60,8 @@ module Sequel
|
|
68
60
|
end
|
69
61
|
|
70
62
|
class Dataset < Sequel::Dataset
|
63
|
+
include DatasetMethods
|
64
|
+
|
71
65
|
def literal(v)
|
72
66
|
case v
|
73
67
|
when OraDate
|
@@ -92,73 +86,6 @@ module Sequel
|
|
92
86
|
end
|
93
87
|
self
|
94
88
|
end
|
95
|
-
|
96
|
-
def empty?
|
97
|
-
db[:dual].where(exists).get(1) == nil
|
98
|
-
end
|
99
|
-
|
100
|
-
# Formats a SELECT statement using the given options and the dataset
|
101
|
-
# options.
|
102
|
-
def select_sql(opts = nil)
|
103
|
-
opts = opts ? @opts.merge(opts) : @opts
|
104
|
-
|
105
|
-
if sql = opts[:sql]
|
106
|
-
return sql
|
107
|
-
end
|
108
|
-
|
109
|
-
columns = opts[:select]
|
110
|
-
select_columns = columns ? column_list(columns) : WILDCARD
|
111
|
-
sql = opts[:distinct] ? \
|
112
|
-
"SELECT DISTINCT #{select_columns}" : \
|
113
|
-
"SELECT #{select_columns}"
|
114
|
-
|
115
|
-
if opts[:from]
|
116
|
-
sql << " FROM #{source_list(opts[:from])}"
|
117
|
-
end
|
118
|
-
|
119
|
-
if join = opts[:join]
|
120
|
-
join.each{|j| sql << literal(j)}
|
121
|
-
end
|
122
|
-
|
123
|
-
if where = opts[:where]
|
124
|
-
sql << " WHERE #{literal(where)}"
|
125
|
-
end
|
126
|
-
|
127
|
-
if group = opts[:group]
|
128
|
-
sql << " GROUP BY #{expression_list(group)}"
|
129
|
-
end
|
130
|
-
|
131
|
-
if having = opts[:having]
|
132
|
-
sql << " HAVING #{literal(having)}"
|
133
|
-
end
|
134
|
-
|
135
|
-
if union = opts[:union]
|
136
|
-
sql << (opts[:union_all] ? \
|
137
|
-
" UNION ALL #{union.sql}" : " UNION #{union.sql}")
|
138
|
-
elsif intersect = opts[:intersect]
|
139
|
-
sql << (opts[:intersect_all] ? \
|
140
|
-
" INTERSECT ALL #{intersect.sql}" : " INTERSECT #{intersect.sql}")
|
141
|
-
elsif except = opts[:except]
|
142
|
-
sql << (opts[:except_all] ? \
|
143
|
-
" EXCEPT ALL #{except.sql}" : " EXCEPT #{except.sql}")
|
144
|
-
end
|
145
|
-
|
146
|
-
if order = opts[:order]
|
147
|
-
sql << " ORDER BY #{expression_list(order)}"
|
148
|
-
end
|
149
|
-
|
150
|
-
if limit = opts[:limit]
|
151
|
-
if (offset = opts[:offset]) && (offset > 0)
|
152
|
-
sql = "SELECT * FROM (SELECT raw_sql_.*, ROWNUM raw_rnum_ FROM(#{sql}) raw_sql_ WHERE ROWNUM <= #{limit + offset}) WHERE raw_rnum_ > #{offset}"
|
153
|
-
else
|
154
|
-
sql = "SELECT * FROM (#{sql}) WHERE ROWNUM <= #{limit}"
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
sql
|
159
|
-
end
|
160
|
-
|
161
|
-
alias sql select_sql
|
162
89
|
end
|
163
90
|
end
|
164
91
|
end
|
@@ -83,7 +83,7 @@ module Sequel
|
|
83
83
|
|
84
84
|
# Hash with integer keys and proc values for converting PostgreSQL types.
|
85
85
|
PG_TYPES = {
|
86
|
-
16 => lambda{ |s|
|
86
|
+
16 => lambda{ |s| s == 't' }, # boolean
|
87
87
|
17 => lambda{ |s| Adapter.unescape_bytea(s).to_blob }, # bytea
|
88
88
|
20 => lambda{ |s| s.to_i }, # int8
|
89
89
|
21 => lambda{ |s| s.to_i }, # int2
|
@@ -93,7 +93,7 @@ module Sequel
|
|
93
93
|
700 => lambda{ |s| s.to_f }, # float4
|
94
94
|
701 => lambda{ |s| s.to_f }, # float8
|
95
95
|
790 => lambda{ |s| s.to_d }, # money
|
96
|
-
1082 => lambda{ |s| s.to_date }, # date
|
96
|
+
1082 => lambda{ |s| @use_iso_date_format ? Date.new(*s.split("-").map{|x| x.to_i}) : s.to_date }, # date
|
97
97
|
1083 => lambda{ |s| s.to_time }, # time without time zone
|
98
98
|
1114 => lambda{ |s| s.to_sequel_time }, # timestamp without time zone
|
99
99
|
1184 => lambda{ |s| s.to_sequel_time }, # timestamp with time zone
|
@@ -102,16 +102,12 @@ module Sequel
|
|
102
102
|
1700 => lambda{ |s| s.to_d }, # numeric
|
103
103
|
}
|
104
104
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
else
|
112
|
-
false
|
113
|
-
end
|
114
|
-
end
|
105
|
+
@use_iso_date_format = true
|
106
|
+
|
107
|
+
# As an optimization, Sequel sets the date style to ISO, so that PostgreSQL provides
|
108
|
+
# the date in a known format that Sequel can parse faster. This can be turned off
|
109
|
+
# if you require a date style other than ISO.
|
110
|
+
metaattr_accessor :use_iso_date_format
|
115
111
|
|
116
112
|
# PGconn subclass for connection specific methods used with the
|
117
113
|
# pg, postgres, or postgres-pr driver.
|
@@ -119,6 +115,13 @@ module Sequel
|
|
119
115
|
include Sequel::Postgres::AdapterMethods
|
120
116
|
self.translate_results = false if respond_to?(:translate_results=)
|
121
117
|
|
118
|
+
# Apply connection settings for this connection. Current sets
|
119
|
+
# the date style to ISO in order make Date object creation in ruby faster,
|
120
|
+
# if Postgres.use_iso_date_format is true.
|
121
|
+
def apply_connection_settings
|
122
|
+
async_exec("SET DateStyle = 'ISO, YMD'") if Postgres.use_iso_date_format
|
123
|
+
end
|
124
|
+
|
122
125
|
# Execute the given SQL with this connection. If a block is given,
|
123
126
|
# yield the results, otherwise, return the number of changed rows.
|
124
127
|
def execute(sql, args=nil)
|
@@ -138,6 +141,12 @@ module Sequel
|
|
138
141
|
q.clear
|
139
142
|
end
|
140
143
|
end
|
144
|
+
|
145
|
+
# Reapply the connection settings if the connection is reset.
|
146
|
+
def reset(*args, &block)
|
147
|
+
super(*args, &block)
|
148
|
+
apply_connection_settings
|
149
|
+
end
|
141
150
|
|
142
151
|
if SEQUEL_POSTGRES_USES_PG
|
143
152
|
# Hash of prepared statements for this connection. Keys are
|
@@ -170,14 +179,14 @@ module Sequel
|
|
170
179
|
@primary_keys = {}
|
171
180
|
@primary_key_sequences = {}
|
172
181
|
end
|
173
|
-
|
182
|
+
|
174
183
|
# Connects to the database. In addition to the standard database
|
175
184
|
# options, using the :encoding or :charset option changes the
|
176
185
|
# client encoding for the connection.
|
177
186
|
def connect(server)
|
178
187
|
opts = server_opts(server)
|
179
188
|
conn = Adapter.connect(
|
180
|
-
opts[:host]
|
189
|
+
(opts[:host] unless opts[:host].blank?),
|
181
190
|
opts[:port] || 5432,
|
182
191
|
nil, '',
|
183
192
|
opts[:database],
|
@@ -185,8 +194,13 @@ module Sequel
|
|
185
194
|
opts[:password]
|
186
195
|
)
|
187
196
|
if encoding = opts[:encoding] || opts[:charset]
|
188
|
-
conn.set_client_encoding
|
197
|
+
if conn.respond_to?(:set_client_encoding)
|
198
|
+
conn.set_client_encoding(encoding)
|
199
|
+
else
|
200
|
+
conn.async_exec("set client_encoding to '#{encoding}'")
|
201
|
+
end
|
189
202
|
end
|
203
|
+
conn.apply_connection_settings
|
190
204
|
conn.db = self
|
191
205
|
conn
|
192
206
|
end
|
@@ -280,21 +294,20 @@ module Sequel
|
|
280
294
|
class Dataset < Sequel::Dataset
|
281
295
|
include Sequel::Postgres::DatasetMethods
|
282
296
|
|
283
|
-
#
|
297
|
+
# Yield all rows returned by executing the given SQL and converting
|
284
298
|
# the types.
|
285
299
|
def fetch_rows(sql)
|
286
|
-
|
300
|
+
cols = []
|
287
301
|
execute(sql) do |res|
|
288
|
-
|
302
|
+
res.nfields.times do |fieldnum|
|
303
|
+
cols << [fieldnum, PG_TYPES[res.ftype(fieldnum)], res.fname(fieldnum).to_sym]
|
304
|
+
end
|
305
|
+
@columns = cols.map{|c| c.at(2)}
|
306
|
+
res.ntuples.times do |recnum|
|
289
307
|
converted_rec = {}
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
converted_rec[fieldsym] = if value = res.getvalue(recnum,fieldnum)
|
294
|
-
(PG_TYPES[res.ftype(fieldnum)] || lambda{|s| s.to_s}).call(value)
|
295
|
-
else
|
296
|
-
value
|
297
|
-
end
|
308
|
+
cols.each do |fieldnum, type_proc, fieldsym|
|
309
|
+
value = res.getvalue(recnum, fieldnum)
|
310
|
+
converted_rec[fieldsym] = (value && type_proc) ? type_proc.call(value) : value
|
298
311
|
end
|
299
312
|
yield converted_rec
|
300
313
|
end
|
@@ -156,7 +156,7 @@ module Sequel
|
|
156
156
|
|
157
157
|
# Transforms an CROSS JOIN to an INNER JOIN if the expr is not nil.
|
158
158
|
# Raises an error on use of :full_outer type, since MySQL doesn't support it.
|
159
|
-
def join_table(type, table, expr=nil, table_alias=
|
159
|
+
def join_table(type, table, expr=nil, table_alias={})
|
160
160
|
type = :inner if (type == :cross) && !expr.nil?
|
161
161
|
raise(Sequel::Error, "MySQL doesn't support FULL OUTER JOIN") if type == :full_outer
|
162
162
|
super(type, table, expr, table_alias)
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Sequel
|
2
|
+
module Oracle
|
3
|
+
module DatabaseMethods
|
4
|
+
def tables
|
5
|
+
from(:tab).select(:tname).filter(:tabtype => 'TABLE').map do |r|
|
6
|
+
r[:tname].downcase.to_sym
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def table_exists?(name)
|
11
|
+
from(:tab).filter(:tname => name.to_s.upcase, :tabtype => 'TABLE').count > 0
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module DatasetMethods
|
16
|
+
def empty?
|
17
|
+
db[:dual].where(exists).get(1) == nil
|
18
|
+
end
|
19
|
+
|
20
|
+
# Formats a SELECT statement using the given options and the dataset
|
21
|
+
# options.
|
22
|
+
def select_sql(opts = nil)
|
23
|
+
opts = opts ? @opts.merge(opts) : @opts
|
24
|
+
|
25
|
+
if sql = opts[:sql]
|
26
|
+
return sql
|
27
|
+
end
|
28
|
+
|
29
|
+
columns = opts[:select]
|
30
|
+
select_columns = columns ? column_list(columns) : '*'
|
31
|
+
sql = opts[:distinct] ? \
|
32
|
+
"SELECT DISTINCT #{select_columns}" : \
|
33
|
+
"SELECT #{select_columns}"
|
34
|
+
|
35
|
+
if opts[:from]
|
36
|
+
sql << " FROM #{source_list(opts[:from])}"
|
37
|
+
end
|
38
|
+
|
39
|
+
if join = opts[:join]
|
40
|
+
join.each{|j| sql << literal(j)}
|
41
|
+
end
|
42
|
+
|
43
|
+
if where = opts[:where]
|
44
|
+
sql << " WHERE #{literal(where)}"
|
45
|
+
end
|
46
|
+
|
47
|
+
if group = opts[:group]
|
48
|
+
sql << " GROUP BY #{expression_list(group)}"
|
49
|
+
end
|
50
|
+
|
51
|
+
if having = opts[:having]
|
52
|
+
sql << " HAVING #{literal(having)}"
|
53
|
+
end
|
54
|
+
|
55
|
+
if union = opts[:union]
|
56
|
+
sql << (opts[:union_all] ? \
|
57
|
+
" UNION ALL #{union.sql}" : " UNION #{union.sql}")
|
58
|
+
elsif intersect = opts[:intersect]
|
59
|
+
sql << (opts[:intersect_all] ? \
|
60
|
+
" INTERSECT ALL #{intersect.sql}" : " INTERSECT #{intersect.sql}")
|
61
|
+
elsif except = opts[:except]
|
62
|
+
sql << (opts[:except_all] ? \
|
63
|
+
" EXCEPT ALL #{except.sql}" : " EXCEPT #{except.sql}")
|
64
|
+
end
|
65
|
+
|
66
|
+
if order = opts[:order]
|
67
|
+
sql << " ORDER BY #{expression_list(order)}"
|
68
|
+
end
|
69
|
+
|
70
|
+
if limit = opts[:limit]
|
71
|
+
if (offset = opts[:offset]) && (offset > 0)
|
72
|
+
sql = "SELECT * FROM (SELECT raw_sql_.*, ROWNUM raw_rnum_ FROM(#{sql}) raw_sql_ WHERE ROWNUM <= #{limit + offset}) WHERE raw_rnum_ > #{offset}"
|
73
|
+
else
|
74
|
+
sql = "SELECT * FROM (#{sql}) WHERE ROWNUM <= #{limit}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
sql
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|