sequel 3.28.0 → 3.29.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.
- data/CHANGELOG +119 -3
- data/Rakefile +5 -3
- data/bin/sequel +1 -5
- data/doc/model_hooks.rdoc +9 -1
- data/doc/opening_databases.rdoc +49 -40
- data/doc/prepared_statements.rdoc +27 -6
- data/doc/release_notes/3.28.0.txt +2 -2
- data/doc/release_notes/3.29.0.txt +459 -0
- data/doc/sharding.rdoc +7 -1
- data/doc/testing.rdoc +18 -9
- data/doc/transactions.rdoc +41 -1
- data/lib/sequel/adapters/ado.rb +28 -17
- data/lib/sequel/adapters/ado/mssql.rb +18 -6
- data/lib/sequel/adapters/amalgalite.rb +11 -7
- data/lib/sequel/adapters/db2.rb +122 -70
- data/lib/sequel/adapters/dbi.rb +15 -15
- data/lib/sequel/adapters/do.rb +5 -36
- data/lib/sequel/adapters/do/mysql.rb +0 -5
- data/lib/sequel/adapters/do/postgres.rb +0 -5
- data/lib/sequel/adapters/do/sqlite.rb +0 -5
- data/lib/sequel/adapters/firebird.rb +3 -6
- data/lib/sequel/adapters/ibmdb.rb +24 -16
- data/lib/sequel/adapters/informix.rb +2 -4
- data/lib/sequel/adapters/jdbc.rb +47 -11
- data/lib/sequel/adapters/jdbc/as400.rb +5 -24
- data/lib/sequel/adapters/jdbc/db2.rb +0 -5
- data/lib/sequel/adapters/jdbc/derby.rb +217 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +0 -5
- data/lib/sequel/adapters/jdbc/h2.rb +10 -12
- data/lib/sequel/adapters/jdbc/hsqldb.rb +166 -0
- data/lib/sequel/adapters/jdbc/informix.rb +0 -5
- data/lib/sequel/adapters/jdbc/jtds.rb +0 -5
- data/lib/sequel/adapters/jdbc/mysql.rb +0 -10
- data/lib/sequel/adapters/jdbc/oracle.rb +70 -3
- data/lib/sequel/adapters/jdbc/postgresql.rb +0 -11
- data/lib/sequel/adapters/jdbc/sqlite.rb +0 -5
- data/lib/sequel/adapters/jdbc/sqlserver.rb +0 -5
- data/lib/sequel/adapters/jdbc/transactions.rb +56 -7
- data/lib/sequel/adapters/mock.rb +315 -0
- data/lib/sequel/adapters/mysql.rb +64 -51
- data/lib/sequel/adapters/mysql2.rb +15 -9
- data/lib/sequel/adapters/odbc.rb +13 -6
- data/lib/sequel/adapters/odbc/db2.rb +0 -4
- data/lib/sequel/adapters/odbc/mssql.rb +0 -5
- data/lib/sequel/adapters/openbase.rb +2 -4
- data/lib/sequel/adapters/oracle.rb +333 -51
- data/lib/sequel/adapters/postgres.rb +80 -27
- data/lib/sequel/adapters/shared/access.rb +0 -6
- data/lib/sequel/adapters/shared/db2.rb +13 -15
- data/lib/sequel/adapters/shared/firebird.rb +6 -6
- data/lib/sequel/adapters/shared/mssql.rb +23 -18
- data/lib/sequel/adapters/shared/mysql.rb +6 -6
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +6 -0
- data/lib/sequel/adapters/shared/oracle.rb +185 -30
- data/lib/sequel/adapters/shared/postgres.rb +35 -18
- data/lib/sequel/adapters/shared/progress.rb +0 -6
- data/lib/sequel/adapters/shared/sqlite.rb +116 -37
- data/lib/sequel/adapters/sqlite.rb +16 -8
- data/lib/sequel/adapters/swift.rb +5 -5
- data/lib/sequel/adapters/swift/mysql.rb +0 -5
- data/lib/sequel/adapters/swift/postgres.rb +0 -5
- data/lib/sequel/adapters/swift/sqlite.rb +6 -4
- data/lib/sequel/adapters/tinytds.rb +13 -10
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +8 -0
- data/lib/sequel/core.rb +40 -0
- data/lib/sequel/database/connecting.rb +1 -2
- data/lib/sequel/database/dataset.rb +3 -3
- data/lib/sequel/database/dataset_defaults.rb +58 -0
- data/lib/sequel/database/misc.rb +62 -2
- data/lib/sequel/database/query.rb +113 -49
- data/lib/sequel/database/schema_methods.rb +7 -2
- data/lib/sequel/dataset/actions.rb +37 -19
- data/lib/sequel/dataset/features.rb +24 -0
- data/lib/sequel/dataset/graph.rb +7 -6
- data/lib/sequel/dataset/misc.rb +11 -3
- data/lib/sequel/dataset/mutation.rb +2 -3
- data/lib/sequel/dataset/prepared_statements.rb +6 -4
- data/lib/sequel/dataset/query.rb +46 -15
- data/lib/sequel/dataset/sql.rb +28 -4
- data/lib/sequel/extensions/named_timezones.rb +5 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
- data/lib/sequel/model.rb +2 -1
- data/lib/sequel/model/associations.rb +115 -33
- data/lib/sequel/model/base.rb +91 -31
- data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
- data/lib/sequel/plugins/dataset_associations.rb +100 -0
- data/lib/sequel/plugins/force_encoding.rb +6 -6
- data/lib/sequel/plugins/identity_map.rb +1 -1
- data/lib/sequel/plugins/many_through_many.rb +6 -10
- data/lib/sequel/plugins/prepared_statements.rb +12 -1
- data/lib/sequel/plugins/prepared_statements_associations.rb +1 -1
- data/lib/sequel/plugins/rcte_tree.rb +29 -15
- data/lib/sequel/plugins/serialization.rb +6 -1
- data/lib/sequel/plugins/sharding.rb +0 -5
- data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
- data/lib/sequel/plugins/typecast_on_load.rb +9 -12
- data/lib/sequel/plugins/update_primary_key.rb +1 -1
- data/lib/sequel/timezones.rb +42 -42
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +29 -29
- data/spec/adapters/mysql_spec.rb +86 -104
- data/spec/adapters/oracle_spec.rb +48 -76
- data/spec/adapters/postgres_spec.rb +98 -33
- data/spec/adapters/spec_helper.rb +0 -5
- data/spec/adapters/sqlite_spec.rb +24 -21
- data/spec/core/connection_pool_spec.rb +9 -15
- data/spec/core/core_sql_spec.rb +20 -31
- data/spec/core/database_spec.rb +491 -227
- data/spec/core/dataset_spec.rb +638 -1051
- data/spec/core/expression_filters_spec.rb +0 -1
- data/spec/core/mock_adapter_spec.rb +378 -0
- data/spec/core/object_graph_spec.rb +48 -114
- data/spec/core/schema_generator_spec.rb +3 -3
- data/spec/core/schema_spec.rb +51 -114
- data/spec/core/spec_helper.rb +3 -90
- data/spec/extensions/class_table_inheritance_spec.rb +1 -1
- data/spec/extensions/dataset_associations_spec.rb +199 -0
- data/spec/extensions/instance_hooks_spec.rb +71 -0
- data/spec/extensions/named_timezones_spec.rb +22 -2
- data/spec/extensions/nested_attributes_spec.rb +3 -0
- data/spec/extensions/schema_spec.rb +1 -1
- data/spec/extensions/serialization_modification_detection_spec.rb +1 -0
- data/spec/extensions/serialization_spec.rb +5 -8
- data/spec/extensions/spec_helper.rb +4 -0
- data/spec/extensions/thread_local_timezones_spec.rb +22 -2
- data/spec/extensions/typecast_on_load_spec.rb +1 -6
- data/spec/integration/associations_test.rb +123 -12
- data/spec/integration/dataset_test.rb +140 -47
- data/spec/integration/eager_loader_test.rb +19 -21
- data/spec/integration/model_test.rb +80 -1
- data/spec/integration/plugin_test.rb +179 -128
- data/spec/integration/prepared_statement_test.rb +92 -91
- data/spec/integration/schema_test.rb +42 -23
- data/spec/integration/spec_helper.rb +25 -31
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +161 -34
- data/spec/integration/type_test.rb +3 -3
- data/spec/model/association_reflection_spec.rb +83 -7
- data/spec/model/associations_spec.rb +393 -676
- data/spec/model/base_spec.rb +186 -116
- data/spec/model/dataset_methods_spec.rb +7 -27
- data/spec/model/eager_loading_spec.rb +343 -867
- data/spec/model/hooks_spec.rb +160 -79
- data/spec/model/model_spec.rb +118 -165
- data/spec/model/plugins_spec.rb +7 -13
- data/spec/model/record_spec.rb +138 -207
- data/spec/model/spec_helper.rb +10 -73
- metadata +14 -8
|
@@ -241,16 +241,6 @@ module Sequel
|
|
|
241
241
|
self << drop_language_sql(name, opts)
|
|
242
242
|
end
|
|
243
243
|
|
|
244
|
-
# Remove the cached entries for primary keys and sequences when dropping a table.
|
|
245
|
-
def drop_table(*names)
|
|
246
|
-
names.each do |name|
|
|
247
|
-
name = quote_schema_table(name)
|
|
248
|
-
@primary_keys.delete(name)
|
|
249
|
-
@primary_key_sequences.delete(name)
|
|
250
|
-
end
|
|
251
|
-
super
|
|
252
|
-
end
|
|
253
|
-
|
|
254
244
|
# Drops a trigger from the database. Arguments:
|
|
255
245
|
# * table : table from which to drop the trigger
|
|
256
246
|
# * name : name of the trigger to drop
|
|
@@ -294,6 +284,16 @@ module Sequel
|
|
|
294
284
|
dataset.from(:pg_class).join(:pg_locks, :relation=>:relfilenode).select(:pg_class__relname, Sequel::SQL::ColumnAll.new(:pg_locks))
|
|
295
285
|
end
|
|
296
286
|
|
|
287
|
+
# Notifies the given channel. See the PostgreSQL NOTIFY documentation. Options:
|
|
288
|
+
#
|
|
289
|
+
# :payload :: The payload string to use for the NOTIFY statement. Only supported
|
|
290
|
+
# in PostgreSQL 9.0+.
|
|
291
|
+
# :server :: The server to which to send the NOTIFY statement, if the sharding support
|
|
292
|
+
# is being used.
|
|
293
|
+
def notify(channel, opts={})
|
|
294
|
+
execute_ddl("NOTIFY #{channel}#{", #{literal(opts[:payload].to_s)}" if opts[:payload]}", opts)
|
|
295
|
+
end
|
|
296
|
+
|
|
297
297
|
# Return primary key for the given table.
|
|
298
298
|
def primary_key(table, opts={})
|
|
299
299
|
quoted_table = quote_schema_table(table)
|
|
@@ -398,8 +398,8 @@ module Sequel
|
|
|
398
398
|
# If the :prepare option is given and we aren't in a savepoint,
|
|
399
399
|
# prepare the transaction for a two-phase commit.
|
|
400
400
|
def commit_transaction(conn, opts={})
|
|
401
|
-
if opts[:prepare] &&
|
|
402
|
-
log_connection_execute(conn, "PREPARE TRANSACTION #{literal(
|
|
401
|
+
if (s = opts[:prepare]) && @transactions[conn][:savepoint_level] <= 1
|
|
402
|
+
log_connection_execute(conn, "PREPARE TRANSACTION #{literal(s)}")
|
|
403
403
|
else
|
|
404
404
|
super
|
|
405
405
|
end
|
|
@@ -548,6 +548,15 @@ module Sequel
|
|
|
548
548
|
PREPARED_ARG_PLACEHOLDER
|
|
549
549
|
end
|
|
550
550
|
|
|
551
|
+
# Remove the cached entries for primary keys and sequences when a table is
|
|
552
|
+
# changed.
|
|
553
|
+
def remove_cached_schema(table)
|
|
554
|
+
tab = quote_schema_table(table)
|
|
555
|
+
@primary_keys.delete(tab)
|
|
556
|
+
@primary_key_sequences.delete(tab)
|
|
557
|
+
super
|
|
558
|
+
end
|
|
559
|
+
|
|
551
560
|
# SQL DDL statement for renaming a table. PostgreSQL doesn't allow you to change a table's schema in
|
|
552
561
|
# a rename table operation, so speciying a new schema in new_name will not have an effect.
|
|
553
562
|
def rename_table_sql(name, new_name)
|
|
@@ -562,8 +571,8 @@ module Sequel
|
|
|
562
571
|
|
|
563
572
|
# The dataset used for parsing table schemas, using the pg_* system catalogs.
|
|
564
573
|
def schema_parse_table(table_name, opts)
|
|
565
|
-
m = output_identifier_meth
|
|
566
|
-
m2 = input_identifier_meth
|
|
574
|
+
m = output_identifier_meth(opts[:dataset])
|
|
575
|
+
m2 = input_identifier_meth(opts[:dataset])
|
|
567
576
|
ds = metadata_dataset.select(:pg_attribute__attname___name,
|
|
568
577
|
SQL::Function.new(:format_type, :pg_type__oid, :pg_attribute__atttypmod).as(:db_type),
|
|
569
578
|
SQL::Function.new(:pg_get_expr, :pg_attrdef__adbin, :pg_class__oid).as(:default),
|
|
@@ -692,7 +701,7 @@ module Sequel
|
|
|
692
701
|
def complex_expression_sql(op, args)
|
|
693
702
|
case op
|
|
694
703
|
when :^
|
|
695
|
-
"(#{
|
|
704
|
+
"(#{args.collect{|a| literal(a)}.join(" # ")})"
|
|
696
705
|
else
|
|
697
706
|
super
|
|
698
707
|
end
|
|
@@ -722,12 +731,14 @@ module Sequel
|
|
|
722
731
|
|
|
723
732
|
# Insert given values into the database.
|
|
724
733
|
def insert(*values, &block)
|
|
725
|
-
if @opts[:
|
|
734
|
+
if @opts[:returning]
|
|
726
735
|
super
|
|
727
|
-
elsif supports_insert_select?
|
|
736
|
+
elsif !@opts[:sql] && supports_insert_select?
|
|
728
737
|
returning(insert_pk).insert(*values){|r| return r.values.first}
|
|
738
|
+
elsif (f = opts[:from]) && !f.empty?
|
|
739
|
+
execute_insert(insert_sql(*values), :table=>f.first, :values=>values.size == 1 ? values.first : values)
|
|
729
740
|
else
|
|
730
|
-
|
|
741
|
+
super
|
|
731
742
|
end
|
|
732
743
|
end
|
|
733
744
|
|
|
@@ -868,6 +879,12 @@ module Sequel
|
|
|
868
879
|
server_version >= 80400 ? SELECT_CLAUSE_METHODS_84 : SELECT_CLAUSE_METHODS
|
|
869
880
|
end
|
|
870
881
|
|
|
882
|
+
# PostgreSQL requires parentheses around compound datasets if they use
|
|
883
|
+
# CTEs, and using them in other places doesn't hurt.
|
|
884
|
+
def compound_dataset_sql(ds)
|
|
885
|
+
"(#{super})"
|
|
886
|
+
end
|
|
887
|
+
|
|
871
888
|
# Support FOR SHARE locking when using the :share lock style.
|
|
872
889
|
def select_lock_sql(sql)
|
|
873
890
|
@opts[:lock] == :share ? (sql << FOR_SHARE) : super
|
|
@@ -11,14 +11,6 @@ module Sequel
|
|
|
11
11
|
TEMP_STORE = [:default, :file, :memory].freeze
|
|
12
12
|
VIEWS_FILTER = "type = 'view'".freeze
|
|
13
13
|
|
|
14
|
-
# Run all alter_table commands in a transaction. This is technically only
|
|
15
|
-
# needed for drop column.
|
|
16
|
-
def alter_table(name, generator=nil, &block)
|
|
17
|
-
remove_cached_schema(name)
|
|
18
|
-
generator ||= Schema::AlterTableGenerator.new(self, &block)
|
|
19
|
-
transaction{generator.operations.each{|op| alter_table_sql_list(name, [op]).flatten.each{|sql| execute_ddl(sql)}}}
|
|
20
|
-
end
|
|
21
|
-
|
|
22
14
|
# A symbol signifying the value of the auto_vacuum PRAGMA.
|
|
23
15
|
def auto_vacuum
|
|
24
16
|
AUTO_VACUUM[pragma_get(:auto_vacuum).to_i]
|
|
@@ -31,7 +23,20 @@ module Sequel
|
|
|
31
23
|
value = AUTO_VACUUM.index(value) || (raise Error, "Invalid value for auto_vacuum option. Please specify one of :none, :full, :incremental.")
|
|
32
24
|
pragma_set(:auto_vacuum, value)
|
|
33
25
|
end
|
|
26
|
+
|
|
27
|
+
# Boolean signifying the value of the case_sensitive_likePRAGMA, or nil
|
|
28
|
+
# if not using SQLite 3.2.3+.
|
|
29
|
+
def case_sensitive_like
|
|
30
|
+
pragma_get(:case_sensitive_like).to_i == 1 if sqlite_version >= 30203
|
|
31
|
+
end
|
|
34
32
|
|
|
33
|
+
# Set the case_sensitive_like PRAGMA using the given boolean value, if using
|
|
34
|
+
# SQLite 3.2.3+. If not using 3.2.3+, no error is raised. See pragma_set.
|
|
35
|
+
# Consider using the :case_sensitive_like Database option instead.
|
|
36
|
+
def case_sensitive_like=(value)
|
|
37
|
+
pragma_set(:case_sensitive_like, !!value ? 'on' : 'off') if sqlite_version >= 30203
|
|
38
|
+
end
|
|
39
|
+
|
|
35
40
|
# SQLite uses the :sqlite database type.
|
|
36
41
|
def database_type
|
|
37
42
|
:sqlite
|
|
@@ -162,6 +167,22 @@ module Sequel
|
|
|
162
167
|
|
|
163
168
|
private
|
|
164
169
|
|
|
170
|
+
# Run all alter_table commands in a transaction. This is technically only
|
|
171
|
+
# needed for drop column.
|
|
172
|
+
def apply_alter_table(table, ops)
|
|
173
|
+
transaction do
|
|
174
|
+
if ops.length > 1 && ops.all?{|op| op[:op] == :add_constraint}
|
|
175
|
+
# If you are just doing constraints, apply all of them at the same time,
|
|
176
|
+
# as otherwise all but the last one get lost.
|
|
177
|
+
alter_table_sql_list(table, [{:op=>:add_constraints, :ops=>ops}]).flatten.each{|sql| execute_ddl(sql)}
|
|
178
|
+
else
|
|
179
|
+
# Run each operation separately, as later operations may depend on the
|
|
180
|
+
# results of earlier operations.
|
|
181
|
+
ops.each{|op| alter_table_sql_list(table, [op]).flatten.each{|sql| execute_ddl(sql)}}
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
165
186
|
# SQLite supports limited table modification. You can add a column
|
|
166
187
|
# or an index. Dropping columns is supported by copying the table into
|
|
167
188
|
# a temporary table, dropping the table, and creating a new table without
|
|
@@ -188,11 +209,26 @@ module Sequel
|
|
|
188
209
|
duplicate_table(table){|columns| columns.each{|s| s[:null] = op[:null] if s[:name].to_s == op[:name].to_s}}
|
|
189
210
|
when :set_column_type
|
|
190
211
|
duplicate_table(table){|columns| columns.each{|s| s[:type] = op[:type] if s[:name].to_s == op[:name].to_s}}
|
|
212
|
+
when :drop_constraint
|
|
213
|
+
case op[:type]
|
|
214
|
+
when :primary_key
|
|
215
|
+
duplicate_table(table){|columns| columns.each{|s| s[:primary_key] = nil}}
|
|
216
|
+
when :foreign_key
|
|
217
|
+
duplicate_table(table){|columns| columns.each{|s| s[:table] = nil}}
|
|
218
|
+
when :unique
|
|
219
|
+
duplicate_table(table)
|
|
220
|
+
else
|
|
221
|
+
raise Error, "Unsupported :type option for drop_constraint: #{op[:type].inspect}"
|
|
222
|
+
end
|
|
223
|
+
when :add_constraint
|
|
224
|
+
duplicate_table(table, :constraints=>[op])
|
|
225
|
+
when :add_constraints
|
|
226
|
+
duplicate_table(table, :constraints=>op[:ops])
|
|
191
227
|
else
|
|
192
|
-
raise Error, "Unsupported ALTER TABLE operation"
|
|
228
|
+
raise Error, "Unsupported ALTER TABLE operation: #{op[:op].inspect}"
|
|
193
229
|
end
|
|
194
230
|
end
|
|
195
|
-
|
|
231
|
+
|
|
196
232
|
# The array of column symbols in the table, except for ones given in opts[:except]
|
|
197
233
|
def backup_table_name(table, opts={})
|
|
198
234
|
table = table.gsub('`', '')
|
|
@@ -202,17 +238,14 @@ module Sequel
|
|
|
202
238
|
end
|
|
203
239
|
end
|
|
204
240
|
|
|
205
|
-
# Allow use without a generator, needed for the alter table hackery that Sequel allows.
|
|
206
|
-
def column_list_sql(generator)
|
|
207
|
-
generator.is_a?(Schema::Generator) ? super : generator.map{|c| column_definition_sql(c)}.join(', ')
|
|
208
|
-
end
|
|
209
|
-
|
|
210
241
|
# Array of PRAGMA SQL statements based on the Database options that should be applied to
|
|
211
242
|
# new connections.
|
|
212
243
|
def connection_pragmas
|
|
213
244
|
ps = []
|
|
214
245
|
v = typecast_value_boolean(opts.fetch(:foreign_keys, 1))
|
|
215
246
|
ps << "PRAGMA foreign_keys = #{v ? 1 : 0}"
|
|
247
|
+
v = typecast_value_boolean(opts.fetch(:case_sensitive_like, 1))
|
|
248
|
+
ps << "PRAGMA case_sensitive_like = #{v ? 1 : 0}"
|
|
216
249
|
[[:auto_vacuum, AUTO_VACUUM], [:synchronous, SYNCHRONOUS], [:temp_store, TEMP_STORE]].each do |prag, con|
|
|
217
250
|
if v = opts[prag]
|
|
218
251
|
raise(Error, "Value for PRAGMA #{prag} not supported, should be one of #{con.join(', ')}") unless v = con.index(v.to_sym)
|
|
@@ -234,15 +267,20 @@ module Sequel
|
|
|
234
267
|
cols.reject!{|c| nono.include? c[:name] }
|
|
235
268
|
end
|
|
236
269
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
270
|
+
begin
|
|
271
|
+
if foreign_keys
|
|
272
|
+
metadata_dataset.with_sql("PRAGMA foreign_key_list(?)", input_identifier_meth.call(table)).each do |row|
|
|
273
|
+
c = cols.find {|co| co[:name] == row[:from] } or next
|
|
274
|
+
c[:table] = row[:table]
|
|
275
|
+
c[:key] = row[:to]
|
|
276
|
+
c[:on_update] = on_delete_sql_to_sym(row[:on_update])
|
|
277
|
+
c[:on_delete] = on_delete_sql_to_sym(row[:on_delete])
|
|
278
|
+
# is there any way to get deferrable status?
|
|
279
|
+
end
|
|
245
280
|
end
|
|
281
|
+
rescue Sequel::DatabaseError
|
|
282
|
+
# Doesn't work correctly on some versions of JDBC SQLite,
|
|
283
|
+
# giving a "query does not return ResultSet" error.
|
|
246
284
|
end
|
|
247
285
|
cols
|
|
248
286
|
end
|
|
@@ -257,17 +295,26 @@ module Sequel
|
|
|
257
295
|
opts[:old_columns_proc].call(old_columns) if opts[:old_columns_proc]
|
|
258
296
|
|
|
259
297
|
yield def_columns if block_given?
|
|
260
|
-
|
|
298
|
+
|
|
299
|
+
constraints = (opts[:constraints] || []).dup
|
|
300
|
+
pks = []
|
|
301
|
+
def_columns.each{|c| pks << c[:name] if c[:primary_key]}
|
|
302
|
+
if pks.length > 1
|
|
303
|
+
constraints << {:type=>:primary_key, :columns=>pks}
|
|
304
|
+
def_columns.each{|c| c[:primary_key] = false if c[:primary_key]}
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def_columns_str = (def_columns.map{|c| column_definition_sql(c)} + constraints.map{|c| constraint_definition_sql(c)}).join(', ')
|
|
261
308
|
new_columns = old_columns.dup
|
|
262
309
|
opts[:new_columns_proc].call(new_columns) if opts[:new_columns_proc]
|
|
263
310
|
|
|
264
311
|
qt = quote_schema_table(table)
|
|
265
312
|
bt = quote_identifier(backup_table_name(qt))
|
|
266
313
|
a = [
|
|
267
|
-
"
|
|
268
|
-
"
|
|
269
|
-
"
|
|
270
|
-
"
|
|
314
|
+
"ALTER TABLE #{qt} RENAME TO #{bt}",
|
|
315
|
+
"CREATE TABLE #{qt}(#{def_columns_str})",
|
|
316
|
+
"INSERT INTO #{qt}(#{dataset.send(:identifier_list, new_columns)}) SELECT #{dataset.send(:identifier_list, old_columns)} FROM #{bt}",
|
|
317
|
+
"DROP TABLE #{bt}"
|
|
271
318
|
]
|
|
272
319
|
indexes(table).each do |name, h|
|
|
273
320
|
if (h[:columns].map{|x| x.to_s} - new_columns).empty?
|
|
@@ -306,7 +353,7 @@ module Sequel
|
|
|
306
353
|
|
|
307
354
|
# Parse the output of the table_info pragma
|
|
308
355
|
def parse_pragma(table_name, opts)
|
|
309
|
-
metadata_dataset.with_sql("PRAGMA table_info(?)", input_identifier_meth.call(table_name)).map do |row|
|
|
356
|
+
metadata_dataset.with_sql("PRAGMA table_info(?)", input_identifier_meth(opts[:dataset]).call(table_name)).map do |row|
|
|
310
357
|
row.delete(:cid)
|
|
311
358
|
row[:allow_null] = row.delete(:notnull).to_i == 0
|
|
312
359
|
row[:default] = row.delete(:dflt_value)
|
|
@@ -326,7 +373,7 @@ module Sequel
|
|
|
326
373
|
# SQLite supports schema parsing using the table_info PRAGMA, so
|
|
327
374
|
# parse the output of that into the format Sequel expects.
|
|
328
375
|
def schema_parse_table(table_name, opts)
|
|
329
|
-
m = output_identifier_meth
|
|
376
|
+
m = output_identifier_meth(opts[:dataset])
|
|
330
377
|
parse_pragma(table_name, opts).map do |row|
|
|
331
378
|
[m.call(row.delete(:name)), row]
|
|
332
379
|
end
|
|
@@ -361,13 +408,16 @@ module Sequel
|
|
|
361
408
|
case op
|
|
362
409
|
when :~, :'!~', :'~*', :'!~*'
|
|
363
410
|
raise Error, "SQLite does not support pattern matching via regular expressions"
|
|
364
|
-
when :
|
|
365
|
-
|
|
366
|
-
|
|
411
|
+
when :ILIKE
|
|
412
|
+
super(:LIKE, args.map{|a| SQL::Function.new(:upper, a)})
|
|
413
|
+
when :"NOT LIKE", :"NOT ILIKE"
|
|
414
|
+
"NOT #{complex_expression_sql((op == :"NOT ILIKE" ? :ILIKE : :LIKE), args)}"
|
|
367
415
|
when :^
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
416
|
+
complex_expression_arg_pairs(args) do |a, b|
|
|
417
|
+
a = literal(a)
|
|
418
|
+
b = literal(b)
|
|
419
|
+
"((~(#{a} & #{b})) & (#{a} | #{b}))"
|
|
420
|
+
end
|
|
371
421
|
when :extract
|
|
372
422
|
part = args.at(0)
|
|
373
423
|
raise(Sequel::Error, "unsupported extract argument: #{part.inspect}") unless format = EXTRACT_MAP[part]
|
|
@@ -408,6 +458,18 @@ module Sequel
|
|
|
408
458
|
"`#{c}`"
|
|
409
459
|
end
|
|
410
460
|
|
|
461
|
+
# When a qualified column is selected on SQLite and the qualifier
|
|
462
|
+
# is a subselect, the column name used is the full qualified name
|
|
463
|
+
# (including the qualifier) instead of just the column name. To
|
|
464
|
+
# get correct column names, you must use an alias.
|
|
465
|
+
def select(*cols)
|
|
466
|
+
if ((f = @opts[:from]) && f.any?{|t| t.is_a?(Dataset) || (t.is_a?(SQL::AliasedExpression) && t.expression.is_a?(Dataset))}) || ((j = @opts[:join]) && j.any?{|t| t.table.is_a?(Dataset)})
|
|
467
|
+
super(*cols.map{|c| alias_qualified_column(c)})
|
|
468
|
+
else
|
|
469
|
+
super
|
|
470
|
+
end
|
|
471
|
+
end
|
|
472
|
+
|
|
411
473
|
# SQLite does not support INTERSECT ALL or EXCEPT ALL
|
|
412
474
|
def supports_intersect_except_all?
|
|
413
475
|
false
|
|
@@ -442,6 +504,23 @@ module Sequel
|
|
|
442
504
|
aliaz = aliaz.value if aliaz.is_a?(SQL::Identifier)
|
|
443
505
|
"#{expression} AS #{literal(aliaz.to_s)}"
|
|
444
506
|
end
|
|
507
|
+
|
|
508
|
+
# If col is a qualified column, alias it to the same as the column name
|
|
509
|
+
def alias_qualified_column(col)
|
|
510
|
+
case col
|
|
511
|
+
when Symbol
|
|
512
|
+
t, c, a = split_symbol(col)
|
|
513
|
+
if t && !a
|
|
514
|
+
alias_qualified_column(SQL::QualifiedIdentifier.new(t, c))
|
|
515
|
+
else
|
|
516
|
+
col
|
|
517
|
+
end
|
|
518
|
+
when SQL::QualifiedIdentifier
|
|
519
|
+
SQL::AliasedExpression.new(col, col.column)
|
|
520
|
+
else
|
|
521
|
+
col
|
|
522
|
+
end
|
|
523
|
+
end
|
|
445
524
|
|
|
446
525
|
# SQL fragment specifying a list of identifiers
|
|
447
526
|
def identifier_list(columns)
|
|
@@ -464,7 +543,7 @@ module Sequel
|
|
|
464
543
|
def select_lock_sql(sql)
|
|
465
544
|
super unless @opts[:lock] == :update
|
|
466
545
|
end
|
|
467
|
-
|
|
546
|
+
|
|
468
547
|
# SQLite treats a DELETE with no WHERE clause as a TRUNCATE
|
|
469
548
|
def _truncate_sql(table)
|
|
470
549
|
"DELETE FROM #{table}"
|
|
@@ -21,7 +21,6 @@ module Sequel
|
|
|
21
21
|
# Hash with string keys and callable values for converting SQLite types.
|
|
22
22
|
SQLITE_TYPES = {}
|
|
23
23
|
{
|
|
24
|
-
%w'timestamp datetime' => ::Sequel.method(:database_to_application_timestamp),
|
|
25
24
|
%w'date' => ::Sequel.method(:string_to_date),
|
|
26
25
|
%w'time' => ::Sequel.method(:string_to_time),
|
|
27
26
|
%w'bit bool boolean' => tt.method(:boolean),
|
|
@@ -47,6 +46,16 @@ module Sequel
|
|
|
47
46
|
end
|
|
48
47
|
|
|
49
48
|
private_class_method :uri_to_options
|
|
49
|
+
|
|
50
|
+
# The conversion procs to use for this database
|
|
51
|
+
attr_reader :conversion_procs
|
|
52
|
+
|
|
53
|
+
def initialize(opts={})
|
|
54
|
+
super
|
|
55
|
+
@conversion_procs = SQLITE_TYPES.dup
|
|
56
|
+
@conversion_procs['timestamp'] = method(:to_application_timestamp)
|
|
57
|
+
@conversion_procs['datetime'] = method(:to_application_timestamp)
|
|
58
|
+
end
|
|
50
59
|
|
|
51
60
|
# Connect to the database. Since SQLite is a file based database,
|
|
52
61
|
# the only options available are :database (to specify the database
|
|
@@ -68,11 +77,6 @@ module Sequel
|
|
|
68
77
|
db
|
|
69
78
|
end
|
|
70
79
|
|
|
71
|
-
# Return instance of Sequel::SQLite::Dataset with the given options.
|
|
72
|
-
def dataset(opts = nil)
|
|
73
|
-
SQLite::Dataset.new(self, opts)
|
|
74
|
-
end
|
|
75
|
-
|
|
76
80
|
# Run the given SQL with the given arguments and yield each row.
|
|
77
81
|
def execute(sql, opts={}, &block)
|
|
78
82
|
_execute(:select, sql, opts, &block)
|
|
@@ -113,7 +117,8 @@ module Sequel
|
|
|
113
117
|
synchronize(opts[:server]) do |conn|
|
|
114
118
|
return execute_prepared_statement(conn, type, sql, opts, &block) if sql.is_a?(Symbol)
|
|
115
119
|
log_args = opts[:arguments]
|
|
116
|
-
args =
|
|
120
|
+
args = {}
|
|
121
|
+
opts.fetch(:arguments, {}).each{|k, v| args[k] = prepared_statement_argument(v)}
|
|
117
122
|
case type
|
|
118
123
|
when :select
|
|
119
124
|
log_yield(sql, log_args){conn.query(sql, args, &block)}
|
|
@@ -200,6 +205,8 @@ module Sequel
|
|
|
200
205
|
# Dataset class for SQLite datasets that use the ruby-sqlite3 driver.
|
|
201
206
|
class Dataset < Sequel::Dataset
|
|
202
207
|
include ::Sequel::SQLite::DatasetMethods
|
|
208
|
+
|
|
209
|
+
Database::DatasetClass = self
|
|
203
210
|
|
|
204
211
|
PREPARED_ARG_PLACEHOLDER = ':'.freeze
|
|
205
212
|
|
|
@@ -288,7 +295,8 @@ module Sequel
|
|
|
288
295
|
def fetch_rows(sql)
|
|
289
296
|
execute(sql) do |result|
|
|
290
297
|
i = -1
|
|
291
|
-
|
|
298
|
+
cps = db.conversion_procs
|
|
299
|
+
type_procs = result.types.map{|t| cps[base_type_name(t)]}
|
|
292
300
|
cols = result.columns.map{|c| i+=1; [output_identifier(c), i, type_procs[i]]}
|
|
293
301
|
@columns = cols.map{|c| c.first}
|
|
294
302
|
result.each do |values|
|
|
@@ -15,16 +15,19 @@ module Sequel
|
|
|
15
15
|
DATABASE_SETUP = {:postgres=>proc do |db|
|
|
16
16
|
Sequel.ts_require 'adapters/swift/postgres'
|
|
17
17
|
db.extend(Sequel::Swift::Postgres::DatabaseMethods)
|
|
18
|
+
db.dataset_class = Sequel::Swift::Postgres::Dataset
|
|
18
19
|
db.swift_class = ::Swift::DB::Postgres
|
|
19
20
|
end,
|
|
20
21
|
:mysql=>proc do |db|
|
|
21
22
|
Sequel.ts_require 'adapters/swift/mysql'
|
|
22
23
|
db.extend(Sequel::Swift::MySQL::DatabaseMethods)
|
|
24
|
+
db.dataset_class = Sequel::Swift::MySQL::Dataset
|
|
23
25
|
db.swift_class = ::Swift::DB::Mysql
|
|
24
26
|
end,
|
|
25
27
|
:sqlite=>proc do |db|
|
|
26
28
|
Sequel.ts_require 'adapters/swift/sqlite'
|
|
27
29
|
db.extend(Sequel::Swift::SQLite::DatabaseMethods)
|
|
30
|
+
db.dataset_class = Sequel::Swift::SQLite::Dataset
|
|
28
31
|
db.swift_class = ::Swift::DB::Sqlite3
|
|
29
32
|
end,
|
|
30
33
|
}
|
|
@@ -59,11 +62,6 @@ module Sequel
|
|
|
59
62
|
setup_connection(swift_class.new(server_opts(server)))
|
|
60
63
|
end
|
|
61
64
|
|
|
62
|
-
# Return a Sequel::Swift::Dataset object for this database.
|
|
63
|
-
def dataset(opts = nil)
|
|
64
|
-
Swift::Dataset.new(self, opts)
|
|
65
|
-
end
|
|
66
|
-
|
|
67
65
|
# Execute the given SQL, yielding a Swift::Result if a block is given.
|
|
68
66
|
def execute(sql, opts={})
|
|
69
67
|
synchronize(opts[:server]) do |conn|
|
|
@@ -134,6 +132,8 @@ module Sequel
|
|
|
134
132
|
end
|
|
135
133
|
|
|
136
134
|
class Dataset < Sequel::Dataset
|
|
135
|
+
Database::DatasetClass = self
|
|
136
|
+
|
|
137
137
|
# Set the columns and yield the hashes to the block.
|
|
138
138
|
def fetch_rows(sql, &block)
|
|
139
139
|
execute(sql) do |res|
|