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
@@ -9,21 +9,44 @@ module Sequel
9
9
  @database_timezone = nil
10
10
  @typecast_timezone = nil
11
11
 
12
+ # Sequel doesn't pay much attention to timezones by default, but you can set it
13
+ # handle timezones if you want. There are three separate timezone settings, application_timezone,
14
+ # database_timezone, and typecast_timezone. All three timezones have getter and setter methods.
15
+ # You can set all three timezones to the same value at once via <tt>Sequel.default_timezone=</tt>.
16
+ #
17
+ # The only timezone values that are supported by default are <tt>:utc</tt> (convert to UTC),
18
+ # <tt>:local</tt> (convert to local time), and +nil+ (don't convert). If you need to
19
+ # convert to a specific timezone, or need the timezones being used to change based
20
+ # on the environment (e.g. current user), you need to use the +named_timezones+ extension (and use
21
+ # +DateTime+ as the +datetime_class+). Sequel also ships with a +thread_local_timezones+ extensions
22
+ # which allows each thread to have its own timezone values for each of the timezones.
12
23
  module Timezones
13
- attr_reader :application_timezone, :database_timezone, :typecast_timezone
24
+ # The timezone you want the application to use. This is the timezone
25
+ # that incoming times from the database and typecasting are converted to.
26
+ attr_reader :application_timezone
27
+
28
+ # The timezone for storage in the database. This is the
29
+ # timezone to which Sequel will convert timestamps before literalizing them
30
+ # for storage in the database. It is also the timezone that Sequel will assume
31
+ # database timestamp values are already in (if they don't include an offset).
32
+ attr_reader :database_timezone
33
+
34
+ # The timezone that incoming data that Sequel needs to typecast
35
+ # is assumed to be already in (if they don't include an offset).
36
+ attr_reader :typecast_timezone
14
37
 
15
38
  %w'application database typecast'.each do |t|
16
39
  class_eval("def #{t}_timezone=(tz); @#{t}_timezone = convert_timezone_setter_arg(tz) end", __FILE__, __LINE__)
17
40
  end
18
41
 
19
- # Convert the given Time/DateTime object into the database timezone, used when
42
+ # Convert the given +Time+/+DateTime+ object into the database timezone, used when
20
43
  # literalizing objects in an SQL string.
21
44
  def application_to_database_timestamp(v)
22
45
  convert_output_timestamp(v, Sequel.database_timezone)
23
46
  end
24
47
 
25
- # Convert the given object into an object of Sequel.datetime_class in the
26
- # application_timezone. Used when coverting datetime/timestamp columns
48
+ # Convert the given object into an object of <tt>Sequel.datetime_class</tt> in the
49
+ # +application_timezone+. Used when coverting datetime/timestamp columns
27
50
  # returned by the database.
28
51
  def database_to_application_timestamp(v)
29
52
  convert_timestamp(v, Sequel.database_timezone)
@@ -36,8 +59,8 @@ module Sequel
36
59
  self.typecast_timezone = tz
37
60
  end
38
61
 
39
- # Convert the given object into an object of Sequel.datetime_class in the
40
- # application_timezone. Used when typecasting values when assigning them
62
+ # Convert the given object into an object of <tt>Sequel.datetime_class</tt> in the
63
+ # +application_timezone+. Used when typecasting values when assigning them
41
64
  # to model datetime attributes.
42
65
  def typecast_to_application_timestamp(v)
43
66
  convert_timestamp(v, Sequel.typecast_timezone)
@@ -45,7 +68,7 @@ module Sequel
45
68
 
46
69
  private
47
70
 
48
- # Convert the given DateTime to the given input_timezone, keeping the
71
+ # Convert the given +DateTime+ to the given input_timezone, keeping the
49
72
  # same time and just modifying the timezone.
50
73
  def convert_input_datetime_no_offset(v, input_timezone)
51
74
  case input_timezone
@@ -58,16 +81,16 @@ module Sequel
58
81
  end
59
82
  end
60
83
 
61
- # Convert the given DateTime to the given input_timezone that is not supported
62
- # by default (such as nil, :local, or :utc). Raises an error by default.
84
+ # Convert the given +DateTime+ to the given input_timezone that is not supported
85
+ # by default (i.e. one other than +nil+, <tt>:local</tt>, or <tt>:utc</tt>). Raises an +InvalidValue+ by default.
63
86
  # Can be overridden in extensions.
64
87
  def convert_input_datetime_other(v, input_timezone)
65
88
  raise InvalidValue, "Invalid input_timezone: #{input_timezone.inspect}"
66
89
  end
67
90
 
68
- # Converts the object from a String, Array, Date, DateTime, or Time into an
69
- # instance of Sequel.datetime_class. If given an array or a string that doesn't
70
- # contain an offset, assume that the array/string is already in the given input_timezone.
91
+ # Converts the object from a +String+, +Array+, +Date+, +DateTime+, or +Time+ into an
92
+ # instance of <tt>Sequel.datetime_class</tt>. If given an array or a string that doesn't
93
+ # contain an offset, assume that the array/string is already in the given +input_timezone+.
71
94
  def convert_input_timestamp(v, input_timezone)
72
95
  case v
73
96
  when String
@@ -110,14 +133,14 @@ module Sequel
110
133
  end
111
134
  end
112
135
 
113
- # Convert the given DateTime to the given output_timezone that is not supported
114
- # by default (such as nil, :local, or :utc). Raises an error by default.
136
+ # Convert the given +DateTime+ to the given output_timezone that is not supported
137
+ # by default (i.e. one other than +nil+, <tt>:local</tt>, or <tt>:utc</tt>). Raises an +InvalidValue+ by default.
115
138
  # Can be overridden in extensions.
116
139
  def convert_output_datetime_other(v, output_timezone)
117
140
  raise InvalidValue, "Invalid output_timezone: #{output_timezone.inspect}"
118
141
  end
119
142
 
120
- # Converts the object to the given output_timezone.
143
+ # Converts the object to the given +output_timezone+.
121
144
  def convert_output_timestamp(v, output_timezone)
122
145
  if output_timezone
123
146
  if v.is_a?(DateTime)
@@ -138,8 +161,8 @@ module Sequel
138
161
  end
139
162
 
140
163
  # Converts the given object from the given input timezone to the
141
- # application timezone using convert_input_timestamp and
142
- # convert_output_timestamp.
164
+ # +application_timezone+ using +convert_input_timestamp+ and
165
+ # +convert_output_timestamp+.
143
166
  def convert_timestamp(v, input_timezone)
144
167
  begin
145
168
  convert_output_timestamp(convert_input_timestamp(v, input_timezone), Sequel.application_timezone)
@@ -1,8 +1,14 @@
1
1
  module Sequel
2
+ # The major version of Sequel. Only bumped for major changes.
2
3
  MAJOR = 3
3
- MINOR = 12
4
- TINY = 1
4
+ # The minor version of Sequel. Bumped for every non-patch level
5
+ # release, generally around once a month.
6
+ MINOR = 13
7
+ # The tiny version of Sequel. Usually 0, only bumped for bugfix
8
+ # releases that fix regressions from previous versions.
9
+ TINY = 0
5
10
 
11
+ # The version of Sequel you are using, as a string (e.g. "2.11.0")
6
12
  VERSION = [MAJOR, MINOR, TINY].join('.')
7
13
 
8
14
  # The version of Sequel you are using, as a string (e.g. "2.11.0")
@@ -1,4 +1,4 @@
1
- require File.join(File.dirname(__FILE__), 'spec_helper.rb')
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper.rb')
2
2
 
3
3
  unless defined?(FIREBIRD_DB)
4
4
  FIREBIRD_URL = 'firebird://sysdba:masterkey@localhost/reality_spec' unless defined? FIREBIRD_URL
@@ -13,7 +13,7 @@ logger = Object.new
13
13
  def logger.method_missing(m, msg)
14
14
  FIREBIRD_DB.sqls.push(msg)
15
15
  end
16
- FIREBIRD_DB.logger = logger
16
+ FIREBIRD_DB.loggers = [logger]
17
17
 
18
18
  FIREBIRD_DB.create_table! :test do
19
19
  varchar :name, :size => 50
@@ -1,4 +1,4 @@
1
- require File.join(File.dirname(__FILE__), 'spec_helper.rb')
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper.rb')
2
2
 
3
3
  unless defined?(INFORMIX_DB)
4
4
  INFORMIX_DB = Sequel.connect('informix://localhost/mydb')
@@ -1,4 +1,4 @@
1
- require File.join(File.dirname(__FILE__), 'spec_helper.rb')
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper.rb')
2
2
 
3
3
  require ENV['SEQUEL_MSSQL_SPEC_REQUIRE'] if ENV['SEQUEL_MSSQL_SPEC_REQUIRE']
4
4
 
@@ -15,7 +15,7 @@ logger = Object.new
15
15
  def logger.method_missing(m, msg)
16
16
  MSSQL_DB.sqls << msg
17
17
  end
18
- MSSQL_DB.logger = logger
18
+ MSSQL_DB.loggers = [logger]
19
19
 
20
20
  MSSQL_DB.create_table! :test do
21
21
  text :name
@@ -1,4 +1,4 @@
1
- require File.join(File.dirname(__FILE__), 'spec_helper.rb')
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper.rb')
2
2
 
3
3
  unless defined?(MYSQL_USER)
4
4
  MYSQL_USER = 'root'
@@ -25,7 +25,7 @@ logger = Object.new
25
25
  def logger.method_missing(m, msg)
26
26
  MYSQL_DB.sqls << msg
27
27
  end
28
- MYSQL_DB.logger = logger
28
+ MYSQL_DB.loggers = [logger]
29
29
  MYSQL_DB.drop_table(:items) rescue nil
30
30
  MYSQL_DB.drop_table(:dolls) rescue nil
31
31
  MYSQL_DB.drop_table(:booltest) rescue nil
@@ -1,4 +1,4 @@
1
- require File.join(File.dirname(__FILE__), 'spec_helper.rb')
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper.rb')
2
2
 
3
3
  unless defined?(ORACLE_DB)
4
4
  ORACLE_DB = Sequel.connect('oracle://hr:hr@localhost/XE')
@@ -1,4 +1,4 @@
1
- require File.join(File.dirname(__FILE__), 'spec_helper.rb')
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper.rb')
2
2
 
3
3
  unless defined?(POSTGRES_DB)
4
4
  POSTGRES_URL = 'postgres://postgres:postgres@localhost:5432/reality_spec' unless defined? POSTGRES_URL
@@ -13,7 +13,7 @@ logger = Object.new
13
13
  def logger.method_missing(m, msg)
14
14
  POSTGRES_DB.sqls << msg
15
15
  end
16
- POSTGRES_DB.logger = logger
16
+ POSTGRES_DB.loggers = [logger]
17
17
 
18
18
  #POSTGRES_DB.instance_variable_set(:@server_version, 80100)
19
19
  POSTGRES_DB.create_table! :test do
@@ -130,6 +130,15 @@ context "A PostgreSQL dataset" do
130
130
  @d.filter(:name => /^bc/).count.should == 1
131
131
  end
132
132
 
133
+ specify "should support NULLS FIRST and NULLS LAST" do
134
+ @d << {:name => 'abc'}
135
+ @d << {:name => 'bcd'}
136
+ @d << {:name => 'bcd', :value => 2}
137
+ @d.order(:value.asc(:nulls=>:first), :name).select_map(:name).should == %w[abc bcd bcd]
138
+ @d.order(:value.asc(:nulls=>:last), :name).select_map(:name).should == %w[bcd abc bcd]
139
+ @d.order(:value.asc(:nulls=>:first), :name).reverse.select_map(:name).should == %w[bcd bcd abc]
140
+ end
141
+
133
142
  specify "#lock should lock tables and yield if a block is given" do
134
143
  @d.lock('EXCLUSIVE'){@d.insert(:name=>'a')}
135
144
  end
@@ -557,9 +566,11 @@ context "Postgres::Database schema qualified tables" do
557
566
  POSTGRES_DB.drop_table(:schema_test.qualify(:schema_test))
558
567
  end
559
568
 
560
- specify "#tables should include only tables in the public schema if no schema is given" do
569
+ specify "#tables should not include tables in a default non-public schema" do
561
570
  POSTGRES_DB.create_table(:schema_test__schema_test){integer :i}
562
- POSTGRES_DB.tables.should_not include(:schema_test)
571
+ POSTGRES_DB.tables.should include(:schema_test)
572
+ POSTGRES_DB.tables.should_not include(:pg_am)
573
+ POSTGRES_DB.tables.should_not include(:domain_udt_usage)
563
574
  end
564
575
 
565
576
  specify "#tables should return tables in the schema provided by the :schema argument" do
@@ -567,9 +578,28 @@ context "Postgres::Database schema qualified tables" do
567
578
  POSTGRES_DB.tables(:schema=>:schema_test).should == [:schema_test]
568
579
  end
569
580
 
570
- specify "#table_exists? should assume the public schema if no schema is provided" do
581
+ specify "#schema should not include columns from tables in a default non-public schema" do
582
+ POSTGRES_DB.create_table(:schema_test__domains){integer :i}
583
+ sch = POSTGRES_DB.schema(:domains)
584
+ cs = sch.map{|x| x.first}
585
+ cs.should include(:i)
586
+ cs.should_not include(:data_type)
587
+ end
588
+
589
+ specify "#schema should only include columns from the table in the given :schema argument" do
590
+ POSTGRES_DB.create_table!(:domains){integer :d}
591
+ POSTGRES_DB.create_table(:schema_test__domains){integer :i}
592
+ sch = POSTGRES_DB.schema(:domains, :schema=>:schema_test)
593
+ cs = sch.map{|x| x.first}
594
+ cs.should include(:i)
595
+ cs.should_not include(:d)
596
+ POSTGRES_DB.drop_table(:domains)
597
+ end
598
+
599
+ specify "#table_exists? should not include tables from the default non-public schemas" do
571
600
  POSTGRES_DB.create_table(:schema_test__schema_test){integer :i}
572
- POSTGRES_DB.table_exists?(:schema_test).should == false
601
+ POSTGRES_DB.table_exists?(:schema_test).should == true
602
+ POSTGRES_DB.table_exists?(:domain_udt_usage).should == false
573
603
  end
574
604
 
575
605
  specify "#table_exists? should see if the table is in a given schema" do
@@ -1,12 +1,12 @@
1
1
  require 'rubygems'
2
2
  require 'logger'
3
3
  unless Object.const_defined?('Sequel')
4
- $:.unshift(File.join(File.dirname(__FILE__), "../../lib/"))
4
+ $:.unshift(File.join(File.dirname(File.expand_path(__FILE__)), "../../lib/"))
5
5
  require 'sequel'
6
6
  Sequel.quote_identifiers = false
7
7
  end
8
8
  begin
9
- require File.join(File.dirname(File.dirname(__FILE__)), 'spec_config.rb')
9
+ require File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), 'spec_config.rb')
10
10
  rescue LoadError
11
11
  end
12
12
 
@@ -1,4 +1,4 @@
1
- require File.join(File.dirname(__FILE__), 'spec_helper.rb')
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper.rb')
2
2
 
3
3
  unless defined?(SQLITE_DB)
4
4
  SQLITE_URL = 'sqlite:/' unless defined? SQLITE_URL
@@ -1,4 +1,4 @@
1
- require File.join(File.dirname(__FILE__), 'spec_helper')
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper')
2
2
  CONNECTION_POOL_DEFAULTS = {:pool_timeout=>5, :pool_sleep_time=>0.001, :max_connections=>4}
3
3
 
4
4
  context "An empty ConnectionPool" do
@@ -70,7 +70,7 @@ context "A connection pool handling connections" do
70
70
  @cpool.hold {:block_return}.should == :block_return
71
71
  end
72
72
 
73
- if RUBY_VERSION < '1.9.0' and (!defined?(RUBY_ENGINE) or RUBY_ENGINE != 'jruby')
73
+ if RUBY_VERSION < '1.9.0' and !defined?(RUBY_ENGINE)
74
74
  specify "#hold should remove dead threads from the pool if it reaches its max_size" do
75
75
  Thread.new{@cpool.hold{Thread.current.exit!}}.join
76
76
  @cpool.allocated.keys.map{|t| t.alive?}.should == [false]
@@ -96,7 +96,7 @@ context "A connection pool handling connections" do
96
96
 
97
97
  specify "#hold should remove the connection if a DatabaseDisconnectError is raised" do
98
98
  @cpool.created_count.should == 0
99
- @cpool.hold{Thread.new{@cpool.hold{}}; sleep 0.01}
99
+ @cpool.hold{Thread.new{@cpool.hold{}}; sleep 0.03}
100
100
  @cpool.created_count.should == 2
101
101
  proc{@cpool.hold{raise Sequel::DatabaseDisconnectError}}.should raise_error(Sequel::DatabaseDisconnectError)
102
102
  @cpool.created_count.should == 1
@@ -1,4 +1,4 @@
1
- require File.join(File.dirname(__FILE__), 'spec_helper')
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper')
2
2
 
3
3
  context "Array#all_two_pairs?" do
4
4
  specify "should return false if empty" do
@@ -39,6 +39,10 @@ context "Array#case and Hash#case" do
39
39
  @d.literal([[:x, :y], [:a, :b]].case(:z, :exp__w)).should == '(CASE exp.w WHEN x THEN y WHEN a THEN b ELSE z END)'
40
40
  end
41
41
 
42
+ specify "should return SQL CASE expression with expression even if nil" do
43
+ @d.literal({:x=>:y}.case(:z, nil)).should == '(CASE NULL WHEN x THEN y ELSE z END)'
44
+ end
45
+
42
46
  specify "should raise an error if an array that isn't all two pairs is used" do
43
47
  proc{[:b].case(:a)}.should raise_error(Sequel::Error)
44
48
  proc{[:b, :c].case(:a)}.should raise_error(Sequel::Error)
@@ -51,19 +55,28 @@ context "Array#case and Hash#case" do
51
55
  end
52
56
  end
53
57
 
54
- context "Array#sql_array" do
58
+ context "Array#sql_value_list and #sql_array" do
55
59
  before do
56
60
  @d = Sequel::Dataset.new(nil)
57
61
  end
58
62
 
59
- specify "should treat the array as an SQL array instead of conditions" do
60
- @d.literal([[:x, 1], [:y, 2]]).should == '((x = 1) AND (y = 2))'
61
- @d.literal([[:x, 1], [:y, 2]].sql_array).should == '((x, 1), (y, 2))'
63
+ specify "should treat the array as an SQL value list instead of conditions when used as a placeholder value" do
64
+ @d.filter("(a, b) IN ?", [[:x, 1], [:y, 2]]).sql.should == 'SELECT * WHERE ((a, b) IN ((x = 1) AND (y = 2)))'
65
+ @d.filter("(a, b) IN ?", [[:x, 1], [:y, 2]].sql_value_list).sql.should == 'SELECT * WHERE ((a, b) IN ((x, 1), (y, 2)))'
66
+ @d.filter("(a, b) IN ?", [[:x, 1], [:y, 2]].sql_array).sql.should == 'SELECT * WHERE ((a, b) IN ((x, 1), (y, 2)))'
67
+ end
68
+
69
+ specify "should be no difference when used as a hash value" do
70
+ @d.filter([:a, :b]=>[[:x, 1], [:y, 2]]).sql.should == 'SELECT * WHERE ((a, b) IN ((x, 1), (y, 2)))'
71
+ @d.filter([:a, :b]=>[[:x, 1], [:y, 2]].sql_value_list).sql.should == 'SELECT * WHERE ((a, b) IN ((x, 1), (y, 2)))'
62
72
  @d.filter([:a, :b]=>[[:x, 1], [:y, 2]].sql_array).sql.should == 'SELECT * WHERE ((a, b) IN ((x, 1), (y, 2)))'
63
73
  end
64
74
  end
65
75
 
66
76
  context "String#lit" do
77
+ before do
78
+ @ds = ds = MockDatabase.new.dataset
79
+ end
67
80
  specify "should return an LiteralString object" do
68
81
  'xyz'.lit.should be_a_kind_of(Sequel::LiteralString)
69
82
  'xyz'.lit.to_s.should == 'xyz'
@@ -77,19 +90,24 @@ context "String#lit" do
77
90
  specify "should return a PlaceholderLiteralString object if args are given" do
78
91
  a = 'DISTINCT ?'.lit(:a)
79
92
  a.should be_a_kind_of(Sequel::SQL::PlaceholderLiteralString)
80
- ds = MockDatabase.new.dataset
81
- ds.literal(a).should == 'DISTINCT a'
82
- ds.quote_identifiers = true
83
- ds.literal(a).should == 'DISTINCT "a"'
93
+ @ds.literal(a).should == 'DISTINCT a'
94
+ @ds.quote_identifiers = true
95
+ @ds.literal(a).should == 'DISTINCT "a"'
84
96
  end
85
97
 
86
98
  specify "should handle named placeholders if given a single argument hash" do
87
99
  a = 'DISTINCT :b'.lit(:b=>:a)
88
100
  a.should be_a_kind_of(Sequel::SQL::PlaceholderLiteralString)
89
- ds = MockDatabase.new.dataset
90
- ds.literal(a).should == 'DISTINCT a'
91
- ds.quote_identifiers = true
92
- ds.literal(a).should == 'DISTINCT "a"'
101
+ @ds.literal(a).should == 'DISTINCT a'
102
+ @ds.quote_identifiers = true
103
+ @ds.literal(a).should == 'DISTINCT "a"'
104
+ end
105
+
106
+ specify "should treat placeholder literal strings as generic expressions" do
107
+ a = ':b'.lit(:b=>:a)
108
+ @ds.literal(a + 1).should == "(a + 1)"
109
+ @ds.literal(a & :b).should == "(a AND b)"
110
+ @ds.literal(a.sql_string + :b).should == "(a || b)"
93
111
  end
94
112
  end
95
113
 
@@ -1,4 +1,4 @@
1
- require File.join(File.dirname(__FILE__), 'spec_helper')
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper')
2
2
 
3
3
  context "A new Database" do
4
4
  before do
@@ -213,6 +213,7 @@ context "Database#log_info" do
213
213
  before do
214
214
  @o = Object.new
215
215
  def @o.logs; @logs || []; end
216
+ def @o.to_ary; [self]; end
216
217
  def @o.method_missing(*args); (@logs ||= []) << args; end
217
218
  @db = Sequel::Database.new(:logger=>@o)
218
219
  end
@@ -234,6 +235,7 @@ context "Database#log_yield" do
234
235
  def @o.logs; @logs || []; end
235
236
  def @o.warn(*args); (@logs ||= []) << [:warn] + args; end
236
237
  def @o.method_missing(*args); (@logs ||= []) << args; end
238
+ def @o.to_ary; [self]; end
237
239
  @db = Sequel::Database.new(:logger=>@o)
238
240
  end
239
241
 
@@ -693,10 +695,33 @@ context "Database#transaction" do
693
695
  end
694
696
 
695
697
  specify "should wrap the supplied block with BEGIN + COMMIT statements" do
696
- @db.transaction {@db.execute 'DROP TABLE test;'}
698
+ @db.transaction{@db.execute 'DROP TABLE test;'}
697
699
  @db.sql.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
698
700
  end
699
701
 
702
+ specify "should support transaction isolation levels" do
703
+ @db.meta_def(:supports_transaction_isolation_levels?){true}
704
+ [:uncommitted, :committed, :repeatable, :serializable].each do |l|
705
+ @db.transaction(:isolation=>l){@db.run "DROP TABLE #{l}"}
706
+ end
707
+ @db.sql.should == ['BEGIN', 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED', 'DROP TABLE uncommitted', 'COMMIT',
708
+ 'BEGIN', 'SET TRANSACTION ISOLATION LEVEL READ COMMITTED', 'DROP TABLE committed', 'COMMIT',
709
+ 'BEGIN', 'SET TRANSACTION ISOLATION LEVEL REPEATABLE READ', 'DROP TABLE repeatable', 'COMMIT',
710
+ 'BEGIN', 'SET TRANSACTION ISOLATION LEVEL SERIALIZABLE', 'DROP TABLE serializable', 'COMMIT']
711
+ end
712
+
713
+ specify "should allow specifying a default transaction isolation level" do
714
+ @db.meta_def(:supports_transaction_isolation_levels?){true}
715
+ [:uncommitted, :committed, :repeatable, :serializable].each do |l|
716
+ @db.transaction_isolation_level = l
717
+ @db.transaction{@db.run "DROP TABLE #{l}"}
718
+ end
719
+ @db.sql.should == ['BEGIN', 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED', 'DROP TABLE uncommitted', 'COMMIT',
720
+ 'BEGIN', 'SET TRANSACTION ISOLATION LEVEL READ COMMITTED', 'DROP TABLE committed', 'COMMIT',
721
+ 'BEGIN', 'SET TRANSACTION ISOLATION LEVEL REPEATABLE READ', 'DROP TABLE repeatable', 'COMMIT',
722
+ 'BEGIN', 'SET TRANSACTION ISOLATION LEVEL SERIALIZABLE', 'DROP TABLE serializable', 'COMMIT']
723
+ end
724
+
700
725
  specify "should handle returning inside of the block by committing" do
701
726
  def @db.ret_commit
702
727
  transaction do
@@ -1581,6 +1606,18 @@ context "Database#supports_savepoints?" do
1581
1606
  end
1582
1607
  end
1583
1608
 
1609
+ context "Database#supports_prepared_transactions?" do
1610
+ specify "should be false by default" do
1611
+ Sequel::Database.new.supports_prepared_transactions?.should == false
1612
+ end
1613
+ end
1614
+
1615
+ context "Database#supports_transaction_isolation_levels?" do
1616
+ specify "should be false by default" do
1617
+ Sequel::Database.new.supports_transaction_isolation_levels?.should == false
1618
+ end
1619
+ end
1620
+
1584
1621
  context "Database#input_identifier_meth" do
1585
1622
  specify "should be the input_identifer method of a default dataset for this database" do
1586
1623
  db = Sequel::Database.new