sequel 3.11.0 → 3.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +70 -0
- data/Rakefile +1 -1
- data/doc/active_record.rdoc +896 -0
- data/doc/advanced_associations.rdoc +46 -31
- data/doc/association_basics.rdoc +14 -9
- data/doc/dataset_basics.rdoc +3 -3
- data/doc/migration.rdoc +1011 -0
- data/doc/model_hooks.rdoc +198 -0
- data/doc/querying.rdoc +811 -86
- data/doc/release_notes/3.12.0.txt +304 -0
- data/doc/sharding.rdoc +17 -0
- data/doc/sql.rdoc +537 -0
- data/doc/validations.rdoc +501 -0
- data/lib/sequel/adapters/jdbc.rb +19 -27
- data/lib/sequel/adapters/jdbc/postgresql.rb +0 -7
- data/lib/sequel/adapters/mysql.rb +5 -4
- data/lib/sequel/adapters/odbc.rb +3 -2
- data/lib/sequel/adapters/shared/mssql.rb +7 -6
- data/lib/sequel/adapters/shared/mysql.rb +2 -7
- data/lib/sequel/adapters/shared/postgres.rb +2 -8
- data/lib/sequel/adapters/shared/sqlite.rb +2 -5
- data/lib/sequel/adapters/sqlite.rb +4 -4
- data/lib/sequel/core.rb +0 -1
- data/lib/sequel/database.rb +2 -1060
- data/lib/sequel/database/connecting.rb +227 -0
- data/lib/sequel/database/dataset.rb +58 -0
- data/lib/sequel/database/dataset_defaults.rb +127 -0
- data/lib/sequel/database/logging.rb +62 -0
- data/lib/sequel/database/misc.rb +246 -0
- data/lib/sequel/database/query.rb +390 -0
- data/lib/sequel/database/schema_generator.rb +7 -3
- data/lib/sequel/database/schema_methods.rb +351 -7
- data/lib/sequel/dataset/actions.rb +9 -2
- data/lib/sequel/dataset/misc.rb +6 -2
- data/lib/sequel/dataset/mutation.rb +3 -11
- data/lib/sequel/dataset/query.rb +49 -6
- data/lib/sequel/exceptions.rb +3 -0
- data/lib/sequel/extensions/migration.rb +395 -113
- data/lib/sequel/extensions/schema_dumper.rb +21 -13
- data/lib/sequel/model.rb +27 -25
- data/lib/sequel/model/associations.rb +72 -34
- data/lib/sequel/model/base.rb +74 -18
- data/lib/sequel/model/errors.rb +8 -1
- data/lib/sequel/plugins/active_model.rb +8 -0
- data/lib/sequel/plugins/association_pks.rb +87 -0
- data/lib/sequel/plugins/association_proxies.rb +8 -0
- data/lib/sequel/plugins/boolean_readers.rb +12 -6
- data/lib/sequel/plugins/caching.rb +14 -7
- data/lib/sequel/plugins/class_table_inheritance.rb +15 -9
- data/lib/sequel/plugins/composition.rb +2 -1
- data/lib/sequel/plugins/force_encoding.rb +10 -7
- data/lib/sequel/plugins/hook_class_methods.rb +12 -11
- data/lib/sequel/plugins/identity_map.rb +9 -0
- data/lib/sequel/plugins/instance_hooks.rb +23 -13
- data/lib/sequel/plugins/lazy_attributes.rb +4 -1
- data/lib/sequel/plugins/many_through_many.rb +18 -4
- data/lib/sequel/plugins/nested_attributes.rb +1 -0
- data/lib/sequel/plugins/optimistic_locking.rb +1 -1
- data/lib/sequel/plugins/rcte_tree.rb +9 -8
- data/lib/sequel/plugins/schema.rb +8 -0
- data/lib/sequel/plugins/serialization.rb +1 -3
- data/lib/sequel/plugins/sharding.rb +135 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +117 -25
- data/lib/sequel/plugins/skip_create_refresh.rb +35 -0
- data/lib/sequel/plugins/string_stripper.rb +26 -0
- data/lib/sequel/plugins/tactical_eager_loading.rb +8 -0
- data/lib/sequel/plugins/timestamps.rb +15 -2
- data/lib/sequel/plugins/touch.rb +13 -0
- data/lib/sequel/plugins/update_primary_key.rb +48 -0
- data/lib/sequel/plugins/validation_class_methods.rb +8 -0
- data/lib/sequel/plugins/validation_helpers.rb +1 -1
- data/lib/sequel/sql.rb +17 -20
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +5 -5
- data/spec/core/core_sql_spec.rb +17 -1
- data/spec/core/database_spec.rb +17 -5
- data/spec/core/dataset_spec.rb +31 -8
- data/spec/core/schema_generator_spec.rb +8 -1
- data/spec/core/schema_spec.rb +13 -0
- data/spec/extensions/association_pks_spec.rb +85 -0
- data/spec/extensions/hook_class_methods_spec.rb +9 -9
- data/spec/extensions/migration_spec.rb +339 -219
- data/spec/extensions/schema_dumper_spec.rb +28 -17
- data/spec/extensions/sharding_spec.rb +272 -0
- data/spec/extensions/single_table_inheritance_spec.rb +92 -4
- data/spec/extensions/skip_create_refresh_spec.rb +17 -0
- data/spec/extensions/string_stripper_spec.rb +23 -0
- data/spec/extensions/update_primary_key_spec.rb +65 -0
- data/spec/extensions/validation_class_methods_spec.rb +5 -5
- data/spec/files/bad_down_migration/001_create_alt_basic.rb +4 -0
- data/spec/files/bad_down_migration/002_create_alt_advanced.rb +4 -0
- data/spec/files/bad_timestamped_migrations/1273253849_create_sessions.rb +9 -0
- data/spec/files/bad_timestamped_migrations/1273253851_create_nodes.rb +9 -0
- data/spec/files/bad_timestamped_migrations/1273253853_3_create_users.rb +3 -0
- data/spec/files/bad_up_migration/001_create_alt_basic.rb +4 -0
- data/spec/files/bad_up_migration/002_create_alt_advanced.rb +3 -0
- data/spec/files/convert_to_timestamp_migrations/001_create_sessions.rb +9 -0
- data/spec/files/convert_to_timestamp_migrations/002_create_nodes.rb +9 -0
- data/spec/files/convert_to_timestamp_migrations/003_3_create_users.rb +4 -0
- data/spec/files/convert_to_timestamp_migrations/1273253850_create_artists.rb +9 -0
- data/spec/files/convert_to_timestamp_migrations/1273253852_create_albums.rb +9 -0
- data/spec/files/duplicate_integer_migrations/001_create_alt_advanced.rb +4 -0
- data/spec/files/duplicate_integer_migrations/001_create_alt_basic.rb +4 -0
- data/spec/files/duplicate_timestamped_migrations/1273253849_create_sessions.rb +9 -0
- data/spec/files/duplicate_timestamped_migrations/1273253853_create_nodes.rb +9 -0
- data/spec/files/duplicate_timestamped_migrations/1273253853_create_users.rb +4 -0
- data/spec/files/integer_migrations/001_create_sessions.rb +9 -0
- data/spec/files/integer_migrations/002_create_nodes.rb +9 -0
- data/spec/files/integer_migrations/003_3_create_users.rb +4 -0
- data/spec/files/interleaved_timestamped_migrations/1273253849_create_sessions.rb +9 -0
- data/spec/files/interleaved_timestamped_migrations/1273253850_create_artists.rb +9 -0
- data/spec/files/interleaved_timestamped_migrations/1273253851_create_nodes.rb +9 -0
- data/spec/files/interleaved_timestamped_migrations/1273253852_create_albums.rb +9 -0
- data/spec/files/interleaved_timestamped_migrations/1273253853_3_create_users.rb +4 -0
- data/spec/files/missing_integer_migrations/001_create_alt_basic.rb +4 -0
- data/spec/files/missing_integer_migrations/003_create_alt_advanced.rb +4 -0
- data/spec/files/missing_timestamped_migrations/1273253849_create_sessions.rb +9 -0
- data/spec/files/missing_timestamped_migrations/1273253853_3_create_users.rb +4 -0
- data/spec/files/timestamped_migrations/1273253849_create_sessions.rb +9 -0
- data/spec/files/timestamped_migrations/1273253851_create_nodes.rb +9 -0
- data/spec/files/timestamped_migrations/1273253853_3_create_users.rb +4 -0
- data/spec/files/uppercase_timestamped_migrations/1273253849_CREATE_SESSIONS.RB +9 -0
- data/spec/files/uppercase_timestamped_migrations/1273253851_CREATE_NODES.RB +9 -0
- data/spec/files/uppercase_timestamped_migrations/1273253853_3_CREATE_USERS.RB +4 -0
- data/spec/integration/eager_loader_test.rb +20 -20
- data/spec/integration/migrator_test.rb +187 -0
- data/spec/integration/plugin_test.rb +150 -0
- data/spec/integration/schema_test.rb +13 -2
- data/spec/model/associations_spec.rb +41 -14
- data/spec/model/base_spec.rb +69 -0
- data/spec/model/eager_loading_spec.rb +7 -3
- data/spec/model/record_spec.rb +79 -4
- data/spec/model/validations_spec.rb +21 -9
- metadata +66 -5
- data/doc/schema.rdoc +0 -36
- 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/
|
|
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
|
|
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/
|
|
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/
|
|
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/
|
|
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
|