sequel 3.12.1 → 3.13.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 +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
|