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
@@ -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