sequel 3.43.0 → 3.44.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. data/CHANGELOG +32 -0
  2. data/doc/association_basics.rdoc +10 -3
  3. data/doc/release_notes/3.37.0.txt +1 -1
  4. data/doc/release_notes/3.44.0.txt +152 -0
  5. data/lib/sequel/adapters/ado.rb +0 -6
  6. data/lib/sequel/adapters/db2.rb +0 -3
  7. data/lib/sequel/adapters/dbi.rb +0 -6
  8. data/lib/sequel/adapters/ibmdb.rb +0 -3
  9. data/lib/sequel/adapters/jdbc.rb +8 -12
  10. data/lib/sequel/adapters/jdbc/as400.rb +2 -18
  11. data/lib/sequel/adapters/jdbc/derby.rb +10 -0
  12. data/lib/sequel/adapters/jdbc/h2.rb +10 -0
  13. data/lib/sequel/adapters/jdbc/hsqldb.rb +10 -0
  14. data/lib/sequel/adapters/jdbc/sqlite.rb +5 -0
  15. data/lib/sequel/adapters/jdbc/sqlserver.rb +2 -4
  16. data/lib/sequel/adapters/mock.rb +5 -0
  17. data/lib/sequel/adapters/mysql2.rb +4 -13
  18. data/lib/sequel/adapters/odbc.rb +0 -5
  19. data/lib/sequel/adapters/oracle.rb +3 -5
  20. data/lib/sequel/adapters/postgres.rb +2 -1
  21. data/lib/sequel/adapters/shared/access.rb +10 -0
  22. data/lib/sequel/adapters/shared/cubrid.rb +9 -0
  23. data/lib/sequel/adapters/shared/db2.rb +10 -0
  24. data/lib/sequel/adapters/shared/mssql.rb +10 -0
  25. data/lib/sequel/adapters/shared/mysql.rb +14 -0
  26. data/lib/sequel/adapters/shared/oracle.rb +15 -0
  27. data/lib/sequel/adapters/shared/postgres.rb +26 -2
  28. data/lib/sequel/adapters/shared/sqlite.rb +15 -0
  29. data/lib/sequel/adapters/tinytds.rb +5 -32
  30. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +2 -37
  31. data/lib/sequel/core.rb +3 -3
  32. data/lib/sequel/database/misc.rb +40 -4
  33. data/lib/sequel/database/query.rb +1 -1
  34. data/lib/sequel/database/schema_methods.rb +33 -12
  35. data/lib/sequel/dataset/actions.rb +51 -2
  36. data/lib/sequel/dataset/features.rb +0 -6
  37. data/lib/sequel/dataset/sql.rb +1 -1
  38. data/lib/sequel/exceptions.rb +22 -7
  39. data/lib/sequel/extensions/columns_introspection.rb +30 -5
  40. data/lib/sequel/extensions/pg_auto_parameterize.rb +9 -0
  41. data/lib/sequel/model/associations.rb +50 -37
  42. data/lib/sequel/model/base.rb +30 -1
  43. data/lib/sequel/plugins/eager_each.rb +17 -21
  44. data/lib/sequel/plugins/identity_map.rb +2 -1
  45. data/lib/sequel/plugins/many_through_many.rb +1 -1
  46. data/lib/sequel/plugins/single_table_inheritance.rb +2 -2
  47. data/lib/sequel/plugins/tactical_eager_loading.rb +1 -1
  48. data/lib/sequel/sql.rb +4 -2
  49. data/lib/sequel/version.rb +1 -1
  50. data/spec/adapters/postgres_spec.rb +32 -2
  51. data/spec/adapters/sqlite_spec.rb +20 -0
  52. data/spec/core/database_spec.rb +40 -0
  53. data/spec/core/dataset_spec.rb +91 -4
  54. data/spec/core/mock_adapter_spec.rb +2 -1
  55. data/spec/core/schema_generator_spec.rb +4 -0
  56. data/spec/core/schema_spec.rb +9 -3
  57. data/spec/extensions/association_dependencies_spec.rb +3 -3
  58. data/spec/extensions/columns_introspection_spec.rb +28 -2
  59. data/spec/extensions/eager_each_spec.rb +0 -1
  60. data/spec/extensions/identity_map_spec.rb +3 -2
  61. data/spec/extensions/migration_spec.rb +6 -0
  62. data/spec/extensions/prepared_statements_associations_spec.rb +2 -2
  63. data/spec/extensions/rcte_tree_spec.rb +2 -2
  64. data/spec/extensions/single_table_inheritance_spec.rb +3 -0
  65. data/spec/extensions/tactical_eager_loading_spec.rb +1 -1
  66. data/spec/extensions/validation_class_methods_spec.rb +8 -0
  67. data/spec/integration/associations_test.rb +4 -4
  68. data/spec/integration/database_test.rb +68 -20
  69. data/spec/integration/dataset_test.rb +48 -0
  70. data/spec/integration/schema_test.rb +25 -1
  71. data/spec/model/associations_spec.rb +21 -8
  72. data/spec/model/dataset_methods_spec.rb +58 -18
  73. 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, rn)
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, rn)
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
@@ -66,6 +66,11 @@ module Sequel
66
66
  db.instance_eval do
67
67
  @primary_key_sequences = {}
68
68
  end
69
+ end,
70
+ 'mssql' => lambda do |db|
71
+ db.instance_eval do
72
+ @server_version = 10000000
73
+ end
69
74
  end
70
75
  }
71
76
 
@@ -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
- cols = r.fields
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
- @columns = r.fields
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
@@ -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
- conn.prefetch_rows = typecast_value_integer(opts.fetch(:prefetch_rows, 100))
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. This method should only be called if you want
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. elements should be
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
- # exclusion_constraint([[:col1, '&&'], [:col2, '=']])
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
- each_opts = {:cache_rows=>false}
219
- each_opts[:timezone] = :utc if db.timezone == :utc
220
- rn = row_number_column if offset = @opts[:offset]
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
- each_opts[:symbolize_keys] = true
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