sequel 3.36.1 → 3.37.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. data/CHANGELOG +84 -0
  2. data/Rakefile +13 -0
  3. data/bin/sequel +12 -16
  4. data/doc/advanced_associations.rdoc +36 -67
  5. data/doc/association_basics.rdoc +11 -16
  6. data/doc/release_notes/3.37.0.txt +338 -0
  7. data/doc/schema_modification.rdoc +4 -0
  8. data/lib/sequel/adapters/jdbc/h2.rb +1 -1
  9. data/lib/sequel/adapters/jdbc/postgresql.rb +26 -8
  10. data/lib/sequel/adapters/mysql2.rb +4 -3
  11. data/lib/sequel/adapters/odbc/mssql.rb +2 -2
  12. data/lib/sequel/adapters/postgres.rb +4 -60
  13. data/lib/sequel/adapters/shared/mssql.rb +2 -1
  14. data/lib/sequel/adapters/shared/mysql.rb +0 -5
  15. data/lib/sequel/adapters/shared/postgres.rb +68 -2
  16. data/lib/sequel/adapters/shared/sqlite.rb +17 -1
  17. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +12 -1
  18. data/lib/sequel/adapters/utils/pg_types.rb +76 -0
  19. data/lib/sequel/core.rb +13 -0
  20. data/lib/sequel/database/misc.rb +41 -1
  21. data/lib/sequel/database/schema_generator.rb +23 -10
  22. data/lib/sequel/database/schema_methods.rb +26 -4
  23. data/lib/sequel/dataset/graph.rb +2 -1
  24. data/lib/sequel/dataset/query.rb +62 -2
  25. data/lib/sequel/extensions/_pretty_table.rb +7 -3
  26. data/lib/sequel/extensions/arbitrary_servers.rb +5 -4
  27. data/lib/sequel/extensions/blank.rb +4 -0
  28. data/lib/sequel/extensions/columns_introspection.rb +13 -2
  29. data/lib/sequel/extensions/core_extensions.rb +6 -0
  30. data/lib/sequel/extensions/eval_inspect.rb +158 -0
  31. data/lib/sequel/extensions/inflector.rb +4 -0
  32. data/lib/sequel/extensions/looser_typecasting.rb +5 -4
  33. data/lib/sequel/extensions/migration.rb +4 -1
  34. data/lib/sequel/extensions/named_timezones.rb +4 -0
  35. data/lib/sequel/extensions/null_dataset.rb +4 -0
  36. data/lib/sequel/extensions/pagination.rb +4 -0
  37. data/lib/sequel/extensions/pg_array.rb +219 -168
  38. data/lib/sequel/extensions/pg_array_ops.rb +7 -2
  39. data/lib/sequel/extensions/pg_auto_parameterize.rb +10 -4
  40. data/lib/sequel/extensions/pg_hstore.rb +3 -1
  41. data/lib/sequel/extensions/pg_hstore_ops.rb +7 -2
  42. data/lib/sequel/extensions/pg_inet.rb +28 -3
  43. data/lib/sequel/extensions/pg_interval.rb +192 -0
  44. data/lib/sequel/extensions/pg_json.rb +21 -9
  45. data/lib/sequel/extensions/pg_range.rb +487 -0
  46. data/lib/sequel/extensions/pg_range_ops.rb +122 -0
  47. data/lib/sequel/extensions/pg_statement_cache.rb +3 -2
  48. data/lib/sequel/extensions/pretty_table.rb +12 -1
  49. data/lib/sequel/extensions/query.rb +4 -0
  50. data/lib/sequel/extensions/query_literals.rb +6 -6
  51. data/lib/sequel/extensions/schema_dumper.rb +39 -38
  52. data/lib/sequel/extensions/select_remove.rb +4 -0
  53. data/lib/sequel/extensions/server_block.rb +3 -2
  54. data/lib/sequel/extensions/split_array_nil.rb +65 -0
  55. data/lib/sequel/extensions/sql_expr.rb +4 -0
  56. data/lib/sequel/extensions/string_date_time.rb +4 -0
  57. data/lib/sequel/extensions/thread_local_timezones.rb +9 -3
  58. data/lib/sequel/extensions/to_dot.rb +4 -0
  59. data/lib/sequel/model/associations.rb +150 -91
  60. data/lib/sequel/plugins/identity_map.rb +2 -2
  61. data/lib/sequel/plugins/list.rb +1 -0
  62. data/lib/sequel/plugins/many_through_many.rb +33 -32
  63. data/lib/sequel/plugins/nested_attributes.rb +11 -3
  64. data/lib/sequel/plugins/rcte_tree.rb +2 -2
  65. data/lib/sequel/plugins/schema.rb +1 -1
  66. data/lib/sequel/sql.rb +14 -14
  67. data/lib/sequel/version.rb +2 -2
  68. data/spec/adapters/mysql_spec.rb +25 -0
  69. data/spec/adapters/postgres_spec.rb +572 -28
  70. data/spec/adapters/sqlite_spec.rb +16 -1
  71. data/spec/core/database_spec.rb +61 -2
  72. data/spec/core/dataset_spec.rb +92 -0
  73. data/spec/core/expression_filters_spec.rb +12 -0
  74. data/spec/extensions/arbitrary_servers_spec.rb +1 -1
  75. data/spec/extensions/boolean_readers_spec.rb +25 -25
  76. data/spec/extensions/eval_inspect_spec.rb +58 -0
  77. data/spec/extensions/json_serializer_spec.rb +0 -6
  78. data/spec/extensions/list_spec.rb +1 -1
  79. data/spec/extensions/looser_typecasting_spec.rb +7 -7
  80. data/spec/extensions/many_through_many_spec.rb +81 -0
  81. data/spec/extensions/nested_attributes_spec.rb +21 -4
  82. data/spec/extensions/pg_array_ops_spec.rb +1 -11
  83. data/spec/extensions/pg_array_spec.rb +181 -90
  84. data/spec/extensions/pg_auto_parameterize_spec.rb +3 -3
  85. data/spec/extensions/pg_hstore_spec.rb +1 -3
  86. data/spec/extensions/pg_inet_spec.rb +6 -1
  87. data/spec/extensions/pg_interval_spec.rb +73 -0
  88. data/spec/extensions/pg_json_spec.rb +5 -9
  89. data/spec/extensions/pg_range_ops_spec.rb +49 -0
  90. data/spec/extensions/pg_range_spec.rb +372 -0
  91. data/spec/extensions/pg_statement_cache_spec.rb +1 -2
  92. data/spec/extensions/query_literals_spec.rb +1 -2
  93. data/spec/extensions/schema_dumper_spec.rb +48 -89
  94. data/spec/extensions/serialization_spec.rb +1 -5
  95. data/spec/extensions/server_block_spec.rb +2 -2
  96. data/spec/extensions/spec_helper.rb +12 -2
  97. data/spec/extensions/split_array_nil_spec.rb +24 -0
  98. data/spec/integration/associations_test.rb +4 -4
  99. data/spec/integration/database_test.rb +2 -2
  100. data/spec/integration/dataset_test.rb +4 -4
  101. data/spec/integration/eager_loader_test.rb +6 -6
  102. data/spec/integration/plugin_test.rb +2 -2
  103. data/spec/integration/spec_helper.rb +2 -2
  104. data/spec/model/association_reflection_spec.rb +5 -0
  105. data/spec/model/associations_spec.rb +156 -49
  106. data/spec/model/eager_loading_spec.rb +137 -2
  107. data/spec/model/model_spec.rb +10 -10
  108. metadata +15 -2
@@ -98,6 +98,10 @@ You've seen this one used already. It's used to create an autoincrementing inte
98
98
 
99
99
  create_table(:a0){primary_key :id}
100
100
 
101
+ If you want an autoincrementing 64-bit integer:
102
+
103
+ create_table(:a0){primary_key :id, :type=>Bignum}
104
+
101
105
  If you want to create a primary key column that doesn't use an autoincrementing integer, you should
102
106
  not use this method. Instead, you should use the :primary_key option to the +column+ method or type
103
107
  method:
@@ -112,7 +112,7 @@ module Sequel
112
112
 
113
113
  # Treat clob as string instead of blob
114
114
  def schema_column_type(db_type)
115
- db_type == 'clob' ? :string : super
115
+ db_type.downcase == 'clob' ? :string : super
116
116
  end
117
117
 
118
118
  # Use BIGINT IDENTITY for identity columns that use bigint, fixes
@@ -45,24 +45,43 @@ module Sequel
45
45
  APOS = Dataset::APOS
46
46
 
47
47
  class ::Sequel::JDBC::Dataset::TYPE_TRANSLATOR
48
- # Convert Java::OrgPostgresqlJdbc4::Jdbc4Array to ruby arrays
49
- def pg_array(v)
50
- _pg_array(v.array)
51
- end
52
-
53
48
  # Convert Java::OrgPostgresqlUtil::PGobject to ruby strings
54
49
  def pg_object(v)
55
50
  v.to_string
56
51
  end
52
+ end
53
+
54
+ # Handle conversions of PostgreSQL array instances
55
+ class PGArrayConverter
56
+ # Set the method that will return the correct conversion
57
+ # proc for elements of this array.
58
+ def initialize(meth)
59
+ @conversion_proc_method = meth
60
+ @conversion_proc = nil
61
+ end
62
+
63
+ # Convert Java::OrgPostgresqlJdbc4::Jdbc4Array to ruby arrays
64
+ def call(v)
65
+ _pg_array(v.array)
66
+ end
57
67
 
58
68
  private
59
69
 
60
70
  # Handle multi-dimensional Java arrays by recursively mapping them
61
- # to ruby arrays.
71
+ # to ruby arrays of ruby values.
62
72
  def _pg_array(v)
63
73
  v.to_ary.map do |i|
64
74
  if i.respond_to?(:to_ary)
65
75
  _pg_array(i)
76
+ elsif i
77
+ if @conversion_proc.nil?
78
+ @conversion_proc = @conversion_proc_method.call(i)
79
+ end
80
+ if @conversion_proc
81
+ @conversion_proc.call(i)
82
+ else
83
+ i
84
+ end
66
85
  else
67
86
  i
68
87
  end
@@ -70,7 +89,6 @@ module Sequel
70
89
  end
71
90
  end
72
91
 
73
- PG_ARRAY_METHOD = TYPE_TRANSLATOR_INSTANCE.method(:pg_array)
74
92
  PG_OBJECT_METHOD = TYPE_TRANSLATOR_INSTANCE.method(:pg_object)
75
93
 
76
94
  # Add the shared PostgreSQL prepared statement methods
@@ -88,7 +106,7 @@ module Sequel
88
106
  def convert_type_proc(v)
89
107
  case v
90
108
  when Java::OrgPostgresqlJdbc4::Jdbc4Array
91
- PG_ARRAY_METHOD
109
+ PGArrayConverter.new(method(:convert_type_proc))
92
110
  when Java::OrgPostgresqlUtil::PGobject
93
111
  PG_OBJECT_METHOD
94
112
  else
@@ -47,6 +47,7 @@ module Sequel
47
47
  opts[:username] ||= opts[:user]
48
48
  opts[:flags] = ::Mysql2::Client::FOUND_ROWS if ::Mysql2::Client.const_defined?(:FOUND_ROWS)
49
49
  conn = ::Mysql2::Client.new(opts)
50
+ conn.query_options.merge!(:symbolize_keys=>true, :cache_rows=>false)
50
51
 
51
52
  sqls = mysql_connection_setting_sqls
52
53
 
@@ -86,7 +87,7 @@ module Sequel
86
87
  # yield the connection if a block is given.
87
88
  def _execute(conn, sql, opts)
88
89
  begin
89
- r = log_yield((log_sql = opts[:log_sql]) ? sql + log_sql : sql){conn.query(sql, :symbolize_keys => true, :database_timezone => timezone, :application_timezone => Sequel.application_timezone)}
90
+ r = log_yield((log_sql = opts[:log_sql]) ? sql + log_sql : sql){conn.query(sql, :database_timezone => timezone, :application_timezone => Sequel.application_timezone, :cast_booleans => convert_tinyint_to_bool)}
90
91
  if opts[:type] == :select
91
92
  yield r if r
92
93
  elsif block_given?
@@ -149,7 +150,7 @@ module Sequel
149
150
  cols = r.fields
150
151
  @columns = cols2 = cols.map{|c| output_identifier(c.to_s)}
151
152
  cs = cols.zip(cols2)
152
- r.each(:cast_booleans => db.convert_tinyint_to_bool) do |row|
153
+ r.each do |row|
153
154
  h = {}
154
155
  cs.each do |a, b|
155
156
  h[b] = row[a]
@@ -158,7 +159,7 @@ module Sequel
158
159
  end
159
160
  else
160
161
  @columns = r.fields
161
- r.each(:cast_booleans => db.convert_tinyint_to_bool){|h| yield h}
162
+ r.each{|h| yield h}
162
163
  end
163
164
  end
164
165
  self
@@ -16,8 +16,8 @@ module Sequel
16
16
  log_yield(sql){conn.do(sql)}
17
17
  begin
18
18
  s = log_yield(LAST_INSERT_ID_SQL){conn.run(LAST_INSERT_ID_SQL)}
19
- if (rows = s.fetch_all) and (row = rows.first)
20
- Integer(row.first)
19
+ if (rows = s.fetch_all) and (row = rows.first) and (v = row.first)
20
+ Integer(v)
21
21
  end
22
22
  ensure
23
23
  s.drop if s
@@ -1,4 +1,5 @@
1
1
  Sequel.require 'adapters/shared/postgres'
2
+ Sequel.require 'adapters/utils/pg_types'
2
3
 
3
4
  begin
4
5
  require 'pg'
@@ -87,68 +88,11 @@ module Sequel
87
88
  Dataset::NON_SQL_OPTIONS << :cursor
88
89
  module Postgres
89
90
  CONVERTED_EXCEPTIONS << PGError
90
-
91
- NAN = 0.0/0.0
92
- PLUS_INFINITY = 1.0/0.0
93
- MINUS_INFINITY = -1.0/0.0
94
- NAN_STR = 'NaN'.freeze
95
- PLUS_INFINITY_STR = 'Infinity'.freeze
96
- MINUS_INFINITY_STR = '-Infinity'.freeze
97
- TRUE_STR = 't'.freeze
98
- DASH_STR = '-'.freeze
99
-
100
- TYPE_TRANSLATOR = tt = Class.new do
101
- def boolean(s) s == TRUE_STR end
91
+
92
+ PG_TYPES[17] = Class.new do
102
93
  def bytea(s) ::Sequel::SQL::Blob.new(Adapter.unescape_bytea(s)) end
103
- def integer(s) s.to_i end
104
- def float(s)
105
- case s
106
- when NAN_STR
107
- NAN
108
- when PLUS_INFINITY_STR
109
- PLUS_INFINITY
110
- when MINUS_INFINITY_STR
111
- MINUS_INFINITY
112
- else
113
- s.to_f
114
- end
115
- end
116
- def date(s) ::Date.new(*s.split(DASH_STR).map{|x| x.to_i}) end
117
- end.new
118
-
119
- # Hash with type name strings/symbols and callable values for converting PostgreSQL types.
120
- # Non-builtin types that don't have fixed numbers should use this to register
121
- # conversion procs.
122
- PG_NAMED_TYPES = {} unless defined?(PG_NAMED_TYPES)
123
-
124
- # Hash with integer keys and callable values for converting PostgreSQL types.
125
- PG_TYPES = {} unless defined?(PG_TYPES)
126
-
127
- {
128
- [16] => tt.method(:boolean),
129
- [17] => tt.method(:bytea),
130
- [20, 21, 22, 23, 26] => tt.method(:integer),
131
- [700, 701] => tt.method(:float),
132
- [790, 1700] => ::BigDecimal.method(:new),
133
- [1083, 1266] => ::Sequel.method(:string_to_time),
134
- }.each do |k,v|
135
- k.each{|n| PG_TYPES[n] = v}
136
- end
137
-
138
- class << self
139
- # As an optimization, Sequel sets the date style to ISO, so that PostgreSQL provides
140
- # the date in a known format that Sequel can parse faster. This can be turned off
141
- # if you require a date style other than ISO.
142
- attr_reader :use_iso_date_format
143
- end
94
+ end.new.method(:bytea)
144
95
 
145
- # Modify the type translator for the date type depending on the value given.
146
- def self.use_iso_date_format=(v)
147
- PG_TYPES[1082] = v ? TYPE_TRANSLATOR.method(:date) : Sequel.method(:string_to_date)
148
- @use_iso_date_format = v
149
- end
150
- self.use_iso_date_format = true
151
-
152
96
  # PGconn subclass for connection specific methods used with the
153
97
  # pg, postgres, or postgres-pr driver.
154
98
  class Adapter < ::PGconn
@@ -316,7 +316,7 @@ module Sequel
316
316
  DELETE_CLAUSE_METHODS = Dataset.clause_methods(:delete, %w'with delete from output from2 where')
317
317
  INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'with insert into columns output values')
318
318
  SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'with select distinct limit columns into from lock join where group having order compounds')
319
- UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'with update table set output from where')
319
+ UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'with update limit table set output from where')
320
320
  NOLOCK = ' WITH (NOLOCK)'.freeze
321
321
  UPDLOCK = ' WITH (UPDLOCK)'.freeze
322
322
  WILDCARD = LiteralString.new('*').freeze
@@ -690,6 +690,7 @@ module Sequel
690
690
  end
691
691
  end
692
692
  end
693
+ alias update_limit_sql select_limit_sql
693
694
 
694
695
  # Support different types of locking styles
695
696
  def select_lock_sql(sql)
@@ -369,11 +369,6 @@ module Sequel
369
369
  end
370
370
  end
371
371
 
372
- # MySQL treats integer primary keys as autoincrementing.
373
- def schema_autoincrementing_primary_key?(schema)
374
- super and schema[:db_type] =~ /int/io
375
- end
376
-
377
372
  # Use the MySQL specific DESCRIBE syntax to get a table description.
378
373
  def schema_parse_table(table_name, opts)
379
374
  m = output_identifier_meth(opts[:dataset])
@@ -50,6 +50,43 @@ module Sequel
50
50
  attr_accessor :force_standard_strings
51
51
  end
52
52
 
53
+ class CreateTableGenerator < Sequel::Schema::Generator
54
+ # Add an exclusion constraint when creating the table. elements should be
55
+ # an array of 2 element arrays, with the first element being the column or
56
+ # expression the exclusion constraint is applied to, and the second element
57
+ # being the operator to use for the column/expression to check for exclusion.
58
+ #
59
+ # Example:
60
+ #
61
+ # exclusion_constraint([[:col1, '&&'], [:col2, '=']])
62
+ # # EXCLUDE USING gist (col1 WITH &&, col2 WITH =)
63
+ #
64
+ # Options supported:
65
+ #
66
+ # :name :: Name the constraint with the given name (useful if you may
67
+ # need to drop the constraint later)
68
+ # :using :: Override the index_method for the exclusion constraint (defaults to gist).
69
+ # :where :: Create a partial exclusion constraint, which only affects
70
+ # a subset of table rows, value should be a filter expression.
71
+ def exclude(elements, opts={})
72
+ constraints << {:type => :exclude, :elements => elements}.merge(opts)
73
+ end
74
+ end
75
+
76
+ class AlterTableGenerator < Sequel::Schema::AlterTableGenerator
77
+ # Adds an exclusion constraint to an existing table, see
78
+ # CreateTableGenerator#exclude.
79
+ def add_exclusion_constraint(elements, opts={})
80
+ @operations << {:op => :add_constraint, :type => :exclude, :elements => elements}.merge(opts)
81
+ end
82
+
83
+ # Validate the constraint with the given name, which should have
84
+ # been added previously with NOT VALID.
85
+ def validate_constraint(name)
86
+ @operations << {:op => :validate_constraint, :name => name}
87
+ end
88
+ end
89
+
53
90
  # Methods shared by Database instances that connect to PostgreSQL.
54
91
  module DatabaseMethods
55
92
  EXCLUDE_SCHEMAS = /pg_*|information_schema/i
@@ -444,7 +481,12 @@ module Sequel
444
481
 
445
482
  private
446
483
 
447
- # Handle :using option for set_column_type op.
484
+ # Use a PostgreSQL-specific alter table generator
485
+ def alter_table_generator_class
486
+ Postgres::AlterTableGenerator
487
+ end
488
+
489
+ # Handle :using option for set_column_type op, and the :validate_constraint op.
448
490
  def alter_table_sql(table, op)
449
491
  case op[:op]
450
492
  when :set_column_type
@@ -455,6 +497,8 @@ module Sequel
455
497
  s << literal(using)
456
498
  end
457
499
  s
500
+ when :validate_constraint
501
+ "ALTER TABLE #{quote_schema_table(table)} VALIDATE CONSTRAINT #{quote_identifier(op[:name])}"
458
502
  else
459
503
  super
460
504
  end
@@ -499,6 +543,23 @@ module Sequel
499
543
  sqls
500
544
  end
501
545
 
546
+ # Handle exclusion constraints.
547
+ def constraint_definition_sql(constraint)
548
+ case constraint[:type]
549
+ when :exclude
550
+ elements = constraint[:elements].map{|c, op| "#{literal(c)} WITH #{op}"}.join(', ')
551
+ "#{"CONSTRAINT #{quote_identifier(constraint[:name])} " if constraint[:name]}EXCLUDE USING #{constraint[:using]||'gist'} (#{elements})#{" WHERE #{filter_expr(constraint[:where])}" if constraint[:where]}"
552
+ when :foreign_key
553
+ sql = super
554
+ if constraint[:not_valid]
555
+ sql << " NOT VALID"
556
+ end
557
+ sql
558
+ else
559
+ super
560
+ end
561
+ end
562
+
502
563
  # SQL statement to create database function.
503
564
  def create_function_sql(name, definition, opts={})
504
565
  args = opts[:args]
@@ -530,6 +591,11 @@ module Sequel
530
591
  "CREATE SCHEMA #{quote_identifier(name)}"
531
592
  end
532
593
 
594
+ # Use a PostgreSQL-specific create table generator
595
+ def create_table_generator_class
596
+ Postgres::CreateTableGenerator
597
+ end
598
+
533
599
  # SQL for creating a database trigger.
534
600
  def create_trigger_sql(table, name, function, opts={})
535
601
  events = opts[:events] ? Array(opts[:events]) : [:insert, :update, :delete]
@@ -642,7 +708,7 @@ module Sequel
642
708
  # PostgreSQL's autoincrementing primary keys are of type integer or bigint
643
709
  # using a nextval function call as a default.
644
710
  def schema_autoincrementing_primary_key?(schema)
645
- super and schema[:db_type] =~ /\A(?:integer|bigint)\z/io and schema[:default]=~/\Anextval/io
711
+ super && schema[:default] =~ /\Anextval/io
646
712
  end
647
713
 
648
714
  # The dataset used for parsing table schemas, using the pg_* system catalogs.
@@ -383,7 +383,7 @@ module Sequel
383
383
 
384
384
  # SQLite treats integer primary keys as autoincrementing (alias of rowid).
385
385
  def schema_autoincrementing_primary_key?(schema)
386
- super and schema[:db_type].downcase == 'integer'
386
+ super && schema[:db_type].downcase == 'integer'
387
387
  end
388
388
 
389
389
  # SQLite supports schema parsing using the table_info PRAGMA, so
@@ -428,6 +428,22 @@ module Sequel
428
428
  DOUBLE_BACKTICK = '``'.freeze
429
429
  BLOB_START = "X'".freeze
430
430
  HSTAR = "H*".freeze
431
+ DATE_OPEN = "date(".freeze
432
+ DATETIME_OPEN = "datetime(".freeze
433
+
434
+ def cast_sql_append(sql, expr, type)
435
+ if type == Time or type == DateTime
436
+ sql << DATETIME_OPEN
437
+ literal_append(sql, expr)
438
+ sql << PAREN_CLOSE
439
+ elsif type == Date
440
+ sql << DATE_OPEN
441
+ literal_append(sql, expr)
442
+ sql << PAREN_CLOSE
443
+ else
444
+ super
445
+ end
446
+ end
431
447
 
432
448
  # SQLite does not support pattern matching via regular expressions.
433
449
  # SQLite is case insensitive (depending on pragma), so use LIKE for
@@ -41,7 +41,12 @@ module Sequel
41
41
  # requires an order.
42
42
  def select_sql
43
43
  return super unless o = @opts[:offset]
44
- raise(Error, "#{db.database_type} requires an order be provided if using an offset") unless order = @opts[:order]
44
+
45
+ order = @opts[:order] || default_offset_order
46
+ if order.nil? || order.empty?
47
+ raise(Error, "#{db.database_type} requires an order be provided if using an offset")
48
+ end
49
+
45
50
  dsa1 = dataset_alias(1)
46
51
  rn = row_number_column
47
52
  sql = @opts[:append_sql] || ''
@@ -57,6 +62,12 @@ module Sequel
57
62
 
58
63
  private
59
64
 
65
+ # The default order to use for datasets with offsets, if no order is defined.
66
+ # By default, orders by all of the columns in the dataset.
67
+ def default_offset_order
68
+ clone(:append_sql=>'').columns
69
+ end
70
+
60
71
  # This emulation adds an extra row number column that should be
61
72
  # eliminated.
62
73
  def offset_returns_row_number_column?
@@ -0,0 +1,76 @@
1
+ module Sequel
2
+ module Postgres
3
+ NAN = 0.0/0.0
4
+ PLUS_INFINITY = 1.0/0.0
5
+ MINUS_INFINITY = -1.0/0.0
6
+ NAN_STR = 'NaN'.freeze
7
+ PLUS_INFINITY_STR = 'Infinity'.freeze
8
+ MINUS_INFINITY_STR = '-Infinity'.freeze
9
+ TRUE_STR = 't'.freeze
10
+ DASH_STR = '-'.freeze
11
+
12
+ TYPE_TRANSLATOR = tt = Class.new do
13
+ def boolean(s) s == TRUE_STR end
14
+ def integer(s) s.to_i end
15
+ def float(s)
16
+ case s
17
+ when NAN_STR
18
+ NAN
19
+ when PLUS_INFINITY_STR
20
+ PLUS_INFINITY
21
+ when MINUS_INFINITY_STR
22
+ MINUS_INFINITY
23
+ else
24
+ s.to_f
25
+ end
26
+ end
27
+ def date(s) ::Date.new(*s.split(DASH_STR).map{|x| x.to_i}) end
28
+ def bytea(str)
29
+ str = if str =~ /\A\\x/
30
+ # PostgreSQL 9.0+ bytea hex format
31
+ str[2..-1].gsub(/(..)/){|s| s.to_i(16).chr}
32
+ else
33
+ # Historical PostgreSQL bytea escape format
34
+ str.gsub(/\\(\\|'|[0-3][0-7][0-7])/) {|s|
35
+ if s.size == 2 then s[1,1] else s[1,3].oct.chr end
36
+ }
37
+ end
38
+ ::Sequel::SQL::Blob.new(str)
39
+ end
40
+ end.new
41
+
42
+ # Hash with type name strings/symbols and callable values for converting PostgreSQL types.
43
+ # Non-builtin types that don't have fixed numbers should use this to register
44
+ # conversion procs.
45
+ PG_NAMED_TYPES = {} unless defined?(PG_NAMED_TYPES)
46
+
47
+ # Hash with integer keys and callable values for converting PostgreSQL types.
48
+ PG_TYPES = {} unless defined?(PG_TYPES)
49
+
50
+ {
51
+ [16] => tt.method(:boolean),
52
+ [17] => tt.method(:bytea),
53
+ [20, 21, 23, 26] => tt.method(:integer),
54
+ [700, 701] => tt.method(:float),
55
+ [1700] => ::BigDecimal.method(:new),
56
+ [1083, 1266] => ::Sequel.method(:string_to_time),
57
+ [1184, 1114] => ::Sequel.method(:database_to_application_timestamp),
58
+ }.each do |k,v|
59
+ k.each{|n| PG_TYPES[n] = v}
60
+ end
61
+
62
+ class << self
63
+ # As an optimization, Sequel sets the date style to ISO, so that PostgreSQL provides
64
+ # the date in a known format that Sequel can parse faster. This can be turned off
65
+ # if you require a date style other than ISO.
66
+ attr_reader :use_iso_date_format
67
+ end
68
+
69
+ # Modify the type translator for the date type depending on the value given.
70
+ def self.use_iso_date_format=(v)
71
+ PG_TYPES[1082] = v ? TYPE_TRANSLATOR.method(:date) : Sequel.method(:string_to_date)
72
+ @use_iso_date_format = v
73
+ end
74
+ self.use_iso_date_format = true
75
+ end
76
+ end