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.
Files changed (150) hide show
  1. data/CHANGELOG +42 -0
  2. data/README.rdoc +137 -118
  3. data/Rakefile +21 -66
  4. data/doc/active_record.rdoc +9 -9
  5. data/doc/advanced_associations.rdoc +59 -188
  6. data/doc/association_basics.rdoc +15 -2
  7. data/doc/cheat_sheet.rdoc +38 -33
  8. data/doc/dataset_filtering.rdoc +16 -7
  9. data/doc/prepared_statements.rdoc +7 -7
  10. data/doc/querying.rdoc +5 -4
  11. data/doc/release_notes/3.13.0.txt +210 -0
  12. data/doc/sharding.rdoc +1 -1
  13. data/doc/sql.rdoc +5 -5
  14. data/doc/validations.rdoc +11 -11
  15. data/lib/sequel/adapters/ado.rb +1 -1
  16. data/lib/sequel/adapters/do.rb +3 -3
  17. data/lib/sequel/adapters/firebird.rb +3 -3
  18. data/lib/sequel/adapters/jdbc/h2.rb +39 -0
  19. data/lib/sequel/adapters/jdbc/mysql.rb +5 -0
  20. data/lib/sequel/adapters/jdbc/oracle.rb +3 -3
  21. data/lib/sequel/adapters/mysql.rb +7 -4
  22. data/lib/sequel/adapters/oracle.rb +3 -3
  23. data/lib/sequel/adapters/shared/mssql.rb +10 -1
  24. data/lib/sequel/adapters/shared/mysql.rb +63 -0
  25. data/lib/sequel/adapters/shared/postgres.rb +61 -3
  26. data/lib/sequel/adapters/sqlite.rb +105 -18
  27. data/lib/sequel/connection_pool.rb +31 -30
  28. data/lib/sequel/core.rb +58 -58
  29. data/lib/sequel/core_sql.rb +52 -43
  30. data/lib/sequel/database/misc.rb +11 -0
  31. data/lib/sequel/database/query.rb +55 -17
  32. data/lib/sequel/dataset/actions.rb +2 -1
  33. data/lib/sequel/dataset/query.rb +2 -3
  34. data/lib/sequel/dataset/sql.rb +24 -11
  35. data/lib/sequel/extensions/schema_dumper.rb +1 -1
  36. data/lib/sequel/metaprogramming.rb +4 -0
  37. data/lib/sequel/model.rb +37 -19
  38. data/lib/sequel/model/associations.rb +33 -25
  39. data/lib/sequel/model/base.rb +2 -2
  40. data/lib/sequel/model/plugins.rb +7 -2
  41. data/lib/sequel/plugins/active_model.rb +1 -1
  42. data/lib/sequel/plugins/association_pks.rb +2 -2
  43. data/lib/sequel/plugins/association_proxies.rb +1 -1
  44. data/lib/sequel/plugins/boolean_readers.rb +2 -2
  45. data/lib/sequel/plugins/class_table_inheritance.rb +10 -2
  46. data/lib/sequel/plugins/identity_map.rb +3 -3
  47. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  48. data/lib/sequel/plugins/json_serializer.rb +212 -0
  49. data/lib/sequel/plugins/lazy_attributes.rb +1 -1
  50. data/lib/sequel/plugins/list.rb +174 -0
  51. data/lib/sequel/plugins/many_through_many.rb +2 -2
  52. data/lib/sequel/plugins/rcte_tree.rb +6 -7
  53. data/lib/sequel/plugins/tree.rb +118 -0
  54. data/lib/sequel/plugins/xml_serializer.rb +321 -0
  55. data/lib/sequel/sql.rb +315 -206
  56. data/lib/sequel/timezones.rb +40 -17
  57. data/lib/sequel/version.rb +8 -2
  58. data/spec/adapters/firebird_spec.rb +2 -2
  59. data/spec/adapters/informix_spec.rb +1 -1
  60. data/spec/adapters/mssql_spec.rb +2 -2
  61. data/spec/adapters/mysql_spec.rb +2 -2
  62. data/spec/adapters/oracle_spec.rb +1 -1
  63. data/spec/adapters/postgres_spec.rb +36 -6
  64. data/spec/adapters/spec_helper.rb +2 -2
  65. data/spec/adapters/sqlite_spec.rb +1 -1
  66. data/spec/core/connection_pool_spec.rb +3 -3
  67. data/spec/core/core_sql_spec.rb +31 -13
  68. data/spec/core/database_spec.rb +39 -2
  69. data/spec/core/dataset_spec.rb +24 -12
  70. data/spec/core/expression_filters_spec.rb +5 -1
  71. data/spec/core/object_graph_spec.rb +1 -1
  72. data/spec/core/schema_generator_spec.rb +1 -1
  73. data/spec/core/schema_spec.rb +1 -1
  74. data/spec/core/spec_helper.rb +1 -1
  75. data/spec/core/version_spec.rb +1 -1
  76. data/spec/extensions/active_model_spec.rb +82 -67
  77. data/spec/extensions/association_dependencies_spec.rb +1 -1
  78. data/spec/extensions/association_pks_spec.rb +1 -1
  79. data/spec/extensions/association_proxies_spec.rb +1 -1
  80. data/spec/extensions/blank_spec.rb +1 -1
  81. data/spec/extensions/boolean_readers_spec.rb +1 -1
  82. data/spec/extensions/caching_spec.rb +1 -1
  83. data/spec/extensions/class_table_inheritance_spec.rb +3 -2
  84. data/spec/extensions/composition_spec.rb +2 -5
  85. data/spec/extensions/force_encoding_spec.rb +3 -1
  86. data/spec/extensions/hook_class_methods_spec.rb +1 -1
  87. data/spec/extensions/identity_map_spec.rb +1 -1
  88. data/spec/extensions/inflector_spec.rb +1 -1
  89. data/spec/extensions/instance_filters_spec.rb +1 -1
  90. data/spec/extensions/instance_hooks_spec.rb +1 -1
  91. data/spec/extensions/json_serializer_spec.rb +154 -0
  92. data/spec/extensions/lazy_attributes_spec.rb +1 -2
  93. data/spec/extensions/list_spec.rb +251 -0
  94. data/spec/extensions/looser_typecasting_spec.rb +1 -1
  95. data/spec/extensions/many_through_many_spec.rb +3 -3
  96. data/spec/extensions/migration_spec.rb +1 -1
  97. data/spec/extensions/named_timezones_spec.rb +5 -6
  98. data/spec/extensions/nested_attributes_spec.rb +1 -1
  99. data/spec/extensions/optimistic_locking_spec.rb +1 -1
  100. data/spec/extensions/pagination_spec.rb +1 -1
  101. data/spec/extensions/pretty_table_spec.rb +1 -1
  102. data/spec/extensions/query_spec.rb +1 -1
  103. data/spec/extensions/rcte_tree_spec.rb +1 -1
  104. data/spec/extensions/schema_dumper_spec.rb +3 -2
  105. data/spec/extensions/schema_spec.rb +1 -1
  106. data/spec/extensions/serialization_spec.rb +6 -2
  107. data/spec/extensions/sharding_spec.rb +1 -1
  108. data/spec/extensions/single_table_inheritance_spec.rb +1 -1
  109. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  110. data/spec/extensions/spec_helper.rb +7 -3
  111. data/spec/extensions/sql_expr_spec.rb +1 -1
  112. data/spec/extensions/string_date_time_spec.rb +1 -1
  113. data/spec/extensions/string_stripper_spec.rb +1 -1
  114. data/spec/extensions/subclasses_spec.rb +1 -1
  115. data/spec/extensions/tactical_eager_loading_spec.rb +1 -1
  116. data/spec/extensions/thread_local_timezones_spec.rb +1 -1
  117. data/spec/extensions/timestamps_spec.rb +1 -1
  118. data/spec/extensions/touch_spec.rb +1 -1
  119. data/spec/extensions/tree_spec.rb +119 -0
  120. data/spec/extensions/typecast_on_load_spec.rb +1 -1
  121. data/spec/extensions/update_primary_key_spec.rb +1 -1
  122. data/spec/extensions/validation_class_methods_spec.rb +1 -1
  123. data/spec/extensions/validation_helpers_spec.rb +1 -1
  124. data/spec/extensions/xml_serializer_spec.rb +142 -0
  125. data/spec/integration/associations_test.rb +1 -1
  126. data/spec/integration/database_test.rb +1 -1
  127. data/spec/integration/dataset_test.rb +29 -14
  128. data/spec/integration/eager_loader_test.rb +1 -1
  129. data/spec/integration/migrator_test.rb +1 -1
  130. data/spec/integration/model_test.rb +1 -1
  131. data/spec/integration/plugin_test.rb +316 -1
  132. data/spec/integration/prepared_statement_test.rb +1 -1
  133. data/spec/integration/schema_test.rb +8 -8
  134. data/spec/integration/spec_helper.rb +1 -1
  135. data/spec/integration/timezone_test.rb +1 -1
  136. data/spec/integration/transaction_test.rb +35 -20
  137. data/spec/integration/type_test.rb +1 -1
  138. data/spec/model/association_reflection_spec.rb +1 -1
  139. data/spec/model/associations_spec.rb +49 -34
  140. data/spec/model/base_spec.rb +1 -1
  141. data/spec/model/dataset_methods_spec.rb +4 -4
  142. data/spec/model/eager_loading_spec.rb +1 -1
  143. data/spec/model/hooks_spec.rb +1 -1
  144. data/spec/model/inflector_spec.rb +1 -1
  145. data/spec/model/model_spec.rb +7 -1
  146. data/spec/model/plugins_spec.rb +1 -1
  147. data/spec/model/record_spec.rb +1 -3
  148. data/spec/model/spec_helper.rb +2 -2
  149. data/spec/model/validations_spec.rb +1 -1
  150. 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, :nspname=>(opts[:schema]||default_schema||'public').to_s)
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.join!(:pg_namespace, :oid=>:pg_class__relnamespace, :nspname=>(opts[:schema] || default_schema).to_s) if opts[:schema] || default_schema
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(opts){|conn| log_yield(sql, opts[:arguments]){conn.execute_batch(sql, opts.fetch(:arguments, []))}; conn.changes}
85
+ _execute(:update, sql, opts)
76
86
  end
77
87
 
78
- # Run the given SQL with the given arguments and return the last inserted row id.
79
- def execute_insert(sql, opts={})
80
- _execute(opts){|conn| log_yield(sql, opts[:arguments]){conn.execute(sql, opts.fetch(:arguments, []))}; conn.last_insert_row_id}
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 yield each row.
84
- def execute(sql, opts={})
85
- _execute(opts) do |conn|
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(opts){|conn| log_yield(sql, opts[:arguments]){conn.get_first_value(sql, opts.fetch(:arguments, []))}}
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], &block)
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 PreparedStatementMethods
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
- db.prepared_statements[name] = ps if name
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 built
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
- # For sharded connection pools, the sharded API:
19
- # * add_servers(Array of Symbols) - start recognizing all shards/servers specified
20
- # by the array of symbols.
21
- # * remove_servers(Array of Symbols) - no longer recognize all shards/servers
22
- # specified by the array of symbols.
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 files (indicating strings 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 :pool_class
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
- # :single_threaded and :servers options.
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
- # * :after_connect - The proc called after each new connection is made, with the
65
- # connection object, useful for customizations that you want to apply to all
66
- # connections.
67
- # * :disconnection_proc - The proc called when removing connections from the pool,
68
- # which is passed the connection to disconnect.
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, not aliased directly for ease of subclass implementation
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 :default by default.
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 class methods that are added via metaprogramming, one for
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
- # Sequel doesn't pay much attention to timezones by default, but you can set it
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 Dates and DateTimes by default,
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 setting this to false.
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. To change it to DateTime, set this to DateTime.
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 all two pairs as
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. Only loads extensions included in this
142
- # release of Sequel, doesn't load external extensions.
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 threadsafe connection pool, which isn't as fast as the
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
- def self.string_to_date(s)
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(s, Sequel.convert_two_digit_years)
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
- def self.string_to_datetime(s)
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(s, convert_two_digit_years)
214
+ DateTime.parse(string, convert_two_digit_years)
220
215
  else
221
- datetime_class.parse(s)
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
- def self.string_to_time(s)
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(s)
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