sequel 2.6.0 → 2.7.0

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