sequel 2.12.0 → 3.0.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 +62 -0
- data/README.rdoc +3 -3
- data/Rakefile +7 -0
- data/doc/advanced_associations.rdoc +44 -0
- data/doc/release_notes/3.0.0.txt +221 -0
- data/lib/sequel/adapters/amalgalite.rb +208 -0
- data/lib/sequel/adapters/db2.rb +3 -0
- data/lib/sequel/adapters/dbi.rb +9 -0
- data/lib/sequel/adapters/do.rb +0 -4
- data/lib/sequel/adapters/firebird.rb +16 -18
- data/lib/sequel/adapters/informix.rb +5 -3
- data/lib/sequel/adapters/jdbc.rb +24 -20
- data/lib/sequel/adapters/jdbc/h2.rb +15 -4
- data/lib/sequel/adapters/mysql.rb +4 -8
- data/lib/sequel/adapters/odbc.rb +0 -4
- data/lib/sequel/adapters/oracle.rb +0 -4
- data/lib/sequel/adapters/shared/mssql.rb +16 -5
- data/lib/sequel/adapters/shared/mysql.rb +87 -86
- data/lib/sequel/adapters/shared/oracle.rb +92 -3
- data/lib/sequel/adapters/shared/postgres.rb +85 -29
- data/lib/sequel/adapters/shared/progress.rb +8 -3
- data/lib/sequel/adapters/shared/sqlite.rb +53 -23
- data/lib/sequel/adapters/sqlite.rb +4 -7
- data/lib/sequel/adapters/utils/unsupported.rb +3 -3
- data/lib/sequel/connection_pool.rb +18 -25
- data/lib/sequel/core.rb +2 -21
- data/lib/sequel/database.rb +60 -44
- data/lib/sequel/database/schema_generator.rb +26 -31
- data/lib/sequel/database/schema_methods.rb +8 -3
- data/lib/sequel/database/schema_sql.rb +114 -28
- data/lib/sequel/dataset.rb +14 -41
- data/lib/sequel/dataset/convenience.rb +31 -54
- data/lib/sequel/dataset/graph.rb +7 -13
- data/lib/sequel/dataset/sql.rb +43 -54
- data/lib/sequel/extensions/inflector.rb +0 -5
- data/lib/sequel/extensions/schema_dumper.rb +238 -0
- data/lib/sequel/metaprogramming.rb +0 -20
- data/lib/sequel/model.rb +1 -2
- data/lib/sequel/model/base.rb +18 -16
- data/lib/sequel/model/inflections.rb +6 -9
- data/lib/sequel/plugins/caching.rb +0 -6
- data/lib/sequel/plugins/hook_class_methods.rb +1 -1
- data/lib/sequel/sql.rb +2 -0
- data/lib/sequel/version.rb +2 -2
- data/spec/adapters/firebird_spec.rb +35 -8
- data/spec/adapters/mysql_spec.rb +173 -266
- data/spec/adapters/oracle_spec.rb +13 -0
- data/spec/adapters/postgres_spec.rb +127 -227
- data/spec/adapters/sqlite_spec.rb +13 -171
- data/spec/core/connection_pool_spec.rb +15 -4
- data/spec/core/core_sql_spec.rb +14 -170
- data/spec/core/database_spec.rb +50 -132
- data/spec/core/dataset_spec.rb +47 -930
- data/spec/core/expression_filters_spec.rb +12 -0
- data/spec/core/schema_generator_spec.rb +37 -45
- data/spec/core/schema_spec.rb +26 -16
- data/spec/core/spec_helper.rb +0 -25
- data/spec/extensions/inflector_spec.rb +0 -3
- data/spec/extensions/schema_dumper_spec.rb +292 -0
- data/spec/extensions/serialization_spec.rb +9 -0
- data/spec/extensions/single_table_inheritance_spec.rb +6 -1
- data/spec/extensions/spec_helper.rb +1 -3
- data/spec/extensions/validation_helpers_spec.rb +4 -4
- data/spec/integration/database_test.rb +18 -0
- data/spec/integration/dataset_test.rb +112 -1
- data/spec/integration/eager_loader_test.rb +70 -9
- data/spec/integration/prepared_statement_test.rb +2 -2
- data/spec/integration/schema_test.rb +76 -27
- data/spec/integration/spec_helper.rb +0 -14
- data/spec/integration/transaction_test.rb +27 -0
- data/spec/model/associations_spec.rb +0 -36
- data/spec/model/base_spec.rb +18 -123
- data/spec/model/hooks_spec.rb +2 -235
- data/spec/model/inflector_spec.rb +15 -115
- data/spec/model/model_spec.rb +0 -120
- data/spec/model/plugins_spec.rb +0 -70
- data/spec/model/record_spec.rb +35 -93
- data/spec/model/spec_helper.rb +0 -27
- data/spec/model/validations_spec.rb +0 -931
- metadata +9 -14
- data/lib/sequel/deprecated.rb +0 -593
- data/lib/sequel/deprecated_migration.rb +0 -91
- data/lib/sequel/model/deprecated.rb +0 -204
- data/lib/sequel/model/deprecated_hooks.rb +0 -103
- data/lib/sequel/model/deprecated_inflector.rb +0 -335
- data/lib/sequel/model/deprecated_validations.rb +0 -388
- data/spec/core/core_ext_spec.rb +0 -156
- data/spec/core/migration_spec.rb +0 -263
- data/spec/core/pretty_table_spec.rb +0 -58
- data/spec/model/caching_spec.rb +0 -217
- data/spec/model/schema_spec.rb +0 -92
@@ -3,6 +3,34 @@ Sequel.require %w'date_format unsupported', 'adapters/utils'
|
|
3
3
|
module Sequel
|
4
4
|
module Oracle
|
5
5
|
module DatabaseMethods
|
6
|
+
TEMPORARY = 'GLOBAL TEMPORARY '.freeze
|
7
|
+
AUTOINCREMENT = ''.freeze
|
8
|
+
|
9
|
+
def create_sequence(name, opts={})
|
10
|
+
self << create_sequence_sql(name, opts)
|
11
|
+
end
|
12
|
+
|
13
|
+
def create_table(name, options={}, &block)
|
14
|
+
options = {:generator=>options} if options.is_a?(Schema::Generator)
|
15
|
+
generator = options[:generator] || Schema::Generator.new(self, &block)
|
16
|
+
drop_statement, create_statements = create_table_sql_list(name, generator, options)
|
17
|
+
(execute_ddl(drop_statement) rescue nil) if drop_statement
|
18
|
+
(create_statements + index_sql_list(name, generator.indexes)).each{|sql| execute_ddl(sql)}
|
19
|
+
end
|
20
|
+
|
21
|
+
def create_trigger(*args)
|
22
|
+
self << create_trigger_sql(*args)
|
23
|
+
end
|
24
|
+
|
25
|
+
def drop_sequence(name)
|
26
|
+
self << drop_sequence_sql(name)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Oracle uses the :oracle database type
|
30
|
+
def database_type
|
31
|
+
:oracle
|
32
|
+
end
|
33
|
+
|
6
34
|
def tables(opts={})
|
7
35
|
ds = from(:tab).server(opts[:server]).select(:tname).filter(:tabtype => 'TABLE')
|
8
36
|
ds.map{|r| ds.send(:output_identifier, r[:tname])}
|
@@ -11,6 +39,62 @@ module Sequel
|
|
11
39
|
def table_exists?(name)
|
12
40
|
from(:tab).filter(:tname =>dataset.send(:input_identifier, name), :tabtype => 'TABLE').count > 0
|
13
41
|
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def auto_increment_sql
|
46
|
+
AUTOINCREMENT
|
47
|
+
end
|
48
|
+
|
49
|
+
# SQL fragment for showing a table is temporary
|
50
|
+
def temporary_table_sql
|
51
|
+
TEMPORARY
|
52
|
+
end
|
53
|
+
|
54
|
+
def create_sequence_sql(name, opts={})
|
55
|
+
"CREATE SEQUENCE #{quote_identifier(name)} start with #{opts [:start_with]||1} increment by #{opts[:increment_by]||1} nomaxvalue"
|
56
|
+
end
|
57
|
+
|
58
|
+
def create_table_sql_list(name, generator, options={})
|
59
|
+
statements = [create_table_sql(name, generator, options)]
|
60
|
+
drop_seq_statement = nil
|
61
|
+
generator.columns.each do |c|
|
62
|
+
if c[:auto_increment]
|
63
|
+
c[:sequence_name] ||= "seq_#{name}_#{c[:name]}"
|
64
|
+
unless c[:create_sequence] == false
|
65
|
+
drop_seq_statement = drop_sequence_sql(c[:sequence_name])
|
66
|
+
statements << create_sequence_sql(c[:sequence_name], c)
|
67
|
+
end
|
68
|
+
unless c[:create_trigger] == false
|
69
|
+
c[:trigger_name] ||= "BI_#{name}_#{c[:name]}"
|
70
|
+
trigger_definition = <<-end_sql
|
71
|
+
BEGIN
|
72
|
+
IF :NEW.#{quote_identifier(c[:name])} IS NULL THEN
|
73
|
+
SELECT #{c[:sequence_name]}.nextval INTO :NEW.#{quote_identifier(c[:name])} FROM dual;
|
74
|
+
END IF;
|
75
|
+
END;
|
76
|
+
end_sql
|
77
|
+
statements << create_trigger_sql(name, c[:trigger_name], trigger_definition, {:events => [:insert]})
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
[drop_seq_statement, statements]
|
82
|
+
end
|
83
|
+
|
84
|
+
def create_trigger_sql(table, name, definition, opts={})
|
85
|
+
events = opts[:events] ? Array(opts[:events]) : [:insert, :update, :delete]
|
86
|
+
sql = <<-end_sql
|
87
|
+
CREATE#{' OR REPLACE' if opts[:replace]} TRIGGER #{quote_identifier(name)}
|
88
|
+
#{opts[:after] ? 'AFTER' : 'BEFORE'} #{events.map{|e| e.to_s.upcase}.join(' OR ')} ON #{quote_schema_table(table)}
|
89
|
+
REFERENCING NEW AS NEW FOR EACH ROW
|
90
|
+
#{definition}
|
91
|
+
end_sql
|
92
|
+
sql
|
93
|
+
end
|
94
|
+
|
95
|
+
def drop_sequence_sql(name)
|
96
|
+
"DROP SEQUENCE #{quote_identifier(name)}"
|
97
|
+
end
|
14
98
|
end
|
15
99
|
|
16
100
|
module DatasetMethods
|
@@ -43,14 +127,19 @@ module Sequel
|
|
43
127
|
"#{expression} #{quote_identifier(aliaz)}"
|
44
128
|
end
|
45
129
|
|
130
|
+
# Oracle uses the SQL standard of only doubling ' inside strings.
|
131
|
+
def literal_string(v)
|
132
|
+
"'#{v.gsub("'", "''")}'"
|
133
|
+
end
|
134
|
+
|
46
135
|
def select_clause_order
|
47
136
|
SELECT_CLAUSE_ORDER
|
48
137
|
end
|
49
138
|
|
50
139
|
# Oracle requires a subselect to do limit and offset
|
51
|
-
def select_limit_sql(sql
|
52
|
-
if limit = opts[:limit]
|
53
|
-
if (offset = opts[:offset]) && (offset > 0)
|
140
|
+
def select_limit_sql(sql)
|
141
|
+
if limit = @opts[:limit]
|
142
|
+
if (offset = @opts[:offset]) && (offset > 0)
|
54
143
|
sql.replace("SELECT * FROM (SELECT raw_sql_.*, ROWNUM raw_rnum_ FROM(#{sql}) raw_sql_ WHERE ROWNUM <= #{limit + offset}) WHERE raw_rnum_ > #{offset}")
|
55
144
|
else
|
56
145
|
sql.replace("SELECT * FROM (#{sql}) WHERE ROWNUM <= #{limit}")
|
@@ -56,12 +56,12 @@ module Sequel
|
|
56
56
|
|
57
57
|
SELECT_CURRVAL = "SELECT currval('%s')".freeze
|
58
58
|
SELECT_CUSTOM_SEQUENCE = proc do |schema, table| <<-end_sql
|
59
|
-
SELECT '"' || name.nspname || '".
|
59
|
+
SELECT '"' || name.nspname || '".' || CASE
|
60
60
|
WHEN split_part(def.adsrc, '''', 2) ~ '.' THEN
|
61
61
|
substr(split_part(def.adsrc, '''', 2),
|
62
62
|
strpos(split_part(def.adsrc, '''', 2), '.')+1)
|
63
63
|
ELSE split_part(def.adsrc, '''', 2)
|
64
|
-
END
|
64
|
+
END
|
65
65
|
FROM pg_class t
|
66
66
|
JOIN pg_namespace name ON (t.relnamespace = name.oid)
|
67
67
|
JOIN pg_attribute attr ON (t.oid = attrelid)
|
@@ -86,7 +86,7 @@ module Sequel
|
|
86
86
|
end_sql
|
87
87
|
end
|
88
88
|
SELECT_SERIAL_SEQUENCE = proc do |schema, table| <<-end_sql
|
89
|
-
SELECT '"' || name.nspname || '".
|
89
|
+
SELECT '"' || name.nspname || '".' || seq.relname || ''
|
90
90
|
FROM pg_class seq, pg_attribute attr, pg_depend dep,
|
91
91
|
pg_namespace name, pg_constraint cons
|
92
92
|
WHERE seq.oid = dep.objid
|
@@ -172,7 +172,6 @@ module Sequel
|
|
172
172
|
SQL_ROLLBACK = 'ROLLBACK'.freeze
|
173
173
|
SQL_RELEASE_SAVEPOINT = 'RELEASE SAVEPOINT autopoint_%d'.freeze
|
174
174
|
SYSTEM_TABLE_REGEXP = /^pg|sql/.freeze
|
175
|
-
TYPES = Sequel::Database::TYPES.merge(File=>'bytea', String=>'text')
|
176
175
|
|
177
176
|
# Creates the function in the database. Arguments:
|
178
177
|
# * name : name of the function to create
|
@@ -222,6 +221,11 @@ module Sequel
|
|
222
221
|
self << create_trigger_sql(table, name, function, opts)
|
223
222
|
end
|
224
223
|
|
224
|
+
# PostgreSQL uses the :postgres database type.
|
225
|
+
def database_type
|
226
|
+
:postgres
|
227
|
+
end
|
228
|
+
|
225
229
|
# Drops the function from the database. Arguments:
|
226
230
|
# * name : name of the function to drop
|
227
231
|
# * opts : options hash:
|
@@ -261,6 +265,34 @@ module Sequel
|
|
261
265
|
self << drop_trigger_sql(table, name, opts)
|
262
266
|
end
|
263
267
|
|
268
|
+
# Return a hash containing index information. Hash keys are index name symbols.
|
269
|
+
# Values are subhashes with two keys, :columns and :unique. The value of :columns
|
270
|
+
# is an array of symbols of column names. The value of :unique is true or false
|
271
|
+
# depending on if the index is unique.
|
272
|
+
def indexes(table)
|
273
|
+
m = output_identifier_meth
|
274
|
+
im = input_identifier_meth
|
275
|
+
schema, table = schema_and_table(table)
|
276
|
+
ds = metadata_dataset.
|
277
|
+
from(:pg_class___tab).
|
278
|
+
join(:pg_index___ind, :indrelid=>:oid, im.call(table)=>:relname).
|
279
|
+
join(:pg_class___indc, :oid=>:indexrelid).
|
280
|
+
join(:pg_attribute___att, :attrelid=>:tab__oid, :attnum=>SQL::Function.new(:ANY, :ind__indkey)).
|
281
|
+
filter(:indc__relkind=>'i', :ind__indisprimary=>false).
|
282
|
+
exclude(0=>SQL::Function.new(:ANY, :ind__indkey)).
|
283
|
+
order(:indc__relname, (0...32).map{|x| [SQL::Subscript.new(:ind__indkey, [x]), x]}.case(32, :att__attnum)).
|
284
|
+
select(:indc__relname___name, :ind__indisunique___unique, :att__attname___column)
|
285
|
+
|
286
|
+
ds.join!(:pg_namespace___nsp, :oid=>:tab__relnamespace, :nspname=>schema) if schema
|
287
|
+
|
288
|
+
indexes = {}
|
289
|
+
ds.each do |r|
|
290
|
+
i = indexes[m.call(r[:name])] ||= {:columns=>[], :unique=>r[:unique]}
|
291
|
+
i[:columns] << m.call(r[:column])
|
292
|
+
end
|
293
|
+
indexes
|
294
|
+
end
|
295
|
+
|
264
296
|
# Dataset containing all current database locks
|
265
297
|
def locks
|
266
298
|
dataset.from(:pg_class).join(:pg_locks, :relation=>:relfilenode).select(:pg_class__relname, Sequel::SQL::ColumnAll.new(:pg_locks))
|
@@ -287,6 +319,16 @@ module Sequel
|
|
287
319
|
synchronize(opts[:server]){|con| con.sequence(*schema_and_table(table))}
|
288
320
|
end
|
289
321
|
end
|
322
|
+
|
323
|
+
# Reset the primary key sequence for the given table, baseing it on the
|
324
|
+
# maximum current value of the table's primary key.
|
325
|
+
def reset_primary_key_sequence(table)
|
326
|
+
pk = SQL::Identifier.new(primary_key(table))
|
327
|
+
seq = primary_key_sequence(table)
|
328
|
+
db = self
|
329
|
+
seq_ds = db.from(seq.lit)
|
330
|
+
get{setval(seq, db[table].select{coalesce(max(pk)+seq_ds.select{:increment_by}, seq_ds.select(:min_value))}, false)}
|
331
|
+
end
|
290
332
|
|
291
333
|
# PostgreSQL uses SERIAL psuedo-type instead of AUTOINCREMENT for
|
292
334
|
# managing incrementing primary keys.
|
@@ -313,9 +355,10 @@ module Sequel
|
|
313
355
|
# * :schema - The schema to search (default_schema by default)
|
314
356
|
# * :server - The server to use
|
315
357
|
def table_exists?(table, opts={})
|
358
|
+
im = input_identifier_meth
|
316
359
|
schema, table = schema_and_table(table)
|
317
360
|
opts[:schema] ||= schema
|
318
|
-
tables(opts){|ds| !ds.first(:relname=>
|
361
|
+
tables(opts){|ds| !ds.first(:relname=>im.call(table)).nil?}
|
319
362
|
end
|
320
363
|
|
321
364
|
# Array of symbols specifying table names in the current database.
|
@@ -326,22 +369,16 @@ module Sequel
|
|
326
369
|
# * :schema - The schema to search (default_schema by default)
|
327
370
|
# * :server - The server to use
|
328
371
|
def tables(opts={})
|
329
|
-
ds =
|
372
|
+
ds = metadata_dataset.from(:pg_class).filter(:relkind=>'r').select(:relname).exclude(SQL::StringExpression.like(:relname, SYSTEM_TABLE_REGEXP)).server(opts[:server])
|
330
373
|
ds.join!(:pg_namespace, :oid=>:relnamespace, :nspname=>(opts[:schema]||default_schema).to_s) if opts[:schema] || default_schema
|
331
|
-
|
332
|
-
ds.
|
333
|
-
ds2 = dataset
|
334
|
-
block_given? ? yield(ds) : ds.map{|r| ds2.send(:output_identifier, r[:relname])}
|
374
|
+
m = output_identifier_meth
|
375
|
+
block_given? ? yield(ds) : ds.map{|r| m.call(r[:relname])}
|
335
376
|
end
|
336
377
|
|
337
378
|
# PostgreSQL supports multi-level transactions using save points.
|
338
379
|
# To use a savepoint instead of reusing the current transaction,
|
339
380
|
# use the :savepoint=>true option.
|
340
381
|
def transaction(opts={})
|
341
|
-
unless opts.is_a?(Hash)
|
342
|
-
Deprecation.deprecate('Passing an argument other than a Hash to Database#transaction', "Use DB.transaction(:server=>#{opts.inspect})")
|
343
|
-
opts = {:server=>opts}
|
344
|
-
end
|
345
382
|
synchronize(opts[:server]) do |conn|
|
346
383
|
return yield(conn) if @transactions.include?(Thread.current) and !opts[:savepoint]
|
347
384
|
conn.transaction_depth ||= 0
|
@@ -388,7 +425,7 @@ module Sequel
|
|
388
425
|
end
|
389
426
|
|
390
427
|
private
|
391
|
-
|
428
|
+
|
392
429
|
# SQL statement to create database function.
|
393
430
|
def create_function_sql(name, definition, opts={})
|
394
431
|
args = opts[:args]
|
@@ -511,10 +548,17 @@ module Sequel
|
|
511
548
|
"ALTER TABLE #{quote_schema_table(name)} RENAME TO #{quote_identifier(schema_and_table(new_name).last)}"
|
512
549
|
end
|
513
550
|
|
551
|
+
# PostgreSQL's autoincrementing primary keys are of type integer or bigint
|
552
|
+
# using a nextval function call as a default.
|
553
|
+
def schema_autoincrementing_primary_key?(schema)
|
554
|
+
super and schema[:db_type] =~ /\A(?:integer|bigint)\z/io and schema[:default]=~/\Anextval/io
|
555
|
+
end
|
556
|
+
|
514
557
|
# The dataset used for parsing table schemas, using the pg_* system catalogs.
|
515
558
|
def schema_parse_table(table_name, opts)
|
516
|
-
|
517
|
-
|
559
|
+
m = output_identifier_meth
|
560
|
+
m2 = input_identifier_meth
|
561
|
+
ds = metadata_dataset.select(:pg_attribute__attname___name,
|
518
562
|
SQL::Function.new(:format_type, :pg_type__oid, :pg_attribute__atttypmod).as(:db_type),
|
519
563
|
SQL::Function.new(:pg_get_expr, :pg_attrdef__adbin, :pg_class__oid).as(:default),
|
520
564
|
SQL::BooleanExpression.new(:NOT, :pg_attribute__attnotnull).as(:allow_null),
|
@@ -526,15 +570,13 @@ module Sequel
|
|
526
570
|
left_outer_join(:pg_index, :indrelid=>:pg_class__oid, :indisprimary=>true).
|
527
571
|
filter(:pg_attribute__attisdropped=>false).
|
528
572
|
filter{|o| o.pg_attribute__attnum > 0}.
|
529
|
-
filter(:pg_class__relname=>
|
573
|
+
filter(:pg_class__relname=>m2.call(table_name)).
|
530
574
|
order(:pg_attribute__attnum)
|
531
575
|
ds.join!(:pg_namespace, :oid=>:pg_class__relnamespace, :nspname=>(opts[:schema] || default_schema).to_s) if opts[:schema] || default_schema
|
532
|
-
ds.identifier_input_method = nil
|
533
|
-
ds.identifier_output_method = nil
|
534
576
|
ds.map do |row|
|
535
577
|
row[:default] = nil if blank_object?(row[:default])
|
536
578
|
row[:type] = schema_column_type(row[:db_type])
|
537
|
-
[
|
579
|
+
[m.call(row.delete(:name)), row]
|
538
580
|
end
|
539
581
|
end
|
540
582
|
|
@@ -543,9 +585,23 @@ module Sequel
|
|
543
585
|
"(#{Array(args).map{|a| Array(a).reverse.join(' ')}.join(', ')})"
|
544
586
|
end
|
545
587
|
|
546
|
-
#
|
547
|
-
def
|
548
|
-
|
588
|
+
# PostgreSQL uses the bytea data type for blobs
|
589
|
+
def type_literal_generic_file(column)
|
590
|
+
:bytea
|
591
|
+
end
|
592
|
+
|
593
|
+
# PostgreSQL prefers the text datatype. If a fixed size is requested,
|
594
|
+
# the char type is used. If the text type is specifically
|
595
|
+
# disallowed or there is a size specified, use the varchar type.
|
596
|
+
# Otherwise use the type type.
|
597
|
+
def type_literal_generic_string(column)
|
598
|
+
if column[:fixed]
|
599
|
+
"char(#{column[:size]||255})"
|
600
|
+
elsif column[:text] == false or column[:size]
|
601
|
+
"varchar(#{column[:size]||255})"
|
602
|
+
else
|
603
|
+
:text
|
604
|
+
end
|
549
605
|
end
|
550
606
|
end
|
551
607
|
|
@@ -712,9 +768,9 @@ module Sequel
|
|
712
768
|
|
713
769
|
# PostgreSQL is smart and can use parantheses around all datasets to get
|
714
770
|
# the correct answers.
|
715
|
-
def select_compounds_sql(sql
|
716
|
-
return unless opts[:compounds]
|
717
|
-
opts[:compounds].each do |type, dataset, all|
|
771
|
+
def select_compounds_sql(sql)
|
772
|
+
return unless @opts[:compounds]
|
773
|
+
@opts[:compounds].each do |type, dataset, all|
|
718
774
|
sql.replace("(#{sql} #{type.to_s.upcase}#{' ALL' if all} #{subselect_sql(dataset)})")
|
719
775
|
end
|
720
776
|
end
|
@@ -725,8 +781,8 @@ module Sequel
|
|
725
781
|
end
|
726
782
|
|
727
783
|
# Support lock mode, allowing FOR SHARE and FOR UPDATE queries.
|
728
|
-
def select_lock_sql(sql
|
729
|
-
case opts[:lock]
|
784
|
+
def select_lock_sql(sql)
|
785
|
+
case @opts[:lock]
|
730
786
|
when :update
|
731
787
|
sql << FOR_UPDATE
|
732
788
|
when :share
|
@@ -4,6 +4,11 @@ module Sequel
|
|
4
4
|
module Progress
|
5
5
|
module DatabaseMethods
|
6
6
|
|
7
|
+
# Progress uses the :progress database type.
|
8
|
+
def database_type
|
9
|
+
:progress
|
10
|
+
end
|
11
|
+
|
7
12
|
def dataset(opts = nil)
|
8
13
|
ds = super
|
9
14
|
ds.extend(DatasetMethods)
|
@@ -25,9 +30,9 @@ module Sequel
|
|
25
30
|
|
26
31
|
# Progress uses TOP for limit, but it is only supported in Progress 10.
|
27
32
|
# The Progress adapter targets Progress 9, so it silently ignores the option.
|
28
|
-
def select_limit_sql(sql
|
29
|
-
raise(Error, "OFFSET not supported") if opts[:offset]
|
30
|
-
#sql << " TOP #{opts[:limit]}" if opts[:limit]
|
33
|
+
def select_limit_sql(sql)
|
34
|
+
raise(Error, "OFFSET not supported") if @opts[:offset]
|
35
|
+
#sql << " TOP #{@opts[:limit]}" if @opts[:limit]
|
31
36
|
end
|
32
37
|
end
|
33
38
|
end
|
@@ -4,10 +4,10 @@ module Sequel
|
|
4
4
|
module SQLite
|
5
5
|
module DatabaseMethods
|
6
6
|
AUTO_VACUUM = [:none, :full, :incremental].freeze
|
7
|
+
PRIMARY_KEY_INDEX_RE = /\Asqlite_autoindex_/.freeze
|
7
8
|
SYNCHRONOUS = [:off, :normal, :full].freeze
|
8
9
|
TABLES_FILTER = "type = 'table' AND NOT name = 'sqlite_sequence'"
|
9
10
|
TEMP_STORE = [:default, :file, :memory].freeze
|
10
|
-
TYPES = Sequel::Database::TYPES.merge(Bignum=>'integer')
|
11
11
|
|
12
12
|
# Run all alter_table commands in a transaction. This is technically only
|
13
13
|
# needed for drop column.
|
@@ -27,6 +27,34 @@ module Sequel
|
|
27
27
|
pragma_set(:auto_vacuum, value)
|
28
28
|
end
|
29
29
|
|
30
|
+
# SQLite uses the :sqlite database type.
|
31
|
+
def database_type
|
32
|
+
:sqlite
|
33
|
+
end
|
34
|
+
|
35
|
+
# Return a hash containing index information. Hash keys are index name symbols.
|
36
|
+
# Values are subhashes with two keys, :columns and :unique. The value of :columns
|
37
|
+
# is an array of symbols of column names. The value of :unique is true or false
|
38
|
+
# depending on if the index is unique.
|
39
|
+
def indexes(table)
|
40
|
+
m = output_identifier_meth
|
41
|
+
im = input_identifier_meth
|
42
|
+
indexes = {}
|
43
|
+
begin
|
44
|
+
metadata_dataset.with_sql("PRAGMA index_list(?)", im.call(table)).each do |r|
|
45
|
+
next if r[:name] =~ PRIMARY_KEY_INDEX_RE
|
46
|
+
indexes[m.call(r[:name])] = {:unique=>r[:unique].to_i==1}
|
47
|
+
end
|
48
|
+
rescue Sequel::DatabaseError
|
49
|
+
nil
|
50
|
+
else
|
51
|
+
indexes.each do |k, v|
|
52
|
+
v[:columns] = metadata_dataset.with_sql("PRAGMA index_info(?)", im.call(k)).map(:name).map{|x| m.call(x)}
|
53
|
+
end
|
54
|
+
end
|
55
|
+
indexes
|
56
|
+
end
|
57
|
+
|
30
58
|
# Get the value of the given PRAGMA.
|
31
59
|
def pragma_get(name)
|
32
60
|
self["PRAGMA #{name}"].single_value
|
@@ -53,11 +81,8 @@ module Sequel
|
|
53
81
|
# Options:
|
54
82
|
# * :server - Set the server to use.
|
55
83
|
def tables(opts={})
|
56
|
-
|
57
|
-
|
58
|
-
ds.identifier_input_method = nil
|
59
|
-
ds2 = dataset
|
60
|
-
ds.map{|r| ds2.send(:output_identifier, r[:name])}
|
84
|
+
m = output_identifier_meth
|
85
|
+
metadata_dataset.from(:sqlite_master).server(opts[:server]).filter(TABLES_FILTER).map{|r| m.call(r[:name])}
|
61
86
|
end
|
62
87
|
|
63
88
|
# A symbol signifying the value of the temp_store PRAGMA.
|
@@ -141,6 +166,11 @@ module Sequel
|
|
141
166
|
cols
|
142
167
|
end
|
143
168
|
|
169
|
+
# Allow use without a generator, needed for the alter table hackery that Sequel allows.
|
170
|
+
def column_list_sql(generator)
|
171
|
+
generator.is_a?(Schema::Generator) ? super : generator.map{|c| column_definition_sql(c)}.join(', ')
|
172
|
+
end
|
173
|
+
|
144
174
|
# The array of column schema hashes, except for the ones given in opts[:except]
|
145
175
|
def defined_columns_for(table, opts={})
|
146
176
|
cols = parse_pragma(table, {})
|
@@ -164,10 +194,7 @@ module Sequel
|
|
164
194
|
|
165
195
|
# Parse the output of the table_info pragma
|
166
196
|
def parse_pragma(table_name, opts)
|
167
|
-
|
168
|
-
ds = self["PRAGMA table_info(?)", ds2.send(:input_identifier, table_name)]
|
169
|
-
ds.identifier_output_method = nil
|
170
|
-
ds.map do |row|
|
197
|
+
metadata_dataset.with_sql("PRAGMA table_info(?)", input_identifier_meth.call(table_name)).map do |row|
|
171
198
|
row.delete(:cid)
|
172
199
|
row[:allow_null] = row.delete(:notnull).to_i == 0
|
173
200
|
row[:default] = row.delete(:dflt_value)
|
@@ -179,18 +206,26 @@ module Sequel
|
|
179
206
|
end
|
180
207
|
end
|
181
208
|
|
209
|
+
# SQLite treats integer primary keys as autoincrementing (alias of rowid).
|
210
|
+
def schema_autoincrementing_primary_key?(schema)
|
211
|
+
super and schema[:db_type].downcase == 'integer'
|
212
|
+
end
|
213
|
+
|
182
214
|
# SQLite supports schema parsing using the table_info PRAGMA, so
|
183
215
|
# parse the output of that into the format Sequel expects.
|
184
216
|
def schema_parse_table(table_name, opts)
|
185
|
-
|
217
|
+
m = output_identifier_meth
|
186
218
|
parse_pragma(table_name, opts).map do |row|
|
187
|
-
[
|
219
|
+
[m.call(row.delete(:name)), row]
|
188
220
|
end
|
189
221
|
end
|
190
222
|
|
191
|
-
#
|
192
|
-
|
193
|
-
|
223
|
+
# SQLite uses the integer data type even for bignums. This is because they
|
224
|
+
# are both stored internally as text, and converted when returned from
|
225
|
+
# the database. Using an integer type instead of bigint makes it more likely
|
226
|
+
# that software will automatically return the column as an integer.
|
227
|
+
def type_literal_generic_bignum(column)
|
228
|
+
:integer
|
194
229
|
end
|
195
230
|
end
|
196
231
|
|
@@ -217,14 +252,8 @@ module Sequel
|
|
217
252
|
# SQLite performs a TRUNCATE style DELETE if no filter is specified.
|
218
253
|
# Since we want to always return the count of records, add a condition
|
219
254
|
# that is always true and then delete.
|
220
|
-
def delete
|
221
|
-
|
222
|
-
if defarg
|
223
|
-
@opts[:where] ? super() : filter(1=>1).delete
|
224
|
-
else
|
225
|
-
opts = @opts.merge(opts)
|
226
|
-
super(opts[:where] ? opts : opts.merge(:where=>{1=>1}))
|
227
|
-
end
|
255
|
+
def delete
|
256
|
+
@opts[:where] ? super : filter(1=>1).delete
|
228
257
|
end
|
229
258
|
|
230
259
|
# Insert the values into the database.
|
@@ -256,6 +285,7 @@ module Sequel
|
|
256
285
|
|
257
286
|
# SQLite uses string literals instead of identifiers in AS clauses.
|
258
287
|
def as_sql(expression, aliaz)
|
288
|
+
aliaz = aliaz.value if aliaz.is_a?(SQL::Identifier)
|
259
289
|
"#{expression} AS #{literal(aliaz.to_s)}"
|
260
290
|
end
|
261
291
|
end
|