sequel 3.11.0 → 3.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (136) hide show
  1. data/CHANGELOG +70 -0
  2. data/Rakefile +1 -1
  3. data/doc/active_record.rdoc +896 -0
  4. data/doc/advanced_associations.rdoc +46 -31
  5. data/doc/association_basics.rdoc +14 -9
  6. data/doc/dataset_basics.rdoc +3 -3
  7. data/doc/migration.rdoc +1011 -0
  8. data/doc/model_hooks.rdoc +198 -0
  9. data/doc/querying.rdoc +811 -86
  10. data/doc/release_notes/3.12.0.txt +304 -0
  11. data/doc/sharding.rdoc +17 -0
  12. data/doc/sql.rdoc +537 -0
  13. data/doc/validations.rdoc +501 -0
  14. data/lib/sequel/adapters/jdbc.rb +19 -27
  15. data/lib/sequel/adapters/jdbc/postgresql.rb +0 -7
  16. data/lib/sequel/adapters/mysql.rb +5 -4
  17. data/lib/sequel/adapters/odbc.rb +3 -2
  18. data/lib/sequel/adapters/shared/mssql.rb +7 -6
  19. data/lib/sequel/adapters/shared/mysql.rb +2 -7
  20. data/lib/sequel/adapters/shared/postgres.rb +2 -8
  21. data/lib/sequel/adapters/shared/sqlite.rb +2 -5
  22. data/lib/sequel/adapters/sqlite.rb +4 -4
  23. data/lib/sequel/core.rb +0 -1
  24. data/lib/sequel/database.rb +2 -1060
  25. data/lib/sequel/database/connecting.rb +227 -0
  26. data/lib/sequel/database/dataset.rb +58 -0
  27. data/lib/sequel/database/dataset_defaults.rb +127 -0
  28. data/lib/sequel/database/logging.rb +62 -0
  29. data/lib/sequel/database/misc.rb +246 -0
  30. data/lib/sequel/database/query.rb +390 -0
  31. data/lib/sequel/database/schema_generator.rb +7 -3
  32. data/lib/sequel/database/schema_methods.rb +351 -7
  33. data/lib/sequel/dataset/actions.rb +9 -2
  34. data/lib/sequel/dataset/misc.rb +6 -2
  35. data/lib/sequel/dataset/mutation.rb +3 -11
  36. data/lib/sequel/dataset/query.rb +49 -6
  37. data/lib/sequel/exceptions.rb +3 -0
  38. data/lib/sequel/extensions/migration.rb +395 -113
  39. data/lib/sequel/extensions/schema_dumper.rb +21 -13
  40. data/lib/sequel/model.rb +27 -25
  41. data/lib/sequel/model/associations.rb +72 -34
  42. data/lib/sequel/model/base.rb +74 -18
  43. data/lib/sequel/model/errors.rb +8 -1
  44. data/lib/sequel/plugins/active_model.rb +8 -0
  45. data/lib/sequel/plugins/association_pks.rb +87 -0
  46. data/lib/sequel/plugins/association_proxies.rb +8 -0
  47. data/lib/sequel/plugins/boolean_readers.rb +12 -6
  48. data/lib/sequel/plugins/caching.rb +14 -7
  49. data/lib/sequel/plugins/class_table_inheritance.rb +15 -9
  50. data/lib/sequel/plugins/composition.rb +2 -1
  51. data/lib/sequel/plugins/force_encoding.rb +10 -7
  52. data/lib/sequel/plugins/hook_class_methods.rb +12 -11
  53. data/lib/sequel/plugins/identity_map.rb +9 -0
  54. data/lib/sequel/plugins/instance_hooks.rb +23 -13
  55. data/lib/sequel/plugins/lazy_attributes.rb +4 -1
  56. data/lib/sequel/plugins/many_through_many.rb +18 -4
  57. data/lib/sequel/plugins/nested_attributes.rb +1 -0
  58. data/lib/sequel/plugins/optimistic_locking.rb +1 -1
  59. data/lib/sequel/plugins/rcte_tree.rb +9 -8
  60. data/lib/sequel/plugins/schema.rb +8 -0
  61. data/lib/sequel/plugins/serialization.rb +1 -3
  62. data/lib/sequel/plugins/sharding.rb +135 -0
  63. data/lib/sequel/plugins/single_table_inheritance.rb +117 -25
  64. data/lib/sequel/plugins/skip_create_refresh.rb +35 -0
  65. data/lib/sequel/plugins/string_stripper.rb +26 -0
  66. data/lib/sequel/plugins/tactical_eager_loading.rb +8 -0
  67. data/lib/sequel/plugins/timestamps.rb +15 -2
  68. data/lib/sequel/plugins/touch.rb +13 -0
  69. data/lib/sequel/plugins/update_primary_key.rb +48 -0
  70. data/lib/sequel/plugins/validation_class_methods.rb +8 -0
  71. data/lib/sequel/plugins/validation_helpers.rb +1 -1
  72. data/lib/sequel/sql.rb +17 -20
  73. data/lib/sequel/version.rb +1 -1
  74. data/spec/adapters/postgres_spec.rb +5 -5
  75. data/spec/core/core_sql_spec.rb +17 -1
  76. data/spec/core/database_spec.rb +17 -5
  77. data/spec/core/dataset_spec.rb +31 -8
  78. data/spec/core/schema_generator_spec.rb +8 -1
  79. data/spec/core/schema_spec.rb +13 -0
  80. data/spec/extensions/association_pks_spec.rb +85 -0
  81. data/spec/extensions/hook_class_methods_spec.rb +9 -9
  82. data/spec/extensions/migration_spec.rb +339 -219
  83. data/spec/extensions/schema_dumper_spec.rb +28 -17
  84. data/spec/extensions/sharding_spec.rb +272 -0
  85. data/spec/extensions/single_table_inheritance_spec.rb +92 -4
  86. data/spec/extensions/skip_create_refresh_spec.rb +17 -0
  87. data/spec/extensions/string_stripper_spec.rb +23 -0
  88. data/spec/extensions/update_primary_key_spec.rb +65 -0
  89. data/spec/extensions/validation_class_methods_spec.rb +5 -5
  90. data/spec/files/bad_down_migration/001_create_alt_basic.rb +4 -0
  91. data/spec/files/bad_down_migration/002_create_alt_advanced.rb +4 -0
  92. data/spec/files/bad_timestamped_migrations/1273253849_create_sessions.rb +9 -0
  93. data/spec/files/bad_timestamped_migrations/1273253851_create_nodes.rb +9 -0
  94. data/spec/files/bad_timestamped_migrations/1273253853_3_create_users.rb +3 -0
  95. data/spec/files/bad_up_migration/001_create_alt_basic.rb +4 -0
  96. data/spec/files/bad_up_migration/002_create_alt_advanced.rb +3 -0
  97. data/spec/files/convert_to_timestamp_migrations/001_create_sessions.rb +9 -0
  98. data/spec/files/convert_to_timestamp_migrations/002_create_nodes.rb +9 -0
  99. data/spec/files/convert_to_timestamp_migrations/003_3_create_users.rb +4 -0
  100. data/spec/files/convert_to_timestamp_migrations/1273253850_create_artists.rb +9 -0
  101. data/spec/files/convert_to_timestamp_migrations/1273253852_create_albums.rb +9 -0
  102. data/spec/files/duplicate_integer_migrations/001_create_alt_advanced.rb +4 -0
  103. data/spec/files/duplicate_integer_migrations/001_create_alt_basic.rb +4 -0
  104. data/spec/files/duplicate_timestamped_migrations/1273253849_create_sessions.rb +9 -0
  105. data/spec/files/duplicate_timestamped_migrations/1273253853_create_nodes.rb +9 -0
  106. data/spec/files/duplicate_timestamped_migrations/1273253853_create_users.rb +4 -0
  107. data/spec/files/integer_migrations/001_create_sessions.rb +9 -0
  108. data/spec/files/integer_migrations/002_create_nodes.rb +9 -0
  109. data/spec/files/integer_migrations/003_3_create_users.rb +4 -0
  110. data/spec/files/interleaved_timestamped_migrations/1273253849_create_sessions.rb +9 -0
  111. data/spec/files/interleaved_timestamped_migrations/1273253850_create_artists.rb +9 -0
  112. data/spec/files/interleaved_timestamped_migrations/1273253851_create_nodes.rb +9 -0
  113. data/spec/files/interleaved_timestamped_migrations/1273253852_create_albums.rb +9 -0
  114. data/spec/files/interleaved_timestamped_migrations/1273253853_3_create_users.rb +4 -0
  115. data/spec/files/missing_integer_migrations/001_create_alt_basic.rb +4 -0
  116. data/spec/files/missing_integer_migrations/003_create_alt_advanced.rb +4 -0
  117. data/spec/files/missing_timestamped_migrations/1273253849_create_sessions.rb +9 -0
  118. data/spec/files/missing_timestamped_migrations/1273253853_3_create_users.rb +4 -0
  119. data/spec/files/timestamped_migrations/1273253849_create_sessions.rb +9 -0
  120. data/spec/files/timestamped_migrations/1273253851_create_nodes.rb +9 -0
  121. data/spec/files/timestamped_migrations/1273253853_3_create_users.rb +4 -0
  122. data/spec/files/uppercase_timestamped_migrations/1273253849_CREATE_SESSIONS.RB +9 -0
  123. data/spec/files/uppercase_timestamped_migrations/1273253851_CREATE_NODES.RB +9 -0
  124. data/spec/files/uppercase_timestamped_migrations/1273253853_3_CREATE_USERS.RB +4 -0
  125. data/spec/integration/eager_loader_test.rb +20 -20
  126. data/spec/integration/migrator_test.rb +187 -0
  127. data/spec/integration/plugin_test.rb +150 -0
  128. data/spec/integration/schema_test.rb +13 -2
  129. data/spec/model/associations_spec.rb +41 -14
  130. data/spec/model/base_spec.rb +69 -0
  131. data/spec/model/eager_loading_spec.rb +7 -3
  132. data/spec/model/record_spec.rb +79 -4
  133. data/spec/model/validations_spec.rb +21 -9
  134. metadata +66 -5
  135. data/doc/schema.rdoc +0 -36
  136. data/lib/sequel/database/schema_sql.rb +0 -320
@@ -13,7 +13,7 @@ module Sequel
13
13
  # the column method, which makes for a nicer DSL.
14
14
  #
15
15
  # For more information on Sequel's support for schema modification, see
16
- # the {"Schema Modification" guide}[link:files/doc/schema_rdoc.html].
16
+ # the {"Migrations and Schema Modification" guide}[link:files/doc/migration_rdoc.html].
17
17
  class Generator
18
18
  # Classes specifying generic types that Sequel will convert to database-specific types.
19
19
  GENERIC_TYPES=[String, Integer, Fixnum, Bignum, Float, Numeric, BigDecimal,
@@ -75,7 +75,7 @@ module Sequel
75
75
  # or not allowing NULL values (if false). If unspecified, will default
76
76
  # to whatever the database default is.
77
77
  # * :on_delete - Specify the behavior of this column when being deleted.
78
- # See Dataset#on_delete_clause for options.
78
+ # See Schema::SQL#on_delete_clause for options.
79
79
  # * :on_update - Specify the behavior of this column when being updated.
80
80
  # See Schema::SQL#on_delete_clause for options.
81
81
  # * :size - The size of the column, generally used with string
@@ -86,6 +86,10 @@ module Sequel
86
86
  # creating a unique index on the column.
87
87
  # * :unsigned - Make the column type unsigned, only useful for integer
88
88
  # columns.
89
+ # * :deferrable - This ensure Referential Integrity will work even if
90
+ # reference table will use for its foreign key a value that does not
91
+ # exists(yet) on referenced table. Basically it adds
92
+ # DEFERRABLE INITIALLY DEFERRED on key creation.
89
93
  def column(name, type, opts = {})
90
94
  columns << {:name => name, :type => type}.merge(opts)
91
95
  index(name) if opts[:index]
@@ -205,7 +209,7 @@ module Sequel
205
209
  # alter a table's description.
206
210
  #
207
211
  # For more information on Sequel's support for schema modification, see
208
- # the {"Schema Modification" guide}[link:files/doc/schema_rdoc.html].
212
+ # the {"Migrations and Schema Modification" guide}[link:files/doc/migration_rdoc.html].
209
213
  class AlterTableGenerator
210
214
  # An array of DDL operations to perform
211
215
  attr_reader :operations
@@ -1,5 +1,25 @@
1
1
  module Sequel
2
2
  class Database
3
+ # ---------------------
4
+ # :section: Methods that modify the database schema
5
+ # These methods execute code on the database that modifies the database's schema.
6
+ # ---------------------
7
+
8
+ AUTOINCREMENT = 'AUTOINCREMENT'.freeze
9
+ CASCADE = 'CASCADE'.freeze
10
+ COMMA_SEPARATOR = ', '.freeze
11
+ NO_ACTION = 'NO ACTION'.freeze
12
+ NOT_NULL = ' NOT NULL'.freeze
13
+ NULL = ' NULL'.freeze
14
+ PRIMARY_KEY = ' PRIMARY KEY'.freeze
15
+ RESTRICT = 'RESTRICT'.freeze
16
+ SET_DEFAULT = 'SET DEFAULT'.freeze
17
+ SET_NULL = 'SET NULL'.freeze
18
+ TEMPORARY = 'TEMPORARY '.freeze
19
+ UNDERSCORE = '_'.freeze
20
+ UNIQUE = ' UNIQUE'.freeze
21
+ UNSIGNED = ' UNSIGNED'.freeze
22
+
3
23
  # Adds a column to the specified table. This method expects a column name,
4
24
  # a datatype and optionally a hash with additional constraints and options:
5
25
  #
@@ -46,11 +66,12 @@ module Sequel
46
66
  # definitions using create_table, and #add_index accepts all the options
47
67
  # available for index definition.
48
68
  #
49
- # See Schema::AlterTableGenerator and the {"Schema Modification" guide}[link:files/doc/schema_rdoc.html].
69
+ # See Schema::AlterTableGenerator and the {"Migrations and Schema Modification" guide}[link:files/doc/migration_rdoc.html].
50
70
  def alter_table(name, generator=nil, &block)
51
- remove_cached_schema(name)
52
71
  generator ||= Schema::AlterTableGenerator.new(self, &block)
53
72
  alter_table_sql_list(name, generator.operations).flatten.each {|sql| execute_ddl(sql)}
73
+ remove_cached_schema(name)
74
+ nil
54
75
  end
55
76
 
56
77
  # Creates a table with the columns given in the provided block:
@@ -66,12 +87,14 @@ module Sequel
66
87
  # * :temp - Create the table as a temporary table.
67
88
  # * :ignore_index_errors - Ignore any errors when creating indexes.
68
89
  #
69
- # See Schema::Generator and the {"Schema Modification" guide}[link:files/doc/schema_rdoc.html].
90
+ # See Schema::Generator and the {"Migrations and Schema Modification" guide}[link:files/doc/migration_rdoc.html].
70
91
  def create_table(name, options={}, &block)
92
+ remove_cached_schema(name)
71
93
  options = {:generator=>options} if options.is_a?(Schema::Generator)
72
94
  generator = options[:generator] || Schema::Generator.new(self, &block)
73
95
  create_table_from_generator(name, generator, options)
74
96
  create_table_indexes_from_generator(name, generator, options)
97
+ nil
75
98
  end
76
99
 
77
100
  # Forcibly creates a table, attempting to drop it unconditionally (and catching any errors), then creating it.
@@ -90,9 +113,10 @@ module Sequel
90
113
  # DB.create_or_replace_view(:cheap_items, "SELECT * FROM items WHERE price < 100")
91
114
  # DB.create_or_replace_view(:ruby_items, DB[:items].filter(:category => 'ruby'))
92
115
  def create_or_replace_view(name, source)
93
- remove_cached_schema(name)
94
116
  source = source.sql if source.is_a?(Dataset)
95
117
  execute_ddl("CREATE OR REPLACE VIEW #{quote_schema_table(name)} AS #{source}")
118
+ remove_cached_schema(name)
119
+ nil
96
120
  end
97
121
 
98
122
  # Creates a view based on a dataset or an SQL string:
@@ -128,9 +152,10 @@ module Sequel
128
152
  # DB.drop_table(:posts, :comments)
129
153
  def drop_table(*names)
130
154
  names.each do |n|
131
- remove_cached_schema(n)
132
155
  execute_ddl(drop_table_sql(n))
156
+ remove_cached_schema(n)
133
157
  end
158
+ nil
134
159
  end
135
160
 
136
161
  # Drops one or more views corresponding to the given names:
@@ -138,9 +163,10 @@ module Sequel
138
163
  # DB.drop_view(:cheap_items)
139
164
  def drop_view(*names)
140
165
  names.each do |n|
141
- remove_cached_schema(n)
142
166
  execute_ddl("DROP VIEW #{quote_schema_table(n)}")
167
+ remove_cached_schema(n)
143
168
  end
169
+ nil
144
170
  end
145
171
 
146
172
  # Renames a table:
@@ -149,8 +175,9 @@ module Sequel
149
175
  # DB.rename_table :items, :old_items
150
176
  # DB.tables #=> [:old_items]
151
177
  def rename_table(name, new_name)
152
- remove_cached_schema(name)
153
178
  execute_ddl(rename_table_sql(name, new_name))
179
+ remove_cached_schema(name)
180
+ nil
154
181
  end
155
182
 
156
183
  # Renames a column in the specified table. This method expects the current
@@ -183,6 +210,108 @@ module Sequel
183
210
 
184
211
  private
185
212
 
213
+ # The SQL to execute to modify the DDL for the given table name. op
214
+ # should be one of the operations returned by the AlterTableGenerator.
215
+ def alter_table_sql(table, op)
216
+ quoted_name = quote_identifier(op[:name]) if op[:name]
217
+ alter_table_op = case op[:op]
218
+ when :add_column
219
+ "ADD COLUMN #{column_definition_sql(op)}"
220
+ when :drop_column
221
+ "DROP COLUMN #{quoted_name}"
222
+ when :rename_column
223
+ "RENAME COLUMN #{quoted_name} TO #{quote_identifier(op[:new_name])}"
224
+ when :set_column_type
225
+ "ALTER COLUMN #{quoted_name} TYPE #{type_literal(op)}"
226
+ when :set_column_default
227
+ "ALTER COLUMN #{quoted_name} SET DEFAULT #{literal(op[:default])}"
228
+ when :set_column_null
229
+ "ALTER COLUMN #{quoted_name} #{op[:null] ? 'DROP' : 'SET'} NOT NULL"
230
+ when :add_index
231
+ return index_definition_sql(table, op)
232
+ when :drop_index
233
+ return drop_index_sql(table, op)
234
+ when :add_constraint
235
+ "ADD #{constraint_definition_sql(op)}"
236
+ when :drop_constraint
237
+ "DROP CONSTRAINT #{quoted_name}"
238
+ else
239
+ raise Error, "Unsupported ALTER TABLE operation"
240
+ end
241
+ "ALTER TABLE #{quote_schema_table(table)} #{alter_table_op}"
242
+ end
243
+
244
+ # Array of SQL DDL modification statements for the given table,
245
+ # corresponding to the DDL changes specified by the operations.
246
+ def alter_table_sql_list(table, operations)
247
+ operations.map{|op| alter_table_sql(table, op)}
248
+ end
249
+
250
+ # The SQL string specify the autoincrement property, generally used by
251
+ # primary keys.
252
+ def auto_increment_sql
253
+ AUTOINCREMENT
254
+ end
255
+
256
+ # SQL DDL fragment containing the column creation SQL for the given column.
257
+ def column_definition_sql(column)
258
+ sql = "#{quote_identifier(column[:name])} #{type_literal(column)}"
259
+ sql << UNIQUE if column[:unique]
260
+ null = column.fetch(:null, column[:allow_null])
261
+ sql << NOT_NULL if null == false
262
+ sql << NULL if null == true
263
+ sql << " DEFAULT #{literal(column[:default])}" if column.include?(:default)
264
+ sql << PRIMARY_KEY if column[:primary_key]
265
+ sql << " #{auto_increment_sql}" if column[:auto_increment]
266
+ sql << column_references_column_constraint_sql(column) if column[:table]
267
+ sql
268
+ end
269
+
270
+ # SQL DDL fragment containing the column creation
271
+ # SQL for all given columns, used inside a CREATE TABLE block.
272
+ def column_list_sql(generator)
273
+ (generator.columns.map{|c| column_definition_sql(c)} + generator.constraints.map{|c| constraint_definition_sql(c)}).join(COMMA_SEPARATOR)
274
+ end
275
+
276
+ # SQL DDL fragment for column foreign key references (column constraints)
277
+ def column_references_column_constraint_sql(column)
278
+ column_references_sql(column)
279
+ end
280
+
281
+ # SQL DDL fragment for column foreign key references
282
+ def column_references_sql(column)
283
+ sql = " REFERENCES #{quote_schema_table(column[:table])}"
284
+ sql << "(#{Array(column[:key]).map{|x| quote_identifier(x)}.join(COMMA_SEPARATOR)})" if column[:key]
285
+ sql << " ON DELETE #{on_delete_clause(column[:on_delete])}" if column[:on_delete]
286
+ sql << " ON UPDATE #{on_delete_clause(column[:on_update])}" if column[:on_update]
287
+ sql << " DEFERRABLE INITIALLY DEFERRED" if column[:deferrable]
288
+ sql
289
+ end
290
+
291
+ # SQL DDL fragment for table foreign key references (table constraints)
292
+ def column_references_table_constraint_sql(constraint)
293
+ "FOREIGN KEY #{literal(constraint[:columns])}#{column_references_sql(constraint)}"
294
+ end
295
+
296
+ # SQL DDL fragment specifying a constraint on a table.
297
+ def constraint_definition_sql(constraint)
298
+ sql = constraint[:name] ? "CONSTRAINT #{quote_identifier(constraint[:name])} " : ""
299
+ case constraint[:type]
300
+ when :check
301
+ check = constraint[:check]
302
+ sql << "CHECK #{filter_expr((check.is_a?(Array) && check.length == 1) ? check.first : check)}"
303
+ when :primary_key
304
+ sql << "PRIMARY KEY #{literal(constraint[:columns])}"
305
+ when :foreign_key
306
+ sql << column_references_table_constraint_sql(constraint)
307
+ when :unique
308
+ sql << "UNIQUE #{literal(constraint[:columns])}"
309
+ else
310
+ raise Error, "Invalid constriant type #{constraint[:type]}, should be :check, :primary_key, :foreign_key, or :unique"
311
+ end
312
+ sql
313
+ end
314
+
186
315
  # Execute the create table statements using the generator.
187
316
  def create_table_from_generator(name, generator, options)
188
317
  execute_ddl(create_table_sql(name, generator, options))
@@ -199,5 +328,220 @@ module Sequel
199
328
  end
200
329
  end
201
330
  end
331
+
332
+ # DDL statement for creating a table with the given name, columns, and options
333
+ def create_table_sql(name, generator, options)
334
+ "CREATE #{temporary_table_sql if options[:temp]}TABLE #{quote_schema_table(name)} (#{column_list_sql(generator)})"
335
+ end
336
+
337
+ # Default index name for the table and columns, may be too long
338
+ # for certain databases.
339
+ def default_index_name(table_name, columns)
340
+ schema, table = schema_and_table(table_name)
341
+ "#{"#{schema}_" if schema and schema != default_schema}#{table}_#{columns.map{|c| [String, Symbol].any?{|cl| c.is_a?(cl)} ? c : literal(c).gsub(/\W/, '_')}.join(UNDERSCORE)}_index"
342
+ end
343
+
344
+ # The SQL to drop an index for the table.
345
+ def drop_index_sql(table, op)
346
+ "DROP INDEX #{quote_identifier(op[:name] || default_index_name(table, op[:columns]))}"
347
+ end
348
+
349
+ # SQL DDL statement to drop the table with the given name.
350
+ def drop_table_sql(name)
351
+ "DROP TABLE #{quote_schema_table(name)}"
352
+ end
353
+
354
+ # Proxy the filter_expr call to the dataset, used for creating constraints.
355
+ def filter_expr(*args, &block)
356
+ schema_utility_dataset.literal(schema_utility_dataset.send(:filter_expr, *args, &block))
357
+ end
358
+
359
+ # SQL DDL statement for creating an index for the table with the given name
360
+ # and index specifications.
361
+ def index_definition_sql(table_name, index)
362
+ index_name = index[:name] || default_index_name(table_name, index[:columns])
363
+ if index[:type]
364
+ raise Error, "Index types are not supported for this database"
365
+ elsif index[:where]
366
+ raise Error, "Partial indexes are not supported for this database"
367
+ else
368
+ "CREATE #{'UNIQUE ' if index[:unique]}INDEX #{quote_identifier(index_name)} ON #{quote_schema_table(table_name)} #{literal(index[:columns])}"
369
+ end
370
+ end
371
+
372
+ # Array of SQL DDL statements, one for each index specification,
373
+ # for the given table.
374
+ def index_sql_list(table_name, indexes)
375
+ indexes.map{|i| index_definition_sql(table_name, i)}
376
+ end
377
+
378
+ # SQL DDL ON DELETE fragment to use, based on the given action.
379
+ # The following actions are recognized:
380
+ #
381
+ # * :cascade - Delete rows referencing this row.
382
+ # * :no_action (default) - Raise an error if other rows reference this
383
+ # row, allow deferring of the integrity check.
384
+ # * :restrict - Raise an error if other rows reference this row,
385
+ # but do not allow deferring the integrity check.
386
+ # * :set_default - Set columns referencing this row to their default value.
387
+ # * :set_null - Set columns referencing this row to NULL.
388
+ def on_delete_clause(action)
389
+ case action
390
+ when :restrict
391
+ RESTRICT
392
+ when :cascade
393
+ CASCADE
394
+ when :set_null
395
+ SET_NULL
396
+ when :set_default
397
+ SET_DEFAULT
398
+ else
399
+ NO_ACTION
400
+ end
401
+ end
402
+
403
+ # Proxy the quote_schema_table method to the dataset
404
+ def quote_schema_table(table)
405
+ schema_utility_dataset.quote_schema_table(table)
406
+ end
407
+
408
+ # Proxy the quote_identifier method to the dataset, used for quoting tables and columns.
409
+ def quote_identifier(v)
410
+ schema_utility_dataset.quote_identifier(v)
411
+ end
412
+
413
+ # SQL DDL statement for renaming a table.
414
+ def rename_table_sql(name, new_name)
415
+ "ALTER TABLE #{quote_schema_table(name)} RENAME TO #{quote_schema_table(new_name)}"
416
+ end
417
+
418
+ # Remove the cached schema_utility_dataset, because the identifier
419
+ # quoting has changed.
420
+ def reset_schema_utility_dataset
421
+ @schema_utility_dataset = nil
422
+ end
423
+
424
+ # Split the schema information from the table
425
+ def schema_and_table(table_name)
426
+ schema_utility_dataset.schema_and_table(table_name)
427
+ end
428
+
429
+ # Return true if the given column schema represents an autoincrementing primary key.
430
+ def schema_autoincrementing_primary_key?(schema)
431
+ !!schema[:primary_key]
432
+ end
433
+
434
+ # The dataset to use for proxying certain schema methods.
435
+ def schema_utility_dataset
436
+ @schema_utility_dataset ||= dataset
437
+ end
438
+
439
+ # SQL DDL fragment for temporary table
440
+ def temporary_table_sql
441
+ self.class.const_get(:TEMPORARY)
442
+ end
443
+
444
+ # SQL fragment specifying the type of a given column.
445
+ def type_literal(column)
446
+ column[:type].is_a?(Class) ? type_literal_generic(column) : type_literal_specific(column)
447
+ end
448
+
449
+ # SQL fragment specifying the full type of a column,
450
+ # consider the type with possible modifiers.
451
+ def type_literal_generic(column)
452
+ meth = "type_literal_generic_#{column[:type].name.to_s.downcase}"
453
+ if respond_to?(meth, true)
454
+ send(meth, column)
455
+ else
456
+ raise Error, "Unsupported ruby class used as database type: #{column[:type]}"
457
+ end
458
+ end
459
+
460
+ # Alias for type_literal_generic_numeric, to make overriding in a subclass easier.
461
+ def type_literal_generic_bigdecimal(column)
462
+ type_literal_generic_numeric(column)
463
+ end
464
+
465
+ # Sequel uses the bigint type by default for Bignums.
466
+ def type_literal_generic_bignum(column)
467
+ :bigint
468
+ end
469
+
470
+ # Sequel uses the date type by default for Dates.
471
+ def type_literal_generic_date(column)
472
+ :date
473
+ end
474
+
475
+ # Sequel uses the timestamp type by default for DateTimes.
476
+ def type_literal_generic_datetime(column)
477
+ :timestamp
478
+ end
479
+
480
+ # Alias for type_literal_generic_trueclass, to make overriding in a subclass easier.
481
+ def type_literal_generic_falseclass(column)
482
+ type_literal_generic_trueclass(column)
483
+ end
484
+
485
+ # Sequel uses the blob type by default for Files.
486
+ def type_literal_generic_file(column)
487
+ :blob
488
+ end
489
+
490
+ # Alias for type_literal_generic_integer, to make overriding in a subclass easier.
491
+ def type_literal_generic_fixnum(column)
492
+ type_literal_generic_integer(column)
493
+ end
494
+
495
+ # Sequel uses the double precision type by default for Floats.
496
+ def type_literal_generic_float(column)
497
+ :"double precision"
498
+ end
499
+
500
+ # Sequel uses the integer type by default for integers
501
+ def type_literal_generic_integer(column)
502
+ :integer
503
+ end
504
+
505
+ # Sequel uses the numeric type by default for Numerics and BigDecimals.
506
+ # If a size is given, it is used, otherwise, it will default to whatever
507
+ # the database default is for an unsized value.
508
+ def type_literal_generic_numeric(column)
509
+ column[:size] ? "numeric(#{Array(column[:size]).join(', ')})" : :numeric
510
+ end
511
+
512
+ # Sequel uses the varchar type by default for Strings. If a
513
+ # size isn't present, Sequel assumes a size of 255. If the
514
+ # :fixed option is used, Sequel uses the char type. If the
515
+ # :text option is used, Sequel uses the :text type.
516
+ def type_literal_generic_string(column)
517
+ if column[:text]
518
+ :text
519
+ elsif column[:fixed]
520
+ "char(#{column[:size]||255})"
521
+ else
522
+ "varchar(#{column[:size]||255})"
523
+ end
524
+ end
525
+
526
+ # Sequel uses the timestamp type by default for Time values.
527
+ # If the :only_time option is used, the time type is used.
528
+ def type_literal_generic_time(column)
529
+ column[:only_time] ? :time : :timestamp
530
+ end
531
+
532
+ # Sequel uses the boolean type by default for TrueClass and FalseClass.
533
+ def type_literal_generic_trueclass(column)
534
+ :boolean
535
+ end
536
+
537
+ # SQL fragment for the given type of a column if the column is not one of the
538
+ # generic types specified with a ruby class.
539
+ def type_literal_specific(column)
540
+ type = column[:type]
541
+ type = "double precision" if type.to_s == 'double'
542
+ column[:size] ||= 255 if type.to_s == 'varchar'
543
+ elements = column[:size] || column[:elements]
544
+ "#{type}#{literal(Array(elements)) if elements}#{UNSIGNED if column[:unsigned]}"
545
+ end
202
546
  end
203
547
  end