sequel 3.12.1 → 3.13.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +42 -0
- data/README.rdoc +137 -118
- data/Rakefile +21 -66
- data/doc/active_record.rdoc +9 -9
- data/doc/advanced_associations.rdoc +59 -188
- data/doc/association_basics.rdoc +15 -2
- data/doc/cheat_sheet.rdoc +38 -33
- data/doc/dataset_filtering.rdoc +16 -7
- data/doc/prepared_statements.rdoc +7 -7
- data/doc/querying.rdoc +5 -4
- data/doc/release_notes/3.13.0.txt +210 -0
- data/doc/sharding.rdoc +1 -1
- data/doc/sql.rdoc +5 -5
- data/doc/validations.rdoc +11 -11
- data/lib/sequel/adapters/ado.rb +1 -1
- data/lib/sequel/adapters/do.rb +3 -3
- data/lib/sequel/adapters/firebird.rb +3 -3
- data/lib/sequel/adapters/jdbc/h2.rb +39 -0
- data/lib/sequel/adapters/jdbc/mysql.rb +5 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +3 -3
- data/lib/sequel/adapters/mysql.rb +7 -4
- data/lib/sequel/adapters/oracle.rb +3 -3
- data/lib/sequel/adapters/shared/mssql.rb +10 -1
- data/lib/sequel/adapters/shared/mysql.rb +63 -0
- data/lib/sequel/adapters/shared/postgres.rb +61 -3
- data/lib/sequel/adapters/sqlite.rb +105 -18
- data/lib/sequel/connection_pool.rb +31 -30
- data/lib/sequel/core.rb +58 -58
- data/lib/sequel/core_sql.rb +52 -43
- data/lib/sequel/database/misc.rb +11 -0
- data/lib/sequel/database/query.rb +55 -17
- data/lib/sequel/dataset/actions.rb +2 -1
- data/lib/sequel/dataset/query.rb +2 -3
- data/lib/sequel/dataset/sql.rb +24 -11
- data/lib/sequel/extensions/schema_dumper.rb +1 -1
- data/lib/sequel/metaprogramming.rb +4 -0
- data/lib/sequel/model.rb +37 -19
- data/lib/sequel/model/associations.rb +33 -25
- data/lib/sequel/model/base.rb +2 -2
- data/lib/sequel/model/plugins.rb +7 -2
- data/lib/sequel/plugins/active_model.rb +1 -1
- data/lib/sequel/plugins/association_pks.rb +2 -2
- data/lib/sequel/plugins/association_proxies.rb +1 -1
- data/lib/sequel/plugins/boolean_readers.rb +2 -2
- data/lib/sequel/plugins/class_table_inheritance.rb +10 -2
- data/lib/sequel/plugins/identity_map.rb +3 -3
- data/lib/sequel/plugins/instance_hooks.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +212 -0
- data/lib/sequel/plugins/lazy_attributes.rb +1 -1
- data/lib/sequel/plugins/list.rb +174 -0
- data/lib/sequel/plugins/many_through_many.rb +2 -2
- data/lib/sequel/plugins/rcte_tree.rb +6 -7
- data/lib/sequel/plugins/tree.rb +118 -0
- data/lib/sequel/plugins/xml_serializer.rb +321 -0
- data/lib/sequel/sql.rb +315 -206
- data/lib/sequel/timezones.rb +40 -17
- data/lib/sequel/version.rb +8 -2
- data/spec/adapters/firebird_spec.rb +2 -2
- data/spec/adapters/informix_spec.rb +1 -1
- data/spec/adapters/mssql_spec.rb +2 -2
- data/spec/adapters/mysql_spec.rb +2 -2
- data/spec/adapters/oracle_spec.rb +1 -1
- data/spec/adapters/postgres_spec.rb +36 -6
- data/spec/adapters/spec_helper.rb +2 -2
- data/spec/adapters/sqlite_spec.rb +1 -1
- data/spec/core/connection_pool_spec.rb +3 -3
- data/spec/core/core_sql_spec.rb +31 -13
- data/spec/core/database_spec.rb +39 -2
- data/spec/core/dataset_spec.rb +24 -12
- data/spec/core/expression_filters_spec.rb +5 -1
- data/spec/core/object_graph_spec.rb +1 -1
- data/spec/core/schema_generator_spec.rb +1 -1
- data/spec/core/schema_spec.rb +1 -1
- data/spec/core/spec_helper.rb +1 -1
- data/spec/core/version_spec.rb +1 -1
- data/spec/extensions/active_model_spec.rb +82 -67
- data/spec/extensions/association_dependencies_spec.rb +1 -1
- data/spec/extensions/association_pks_spec.rb +1 -1
- data/spec/extensions/association_proxies_spec.rb +1 -1
- data/spec/extensions/blank_spec.rb +1 -1
- data/spec/extensions/boolean_readers_spec.rb +1 -1
- data/spec/extensions/caching_spec.rb +1 -1
- data/spec/extensions/class_table_inheritance_spec.rb +3 -2
- data/spec/extensions/composition_spec.rb +2 -5
- data/spec/extensions/force_encoding_spec.rb +3 -1
- data/spec/extensions/hook_class_methods_spec.rb +1 -1
- data/spec/extensions/identity_map_spec.rb +1 -1
- data/spec/extensions/inflector_spec.rb +1 -1
- data/spec/extensions/instance_filters_spec.rb +1 -1
- data/spec/extensions/instance_hooks_spec.rb +1 -1
- data/spec/extensions/json_serializer_spec.rb +154 -0
- data/spec/extensions/lazy_attributes_spec.rb +1 -2
- data/spec/extensions/list_spec.rb +251 -0
- data/spec/extensions/looser_typecasting_spec.rb +1 -1
- data/spec/extensions/many_through_many_spec.rb +3 -3
- data/spec/extensions/migration_spec.rb +1 -1
- data/spec/extensions/named_timezones_spec.rb +5 -6
- data/spec/extensions/nested_attributes_spec.rb +1 -1
- data/spec/extensions/optimistic_locking_spec.rb +1 -1
- data/spec/extensions/pagination_spec.rb +1 -1
- data/spec/extensions/pretty_table_spec.rb +1 -1
- data/spec/extensions/query_spec.rb +1 -1
- data/spec/extensions/rcte_tree_spec.rb +1 -1
- data/spec/extensions/schema_dumper_spec.rb +3 -2
- data/spec/extensions/schema_spec.rb +1 -1
- data/spec/extensions/serialization_spec.rb +6 -2
- data/spec/extensions/sharding_spec.rb +1 -1
- data/spec/extensions/single_table_inheritance_spec.rb +1 -1
- data/spec/extensions/skip_create_refresh_spec.rb +1 -1
- data/spec/extensions/spec_helper.rb +7 -3
- data/spec/extensions/sql_expr_spec.rb +1 -1
- data/spec/extensions/string_date_time_spec.rb +1 -1
- data/spec/extensions/string_stripper_spec.rb +1 -1
- data/spec/extensions/subclasses_spec.rb +1 -1
- data/spec/extensions/tactical_eager_loading_spec.rb +1 -1
- data/spec/extensions/thread_local_timezones_spec.rb +1 -1
- data/spec/extensions/timestamps_spec.rb +1 -1
- data/spec/extensions/touch_spec.rb +1 -1
- data/spec/extensions/tree_spec.rb +119 -0
- data/spec/extensions/typecast_on_load_spec.rb +1 -1
- data/spec/extensions/update_primary_key_spec.rb +1 -1
- data/spec/extensions/validation_class_methods_spec.rb +1 -1
- data/spec/extensions/validation_helpers_spec.rb +1 -1
- data/spec/extensions/xml_serializer_spec.rb +142 -0
- data/spec/integration/associations_test.rb +1 -1
- data/spec/integration/database_test.rb +1 -1
- data/spec/integration/dataset_test.rb +29 -14
- data/spec/integration/eager_loader_test.rb +1 -1
- data/spec/integration/migrator_test.rb +1 -1
- data/spec/integration/model_test.rb +1 -1
- data/spec/integration/plugin_test.rb +316 -1
- data/spec/integration/prepared_statement_test.rb +1 -1
- data/spec/integration/schema_test.rb +8 -8
- data/spec/integration/spec_helper.rb +1 -1
- data/spec/integration/timezone_test.rb +1 -1
- data/spec/integration/transaction_test.rb +35 -20
- data/spec/integration/type_test.rb +1 -1
- data/spec/model/association_reflection_spec.rb +1 -1
- data/spec/model/associations_spec.rb +49 -34
- data/spec/model/base_spec.rb +1 -1
- data/spec/model/dataset_methods_spec.rb +4 -4
- data/spec/model/eager_loading_spec.rb +1 -1
- data/spec/model/hooks_spec.rb +1 -1
- data/spec/model/inflector_spec.rb +1 -1
- data/spec/model/model_spec.rb +7 -1
- data/spec/model/plugins_spec.rb +1 -1
- data/spec/model/record_spec.rb +1 -3
- data/spec/model/spec_helper.rb +2 -2
- data/spec/model/validations_spec.rb +1 -1
- metadata +29 -5
@@ -157,10 +157,17 @@ module Sequel
|
|
157
157
|
|
158
158
|
# Methods shared by Database instances that connect to PostgreSQL.
|
159
159
|
module DatabaseMethods
|
160
|
+
EXCLUDE_SCHEMAS = %w[pg_catalog pg_toast pg_temp_1 pg_toast_temp_1 information_schema]
|
160
161
|
PREPARED_ARG_PLACEHOLDER = LiteralString.new('$').freeze
|
161
162
|
RE_CURRVAL_ERROR = /currval of sequence "(.*)" is not yet defined in this session|relation "(.*)" does not exist/.freeze
|
162
163
|
SYSTEM_TABLE_REGEXP = /^pg|sql/.freeze
|
163
164
|
|
165
|
+
# Commit an existing prepared transaction with the given transaction
|
166
|
+
# identifier string.
|
167
|
+
def commit_prepared_transaction(transaction_id)
|
168
|
+
run("COMMIT PREPARED #{literal(transaction_id)}")
|
169
|
+
end
|
170
|
+
|
164
171
|
# Creates the function in the database. Arguments:
|
165
172
|
# * name : name of the function to create
|
166
173
|
# * definition : string definition of the function, or object file for a dynamically loaded C function.
|
@@ -318,6 +325,12 @@ module Sequel
|
|
318
325
|
get{setval(seq, db[table].select{coalesce(max(pk)+seq_ds.select{:increment_by}, seq_ds.select(:min_value))}, false)}
|
319
326
|
end
|
320
327
|
|
328
|
+
# Rollback an existing prepared transaction with the given transaction
|
329
|
+
# identifier string.
|
330
|
+
def rollback_prepared_transaction(transaction_id)
|
331
|
+
run("ROLLBACK PREPARED #{literal(transaction_id)}")
|
332
|
+
end
|
333
|
+
|
321
334
|
# PostgreSQL uses SERIAL psuedo-type instead of AUTOINCREMENT for
|
322
335
|
# managing incrementing primary keys.
|
323
336
|
def serial_primary_key_options
|
@@ -337,11 +350,23 @@ module Sequel
|
|
337
350
|
@server_version
|
338
351
|
end
|
339
352
|
|
353
|
+
# PostgreSQL supports prepared transactions (two-phase commit) if
|
354
|
+
# max_prepared_transactions is greater than 0.
|
355
|
+
def supports_prepared_transactions?
|
356
|
+
return @supports_prepared_transactions if defined?(@supports_prepared_transactions)
|
357
|
+
@supports_prepared_transactions = self['SHOW max_prepared_transactions'].get.to_i > 0
|
358
|
+
end
|
359
|
+
|
340
360
|
# PostgreSQL supports savepoints
|
341
361
|
def supports_savepoints?
|
342
362
|
true
|
343
363
|
end
|
344
364
|
|
365
|
+
# PostgreSQL supports transaction isolation levels
|
366
|
+
def supports_transaction_isolation_levels?
|
367
|
+
true
|
368
|
+
end
|
369
|
+
|
345
370
|
# Whether the given table exists in the database
|
346
371
|
#
|
347
372
|
# Options:
|
@@ -362,13 +387,24 @@ module Sequel
|
|
362
387
|
# * :schema - The schema to search (default_schema by default)
|
363
388
|
# * :server - The server to use
|
364
389
|
def tables(opts={})
|
365
|
-
ds = metadata_dataset.from(:pg_class).filter(:relkind=>'r').select(:relname).exclude(SQL::StringExpression.like(:relname, SYSTEM_TABLE_REGEXP)).server(opts[:server]).join(:pg_namespace, :oid=>:relnamespace
|
390
|
+
ds = metadata_dataset.from(:pg_class).filter(:relkind=>'r').select(:relname).exclude(SQL::StringExpression.like(:relname, SYSTEM_TABLE_REGEXP)).server(opts[:server]).join(:pg_namespace, :oid=>:relnamespace)
|
391
|
+
ds = filter_schema(ds, opts)
|
366
392
|
m = output_identifier_meth
|
367
393
|
block_given? ? yield(ds) : ds.map{|r| m.call(r[:relname])}
|
368
394
|
end
|
369
395
|
|
370
396
|
private
|
371
|
-
|
397
|
+
|
398
|
+
# If the :prepare option is given and we aren't in a savepoint,
|
399
|
+
# prepare the transaction for a two-phase commit.
|
400
|
+
def commit_transaction(conn, opts={})
|
401
|
+
if opts[:prepare] && Thread.current[:sequel_transaction_depth] <= 1
|
402
|
+
log_connection_execute(conn, "PREPARE TRANSACTION #{literal(opts[:prepare])}")
|
403
|
+
else
|
404
|
+
super
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
372
408
|
# SQL statement to create database function.
|
373
409
|
def create_function_sql(name, definition, opts={})
|
374
410
|
args = opts[:args]
|
@@ -427,6 +463,16 @@ module Sequel
|
|
427
463
|
"DROP TRIGGER#{' IF EXISTS' if opts[:if_exists]} #{name} ON #{quote_schema_table(table)}#{' CASCADE' if opts[:cascade]}"
|
428
464
|
end
|
429
465
|
|
466
|
+
# If opts includes a :schema option, or a default schema is used, restrict the dataset to
|
467
|
+
# that schema. Otherwise, just exclude the default PostgreSQL schemas except for public.
|
468
|
+
def filter_schema(ds, opts)
|
469
|
+
if schema = opts[:schema] || default_schema
|
470
|
+
ds.filter(:pg_namespace__nspname=>schema.to_s)
|
471
|
+
else
|
472
|
+
ds.exclude(:pg_namespace__nspname=>EXCLUDE_SCHEMAS)
|
473
|
+
end
|
474
|
+
end
|
475
|
+
|
430
476
|
# PostgreSQL folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on input.
|
431
477
|
def identifier_input_method_default
|
432
478
|
nil
|
@@ -523,13 +569,14 @@ module Sequel
|
|
523
569
|
from(:pg_class).
|
524
570
|
join(:pg_attribute, :attrelid=>:oid).
|
525
571
|
join(:pg_type, :oid=>:atttypid).
|
572
|
+
join(:pg_namespace, :oid=>:pg_class__relnamespace).
|
526
573
|
left_outer_join(:pg_attrdef, :adrelid=>:pg_class__oid, :adnum=>:pg_attribute__attnum).
|
527
574
|
left_outer_join(:pg_index, :indrelid=>:pg_class__oid, :indisprimary=>true).
|
528
575
|
filter(:pg_attribute__attisdropped=>false).
|
529
576
|
filter{|o| o.pg_attribute__attnum > 0}.
|
530
577
|
filter(:pg_class__relname=>m2.call(table_name)).
|
531
578
|
order(:pg_attribute__attnum)
|
532
|
-
ds
|
579
|
+
ds = filter_schema(ds, opts)
|
533
580
|
ds.map do |row|
|
534
581
|
row[:default] = nil if blank_object?(row[:default])
|
535
582
|
row[:type] = schema_column_type(row[:db_type])
|
@@ -627,6 +674,17 @@ module Sequel
|
|
627
674
|
explain(:analyze=>true)
|
628
675
|
end
|
629
676
|
|
677
|
+
# Handle converting the ruby xor operator (^) into the
|
678
|
+
# PostgreSQL xor operator (#).
|
679
|
+
def complex_expression_sql(op, args)
|
680
|
+
case op
|
681
|
+
when :^
|
682
|
+
"(#{literal(args.at(0))} # #{literal(args.at(1))})"
|
683
|
+
else
|
684
|
+
super
|
685
|
+
end
|
686
|
+
end
|
687
|
+
|
630
688
|
# Disable the use of INSERT RETURNING, even if the server supports it
|
631
689
|
def disable_insert_returning
|
632
690
|
clone(:disable_insert_returning=>true)
|
@@ -61,6 +61,11 @@ module Sequel
|
|
61
61
|
|
62
62
|
# Handle blob values with Sequel::SQL::Blob
|
63
63
|
db.translator.add_translator("blob"){|t,v| ::Sequel::SQL::Blob.new(v)}
|
64
|
+
|
65
|
+
class << db
|
66
|
+
attr_reader :prepared_statements
|
67
|
+
end
|
68
|
+
db.instance_variable_set(:@prepared_statements, {})
|
64
69
|
|
65
70
|
db
|
66
71
|
end
|
@@ -70,39 +75,60 @@ module Sequel
|
|
70
75
|
SQLite::Dataset.new(self, opts)
|
71
76
|
end
|
72
77
|
|
78
|
+
# Run the given SQL with the given arguments and yield each row.
|
79
|
+
def execute(sql, opts={}, &block)
|
80
|
+
_execute(:select, sql, opts, &block)
|
81
|
+
end
|
82
|
+
|
73
83
|
# Run the given SQL with the given arguments and return the number of changed rows.
|
74
84
|
def execute_dui(sql, opts={})
|
75
|
-
_execute(
|
85
|
+
_execute(:update, sql, opts)
|
76
86
|
end
|
77
87
|
|
78
|
-
#
|
79
|
-
|
80
|
-
|
88
|
+
# Drop any prepared statements on the connection when executing DDL. This is because
|
89
|
+
# prepared statements lock the table in such a way that you can't drop or alter the
|
90
|
+
# table while a prepared statement that references it still exists.
|
91
|
+
def execute_ddl(sql, opts={})
|
92
|
+
synchronize(opts[:server]) do |conn|
|
93
|
+
conn.prepared_statements.values.each{|cps, s| cps.close}
|
94
|
+
conn.prepared_statements.clear
|
95
|
+
super
|
96
|
+
end
|
81
97
|
end
|
82
98
|
|
83
|
-
# Run the given SQL with the given arguments and
|
84
|
-
def
|
85
|
-
_execute(opts)
|
86
|
-
begin
|
87
|
-
yield(result = log_yield(sql, opts[:arguments]){conn.query(sql, opts.fetch(:arguments, []))})
|
88
|
-
ensure
|
89
|
-
result.close if result
|
90
|
-
end
|
91
|
-
end
|
99
|
+
# Run the given SQL with the given arguments and return the last inserted row id.
|
100
|
+
def execute_insert(sql, opts={})
|
101
|
+
_execute(:insert, sql, opts)
|
92
102
|
end
|
93
103
|
|
94
104
|
# Run the given SQL with the given arguments and return the first value of the first row.
|
95
105
|
def single_value(sql, opts={})
|
96
|
-
_execute(
|
106
|
+
_execute(:single_value, sql, opts)
|
97
107
|
end
|
98
108
|
|
99
109
|
private
|
100
110
|
|
101
111
|
# Yield an available connection. Rescue
|
102
112
|
# any SQLite3::Exceptions and turn them into DatabaseErrors.
|
103
|
-
def _execute(opts, &block)
|
113
|
+
def _execute(type, sql, opts, &block)
|
104
114
|
begin
|
105
|
-
synchronize(opts[:server]
|
115
|
+
synchronize(opts[:server]) do |conn|
|
116
|
+
return execute_prepared_statement(conn, type, sql, opts, &block) if sql.is_a?(Symbol)
|
117
|
+
log_args = opts[:arguments]
|
118
|
+
args = opts.fetch(:arguments, [])
|
119
|
+
case type
|
120
|
+
when :select
|
121
|
+
log_yield(sql, log_args){conn.query(sql, args, &block)}
|
122
|
+
when :single_value
|
123
|
+
log_yield(sql, log_args){conn.get_first_value(sql, args)}
|
124
|
+
when :insert
|
125
|
+
log_yield(sql, log_args){conn.execute(sql, args)}
|
126
|
+
conn.last_insert_row_id
|
127
|
+
when :update
|
128
|
+
log_yield(sql, log_args){conn.execute_batch(sql, args)}
|
129
|
+
conn.changes
|
130
|
+
end
|
131
|
+
end
|
106
132
|
rescue SQLite3::Exception => e
|
107
133
|
raise_error(e)
|
108
134
|
end
|
@@ -119,6 +145,35 @@ module Sequel
|
|
119
145
|
o
|
120
146
|
end
|
121
147
|
|
148
|
+
# Execute a prepared statement on the database using the given name.
|
149
|
+
def execute_prepared_statement(conn, type, name, opts, &block)
|
150
|
+
ps = prepared_statements[name]
|
151
|
+
sql = ps.prepared_sql
|
152
|
+
args = opts[:arguments]
|
153
|
+
if cpsa = conn.prepared_statements[name]
|
154
|
+
cps, cps_sql = cpsa
|
155
|
+
if cps_sql != sql
|
156
|
+
cps.close
|
157
|
+
cps = nil
|
158
|
+
end
|
159
|
+
end
|
160
|
+
unless cps
|
161
|
+
cps = conn.prepare(sql)
|
162
|
+
conn.prepared_statements[name] = [cps, sql]
|
163
|
+
end
|
164
|
+
if block
|
165
|
+
log_yield("Executing prepared statement #{name}", args){cps.execute(args, &block)}
|
166
|
+
else
|
167
|
+
log_yield("Executing prepared statement #{name}", args){cps.execute!(args){|r|}}
|
168
|
+
case type
|
169
|
+
when :insert
|
170
|
+
conn.last_insert_row_id
|
171
|
+
when :update
|
172
|
+
conn.changes
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
122
177
|
# The main error class that SQLite3 raises
|
123
178
|
def database_error_classes
|
124
179
|
[SQLite3::Exception]
|
@@ -161,7 +216,7 @@ module Sequel
|
|
161
216
|
|
162
217
|
# SQLite prepared statement uses a new prepared statement each time
|
163
218
|
# it is called, but it does use the bind arguments.
|
164
|
-
module
|
219
|
+
module BindArgumentMethods
|
165
220
|
include ArgumentMapper
|
166
221
|
|
167
222
|
private
|
@@ -182,6 +237,35 @@ module Sequel
|
|
182
237
|
super(sql, {:arguments=>bind_arguments}.merge(opts), &block)
|
183
238
|
end
|
184
239
|
end
|
240
|
+
|
241
|
+
module PreparedStatementMethods
|
242
|
+
include BindArgumentMethods
|
243
|
+
|
244
|
+
private
|
245
|
+
|
246
|
+
# Execute the stored prepared statement name and the stored bind
|
247
|
+
# arguments instead of the SQL given.
|
248
|
+
def execute(sql, opts={}, &block)
|
249
|
+
super(prepared_statement_name, opts, &block)
|
250
|
+
end
|
251
|
+
|
252
|
+
# Same as execute, explicit due to intricacies of alias and super.
|
253
|
+
def execute_dui(sql, opts={}, &block)
|
254
|
+
super(prepared_statement_name, opts, &block)
|
255
|
+
end
|
256
|
+
|
257
|
+
# Same as execute, explicit due to intricacies of alias and super.
|
258
|
+
def execute_insert(sql, opts={}, &block)
|
259
|
+
super(prepared_statement_name, opts, &block)
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
# Execute the given type of statement with the hash of values.
|
264
|
+
def call(type, bind_vars={}, *values, &block)
|
265
|
+
ps = to_prepared_statement(type, values)
|
266
|
+
ps.extend(BindArgumentMethods)
|
267
|
+
ps.call(bind_vars, &block)
|
268
|
+
end
|
185
269
|
|
186
270
|
# Yield a hash for each row in the dataset.
|
187
271
|
def fetch_rows(sql)
|
@@ -203,7 +287,10 @@ module Sequel
|
|
203
287
|
def prepare(type, name=nil, *values)
|
204
288
|
ps = to_prepared_statement(type, values)
|
205
289
|
ps.extend(PreparedStatementMethods)
|
206
|
-
|
290
|
+
if name
|
291
|
+
ps.prepared_statement_name = name
|
292
|
+
db.prepared_statements[name] = ps
|
293
|
+
end
|
207
294
|
ps.prepared_sql
|
208
295
|
ps
|
209
296
|
end
|
@@ -1,31 +1,32 @@
|
|
1
|
-
# The base connection pool class, which all other connection pools are
|
1
|
+
# The base connection pool class, which all other connection pools are based
|
2
2
|
# on. This class is not instantiated directly, but subclasses should at
|
3
3
|
# the very least implement the following API:
|
4
|
-
# * initialize(Hash, &block) - The block is used as the connection proc,
|
5
|
-
# which should accept a single symbol argument.
|
6
|
-
# * hold(Symbol, &block) - yield a connection object (obtained from calling
|
7
|
-
# the block passed to initialize) to the current block. For sharded
|
8
|
-
# connection pools, the Symbol passed is the shard/server to use.
|
9
|
-
# * disconnect(Symbol, &block) - disconnect the connection object. If a
|
10
|
-
# block is given, pass the connection option to it, otherwise use the
|
11
|
-
# :disconnection_proc option in the hash passed to initialize. For sharded
|
12
|
-
# connection pools, the Symbol passed is the shard/server to use.
|
13
|
-
# * servers - an array of shard/server symbols for all shards/servers that this
|
14
|
-
# connection pool recognizes.
|
15
|
-
# * size - an integer representing the total number of connections in the pool,
|
16
|
-
# or for the given shard/server if sharding is supported.
|
17
4
|
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
5
|
+
# initialize(Hash, &block) :: The +block+ is used as the connection proc,
|
6
|
+
# which should accept a single symbol argument.
|
7
|
+
# hold(Symbol, &block) :: Yield a connection object (obtained from calling
|
8
|
+
# the block passed to +initialize+) to the current block. For sharded
|
9
|
+
# connection pools, the Symbol passed is the shard/server to use.
|
10
|
+
# disconnect(Symbol, &block) :: Disconnect the connection object. If a
|
11
|
+
# block is given, pass the connection option to it, otherwise use the
|
12
|
+
# <tt>:disconnection_proc</tt> option in the hash passed to initialize. For sharded
|
13
|
+
# connection pools, the Symbol passed is the shard/server to use.
|
14
|
+
# servers :: An array of shard/server symbols for all shards/servers that this
|
15
|
+
# connection pool recognizes.
|
16
|
+
# size :: an integer representing the total number of connections in the pool,
|
17
|
+
# or for the given shard/server if sharding is supported.
|
18
|
+
#
|
19
|
+
# For sharded connection pools, the sharded API adds the following methods:
|
20
|
+
#
|
21
|
+
# add_servers(Array of Symbols) :: start recognizing all shards/servers specified
|
22
|
+
# by the array of symbols.
|
23
|
+
# * remove_servers(Array of Symbols) :: no longer recognize all shards/servers
|
24
|
+
# specified by the array of symbols.
|
23
25
|
class Sequel::ConnectionPool
|
24
26
|
# The default server to use
|
25
27
|
DEFAULT_SERVER = :default
|
26
28
|
|
27
|
-
# A map of [single threaded, sharded] values to
|
28
|
-
# be required) ConnectionPool subclasses.
|
29
|
+
# A map of [single threaded, sharded] values to symbols or ConnectionPool subclasses.
|
29
30
|
CONNECTION_POOL_MAP = {[true, false] => :single,
|
30
31
|
[true, true] => :sharded_single,
|
31
32
|
[false, false] => :threaded,
|
@@ -34,10 +35,10 @@ class Sequel::ConnectionPool
|
|
34
35
|
# Class methods used to return an appropriate pool subclass, separated
|
35
36
|
# into a module for easier overridding by extensions.
|
36
37
|
module ClassMethods
|
37
|
-
# Return a pool subclass instance based on the given options. If a
|
38
|
+
# Return a pool subclass instance based on the given options. If a <tt>:pool_class</tt>
|
38
39
|
# option is provided is provided, use that pool class, otherwise
|
39
40
|
# use a new instance of an appropriate pool subclass based on the
|
40
|
-
#
|
41
|
+
# <tt>:single_threaded</tt> and <tt>:servers</tt> options.
|
41
42
|
def get_pool(opts = {}, &block)
|
42
43
|
case v = connection_pool_class(opts)
|
43
44
|
when Class
|
@@ -61,23 +62,23 @@ class Sequel::ConnectionPool
|
|
61
62
|
# with a single symbol (specifying the server/shard to use) every time a new
|
62
63
|
# connection is needed. The following options are respected for all connection
|
63
64
|
# pools:
|
64
|
-
#
|
65
|
-
#
|
66
|
-
#
|
67
|
-
#
|
68
|
-
#
|
65
|
+
# :after_connect :: The proc called after each new connection is made, with the
|
66
|
+
# connection object, useful for customizations that you want to apply to all
|
67
|
+
# connections.
|
68
|
+
# :disconnection_proc :: The proc called when removing connections from the pool,
|
69
|
+
# which is passed the connection to disconnect.
|
69
70
|
def initialize(opts={}, &block)
|
70
71
|
raise(Sequel::Error, "No connection proc specified") unless @connection_proc = block
|
71
72
|
@disconnection_proc = opts[:disconnection_proc]
|
72
73
|
@after_connect = opts[:after_connect]
|
73
74
|
end
|
74
75
|
|
75
|
-
# Alias for size
|
76
|
+
# Alias for +size+, not aliased directly for ease of subclass implementation
|
76
77
|
def created_count(*args)
|
77
78
|
size(*args)
|
78
79
|
end
|
79
80
|
|
80
|
-
# An array of symbols for all shards/servers, which is a single
|
81
|
+
# An array of symbols for all shards/servers, which is a single <tt>:default</tt> by default.
|
81
82
|
def servers
|
82
83
|
[:default]
|
83
84
|
end
|
data/lib/sequel/core.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
# Top level module for Sequel
|
4
4
|
#
|
5
|
-
# There are some
|
5
|
+
# There are some module methods that are added via metaprogramming, one for
|
6
6
|
# each supported adapter. For example:
|
7
7
|
#
|
8
8
|
# DB = Sequel.sqlite # Memory database
|
@@ -17,34 +17,7 @@
|
|
17
17
|
#
|
18
18
|
# Sequel.sqlite('blog.db'){|db| puts db[:users].count}
|
19
19
|
#
|
20
|
-
#
|
21
|
-
# handle timezones if you want. There are three separate timezone settings:
|
22
|
-
#
|
23
|
-
# * application_timezone - The timezone you want the application to use. This is
|
24
|
-
# the timezone that incoming times from the database and typecasting are converted
|
25
|
-
# to.
|
26
|
-
# * database_timezone - The timezone for storage in the database. This is the
|
27
|
-
# timezone to which Sequel will convert timestamps before literalizing them
|
28
|
-
# for storage in the database. It is also the timezone that Sequel will assume
|
29
|
-
# database timestamp values are already in (if they don't include an offset).
|
30
|
-
# * typecast_timezone - The timezone that incoming data that Sequel needs to typecast
|
31
|
-
# is assumed to be already in (if they don't include an offset).
|
32
|
-
#
|
33
|
-
# You can set also set all three timezones to the same value at once via
|
34
|
-
# Sequel.default_timezone=.
|
35
|
-
#
|
36
|
-
# Sequel.application_timezone = :utc # or :local or nil
|
37
|
-
# Sequel.database_timezone = :utc # or :local or nil
|
38
|
-
# Sequel.typecast_timezone = :utc # or :local or nil
|
39
|
-
# Sequel.default_timezone = :utc # or :local or nil
|
40
|
-
#
|
41
|
-
# The only timezone values that are supported by default are :utc (convert to UTC),
|
42
|
-
# :local (convert to local time), and nil (don't convert). If you need to
|
43
|
-
# convert to a specific timezone, or need the timezones being used to change based
|
44
|
-
# on the environment (e.g. current user), you need to use an extension (and use
|
45
|
-
# DateTime as the datetime_class).
|
46
|
-
#
|
47
|
-
# You can set the SEQUEL_NO_CORE_EXTENSIONS constant or environment variable to have
|
20
|
+
# You can set the +SEQUEL_NO_CORE_EXTENSIONS+ constant or environment variable to have
|
48
21
|
# Sequel not extend the core classes.
|
49
22
|
#
|
50
23
|
# For a more expanded introduction, see the {README}[link:files/README_rdoc.html].
|
@@ -55,20 +28,25 @@ module Sequel
|
|
55
28
|
@virtual_row_instance_eval = true
|
56
29
|
@require_thread = nil
|
57
30
|
|
58
|
-
# Mutex used to protect file loading
|
31
|
+
# Mutex used to protect file loading/requireing
|
59
32
|
@require_mutex = Mutex.new
|
60
33
|
|
61
34
|
class << self
|
62
|
-
# Sequel converts two digit years in
|
35
|
+
# Sequel converts two digit years in <tt>Date</tt>s and <tt>DateTime</tt>s by default,
|
63
36
|
# so 01/02/03 is interpreted at January 2nd, 2003, and 12/13/99 is interpreted
|
64
37
|
# as December 13, 1999. You can override this to treat those dates as
|
65
|
-
# January 2nd, 0003 and December 13, 0099, respectively, by
|
38
|
+
# January 2nd, 0003 and December 13, 0099, respectively, by:
|
39
|
+
#
|
40
|
+
# Sequel.convert_two_digit_years = false
|
66
41
|
attr_accessor :convert_two_digit_years
|
67
42
|
|
68
|
-
# Sequel can use either Time or DateTime for times returned from the
|
69
|
-
# database. It defaults to Time
|
43
|
+
# Sequel can use either +Time+ or +DateTime+ for times returned from the
|
44
|
+
# database. It defaults to +Time+. To change it to +DateTime+:
|
45
|
+
#
|
46
|
+
# Sequel.datetime_class = DateTime
|
70
47
|
attr_accessor :datetime_class
|
71
48
|
|
49
|
+
# For backwards compatibility, has no effect.
|
72
50
|
attr_accessor :virtual_row_instance_eval
|
73
51
|
|
74
52
|
# Alias to the standard version of require
|
@@ -92,14 +70,20 @@ module Sequel
|
|
92
70
|
end
|
93
71
|
|
94
72
|
# Returns true if the passed object could be a specifier of conditions, false otherwise.
|
95
|
-
# Currently, Sequel considers hashes and arrays of
|
73
|
+
# Currently, Sequel considers hashes and arrays of two element arrays as
|
96
74
|
# condition specifiers.
|
75
|
+
#
|
76
|
+
# Sequel.condition_specifier?({}) # => true
|
77
|
+
# Sequel.condition_specifier?([[1, 2]]) # => true
|
78
|
+
# Sequel.condition_specifier?([]) # => false
|
79
|
+
# Sequel.condition_specifier?([1]) # => false
|
80
|
+
# Sequel.condition_specifier?(1) # => false
|
97
81
|
def self.condition_specifier?(obj)
|
98
82
|
case obj
|
99
83
|
when Hash
|
100
84
|
true
|
101
85
|
when Array
|
102
|
-
!obj.empty? && obj.all?{|i| (Array === i) && (i.length == 2)}
|
86
|
+
!obj.empty? && !obj.is_a?(SQL::ValueList) && obj.all?{|i| (Array === i) && (i.length == 2)}
|
103
87
|
else
|
104
88
|
false
|
105
89
|
end
|
@@ -116,7 +100,7 @@ module Sequel
|
|
116
100
|
# DB = Sequel.connect('postgres://user:password@host:port/database_name')
|
117
101
|
# DB = Sequel.connect('sqlite:///blog.db', :max_connections=>10)
|
118
102
|
#
|
119
|
-
# If a block is given, it is passed the opened Database object, which is
|
103
|
+
# If a block is given, it is passed the opened +Database+ object, which is
|
120
104
|
# closed when the block exits. For example:
|
121
105
|
#
|
122
106
|
# Sequel.connect('sqlite://blog.db'){|db| puts db[:users].count}
|
@@ -127,9 +111,9 @@ module Sequel
|
|
127
111
|
Database.connect(*args, &block)
|
128
112
|
end
|
129
113
|
|
130
|
-
# Convert the exception to the given class. The given class should be
|
131
|
-
# Sequel::Error or a subclass. Returns an instance of klass with
|
132
|
-
# the message and backtrace of exception
|
114
|
+
# Convert the +exception+ to the given class. The given class should be
|
115
|
+
# <tt>Sequel::Error</tt> or a subclass. Returns an instance of +klass+ with
|
116
|
+
# the message and backtrace of +exception+.
|
133
117
|
def self.convert_exception_class(exception, klass)
|
134
118
|
return exception if exception.is_a?(klass)
|
135
119
|
e = klass.new("#{exception.class}: #{exception.message}")
|
@@ -138,8 +122,13 @@ module Sequel
|
|
138
122
|
e
|
139
123
|
end
|
140
124
|
|
141
|
-
# Load all Sequel extensions given.
|
142
|
-
#
|
125
|
+
# Load all Sequel extensions given. Extensions are just files that exist under
|
126
|
+
# <tt>sequel/extensions</tt> in the load path, and are just required. Generally,
|
127
|
+
# extensions modify the behavior of +Database+ and/or +Dataset+, but Sequel ships
|
128
|
+
# with some extensions that modify other classes that exist for backwards compatibility.
|
129
|
+
# In some cases, requiring an extension modifies classes directly, and in others,
|
130
|
+
# it just loads a module that you can extend other classes with. Consult the documentation
|
131
|
+
# for each extension you plan on using for usage.
|
143
132
|
#
|
144
133
|
# Sequel.extension(:schema_dumper)
|
145
134
|
# Sequel.extension(:pagination, :query)
|
@@ -186,14 +175,16 @@ module Sequel
|
|
186
175
|
Database.quote_identifiers = value
|
187
176
|
end
|
188
177
|
|
189
|
-
# Require all given files which should be in the same or a subdirectory of
|
190
|
-
# this file. If a subdir is given, assume all files are in that subdir.
|
178
|
+
# Require all given +files+ which should be in the same or a subdirectory of
|
179
|
+
# this file. If a +subdir+ is given, assume all +files+ are in that subdir.
|
180
|
+
# This is used to ensure that the files loaded are from the same version of
|
181
|
+
# Sequel as this file.
|
191
182
|
def self.require(files, subdir=nil)
|
192
183
|
Array(files).each{|f| super("#{File.dirname(__FILE__)}/#{"#{subdir}/" if subdir}#{f}")}
|
193
184
|
end
|
194
185
|
|
195
186
|
# Set whether to set the single threaded mode for all databases by default. By default,
|
196
|
-
# Sequel uses a
|
187
|
+
# Sequel uses a thread-safe connection pool, which isn't as fast as the
|
197
188
|
# single threaded connection pool. If your program will only have one thread,
|
198
189
|
# and speed is a priority, you may want to set this to true:
|
199
190
|
#
|
@@ -202,33 +193,39 @@ module Sequel
|
|
202
193
|
Database.single_threaded = value
|
203
194
|
end
|
204
195
|
|
205
|
-
# Converts the given string into a Date object.
|
206
|
-
|
196
|
+
# Converts the given +string+ into a +Date+ object.
|
197
|
+
#
|
198
|
+
# Sequel.string_to_date('2010-09-10') # Date.civil(2010, 09, 10)
|
199
|
+
def self.string_to_date(string)
|
207
200
|
begin
|
208
|
-
Date.parse(
|
201
|
+
Date.parse(string, Sequel.convert_two_digit_years)
|
209
202
|
rescue => e
|
210
203
|
raise convert_exception_class(e, InvalidValue)
|
211
204
|
end
|
212
205
|
end
|
213
206
|
|
214
|
-
# Converts the given string into a Time or DateTime object, depending on the
|
215
|
-
# value of Sequel.datetime_class
|
216
|
-
|
207
|
+
# Converts the given +string+ into a +Time+ or +DateTime+ object, depending on the
|
208
|
+
# value of <tt>Sequel.datetime_class</tt>.
|
209
|
+
#
|
210
|
+
# Sequel.string_to_datetime('2010-09-10 10:20:30') # Time.local(2010, 09, 10, 10, 20, 30)
|
211
|
+
def self.string_to_datetime(string)
|
217
212
|
begin
|
218
213
|
if datetime_class == DateTime
|
219
|
-
DateTime.parse(
|
214
|
+
DateTime.parse(string, convert_two_digit_years)
|
220
215
|
else
|
221
|
-
datetime_class.parse(
|
216
|
+
datetime_class.parse(string)
|
222
217
|
end
|
223
218
|
rescue => e
|
224
219
|
raise convert_exception_class(e, InvalidValue)
|
225
220
|
end
|
226
221
|
end
|
227
222
|
|
228
|
-
# Converts the given string into a Time object.
|
229
|
-
|
223
|
+
# Converts the given +string+ into a +Time+ object.
|
224
|
+
#
|
225
|
+
# Sequel.string_to_datetime('10:20:30') # Time.parse('10:20:30')
|
226
|
+
def self.string_to_time(string)
|
230
227
|
begin
|
231
|
-
Time.parse(
|
228
|
+
Time.parse(string)
|
232
229
|
rescue => e
|
233
230
|
raise convert_exception_class(e, InvalidValue)
|
234
231
|
end
|
@@ -245,9 +242,12 @@ module Sequel
|
|
245
242
|
end
|
246
243
|
|
247
244
|
# If the supplied block takes a single argument,
|
248
|
-
# yield a new SQL::VirtualRow instance to the block
|
245
|
+
# yield a new <tt>SQL::VirtualRow</tt> instance to the block
|
249
246
|
# argument. Otherwise, evaluate the block in the context of a new
|
250
|
-
# SQL::VirtualRow instance.
|
247
|
+
# <tt>SQL::VirtualRow</tt> instance.
|
248
|
+
#
|
249
|
+
# Sequel.virtual_row{a} # Sequel::SQL::Identifier.new(:a)
|
250
|
+
# Sequel.virtual_row{|o| o.a{}} # Sequel::SQL::Function.new(:a)
|
251
251
|
def self.virtual_row(&block)
|
252
252
|
vr = SQL::VirtualRow.new
|
253
253
|
case block.arity
|