colincasey-sequel 2.10.0 → 2.10.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (137) hide show
  1. data/CHANGELOG +7 -1
  2. data/doc/advanced_associations.rdoc +614 -0
  3. data/doc/cheat_sheet.rdoc +223 -0
  4. data/doc/dataset_filtering.rdoc +158 -0
  5. data/doc/prepared_statements.rdoc +104 -0
  6. data/doc/release_notes/1.0.txt +38 -0
  7. data/doc/release_notes/1.1.txt +143 -0
  8. data/doc/release_notes/1.3.txt +101 -0
  9. data/doc/release_notes/1.4.0.txt +53 -0
  10. data/doc/release_notes/1.5.0.txt +155 -0
  11. data/doc/release_notes/2.0.0.txt +298 -0
  12. data/doc/release_notes/2.1.0.txt +271 -0
  13. data/doc/release_notes/2.10.0.txt +328 -0
  14. data/doc/release_notes/2.2.0.txt +253 -0
  15. data/doc/release_notes/2.3.0.txt +88 -0
  16. data/doc/release_notes/2.4.0.txt +106 -0
  17. data/doc/release_notes/2.5.0.txt +137 -0
  18. data/doc/release_notes/2.6.0.txt +157 -0
  19. data/doc/release_notes/2.7.0.txt +166 -0
  20. data/doc/release_notes/2.8.0.txt +171 -0
  21. data/doc/release_notes/2.9.0.txt +97 -0
  22. data/doc/schema.rdoc +29 -0
  23. data/doc/sharding.rdoc +113 -0
  24. data/lib/sequel.rb +1 -0
  25. data/lib/sequel_core/adapters/ado.rb +89 -0
  26. data/lib/sequel_core/adapters/db2.rb +143 -0
  27. data/lib/sequel_core/adapters/dbi.rb +112 -0
  28. data/lib/sequel_core/adapters/do/mysql.rb +38 -0
  29. data/lib/sequel_core/adapters/do/postgres.rb +92 -0
  30. data/lib/sequel_core/adapters/do/sqlite.rb +31 -0
  31. data/lib/sequel_core/adapters/do.rb +205 -0
  32. data/lib/sequel_core/adapters/firebird.rb +298 -0
  33. data/lib/sequel_core/adapters/informix.rb +85 -0
  34. data/lib/sequel_core/adapters/jdbc/h2.rb +69 -0
  35. data/lib/sequel_core/adapters/jdbc/mysql.rb +66 -0
  36. data/lib/sequel_core/adapters/jdbc/oracle.rb +23 -0
  37. data/lib/sequel_core/adapters/jdbc/postgresql.rb +113 -0
  38. data/lib/sequel_core/adapters/jdbc/sqlite.rb +43 -0
  39. data/lib/sequel_core/adapters/jdbc.rb +491 -0
  40. data/lib/sequel_core/adapters/mysql.rb +369 -0
  41. data/lib/sequel_core/adapters/odbc.rb +174 -0
  42. data/lib/sequel_core/adapters/openbase.rb +68 -0
  43. data/lib/sequel_core/adapters/oracle.rb +107 -0
  44. data/lib/sequel_core/adapters/postgres.rb +456 -0
  45. data/lib/sequel_core/adapters/shared/ms_access.rb +110 -0
  46. data/lib/sequel_core/adapters/shared/mssql.rb +102 -0
  47. data/lib/sequel_core/adapters/shared/mysql.rb +325 -0
  48. data/lib/sequel_core/adapters/shared/oracle.rb +61 -0
  49. data/lib/sequel_core/adapters/shared/postgres.rb +715 -0
  50. data/lib/sequel_core/adapters/shared/progress.rb +31 -0
  51. data/lib/sequel_core/adapters/shared/sqlite.rb +265 -0
  52. data/lib/sequel_core/adapters/sqlite.rb +248 -0
  53. data/lib/sequel_core/connection_pool.rb +258 -0
  54. data/lib/sequel_core/core_ext.rb +217 -0
  55. data/lib/sequel_core/core_sql.rb +202 -0
  56. data/lib/sequel_core/database/schema.rb +164 -0
  57. data/lib/sequel_core/database.rb +691 -0
  58. data/lib/sequel_core/dataset/callback.rb +13 -0
  59. data/lib/sequel_core/dataset/convenience.rb +237 -0
  60. data/lib/sequel_core/dataset/pagination.rb +96 -0
  61. data/lib/sequel_core/dataset/prepared_statements.rb +220 -0
  62. data/lib/sequel_core/dataset/query.rb +41 -0
  63. data/lib/sequel_core/dataset/schema.rb +15 -0
  64. data/lib/sequel_core/dataset/sql.rb +1010 -0
  65. data/lib/sequel_core/dataset/stored_procedures.rb +75 -0
  66. data/lib/sequel_core/dataset/unsupported.rb +43 -0
  67. data/lib/sequel_core/dataset.rb +511 -0
  68. data/lib/sequel_core/deprecated.rb +26 -0
  69. data/lib/sequel_core/exceptions.rb +44 -0
  70. data/lib/sequel_core/migration.rb +212 -0
  71. data/lib/sequel_core/object_graph.rb +230 -0
  72. data/lib/sequel_core/pretty_table.rb +71 -0
  73. data/lib/sequel_core/schema/generator.rb +320 -0
  74. data/lib/sequel_core/schema/sql.rb +325 -0
  75. data/lib/sequel_core/schema.rb +2 -0
  76. data/lib/sequel_core/sql.rb +887 -0
  77. data/lib/sequel_core/version.rb +11 -0
  78. data/lib/sequel_core.rb +172 -0
  79. data/lib/sequel_model/association_reflection.rb +267 -0
  80. data/lib/sequel_model/associations.rb +499 -0
  81. data/lib/sequel_model/base.rb +523 -0
  82. data/lib/sequel_model/caching.rb +82 -0
  83. data/lib/sequel_model/dataset_methods.rb +26 -0
  84. data/lib/sequel_model/eager_loading.rb +370 -0
  85. data/lib/sequel_model/exceptions.rb +7 -0
  86. data/lib/sequel_model/hooks.rb +101 -0
  87. data/lib/sequel_model/inflector.rb +281 -0
  88. data/lib/sequel_model/plugins.rb +62 -0
  89. data/lib/sequel_model/record.rb +568 -0
  90. data/lib/sequel_model/schema.rb +49 -0
  91. data/lib/sequel_model/validations.rb +429 -0
  92. data/lib/sequel_model.rb +91 -0
  93. data/spec/adapters/ado_spec.rb +46 -0
  94. data/spec/adapters/firebird_spec.rb +376 -0
  95. data/spec/adapters/informix_spec.rb +96 -0
  96. data/spec/adapters/mysql_spec.rb +881 -0
  97. data/spec/adapters/oracle_spec.rb +244 -0
  98. data/spec/adapters/postgres_spec.rb +687 -0
  99. data/spec/adapters/spec_helper.rb +10 -0
  100. data/spec/adapters/sqlite_spec.rb +555 -0
  101. data/spec/integration/dataset_test.rb +134 -0
  102. data/spec/integration/eager_loader_test.rb +696 -0
  103. data/spec/integration/prepared_statement_test.rb +130 -0
  104. data/spec/integration/schema_test.rb +180 -0
  105. data/spec/integration/spec_helper.rb +58 -0
  106. data/spec/integration/type_test.rb +96 -0
  107. data/spec/rcov.opts +6 -0
  108. data/spec/sequel_core/connection_pool_spec.rb +526 -0
  109. data/spec/sequel_core/core_ext_spec.rb +156 -0
  110. data/spec/sequel_core/core_sql_spec.rb +522 -0
  111. data/spec/sequel_core/database_spec.rb +1188 -0
  112. data/spec/sequel_core/dataset_spec.rb +3481 -0
  113. data/spec/sequel_core/expression_filters_spec.rb +363 -0
  114. data/spec/sequel_core/migration_spec.rb +261 -0
  115. data/spec/sequel_core/object_graph_spec.rb +272 -0
  116. data/spec/sequel_core/pretty_table_spec.rb +58 -0
  117. data/spec/sequel_core/schema_generator_spec.rb +167 -0
  118. data/spec/sequel_core/schema_spec.rb +780 -0
  119. data/spec/sequel_core/spec_helper.rb +55 -0
  120. data/spec/sequel_core/version_spec.rb +7 -0
  121. data/spec/sequel_model/association_reflection_spec.rb +93 -0
  122. data/spec/sequel_model/associations_spec.rb +1767 -0
  123. data/spec/sequel_model/base_spec.rb +419 -0
  124. data/spec/sequel_model/caching_spec.rb +215 -0
  125. data/spec/sequel_model/dataset_methods_spec.rb +78 -0
  126. data/spec/sequel_model/eager_loading_spec.rb +1165 -0
  127. data/spec/sequel_model/hooks_spec.rb +485 -0
  128. data/spec/sequel_model/inflector_spec.rb +119 -0
  129. data/spec/sequel_model/model_spec.rb +588 -0
  130. data/spec/sequel_model/plugins_spec.rb +80 -0
  131. data/spec/sequel_model/record_spec.rb +1184 -0
  132. data/spec/sequel_model/schema_spec.rb +90 -0
  133. data/spec/sequel_model/spec_helper.rb +78 -0
  134. data/spec/sequel_model/validations_spec.rb +1067 -0
  135. data/spec/spec.opts +0 -0
  136. data/spec/spec_config.rb.example +10 -0
  137. metadata +177 -3
@@ -0,0 +1,320 @@
1
+ module Sequel
2
+ # The Schema module holds the schema generators and the SQL code relating
3
+ # to SQL DDL (Data Definition Language).
4
+ module Schema
5
+ # Schema::Generator is an internal class that the user is not expected
6
+ # to instantiate directly. Instances are created by Database#create_table.
7
+ # It is used to specify table creation parameters. It takes a Database
8
+ # object and a block of column/index/constraint specifications, and
9
+ # gives the Database a table description, which the database uses to
10
+ # create a table.
11
+ #
12
+ # Schema::Generator has some methods but also includes method_missing,
13
+ # allowing users to specify column type as a method instead of using
14
+ # the column method, which makes for a nicer DSL.
15
+ class Generator
16
+ # Classes specifying generic types that Sequel will convert to database-specific types.
17
+ GENERIC_TYPES=[String, Integer, Fixnum, Bignum, Float, Numeric, BigDecimal,
18
+ Date, DateTime, Time, File, TrueClass, FalseClass]
19
+
20
+ # Set the database in which to create the table, and evaluate the block
21
+ # in the context of this object.
22
+ def initialize(db, &block)
23
+ @db = db
24
+ @columns = []
25
+ @indexes = []
26
+ @primary_key = nil
27
+ instance_eval(&block) if block
28
+ end
29
+
30
+ # Add a method for each of the given types that creates a column
31
+ # with that type as a constant. Types given should either already
32
+ # be constants/classes or a capitalized string/symbol with the same name
33
+ # as a constant/class.
34
+ def self.add_type_method(*types)
35
+ types.each do |type|
36
+ class_eval "def #{type}(name, opts={}); column(name, #{type}, opts); end"
37
+ end
38
+ end
39
+
40
+ # Add a unnamed constraint to the DDL, specified by the given block
41
+ # or args.
42
+ def check(*args, &block)
43
+ constraint(nil, *args, &block)
44
+ end
45
+
46
+ # Add a column with the given name, type, and opts to the DDL.
47
+ #
48
+ # You can also create columns via method missing, so the following are
49
+ # equivalent:
50
+ #
51
+ # column :number, :integer
52
+ # integer :number
53
+ #
54
+ # The following options are supported:
55
+ #
56
+ # * :default - The default value for the column.
57
+ # * :index - Create an index on this column.
58
+ # * :key - For foreign key columns, the column in the associated table
59
+ # that this column references. Unnecessary if this column
60
+ # references the primary key of the associated table.
61
+ # * :null - Mark the column as allowing NULL values (if true),
62
+ # or not allowing NULL values (if false). If unspecified, will default
63
+ # to whatever the database default is.
64
+ # * :on_delete - Specify the behavior of this column when being deleted.
65
+ # See Schema::SQL#on_delete_clause for options.
66
+ # * :on_update - Specify the behavior of this column when being updated.
67
+ # See Schema::SQL#on_delete_clause for options.
68
+ # * :size - The size of the column, generally used with string
69
+ # columns to specify the maximum number of characters the column will hold.
70
+ # * :unique - Mark the column is unique, generally has the same effect as
71
+ # creating a unique index on the column.
72
+ # * :unsigned - Make the column type unsigned, only useful for integer
73
+ # columns.
74
+ def column(name, type, opts = {})
75
+ @columns << {:name => name, :type => type}.merge(opts)
76
+ index(name) if opts[:index]
77
+ end
78
+
79
+ # Adds a named constraint (or unnamed if name is nil) to the DDL,
80
+ # with the given block or args.
81
+ def constraint(name, *args, &block)
82
+ @columns << {:name => name, :type => :check, :check => block || args,
83
+ :constraint_type => :check}
84
+ end
85
+
86
+ # Return the DDL created by the generator as a array of two elements,
87
+ # the first being the columns and the second being the indexes.
88
+ def create_info
89
+ @columns.unshift(@primary_key) if @primary_key && !has_column?(primary_key_name)
90
+ [@columns, @indexes]
91
+ end
92
+
93
+ # Add a foreign key in the table that references another table to the DDL. See column
94
+ # for available options.
95
+ def foreign_key(name, table=nil, opts = {})
96
+ opts = case table
97
+ when Hash
98
+ table.merge(opts)
99
+ when Symbol
100
+ opts.merge(:table=>table)
101
+ when NilClass
102
+ opts
103
+ else
104
+ raise(Error, "The second argument to foreign_key should be a Hash, Symbol, or nil")
105
+ end
106
+ return composite_foreign_key(name, opts) if name.is_a?(Array)
107
+ column(name, Integer, opts)
108
+ end
109
+
110
+ # Add a full text index on the given columns to the DDL.
111
+ def full_text_index(columns, opts = {})
112
+ index(columns, opts.merge(:type => :full_text))
113
+ end
114
+
115
+ # True if the DDL includes the creation of a column with the given name.
116
+ def has_column?(name)
117
+ @columns.any?{|c| c[:name] == name}
118
+ end
119
+
120
+ # Add an index on the given column(s) with the given options to the DDL.
121
+ # The available options are:
122
+ #
123
+ # * :type - The type of index to use (only supported by some databases)
124
+ # * :unique - Make the index unique, so duplicate values are not allowed.
125
+ # * :where - Create a partial index (only supported by some databases)
126
+ def index(columns, opts = {})
127
+ @indexes << {:columns => Array(columns)}.merge(opts)
128
+ end
129
+
130
+ # Add a column with the given type, name, and opts to the DDL. See column for available
131
+ # options.
132
+ def method_missing(type, name = nil, opts = {})
133
+ name ? column(name, type, opts) : super
134
+ end
135
+
136
+ # Add primary key information to the DDL. Takes between one and three
137
+ # arguments. The last one is an options hash as for Generator#column.
138
+ # The first one distinguishes two modes: an array of existing column
139
+ # names adds a composite primary key constraint. A single symbol adds a
140
+ # new column of that name and makes it the primary key. In that case the
141
+ # optional middle argument denotes the type.
142
+ #
143
+ # Examples:
144
+ # primary_key(:id)
145
+ # primary_key(:zip_code, :null => false)
146
+ # primary_key([:street_number, :house_number])
147
+ # primary_key(:id, :string, :auto_increment => false)
148
+ def primary_key(name, *args)
149
+ return composite_primary_key(name, *args) if name.is_a?(Array)
150
+ @primary_key = @db.serial_primary_key_options.merge({:name => name})
151
+
152
+ if opts = args.pop
153
+ opts = {:type => opts} unless opts.is_a?(Hash)
154
+ if type = args.pop
155
+ opts.merge!(:type => type)
156
+ end
157
+ @primary_key.merge!(opts)
158
+ end
159
+ @primary_key
160
+ end
161
+
162
+ # The name of the primary key for this table, if it has a primary key.
163
+ def primary_key_name
164
+ @primary_key[:name] if @primary_key
165
+ end
166
+
167
+ # Add a spatial index on the given columns to the DDL.
168
+ def spatial_index(columns, opts = {})
169
+ index(columns, opts.merge(:type => :spatial))
170
+ end
171
+
172
+ # Add a unique constraint on the given columns to the DDL.
173
+ def unique(columns, opts = {})
174
+ @columns << {:type => :check, :constraint_type => :unique,
175
+ :name => nil, :columns => Array(columns)}.merge(opts)
176
+ end
177
+
178
+ private
179
+
180
+ def composite_primary_key(columns, *args)
181
+ opts = args.pop || {}
182
+ @columns << {:type => :check, :constraint_type => :primary_key,
183
+ :name => nil, :columns => columns}.merge(opts)
184
+ end
185
+
186
+ def composite_foreign_key(columns, opts)
187
+ @columns << {:type => :check, :constraint_type => :foreign_key,
188
+ :name => nil, :columns => columns }.merge(opts)
189
+ end
190
+
191
+ add_type_method(*GENERIC_TYPES)
192
+ end
193
+
194
+ # Schema::AlterTableGenerator is an internal class that the user is not expected
195
+ # to instantiate directly. Instances are created by Database#alter_table.
196
+ # It is used to specify table alteration parameters. It takes a Database
197
+ # object and a block of operations to perform on the table, and
198
+ # gives the Database a table an array of operations, which the database uses to
199
+ # alter a table's description.
200
+ class AlterTableGenerator
201
+ # An array of DDL operations to perform
202
+ attr_reader :operations
203
+
204
+ # Set the Database object to which to apply the DDL, and evaluate the
205
+ # block in the context of this object.
206
+ def initialize(db, &block)
207
+ @db = db
208
+ @operations = []
209
+ instance_eval(&block) if block
210
+ end
211
+
212
+ # Add a column with the given name, type, and opts to the DDL for the table.
213
+ # See Generator#column for the available options.
214
+ def add_column(name, type, opts = {})
215
+ @operations << {:op => :add_column, :name => name, :type => type}.merge(opts)
216
+ end
217
+
218
+ # Add a constraint with the given name and args to the DDL for the table.
219
+ # See Generator#constraint.
220
+ def add_constraint(name, *args, &block)
221
+ @operations << {:op => :add_constraint, :name => name, :type => :check, \
222
+ :constraint_type => :check, :check => block || args}
223
+ end
224
+
225
+ def add_unique_constraint(columns, opts = {})
226
+ @operations << {:op => :add_constraint, :type => :check,
227
+ :constraint_type => :unique, :columns => Array(columns)}.merge(opts)
228
+ end
229
+
230
+ # Add a foreign key with the given name and referencing the given table
231
+ # to the DDL for the table. See Generator#column for the available options.
232
+ #
233
+ # You can also pass an array of column names for creating composite foreign
234
+ # keys. In this case, it will assume the columns exists and will only add
235
+ # the constraint.
236
+ #
237
+ # NOTE: If you need to add a foreign key constraint to an existing column
238
+ # use the composite key syntax even if it is only one column.
239
+ def add_foreign_key(name, table, opts = {})
240
+ return add_composite_foreign_key(name, table, opts) if name.is_a?(Array)
241
+ add_column(name, Integer, {:table=>table}.merge(opts))
242
+ end
243
+
244
+ # Add a full text index on the given columns to the DDL for the table.
245
+ # See Generator#index for available options.
246
+ def add_full_text_index(columns, opts = {})
247
+ add_index(columns, {:type=>:full_text}.merge(opts))
248
+ end
249
+
250
+ # Add an index on the given columns to the DDL for the table. See
251
+ # Generator#index for available options.
252
+ def add_index(columns, opts = {})
253
+ @operations << {:op => :add_index, :columns => Array(columns)}.merge(opts)
254
+ end
255
+
256
+ # Add a primary key to the DDL for the table. See Generator#column
257
+ # for the available options.
258
+ def add_primary_key(name, opts = {})
259
+ return add_composite_primary_key(name, opts) if name.is_a?(Array)
260
+ opts = @db.serial_primary_key_options.merge(opts)
261
+ add_column(name, opts.delete(:type), opts)
262
+ end
263
+
264
+ # Add a spatial index on the given columns to the DDL for the table.
265
+ # See Generator#index for available options.
266
+ def add_spatial_index(columns, opts = {})
267
+ add_index(columns, {:type=>:spatial}.merge(opts))
268
+ end
269
+
270
+ # Remove a column from the DDL for the table.
271
+ def drop_column(name)
272
+ @operations << {:op => :drop_column, :name => name}
273
+ end
274
+
275
+ # Remove a constraint from the DDL for the table.
276
+ def drop_constraint(name)
277
+ @operations << {:op => :drop_constraint, :name => name}
278
+ end
279
+
280
+ # Remove an index from the DDL for the table.
281
+ def drop_index(columns)
282
+ @operations << {:op => :drop_index, :columns => Array(columns)}
283
+ end
284
+
285
+ # Modify a column's name in the DDL for the table.
286
+ def rename_column(name, new_name, opts = {})
287
+ @operations << {:op => :rename_column, :name => name, :new_name => new_name}.merge(opts)
288
+ end
289
+
290
+ # Modify a column's default value in the DDL for the table.
291
+ def set_column_default(name, default)
292
+ @operations << {:op => :set_column_default, :name => name, :default => default}
293
+ end
294
+
295
+ # Modify a column's type in the DDL for the table.
296
+ def set_column_type(name, type, opts={})
297
+ @operations << {:op => :set_column_type, :name => name, :type => type}.merge(opts)
298
+ end
299
+
300
+ # Modify a column's NOT NULL constraint.
301
+ def set_column_allow_null(name, allow_null)
302
+ @operations << {:op => :set_column_null, :name => name, :null => allow_null}
303
+ end
304
+
305
+ private
306
+
307
+ def add_composite_primary_key(columns, opts)
308
+ @operations << {:op => :add_constraint, :type => :check,
309
+ :constraint_type => :primary_key, :columns => columns}.merge(opts)
310
+ end
311
+
312
+ def add_composite_foreign_key(columns, table, opts)
313
+ @operations << {:op => :add_constraint, :type => :check,
314
+ :constraint_type => :foreign_key, :columns => columns,
315
+ :table => table}.merge(opts)
316
+ end
317
+ end
318
+ end
319
+ end
320
+
@@ -0,0 +1,325 @@
1
+ module Sequel
2
+ module Schema
3
+ module SQL
4
+ AUTOINCREMENT = 'AUTOINCREMENT'.freeze
5
+ CASCADE = 'CASCADE'.freeze
6
+ COMMA_SEPARATOR = ', '.freeze
7
+ NO_ACTION = 'NO ACTION'.freeze
8
+ NOT_NULL = ' NOT NULL'.freeze
9
+ NULL = ' NULL'.freeze
10
+ PRIMARY_KEY = ' PRIMARY KEY'.freeze
11
+ RESTRICT = 'RESTRICT'.freeze
12
+ SET_DEFAULT = 'SET DEFAULT'.freeze
13
+ SET_NULL = 'SET NULL'.freeze
14
+ TYPES = Hash.new {|h, k| k}
15
+ TYPES.merge!(:double=>'double precision', String=>'varchar(255)',
16
+ Integer=>'integer', Fixnum=>'integer', Bignum=>'bigint',
17
+ Float=>'double precision', BigDecimal=>'numeric', Numeric=>'numeric',
18
+ Date=>'date', DateTime=>'timestamp', Time=>'timestamp', File=>'blob',
19
+ TrueClass=>'boolean', FalseClass=>'boolean')
20
+ UNDERSCORE = '_'.freeze
21
+ UNIQUE = ' UNIQUE'.freeze
22
+ UNSIGNED = ' UNSIGNED'.freeze
23
+
24
+ # The SQL to execute to modify the DDL for the given table name. op
25
+ # should be one of the operations returned by the AlterTableGenerator.
26
+ def alter_table_sql(table, op)
27
+ quoted_name = quote_identifier(op[:name]) if op[:name]
28
+ alter_table_op = case op[:op]
29
+ when :add_column
30
+ "ADD COLUMN #{column_definition_sql(op)}"
31
+ when :drop_column
32
+ "DROP COLUMN #{quoted_name}"
33
+ when :rename_column
34
+ "RENAME COLUMN #{quoted_name} TO #{quote_identifier(op[:new_name])}"
35
+ when :set_column_type
36
+ "ALTER COLUMN #{quoted_name} TYPE #{type_literal(op)}"
37
+ when :set_column_default
38
+ "ALTER COLUMN #{quoted_name} SET DEFAULT #{literal(op[:default])}"
39
+ when :set_column_null
40
+ "ALTER COLUMN #{quoted_name} #{op[:null] ? 'DROP' : 'SET'} NOT NULL"
41
+ when :add_index
42
+ return index_definition_sql(table, op)
43
+ when :drop_index
44
+ return drop_index_sql(table, op)
45
+ when :add_constraint
46
+ "ADD #{constraint_definition_sql(op)}"
47
+ when :drop_constraint
48
+ "DROP CONSTRAINT #{quoted_name}"
49
+ else
50
+ raise Error, "Unsupported ALTER TABLE operation"
51
+ end
52
+ "ALTER TABLE #{quote_schema_table(table)} #{alter_table_op}"
53
+ end
54
+
55
+ # Array of SQL DDL modification statements for the given table,
56
+ # corresponding to the DDL changes specified by the operations.
57
+ def alter_table_sql_list(table, operations)
58
+ operations.map{|op| alter_table_sql(table, op)}
59
+ end
60
+
61
+ # The SQL string specify the autoincrement property, generally used by
62
+ # primary keys.
63
+ def auto_increment_sql
64
+ AUTOINCREMENT
65
+ end
66
+
67
+ # SQL DDL fragment containing the column creation SQL for the given column.
68
+ def column_definition_sql(column)
69
+ return constraint_definition_sql(column) if column[:type] == :check
70
+ sql = "#{quote_identifier(column[:name])} #{type_literal(column)}"
71
+ sql << UNIQUE if column[:unique]
72
+ sql << NOT_NULL if column[:null] == false
73
+ sql << NULL if column[:null] == true
74
+ sql << " DEFAULT #{literal(column[:default])}" if column.include?(:default)
75
+ sql << PRIMARY_KEY if column[:primary_key]
76
+ sql << " #{auto_increment_sql}" if column[:auto_increment]
77
+ sql << column_references_sql(column) if column[:table]
78
+ sql
79
+ end
80
+
81
+ # SQL DDL fragment containing the column creation
82
+ # SQL for all given columns, used instead a CREATE TABLE block.
83
+ def column_list_sql(columns)
84
+ columns.map{|c| column_definition_sql(c)}.join(COMMA_SEPARATOR)
85
+ end
86
+
87
+ # SQL DDL fragment for column foreign key references
88
+ def column_references_sql(column)
89
+ sql = " REFERENCES #{quote_schema_table(column[:table])}"
90
+ sql << "(#{Array(column[:key]).map{|x| quote_identifier(x)}.join(COMMA_SEPARATOR)})" if column[:key]
91
+ sql << " ON DELETE #{on_delete_clause(column[:on_delete])}" if column[:on_delete]
92
+ sql << " ON UPDATE #{on_delete_clause(column[:on_update])}" if column[:on_update]
93
+ sql
94
+ end
95
+
96
+ # SQL DDL fragment specifying a constraint on a table.
97
+ def constraint_definition_sql(constraint)
98
+ sql = constraint[:name] ? "CONSTRAINT #{quote_identifier(constraint[:name])} " : ""
99
+ case constraint[:constraint_type]
100
+ when :primary_key
101
+ sql << "PRIMARY KEY #{literal(constraint[:columns])}"
102
+ when :foreign_key
103
+ sql << "FOREIGN KEY #{literal(constraint[:columns])}"
104
+ sql << column_references_sql(constraint)
105
+ when :unique
106
+ sql << "UNIQUE #{literal(constraint[:columns])}"
107
+ else
108
+ check = constraint[:check]
109
+ sql << "CHECK #{filter_expr((check.is_a?(Array) && check.length == 1) ? check.first : check)}"
110
+ end
111
+ sql
112
+ end
113
+
114
+ # Array of SQL DDL statements, the first for creating a table with the given
115
+ # name and column specifications, and the others for specifying indexes on
116
+ # the table.
117
+ def create_table_sql_list(name, columns, indexes = nil, options = {})
118
+ sql = ["CREATE TABLE #{quote_schema_table(name)} (#{column_list_sql(columns)})"]
119
+ sql.concat(index_list_sql_list(name, indexes)) if indexes && !indexes.empty?
120
+ sql
121
+ end
122
+
123
+ # Default index name for the table and columns, may be too long
124
+ # for certain databases.
125
+ def default_index_name(table_name, columns)
126
+ schema, table = schema_and_table(table_name)
127
+ "#{"#{schema}_" if schema and schema != default_schema}#{table}_#{columns.map{|c| c.is_one_of?(String, Symbol) ? c : literal(c).gsub(/\W/, '_')}.join(UNDERSCORE)}_index"
128
+ end
129
+
130
+ # The SQL to drop an index for the table.
131
+ def drop_index_sql(table, op)
132
+ "DROP INDEX #{quote_identifier(op[:name] || default_index_name(table, op[:columns]))}"
133
+ end
134
+
135
+ # SQL DDL statement to drop the table with the given name.
136
+ def drop_table_sql(name)
137
+ "DROP TABLE #{quote_schema_table(name)}"
138
+ end
139
+
140
+ # Proxy the filter_expr call to the dataset, used for creating constraints.
141
+ def filter_expr(*args, &block)
142
+ schema_utility_dataset.literal(schema_utility_dataset.send(:filter_expr, *args, &block))
143
+ end
144
+
145
+ # SQL DDL statement for creating an index for the table with the given name
146
+ # and index specifications.
147
+ def index_definition_sql(table_name, index)
148
+ index_name = index[:name] || default_index_name(table_name, index[:columns])
149
+ if index[:type]
150
+ raise Error, "Index types are not supported for this database"
151
+ elsif index[:where]
152
+ raise Error, "Partial indexes are not supported for this database"
153
+ else
154
+ "CREATE #{'UNIQUE ' if index[:unique]}INDEX #{quote_identifier(index_name)} ON #{quote_identifier(table_name)} #{literal(index[:columns])}"
155
+ end
156
+ end
157
+
158
+ # Array of SQL DDL statements, one for each index specification,
159
+ # for the given table.
160
+ def index_list_sql_list(table_name, indexes)
161
+ indexes.map{|i| index_definition_sql(table_name, i)}
162
+ end
163
+
164
+ # Proxy the literal call to the dataset, used for default values.
165
+ def literal(v)
166
+ schema_utility_dataset.literal(v)
167
+ end
168
+
169
+ # SQL DDL ON DELETE fragment to use, based on the given action.
170
+ # The following actions are recognized:
171
+ #
172
+ # * :cascade - Delete rows referencing this row.
173
+ # * :no_action (default) - Raise an error if other rows reference this
174
+ # row, allow deferring of the integrity check.
175
+ # * :restrict - Raise an error if other rows reference this row,
176
+ # but do not allow deferring the integrity check.
177
+ # * :set_default - Set columns referencing this row to their default value.
178
+ # * :set_null - Set columns referencing this row to NULL.
179
+ def on_delete_clause(action)
180
+ case action
181
+ when :restrict
182
+ RESTRICT
183
+ when :cascade
184
+ CASCADE
185
+ when :set_null
186
+ SET_NULL
187
+ when :set_default
188
+ SET_DEFAULT
189
+ else
190
+ NO_ACTION
191
+ end
192
+ end
193
+
194
+ # Proxy the quote_schema_table method to the dataset
195
+ def quote_schema_table(table)
196
+ schema_utility_dataset.quote_schema_table(table)
197
+ end
198
+
199
+ # Proxy the quote_identifier method to the dataset, used for quoting tables and columns.
200
+ def quote_identifier(v)
201
+ schema_utility_dataset.quote_identifier(v)
202
+ end
203
+
204
+ # SQL DDL statement for renaming a table.
205
+ def rename_table_sql(name, new_name)
206
+ "ALTER TABLE #{quote_schema_table(name)} RENAME TO #{quote_schema_table(new_name)}"
207
+ end
208
+
209
+ # Parse the schema from the database.
210
+ # If the table_name is not given, returns the schema for all tables as a hash.
211
+ # If the table_name is given, returns the schema for a single table as an
212
+ # array with all members being arrays of length 2. Available options are:
213
+ #
214
+ # * :reload - Get fresh information from the database, instead of using
215
+ # cached information. If table_name is blank, :reload should be used
216
+ # unless you are sure that schema has not been called before with a
217
+ # table_name, otherwise you may only getting the schemas for tables
218
+ # that have been requested explicitly.
219
+ # * :schema - An explicit schema to use. It may also be implicitly provided
220
+ # via the table name.
221
+ def schema(table = nil, opts={})
222
+ raise(Error, 'schema parsing is not implemented on this database') unless respond_to?(:schema_parse_table, true)
223
+
224
+ if table
225
+ sch, table_name = schema_and_table(table)
226
+ quoted_name = quote_schema_table(table)
227
+ end
228
+ opts = opts.merge(:schema=>sch) if sch && !opts.include?(:schema)
229
+ if opts[:reload] && @schemas
230
+ if table_name
231
+ @schemas.delete(quoted_name)
232
+ else
233
+ @schemas = nil
234
+ end
235
+ end
236
+
237
+ if @schemas
238
+ if table_name
239
+ return @schemas[quoted_name] if @schemas[quoted_name]
240
+ else
241
+ return @schemas
242
+ end
243
+ end
244
+
245
+ raise(Error, '#tables does not exist, you must provide a specific table to #schema') if table.nil? && !respond_to?(:tables, true)
246
+
247
+ @schemas ||= Hash.new do |h,k|
248
+ quote_name = quote_schema_table(k)
249
+ h[quote_name] if h.include?(quote_name)
250
+ end
251
+
252
+ if table_name
253
+ cols = schema_parse_table(table_name, opts)
254
+ raise(Error, 'schema parsing returned no columns, table probably doesn\'t exist') if cols.blank?
255
+ @schemas[quoted_name] = cols
256
+ else
257
+ tables.each{|t| @schemas[quote_schema_table(t)] = schema_parse_table(t.to_s, opts)}
258
+ @schemas
259
+ end
260
+ end
261
+
262
+ # The dataset to use for proxying certain schema methods.
263
+ def schema_utility_dataset
264
+ @schema_utility_dataset ||= dataset
265
+ end
266
+
267
+ private
268
+
269
+ # Remove the cached schema for the given schema name
270
+ def remove_cached_schema(table)
271
+ @schemas.delete(quote_schema_table(table)) if @schemas
272
+ end
273
+
274
+ # Remove the cached schema_utility_dataset, because the identifier
275
+ # quoting has changed.
276
+ def reset_schema_utility_dataset
277
+ @schema_utility_dataset = nil
278
+ end
279
+
280
+ # Match the database's column type to a ruby type via a
281
+ # regular expression. The following ruby types are supported:
282
+ # integer, string, date, datetime, boolean, and float.
283
+ def schema_column_type(db_type)
284
+ case db_type
285
+ when /\Atinyint/io
286
+ Sequel.convert_tinyint_to_bool ? :boolean : :integer
287
+ when /\Ainterval\z/io
288
+ :interval
289
+ when /\A(character( varying)?|varchar|text)/io
290
+ :string
291
+ when /\A(int(eger)?|bigint|smallint)/io
292
+ :integer
293
+ when /\Adate\z/io
294
+ :date
295
+ when /\A(datetime|timestamp( with(out)? time zone)?)\z/io
296
+ :datetime
297
+ when /\Atime( with(out)? time zone)?\z/io
298
+ :time
299
+ when /\Aboolean\z/io
300
+ :boolean
301
+ when /\A(real|float|double( precision)?)\z/io
302
+ :float
303
+ when /\A(numeric(\(\d+,\d+\))?|decimal|money)\z/io
304
+ :decimal
305
+ when /\Abytea\z/io
306
+ :blob
307
+ end
308
+ end
309
+
310
+ # SQL fragment specifying the type of a given column.
311
+ def type_literal(column)
312
+ type = type_literal_base(column)
313
+ column[:size] ||= 255 if type.to_s == 'varchar'
314
+ elements = column[:size] || column[:elements]
315
+ "#{type}#{literal(Array(elements)) if elements}#{UNSIGNED if column[:unsigned]}"
316
+ end
317
+
318
+ # SQL fragment specifying the base type of a given column,
319
+ # without the size or elements.
320
+ def type_literal_base(column)
321
+ TYPES[column[:type]]
322
+ end
323
+ end
324
+ end
325
+ end
@@ -0,0 +1,2 @@
1
+ require 'sequel_core/schema/generator'
2
+ require 'sequel_core/schema/sql'