sequel 3.28.0 → 3.29.0
Sign up to get free protection for your applications and to get access to all the features.
- 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|
|