sequel 2.6.0 → 2.7.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.
Files changed (50) hide show
  1. data/CHANGELOG +64 -0
  2. data/Rakefile +1 -1
  3. data/lib/sequel_core/adapters/jdbc.rb +6 -2
  4. data/lib/sequel_core/adapters/jdbc/oracle.rb +23 -0
  5. data/lib/sequel_core/adapters/oracle.rb +4 -77
  6. data/lib/sequel_core/adapters/postgres.rb +39 -26
  7. data/lib/sequel_core/adapters/shared/mssql.rb +0 -1
  8. data/lib/sequel_core/adapters/shared/mysql.rb +1 -1
  9. data/lib/sequel_core/adapters/shared/oracle.rb +82 -0
  10. data/lib/sequel_core/adapters/shared/postgres.rb +65 -46
  11. data/lib/sequel_core/core_ext.rb +10 -0
  12. data/lib/sequel_core/core_sql.rb +7 -0
  13. data/lib/sequel_core/database.rb +22 -0
  14. data/lib/sequel_core/database/schema.rb +1 -1
  15. data/lib/sequel_core/dataset.rb +29 -11
  16. data/lib/sequel_core/dataset/sql.rb +27 -7
  17. data/lib/sequel_core/migration.rb +20 -2
  18. data/lib/sequel_core/object_graph.rb +24 -10
  19. data/lib/sequel_core/schema/generator.rb +22 -9
  20. data/lib/sequel_core/schema/sql.rb +13 -9
  21. data/lib/sequel_core/sql.rb +27 -2
  22. data/lib/sequel_model/association_reflection.rb +251 -141
  23. data/lib/sequel_model/associations.rb +114 -61
  24. data/lib/sequel_model/base.rb +25 -21
  25. data/lib/sequel_model/eager_loading.rb +17 -40
  26. data/lib/sequel_model/hooks.rb +25 -24
  27. data/lib/sequel_model/record.rb +29 -51
  28. data/lib/sequel_model/schema.rb +1 -1
  29. data/lib/sequel_model/validations.rb +13 -3
  30. data/spec/adapters/postgres_spec.rb +104 -18
  31. data/spec/adapters/spec_helper.rb +4 -1
  32. data/spec/integration/eager_loader_test.rb +5 -4
  33. data/spec/integration/spec_helper.rb +4 -1
  34. data/spec/sequel_core/connection_pool_spec.rb +24 -24
  35. data/spec/sequel_core/core_sql_spec.rb +12 -0
  36. data/spec/sequel_core/dataset_spec.rb +77 -2
  37. data/spec/sequel_core/expression_filters_spec.rb +6 -0
  38. data/spec/sequel_core/object_graph_spec.rb +40 -2
  39. data/spec/sequel_core/schema_spec.rb +13 -0
  40. data/spec/sequel_model/association_reflection_spec.rb +8 -8
  41. data/spec/sequel_model/associations_spec.rb +164 -3
  42. data/spec/sequel_model/caching_spec.rb +2 -1
  43. data/spec/sequel_model/eager_loading_spec.rb +107 -3
  44. data/spec/sequel_model/hooks_spec.rb +38 -22
  45. data/spec/sequel_model/model_spec.rb +11 -35
  46. data/spec/sequel_model/plugins_spec.rb +4 -2
  47. data/spec/sequel_model/record_spec.rb +8 -5
  48. data/spec/sequel_model/validations_spec.rb +25 -0
  49. data/spec/spec_config.rb +4 -3
  50. 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 t.oid = '%s'::regclass
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 AND
32
- pg_class.oid = pg_index.indrelid AND
33
- pg_index.indkey[0] = pg_attribute.attnum AND
34
- pg_index.indisprimary = 't' AND
35
- pg_class.relname = '%s'
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 dep.refobjid = '%s'::regclass
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
- s = name.to_sym
110
- @primary_keys.delete(s)
111
- @primary_key_sequences.delete(s)
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
- lang = "#{literal(index[:language] || 'simple')}, "
132
- cols = index[:columns].map {|c| literal(c)}.join(" || ")
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
- case table_name
156
- when Symbol
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
- def tables
190
- dataset(RELATION_QUERY).filter(RELATION_FILTER).map{|r| r[:relname].to_sym}
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.include?(table) ? @primary_keys[table] : (@primary_keys[table] = conn.primary_key(table))
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.include?(table) ? @primary_key_sequences[table] : (@primary_key_sequences[table] = conn.sequence(table))
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] || 'public') if opts[:schema] || !opts.include?(: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] ? "#{literal(opts[:language])}, " : ""
405
- cols = cols.is_a?(Array) ? cols.map {|c| literal(c)}.join(" || ") : literal(cols)
406
- terms = terms.is_a?(Array) ? literal(terms.join(" | ")) : literal(terms)
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.
@@ -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
  #
@@ -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
@@ -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
@@ -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 filter from from_self full_outer_join graph
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 @opts[:graph] and !(opts && opts[:graph] == false)
196
- graph_each(opts, &block)
197
- else
198
- row_proc = @row_proc unless opts && opts[:naked]
199
- transform = @transform
200
- fetch_rows(select_sql(opts)) do |r|
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
- # * table_alias - the name of the table's alias when joining, necessary for joining
378
- # to the same table more than once. No alias is used by default.
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, table_alias=nil, &block)
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 = @opts[:last_joined_table] || (first_source.is_a?(Dataset) ? 't1' : first_source)
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.empty? ? '(NULL)' : "(#{expression_list(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
- alias_method :sql, :select_sql
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
- # execute 'DROP TABLE sessions'
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)