sequel 2.6.0 → 2.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +64 -0
- data/Rakefile +1 -1
- data/lib/sequel_core/adapters/jdbc.rb +6 -2
- data/lib/sequel_core/adapters/jdbc/oracle.rb +23 -0
- data/lib/sequel_core/adapters/oracle.rb +4 -77
- data/lib/sequel_core/adapters/postgres.rb +39 -26
- data/lib/sequel_core/adapters/shared/mssql.rb +0 -1
- data/lib/sequel_core/adapters/shared/mysql.rb +1 -1
- data/lib/sequel_core/adapters/shared/oracle.rb +82 -0
- data/lib/sequel_core/adapters/shared/postgres.rb +65 -46
- data/lib/sequel_core/core_ext.rb +10 -0
- data/lib/sequel_core/core_sql.rb +7 -0
- data/lib/sequel_core/database.rb +22 -0
- data/lib/sequel_core/database/schema.rb +1 -1
- data/lib/sequel_core/dataset.rb +29 -11
- data/lib/sequel_core/dataset/sql.rb +27 -7
- data/lib/sequel_core/migration.rb +20 -2
- data/lib/sequel_core/object_graph.rb +24 -10
- data/lib/sequel_core/schema/generator.rb +22 -9
- data/lib/sequel_core/schema/sql.rb +13 -9
- data/lib/sequel_core/sql.rb +27 -2
- data/lib/sequel_model/association_reflection.rb +251 -141
- data/lib/sequel_model/associations.rb +114 -61
- data/lib/sequel_model/base.rb +25 -21
- data/lib/sequel_model/eager_loading.rb +17 -40
- data/lib/sequel_model/hooks.rb +25 -24
- data/lib/sequel_model/record.rb +29 -51
- data/lib/sequel_model/schema.rb +1 -1
- data/lib/sequel_model/validations.rb +13 -3
- data/spec/adapters/postgres_spec.rb +104 -18
- data/spec/adapters/spec_helper.rb +4 -1
- data/spec/integration/eager_loader_test.rb +5 -4
- data/spec/integration/spec_helper.rb +4 -1
- data/spec/sequel_core/connection_pool_spec.rb +24 -24
- data/spec/sequel_core/core_sql_spec.rb +12 -0
- data/spec/sequel_core/dataset_spec.rb +77 -2
- data/spec/sequel_core/expression_filters_spec.rb +6 -0
- data/spec/sequel_core/object_graph_spec.rb +40 -2
- data/spec/sequel_core/schema_spec.rb +13 -0
- data/spec/sequel_model/association_reflection_spec.rb +8 -8
- data/spec/sequel_model/associations_spec.rb +164 -3
- data/spec/sequel_model/caching_spec.rb +2 -1
- data/spec/sequel_model/eager_loading_spec.rb +107 -3
- data/spec/sequel_model/hooks_spec.rb +38 -22
- data/spec/sequel_model/model_spec.rb +11 -35
- data/spec/sequel_model/plugins_spec.rb +4 -2
- data/spec/sequel_model/record_spec.rb +8 -5
- data/spec/sequel_model/validations_spec.rb +25 -0
- data/spec/spec_config.rb +4 -3
- metadata +21 -19
@@ -10,32 +10,35 @@ module Sequel
|
|
10
10
|
|
11
11
|
SELECT_CURRVAL = "SELECT currval('%s')".freeze
|
12
12
|
SELECT_CUSTOM_SEQUENCE = <<-end_sql
|
13
|
-
SELECT CASE
|
13
|
+
SELECT '"' || name.nspname || '"."' || CASE
|
14
14
|
WHEN split_part(def.adsrc, '''', 2) ~ '.' THEN
|
15
15
|
substr(split_part(def.adsrc, '''', 2),
|
16
16
|
strpos(split_part(def.adsrc, '''', 2), '.')+1)
|
17
17
|
ELSE split_part(def.adsrc, '''', 2)
|
18
|
-
END
|
18
|
+
END || '"'
|
19
19
|
FROM pg_class t
|
20
20
|
JOIN pg_namespace name ON (t.relnamespace = name.oid)
|
21
21
|
JOIN pg_attribute attr ON (t.oid = attrelid)
|
22
22
|
JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
|
23
23
|
JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
|
24
|
-
WHERE
|
25
|
-
AND cons.contype = 'p'
|
24
|
+
WHERE cons.contype = 'p'
|
26
25
|
AND def.adsrc ~* 'nextval'
|
26
|
+
AND name.nspname = '%s'
|
27
|
+
AND t.relname = '%s'
|
27
28
|
end_sql
|
28
29
|
SELECT_PK = <<-end_sql
|
29
30
|
SELECT pg_attribute.attname
|
30
|
-
FROM pg_class, pg_attribute, pg_index
|
31
|
-
WHERE pg_class.oid = pg_attribute.attrelid
|
32
|
-
pg_class.
|
33
|
-
|
34
|
-
pg_index.
|
35
|
-
|
31
|
+
FROM pg_class, pg_attribute, pg_index, pg_namespace
|
32
|
+
WHERE pg_class.oid = pg_attribute.attrelid
|
33
|
+
AND pg_class.relnamespace = pg_namespace.oid
|
34
|
+
AND pg_class.oid = pg_index.indrelid
|
35
|
+
AND pg_index.indkey[0] = pg_attribute.attnum
|
36
|
+
AND pg_index.indisprimary = 't'
|
37
|
+
AND pg_namespace.nspname = '%s'
|
38
|
+
AND pg_class.relname = '%s'
|
36
39
|
end_sql
|
37
40
|
SELECT_SERIAL_SEQUENCE = <<-end_sql
|
38
|
-
SELECT seq.relname
|
41
|
+
SELECT '"' || name.nspname || '"."' || seq.relname || '"'
|
39
42
|
FROM pg_class seq, pg_attribute attr, pg_depend dep,
|
40
43
|
pg_namespace name, pg_constraint cons
|
41
44
|
WHERE seq.oid = dep.objid
|
@@ -46,7 +49,8 @@ module Sequel
|
|
46
49
|
AND attr.attrelid = cons.conrelid
|
47
50
|
AND attr.attnum = cons.conkey[1]
|
48
51
|
AND cons.contype = 'p'
|
49
|
-
AND
|
52
|
+
AND name.nspname = '%s'
|
53
|
+
AND seq.relname = '%s'
|
50
54
|
end_sql
|
51
55
|
|
52
56
|
# Depth of the current transaction on this connection, used
|
@@ -64,15 +68,15 @@ module Sequel
|
|
64
68
|
end
|
65
69
|
|
66
70
|
# Get the primary key and sequence for the given table.
|
67
|
-
def sequence(table)
|
68
|
-
sql = SELECT_SERIAL_SEQUENCE % table
|
71
|
+
def sequence(schema, table)
|
72
|
+
sql = SELECT_SERIAL_SEQUENCE % [schema, table]
|
69
73
|
@db.log_info(sql)
|
70
74
|
execute(sql) do |r|
|
71
75
|
seq = single_value(r)
|
72
76
|
return seq if seq
|
73
77
|
end
|
74
78
|
|
75
|
-
sql = SELECT_CUSTOM_SEQUENCE % table
|
79
|
+
sql = SELECT_CUSTOM_SEQUENCE % [schema, table]
|
76
80
|
@db.log_info(sql)
|
77
81
|
execute(sql) do |r|
|
78
82
|
return single_value(r)
|
@@ -80,8 +84,8 @@ module Sequel
|
|
80
84
|
end
|
81
85
|
|
82
86
|
# Get the primary key for the given table.
|
83
|
-
def primary_key(table)
|
84
|
-
sql = SELECT_PK % table
|
87
|
+
def primary_key(schema, table)
|
88
|
+
sql = SELECT_PK % [schema, table]
|
85
89
|
@db.log_info(sql)
|
86
90
|
execute(sql) do |r|
|
87
91
|
return single_value(r)
|
@@ -93,8 +97,6 @@ module Sequel
|
|
93
97
|
module DatabaseMethods
|
94
98
|
PREPARED_ARG_PLACEHOLDER = '$'.lit.freeze
|
95
99
|
RE_CURRVAL_ERROR = /currval of sequence "(.*)" is not yet defined in this session|relation "(.*)" does not exist/.freeze
|
96
|
-
RELATION_QUERY = {:from => [:pg_class], :select => [:relname]}.freeze
|
97
|
-
RELATION_FILTER = "(relkind = 'r') AND (relname !~ '^pg|sql')".freeze
|
98
100
|
SQL_BEGIN = 'BEGIN'.freeze
|
99
101
|
SQL_SAVEPOINT = 'SAVEPOINT autopoint_%d'.freeze
|
100
102
|
SQL_COMMIT = 'COMMIT'.freeze
|
@@ -103,19 +105,29 @@ module Sequel
|
|
103
105
|
SQL_RELEASE_SAVEPOINT = 'RELEASE SAVEPOINT autopoint_%d'.freeze
|
104
106
|
SYSTEM_TABLE_REGEXP = /^pg|sql/.freeze
|
105
107
|
|
108
|
+
# The default schema to use if none is specified (default: public)
|
109
|
+
def default_schema
|
110
|
+
@default_schema ||= :public
|
111
|
+
end
|
112
|
+
|
113
|
+
# Set a new default schema to use.
|
114
|
+
def default_schema=(schema)
|
115
|
+
@default_schema = schema
|
116
|
+
end
|
117
|
+
|
106
118
|
# Remove the cached entries for primary keys and sequences when dropping a table.
|
107
119
|
def drop_table(*names)
|
108
120
|
names.each do |name|
|
109
|
-
|
110
|
-
@primary_keys.delete(
|
111
|
-
@primary_key_sequences.delete(
|
121
|
+
name = quote_schema_table(name)
|
122
|
+
@primary_keys.delete(name)
|
123
|
+
@primary_key_sequences.delete(name)
|
112
124
|
end
|
113
125
|
super
|
114
126
|
end
|
115
127
|
|
116
128
|
# Always CASCADE the table drop
|
117
129
|
def drop_table_sql(name)
|
118
|
-
"DROP TABLE #{name} CASCADE"
|
130
|
+
"DROP TABLE #{quote_schema_table(name)} CASCADE"
|
119
131
|
end
|
120
132
|
|
121
133
|
# PostgreSQL specific index SQL.
|
@@ -128,9 +140,8 @@ module Sequel
|
|
128
140
|
filter = " WHERE #{filter_expr(filter)}" if filter
|
129
141
|
case index_type
|
130
142
|
when :full_text
|
131
|
-
|
132
|
-
|
133
|
-
expr = "(to_tsvector(#{lang}#{cols}))"
|
143
|
+
cols = Array(index[:columns]).map{|x| :COALESCE[x, '']}.sql_string_join(' ')
|
144
|
+
expr = "(to_tsvector(#{literal(index[:language] || 'simple')}, #{literal(cols)}))"
|
134
145
|
index_type = :gin
|
135
146
|
when :spatial
|
136
147
|
index_type = :gist
|
@@ -152,17 +163,8 @@ module Sequel
|
|
152
163
|
|
153
164
|
# Support :schema__table format for table
|
154
165
|
def schema(table_name=nil, opts={})
|
155
|
-
|
156
|
-
|
157
|
-
t, c, a = dataset.send(:split_symbol, table_name)
|
158
|
-
opts[:schema] ||= t
|
159
|
-
table_name = c
|
160
|
-
when SQL::QualifiedIdentifier
|
161
|
-
opts[:schema] ||= table_name.table
|
162
|
-
table_name = table_name.column
|
163
|
-
when SQL::Identifier
|
164
|
-
table_name = table_name.value
|
165
|
-
end
|
166
|
+
schema, table_name = schema_and_table(table_name) if table_name
|
167
|
+
opts[:schema] = schema if schema
|
166
168
|
super(table_name, opts)
|
167
169
|
end
|
168
170
|
|
@@ -185,9 +187,27 @@ module Sequel
|
|
185
187
|
@server_version
|
186
188
|
end
|
187
189
|
|
190
|
+
# Whether the given table exists in the database
|
191
|
+
#
|
192
|
+
# Options:
|
193
|
+
# * :schema - The schema to search (default_schema by default)
|
194
|
+
# * :server - The server to use
|
195
|
+
def table_exists?(table, opts={})
|
196
|
+
schema, table = schema_and_table(table)
|
197
|
+
opts[:schema] ||= schema
|
198
|
+
tables(opts){|ds| !ds.first(:relname=>table.to_s).nil?}
|
199
|
+
end
|
200
|
+
|
188
201
|
# Array of symbols specifying table names in the current database.
|
189
|
-
|
190
|
-
|
202
|
+
# The dataset used is yielded to the block if one is provided,
|
203
|
+
# otherwise, an array of symbols of table names is returned.
|
204
|
+
#
|
205
|
+
# Options:
|
206
|
+
# * :schema - The schema to search (default_schema by default)
|
207
|
+
# * :server - The server to use
|
208
|
+
def tables(opts={})
|
209
|
+
ds = self[:pg_class].join(:pg_namespace, :oid=>:relnamespace, 'r'=>:relkind, :nspname=>(opts[:schema]||default_schema).to_s).select(:relname).exclude(:relname.like(SYSTEM_TABLE_REGEXP)).server(opts[:server])
|
210
|
+
block_given? ? yield(ds) : ds.map{|r| r[:relname].to_sym}
|
191
211
|
end
|
192
212
|
|
193
213
|
# PostgreSQL supports multi-level transactions using save points.
|
@@ -273,14 +293,14 @@ module Sequel
|
|
273
293
|
# cached, and if the primary key for a table is changed, the
|
274
294
|
# @primary_keys instance variable should be reset manually.
|
275
295
|
def primary_key_for_table(conn, table)
|
276
|
-
@primary_keys
|
296
|
+
@primary_keys[quote_schema_table(table)] ||= conn.primary_key(*schema_and_table(table))
|
277
297
|
end
|
278
298
|
|
279
299
|
# Returns primary key for the given table. This information is
|
280
300
|
# cached, and if the primary key for a table is changed, the
|
281
301
|
# @primary_keys instance variable should be reset manually.
|
282
302
|
def primary_key_sequence_for_table(conn, table)
|
283
|
-
@primary_key_sequences
|
303
|
+
@primary_key_sequences[quote_schema_table(table)] ||= conn.sequence(*schema_and_table(table))
|
284
304
|
end
|
285
305
|
|
286
306
|
# Set the default of the row to NULL if it is blank, and set
|
@@ -330,7 +350,7 @@ module Sequel
|
|
330
350
|
else
|
331
351
|
ds.select_more!(:pg_class__relname___table)
|
332
352
|
end
|
333
|
-
ds.join!(:pg_namespace, :oid=>:pg_class__relnamespace, :nspname=>opts[:schema] ||
|
353
|
+
ds.join!(:pg_namespace, :oid=>:pg_class__relnamespace, :nspname=>(opts[:schema] || default_schema).to_s) if opts[:schema] || !opts.include?(:schema)
|
334
354
|
ds
|
335
355
|
end
|
336
356
|
end
|
@@ -401,10 +421,9 @@ module Sequel
|
|
401
421
|
# PostgreSQL specific full text search syntax, using tsearch2 (included
|
402
422
|
# in 8.3 by default, and available for earlier versions as an add-on).
|
403
423
|
def full_text_search(cols, terms, opts = {})
|
404
|
-
lang = opts[:language]
|
405
|
-
cols =
|
406
|
-
|
407
|
-
filter("to_tsvector(#{lang}#{cols}) @@ to_tsquery(#{lang}#{terms})")
|
424
|
+
lang = opts[:language] || 'simple'
|
425
|
+
cols = Array(cols).map{|x| :COALESCE[x, '']}.sql_string_join(' ')
|
426
|
+
filter("to_tsvector(#{literal(lang)}, #{literal(cols)}) @@ to_tsquery(#{literal(lang)}, #{literal(Array(terms).join(' | '))})")
|
408
427
|
end
|
409
428
|
|
410
429
|
# Insert given values into the database.
|
data/lib/sequel_core/core_ext.rb
CHANGED
@@ -52,6 +52,16 @@ class Module
|
|
52
52
|
|
53
53
|
private
|
54
54
|
|
55
|
+
# Define instance method(s) that calls class method(s) of the
|
56
|
+
# same name, caching the result in an instance variable. Define
|
57
|
+
# standard attr_writer method for modifying that instance variable
|
58
|
+
def class_attr_overridable(*meths)
|
59
|
+
meths.each{|meth| class_eval("def #{meth}; @#{meth}.nil? ? (@#{meth} = self.class.#{meth}) : @#{meth} end")}
|
60
|
+
attr_writer(*meths)
|
61
|
+
public(*meths)
|
62
|
+
public(*meths.collect{|m|"#{m}="})
|
63
|
+
end
|
64
|
+
|
55
65
|
# Define instance method(s) that calls class method(s) of the
|
56
66
|
# same name. Replaces the construct:
|
57
67
|
#
|
data/lib/sequel_core/core_sql.rb
CHANGED
@@ -11,6 +11,13 @@ class Array
|
|
11
11
|
::Sequel::SQL::CaseExpression.new(self, default, expression)
|
12
12
|
end
|
13
13
|
|
14
|
+
# Return a Sequel::SQL::Array created from this array. Used if this array contains
|
15
|
+
# all two pairs and you want it treated as an SQL array instead of a ordered hash-like
|
16
|
+
# conditions.
|
17
|
+
def sql_array
|
18
|
+
::Sequel::SQL::SQLArray.new(self)
|
19
|
+
end
|
20
|
+
|
14
21
|
# Return a Sequel::SQL::BooleanExpression created from this array, matching all of the
|
15
22
|
# conditions.
|
16
23
|
def sql_expr
|
data/lib/sequel_core/database.rb
CHANGED
@@ -495,6 +495,11 @@ module Sequel
|
|
495
495
|
def connection_pool_default_options
|
496
496
|
{}
|
497
497
|
end
|
498
|
+
|
499
|
+
# Sequel doesn't use database schema's by default.
|
500
|
+
def default_schema
|
501
|
+
nil
|
502
|
+
end
|
498
503
|
|
499
504
|
# SQL to ROLLBACK a transaction.
|
500
505
|
def rollback_transaction_sql
|
@@ -512,6 +517,23 @@ module Sequel
|
|
512
517
|
raise exception
|
513
518
|
end
|
514
519
|
end
|
520
|
+
|
521
|
+
# Split the schema information from the table
|
522
|
+
def schema_and_table(table_name)
|
523
|
+
case table_name
|
524
|
+
when Symbol
|
525
|
+
s, t, a = dataset.send(:split_symbol, table_name)
|
526
|
+
[s||default_schema, t]
|
527
|
+
when SQL::QualifiedIdentifier
|
528
|
+
[table_name.table, table_name.column]
|
529
|
+
when SQL::Identifier
|
530
|
+
[default_schema, table_name.value]
|
531
|
+
when String
|
532
|
+
[default_schema, table_name]
|
533
|
+
else
|
534
|
+
raise Error, 'table_name should be a Symbol, SQL::QualifiedIdentifier, SQL::Identifier, or String'
|
535
|
+
end
|
536
|
+
end
|
515
537
|
|
516
538
|
# Return the options for the given server by merging the generic
|
517
539
|
# options for all server with the specific options for the given
|
@@ -109,7 +109,7 @@ module Sequel
|
|
109
109
|
# DB.drop_table(:posts, :comments)
|
110
110
|
def drop_table(*names)
|
111
111
|
names.each do |n|
|
112
|
-
@schemas.delete(n.to_sym) if @schemas
|
112
|
+
@schemas.delete(n.is_a?(String) ? n.to_sym : n) if @schemas
|
113
113
|
execute_ddl(drop_table_sql(n))
|
114
114
|
end
|
115
115
|
end
|
data/lib/sequel_core/dataset.rb
CHANGED
@@ -48,7 +48,8 @@ module Sequel
|
|
48
48
|
|
49
49
|
# All methods that should have a ! method added that modifies
|
50
50
|
# the receiver.
|
51
|
-
MUTATION_METHODS = %w'and distinct exclude exists
|
51
|
+
MUTATION_METHODS = %w'add_graph_aliases and distinct exclude exists
|
52
|
+
filter from from_self full_outer_join graph
|
52
53
|
group group_and_count group_by having inner_join intersect invert join
|
53
54
|
left_outer_join limit naked or order order_by order_more paginate query reject
|
54
55
|
reverse reverse_order right_outer_join select select_all select_more
|
@@ -190,18 +191,18 @@ module Sequel
|
|
190
191
|
execute_dui(delete_sql(*args))
|
191
192
|
end
|
192
193
|
|
193
|
-
# Iterates over the records in the dataset.
|
194
|
+
# Iterates over the records in the dataset and returns set. If opts
|
195
|
+
# have been passed that modify the columns, reset the column information.
|
194
196
|
def each(opts = nil, &block)
|
195
|
-
if
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
r = transform_load(r) if transform
|
202
|
-
r = row_proc[r] if row_proc
|
203
|
-
yield r
|
197
|
+
if opts && opts.keys.any?{|o| COLUMN_CHANGE_OPTS.include?(o)}
|
198
|
+
prev_columns = @columns
|
199
|
+
begin
|
200
|
+
_each(opts, &block)
|
201
|
+
ensure
|
202
|
+
@columns = prev_columns
|
204
203
|
end
|
204
|
+
else
|
205
|
+
_each(opts, &block)
|
205
206
|
end
|
206
207
|
self
|
207
208
|
end
|
@@ -432,6 +433,23 @@ module Sequel
|
|
432
433
|
|
433
434
|
private
|
434
435
|
|
436
|
+
# Runs #graph_each if graphing. Otherwise, iterates through the records
|
437
|
+
# yielded by #fetch_rows, applying any row_proc or transform if necessary,
|
438
|
+
# and yielding the result.
|
439
|
+
def _each(opts, &block)
|
440
|
+
if @opts[:graph] and !(opts && opts[:graph] == false)
|
441
|
+
graph_each(opts, &block)
|
442
|
+
else
|
443
|
+
row_proc = @row_proc unless opts && opts[:naked]
|
444
|
+
transform = @transform
|
445
|
+
fetch_rows(select_sql(opts)) do |r|
|
446
|
+
r = transform_load(r) if transform
|
447
|
+
r = row_proc[r] if row_proc
|
448
|
+
yield r
|
449
|
+
end
|
450
|
+
end
|
451
|
+
end
|
452
|
+
|
435
453
|
# Execute the given SQL on the database using execute.
|
436
454
|
def execute(sql, opts={}, &block)
|
437
455
|
@db.execute(sql, {:server=>@opts[:server] || :read_only}.merge(opts), &block)
|
@@ -29,6 +29,11 @@ module Sequel
|
|
29
29
|
as_sql(literal(ae.expression), ae.aliaz)
|
30
30
|
end
|
31
31
|
|
32
|
+
# SQL fragment for the SQL array.
|
33
|
+
def array_sql(a)
|
34
|
+
a.empty? ? '(NULL)' : "(#{expression_list(a)})"
|
35
|
+
end
|
36
|
+
|
32
37
|
# SQL fragment for specifying given CaseExpression.
|
33
38
|
def case_expression_sql(ce)
|
34
39
|
sql = '(CASE '
|
@@ -363,7 +368,8 @@ module Sequel
|
|
363
368
|
# * String, Symbol: table
|
364
369
|
# * expr - specifies conditions, depends on type:
|
365
370
|
# * Hash, Array with all two pairs - Assumes key (1st arg) is column of joined table (unless already
|
366
|
-
# qualified), and value (2nd arg) is column of the last joined or primary table
|
371
|
+
# qualified), and value (2nd arg) is column of the last joined or primary table (or the
|
372
|
+
# :implicit_qualifier option).
|
367
373
|
# To specify multiple conditions on a single joined table column, you must use an array.
|
368
374
|
# Uses a JOIN with an ON clause.
|
369
375
|
# * Array - If all members of the array are symbols, considers them as columns and
|
@@ -374,13 +380,23 @@ module Sequel
|
|
374
380
|
# * Everything else - pretty much the same as a using the argument in a call to filter,
|
375
381
|
# so strings are considered literal, symbols specify boolean columns, and blockless
|
376
382
|
# filter expressions can be used. Uses a JOIN with an ON clause.
|
377
|
-
# *
|
378
|
-
#
|
383
|
+
# * options - a hash of options, with any of the following keys:
|
384
|
+
# * :table_alias - the name of the table's alias when joining, necessary for joining
|
385
|
+
# to the same table more than once. No alias is used by default.
|
386
|
+
# * :implicit_qualifer - The name to use for qualifying implicit conditions. By default,
|
387
|
+
# the last joined or primary table is used.
|
379
388
|
# * block - The block argument should only be given if a JOIN with an ON clause is used,
|
380
389
|
# in which case it yields the table alias/name for the table currently being joined,
|
381
390
|
# the table alias/name for the last joined (or first table), and an array of previous
|
382
391
|
# SQL::JoinClause.
|
383
|
-
def join_table(type, table, expr=nil,
|
392
|
+
def join_table(type, table, expr=nil, options={}, &block)
|
393
|
+
if options.is_one_of?(Symbol, String)
|
394
|
+
table_alias = options
|
395
|
+
last_alias = nil
|
396
|
+
else
|
397
|
+
table_alias = options[:table_alias]
|
398
|
+
last_alias = options[:implicit_qualifier]
|
399
|
+
end
|
384
400
|
if Dataset === table
|
385
401
|
if table_alias.nil?
|
386
402
|
table_alias_num = (@opts[:num_dataset_sources] || 0) + 1
|
@@ -398,7 +414,7 @@ module Sequel
|
|
398
414
|
raise(Sequel::Error, "can't use a block if providing an array of symbols as expr") if block_given?
|
399
415
|
SQL::JoinUsingClause.new(expr, type, table, table_alias)
|
400
416
|
else
|
401
|
-
last_alias
|
417
|
+
last_alias ||= @opts[:last_joined_table] || (first_source.is_a?(Dataset) ? 't1' : first_source)
|
402
418
|
if Hash === expr or (Array === expr and expr.all_two_pairs?)
|
403
419
|
expr = expr.collect do |k, v|
|
404
420
|
k = qualified_column_name(k, table_name) if k.is_a?(Symbol)
|
@@ -470,7 +486,7 @@ module Sequel
|
|
470
486
|
when ::Sequel::SQL::Expression
|
471
487
|
v.to_s(self)
|
472
488
|
when Array
|
473
|
-
v.all_two_pairs? ? literal(v.sql_expr) : (v
|
489
|
+
v.all_two_pairs? ? literal(v.sql_expr) : array_sql(v)
|
474
490
|
when Hash
|
475
491
|
literal(v.sql_expr)
|
476
492
|
when Time, DateTime
|
@@ -653,7 +669,11 @@ module Sequel
|
|
653
669
|
|
654
670
|
sql
|
655
671
|
end
|
656
|
-
|
672
|
+
|
673
|
+
# Same as select_sql, not aliased directly to make subclassing simpler.
|
674
|
+
def sql(*args)
|
675
|
+
select_sql(*args)
|
676
|
+
end
|
657
677
|
|
658
678
|
# SQL fragment for specifying subscripts (SQL arrays)
|
659
679
|
def subscript_sql(s)
|
@@ -13,7 +13,22 @@ module Sequel
|
|
13
13
|
# end
|
14
14
|
#
|
15
15
|
# def down
|
16
|
-
#
|
16
|
+
# # You can use raw SQL if you need to
|
17
|
+
# self << 'DROP TABLE sessions'
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# class AlterItems < Sequel::Migration
|
22
|
+
# def up
|
23
|
+
# alter_table :items do
|
24
|
+
# add_column :category, :text, :default => 'ruby'
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# def down
|
29
|
+
# alter_table :items do
|
30
|
+
# drop_column :category
|
31
|
+
# end
|
17
32
|
# end
|
18
33
|
# end
|
19
34
|
#
|
@@ -25,7 +40,10 @@ module Sequel
|
|
25
40
|
#
|
26
41
|
# See Sequel::Schema::Generator for the syntax to use for creating tables,
|
27
42
|
# and Sequel::Schema::AlterTableGenerator for the syntax to use when
|
28
|
-
# altering existing tables.
|
43
|
+
# altering existing tables. Migrations act as a proxy for the database
|
44
|
+
# given in #apply, so inside #down and #up, you can act as though self
|
45
|
+
# refers to the database. So you can use any of the Sequel::Database
|
46
|
+
# instance methods directly.
|
29
47
|
class Migration
|
30
48
|
# Creates a new instance of the migration and sets the @db attribute.
|
31
49
|
def initialize(db)
|