sequel 3.21.0 → 3.28.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 +294 -0
- data/README.rdoc +20 -6
- data/Rakefile +20 -15
- data/doc/association_basics.rdoc +210 -43
- data/doc/dataset_basics.rdoc +4 -4
- data/doc/mass_assignment.rdoc +54 -0
- data/doc/migration.rdoc +15 -538
- data/doc/model_hooks.rdoc +64 -27
- data/doc/opening_databases.rdoc +37 -10
- data/doc/prepared_statements.rdoc +16 -10
- data/doc/reflection.rdoc +8 -2
- data/doc/release_notes/3.22.0.txt +39 -0
- data/doc/release_notes/3.23.0.txt +172 -0
- data/doc/release_notes/3.24.0.txt +420 -0
- data/doc/release_notes/3.25.0.txt +88 -0
- data/doc/release_notes/3.26.0.txt +88 -0
- data/doc/release_notes/3.27.0.txt +82 -0
- data/doc/release_notes/3.28.0.txt +304 -0
- data/doc/schema_modification.rdoc +547 -0
- data/doc/testing.rdoc +106 -0
- data/doc/transactions.rdoc +97 -0
- data/doc/virtual_rows.rdoc +2 -2
- data/lib/sequel/adapters/ado.rb +12 -1
- data/lib/sequel/adapters/amalgalite.rb +4 -0
- data/lib/sequel/adapters/db2.rb +95 -58
- data/lib/sequel/adapters/do.rb +12 -0
- data/lib/sequel/adapters/firebird.rb +25 -203
- data/lib/sequel/adapters/ibmdb.rb +440 -0
- data/lib/sequel/adapters/informix.rb +4 -19
- data/lib/sequel/adapters/jdbc/as400.rb +0 -7
- data/lib/sequel/adapters/jdbc/db2.rb +49 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +34 -0
- data/lib/sequel/adapters/jdbc/h2.rb +16 -5
- data/lib/sequel/adapters/jdbc/informix.rb +31 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
- data/lib/sequel/adapters/jdbc/mysql.rb +9 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +2 -27
- data/lib/sequel/adapters/jdbc/postgresql.rb +6 -0
- data/lib/sequel/adapters/jdbc/sqlserver.rb +46 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +34 -0
- data/lib/sequel/adapters/jdbc.rb +62 -29
- data/lib/sequel/adapters/mysql.rb +22 -139
- data/lib/sequel/adapters/mysql2.rb +9 -14
- data/lib/sequel/adapters/odbc/db2.rb +21 -0
- data/lib/sequel/adapters/odbc.rb +15 -3
- data/lib/sequel/adapters/oracle.rb +17 -1
- data/lib/sequel/adapters/postgres.rb +111 -16
- data/lib/sequel/adapters/shared/access.rb +21 -0
- data/lib/sequel/adapters/shared/db2.rb +290 -0
- data/lib/sequel/adapters/shared/firebird.rb +214 -0
- data/lib/sequel/adapters/shared/informix.rb +45 -0
- data/lib/sequel/adapters/shared/mssql.rb +85 -47
- data/lib/sequel/adapters/shared/mysql.rb +50 -7
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +143 -0
- data/lib/sequel/adapters/shared/oracle.rb +0 -4
- data/lib/sequel/adapters/shared/postgres.rb +75 -43
- data/lib/sequel/adapters/shared/sqlite.rb +56 -8
- data/lib/sequel/adapters/sqlite.rb +12 -11
- data/lib/sequel/adapters/swift/mysql.rb +9 -0
- data/lib/sequel/adapters/tinytds.rb +139 -7
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +55 -0
- data/lib/sequel/ast_transformer.rb +190 -0
- data/lib/sequel/connection_pool/threaded.rb +3 -2
- data/lib/sequel/connection_pool.rb +1 -1
- data/lib/sequel/core.rb +6 -5
- data/lib/sequel/database/connecting.rb +5 -5
- data/lib/sequel/database/dataset.rb +1 -1
- data/lib/sequel/database/dataset_defaults.rb +1 -1
- data/lib/sequel/database/logging.rb +1 -1
- data/lib/sequel/database/misc.rb +38 -17
- data/lib/sequel/database/query.rb +50 -19
- data/lib/sequel/database/schema_generator.rb +8 -5
- data/lib/sequel/database/schema_methods.rb +52 -27
- data/lib/sequel/dataset/actions.rb +167 -48
- data/lib/sequel/dataset/features.rb +57 -8
- data/lib/sequel/dataset/graph.rb +1 -1
- data/lib/sequel/dataset/misc.rb +39 -20
- data/lib/sequel/dataset/mutation.rb +3 -3
- data/lib/sequel/dataset/prepared_statements.rb +29 -14
- data/lib/sequel/dataset/query.rb +182 -32
- data/lib/sequel/dataset/sql.rb +31 -58
- data/lib/sequel/dataset.rb +8 -0
- data/lib/sequel/exceptions.rb +4 -0
- data/lib/sequel/extensions/columns_introspection.rb +61 -0
- data/lib/sequel/extensions/migration.rb +6 -4
- data/lib/sequel/extensions/to_dot.rb +95 -83
- data/lib/sequel/model/associations.rb +893 -309
- data/lib/sequel/model/base.rb +302 -105
- data/lib/sequel/model/errors.rb +1 -1
- data/lib/sequel/model/exceptions.rb +5 -1
- data/lib/sequel/model.rb +13 -7
- data/lib/sequel/plugins/association_pks.rb +22 -4
- data/lib/sequel/plugins/defaults_setter.rb +58 -0
- data/lib/sequel/plugins/identity_map.rb +113 -6
- data/lib/sequel/plugins/many_through_many.rb +67 -5
- data/lib/sequel/plugins/prepared_statements.rb +140 -0
- data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
- data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
- data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
- data/lib/sequel/plugins/sharding.rb +12 -20
- data/lib/sequel/plugins/single_table_inheritance.rb +2 -0
- data/lib/sequel/plugins/update_primary_key.rb +1 -1
- data/lib/sequel/plugins/xml_serializer.rb +3 -3
- data/lib/sequel/sql.rb +107 -51
- data/lib/sequel/timezones.rb +12 -3
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/db2_spec.rb +146 -0
- data/spec/adapters/mssql_spec.rb +36 -0
- data/spec/adapters/mysql_spec.rb +36 -19
- data/spec/adapters/postgres_spec.rb +115 -28
- data/spec/adapters/spec_helper.rb +6 -0
- data/spec/adapters/sqlite_spec.rb +11 -0
- data/spec/core/connection_pool_spec.rb +62 -77
- data/spec/core/database_spec.rb +244 -287
- data/spec/core/dataset_spec.rb +383 -34
- data/spec/core/expression_filters_spec.rb +159 -41
- data/spec/core/schema_spec.rb +326 -3
- data/spec/core/spec_helper.rb +45 -0
- data/spec/extensions/association_pks_spec.rb +38 -0
- data/spec/extensions/columns_introspection_spec.rb +91 -0
- data/spec/extensions/defaults_setter_spec.rb +64 -0
- data/spec/extensions/identity_map_spec.rb +162 -0
- data/spec/extensions/many_through_many_spec.rb +195 -20
- data/spec/extensions/migration_spec.rb +17 -17
- data/spec/extensions/nested_attributes_spec.rb +1 -0
- data/spec/extensions/prepared_statements_associations_spec.rb +126 -0
- data/spec/extensions/prepared_statements_safe_spec.rb +79 -0
- data/spec/extensions/prepared_statements_spec.rb +72 -0
- data/spec/extensions/prepared_statements_with_pk_spec.rb +38 -0
- data/spec/extensions/schema_dumper_spec.rb +2 -2
- data/spec/extensions/schema_spec.rb +12 -20
- data/spec/extensions/serialization_modification_detection_spec.rb +36 -0
- data/spec/extensions/single_table_inheritance_spec.rb +11 -0
- data/spec/extensions/spec_helper.rb +3 -1
- data/spec/extensions/to_dot_spec.rb +3 -5
- data/spec/extensions/xml_serializer_spec.rb +16 -4
- data/spec/integration/associations_test.rb +405 -15
- data/spec/integration/database_test.rb +4 -2
- data/spec/integration/dataset_test.rb +240 -20
- data/spec/integration/plugin_test.rb +142 -5
- data/spec/integration/prepared_statement_test.rb +174 -95
- data/spec/integration/schema_test.rb +128 -16
- data/spec/integration/spec_helper.rb +15 -0
- data/spec/integration/transaction_test.rb +40 -0
- data/spec/integration/type_test.rb +16 -2
- data/spec/model/association_reflection_spec.rb +91 -0
- data/spec/model/associations_spec.rb +476 -5
- data/spec/model/base_spec.rb +91 -1
- data/spec/model/eager_loading_spec.rb +519 -31
- data/spec/model/hooks_spec.rb +161 -0
- data/spec/model/model_spec.rb +89 -2
- data/spec/model/plugins_spec.rb +17 -0
- data/spec/model/record_spec.rb +184 -12
- data/spec/model/spec_helper.rb +5 -0
- data/spec/model/validations_spec.rb +11 -0
- metadata +85 -34
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module Sequel
|
|
2
2
|
class Database
|
|
3
3
|
# ---------------------
|
|
4
|
-
# :section: Methods that modify the database schema
|
|
4
|
+
# :section: 2 - Methods that modify the database schema
|
|
5
5
|
# These methods execute code on the database that modifies the database's schema.
|
|
6
6
|
# ---------------------
|
|
7
7
|
|
|
@@ -21,7 +21,7 @@ module Sequel
|
|
|
21
21
|
UNSIGNED = ' UNSIGNED'.freeze
|
|
22
22
|
|
|
23
23
|
# The order of column modifiers to use when defining a column.
|
|
24
|
-
COLUMN_DEFINITION_ORDER = [:default, :null, :unique, :primary_key, :auto_increment, :references]
|
|
24
|
+
COLUMN_DEFINITION_ORDER = [:collate, :default, :null, :unique, :primary_key, :auto_increment, :references]
|
|
25
25
|
|
|
26
26
|
# Adds a column to the specified table. This method expects a column name,
|
|
27
27
|
# a datatype and optionally a hash with additional constraints and options:
|
|
@@ -29,7 +29,7 @@ module Sequel
|
|
|
29
29
|
# DB.add_column :items, :name, :text, :unique => true, :null => false
|
|
30
30
|
# DB.add_column :items, :category, :text, :default => 'ruby'
|
|
31
31
|
#
|
|
32
|
-
# See alter_table
|
|
32
|
+
# See <tt>alter_table</tt>.
|
|
33
33
|
def add_column(table, *args)
|
|
34
34
|
alter_table(table) {add_column(*args)}
|
|
35
35
|
end
|
|
@@ -40,9 +40,9 @@ module Sequel
|
|
|
40
40
|
# DB.add_index :posts, [:author, :title], :unique => true
|
|
41
41
|
#
|
|
42
42
|
# Options:
|
|
43
|
-
#
|
|
43
|
+
# :ignore_errors :: Ignore any DatabaseErrors that are raised
|
|
44
44
|
#
|
|
45
|
-
# See alter_table
|
|
45
|
+
# See <tt>alter_table</tt>.
|
|
46
46
|
def add_index(table, columns, options={})
|
|
47
47
|
e = options[:ignore_errors]
|
|
48
48
|
begin
|
|
@@ -65,10 +65,10 @@ module Sequel
|
|
|
65
65
|
# end
|
|
66
66
|
#
|
|
67
67
|
# Note that +add_column+ accepts all the options available for column
|
|
68
|
-
# definitions using create_table
|
|
68
|
+
# definitions using <tt>create_table</tt>, and +add_index+ accepts all the options
|
|
69
69
|
# available for index definition.
|
|
70
70
|
#
|
|
71
|
-
# See Schema::AlterTableGenerator and the {"Migrations and Schema Modification" guide}[link:files/doc/migration_rdoc.html].
|
|
71
|
+
# See <tt>Schema::AlterTableGenerator</tt> and the {"Migrations and Schema Modification" guide}[link:files/doc/migration_rdoc.html].
|
|
72
72
|
def alter_table(name, generator=nil, &block)
|
|
73
73
|
generator ||= Schema::AlterTableGenerator.new(self, &block)
|
|
74
74
|
alter_table_sql_list(name, generator.operations).flatten.each {|sql| execute_ddl(sql)}
|
|
@@ -89,7 +89,7 @@ module Sequel
|
|
|
89
89
|
# :temp :: Create the table as a temporary table.
|
|
90
90
|
# :ignore_index_errors :: Ignore any errors when creating indexes.
|
|
91
91
|
#
|
|
92
|
-
# See Schema::Generator and the {"Migrations and Schema Modification" guide}[link:files/doc/migration_rdoc.html].
|
|
92
|
+
# See <tt>Schema::Generator</tt> and the {"Migrations and Schema Modification" guide}[link:files/doc/migration_rdoc.html].
|
|
93
93
|
def create_table(name, options={}, &block)
|
|
94
94
|
remove_cached_schema(name)
|
|
95
95
|
options = {:generator=>options} if options.is_a?(Schema::Generator)
|
|
@@ -99,19 +99,28 @@ module Sequel
|
|
|
99
99
|
nil
|
|
100
100
|
end
|
|
101
101
|
|
|
102
|
-
# Forcibly
|
|
102
|
+
# Forcibly create a table, attempting to drop it if it already exists, then creating it.
|
|
103
103
|
#
|
|
104
104
|
# DB.create_table!(:a){Integer :a}
|
|
105
|
-
# #
|
|
105
|
+
# # SELECT * FROM a LIMIT a -- check existence
|
|
106
|
+
# # DROP TABLE a -- drop table if already exists
|
|
106
107
|
# # CREATE TABLE a (a integer)
|
|
107
108
|
def create_table!(name, options={}, &block)
|
|
108
|
-
drop_table(name)
|
|
109
|
+
drop_table(name) if table_exists?(name)
|
|
109
110
|
create_table(name, options, &block)
|
|
110
111
|
end
|
|
111
112
|
|
|
112
|
-
# Creates the table unless the table already exists
|
|
113
|
+
# Creates the table unless the table already exists.
|
|
114
|
+
#
|
|
115
|
+
# DB.create_table?(:a){Integer :a}
|
|
116
|
+
# # SELECT * FROM a LIMIT a -- check existence
|
|
117
|
+
# # CREATE TABLE a (a integer) -- if it doesn't already exist
|
|
113
118
|
def create_table?(name, options={}, &block)
|
|
114
|
-
|
|
119
|
+
if supports_create_table_if_not_exists?
|
|
120
|
+
create_table(name, options.merge(:if_not_exists=>true), &block)
|
|
121
|
+
elsif !table_exists?(name)
|
|
122
|
+
create_table(name, options, &block)
|
|
123
|
+
end
|
|
115
124
|
end
|
|
116
125
|
|
|
117
126
|
# Creates a view, replacing it if it already exists:
|
|
@@ -138,7 +147,7 @@ module Sequel
|
|
|
138
147
|
#
|
|
139
148
|
# DB.drop_column :items, :category
|
|
140
149
|
#
|
|
141
|
-
# See alter_table
|
|
150
|
+
# See <tt>alter_table</tt>.
|
|
142
151
|
def drop_column(table, *args)
|
|
143
152
|
alter_table(table) {drop_column(*args)}
|
|
144
153
|
end
|
|
@@ -148,17 +157,20 @@ module Sequel
|
|
|
148
157
|
# DB.drop_index :posts, :title
|
|
149
158
|
# DB.drop_index :posts, [:author, :title]
|
|
150
159
|
#
|
|
151
|
-
# See alter_table
|
|
160
|
+
# See <tt>alter_table</tt>.
|
|
152
161
|
def drop_index(table, columns, options={})
|
|
153
162
|
alter_table(table){drop_index(columns, options)}
|
|
154
163
|
end
|
|
155
164
|
|
|
156
165
|
# Drops one or more tables corresponding to the given names:
|
|
157
166
|
#
|
|
167
|
+
# DB.drop_table(:posts)
|
|
158
168
|
# DB.drop_table(:posts, :comments)
|
|
169
|
+
# DB.drop_table(:posts, :comments, :cascade=>true)
|
|
159
170
|
def drop_table(*names)
|
|
171
|
+
options = names.last.is_a?(Hash) ? names.pop : {}
|
|
160
172
|
names.each do |n|
|
|
161
|
-
execute_ddl(drop_table_sql(n))
|
|
173
|
+
execute_ddl(drop_table_sql(n, options))
|
|
162
174
|
remove_cached_schema(n)
|
|
163
175
|
end
|
|
164
176
|
nil
|
|
@@ -167,9 +179,12 @@ module Sequel
|
|
|
167
179
|
# Drops one or more views corresponding to the given names:
|
|
168
180
|
#
|
|
169
181
|
# DB.drop_view(:cheap_items)
|
|
182
|
+
# DB.drop_view(:cheap_items, :pricey_items)
|
|
183
|
+
# DB.drop_view(:cheap_items, :pricey_items, :cascade=>true)
|
|
170
184
|
def drop_view(*names)
|
|
185
|
+
options = names.last.is_a?(Hash) ? names.pop : {}
|
|
171
186
|
names.each do |n|
|
|
172
|
-
execute_ddl(
|
|
187
|
+
execute_ddl(drop_view_sql(n, options))
|
|
173
188
|
remove_cached_schema(n)
|
|
174
189
|
end
|
|
175
190
|
nil
|
|
@@ -191,7 +206,7 @@ module Sequel
|
|
|
191
206
|
#
|
|
192
207
|
# DB.rename_column :items, :cntr, :counter
|
|
193
208
|
#
|
|
194
|
-
# See alter_table
|
|
209
|
+
# See <tt>alter_table</tt>.
|
|
195
210
|
def rename_column(table, *args)
|
|
196
211
|
alter_table(table) {rename_column(*args)}
|
|
197
212
|
end
|
|
@@ -200,7 +215,7 @@ module Sequel
|
|
|
200
215
|
#
|
|
201
216
|
# DB.set_column_default :items, :category, 'perl!'
|
|
202
217
|
#
|
|
203
|
-
# See alter_table
|
|
218
|
+
# See <tt>alter_table</tt>.
|
|
204
219
|
def set_column_default(table, *args)
|
|
205
220
|
alter_table(table) {set_column_default(*args)}
|
|
206
221
|
end
|
|
@@ -209,7 +224,7 @@ module Sequel
|
|
|
209
224
|
#
|
|
210
225
|
# DB.set_column_type :items, :price, :float
|
|
211
226
|
#
|
|
212
|
-
# See alter_table
|
|
227
|
+
# See <tt>alter_table</tt>.
|
|
213
228
|
def set_column_type(table, *args)
|
|
214
229
|
alter_table(table) {set_column_type(*args)}
|
|
215
230
|
end
|
|
@@ -224,7 +239,7 @@ module Sequel
|
|
|
224
239
|
when :add_column
|
|
225
240
|
"ADD COLUMN #{column_definition_sql(op)}"
|
|
226
241
|
when :drop_column
|
|
227
|
-
"DROP COLUMN #{quoted_name}"
|
|
242
|
+
"DROP COLUMN #{quoted_name}#{' CASCADE' if op[:cascade]}"
|
|
228
243
|
when :rename_column
|
|
229
244
|
"RENAME COLUMN #{quoted_name} TO #{quote_identifier(op[:new_name])}"
|
|
230
245
|
when :set_column_type
|
|
@@ -240,7 +255,7 @@ module Sequel
|
|
|
240
255
|
when :add_constraint
|
|
241
256
|
"ADD #{constraint_definition_sql(op)}"
|
|
242
257
|
when :drop_constraint
|
|
243
|
-
"DROP CONSTRAINT #{quoted_name}"
|
|
258
|
+
"DROP CONSTRAINT #{quoted_name}#{' CASCADE' if op[:cascade]}"
|
|
244
259
|
else
|
|
245
260
|
raise Error, "Unsupported ALTER TABLE operation"
|
|
246
261
|
end
|
|
@@ -275,7 +290,12 @@ module Sequel
|
|
|
275
290
|
def column_definition_auto_increment_sql(sql, column)
|
|
276
291
|
sql << " #{auto_increment_sql}" if column[:auto_increment]
|
|
277
292
|
end
|
|
278
|
-
|
|
293
|
+
|
|
294
|
+
# Add collate SQL fragment to column creation SQL.
|
|
295
|
+
def column_definition_collate_sql(sql, column)
|
|
296
|
+
sql << " COLLATE #{column[:collate]}" if column[:collate]
|
|
297
|
+
end
|
|
298
|
+
|
|
279
299
|
# Add default SQL fragment to column creation SQL.
|
|
280
300
|
def column_definition_default_sql(sql, column)
|
|
281
301
|
sql << " DEFAULT #{literal(column[:default])}" if column.include?(:default)
|
|
@@ -354,7 +374,7 @@ module Sequel
|
|
|
354
374
|
|
|
355
375
|
# Execute the create index statements using the generator.
|
|
356
376
|
def create_table_indexes_from_generator(name, generator, options)
|
|
357
|
-
e = options[:ignore_index_errors]
|
|
377
|
+
e = options[:ignore_index_errors] || options[:if_not_exists]
|
|
358
378
|
generator.indexes.each do |index|
|
|
359
379
|
begin
|
|
360
380
|
index_sql_list(name, [index]).each{|sql| execute_ddl(sql)}
|
|
@@ -366,7 +386,7 @@ module Sequel
|
|
|
366
386
|
|
|
367
387
|
# DDL statement for creating a table with the given name, columns, and options
|
|
368
388
|
def create_table_sql(name, generator, options)
|
|
369
|
-
"CREATE #{temporary_table_sql if options[:temp]}TABLE #{quote_schema_table(name)} (#{column_list_sql(generator)})"
|
|
389
|
+
"CREATE #{temporary_table_sql if options[:temp]}TABLE#{' IF NOT EXISTS' if options[:if_not_exists]} #{options[:temp] ? quote_identifier(name) : quote_schema_table(name)} (#{column_list_sql(generator)})"
|
|
370
390
|
end
|
|
371
391
|
|
|
372
392
|
# Default index name for the table and columns, may be too long
|
|
@@ -382,10 +402,15 @@ module Sequel
|
|
|
382
402
|
end
|
|
383
403
|
|
|
384
404
|
# SQL DDL statement to drop the table with the given name.
|
|
385
|
-
def drop_table_sql(name)
|
|
386
|
-
"DROP TABLE #{quote_schema_table(name)}"
|
|
405
|
+
def drop_table_sql(name, options)
|
|
406
|
+
"DROP TABLE #{quote_schema_table(name)}#{' CASCADE' if options[:cascade]}"
|
|
387
407
|
end
|
|
388
408
|
|
|
409
|
+
# SQL DDL statement to drop a view with the given name.
|
|
410
|
+
def drop_view_sql(name, options)
|
|
411
|
+
"DROP VIEW #{quote_schema_table(name)}#{' CASCADE' if options[:cascade]}"
|
|
412
|
+
end
|
|
413
|
+
|
|
389
414
|
# Proxy the filter_expr call to the dataset, used for creating constraints.
|
|
390
415
|
def filter_expr(*args, &block)
|
|
391
416
|
schema_utility_dataset.literal(schema_utility_dataset.send(:filter_expr, *args, &block))
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module Sequel
|
|
2
2
|
class Dataset
|
|
3
3
|
# ---------------------
|
|
4
|
-
# :section: Methods that execute code on the database
|
|
4
|
+
# :section: 2 - Methods that execute code on the database
|
|
5
5
|
# These methods all execute the dataset's SQL on the database.
|
|
6
6
|
# They don't return modified datasets, so if used in a method chain
|
|
7
7
|
# they should be the last method called.
|
|
@@ -101,10 +101,15 @@ module Sequel
|
|
|
101
101
|
#
|
|
102
102
|
# DB[:table].delete # DELETE * FROM table
|
|
103
103
|
# # => 3
|
|
104
|
-
def delete
|
|
105
|
-
|
|
104
|
+
def delete(&block)
|
|
105
|
+
sql = delete_sql
|
|
106
|
+
if uses_returning?(:delete)
|
|
107
|
+
returning_fetch_rows(sql, &block)
|
|
108
|
+
else
|
|
109
|
+
execute_dui(sql)
|
|
110
|
+
end
|
|
106
111
|
end
|
|
107
|
-
|
|
112
|
+
|
|
108
113
|
# Iterates over the records in the dataset as they are yielded from the
|
|
109
114
|
# database adapter, and returns self.
|
|
110
115
|
#
|
|
@@ -248,14 +253,16 @@ module Sequel
|
|
|
248
253
|
# the value of the primary key for the inserted row, but that is adapter dependent.
|
|
249
254
|
#
|
|
250
255
|
# +insert+ handles a number of different argument formats:
|
|
251
|
-
#
|
|
252
|
-
#
|
|
253
|
-
#
|
|
254
|
-
#
|
|
255
|
-
#
|
|
256
|
-
#
|
|
257
|
-
#
|
|
258
|
-
#
|
|
256
|
+
# no arguments or single empty hash :: Uses DEFAULT VALUES
|
|
257
|
+
# single hash :: Most common format, treats keys as columns an values as values
|
|
258
|
+
# single array :: Treats entries as values, with no columns
|
|
259
|
+
# two arrays :: Treats first array as columns, second array as values
|
|
260
|
+
# single Dataset :: Treats as an insert based on a selection from the dataset given,
|
|
261
|
+
# with no columns
|
|
262
|
+
# array and dataset :: Treats as an insert based on a selection from the dataset
|
|
263
|
+
# given, with the columns given by the array.
|
|
264
|
+
#
|
|
265
|
+
# Examples:
|
|
259
266
|
#
|
|
260
267
|
# DB[:items].insert
|
|
261
268
|
# # INSERT INTO items DEFAULT VALUES
|
|
@@ -277,8 +284,13 @@ module Sequel
|
|
|
277
284
|
#
|
|
278
285
|
# DB[:items].insert([:a, :b], DB[:old_items])
|
|
279
286
|
# # INSERT INTO items (a, b) SELECT * FROM old_items
|
|
280
|
-
def insert(*values)
|
|
281
|
-
|
|
287
|
+
def insert(*values, &block)
|
|
288
|
+
sql = insert_sql(*values)
|
|
289
|
+
if uses_returning?(:insert)
|
|
290
|
+
returning_fetch_rows(sql, &block)
|
|
291
|
+
else
|
|
292
|
+
execute_insert(sql)
|
|
293
|
+
end
|
|
282
294
|
end
|
|
283
295
|
|
|
284
296
|
# Inserts multiple values. If a block is given it is invoked for each
|
|
@@ -310,7 +322,7 @@ module Sequel
|
|
|
310
322
|
aggregate_dataset.get{max(column) - min(column)}
|
|
311
323
|
end
|
|
312
324
|
|
|
313
|
-
# Reverses the order and then runs first. Note that this
|
|
325
|
+
# Reverses the order and then runs #first with the given arguments and block. Note that this
|
|
314
326
|
# will not necessarily give you the last record in the dataset,
|
|
315
327
|
# unless you have an unambiguous order. If there is not
|
|
316
328
|
# currently an order for this dataset, raises an +Error+.
|
|
@@ -334,10 +346,19 @@ module Sequel
|
|
|
334
346
|
#
|
|
335
347
|
# DB[:table].map{|r| r[:id] * 2} # SELECT * FROM table
|
|
336
348
|
# # => [2, 4, 6, ...]
|
|
349
|
+
#
|
|
350
|
+
# You can also provide an array of column names:
|
|
351
|
+
#
|
|
352
|
+
# DB[:table].map([:id, :name]) # SELECT * FROM table
|
|
353
|
+
# # => [[1, 'A'], [2, 'B'], [3, 'C'], ...]
|
|
337
354
|
def map(column=nil, &block)
|
|
338
355
|
if column
|
|
339
356
|
raise(Error, ARG_BLOCK_ERROR_MSG) if block
|
|
340
|
-
|
|
357
|
+
if column.is_a?(Array)
|
|
358
|
+
super(){|r| column.map{|c| r[c]}}
|
|
359
|
+
else
|
|
360
|
+
super(){|r| r[column]}
|
|
361
|
+
end
|
|
341
362
|
else
|
|
342
363
|
super(&block)
|
|
343
364
|
end
|
|
@@ -393,49 +414,63 @@ module Sequel
|
|
|
393
414
|
#
|
|
394
415
|
# DB[:table].select_hash(:id, :name) # SELECT id, name FROM table
|
|
395
416
|
# # => {1=>'a', 2=>'b', ...}
|
|
417
|
+
#
|
|
418
|
+
# You can also provide an array of column names for either the key_column,
|
|
419
|
+
# the value column, or both:
|
|
420
|
+
#
|
|
421
|
+
# DB[:table].select_hash([:id, :foo], [:name, :bar]) # SELECT * FROM table
|
|
422
|
+
# # {[1, 3]=>['a', 'c'], [2, 4]=>['b', 'd'], ...}
|
|
396
423
|
def select_hash(key_column, value_column)
|
|
397
|
-
|
|
424
|
+
if key_column.is_a?(Array)
|
|
425
|
+
if value_column.is_a?(Array)
|
|
426
|
+
select(*(key_column + value_column)).to_hash(key_column.map{|c| hash_key_symbol(c)}, value_column.map{|c| hash_key_symbol(c)})
|
|
427
|
+
else
|
|
428
|
+
select(*(key_column + [value_column])).to_hash(key_column.map{|c| hash_key_symbol(c)}, hash_key_symbol(value_column))
|
|
429
|
+
end
|
|
430
|
+
elsif value_column.is_a?(Array)
|
|
431
|
+
select(key_column, *value_column).to_hash(hash_key_symbol(key_column), value_column.map{|c| hash_key_symbol(c)})
|
|
432
|
+
else
|
|
433
|
+
select(key_column, value_column).to_hash(hash_key_symbol(key_column), hash_key_symbol(value_column))
|
|
434
|
+
end
|
|
398
435
|
end
|
|
399
436
|
|
|
400
437
|
# Selects the column given (either as an argument or as a block), and
|
|
401
438
|
# returns an array of all values of that column in the dataset. If you
|
|
402
439
|
# give a block argument that returns an array with multiple entries,
|
|
403
|
-
# the contents of the resulting array are undefined.
|
|
440
|
+
# the contents of the resulting array are undefined. Raises an Error
|
|
441
|
+
# if called with both an argument and a block.
|
|
404
442
|
#
|
|
405
443
|
# DB[:table].select_map(:id) # SELECT id FROM table
|
|
406
444
|
# # => [3, 5, 8, 1, ...]
|
|
407
445
|
#
|
|
408
|
-
# DB[:table].select_map{
|
|
409
|
-
# # => [
|
|
446
|
+
# DB[:table].select_map{id * 2} # SELECT (id * 2) FROM table
|
|
447
|
+
# # => [6, 10, 16, 2, ...]
|
|
448
|
+
#
|
|
449
|
+
# You can also provide an array of column names:
|
|
450
|
+
#
|
|
451
|
+
# DB[:table].select_map([:id, :name]) # SELECT id, name FROM table
|
|
452
|
+
# # => [[1, 'A'], [2, 'B'], [3, 'C'], ...]
|
|
410
453
|
def select_map(column=nil, &block)
|
|
411
|
-
|
|
412
|
-
ds = if column
|
|
413
|
-
raise(Error, ARG_BLOCK_ERROR_MSG) if block
|
|
414
|
-
ds.select(column)
|
|
415
|
-
else
|
|
416
|
-
ds.select(&block)
|
|
417
|
-
end
|
|
418
|
-
ds.map{|r| r.values.first}
|
|
454
|
+
_select_map(column, false, &block)
|
|
419
455
|
end
|
|
456
|
+
|
|
420
457
|
|
|
421
458
|
# The same as select_map, but in addition orders the array by the column.
|
|
422
459
|
#
|
|
423
460
|
# DB[:table].select_order_map(:id) # SELECT id FROM table ORDER BY id
|
|
424
461
|
# # => [1, 2, 3, 4, ...]
|
|
425
462
|
#
|
|
426
|
-
# DB[:table].select_order_map{
|
|
427
|
-
# # => [
|
|
463
|
+
# DB[:table].select_order_map{id * 2} # SELECT (id * 2) FROM table ORDER BY (id * 2)
|
|
464
|
+
# # => [2, 4, 6, 8, ...]
|
|
465
|
+
#
|
|
466
|
+
# You can also provide an array of column names:
|
|
467
|
+
#
|
|
468
|
+
# DB[:table].select_order_map([:id, :name]) # SELECT id, name FROM table ORDER BY id, name
|
|
469
|
+
# # => [[1, 'A'], [2, 'B'], [3, 'C'], ...]
|
|
428
470
|
def select_order_map(column=nil, &block)
|
|
429
|
-
|
|
430
|
-
ds = if column
|
|
431
|
-
raise(Error, ARG_BLOCK_ERROR_MSG) if block
|
|
432
|
-
ds.select(column).order(unaliased_identifier(column))
|
|
433
|
-
else
|
|
434
|
-
ds.select(&block).order(&block)
|
|
435
|
-
end
|
|
436
|
-
ds.map{|r| r.values.first}
|
|
471
|
+
_select_map(column, true, &block)
|
|
437
472
|
end
|
|
438
|
-
|
|
473
|
+
|
|
439
474
|
# Alias for update, but not aliased directly so subclasses
|
|
440
475
|
# don't have to override both methods.
|
|
441
476
|
def set(*args)
|
|
@@ -499,11 +534,37 @@ module Sequel
|
|
|
499
534
|
#
|
|
500
535
|
# DB[:table].to_hash(:id) # SELECT * FROM table
|
|
501
536
|
# # {1=>{:id=>1, :name=>'Jim'}, 2=>{:id=>2, :name=>'Bob'}, ...}
|
|
537
|
+
#
|
|
538
|
+
# You can also provide an array of column names for either the key_column,
|
|
539
|
+
# the value column, or both:
|
|
540
|
+
#
|
|
541
|
+
# DB[:table].to_hash([:id, :foo], [:name, :bar]) # SELECT * FROM table
|
|
542
|
+
# # {[1, 3]=>['Jim', 'bo'], [2, 4]=>['Bob', 'be'], ...}
|
|
543
|
+
#
|
|
544
|
+
# DB[:table].to_hash([:id, :name]) # SELECT * FROM table
|
|
545
|
+
# # {[1, 'Jim']=>{:id=>1, :name=>'Jim'}, [2, 'Bob'=>{:id=>2, :name=>'Bob'}, ...}
|
|
502
546
|
def to_hash(key_column, value_column = nil)
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
547
|
+
h = {}
|
|
548
|
+
if value_column
|
|
549
|
+
if value_column.is_a?(Array)
|
|
550
|
+
if key_column.is_a?(Array)
|
|
551
|
+
each{|r| h[key_column.map{|c| r[c]}] = value_column.map{|c| r[c]}}
|
|
552
|
+
else
|
|
553
|
+
each{|r| h[r[key_column]] = value_column.map{|c| r[c]}}
|
|
554
|
+
end
|
|
555
|
+
else
|
|
556
|
+
if key_column.is_a?(Array)
|
|
557
|
+
each{|r| h[key_column.map{|c| r[c]}] = r[value_column]}
|
|
558
|
+
else
|
|
559
|
+
each{|r| h[r[key_column]] = r[value_column]}
|
|
560
|
+
end
|
|
561
|
+
end
|
|
562
|
+
elsif key_column.is_a?(Array)
|
|
563
|
+
each{|r| h[key_column.map{|c| r[c]}] = r}
|
|
564
|
+
else
|
|
565
|
+
each{|r| h[r[key_column]] = r}
|
|
506
566
|
end
|
|
567
|
+
h
|
|
507
568
|
end
|
|
508
569
|
|
|
509
570
|
# Truncates the dataset. Returns nil.
|
|
@@ -522,14 +583,40 @@ module Sequel
|
|
|
522
583
|
# DB[:table].update(:x=>nil) # UPDATE table SET x = NULL
|
|
523
584
|
# # => 10
|
|
524
585
|
#
|
|
525
|
-
# DB[:table].update(:x=>:x+1, :y=>0) # UPDATE table SET x = (x + 1),
|
|
586
|
+
# DB[:table].update(:x=>:x+1, :y=>0) # UPDATE table SET x = (x + 1), y = 0
|
|
526
587
|
# # => 10
|
|
527
|
-
def update(values={})
|
|
528
|
-
|
|
588
|
+
def update(values={}, &block)
|
|
589
|
+
sql = update_sql(values)
|
|
590
|
+
if uses_returning?(:update)
|
|
591
|
+
returning_fetch_rows(sql, &block)
|
|
592
|
+
else
|
|
593
|
+
execute_dui(sql)
|
|
594
|
+
end
|
|
529
595
|
end
|
|
530
596
|
|
|
531
597
|
private
|
|
532
598
|
|
|
599
|
+
# Internals of +select_map+ and +select_order_map+
|
|
600
|
+
def _select_map(column, order, &block)
|
|
601
|
+
ds = naked.ungraphed
|
|
602
|
+
if column
|
|
603
|
+
raise(Error, ARG_BLOCK_ERROR_MSG) if block
|
|
604
|
+
columns = Array(column)
|
|
605
|
+
select_cols = order ? columns.map{|c| c.is_a?(SQL::OrderedExpression) ? c.expression : c} : columns
|
|
606
|
+
ds = ds.select(*select_cols)
|
|
607
|
+
ds = ds.order(*columns.map{|c| unaliased_identifier(c)}) if order
|
|
608
|
+
else
|
|
609
|
+
ds = ds.select(&block)
|
|
610
|
+
ds = ds.order(&block) if order
|
|
611
|
+
end
|
|
612
|
+
if ds.opts[:select].length > 1
|
|
613
|
+
ret_cols = select_cols.map{|c| hash_key_symbol(c)}
|
|
614
|
+
ds.map{|r| ret_cols.map{|c| r[c]}}
|
|
615
|
+
else
|
|
616
|
+
ds.map{|r| r.values.first}
|
|
617
|
+
end
|
|
618
|
+
end
|
|
619
|
+
|
|
533
620
|
# Set the server to use to :default unless it is already set in the passed opts
|
|
534
621
|
def default_server_opts(opts)
|
|
535
622
|
{:server=>@opts[:server] || :default}.merge(opts)
|
|
@@ -561,9 +648,19 @@ module Sequel
|
|
|
561
648
|
# specifying the symbol that is likely to be used as the hash key
|
|
562
649
|
# for the column when records are returned.
|
|
563
650
|
def hash_key_symbol(s)
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
651
|
+
case s
|
|
652
|
+
when Symbol
|
|
653
|
+
_, c, a = split_symbol(s)
|
|
654
|
+
(a || c).to_sym
|
|
655
|
+
when SQL::Identifier
|
|
656
|
+
hash_key_symbol(s.value)
|
|
657
|
+
when SQL::QualifiedIdentifier
|
|
658
|
+
hash_key_symbol(s.column)
|
|
659
|
+
when SQL::AliasedExpression
|
|
660
|
+
hash_key_symbol(s.aliaz)
|
|
661
|
+
else
|
|
662
|
+
raise(Error, "#{s.inspect} is not supported, should be a Symbol, String, SQL::Identifier, SQL::QualifiedIdentifier, or SQL::AliasedExpression")
|
|
663
|
+
end
|
|
567
664
|
end
|
|
568
665
|
|
|
569
666
|
# Modify the identifier returned from the database based on the
|
|
@@ -580,6 +677,20 @@ module Sequel
|
|
|
580
677
|
def post_load(all_records)
|
|
581
678
|
end
|
|
582
679
|
|
|
680
|
+
# Called by insert/update/delete when returning is used.
|
|
681
|
+
# Yields each row as a plain hash to the block if one is given, or returns
|
|
682
|
+
# an array of plain hashes for all rows if a block is not given
|
|
683
|
+
def returning_fetch_rows(sql, &block)
|
|
684
|
+
if block
|
|
685
|
+
default_server.fetch_rows(sql, &block)
|
|
686
|
+
nil
|
|
687
|
+
else
|
|
688
|
+
rows = []
|
|
689
|
+
default_server.fetch_rows(sql){|r| rows << r}
|
|
690
|
+
rows
|
|
691
|
+
end
|
|
692
|
+
end
|
|
693
|
+
|
|
583
694
|
# Return the unaliased part of the identifier. Handles both
|
|
584
695
|
# implicit aliases in symbols, as well as SQL::AliasedExpression
|
|
585
696
|
# objects. Other objects are returned as is.
|
|
@@ -590,6 +701,14 @@ module Sequel
|
|
|
590
701
|
c_table ? SQL::QualifiedIdentifier.new(c_table, column.to_sym) : column.to_sym
|
|
591
702
|
when SQL::AliasedExpression
|
|
592
703
|
c.expression
|
|
704
|
+
when SQL::OrderedExpression
|
|
705
|
+
expr = c.expression
|
|
706
|
+
if expr.is_a?(Symbol)
|
|
707
|
+
expr = unaliased_identifier(expr)
|
|
708
|
+
SQL::OrderedExpression.new(unaliased_identifier(c.expression), c.descending, :nulls=>c.nulls)
|
|
709
|
+
else
|
|
710
|
+
c
|
|
711
|
+
end
|
|
593
712
|
else
|
|
594
713
|
c
|
|
595
714
|
end
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
module Sequel
|
|
2
2
|
class Dataset
|
|
3
3
|
# ---------------------
|
|
4
|
-
# :section: Methods that describe what the dataset supports
|
|
4
|
+
# :section: 4 - Methods that describe what the dataset supports
|
|
5
5
|
# These methods all return booleans, with most describing whether or not the
|
|
6
6
|
# dataset supports a feature.
|
|
7
7
|
# ---------------------
|
|
8
8
|
|
|
9
|
-
# Method used to check if WITH is supported
|
|
10
|
-
WITH_SUPPORTED=:select_with_sql
|
|
11
|
-
|
|
12
9
|
# Whether this dataset quotes identifiers.
|
|
13
10
|
def quote_identifiers?
|
|
14
|
-
@quote_identifiers
|
|
11
|
+
if defined?(@quote_identifiers)
|
|
12
|
+
@quote_identifiers
|
|
13
|
+
elsif db.respond_to?(:quote_identifiers?)
|
|
14
|
+
@quote_identifiers = db.quote_identifiers?
|
|
15
|
+
else
|
|
16
|
+
@quote_identifiers = false
|
|
17
|
+
end
|
|
15
18
|
end
|
|
16
19
|
|
|
17
20
|
# Whether this dataset will provide accurate number of rows matched for
|
|
@@ -28,15 +31,30 @@ module Sequel
|
|
|
28
31
|
end
|
|
29
32
|
|
|
30
33
|
# Whether the dataset supports common table expressions (the WITH clause).
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
# If given, +type+ can be :select, :insert, :update, or :delete, in which case it
|
|
35
|
+
# determines whether WITH is supported for the respective statement type.
|
|
36
|
+
def supports_cte?(type=:select)
|
|
37
|
+
send(:"#{type}_clause_methods").include?(:"#{type}_with_sql")
|
|
33
38
|
end
|
|
34
39
|
|
|
35
|
-
# Whether the dataset supports
|
|
40
|
+
# Whether the dataset supports common table expressions (the WITH clause)
|
|
41
|
+
# in subqueries. If false, applies the WITH clause to the main query, which can cause issues
|
|
42
|
+
# if multiple WITH clauses use the same name.
|
|
43
|
+
def supports_cte_in_subqueries?
|
|
44
|
+
false
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Whether the dataset supports or can emulate the DISTINCT ON clause, false by default.
|
|
36
48
|
def supports_distinct_on?
|
|
37
49
|
false
|
|
38
50
|
end
|
|
39
51
|
|
|
52
|
+
# Whether this dataset supports the +insert_select+ method for returning all columns values
|
|
53
|
+
# directly from an insert query.
|
|
54
|
+
def supports_insert_select?
|
|
55
|
+
supports_returning?(:insert)
|
|
56
|
+
end
|
|
57
|
+
|
|
40
58
|
# Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
|
|
41
59
|
def supports_intersect_except?
|
|
42
60
|
true
|
|
@@ -67,7 +85,24 @@ module Sequel
|
|
|
67
85
|
def supports_multiple_column_in?
|
|
68
86
|
true
|
|
69
87
|
end
|
|
88
|
+
|
|
89
|
+
# Whether the dataset supports or can fully emulate the DISTINCT ON clause,
|
|
90
|
+
# including respecting the ORDER BY clause, false by default
|
|
91
|
+
def supports_ordered_distinct_on?
|
|
92
|
+
supports_distinct_on?
|
|
93
|
+
end
|
|
70
94
|
|
|
95
|
+
# Whether the RETURNING clause is supported for the given type of query.
|
|
96
|
+
# +type+ can be :insert, :update, or :delete.
|
|
97
|
+
def supports_returning?(type)
|
|
98
|
+
send(:"#{type}_clause_methods").include?(:"#{type}_returning_sql")
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Whether the database supports SELECT *, column FROM table
|
|
102
|
+
def supports_select_all_and_column?
|
|
103
|
+
true
|
|
104
|
+
end
|
|
105
|
+
|
|
71
106
|
# Whether the dataset supports timezones in literal timestamps
|
|
72
107
|
def supports_timestamp_timezones?
|
|
73
108
|
false
|
|
@@ -82,5 +117,19 @@ module Sequel
|
|
|
82
117
|
def supports_window_functions?
|
|
83
118
|
false
|
|
84
119
|
end
|
|
120
|
+
|
|
121
|
+
# Whether the dataset supports WHERE TRUE (or WHERE 1 for databases that
|
|
122
|
+
# that use 1 for true).
|
|
123
|
+
def supports_where_true?
|
|
124
|
+
true
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
private
|
|
128
|
+
|
|
129
|
+
# Whether the RETURNING clause is used for the given dataset.
|
|
130
|
+
# +type+ can be :insert, :update, or :delete.
|
|
131
|
+
def uses_returning?(type)
|
|
132
|
+
opts[:returning] && !@opts[:sql] && supports_returning?(type)
|
|
133
|
+
end
|
|
85
134
|
end
|
|
86
135
|
end
|
data/lib/sequel/dataset/graph.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module Sequel
|
|
2
2
|
class Dataset
|
|
3
3
|
# ---------------------
|
|
4
|
-
# :section: Methods related to dataset graphing
|
|
4
|
+
# :section: 5 - Methods related to dataset graphing
|
|
5
5
|
# Dataset graphing changes the dataset to yield hashes where keys are table
|
|
6
6
|
# name symbols and values are hashes representing the columns related to
|
|
7
7
|
# that table. All of these methods return modified copies of the receiver.
|