sequel 5.19.0 → 5.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG +102 -0
- data/doc/dataset_filtering.rdoc +15 -0
- data/doc/opening_databases.rdoc +5 -1
- data/doc/release_notes/5.20.0.txt +89 -0
- data/doc/release_notes/5.21.0.txt +87 -0
- data/doc/release_notes/5.22.0.txt +48 -0
- data/doc/release_notes/5.23.0.txt +56 -0
- data/doc/release_notes/5.24.0.txt +56 -0
- data/doc/sharding.rdoc +2 -0
- data/doc/testing.rdoc +1 -0
- data/doc/transactions.rdoc +38 -0
- data/lib/sequel/adapters/ado.rb +27 -19
- data/lib/sequel/adapters/jdbc.rb +7 -1
- data/lib/sequel/adapters/jdbc/mysql.rb +2 -2
- data/lib/sequel/adapters/jdbc/postgresql.rb +1 -13
- data/lib/sequel/adapters/jdbc/sqlite.rb +29 -0
- data/lib/sequel/adapters/mysql2.rb +2 -3
- data/lib/sequel/adapters/shared/mssql.rb +7 -7
- data/lib/sequel/adapters/shared/postgres.rb +37 -19
- data/lib/sequel/adapters/shared/sqlite.rb +27 -3
- data/lib/sequel/adapters/sqlite.rb +1 -1
- data/lib/sequel/adapters/tinytds.rb +12 -0
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +2 -0
- data/lib/sequel/database/logging.rb +7 -1
- data/lib/sequel/database/query.rb +1 -1
- data/lib/sequel/database/schema_generator.rb +12 -3
- data/lib/sequel/database/schema_methods.rb +2 -0
- data/lib/sequel/database/transactions.rb +57 -5
- data/lib/sequel/dataset.rb +4 -2
- data/lib/sequel/dataset/actions.rb +3 -2
- data/lib/sequel/dataset/placeholder_literalizer.rb +4 -1
- data/lib/sequel/dataset/query.rb +5 -1
- data/lib/sequel/dataset/sql.rb +11 -7
- data/lib/sequel/extensions/named_timezones.rb +52 -8
- data/lib/sequel/extensions/pg_array.rb +4 -0
- data/lib/sequel/extensions/pg_json.rb +387 -123
- data/lib/sequel/extensions/pg_range.rb +3 -2
- data/lib/sequel/extensions/pg_row.rb +3 -1
- data/lib/sequel/extensions/schema_dumper.rb +1 -1
- data/lib/sequel/extensions/server_block.rb +15 -4
- data/lib/sequel/model/associations.rb +35 -9
- data/lib/sequel/model/plugins.rb +104 -0
- data/lib/sequel/plugins/association_dependencies.rb +3 -3
- data/lib/sequel/plugins/association_pks.rb +14 -4
- data/lib/sequel/plugins/association_proxies.rb +3 -2
- data/lib/sequel/plugins/class_table_inheritance.rb +11 -0
- data/lib/sequel/plugins/composition.rb +13 -9
- data/lib/sequel/plugins/finder.rb +2 -2
- data/lib/sequel/plugins/hook_class_methods.rb +17 -5
- data/lib/sequel/plugins/insert_conflict.rb +72 -0
- data/lib/sequel/plugins/inverted_subsets.rb +2 -2
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +147 -59
- data/lib/sequel/plugins/rcte_tree.rb +6 -0
- data/lib/sequel/plugins/static_cache.rb +8 -3
- data/lib/sequel/plugins/static_cache_cache.rb +53 -0
- data/lib/sequel/plugins/subset_conditions.rb +2 -2
- data/lib/sequel/plugins/validation_class_methods.rb +5 -3
- data/lib/sequel/sql.rb +15 -3
- data/lib/sequel/timezones.rb +50 -11
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +24 -0
- data/spec/adapters/mysql_spec.rb +0 -5
- data/spec/adapters/postgres_spec.rb +319 -1
- data/spec/bin_spec.rb +1 -1
- data/spec/core/database_spec.rb +123 -2
- data/spec/core/dataset_spec.rb +33 -1
- data/spec/core/expression_filters_spec.rb +25 -1
- data/spec/core/schema_spec.rb +24 -0
- data/spec/extensions/class_table_inheritance_spec.rb +30 -8
- data/spec/extensions/core_refinements_spec.rb +1 -1
- data/spec/extensions/hook_class_methods_spec.rb +22 -0
- data/spec/extensions/insert_conflict_spec.rb +103 -0
- data/spec/extensions/migration_spec.rb +13 -0
- data/spec/extensions/named_timezones_spec.rb +109 -2
- data/spec/extensions/pg_auto_constraint_validations_spec.rb +45 -0
- data/spec/extensions/pg_json_spec.rb +218 -29
- data/spec/extensions/pg_range_spec.rb +76 -9
- data/spec/extensions/rcte_tree_spec.rb +6 -0
- data/spec/extensions/s_spec.rb +1 -1
- data/spec/extensions/schema_dumper_spec.rb +4 -2
- data/spec/extensions/server_block_spec.rb +38 -0
- data/spec/extensions/spec_helper.rb +8 -1
- data/spec/extensions/static_cache_cache_spec.rb +35 -0
- data/spec/integration/dataset_test.rb +25 -9
- data/spec/integration/plugin_test.rb +42 -0
- data/spec/integration/schema_test.rb +7 -2
- data/spec/integration/transaction_test.rb +50 -0
- data/spec/model/associations_spec.rb +84 -4
- data/spec/model/plugins_spec.rb +111 -0
- metadata +16 -2
data/doc/sharding.rdoc
CHANGED
|
@@ -140,6 +140,8 @@ the shard to use. This is fairly easy using a Sequel::Model:
|
|
|
140
140
|
|
|
141
141
|
Rainbow.plaintext_for_hash("e580726d31f6e1ad216ffd87279e536d1f74e606")
|
|
142
142
|
|
|
143
|
+
=== :servers_hash Option
|
|
144
|
+
|
|
143
145
|
The connection pool can be further controlled to change how it handles attempts
|
|
144
146
|
to access shards that haven't been configured. The default is
|
|
145
147
|
to assume the :default shard. However, you can specify a
|
data/doc/testing.rdoc
CHANGED
|
@@ -169,3 +169,4 @@ SEQUEL_NO_PENDING :: Don't skip any specs, try running all specs (note, can caus
|
|
|
169
169
|
SEQUEL_PG_TIMESTAMPTZ :: Use the pg_timestamptz extension when running the postgres specs
|
|
170
170
|
SEQUEL_SPLIT_SYMBOLS :: Turn on symbol splitting when running the adapter and integration specs
|
|
171
171
|
SEQUEL_SYNCHRONIZE_SQL :: Use the synchronize_sql extension when running the specs
|
|
172
|
+
SEQUEL_TZINFO_VERSION :: Force the given tzinfo version when running the specs (e.g. '>=2')
|
data/doc/transactions.rdoc
CHANGED
|
@@ -169,6 +169,44 @@ If you want the current savepoint and potentially enclosing savepoints to be rol
|
|
|
169
169
|
end # ROLLBACK TO SAVEPOINT
|
|
170
170
|
end # ROLLBACK
|
|
171
171
|
|
|
172
|
+
=== Savepoint Hooks
|
|
173
|
+
|
|
174
|
+
When using savepoints, you can use the +:savepoint+ option to +after_commit+ or +after_rollback+ to use a savepoint hook. For +after_commit+, this will only run the hook after transaction commit if all enclosing savepoints are released (not rolled back). For +after_rollback+, this will run the hook after any enclosing savepoint is rolled back (before transaction commit), or after the transaction is rolled back if all enclosing savepoints are released:
|
|
175
|
+
|
|
176
|
+
x = nil
|
|
177
|
+
DB.transaction do # BEGIN
|
|
178
|
+
DB.transaction(savepoint: true) do # SAVEPOINT
|
|
179
|
+
DB.after_commit(savepoint: true){x = 1}
|
|
180
|
+
DB.after_rollback(savepoint: true){x = 2}
|
|
181
|
+
x # nil
|
|
182
|
+
end # RELEASE SAVEPOINT
|
|
183
|
+
x # nil
|
|
184
|
+
end # COMMIT
|
|
185
|
+
x # 1
|
|
186
|
+
|
|
187
|
+
x = nil
|
|
188
|
+
DB.transaction do # BEGIN
|
|
189
|
+
DB.transaction(savepoint: true) do # SAVEPOINT
|
|
190
|
+
DB.after_commit(savepoint: true){x = 1}
|
|
191
|
+
DB.after_rollback(savepoint: true){x = 2}
|
|
192
|
+
x # nil
|
|
193
|
+
raise Sequel::Rollback
|
|
194
|
+
end # ROLLBACK TO SAVEPOINT
|
|
195
|
+
x # 2
|
|
196
|
+
end # COMMIT
|
|
197
|
+
x # 2
|
|
198
|
+
|
|
199
|
+
x = nil
|
|
200
|
+
DB.transaction do # BEGIN
|
|
201
|
+
DB.transaction(savepoint: true) do # SAVEPOINT
|
|
202
|
+
DB.after_commit(savepoint: true){x = 1}
|
|
203
|
+
DB.after_rollback(savepoint: true){x = 2}
|
|
204
|
+
end # RELEASE SAVEPOINT
|
|
205
|
+
x # nil
|
|
206
|
+
raise Sequel::Rollback
|
|
207
|
+
end
|
|
208
|
+
x # 2
|
|
209
|
+
|
|
172
210
|
== Prepared Transactions / Two-Phase Commit
|
|
173
211
|
|
|
174
212
|
Sequel supports database prepared transactions on PostgreSQL, MySQL, and H2. With prepared transactions, at the end of the transaction, the transaction is not immediately committed (it acts like a rollback). Later, you can call +commit_prepared_transaction+ to commit the transaction or +rollback_prepared_transaction+ to roll the transaction back. Prepared transactions are usually used with distributed databases to make sure all databases commit the same transaction or none of them do.
|
data/lib/sequel/adapters/ado.rb
CHANGED
|
@@ -47,34 +47,40 @@ module Sequel
|
|
|
47
47
|
#AdVarWChar = 202
|
|
48
48
|
#AdWChar = 130
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
def cp.bigint(v)
|
|
50
|
+
bigint = Object.new
|
|
51
|
+
def bigint.call(v)
|
|
53
52
|
v.to_i
|
|
54
53
|
end
|
|
55
54
|
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
numeric = Object.new
|
|
56
|
+
def numeric.call(v)
|
|
57
|
+
if v.include?(',')
|
|
58
|
+
BigDecimal(v.tr(',', '.'))
|
|
59
|
+
else
|
|
60
|
+
BigDecimal(v)
|
|
61
|
+
end
|
|
58
62
|
end
|
|
59
63
|
|
|
60
|
-
|
|
64
|
+
binary = Object.new
|
|
65
|
+
def binary.call(v)
|
|
61
66
|
Sequel.blob(v.pack('c*'))
|
|
62
67
|
end
|
|
63
68
|
|
|
64
|
-
|
|
69
|
+
date = Object.new
|
|
70
|
+
def date.call(v)
|
|
65
71
|
Date.new(v.year, v.month, v.day)
|
|
66
72
|
end
|
|
67
73
|
|
|
68
74
|
CONVERSION_PROCS = {}
|
|
69
75
|
[
|
|
70
|
-
[
|
|
71
|
-
[
|
|
72
|
-
[
|
|
73
|
-
[
|
|
74
|
-
].each do |
|
|
75
|
-
|
|
76
|
+
[bigint, AdBigInt],
|
|
77
|
+
[numeric, AdNumeric, AdVarNumeric],
|
|
78
|
+
[date, AdDBDate],
|
|
79
|
+
[binary, AdBinary, AdVarBinary, AdLongVarBinary]
|
|
80
|
+
].each do |callable, *types|
|
|
81
|
+
callable.freeze
|
|
76
82
|
types.each do |i|
|
|
77
|
-
CONVERSION_PROCS[i] =
|
|
83
|
+
CONVERSION_PROCS[i] = callable
|
|
78
84
|
end
|
|
79
85
|
end
|
|
80
86
|
CONVERSION_PROCS.freeze
|
|
@@ -227,7 +233,6 @@ module Sequel
|
|
|
227
233
|
cols = []
|
|
228
234
|
conversion_procs = db.conversion_procs
|
|
229
235
|
|
|
230
|
-
i = -1
|
|
231
236
|
ts_cp = nil
|
|
232
237
|
recordset.Fields.each do |field|
|
|
233
238
|
type = field.Type
|
|
@@ -244,18 +249,21 @@ module Sequel
|
|
|
244
249
|
else
|
|
245
250
|
conversion_procs[type]
|
|
246
251
|
end
|
|
247
|
-
cols << [output_identifier(field.Name), cp
|
|
252
|
+
cols << [output_identifier(field.Name), cp]
|
|
248
253
|
end
|
|
249
254
|
|
|
250
255
|
self.columns = cols.map(&:first)
|
|
251
256
|
return if recordset.EOF
|
|
257
|
+
max = cols.length
|
|
252
258
|
|
|
253
259
|
recordset.GetRows.transpose.each do |field_values|
|
|
254
260
|
h = {}
|
|
255
261
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
262
|
+
i = -1
|
|
263
|
+
while (i += 1) < max
|
|
264
|
+
name, cp = cols[i]
|
|
265
|
+
h[name] = if (v = field_values[i]) && cp
|
|
266
|
+
cp.call(v)
|
|
259
267
|
else
|
|
260
268
|
v
|
|
261
269
|
end
|
data/lib/sequel/adapters/jdbc.rb
CHANGED
|
@@ -102,12 +102,17 @@ module Sequel
|
|
|
102
102
|
v.getSubString(1, v.length)
|
|
103
103
|
end
|
|
104
104
|
end
|
|
105
|
+
x = convertors[:RubyArray] = Object.new
|
|
106
|
+
def x.call(r, i)
|
|
107
|
+
if v = r.getArray(i)
|
|
108
|
+
v.array.to_ary
|
|
109
|
+
end
|
|
110
|
+
end
|
|
105
111
|
|
|
106
112
|
MAP = Hash.new(convertors[:Object])
|
|
107
113
|
types = Java::JavaSQL::Types
|
|
108
114
|
|
|
109
115
|
{
|
|
110
|
-
:ARRAY => :Array,
|
|
111
116
|
:BOOLEAN => :Boolean,
|
|
112
117
|
:CHAR => :String,
|
|
113
118
|
:DOUBLE => :Double,
|
|
@@ -126,6 +131,7 @@ module Sequel
|
|
|
126
131
|
BASIC_MAP = MAP.dup
|
|
127
132
|
|
|
128
133
|
{
|
|
134
|
+
:ARRAY => :Array,
|
|
129
135
|
:BINARY => :Blob,
|
|
130
136
|
:BLOB => :Blob,
|
|
131
137
|
:CLOB => :Clob,
|
|
@@ -54,12 +54,12 @@ module Sequel
|
|
|
54
54
|
# MySQL 5.1.12 JDBC adapter requires generated keys
|
|
55
55
|
# and previous versions don't mind.
|
|
56
56
|
def execute_statement_insert(stmt, sql)
|
|
57
|
-
stmt.executeUpdate(sql, JavaSQL::Statement
|
|
57
|
+
stmt.executeUpdate(sql, JavaSQL::Statement::RETURN_GENERATED_KEYS)
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
# Return generated keys for insert statements.
|
|
61
61
|
def prepare_jdbc_statement(conn, sql, opts)
|
|
62
|
-
opts[:type] == :insert ? conn.prepareStatement(sql, JavaSQL::Statement
|
|
62
|
+
opts[:type] == :insert ? conn.prepareStatement(sql, JavaSQL::Statement::RETURN_GENERATED_KEYS) : super
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
# Convert tinyint(1) type to boolean
|
|
@@ -195,17 +195,7 @@ module Sequel
|
|
|
195
195
|
|
|
196
196
|
STRING_TYPE = Java::JavaSQL::Types::VARCHAR
|
|
197
197
|
ARRAY_TYPE = Java::JavaSQL::Types::ARRAY
|
|
198
|
-
PG_SPECIFIC_TYPES = [
|
|
199
|
-
|
|
200
|
-
# Return PostgreSQL array types as ruby Arrays instead of
|
|
201
|
-
# JDBC PostgreSQL driver-specific array type. Only used if the
|
|
202
|
-
# database does not have a conversion proc for the type.
|
|
203
|
-
ARRAY_METHOD = Object.new
|
|
204
|
-
def ARRAY_METHOD.call(r, i)
|
|
205
|
-
if v = r.getArray(i)
|
|
206
|
-
v.array.to_ary
|
|
207
|
-
end
|
|
208
|
-
end
|
|
198
|
+
PG_SPECIFIC_TYPES = [Java::JavaSQL::Types::ARRAY, Java::JavaSQL::Types::OTHER, Java::JavaSQL::Types::STRUCT, Java::JavaSQL::Types::TIME_WITH_TIMEZONE, Java::JavaSQL::Types::TIME].freeze
|
|
209
199
|
|
|
210
200
|
# Return PostgreSQL hstore types as ruby Hashes instead of
|
|
211
201
|
# Java HashMaps. Only used if the database does not have a
|
|
@@ -223,8 +213,6 @@ module Sequel
|
|
|
223
213
|
oid = meta.getField(i).getOID
|
|
224
214
|
if pr = db.oid_convertor_proc(oid)
|
|
225
215
|
pr
|
|
226
|
-
elsif type == ARRAY_TYPE
|
|
227
|
-
ARRAY_METHOD
|
|
228
216
|
elsif oid == 2950 # UUID
|
|
229
217
|
map[STRING_TYPE]
|
|
230
218
|
elsif meta.getPGType(i) == 'hstore'
|
|
@@ -15,6 +15,24 @@ module Sequel
|
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
module SQLite
|
|
18
|
+
module ForeignKeyListPragmaConvertorFix
|
|
19
|
+
# For the use of the convertor for String, working around a bug
|
|
20
|
+
# in jdbc-sqlite3 that reports fields are of type
|
|
21
|
+
# java.sql.types.NUMERIC even though they contain non-numeric data.
|
|
22
|
+
def type_convertor(_, _, _, i)
|
|
23
|
+
i > 2 ? TypeConvertor::CONVERTORS[:String] : super
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
module TableInfoPragmaConvertorFix
|
|
28
|
+
# For the use of the convertor for String, working around a bug
|
|
29
|
+
# in jdbc-sqlite3 that reports dflt_value field is of type
|
|
30
|
+
# java.sql.types.NUMERIC even though they contain string data.
|
|
31
|
+
def type_convertor(_, _, _, i)
|
|
32
|
+
i == 5 ? TypeConvertor::CONVERTORS[:String] : super
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
18
36
|
module DatabaseMethods
|
|
19
37
|
include Sequel::SQLite::DatabaseMethods
|
|
20
38
|
|
|
@@ -37,6 +55,17 @@ module Sequel
|
|
|
37
55
|
end
|
|
38
56
|
|
|
39
57
|
private
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# Add workaround for bug when running foreign_key_list pragma
|
|
61
|
+
def _foreign_key_list_ds(_)
|
|
62
|
+
super.with_extend(ForeignKeyListPragmaConvertorFix)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Add workaround for bug when running table_info pragma
|
|
66
|
+
def _parse_pragma_ds(_, _)
|
|
67
|
+
super.with_extend(TableInfoPragmaConvertorFix)
|
|
68
|
+
end
|
|
40
69
|
|
|
41
70
|
DATABASE_ERROR_REGEXPS = Sequel::SQLite::DatabaseMethods::DATABASE_ERROR_REGEXPS.merge(/Abort due to constraint violation/ => ConstraintViolation).freeze
|
|
42
71
|
def database_error_regexps
|
|
@@ -36,7 +36,6 @@ module Sequel
|
|
|
36
36
|
# options such as :local_infile.
|
|
37
37
|
def connect(server)
|
|
38
38
|
opts = server_opts(server)
|
|
39
|
-
opts[:host] ||= 'localhost'
|
|
40
39
|
opts[:username] ||= opts.delete(:user)
|
|
41
40
|
opts[:flags] ||= 0
|
|
42
41
|
opts[:flags] |= ::Mysql2::Client::FOUND_ROWS if ::Mysql2::Client.const_defined?(:FOUND_ROWS)
|
|
@@ -78,8 +77,8 @@ module Sequel
|
|
|
78
77
|
end
|
|
79
78
|
|
|
80
79
|
# Return the version of the MySQL server to which we are connecting.
|
|
81
|
-
def server_version(
|
|
82
|
-
@server_version ||= (
|
|
80
|
+
def server_version(_server=nil)
|
|
81
|
+
@server_version ||= super()
|
|
83
82
|
end
|
|
84
83
|
|
|
85
84
|
private
|
|
@@ -427,20 +427,19 @@ module Sequel
|
|
|
427
427
|
m = output_identifier_meth(opts[:dataset])
|
|
428
428
|
m2 = input_identifier_meth(opts[:dataset])
|
|
429
429
|
tn = m2.call(table_name.to_s)
|
|
430
|
-
table_id = get(Sequel.function(:object_id, tn))
|
|
431
430
|
info_sch_sch = opts[:information_schema_schema]
|
|
432
431
|
inf_sch_qual = lambda{|s| info_sch_sch ? Sequel.qualify(info_sch_sch, s) : Sequel[s]}
|
|
433
|
-
|
|
432
|
+
table_id = metadata_dataset.from(inf_sch_qual.call(Sequel[:sys][:objects])).where(:name => tn).select_map(:object_id).first
|
|
434
433
|
|
|
435
|
-
identity_cols = metadata_dataset.from(
|
|
434
|
+
identity_cols = metadata_dataset.from(inf_sch_qual.call(Sequel[:sys][:columns])).
|
|
436
435
|
where(:object_id=>table_id, :is_identity=>true).
|
|
437
436
|
select_map(:name)
|
|
438
437
|
|
|
439
|
-
pk_index_id = metadata_dataset.from(
|
|
438
|
+
pk_index_id = metadata_dataset.from(inf_sch_qual.call(Sequel[:sys][:sysindexes])).
|
|
440
439
|
where(:id=>table_id, :indid=>1..254){{(status & 2048)=>2048}}.
|
|
441
440
|
get(:indid)
|
|
442
|
-
pk_cols = metadata_dataset.from(
|
|
443
|
-
join(
|
|
441
|
+
pk_cols = metadata_dataset.from(inf_sch_qual.call(Sequel[:sys][:sysindexkeys]).as(:sik)).
|
|
442
|
+
join(inf_sch_qual.call(Sequel[:sys][:syscolumns]).as(:sc), :id=>:id, :colid=>:colid).
|
|
444
443
|
where{{sik[:id]=>table_id, sik[:indid]=>pk_index_id}}.
|
|
445
444
|
select_order_map{sc[:name]}
|
|
446
445
|
|
|
@@ -765,8 +764,9 @@ module Sequel
|
|
|
765
764
|
output(nil, [SQL::QualifiedIdentifier.new(:inserted, first_primary_key)])._import(columns, values, opts)
|
|
766
765
|
elsif @opts[:output]
|
|
767
766
|
statements = multi_insert_sql(columns, values)
|
|
767
|
+
ds = naked
|
|
768
768
|
@db.transaction(opts.merge(:server=>@opts[:server])) do
|
|
769
|
-
statements.map{|st| with_sql(st)}
|
|
769
|
+
statements.map{|st| ds.with_sql(st)}
|
|
770
770
|
end.first.map{|v| v.length == 1 ? v.values.first : v}
|
|
771
771
|
else
|
|
772
772
|
super
|
|
@@ -97,13 +97,17 @@ module Sequel
|
|
|
97
97
|
# Add an exclusion constraint when creating the table. Elements should be
|
|
98
98
|
# an array of 2 element arrays, with the first element being the column or
|
|
99
99
|
# expression the exclusion constraint is applied to, and the second element
|
|
100
|
-
# being the operator to use for the column/expression to check for exclusion
|
|
101
|
-
#
|
|
102
|
-
# Example:
|
|
100
|
+
# being the operator to use for the column/expression to check for exclusion:
|
|
103
101
|
#
|
|
104
102
|
# exclude([[:col1, '&&'], [:col2, '=']])
|
|
105
103
|
# # EXCLUDE USING gist (col1 WITH &&, col2 WITH =)
|
|
106
104
|
#
|
|
105
|
+
# To use a custom operator class, you need to use Sequel.lit with the expression
|
|
106
|
+
# and operator class:
|
|
107
|
+
#
|
|
108
|
+
# exclude([[Sequel.lit('col1 inet_ops'), '&&'], [:col2, '=']])
|
|
109
|
+
# # EXCLUDE USING gist (col1 inet_ops WITH &&, col2 WITH =)
|
|
110
|
+
#
|
|
107
111
|
# Options supported:
|
|
108
112
|
#
|
|
109
113
|
# :name :: Name the constraint with the given name (useful if you may
|
|
@@ -147,10 +151,10 @@ module Sequel
|
|
|
147
151
|
SELECT_CUSTOM_SEQUENCE_SQL = (<<-end_sql
|
|
148
152
|
SELECT name.nspname AS "schema",
|
|
149
153
|
CASE
|
|
150
|
-
WHEN split_part(def.
|
|
151
|
-
substr(split_part(def.
|
|
152
|
-
strpos(split_part(def.
|
|
153
|
-
ELSE split_part(def.
|
|
154
|
+
WHEN split_part(pg_get_expr(def.adbin, attr.attrelid), '''', 2) ~ '.' THEN
|
|
155
|
+
substr(split_part(pg_get_expr(def.adbin, attr.attrelid), '''', 2),
|
|
156
|
+
strpos(split_part(pg_get_expr(def.adbin, attr.attrelid), '''', 2), '.')+1)
|
|
157
|
+
ELSE split_part(pg_get_expr(def.adbin, attr.attrelid), '''', 2)
|
|
154
158
|
END AS "sequence"
|
|
155
159
|
FROM pg_class t
|
|
156
160
|
JOIN pg_namespace name ON (t.relnamespace = name.oid)
|
|
@@ -158,7 +162,7 @@ module Sequel
|
|
|
158
162
|
JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
|
|
159
163
|
JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
|
|
160
164
|
WHERE cons.contype = 'p'
|
|
161
|
-
AND def.
|
|
165
|
+
AND pg_get_expr(def.adbin, attr.attrelid) ~* 'nextval'
|
|
162
166
|
end_sql
|
|
163
167
|
).strip.gsub(/\s+/, ' ').freeze
|
|
164
168
|
|
|
@@ -220,24 +224,22 @@ module Sequel
|
|
|
220
224
|
# A hash of metadata for CHECK constraints on the table.
|
|
221
225
|
# Keys are CHECK constraint name symbols. Values are hashes with the following keys:
|
|
222
226
|
# :definition :: An SQL fragment for the definition of the constraint
|
|
223
|
-
# :columns :: An array of column symbols for the columns referenced in the constraint
|
|
227
|
+
# :columns :: An array of column symbols for the columns referenced in the constraint,
|
|
228
|
+
# can be an empty array if the database cannot deteremine the column symbols.
|
|
224
229
|
def check_constraints(table)
|
|
225
230
|
m = output_identifier_meth
|
|
226
231
|
|
|
227
232
|
rows = metadata_dataset.
|
|
228
233
|
from{pg_constraint.as(:co)}.
|
|
229
|
-
|
|
234
|
+
left_join(Sequel[:pg_attribute].as(:att), :attrelid=>:conrelid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
|
|
230
235
|
where(:conrelid=>regclass_oid(table), :contype=>'c').
|
|
231
236
|
select{[co[:conname].as(:constraint), att[:attname].as(:column), pg_get_constraintdef(co[:oid]).as(:definition)]}
|
|
232
237
|
|
|
233
238
|
hash = {}
|
|
234
239
|
rows.each do |row|
|
|
235
240
|
constraint = m.call(row[:constraint])
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
else
|
|
239
|
-
hash[constraint] = {:definition=>row[:definition], :columns=>[m.call(row[:column])]}
|
|
240
|
-
end
|
|
241
|
+
entry = hash[constraint] ||= {:definition=>row[:definition], :columns=>[]}
|
|
242
|
+
entry[:columns] << m.call(row[:column]) if row[:column]
|
|
241
243
|
end
|
|
242
244
|
|
|
243
245
|
hash
|
|
@@ -838,10 +840,14 @@ module Sequel
|
|
|
838
840
|
# default value is given.
|
|
839
841
|
def column_definition_default_sql(sql, column)
|
|
840
842
|
super
|
|
841
|
-
if !column[:serial] && !['serial', 'bigserial'].include?(column[:type].to_s) && !column[:default]
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
843
|
+
if !column[:serial] && !['serial', 'bigserial'].include?(column[:type].to_s) && !column[:default]
|
|
844
|
+
if (identity = column[:identity])
|
|
845
|
+
sql << " GENERATED "
|
|
846
|
+
sql << (identity == :always ? "ALWAYS" : "BY DEFAULT")
|
|
847
|
+
sql << " AS IDENTITY"
|
|
848
|
+
elsif (generated = column[:generated_always_as])
|
|
849
|
+
sql << " GENERATED ALWAYS AS (#{literal(generated)}) STORED"
|
|
850
|
+
end
|
|
845
851
|
end
|
|
846
852
|
end
|
|
847
853
|
|
|
@@ -1922,6 +1928,18 @@ module Sequel
|
|
|
1922
1928
|
opts[:with].any?{|w| w[:recursive]} ? "WITH RECURSIVE " : super
|
|
1923
1929
|
end
|
|
1924
1930
|
|
|
1931
|
+
# Support WITH AS [NOT] MATERIALIZED if :materialized option is used.
|
|
1932
|
+
def select_with_sql_prefix(sql, w)
|
|
1933
|
+
super
|
|
1934
|
+
|
|
1935
|
+
case w[:materialized]
|
|
1936
|
+
when true
|
|
1937
|
+
sql << "MATERIALIZED "
|
|
1938
|
+
when false
|
|
1939
|
+
sql << "NOT MATERIALIZED "
|
|
1940
|
+
end
|
|
1941
|
+
end
|
|
1942
|
+
|
|
1925
1943
|
# The version of the database server
|
|
1926
1944
|
def server_version
|
|
1927
1945
|
db.server_version(@opts[:server])
|
|
@@ -10,6 +10,10 @@ module Sequel
|
|
|
10
10
|
def self.mock_adapter_setup(db)
|
|
11
11
|
db.instance_exec do
|
|
12
12
|
@sqlite_version = 30903
|
|
13
|
+
|
|
14
|
+
def schema_parse_table(*)
|
|
15
|
+
[]
|
|
16
|
+
end
|
|
13
17
|
end
|
|
14
18
|
end
|
|
15
19
|
|
|
@@ -60,7 +64,7 @@ module Sequel
|
|
|
60
64
|
def foreign_key_list(table, opts=OPTS)
|
|
61
65
|
m = output_identifier_meth
|
|
62
66
|
h = {}
|
|
63
|
-
|
|
67
|
+
_foreign_key_list_ds(table).each do |row|
|
|
64
68
|
if r = h[row[:id]]
|
|
65
69
|
r[:columns] << m.call(row[:from])
|
|
66
70
|
r[:key] << m.call(row[:to]) if r[:key]
|
|
@@ -173,6 +177,16 @@ module Sequel
|
|
|
173
177
|
|
|
174
178
|
private
|
|
175
179
|
|
|
180
|
+
# Dataset used for parsing foreign key lists
|
|
181
|
+
def _foreign_key_list_ds(table)
|
|
182
|
+
metadata_dataset.with_sql("PRAGMA foreign_key_list(?)", input_identifier_meth.call(table))
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Dataset used for parsing schema
|
|
186
|
+
def _parse_pragma_ds(table_name, opts)
|
|
187
|
+
metadata_dataset.with_sql("PRAGMA table_info(?)", input_identifier_meth(opts[:dataset]).call(table_name))
|
|
188
|
+
end
|
|
189
|
+
|
|
176
190
|
# Run all alter_table commands in a transaction. This is technically only
|
|
177
191
|
# needed for drop column.
|
|
178
192
|
def apply_alter_table(table, ops)
|
|
@@ -445,7 +459,7 @@ module Sequel
|
|
|
445
459
|
# Parse the output of the table_info pragma
|
|
446
460
|
def parse_pragma(table_name, opts)
|
|
447
461
|
pks = 0
|
|
448
|
-
sch =
|
|
462
|
+
sch = _parse_pragma_ds(table_name, opts).map do |row|
|
|
449
463
|
row.delete(:cid)
|
|
450
464
|
row[:allow_null] = row.delete(:notnull).to_i == 0
|
|
451
465
|
row[:default] = row.delete(:dflt_value)
|
|
@@ -513,7 +527,7 @@ module Sequel
|
|
|
513
527
|
|
|
514
528
|
Dataset.def_sql_method(self, :delete, [['if db.sqlite_version >= 30803', %w'with delete from where'], ["else", %w'delete from where']])
|
|
515
529
|
Dataset.def_sql_method(self, :insert, [['if db.sqlite_version >= 30803', %w'with insert conflict into columns values on_conflict'], ["else", %w'insert conflict into columns values']])
|
|
516
|
-
Dataset.def_sql_method(self, :select, [['if opts[:values]', %w'with values compounds'], ['else', %w'with select distinct columns from join where group having compounds order limit lock']])
|
|
530
|
+
Dataset.def_sql_method(self, :select, [['if opts[:values]', %w'with values compounds'], ['else', %w'with select distinct columns from join where group having window compounds order limit lock']])
|
|
517
531
|
Dataset.def_sql_method(self, :update, [['if db.sqlite_version >= 30803', %w'with update table set where'], ["else", %w'update table set where']])
|
|
518
532
|
|
|
519
533
|
def cast_sql_append(sql, expr, type)
|
|
@@ -732,6 +746,11 @@ module Sequel
|
|
|
732
746
|
def supports_where_true?
|
|
733
747
|
false
|
|
734
748
|
end
|
|
749
|
+
|
|
750
|
+
# SQLite 3.28+ supports the WINDOW clause.
|
|
751
|
+
def supports_window_clause?
|
|
752
|
+
db.sqlite_version >= 32800
|
|
753
|
+
end
|
|
735
754
|
|
|
736
755
|
# SQLite 3.25+ supports window functions. However, support is only enabled
|
|
737
756
|
# on SQLite 3.26.0+ because internal Sequel usage of window functions
|
|
@@ -741,6 +760,11 @@ module Sequel
|
|
|
741
760
|
db.sqlite_version >= 32600
|
|
742
761
|
end
|
|
743
762
|
|
|
763
|
+
# SQLite 3.28.0+ supports all window frame options that Sequel supports
|
|
764
|
+
def supports_window_function_frame_option?(option)
|
|
765
|
+
db.sqlite_version >= 32800 ? true : super
|
|
766
|
+
end
|
|
767
|
+
|
|
744
768
|
private
|
|
745
769
|
|
|
746
770
|
# SQLite uses string literals instead of identifiers in AS clauses.
|