sequel 3.40.0 → 3.41.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 (89) hide show
  1. data/CHANGELOG +40 -0
  2. data/README.rdoc +2 -2
  3. data/doc/advanced_associations.rdoc +12 -0
  4. data/doc/bin_sequel.rdoc +144 -0
  5. data/doc/migration.rdoc +1 -1
  6. data/doc/object_model.rdoc +29 -0
  7. data/doc/release_notes/3.41.0.txt +155 -0
  8. data/lib/sequel/adapters/ado.rb +4 -4
  9. data/lib/sequel/adapters/amalgalite.rb +0 -5
  10. data/lib/sequel/adapters/cubrid.rb +2 -2
  11. data/lib/sequel/adapters/db2.rb +9 -5
  12. data/lib/sequel/adapters/dbi.rb +4 -6
  13. data/lib/sequel/adapters/do.rb +4 -5
  14. data/lib/sequel/adapters/firebird.rb +8 -4
  15. data/lib/sequel/adapters/ibmdb.rb +2 -3
  16. data/lib/sequel/adapters/informix.rb +0 -6
  17. data/lib/sequel/adapters/jdbc.rb +11 -7
  18. data/lib/sequel/adapters/jdbc/db2.rb +22 -0
  19. data/lib/sequel/adapters/jdbc/derby.rb +5 -5
  20. data/lib/sequel/adapters/jdbc/h2.rb +0 -5
  21. data/lib/sequel/adapters/jdbc/jtds.rb +1 -1
  22. data/lib/sequel/adapters/jdbc/sqlserver.rb +6 -0
  23. data/lib/sequel/adapters/mock.rb +3 -3
  24. data/lib/sequel/adapters/mysql.rb +7 -7
  25. data/lib/sequel/adapters/mysql2.rb +0 -5
  26. data/lib/sequel/adapters/odbc.rb +4 -4
  27. data/lib/sequel/adapters/openbase.rb +4 -6
  28. data/lib/sequel/adapters/oracle.rb +14 -6
  29. data/lib/sequel/adapters/postgres.rb +12 -8
  30. data/lib/sequel/adapters/shared/db2.rb +5 -0
  31. data/lib/sequel/adapters/shared/firebird.rb +10 -0
  32. data/lib/sequel/adapters/shared/mssql.rb +43 -1
  33. data/lib/sequel/adapters/shared/mysql.rb +1 -0
  34. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +1 -1
  35. data/lib/sequel/adapters/shared/postgres.rb +12 -0
  36. data/lib/sequel/adapters/shared/sqlite.rb +32 -0
  37. data/lib/sequel/adapters/sqlite.rb +9 -8
  38. data/lib/sequel/adapters/swift.rb +3 -8
  39. data/lib/sequel/adapters/tinytds.rb +5 -5
  40. data/lib/sequel/connection_pool.rb +13 -19
  41. data/lib/sequel/connection_pool/sharded_single.rb +12 -12
  42. data/lib/sequel/connection_pool/sharded_threaded.rb +37 -17
  43. data/lib/sequel/connection_pool/single.rb +6 -3
  44. data/lib/sequel/connection_pool/threaded.rb +33 -13
  45. data/lib/sequel/database/connecting.rb +28 -1
  46. data/lib/sequel/database/logging.rb +1 -1
  47. data/lib/sequel/database/misc.rb +2 -5
  48. data/lib/sequel/database/query.rb +2 -2
  49. data/lib/sequel/database/schema_generator.rb +1 -1
  50. data/lib/sequel/database/schema_methods.rb +3 -0
  51. data/lib/sequel/dataset/query.rb +8 -4
  52. data/lib/sequel/dataset/sql.rb +7 -0
  53. data/lib/sequel/extensions/arbitrary_servers.rb +1 -1
  54. data/lib/sequel/extensions/connection_validator.rb +109 -0
  55. data/lib/sequel/extensions/pg_array.rb +2 -0
  56. data/lib/sequel/extensions/pg_hstore.rb +2 -0
  57. data/lib/sequel/extensions/pg_json.rb +4 -0
  58. data/lib/sequel/extensions/pg_range.rb +1 -0
  59. data/lib/sequel/extensions/pg_row.rb +4 -0
  60. data/lib/sequel/plugins/prepared_statements.rb +2 -1
  61. data/lib/sequel/plugins/single_table_inheritance.rb +53 -10
  62. data/lib/sequel/plugins/touch.rb +18 -6
  63. data/lib/sequel/plugins/validation_class_methods.rb +1 -0
  64. data/lib/sequel/plugins/validation_helpers.rb +3 -1
  65. data/lib/sequel/sql.rb +61 -19
  66. data/lib/sequel/version.rb +1 -1
  67. data/spec/adapters/firebird_spec.rb +52 -38
  68. data/spec/adapters/mssql_spec.rb +67 -0
  69. data/spec/adapters/mysql_spec.rb +192 -116
  70. data/spec/adapters/postgres_spec.rb +133 -70
  71. data/spec/adapters/spec_helper.rb +7 -0
  72. data/spec/adapters/sqlite_spec.rb +34 -1
  73. data/spec/core/connection_pool_spec.rb +79 -75
  74. data/spec/core/database_spec.rb +9 -4
  75. data/spec/core/dataset_spec.rb +15 -0
  76. data/spec/core/expression_filters_spec.rb +40 -2
  77. data/spec/extensions/connection_validator_spec.rb +118 -0
  78. data/spec/extensions/pg_array_spec.rb +4 -0
  79. data/spec/extensions/single_table_inheritance_spec.rb +42 -0
  80. data/spec/extensions/touch_spec.rb +40 -0
  81. data/spec/extensions/validation_class_methods_spec.rb +19 -1
  82. data/spec/extensions/validation_helpers_spec.rb +17 -0
  83. data/spec/integration/database_test.rb +14 -0
  84. data/spec/integration/dataset_test.rb +3 -3
  85. data/spec/integration/plugin_test.rb +41 -12
  86. data/spec/integration/schema_test.rb +14 -0
  87. data/spec/integration/spec_helper.rb +7 -0
  88. data/spec/integration/type_test.rb +3 -0
  89. metadata +9 -3
@@ -99,6 +99,8 @@ module Sequel
99
99
  module Postgres
100
100
  # Represents a PostgreSQL array column value.
101
101
  class PGArray < DelegateClass(Array)
102
+ include Sequel::SQL::AliasMethods
103
+
102
104
  ARRAY = "ARRAY".freeze
103
105
  DOUBLE_COLON = '::'.freeze
104
106
  EMPTY_BRACKET = '[]'.freeze
@@ -86,6 +86,8 @@ require 'strscan'
86
86
  module Sequel
87
87
  module Postgres
88
88
  class HStore < DelegateClass(Hash)
89
+ include Sequel::SQL::AliasMethods
90
+
89
91
  # Parser for PostgreSQL hstore output format.
90
92
  class Parser < StringScanner
91
93
  QUOTE_RE = /"/.freeze
@@ -63,6 +63,8 @@ module Sequel
63
63
 
64
64
  # Class representating PostgreSQL JSON column array values.
65
65
  class JSONArray < DelegateClass(Array)
66
+ include Sequel::SQL::AliasMethods
67
+
66
68
  # Convert the array to a string using to_json, append a
67
69
  # literalized version of the string to the sql, and explicitly
68
70
  # cast the string to json.
@@ -74,6 +76,8 @@ module Sequel
74
76
 
75
77
  # Class representating PostgreSQL JSON column hash/object values.
76
78
  class JSONHash < DelegateClass(Hash)
79
+ include Sequel::SQL::AliasMethods
80
+
77
81
  # Convert the array to a string using to_json, append a
78
82
  # literalized version of the string to the sql, and explicitly
79
83
  # cast the string to json.
@@ -59,6 +59,7 @@ Sequel.require 'adapters/utils/pg_types'
59
59
  module Sequel
60
60
  module Postgres
61
61
  class PGRange
62
+ include Sequel::SQL::AliasMethods
62
63
 
63
64
  # Map of string database type names to type symbols (e.g. 'int4range' => :int4range),
64
65
  # used in the schema parsing.
@@ -81,6 +81,8 @@ module Sequel
81
81
  # this is only used for generic PostgreSQL record types, as registered
82
82
  # types use HashRow by default.
83
83
  class ArrayRow < DelegateClass(Array)
84
+ include Sequel::SQL::AliasMethods
85
+
84
86
  class << self
85
87
  # The database type for this class. May be nil if this class
86
88
  # done not have a specific database type.
@@ -125,6 +127,8 @@ module Sequel
125
127
  # Types registered via Database#register_row_type will use this
126
128
  # class by default.
127
129
  class HashRow < DelegateClass(Hash)
130
+ include Sequel::SQL::AliasMethods
131
+
128
132
  class << self
129
133
  # The columns associated with this class.
130
134
  attr_accessor :columns
@@ -11,7 +11,8 @@ module Sequel
11
11
  # of prepared statements that can be created, unless you tightly control how your
12
12
  # model instances are saved.
13
13
  #
14
- # This plugin probably does not work correctly with the instance filters plugin.
14
+ # This plugin does not work correctly with the instance filters plugin
15
+ # or the update_primary_key plugin.
15
16
  #
16
17
  # Usage:
17
18
  #
@@ -35,6 +35,17 @@ module Sequel
35
35
  # Employee.plugin :single_table_inheritance, :type,
36
36
  # :model_map=>{'line staff'=>:Staff, 'supervisor'=>:Manager}
37
37
  #
38
+ # # By default the plugin sets the respective column value
39
+ # # when a new instance is created.
40
+ # Staff.create.type == 'line staff'
41
+ # Manager.create.type == 'supervisor'
42
+ #
43
+ # # You can customize this behavior with the :key_chooser option.
44
+ # # This is most useful when using a non-bijective mapping.
45
+ # Employee.plugin :single_table_inheritance, :type,
46
+ # :model_map=>{'line staff'=>:Staff, 'supervisor'=>:Manager},
47
+ # :key_chooser=>proc{|instance| instance.model.sti_key_map[instance.model.to_s].first || 'stranger' }
48
+ #
38
49
  # # Using custom procs, with :model_map taking column values
39
50
  # # and yielding either a class, string, symbol, or nil,
40
51
  # # and :key_map taking a class object and returning the column
@@ -43,6 +54,15 @@ module Sequel
43
54
  # :model_map=>proc{|v| v.reverse},
44
55
  # :key_map=>proc{|klass| klass.name.reverse}
45
56
  #
57
+ # # You can use the same class for multiple values.
58
+ # # This is mainly useful when the sti_key column contains multiple values
59
+ # # which are different but do not require different code.
60
+ # Employee.plugin :single_table_inheritance, :type,
61
+ # :model_map=>{'staff' => "Staff",
62
+ # 'manager => "Manager",
63
+ # 'overpayed staff => "Staff",
64
+ # 'underpayed staff' => "Staff"}
65
+ #
46
66
  # One minor issue to note is that if you specify the <tt>:key_map</tt>
47
67
  # option as a hash, instead of having it inferred from the <tt>:model_map</tt>,
48
68
  # you should only use class name strings as keys, you should not use symbols
@@ -57,20 +77,38 @@ module Sequel
57
77
  @sti_model_map = opts[:model_map] || lambda{|v| v if v && v != ''}
58
78
  @sti_key_map = if km = opts[:key_map]
59
79
  if km.is_a?(Hash)
60
- h = Hash.new{|h,k| h[k.to_s] unless k.is_a?(String)}
61
- h.merge!(km)
80
+ h = Hash.new do |h,k|
81
+ unless k.is_a?(String)
82
+ h[k.to_s]
83
+ else
84
+ []
85
+ end
86
+ end
87
+ km.each do |k,v|
88
+ h[k.to_s] = [ ] unless h.key?(k.to_s)
89
+ h[k.to_s].push( *Array(v) )
90
+ end
91
+ h
62
92
  else
63
93
  km
64
94
  end
65
95
  elsif sti_model_map.is_a?(Hash)
66
- h = Hash.new{|h,k| h[k.to_s] unless k.is_a?(String)}
96
+ h = Hash.new do |h,k|
97
+ unless k.is_a?(String)
98
+ h[k.to_s]
99
+ else
100
+ []
101
+ end
102
+ end
67
103
  sti_model_map.each do |k,v|
68
- h[v.to_s] = k
104
+ h[v.to_s] = [ ] unless h.key?(v.to_s)
105
+ h[v.to_s] << k
69
106
  end
70
107
  h
71
108
  else
72
109
  lambda{|klass| klass.name.to_s}
73
110
  end
111
+ @sti_key_chooser = opts[:key_chooser] || lambda{|inst| Array(inst.model.sti_key_map[inst.model]).last }
74
112
  dataset.row_proc = lambda{|r| model.sti_load(r)}
75
113
  end
76
114
  end
@@ -97,6 +135,10 @@ module Sequel
97
135
  # the value of the sti_key column to the appropriate class to use.
98
136
  attr_reader :sti_model_map
99
137
 
138
+ # A proc which returns the value to use for new instances.
139
+ # This defaults to a lookup in the key map.
140
+ attr_reader :sti_key_chooser
141
+
100
142
  # Copy the necessary attributes to the subclasses, and filter the
101
143
  # subclass's dataset based on the sti_kep_map entry for the class.
102
144
  def inherited(subclass)
@@ -105,18 +147,19 @@ module Sequel
105
147
  sd = sti_dataset
106
148
  skm = sti_key_map
107
149
  smm = sti_model_map
108
- key = skm[subclass]
150
+ skc = sti_key_chooser
151
+ key = Array(skm[subclass]).dup
109
152
  sti_subclass_added(key)
110
- ska = [key]
111
153
  rp = dataset.row_proc
112
- subclass.set_dataset(sd.filter(SQL::QualifiedIdentifier.new(table_name, sk)=>ska), :inherited=>true)
154
+ subclass.set_dataset(sd.filter(SQL::QualifiedIdentifier.new(table_name, sk)=>key), :inherited=>true)
113
155
  subclass.instance_eval do
114
156
  dataset.row_proc = rp
115
157
  @sti_key = sk
116
- @sti_key_array = ska
158
+ @sti_key_array = key
117
159
  @sti_dataset = sd
118
160
  @sti_key_map = skm
119
161
  @sti_model_map = smm
162
+ @sti_key_chooser = skc
120
163
  self.simple_table = nil
121
164
  end
122
165
  end
@@ -131,7 +174,7 @@ module Sequel
131
174
  # keys for all of their descendant classes.
132
175
  def sti_subclass_added(key)
133
176
  if sti_key_array
134
- Sequel.synchronize{sti_key_array << key}
177
+ Sequel.synchronize{sti_key_array.push(*Array(key))}
135
178
  superclass.sti_subclass_added(key)
136
179
  end
137
180
  end
@@ -159,7 +202,7 @@ module Sequel
159
202
  module InstanceMethods
160
203
  # Set the sti_key column based on the sti_key_map.
161
204
  def before_create
162
- send("#{model.sti_key}=", model.sti_key_map[model]) unless self[model.sti_key]
205
+ send("#{model.sti_key}=", model.sti_key_chooser.call(self)) unless self[model.sti_key]
163
206
  super
164
207
  end
165
208
  end
@@ -52,7 +52,7 @@ module Sequel
52
52
  attr_accessor :touch_column
53
53
 
54
54
  # A hash specifying the associations to touch when instances are
55
- # updated or destroyed. Keys are association dataset method name symbols and values
55
+ # updated or destroyed. Keys are association name symbols and values
56
56
  # are column name symbols.
57
57
  attr_reader :touched_associations
58
58
 
@@ -71,8 +71,8 @@ module Sequel
71
71
  associations.flatten.each do |a|
72
72
  a = {a=>touch_column} if a.is_a?(Symbol)
73
73
  a.each do |k,v|
74
- raise(Error, "invalid association: #{k}") unless r = association_reflection(k)
75
- touched_associations[r.dataset_method] = v
74
+ raise(Error, "invalid association: #{k}") unless association_reflection(k)
75
+ touched_associations[k] = v
76
76
  end
77
77
  end
78
78
  end
@@ -113,10 +113,22 @@ module Sequel
113
113
  Sequel::CURRENT_TIMESTAMP
114
114
  end
115
115
 
116
- # Directly update the database using the association dataset for each association.
116
+ # Update the updated at field for all associated objects that should be touched.
117
117
  def touch_associations
118
- model.touched_associations.each do |meth, column|
119
- send(meth).update(column=>touch_association_value)
118
+ model.touched_associations.each do |assoc, column|
119
+ r = model.association_reflection(assoc)
120
+ next unless r.can_have_associated_objects?(self)
121
+ ds = send(r.dataset_method)
122
+
123
+ if ds.send(:joined_dataset?)
124
+ # Can't update all values at once, so update each instance individually.
125
+ # Instead if doing a simple save, update via the instance's dataset,
126
+ # to avoid going into an infinite loop in some cases.
127
+ send(r[:name]).each{|x| x.this.update(column=>touch_association_value)}
128
+ else
129
+ # Update all values at once for performance reasons.
130
+ ds.update(column=>touch_association_value)
131
+ end
120
132
  end
121
133
  end
122
134
 
@@ -385,6 +385,7 @@ module Sequel
385
385
  error_field = a
386
386
  a = Array(a)
387
387
  v = Array(v)
388
+ next if v.empty? || !v.all?
388
389
  ds = o.class.filter(a.zip(v))
389
390
  num_dups = ds.count
390
391
  allow = if num_dups == 0
@@ -224,7 +224,9 @@ module Sequel
224
224
  ds = if where
225
225
  where.call(model.dataset, self, arr)
226
226
  else
227
- model.where(arr.map{|x| [x, send(x)]})
227
+ vals = arr.map{|x| send(x)}
228
+ next unless vals.all?
229
+ model.where(arr.zip(vals))
228
230
  end
229
231
  ds = yield(ds) if block_given?
230
232
  ds = ds.exclude(pk_hash) unless new?
data/lib/sequel/sql.rb CHANGED
@@ -377,6 +377,27 @@ module Sequel
377
377
  SQL::EmulatedFunction.new(:char_length, arg)
378
378
  end
379
379
 
380
+ # Return a delayed evaluation that uses the passed block. This is used
381
+ # to delay evaluations of the code to runtime. For example, with
382
+ # the following code:
383
+ #
384
+ # ds = DB[:table].where{column > Time.now}
385
+ #
386
+ # The filter is fixed to the time that where was called. Unless you are
387
+ # only using the dataset once immediately after creating it, that's
388
+ # probably not desired. If you just want to set it to the time when the
389
+ # query is sent to the database, you can wrap it in Sequel.delay:
390
+ #
391
+ # ds = DB[:table].where{column > Sequel.delay{Time.now}}
392
+ #
393
+ # Note that for dates and timestamps, you are probably better off using
394
+ # Sequel::CURRENT_DATE and Sequel::CURRENT_TIMESTAMP instead of this
395
+ # generic delayed evaluation facility.
396
+ def delay(&block)
397
+ raise(Error, "Sequel.delay requires a block") unless block
398
+ SQL::DelayedEvaluation.new(block)
399
+ end
400
+
380
401
  # Order the given argument descending.
381
402
  # Options:
382
403
  #
@@ -941,27 +962,33 @@ module Sequel
941
962
  # ~from_value_pairs(hash)
942
963
  # from_value_pairs(hash, :OR, true)
943
964
  def self.from_value_pairs(pairs, op=:AND, negate=false)
944
- pairs = pairs.collect do |l,r|
945
- ce = case r
946
- when Range
947
- new(:AND, new(:>=, l, r.begin), new(r.exclude_end? ? :< : :<=, l, r.end))
948
- when ::Array, ::Sequel::Dataset
949
- new(:IN, l, r)
950
- when NegativeBooleanConstant
951
- new(:"IS NOT", l, r.constant)
952
- when BooleanConstant
953
- new(:IS, l, r.constant)
954
- when NilClass, TrueClass, FalseClass
955
- new(:IS, l, r)
956
- when Regexp
957
- StringExpression.like(l, r)
958
- else
959
- new(:'=', l, r)
960
- end
961
- negate ? invert(ce) : ce
962
- end
965
+ pairs = pairs.map{|l,r| from_value_pair(l, r)}
966
+ pairs.map!{|ce| invert(ce)} if negate
963
967
  pairs.length == 1 ? pairs.at(0) : new(op, *pairs)
964
968
  end
969
+
970
+ # Return a BooleanExpression based on the right side of the pair.
971
+ def self.from_value_pair(l, r)
972
+ case r
973
+ when Range
974
+ new(:AND, new(:>=, l, r.begin), new(r.exclude_end? ? :< : :<=, l, r.end))
975
+ when ::Array, ::Sequel::Dataset
976
+ new(:IN, l, r)
977
+ when NegativeBooleanConstant
978
+ new(:"IS NOT", l, r.constant)
979
+ when BooleanConstant
980
+ new(:IS, l, r.constant)
981
+ when NilClass, TrueClass, FalseClass
982
+ new(:IS, l, r)
983
+ when Regexp
984
+ StringExpression.like(l, r)
985
+ when DelayedEvaluation
986
+ Sequel.delay{from_value_pair(l, r.callable.call)}
987
+ else
988
+ new(:'=', l, r)
989
+ end
990
+ end
991
+ private_class_method :from_value_pair
965
992
 
966
993
  # Invert the expression, if possible. If the expression cannot
967
994
  # be inverted, raise an error. An inverted expression should match everything that the
@@ -1140,6 +1167,21 @@ module Sequel
1140
1167
  Constants::NULL=>Constants::NOTNULL, Constants::NOTNULL=>Constants::NULL}
1141
1168
  end
1142
1169
 
1170
+ # Represents a delayed evaluation, encapsulating a callable
1171
+ # object which returns the value to use when called.
1172
+ class DelayedEvaluation < GenericExpression
1173
+ # A callable object that returns the value of the evaluation
1174
+ # when called.
1175
+ attr_reader :callable
1176
+
1177
+ # Set the callable object
1178
+ def initialize(callable)
1179
+ @callable = callable
1180
+ end
1181
+
1182
+ to_s_method :delayed_evaluation_sql, '@callable'
1183
+ end
1184
+
1143
1185
  # Represents an SQL function call.
1144
1186
  class Function < GenericExpression
1145
1187
  # The SQL function to call
@@ -3,7 +3,7 @@ module Sequel
3
3
  MAJOR = 3
4
4
  # The minor version of Sequel. Bumped for every non-patch level
5
5
  # release, generally around once a month.
6
- MINOR = 40
6
+ MINOR = 41
7
7
  # The tiny version of Sequel. Usually 0, only bumped for bugfix
8
8
  # releases that fix regressions from previous versions.
9
9
  TINY = 0
@@ -253,68 +253,82 @@ describe "A Firebird database" do
253
253
 
254
254
  specify "should allow us to name the sequences" do
255
255
  @db.create_table(:posts){primary_key :id, :sequence_name => "seq_test"}
256
- @db.sqls.should == [
257
- "DROP SEQUENCE SEQ_TEST",
258
- "CREATE TABLE POSTS (ID integer PRIMARY KEY )",
259
- "CREATE SEQUENCE SEQ_TEST",
260
- " CREATE TRIGGER BI_POSTS_ID for POSTS\n ACTIVE BEFORE INSERT position 0\n as begin\n if ((new.ID is null) or (new.ID = 0)) then\n begin\n new.ID = next value for seq_test;\n end\n end\n\n"
261
- ]
256
+ check_sqls do
257
+ @db.sqls.should == [
258
+ "DROP SEQUENCE SEQ_TEST",
259
+ "CREATE TABLE POSTS (ID integer PRIMARY KEY )",
260
+ "CREATE SEQUENCE SEQ_TEST",
261
+ " CREATE TRIGGER BI_POSTS_ID for POSTS\n ACTIVE BEFORE INSERT position 0\n as begin\n if ((new.ID is null) or (new.ID = 0)) then\n begin\n new.ID = next value for seq_test;\n end\n end\n\n"
262
+ ]
263
+ end
262
264
  end
263
265
 
264
266
  specify "should allow us to set the starting position for the sequences" do
265
267
  @db.create_table(:posts){primary_key :id, :sequence_start_position => 999}
266
- @db.sqls.should == [
267
- "DROP SEQUENCE SEQ_POSTS_ID",
268
- "CREATE TABLE POSTS (ID integer PRIMARY KEY )",
269
- "CREATE SEQUENCE SEQ_POSTS_ID",
270
- "ALTER SEQUENCE SEQ_POSTS_ID RESTART WITH 999",
271
- " CREATE TRIGGER BI_POSTS_ID for POSTS\n ACTIVE BEFORE INSERT position 0\n as begin\n if ((new.ID is null) or (new.ID = 0)) then\n begin\n new.ID = next value for seq_posts_id;\n end\n end\n\n"
272
- ]
268
+ check_sqls do
269
+ @db.sqls.should == [
270
+ "DROP SEQUENCE SEQ_POSTS_ID",
271
+ "CREATE TABLE POSTS (ID integer PRIMARY KEY )",
272
+ "CREATE SEQUENCE SEQ_POSTS_ID",
273
+ "ALTER SEQUENCE SEQ_POSTS_ID RESTART WITH 999",
274
+ " CREATE TRIGGER BI_POSTS_ID for POSTS\n ACTIVE BEFORE INSERT position 0\n as begin\n if ((new.ID is null) or (new.ID = 0)) then\n begin\n new.ID = next value for seq_posts_id;\n end\n end\n\n"
275
+ ]
276
+ end
273
277
  end
274
278
 
275
279
  specify "should allow us to name and set the starting position for the sequences" do
276
280
  @db.create_table(:posts){primary_key :id, :sequence_name => "seq_test", :sequence_start_position => 999}
277
- @db.sqls.should == [
278
- "DROP SEQUENCE SEQ_TEST",
279
- "CREATE TABLE POSTS (ID integer PRIMARY KEY )",
280
- "CREATE SEQUENCE SEQ_TEST",
281
- "ALTER SEQUENCE SEQ_TEST RESTART WITH 999",
282
- " CREATE TRIGGER BI_POSTS_ID for POSTS\n ACTIVE BEFORE INSERT position 0\n as begin\n if ((new.ID is null) or (new.ID = 0)) then\n begin\n new.ID = next value for seq_test;\n end\n end\n\n"
283
- ]
281
+ check_sqls do
282
+ @db.sqls.should == [
283
+ "DROP SEQUENCE SEQ_TEST",
284
+ "CREATE TABLE POSTS (ID integer PRIMARY KEY )",
285
+ "CREATE SEQUENCE SEQ_TEST",
286
+ "ALTER SEQUENCE SEQ_TEST RESTART WITH 999",
287
+ " CREATE TRIGGER BI_POSTS_ID for POSTS\n ACTIVE BEFORE INSERT position 0\n as begin\n if ((new.ID is null) or (new.ID = 0)) then\n begin\n new.ID = next value for seq_test;\n end\n end\n\n"
288
+ ]
289
+ end
284
290
  end
285
291
 
286
292
  specify "should allow us to name the triggers" do
287
293
  @db.create_table(:posts){primary_key :id, :trigger_name => "trig_test"}
288
- @db.sqls.should == [
289
- "DROP SEQUENCE SEQ_POSTS_ID",
290
- "CREATE TABLE POSTS (ID integer PRIMARY KEY )",
291
- "CREATE SEQUENCE SEQ_POSTS_ID",
292
- " CREATE TRIGGER TRIG_TEST for POSTS\n ACTIVE BEFORE INSERT position 0\n as begin\n if ((new.ID is null) or (new.ID = 0)) then\n begin\n new.ID = next value for seq_posts_id;\n end\n end\n\n"
293
- ]
294
+ check_sqls do
295
+ @db.sqls.should == [
296
+ "DROP SEQUENCE SEQ_POSTS_ID",
297
+ "CREATE TABLE POSTS (ID integer PRIMARY KEY )",
298
+ "CREATE SEQUENCE SEQ_POSTS_ID",
299
+ " CREATE TRIGGER TRIG_TEST for POSTS\n ACTIVE BEFORE INSERT position 0\n as begin\n if ((new.ID is null) or (new.ID = 0)) then\n begin\n new.ID = next value for seq_posts_id;\n end\n end\n\n"
300
+ ]
301
+ end
294
302
  end
295
303
 
296
304
  specify "should allow us to not create the sequence" do
297
305
  @db.create_table(:posts){primary_key :id, :create_sequence => false}
298
- @db.sqls.should == [
299
- "CREATE TABLE POSTS (ID integer PRIMARY KEY )",
300
- " CREATE TRIGGER BI_POSTS_ID for POSTS\n ACTIVE BEFORE INSERT position 0\n as begin\n if ((new.ID is null) or (new.ID = 0)) then\n begin\n new.ID = next value for seq_posts_id;\n end\n end\n\n"
301
- ]
306
+ check_sqls do
307
+ @db.sqls.should == [
308
+ "CREATE TABLE POSTS (ID integer PRIMARY KEY )",
309
+ " CREATE TRIGGER BI_POSTS_ID for POSTS\n ACTIVE BEFORE INSERT position 0\n as begin\n if ((new.ID is null) or (new.ID = 0)) then\n begin\n new.ID = next value for seq_posts_id;\n end\n end\n\n"
310
+ ]
311
+ end
302
312
  end
303
313
 
304
314
  specify "should allow us to not create the trigger" do
305
315
  @db.create_table(:posts){primary_key :id, :create_trigger => false}
306
- @db.sqls.should == [
307
- "DROP SEQUENCE SEQ_POSTS_ID",
308
- "CREATE TABLE POSTS (ID integer PRIMARY KEY )",
309
- "CREATE SEQUENCE SEQ_POSTS_ID",
310
- ]
316
+ check_sqls do
317
+ @db.sqls.should == [
318
+ "DROP SEQUENCE SEQ_POSTS_ID",
319
+ "CREATE TABLE POSTS (ID integer PRIMARY KEY )",
320
+ "CREATE SEQUENCE SEQ_POSTS_ID",
321
+ ]
322
+ end
311
323
  end
312
324
 
313
325
  specify "should allow us to not create either the sequence nor the trigger" do
314
326
  @db.create_table(:posts){primary_key :id, :create_sequence => false, :create_trigger => false}
315
- @db.sqls.should == [
316
- "CREATE TABLE POSTS (ID integer PRIMARY KEY )"
317
- ]
327
+ check_sqls do
328
+ @db.sqls.should == [
329
+ "CREATE TABLE POSTS (ID integer PRIMARY KEY )"
330
+ ]
331
+ end
318
332
  end
319
333
 
320
334
  specify "should support column operations" do