sequel 3.36.1 → 3.37.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +84 -0
- data/Rakefile +13 -0
- data/bin/sequel +12 -16
- data/doc/advanced_associations.rdoc +36 -67
- data/doc/association_basics.rdoc +11 -16
- data/doc/release_notes/3.37.0.txt +338 -0
- data/doc/schema_modification.rdoc +4 -0
- data/lib/sequel/adapters/jdbc/h2.rb +1 -1
- data/lib/sequel/adapters/jdbc/postgresql.rb +26 -8
- data/lib/sequel/adapters/mysql2.rb +4 -3
- data/lib/sequel/adapters/odbc/mssql.rb +2 -2
- data/lib/sequel/adapters/postgres.rb +4 -60
- data/lib/sequel/adapters/shared/mssql.rb +2 -1
- data/lib/sequel/adapters/shared/mysql.rb +0 -5
- data/lib/sequel/adapters/shared/postgres.rb +68 -2
- data/lib/sequel/adapters/shared/sqlite.rb +17 -1
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +12 -1
- data/lib/sequel/adapters/utils/pg_types.rb +76 -0
- data/lib/sequel/core.rb +13 -0
- data/lib/sequel/database/misc.rb +41 -1
- data/lib/sequel/database/schema_generator.rb +23 -10
- data/lib/sequel/database/schema_methods.rb +26 -4
- data/lib/sequel/dataset/graph.rb +2 -1
- data/lib/sequel/dataset/query.rb +62 -2
- data/lib/sequel/extensions/_pretty_table.rb +7 -3
- data/lib/sequel/extensions/arbitrary_servers.rb +5 -4
- data/lib/sequel/extensions/blank.rb +4 -0
- data/lib/sequel/extensions/columns_introspection.rb +13 -2
- data/lib/sequel/extensions/core_extensions.rb +6 -0
- data/lib/sequel/extensions/eval_inspect.rb +158 -0
- data/lib/sequel/extensions/inflector.rb +4 -0
- data/lib/sequel/extensions/looser_typecasting.rb +5 -4
- data/lib/sequel/extensions/migration.rb +4 -1
- data/lib/sequel/extensions/named_timezones.rb +4 -0
- data/lib/sequel/extensions/null_dataset.rb +4 -0
- data/lib/sequel/extensions/pagination.rb +4 -0
- data/lib/sequel/extensions/pg_array.rb +219 -168
- data/lib/sequel/extensions/pg_array_ops.rb +7 -2
- data/lib/sequel/extensions/pg_auto_parameterize.rb +10 -4
- data/lib/sequel/extensions/pg_hstore.rb +3 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +7 -2
- data/lib/sequel/extensions/pg_inet.rb +28 -3
- data/lib/sequel/extensions/pg_interval.rb +192 -0
- data/lib/sequel/extensions/pg_json.rb +21 -9
- data/lib/sequel/extensions/pg_range.rb +487 -0
- data/lib/sequel/extensions/pg_range_ops.rb +122 -0
- data/lib/sequel/extensions/pg_statement_cache.rb +3 -2
- data/lib/sequel/extensions/pretty_table.rb +12 -1
- data/lib/sequel/extensions/query.rb +4 -0
- data/lib/sequel/extensions/query_literals.rb +6 -6
- data/lib/sequel/extensions/schema_dumper.rb +39 -38
- data/lib/sequel/extensions/select_remove.rb +4 -0
- data/lib/sequel/extensions/server_block.rb +3 -2
- data/lib/sequel/extensions/split_array_nil.rb +65 -0
- data/lib/sequel/extensions/sql_expr.rb +4 -0
- data/lib/sequel/extensions/string_date_time.rb +4 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +9 -3
- data/lib/sequel/extensions/to_dot.rb +4 -0
- data/lib/sequel/model/associations.rb +150 -91
- data/lib/sequel/plugins/identity_map.rb +2 -2
- data/lib/sequel/plugins/list.rb +1 -0
- data/lib/sequel/plugins/many_through_many.rb +33 -32
- data/lib/sequel/plugins/nested_attributes.rb +11 -3
- data/lib/sequel/plugins/rcte_tree.rb +2 -2
- data/lib/sequel/plugins/schema.rb +1 -1
- data/lib/sequel/sql.rb +14 -14
- data/lib/sequel/version.rb +2 -2
- data/spec/adapters/mysql_spec.rb +25 -0
- data/spec/adapters/postgres_spec.rb +572 -28
- data/spec/adapters/sqlite_spec.rb +16 -1
- data/spec/core/database_spec.rb +61 -2
- data/spec/core/dataset_spec.rb +92 -0
- data/spec/core/expression_filters_spec.rb +12 -0
- data/spec/extensions/arbitrary_servers_spec.rb +1 -1
- data/spec/extensions/boolean_readers_spec.rb +25 -25
- data/spec/extensions/eval_inspect_spec.rb +58 -0
- data/spec/extensions/json_serializer_spec.rb +0 -6
- data/spec/extensions/list_spec.rb +1 -1
- data/spec/extensions/looser_typecasting_spec.rb +7 -7
- data/spec/extensions/many_through_many_spec.rb +81 -0
- data/spec/extensions/nested_attributes_spec.rb +21 -4
- data/spec/extensions/pg_array_ops_spec.rb +1 -11
- data/spec/extensions/pg_array_spec.rb +181 -90
- data/spec/extensions/pg_auto_parameterize_spec.rb +3 -3
- data/spec/extensions/pg_hstore_spec.rb +1 -3
- data/spec/extensions/pg_inet_spec.rb +6 -1
- data/spec/extensions/pg_interval_spec.rb +73 -0
- data/spec/extensions/pg_json_spec.rb +5 -9
- data/spec/extensions/pg_range_ops_spec.rb +49 -0
- data/spec/extensions/pg_range_spec.rb +372 -0
- data/spec/extensions/pg_statement_cache_spec.rb +1 -2
- data/spec/extensions/query_literals_spec.rb +1 -2
- data/spec/extensions/schema_dumper_spec.rb +48 -89
- data/spec/extensions/serialization_spec.rb +1 -5
- data/spec/extensions/server_block_spec.rb +2 -2
- data/spec/extensions/spec_helper.rb +12 -2
- data/spec/extensions/split_array_nil_spec.rb +24 -0
- data/spec/integration/associations_test.rb +4 -4
- data/spec/integration/database_test.rb +2 -2
- data/spec/integration/dataset_test.rb +4 -4
- data/spec/integration/eager_loader_test.rb +6 -6
- data/spec/integration/plugin_test.rb +2 -2
- data/spec/integration/spec_helper.rb +2 -2
- data/spec/model/association_reflection_spec.rb +5 -0
- data/spec/model/associations_spec.rb +156 -49
- data/spec/model/eager_loading_spec.rb +137 -2
- data/spec/model/model_spec.rb +10 -10
- 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
|
-
|
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, :
|
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
|
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
|
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(
|
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
|
-
|
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
|
-
|
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
|
-
#
|
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
|
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
|
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
|
-
|
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
|