sequel 3.43.0 → 3.44.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +32 -0
- data/doc/association_basics.rdoc +10 -3
- data/doc/release_notes/3.37.0.txt +1 -1
- data/doc/release_notes/3.44.0.txt +152 -0
- data/lib/sequel/adapters/ado.rb +0 -6
- data/lib/sequel/adapters/db2.rb +0 -3
- data/lib/sequel/adapters/dbi.rb +0 -6
- data/lib/sequel/adapters/ibmdb.rb +0 -3
- data/lib/sequel/adapters/jdbc.rb +8 -12
- data/lib/sequel/adapters/jdbc/as400.rb +2 -18
- data/lib/sequel/adapters/jdbc/derby.rb +10 -0
- data/lib/sequel/adapters/jdbc/h2.rb +10 -0
- data/lib/sequel/adapters/jdbc/hsqldb.rb +10 -0
- data/lib/sequel/adapters/jdbc/sqlite.rb +5 -0
- data/lib/sequel/adapters/jdbc/sqlserver.rb +2 -4
- data/lib/sequel/adapters/mock.rb +5 -0
- data/lib/sequel/adapters/mysql2.rb +4 -13
- data/lib/sequel/adapters/odbc.rb +0 -5
- data/lib/sequel/adapters/oracle.rb +3 -5
- data/lib/sequel/adapters/postgres.rb +2 -1
- data/lib/sequel/adapters/shared/access.rb +10 -0
- data/lib/sequel/adapters/shared/cubrid.rb +9 -0
- data/lib/sequel/adapters/shared/db2.rb +10 -0
- data/lib/sequel/adapters/shared/mssql.rb +10 -0
- data/lib/sequel/adapters/shared/mysql.rb +14 -0
- data/lib/sequel/adapters/shared/oracle.rb +15 -0
- data/lib/sequel/adapters/shared/postgres.rb +26 -2
- data/lib/sequel/adapters/shared/sqlite.rb +15 -0
- data/lib/sequel/adapters/tinytds.rb +5 -32
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +2 -37
- data/lib/sequel/core.rb +3 -3
- data/lib/sequel/database/misc.rb +40 -4
- data/lib/sequel/database/query.rb +1 -1
- data/lib/sequel/database/schema_methods.rb +33 -12
- data/lib/sequel/dataset/actions.rb +51 -2
- data/lib/sequel/dataset/features.rb +0 -6
- data/lib/sequel/dataset/sql.rb +1 -1
- data/lib/sequel/exceptions.rb +22 -7
- data/lib/sequel/extensions/columns_introspection.rb +30 -5
- data/lib/sequel/extensions/pg_auto_parameterize.rb +9 -0
- data/lib/sequel/model/associations.rb +50 -37
- data/lib/sequel/model/base.rb +30 -1
- data/lib/sequel/plugins/eager_each.rb +17 -21
- data/lib/sequel/plugins/identity_map.rb +2 -1
- data/lib/sequel/plugins/many_through_many.rb +1 -1
- data/lib/sequel/plugins/single_table_inheritance.rb +2 -2
- data/lib/sequel/plugins/tactical_eager_loading.rb +1 -1
- data/lib/sequel/sql.rb +4 -2
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +32 -2
- data/spec/adapters/sqlite_spec.rb +20 -0
- data/spec/core/database_spec.rb +40 -0
- data/spec/core/dataset_spec.rb +91 -4
- data/spec/core/mock_adapter_spec.rb +2 -1
- data/spec/core/schema_generator_spec.rb +4 -0
- data/spec/core/schema_spec.rb +9 -3
- data/spec/extensions/association_dependencies_spec.rb +3 -3
- data/spec/extensions/columns_introspection_spec.rb +28 -2
- data/spec/extensions/eager_each_spec.rb +0 -1
- data/spec/extensions/identity_map_spec.rb +3 -2
- data/spec/extensions/migration_spec.rb +6 -0
- data/spec/extensions/prepared_statements_associations_spec.rb +2 -2
- data/spec/extensions/rcte_tree_spec.rb +2 -2
- data/spec/extensions/single_table_inheritance_spec.rb +3 -0
- data/spec/extensions/tactical_eager_loading_spec.rb +1 -1
- data/spec/extensions/validation_class_methods_spec.rb +8 -0
- data/spec/integration/associations_test.rb +4 -4
- data/spec/integration/database_test.rb +68 -20
- data/spec/integration/dataset_test.rb +48 -0
- data/spec/integration/schema_test.rb +25 -1
- data/spec/model/associations_spec.rb +21 -8
- data/spec/model/dataset_methods_spec.rb +58 -18
- metadata +4 -2
@@ -30,6 +30,11 @@ module Sequel
|
|
30
30
|
|
31
31
|
private
|
32
32
|
|
33
|
+
DATABASE_ERROR_REGEXPS = {/Abort due to constraint violation/ => ConstraintViolation}.freeze
|
34
|
+
def database_error_regexps
|
35
|
+
DATABASE_ERROR_REGEXPS
|
36
|
+
end
|
37
|
+
|
33
38
|
# Use last_insert_rowid() to get the last inserted id.
|
34
39
|
def last_insert_id(conn, opts={})
|
35
40
|
statement(conn) do |stmt|
|
@@ -18,7 +18,7 @@ module Sequel
|
|
18
18
|
# than getObject() for this column avoids the problem.
|
19
19
|
# Reference: http://social.msdn.microsoft.com/Forums/en/sqldataaccess/thread/20df12f3-d1bf-4526-9daa-239a83a8e435
|
20
20
|
module MetadataDatasetMethods
|
21
|
-
def process_result_set_convert(cols, result
|
21
|
+
def process_result_set_convert(cols, result)
|
22
22
|
while result.next
|
23
23
|
row = {}
|
24
24
|
cols.each do |n, i, p|
|
@@ -40,18 +40,16 @@ module Sequel
|
|
40
40
|
v
|
41
41
|
end
|
42
42
|
end
|
43
|
-
row.delete(rn) if rn
|
44
43
|
yield row
|
45
44
|
end
|
46
45
|
end
|
47
46
|
|
48
|
-
def process_result_set_no_convert(cols, result
|
47
|
+
def process_result_set_no_convert(cols, result)
|
49
48
|
while result.next
|
50
49
|
row = {}
|
51
50
|
cols.each do |n, i|
|
52
51
|
row[n] = (n == :is_autoincrement ? result.getString(i) : result.getObject(i))
|
53
52
|
end
|
54
|
-
row.delete(rn) if rn
|
55
53
|
yield row
|
56
54
|
end
|
57
55
|
end
|
data/lib/sequel/adapters/mock.rb
CHANGED
@@ -138,21 +138,12 @@ module Sequel
|
|
138
138
|
# Yield all rows matching this dataset.
|
139
139
|
def fetch_rows(sql)
|
140
140
|
execute(sql) do |r|
|
141
|
-
if identifier_output_method
|
142
|
-
|
143
|
-
@columns = cols2 = cols.map{|c| output_identifier(c.to_s)}
|
144
|
-
cs = cols.zip(cols2)
|
145
|
-
r.each(:cast_booleans=>convert_tinyint_to_bool?) do |row|
|
146
|
-
h = {}
|
147
|
-
cs.each do |a, b|
|
148
|
-
h[b] = row[a]
|
149
|
-
end
|
150
|
-
yield h
|
151
|
-
end
|
141
|
+
@columns = if identifier_output_method
|
142
|
+
r.fields.map!{|c| output_identifier(c.to_s)}
|
152
143
|
else
|
153
|
-
|
154
|
-
r.each(:cast_booleans=>convert_tinyint_to_bool?){|h| yield h}
|
144
|
+
r.fields
|
155
145
|
end
|
146
|
+
r.each(:cast_booleans=>convert_tinyint_to_bool?){|h| yield h}
|
156
147
|
end
|
157
148
|
self
|
158
149
|
end
|
data/lib/sequel/adapters/odbc.rb
CHANGED
@@ -105,16 +105,11 @@ module Sequel
|
|
105
105
|
i = -1
|
106
106
|
cols = s.columns(true).map{|c| [output_identifier(c.name), i+=1]}
|
107
107
|
columns = cols.map{|c| c.at(0)}
|
108
|
-
if opts[:offset] && offset_returns_row_number_column?
|
109
|
-
rn = row_number_column
|
110
|
-
columns.delete(rn)
|
111
|
-
end
|
112
108
|
@columns = columns
|
113
109
|
if rows = s.fetch_all
|
114
110
|
rows.each do |row|
|
115
111
|
hash = {}
|
116
112
|
cols.each{|n,i| hash[n] = convert_odbc_value(row[i])}
|
117
|
-
hash.delete(rn) if rn
|
118
113
|
yield hash
|
119
114
|
end
|
120
115
|
end
|
@@ -37,7 +37,9 @@ module Sequel
|
|
37
37
|
dbname = opts[:host]
|
38
38
|
end
|
39
39
|
conn = OCI8.new(opts[:user], opts[:password], dbname, opts[:privilege])
|
40
|
-
|
40
|
+
if prefetch_rows = opts.fetch(:prefetch_rows, 100)
|
41
|
+
conn.prefetch_rows = typecast_value_integer(prefetch_rows)
|
42
|
+
end
|
41
43
|
conn.autocommit = true
|
42
44
|
conn.non_blocking = true
|
43
45
|
|
@@ -383,18 +385,14 @@ module Sequel
|
|
383
385
|
|
384
386
|
def fetch_rows(sql)
|
385
387
|
execute(sql) do |cursor|
|
386
|
-
offset = @opts[:offset]
|
387
|
-
rn = row_number_column
|
388
388
|
cps = db.conversion_procs
|
389
389
|
cols = columns = cursor.get_col_names.map{|c| output_identifier(c)}
|
390
390
|
metadata = cursor.column_metadata
|
391
391
|
cm = cols.zip(metadata).map{|c, m| [c, cps[m.data_type]]}
|
392
|
-
columns = cols.reject{|x| x == rn} if offset
|
393
392
|
@columns = columns
|
394
393
|
while r = cursor.fetch
|
395
394
|
row = {}
|
396
395
|
r.zip(cm).each{|v, (c, cp)| row[c] = ((v && cp) ? cp.call(v) : v)}
|
397
|
-
row.delete(rn) if offset
|
398
396
|
yield row
|
399
397
|
end
|
400
398
|
end
|
@@ -289,7 +289,8 @@ module Sequel
|
|
289
289
|
|
290
290
|
# +copy_into+ uses PostgreSQL's +COPY FROM STDIN+ SQL statement to do very fast inserts
|
291
291
|
# into a table using input preformatting in either CSV or PostgreSQL text format.
|
292
|
-
# This method is only supported if pg is the underlying ruby driver.
|
292
|
+
# This method is only supported if pg 0.14.0+ is the underlying ruby driver.
|
293
|
+
# This method should only be called if you want
|
293
294
|
# results returned to the client. If you are using +COPY FROM+
|
294
295
|
# with a filename, you should just use +run+ instead of this method.
|
295
296
|
#
|
@@ -43,6 +43,16 @@ module Sequel
|
|
43
43
|
run(ds.into(name).sql)
|
44
44
|
end
|
45
45
|
|
46
|
+
DATABASE_ERROR_REGEXPS = {
|
47
|
+
/The changes you requested to the table were not successful because they would create duplicate values in the index, primary key, or relationship/ => UniqueConstraintViolation,
|
48
|
+
/You cannot add or change a record because a related record is required|The record cannot be deleted or changed because table/ => ForeignKeyConstraintViolation,
|
49
|
+
/One or more values are prohibited by the validation rule/ => CheckConstraintViolation,
|
50
|
+
/You must enter a value in the .+ field|cannot contain a Null value because the Required property for this field is set to True/ => NotNullConstraintViolation,
|
51
|
+
}.freeze
|
52
|
+
def database_error_regexps
|
53
|
+
DATABASE_ERROR_REGEXPS
|
54
|
+
end
|
55
|
+
|
46
56
|
# The SQL to drop an index for the table.
|
47
57
|
def drop_index_sql(table, op)
|
48
58
|
"DROP INDEX #{quote_identifier(op[:name] || default_index_name(table, op[:columns]))} ON #{quote_schema_table(table)}"
|
@@ -126,6 +126,15 @@ module Sequel
|
|
126
126
|
:query
|
127
127
|
end
|
128
128
|
|
129
|
+
DATABASE_ERROR_REGEXPS = {
|
130
|
+
/Operation would have caused one or more unique constraint violations/ => UniqueConstraintViolation,
|
131
|
+
/The constraint of the foreign key .+ is invalid|Update\/Delete operations are restricted by the foreign key/ => ForeignKeyConstraintViolation,
|
132
|
+
/cannot be made NULL/ => NotNullConstraintViolation,
|
133
|
+
}.freeze
|
134
|
+
def database_error_regexps
|
135
|
+
DATABASE_ERROR_REGEXPS
|
136
|
+
end
|
137
|
+
|
129
138
|
# CUBRID is case insensitive, so don't modify identifiers
|
130
139
|
def identifier_input_method_default
|
131
140
|
nil
|
@@ -154,6 +154,16 @@ module Sequel
|
|
154
154
|
end
|
155
155
|
end
|
156
156
|
|
157
|
+
DATABASE_ERROR_REGEXPS = {
|
158
|
+
/DB2 SQL Error: SQLCODE=-803, SQLSTATE=23505|One or more values in the INSERT statement, UPDATE statement, or foreign key update caused by a DELETE statement are not valid because the primary key, unique constraint or unique index/ => UniqueConstraintViolation,
|
159
|
+
/DB2 SQL Error: (SQLCODE=-530, SQLSTATE=23503|SQLCODE=-532, SQLSTATE=23504)|The insert or update value of the FOREIGN KEY .+ is not equal to any value of the parent key of the parent table|A parent row cannot be deleted because the relationship .+ restricts the deletion/ => ForeignKeyConstraintViolation,
|
160
|
+
/DB2 SQL Error: SQLCODE=-545, SQLSTATE=23513|The requested operation is not allowed because a row does not satisfy the check constraint/ => CheckConstraintViolation,
|
161
|
+
/DB2 SQL Error: SQLCODE=-407, SQLSTATE=23502|Assignment of a NULL value to a NOT NULL column/ => NotNullConstraintViolation,
|
162
|
+
}.freeze
|
163
|
+
def database_error_regexps
|
164
|
+
DATABASE_ERROR_REGEXPS
|
165
|
+
end
|
166
|
+
|
157
167
|
# DB2 has issues with quoted identifiers, so
|
158
168
|
# turn off database quoting by default.
|
159
169
|
def quote_identifiers_default
|
@@ -238,6 +238,16 @@ module Sequel
|
|
238
238
|
run(ds.into(name).sql)
|
239
239
|
end
|
240
240
|
|
241
|
+
DATABASE_ERROR_REGEXPS = {
|
242
|
+
/Violation of UNIQUE KEY constraint/ => UniqueConstraintViolation,
|
243
|
+
/conflicted with the (FOREIGN KEY.*|REFERENCE) constraint/ => ForeignKeyConstraintViolation,
|
244
|
+
/conflicted with the CHECK constraint/ => CheckConstraintViolation,
|
245
|
+
/column does not allow nulls/ => NotNullConstraintViolation,
|
246
|
+
}.freeze
|
247
|
+
def database_error_regexps
|
248
|
+
DATABASE_ERROR_REGEXPS
|
249
|
+
end
|
250
|
+
|
241
251
|
# The name of the constraint for setting the default value on the table and column.
|
242
252
|
# The SQL used to select default constraints utilizes MSSQL catalog views which were introduced in 2005.
|
243
253
|
# This method intentionally does not support MSSQL 2000.
|
@@ -350,6 +350,15 @@ module Sequel
|
|
350
350
|
"#{super}#{" ENGINE=#{engine}" if engine}#{" DEFAULT CHARSET=#{charset}" if charset}#{" DEFAULT COLLATE=#{collate}" if collate}"
|
351
351
|
end
|
352
352
|
|
353
|
+
DATABASE_ERROR_REGEXPS = {
|
354
|
+
/Duplicate entry .+ for key/ => UniqueConstraintViolation,
|
355
|
+
/foreign key constraint fails/ => ForeignKeyConstraintViolation,
|
356
|
+
/cannot be null/ => NotNullConstraintViolation,
|
357
|
+
}.freeze
|
358
|
+
def database_error_regexps
|
359
|
+
DATABASE_ERROR_REGEXPS
|
360
|
+
end
|
361
|
+
|
353
362
|
# Backbone of the tables and views support using SHOW FULL TABLES.
|
354
363
|
def full_tables(type, opts)
|
355
364
|
m = output_identifier_meth
|
@@ -430,6 +439,11 @@ module Sequel
|
|
430
439
|
true
|
431
440
|
end
|
432
441
|
|
442
|
+
# MySQL supports CREATE OR REPLACE VIEW.
|
443
|
+
def supports_create_or_replace_view?
|
444
|
+
true
|
445
|
+
end
|
446
|
+
|
433
447
|
# Respect the :size option if given to produce
|
434
448
|
# tinyblob, mediumblob, and longblob if :tiny,
|
435
449
|
# :medium, or :long is given.
|
@@ -136,6 +136,16 @@ module Sequel
|
|
136
136
|
sql
|
137
137
|
end
|
138
138
|
|
139
|
+
DATABASE_ERROR_REGEXPS = {
|
140
|
+
/unique constraint .+ violated/ => UniqueConstraintViolation,
|
141
|
+
/integrity constraint .+ violated/ => ForeignKeyConstraintViolation,
|
142
|
+
/check constraint .+ violated/ => CheckConstraintViolation,
|
143
|
+
/cannot insert NULL into|cannot update .+ to NULL/ => NotNullConstraintViolation,
|
144
|
+
}.freeze
|
145
|
+
def database_error_regexps
|
146
|
+
DATABASE_ERROR_REGEXPS
|
147
|
+
end
|
148
|
+
|
139
149
|
def default_sequence_name(table, column)
|
140
150
|
"seq_#{table}_#{column}"
|
141
151
|
end
|
@@ -160,6 +170,11 @@ module Sequel
|
|
160
170
|
end
|
161
171
|
end
|
162
172
|
|
173
|
+
# Oracle supports CREATE OR REPLACE VIEW.
|
174
|
+
def supports_create_or_replace_view?
|
175
|
+
true
|
176
|
+
end
|
177
|
+
|
163
178
|
# Oracle's integer/:number type handles larger values than
|
164
179
|
# most other databases's bigint types, so it should be
|
165
180
|
# safe to use for Bignum.
|
@@ -53,14 +53,14 @@ module Sequel
|
|
53
53
|
end
|
54
54
|
|
55
55
|
class CreateTableGenerator < Sequel::Schema::Generator
|
56
|
-
# Add an exclusion constraint when creating the table.
|
56
|
+
# Add an exclusion constraint when creating the table. Elements should be
|
57
57
|
# an array of 2 element arrays, with the first element being the column or
|
58
58
|
# expression the exclusion constraint is applied to, and the second element
|
59
59
|
# being the operator to use for the column/expression to check for exclusion.
|
60
60
|
#
|
61
61
|
# Example:
|
62
62
|
#
|
63
|
-
#
|
63
|
+
# exclude([[:col1, '&&'], [:col2, '=']])
|
64
64
|
# # EXCLUDE USING gist (col1 WITH &&, col2 WITH =)
|
65
65
|
#
|
66
66
|
# Options supported:
|
@@ -89,6 +89,9 @@ module Sequel
|
|
89
89
|
end
|
90
90
|
end
|
91
91
|
|
92
|
+
# Error raised when Sequel determines a PostgreSQL exclusion constraint has been violated.
|
93
|
+
class ExclusionConstraintViolation < Sequel::ConstraintViolation; end
|
94
|
+
|
92
95
|
# Methods shared by Database instances that connect to PostgreSQL.
|
93
96
|
module DatabaseMethods
|
94
97
|
EXCLUDE_SCHEMAS = /pg_*|information_schema/i
|
@@ -627,6 +630,17 @@ module Sequel
|
|
627
630
|
end
|
628
631
|
end
|
629
632
|
|
633
|
+
DATABASE_ERROR_REGEXPS = {
|
634
|
+
/duplicate key value violates unique constraint/ => UniqueConstraintViolation,
|
635
|
+
/violates foreign key constraint/ => ForeignKeyConstraintViolation,
|
636
|
+
/violates check constraint/ => CheckConstraintViolation,
|
637
|
+
/violates not-null constraint/ => NotNullConstraintViolation,
|
638
|
+
/conflicting key value violates exclusion constraint/ => ExclusionConstraintViolation,
|
639
|
+
}.freeze
|
640
|
+
def database_error_regexps
|
641
|
+
DATABASE_ERROR_REGEXPS
|
642
|
+
end
|
643
|
+
|
630
644
|
# SQL for doing fast table insert from stdin.
|
631
645
|
def copy_into_sql(table, opts)
|
632
646
|
sql = "COPY #{literal(table)}"
|
@@ -717,6 +731,11 @@ module Sequel
|
|
717
731
|
"CREATE TRIGGER #{name} #{whence} #{events.map{|e| e.to_s.upcase}.join(' OR ')} ON #{quote_schema_table(table)}#{' FOR EACH ROW' if opts[:each_row]} EXECUTE PROCEDURE #{function}(#{Array(opts[:args]).map{|a| literal(a)}.join(', ')})"
|
718
732
|
end
|
719
733
|
|
734
|
+
# DDL fragment for initial part of CREATE VIEW statement
|
735
|
+
def create_view_prefix_sql(name, options)
|
736
|
+
"CREATE #{'OR REPLACE 'if options[:replace]}#{'TEMPORARY 'if options[:temp]}VIEW #{quote_schema_table(name)}"
|
737
|
+
end
|
738
|
+
|
720
739
|
# The errors that the main adapters can raise, depends on the adapter being used
|
721
740
|
def database_error_classes
|
722
741
|
CONVERTED_EXCEPTIONS
|
@@ -919,6 +938,11 @@ module Sequel
|
|
919
938
|
true
|
920
939
|
end
|
921
940
|
|
941
|
+
# PostgreSQL supports CREATE OR REPLACE VIEW.
|
942
|
+
def supports_create_or_replace_view?
|
943
|
+
true
|
944
|
+
end
|
945
|
+
|
922
946
|
# Handle bigserial type if :serial option is present
|
923
947
|
def type_literal_generic_bignum(column)
|
924
948
|
column[:serial] ? :bigserial : super
|
@@ -312,6 +312,21 @@ module Sequel
|
|
312
312
|
ps
|
313
313
|
end
|
314
314
|
|
315
|
+
# SQLite support creating temporary views.
|
316
|
+
def create_view_prefix_sql(name, options)
|
317
|
+
"CREATE #{'TEMPORARY 'if options[:temp]}VIEW #{quote_schema_table(name)}"
|
318
|
+
end
|
319
|
+
|
320
|
+
DATABASE_ERROR_REGEXPS = {
|
321
|
+
/is not unique\z/ => UniqueConstraintViolation,
|
322
|
+
/foreign key constraint failed\z/ => ForeignKeyConstraintViolation,
|
323
|
+
/\A(SQLITE ERROR 19 \(CONSTRAINT\) : )?constraint failed\z/ => ConstraintViolation,
|
324
|
+
/may not be NULL\z/ => NotNullConstraintViolation,
|
325
|
+
}.freeze
|
326
|
+
def database_error_regexps
|
327
|
+
DATABASE_ERROR_REGEXPS
|
328
|
+
end
|
329
|
+
|
315
330
|
# The array of column schema hashes for the current columns in the table
|
316
331
|
def defined_columns_for(table)
|
317
332
|
cols = parse_pragma(table, {})
|
@@ -18,6 +18,7 @@ module Sequel
|
|
18
18
|
opts = server_opts(server)
|
19
19
|
opts[:username] = opts[:user]
|
20
20
|
c = TinyTds::Client.new(opts)
|
21
|
+
c.query_options.merge!(:cache_rows=>false)
|
21
22
|
|
22
23
|
if (ts = opts[:textsize])
|
23
24
|
sql = "SET TEXTSIZE #{typecast_value_integer(ts)}"
|
@@ -215,40 +216,12 @@ module Sequel
|
|
215
216
|
# various cases.
|
216
217
|
def fetch_rows(sql)
|
217
218
|
execute(sql) do |result|
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
columns = cols = result.fields.map{|c| output_identifier(c)}
|
222
|
-
if offset
|
223
|
-
rn = row_number_column
|
224
|
-
columns = columns.dup
|
225
|
-
columns.delete(rn)
|
226
|
-
end
|
227
|
-
@columns = columns
|
228
|
-
#if identifier_output_method
|
229
|
-
each_opts[:as] = :array
|
230
|
-
result.each(each_opts) do |r|
|
231
|
-
h = {}
|
232
|
-
cols.zip(r).each{|k, v| h[k] = v}
|
233
|
-
h.delete(rn) if rn
|
234
|
-
yield h
|
235
|
-
end
|
236
|
-
=begin
|
237
|
-
# Temporarily disable this optimization, as tiny_tds uses string keys
|
238
|
-
# if result.fields is called before result.each(:symbolize_keys=>true).
|
239
|
-
# See https://github.com/rails-sqlserver/tiny_tds/issues/57
|
219
|
+
@columns = result.fields.map!{|c| output_identifier(c)}
|
220
|
+
if db.timezone == :utc
|
221
|
+
result.each(:timezone=>:utc){|r| yield r}
|
240
222
|
else
|
241
|
-
|
242
|
-
if offset
|
243
|
-
result.each(each_opts) do |r|
|
244
|
-
r.delete(rn) if rn
|
245
|
-
yield r
|
246
|
-
end
|
247
|
-
else
|
248
|
-
result.each(each_opts, &Proc.new)
|
249
|
-
end
|
223
|
+
result.each{|r| yield r}
|
250
224
|
end
|
251
|
-
=end
|
252
225
|
end
|
253
226
|
self
|
254
227
|
end
|
@@ -1,36 +1,5 @@
|
|
1
1
|
module Sequel
|
2
2
|
module EmulateOffsetWithRowNumber
|
3
|
-
# When a subselect that uses :offset is used in IN or NOT IN,
|
4
|
-
# use a nested subselect that only includes the first column
|
5
|
-
# instead of the ROW_NUMBER column added by the emulated offset support.
|
6
|
-
def complex_expression_sql_append(sql, op, args)
|
7
|
-
case op
|
8
|
-
when :IN, :"NOT IN"
|
9
|
-
ds = args.at(1)
|
10
|
-
if ds.is_a?(Sequel::Dataset) && ds.opts[:offset]
|
11
|
-
c = ds.opts[:select].first
|
12
|
-
case c
|
13
|
-
when Symbol
|
14
|
-
t, cl, a = split_symbol(c)
|
15
|
-
if a
|
16
|
-
c = SQL::Identifier.new(a)
|
17
|
-
elsif t
|
18
|
-
c = SQL::Identifier.new(cl)
|
19
|
-
end
|
20
|
-
when SQL::AliasedExpression
|
21
|
-
c = SQL::Identifier.new(c.aliaz)
|
22
|
-
when SQL::QualifiedIdentifier
|
23
|
-
c = SQL::Identifier.new(c.column)
|
24
|
-
end
|
25
|
-
super(sql, op, [args.at(0), ds.from_self.select(c)])
|
26
|
-
else
|
27
|
-
super
|
28
|
-
end
|
29
|
-
else
|
30
|
-
super
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
3
|
# Emulate OFFSET support with the ROW_NUMBER window function
|
35
4
|
#
|
36
5
|
# The implementation is ugly, cloning the current dataset and modifying
|
@@ -47,6 +16,7 @@ module Sequel
|
|
47
16
|
raise(Error, "#{db.database_type} requires an order be provided if using an offset")
|
48
17
|
end
|
49
18
|
|
19
|
+
columns = clone(:append_sql=>'').columns
|
50
20
|
dsa1 = dataset_alias(1)
|
51
21
|
rn = row_number_column
|
52
22
|
sql = @opts[:append_sql] || ''
|
@@ -54,6 +24,7 @@ module Sequel
|
|
54
24
|
unordered.
|
55
25
|
select_append{ROW_NUMBER(:over, :order=>order){}.as(rn)}.
|
56
26
|
from_self(:alias=>dsa1).
|
27
|
+
select(*columns).
|
57
28
|
limit(@opts[:limit]).
|
58
29
|
where(SQL::Identifier.new(rn) > o).
|
59
30
|
order(rn))
|
@@ -67,11 +38,5 @@ module Sequel
|
|
67
38
|
def default_offset_order
|
68
39
|
clone(:append_sql=>'').columns
|
69
40
|
end
|
70
|
-
|
71
|
-
# This emulation adds an extra row number column that should be
|
72
|
-
# eliminated.
|
73
|
-
def offset_returns_row_number_column?
|
74
|
-
true
|
75
|
-
end
|
76
41
|
end
|
77
42
|
end
|