sequel 3.12.1 → 3.13.0

Sign up to get free protection for your applications and to get access to all the features.
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