sequel 2.5.0 → 2.6.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 (41) hide show
  1. data/CHANGELOG +48 -0
  2. data/Rakefile +16 -6
  3. data/bin/sequel +0 -0
  4. data/doc/cheat_sheet.rdoc +4 -4
  5. data/doc/schema.rdoc +9 -0
  6. data/lib/sequel_core/adapters/jdbc.rb +7 -7
  7. data/lib/sequel_core/adapters/mysql.rb +6 -11
  8. data/lib/sequel_core/adapters/shared/mssql.rb +21 -1
  9. data/lib/sequel_core/adapters/shared/mysql.rb +19 -27
  10. data/lib/sequel_core/adapters/shared/postgres.rb +67 -11
  11. data/lib/sequel_core/adapters/shared/sqlite.rb +11 -22
  12. data/lib/sequel_core/adapters/sqlite.rb +8 -4
  13. data/lib/sequel_core/core_sql.rb +4 -4
  14. data/lib/sequel_core/database.rb +56 -31
  15. data/lib/sequel_core/database/schema.rb +13 -5
  16. data/lib/sequel_core/dataset/convenience.rb +1 -1
  17. data/lib/sequel_core/dataset/sql.rb +30 -15
  18. data/lib/sequel_core/migration.rb +7 -0
  19. data/lib/sequel_core/schema/generator.rb +13 -2
  20. data/lib/sequel_core/schema/sql.rb +27 -81
  21. data/lib/sequel_core/sql.rb +5 -2
  22. data/lib/sequel_model.rb +8 -2
  23. data/lib/sequel_model/associations.rb +7 -4
  24. data/lib/sequel_model/base.rb +10 -1
  25. data/lib/sequel_model/eager_loading.rb +29 -10
  26. data/lib/sequel_model/record.rb +19 -5
  27. data/spec/adapters/mysql_spec.rb +2 -2
  28. data/spec/adapters/postgres_spec.rb +26 -5
  29. data/spec/adapters/sqlite_spec.rb +42 -8
  30. data/spec/integration/eager_loader_test.rb +51 -58
  31. data/spec/integration/schema_test.rb +28 -4
  32. data/spec/sequel_core/core_sql_spec.rb +3 -0
  33. data/spec/sequel_core/database_spec.rb +24 -0
  34. data/spec/sequel_core/dataset_spec.rb +25 -17
  35. data/spec/sequel_core/schema_spec.rb +58 -26
  36. data/spec/sequel_model/eager_loading_spec.rb +65 -0
  37. data/spec/sequel_model/hooks_spec.rb +1 -1
  38. data/spec/sequel_model/model_spec.rb +1 -0
  39. data/spec/sequel_model/record_spec.rb +74 -1
  40. data/spec/sequel_model/spec_helper.rb +8 -0
  41. metadata +5 -3
@@ -481,11 +481,14 @@ module Sequel
481
481
  # The default value if no conditions are true
482
482
  attr_reader :default
483
483
 
484
+ # The expression to test the conditions against
485
+ attr_reader :expression
486
+
484
487
  # Create an object with the given conditions and
485
488
  # default value.
486
- def initialize(conditions, default)
489
+ def initialize(conditions, default, expression = nil)
487
490
  raise(Sequel::Error, 'CaseExpression conditions must be an array with all_two_pairs') unless Array === conditions and conditions.all_two_pairs?
488
- @conditions, @default = conditions, default
491
+ @conditions, @default, @expression = conditions, default, expression
489
492
  end
490
493
 
491
494
  # Delegate the creation of the resulting SQL to the given dataset,
@@ -50,8 +50,9 @@ module Sequel
50
50
  # cache_store, cache_ttl, dataset_methods, primary_key, restricted_columns,
51
51
  # sti_dataset, and sti_key. You should not usually need to
52
52
  # access these directly.
53
- # * The following class level attr_accessors are created: raise_on_save_failure,
54
- # strict_param_setting, typecast_empty_string_to_nil, and typecast_on_assignment:
53
+ # * The following class level attr_accessors are created: raise_on_typecast_failure,
54
+ # raise_on_save_failure, strict_param_setting, typecast_empty_string_to_nil,
55
+ # and typecast_on_assignment:
55
56
  #
56
57
  # # Don't raise an error if a validation attempt fails in
57
58
  # # save/create/save_changes/etc.
@@ -72,6 +73,11 @@ module Sequel
72
73
  # Model.typecast_empty_string_to_nil = false
73
74
  # m.number = ''
74
75
  # m.number # => '' instead of nil
76
+ # # Don't raise if unable to typecast data for a column
77
+ # Model.typecast_empty_string_to_nil = true
78
+ # Model.raise_on_typecast_failure = false
79
+ # m.not_null_column = '' # => nil
80
+ # m.number = 'A' # => 'A'
75
81
  #
76
82
  # * The following class level method aliases are defined:
77
83
  # * Model.dataset= => set_dataset
@@ -135,6 +135,8 @@ module Sequel::Model::Associations
135
135
  # an array with two arguments for the value to specify a limit and offset.
136
136
  # - :order - the column(s) by which to order the association dataset. Can be a
137
137
  # singular column or an array.
138
+ # - :order_eager_graph - Whether to add the order to the dataset's order when graphing
139
+ # via eager graph. Defaults to true, so set to false to disable.
138
140
  # - :read_only - Do not add a setter method (for many_to_one or one_to_many with :one_to_one),
139
141
  # or add_/remove_/remove_all_ methods (for one_to_many, many_to_many)
140
142
  # - :reciprocal - the symbol name of the reciprocal association,
@@ -192,6 +194,7 @@ module Sequel::Model::Associations
192
194
  opts = AssociationReflection.new.merge!(opts)
193
195
  opts[:eager_block] = block unless opts.include?(:eager_block)
194
196
  opts[:graph_join_type] ||= :left_outer
197
+ opts[:order_eager_graph] = true unless opts.include?(:order_eager_graph)
195
198
  opts[:graph_conditions] = opts[:graph_conditions] ? opts[:graph_conditions].to_a : []
196
199
  opts[:graph_select] = Array(opts[:graph_select]) if opts[:graph_select]
197
200
  [:before_add, :before_remove, :after_add, :after_remove, :after_load, :extend].each do |cb_type|
@@ -325,12 +328,12 @@ module Sequel::Model::Associations
325
328
  opts[:eager_loader] ||= proc do |key_hash, records, associations|
326
329
  h = key_hash[key]
327
330
  keys = h.keys
331
+ # Default the cached association to nil, so any object that doesn't have it
332
+ # populated will have cached the negative lookup.
333
+ records.each{|object| object.associations[name] = nil}
328
334
  # Skip eager loading if no objects have a foreign key for this association
329
335
  unless keys.empty?
330
- # Default the cached association to nil, so any object that doesn't have it
331
- # populated will have cached the negative lookup.
332
- records.each{|object| object.associations[name] = nil}
333
- model.eager_loading_dataset(opts, opts.associated_class.filter(opts.associated_primary_key.qualify(opts.associated_class.table_name)=>keys), opts.select, associations).all do |assoc_record|
336
+ model.eager_loading_dataset(opts, opts.associated_class.filter(opts.associated_primary_key.qualify(opts.associated_class.table_name)=>keys), opts.select, associations).all do |assoc_record|
334
337
  next unless objects = h[assoc_record.pk]
335
338
  objects.each{|object| object.associations[name] = assoc_record}
336
339
  end
@@ -9,6 +9,7 @@ module Sequel
9
9
  @dataset_methods = {}
10
10
  @primary_key = :id
11
11
  @raise_on_save_failure = true
12
+ @raise_on_typecast_failure = true
12
13
  @restrict_primary_key = true
13
14
  @restricted_columns = nil
14
15
  @sti_dataset = nil
@@ -32,6 +33,10 @@ module Sequel
32
33
  # to save/create/save_changes/etc.
33
34
  metaattr_accessor :raise_on_save_failure
34
35
 
36
+ # Whether to raise an error when unable to typecast data for a column
37
+ # (default: true)
38
+ metaattr_accessor :raise_on_typecast_failure
39
+
35
40
  # Which columns should not be update in a call to set
36
41
  # (default: no columns).
37
42
  metaattr_reader :restricted_columns
@@ -70,7 +75,8 @@ module Sequel
70
75
  :@cache_ttl=>nil, :@dataset_methods=>:dup, :@primary_key=>nil,
71
76
  :@raise_on_save_failure=>nil, :@restricted_columns=>:dup, :@restrict_primary_key=>nil,
72
77
  :@sti_dataset=>nil, :@sti_key=>nil, :@strict_param_setting=>nil,
73
- :@typecast_empty_string_to_nil=>nil, :@typecast_on_assignment=>nil}
78
+ :@typecast_empty_string_to_nil=>nil, :@typecast_on_assignment=>nil,
79
+ :@raise_on_typecast_failure=>nil}
74
80
 
75
81
  # Returns the first record from the database matching the conditions.
76
82
  # If a hash is given, it is used as the conditions. If another
@@ -470,6 +476,9 @@ module Sequel
470
476
  # returned by the schema.
471
477
  cols = schema_array.collect{|k,v| k}
472
478
  set_columns(cols)
479
+ # Set the primary key(s) based on the schema information
480
+ pks = schema_array.collect{|k,v| k if v[:primary_key]}.compact
481
+ pks.length > 0 ? set_primary_key(*pks) : no_primary_key
473
482
  # Also set the columns for the dataset, so the dataset
474
483
  # doesn't have to do a query to get them.
475
484
  dataset.instance_variable_set(:@columns, cols)
@@ -87,10 +87,8 @@ module Sequel::Model::Associations::EagerLoading
87
87
  # create large cartesian products. If you must graph multiple *_to_many associations,
88
88
  # make sure your filters are specific if you have a large database.
89
89
  #
90
- # This does not respect each association's order, as all associations are loaded in
91
- # a single query. If you want to order the results, you must manually call .order.
92
- #
93
- # #eager_graph probably won't work correctly on a limited dataset, unless you are
90
+ # Each association's order, if definied, is respected. #eager_graph probably
91
+ # won't work correctly on a limited dataset, unless you are
94
92
  # only graphing many_to_one associations.
95
93
  #
96
94
  # Does not use the block defined for the association, since it does a single query for
@@ -149,6 +147,8 @@ module Sequel::Model::Associations::EagerLoading
149
147
  ds = ds.graph(r[:join_table], use_jt_only_conditions ? r[:graph_join_table_only_conditions] : [[r[:left_key], model.primary_key.qualify(ta)]] + r[:graph_join_table_conditions], :select=>false, :table_alias=>ds.eager_unique_table_alias(ds, r[:join_table]), :join_type=>r[:graph_join_table_join_type], &r[:graph_join_table_block])
150
148
  ds.graph(klass, use_only_conditions ? only_conditions : [[klass.primary_key, r[:right_key]]] + conditions, :select=>select, :table_alias=>assoc_table_alias, :join_type=>join_type, &graph_block)
151
149
  end
150
+
151
+ ds = ds.order_more(*Array(r[:order]).map{|c| eager_graph_qualify_order(assoc_table_alias, c)}) if r[:order] and r[:order_eager_graph]
152
152
  eager_graph = ds.opts[:eager_graph]
153
153
  eager_graph[:requirements][assoc_table_alias] = requirements.dup
154
154
  eager_graph[:alias_association_name_map][assoc_table_alias] = assoc_name
@@ -250,16 +250,18 @@ module Sequel::Model::Associations::EagerLoading
250
250
  # Will either be the table_alias itself or table_alias_N for some integer
251
251
  # N (starting at 0 and increasing until an unused one is found).
252
252
  def eager_unique_table_alias(ds, table_alias)
253
- if (graph = ds.opts[:graph]) && (table_aliases = graph[:table_aliases]) && (table_aliases.include?(table_alias))
253
+ used_aliases = ds.opts[:from]
254
+ graph = ds.opts[:graph]
255
+ used_aliases += graph[:table_aliases].keys if graph
256
+ if used_aliases.include?(table_alias)
254
257
  i = 0
255
258
  loop do
256
259
  ta = :"#{table_alias}_#{i}"
257
- return ta unless table_aliases[ta]
260
+ return ta unless used_aliases.include?(ta)
258
261
  i += 1
259
262
  end
260
- else
261
- table_alias
262
263
  end
264
+ table_alias
263
265
  end
264
266
 
265
267
  private
@@ -323,13 +325,30 @@ module Sequel::Model::Associations::EagerLoading
323
325
  else
324
326
  list = record.send(alias_map[ta])
325
327
  list.uniq!
326
- # Recurse into dependencies
327
- list.each{|rec| eager_graph_make_associations_unique(rec, deps, alias_map, type_map)}
328
328
  end
329
+ # Recurse into dependencies
330
+ eager_graph_make_associations_unique(list, deps, alias_map, type_map) if list
329
331
  end
330
332
  end
331
333
  end
332
334
 
335
+ # Qualify the given expression if necessary. The only expressions which are qualified are
336
+ # unqualified symbols and identifiers, either of which may by sorted.
337
+ def eager_graph_qualify_order(table_alias, expression)
338
+ case expression
339
+ when Symbol
340
+ table, column, aliaz = split_symbol(expression)
341
+ raise(Sequel::Error, "Can't use an aliased expression in the :order option") if aliaz
342
+ table ? expression : Sequel::SQL::QualifiedIdentifier.new(table_alias, expression)
343
+ when Sequel::SQL::Identifier
344
+ Sequel::SQL::QualifiedIdentifier.new(table_alias, expression)
345
+ when Sequel::SQL::OrderedExpression
346
+ Sequel::SQL::OrderedExpression.new(eager_graph_qualify_order(table_alias, expression.expression), expression.descending)
347
+ else
348
+ expression
349
+ end
350
+ end
351
+
333
352
  # Eagerly load all specified associations
334
353
  def eager_load(a)
335
354
  return if a.empty?
@@ -2,7 +2,7 @@ module Sequel
2
2
  class Model
3
3
  # The setter methods (methods ending with =) that are never allowed
4
4
  # to be called automatically via set.
5
- RESTRICTED_SETTER_METHODS = %w"== === []= taguri= typecast_empty_string_to_nil= typecast_on_assignment= strict_param_setting= raise_on_save_failure="
5
+ RESTRICTED_SETTER_METHODS = %w"== === []= taguri= typecast_empty_string_to_nil= typecast_on_assignment= strict_param_setting= raise_on_save_failure= raise_on_typecast_failure="
6
6
 
7
7
  # The current cached associations. A hash with the keys being the
8
8
  # association name symbols and the values being the associated object
@@ -17,6 +17,10 @@ module Sequel
17
17
  # returning nil on a failure to save/save_changes/etc.
18
18
  attr_writer :raise_on_save_failure
19
19
 
20
+ # Whether this model instance should raise an error when it cannot typecast
21
+ # data for a column correctly.
22
+ attr_writer :raise_on_typecast_failure
23
+
20
24
  # Whether this model instance should raise an error if attempting
21
25
  # to call a method through set/update and their variants that either
22
26
  # doesn't exist or access to it is denied.
@@ -54,6 +58,7 @@ module Sequel
54
58
  @strict_param_setting = model.strict_param_setting
55
59
  @typecast_on_assignment = model.typecast_on_assignment
56
60
  @typecast_empty_string_to_nil = model.typecast_empty_string_to_nil
61
+ @raise_on_typecast_failure = model.raise_on_typecast_failure
57
62
  if from_db
58
63
  @new = false
59
64
  @values = values
@@ -199,7 +204,8 @@ module Sequel
199
204
  # Creates or updates the record, after making sure the record
200
205
  # is valid. If the record is not valid, or before_save,
201
206
  # before_create (if new?), or before_update (if !new?) return
202
- # false, returns nil unless raise_on_save_failure is true.
207
+ # false, returns nil unless raise_on_save_failure is true (if it
208
+ # is true, it raises an error).
203
209
  # Otherwise, returns self. You can provide an optional list of
204
210
  # columns to update, in which case it only updates those columns.
205
211
  def save(*columns)
@@ -212,7 +218,7 @@ module Sequel
212
218
  # in which case it only updates those columns.
213
219
  # If before_save, before_create (if new?), or before_update
214
220
  # (if !new?) return false, returns nil unless raise_on_save_failure
215
- # is true. Otherwise, returns self.
221
+ # is true (if it is true, it raises an error). Otherwise, returns self.
216
222
  def save!(*columns)
217
223
  opts = columns.extract_options!
218
224
  return save_failure(:save) if before_save == false
@@ -531,8 +537,16 @@ module Sequel
531
537
  def typecast_value(column, value)
532
538
  return value unless @typecast_on_assignment && @db_schema && (col_schema = @db_schema[column])
533
539
  value = nil if value == '' and @typecast_empty_string_to_nil and col_schema[:type] and ![:string, :blob].include?(col_schema[:type])
534
- raise(Error, "nil/NULL is not allowed for the #{column} column") if value.nil? && (col_schema[:allow_null] == false)
535
- model.db.typecast_value(col_schema[:type], value)
540
+ raise(Error::InvalidValue, "nil/NULL is not allowed for the #{column} column") if @raise_on_typecast_failure && value.nil? && (col_schema[:allow_null] == false)
541
+ begin
542
+ model.db.typecast_value(col_schema[:type], value)
543
+ rescue Error::InvalidValue
544
+ if @raise_on_typecast_failure
545
+ raise
546
+ else
547
+ value
548
+ end
549
+ end
536
550
  end
537
551
 
538
552
  # Set the columns, filtered by the only and except arrays.
@@ -69,10 +69,10 @@ context "A MySQL database" do
69
69
  end
70
70
 
71
71
  specify "should correctly parse the schema" do
72
- @db.schema(:booltest, :reload=>true).should == [[:value, {:type=>:boolean, :allow_null=>true, :max_chars=>nil, :default=>nil, :db_type=>"tinyint", :numeric_precision=>3}]]
72
+ @db.schema(:booltest, :reload=>true).should == [[:value, {:type=>:boolean, :allow_null=>true, :primary_key=>false, :default=>nil, :db_type=>"tinyint(4)"}]]
73
73
 
74
74
  Sequel.convert_tinyint_to_bool = false
75
- @db.schema(:booltest, :reload=>true).should == [[:value, {:type=>:integer, :allow_null=>true, :max_chars=>nil, :default=>nil, :db_type=>"tinyint", :numeric_precision=>3}]]
75
+ @db.schema(:booltest, :reload=>true).should == [[:value, {:type=>:integer, :allow_null=>true, :primary_key=>false, :default=>nil, :db_type=>"tinyint(4)"}]]
76
76
  end
77
77
 
78
78
  specify "should get the schema all database tables if no table name is used" do
@@ -47,13 +47,14 @@ context "A PostgreSQL database" do
47
47
  end
48
48
 
49
49
  specify "should correctly parse the schema" do
50
+ require 'logger'
50
51
  @db.schema(:test3, :reload=>true).should == [
51
- [:value, {:type=>:integer, :allow_null=>true, :max_chars=>nil, :default=>nil, :db_type=>"integer", :numeric_precision=>32}],
52
- [:time, {:type=>:datetime, :allow_null=>true, :max_chars=>nil, :default=>nil, :db_type=>"timestamp without time zone", :numeric_precision=>nil}]
52
+ [:value, {:type=>:integer, :allow_null=>true, :default=>nil, :db_type=>"integer", :primary_key=>false}],
53
+ [:time, {:type=>:datetime, :allow_null=>true, :default=>nil, :db_type=>"timestamp without time zone", :primary_key=>false}]
53
54
  ]
54
55
  @db.schema(:test4, :reload=>true).should == [
55
- [:name, {:type=>:string, :allow_null=>true, :max_chars=>20, :default=>nil, :db_type=>"character varying", :numeric_precision=>nil}],
56
- [:value, {:type=>:blob, :allow_null=>true, :max_chars=>nil, :default=>nil, :db_type=>"bytea", :numeric_precision=>nil}]
56
+ [:name, {:type=>:string, :allow_null=>true, :default=>nil, :db_type=>"character varying(20)", :primary_key=>false}],
57
+ [:value, {:type=>:blob, :allow_null=>true, :default=>nil, :db_type=>"bytea", :primary_key=>false}]
57
58
  ]
58
59
  end
59
60
 
@@ -343,7 +344,7 @@ context "A PostgreSQL database" do
343
344
  end
344
345
  POSTGRES_DB.create_table_sql_list(:posts, *g.create_info).should == [
345
346
  "CREATE TABLE posts (title text, body text)",
346
- "CREATE INDEX posts_title_body_index ON posts USING gin (to_tsvector(title || body))"
347
+ "CREATE INDEX posts_title_body_index ON posts USING gin (to_tsvector('simple', title || body))"
347
348
  ]
348
349
  end
349
350
 
@@ -494,3 +495,23 @@ context "Postgres::Dataset#insert" do
494
495
  ds.insert(:name=>'a').should == nil
495
496
  end
496
497
  end
498
+
499
+ if POSTGRES_DB.server_version >= 80300
500
+
501
+ POSTGRES_DB.create_table! :test6 do
502
+ text :title
503
+ full_text_index [:title]
504
+ end
505
+
506
+ context "PostgreSQL tsearch2" do
507
+
508
+ specify "should search by indexed column" do
509
+ # tsearch is by default included from PostgreSQL 8.3
510
+ ds = POSTGRES_DB[:test6]
511
+ record = {:title => "oopsla conference"}
512
+ ds << record
513
+ actual = ds.full_text_search(:title, "oopsla")
514
+ actual.should include(record)
515
+ end
516
+ end
517
+ end
@@ -9,6 +9,9 @@ context "An SQLite database" do
9
9
  before do
10
10
  @db = SQLITE_DB
11
11
  end
12
+ after do
13
+ Sequel.datetime_class = Time
14
+ end
12
15
 
13
16
  if SQLITE_DB.auto_vacuum == :none
14
17
  specify "should support getting pragma values" do
@@ -128,12 +131,17 @@ context "An SQLite database" do
128
131
  @db[:items2].count.should == 2
129
132
  end
130
133
 
131
- specify "should support timestamps" do
132
- t1 = Time.at(Time.now.to_i) #normalize time
133
- @db.create_table!(:time) {timestamp :t}
134
- @db[:time] << {:t => t1}
135
- x = @db[:time].first[:t]
136
- [t1.iso8601, t1.to_s].should include(x.respond_to?(:iso8601) ? x.iso8601 : x.to_s)
134
+ specify "should support timestamps and datetimes and respect datetime_class" do
135
+ @db.create_table!(:time){timestamp :t; datetime :d}
136
+ t1 = Time.at(1)
137
+ @db[:time] << {:t => t1, :d => t1.to_i}
138
+ @db[:time] << {:t => t1.to_i, :d => t1}
139
+ @db[:time].map(:t).should == [t1, t1]
140
+ @db[:time].map(:d).should == [t1, t1]
141
+ t2 = t1.iso8601.to_datetime
142
+ Sequel.datetime_class = DateTime
143
+ @db[:time].map(:t).should == [t2, t2]
144
+ @db[:time].map(:d).should == [t2, t2]
137
145
  end
138
146
 
139
147
  specify "should support sequential primary keys" do
@@ -159,12 +167,12 @@ context "An SQLite database" do
159
167
 
160
168
  specify "should correctly parse the schema" do
161
169
  @db.create_table!(:time2) {timestamp :t}
162
- @db.schema(:time2, :reload=>true).should == [[:t, {:type=>:datetime, :allow_null=>true, :max_chars=>nil, :default=>nil, :db_type=>"timestamp", :numeric_precision=>nil, :primary_key=>false}]]
170
+ @db.schema(:time2, :reload=>true).should == [[:t, {:type=>:datetime, :allow_null=>true, :default=>nil, :db_type=>"timestamp", :primary_key=>false}]]
163
171
  end
164
172
 
165
173
  specify "should get the schema all database tables if no table name is used" do
166
174
  @db.create_table!(:time2) {timestamp :t}
167
- @db.schema(:time2, :reload=>true).should == @db.schema(nil, :reload=>true)[:time]
175
+ @db.schema(:time2, :reload=>true).should == @db.schema(nil, :reload=>true)[:time2]
168
176
  end
169
177
  end
170
178
 
@@ -240,6 +248,32 @@ context "An SQLite dataset" do
240
248
  end
241
249
  end
242
250
 
251
+ context "An SQLite dataset AS clause" do
252
+ specify "should use a string literal for :col___alias" do
253
+ SQLITE_DB.literal(:c___a).should == "c AS 'a'"
254
+ end
255
+
256
+ specify "should use a string literal for :table__col___alias" do
257
+ SQLITE_DB.literal(:t__c___a).should == "t.c AS 'a'"
258
+ end
259
+
260
+ specify "should use a string literal for :column.as(:alias)" do
261
+ SQLITE_DB.literal(:c.as(:a)).should == "c AS 'a'"
262
+ end
263
+
264
+ specify "should use a string literal in the SELECT clause" do
265
+ SQLITE_DB[:t].select(:c___a).sql.should == "SELECT c AS 'a' FROM t"
266
+ end
267
+
268
+ specify "should use a string literal in the FROM clause" do
269
+ SQLITE_DB[:t___a].sql.should == "SELECT * FROM t AS 'a'"
270
+ end
271
+
272
+ specify "should use a string literal in the JOIN clause" do
273
+ SQLITE_DB[:t].join_table(:natural, :j, nil, :a).sql.should == "SELECT * FROM t NATURAL JOIN j AS 'a'"
274
+ end
275
+ end
276
+
243
277
  context "An SQLite dataset" do
244
278
  setup do
245
279
  SQLITE_DB.create_table! :items do
@@ -2,13 +2,12 @@ require File.join(File.dirname(__FILE__), 'spec_helper.rb')
2
2
 
3
3
  describe "Eagerly loading a tree structure" do
4
4
  before do
5
+ INTEGRATION_DB.instance_variable_set(:@schemas, nil)
6
+ INTEGRATION_DB.create_table!(:nodes) do
7
+ primary_key :id
8
+ foreign_key :parent_id, :nodes
9
+ end
5
10
  class ::Node < Sequel::Model
6
- set_schema do
7
- primary_key :id
8
- foreign_key :parent_id, :nodes
9
- end
10
- create_table!
11
-
12
11
  many_to_one :parent
13
12
  one_to_many :children, :key=>:parent_id
14
13
 
@@ -146,11 +145,11 @@ describe "Association Extensions" do
146
145
  @opts[:models][nil].create(vals.merge(:author_id=>author_id))
147
146
  end
148
147
  end
148
+ INTEGRATION_DB.instance_variable_set(:@schemas, nil)
149
+ INTEGRATION_DB.create_table!(:authors) do
150
+ primary_key :id
151
+ end
149
152
  class ::Author < Sequel::Model
150
- set_schema do
151
- primary_key :id
152
- end
153
- create_table!
154
153
  one_to_many :authorships, :extend=>FindOrCreate, :dataset=>(proc do
155
154
  key = pk
156
155
  ds = Authorship.filter(:author_id=>key)
@@ -160,13 +159,12 @@ describe "Association Extensions" do
160
159
  ds
161
160
  end)
162
161
  end
162
+ INTEGRATION_DB.create_table!(:authorships) do
163
+ primary_key :id
164
+ foreign_key :author_id, :authors
165
+ text :name
166
+ end
163
167
  class ::Authorship < Sequel::Model
164
- set_schema do
165
- primary_key :id
166
- foreign_key :author_id, :authors
167
- text :name
168
- end
169
- create_table!
170
168
  many_to_one :author
171
169
  end
172
170
  @author = Author.create
@@ -212,11 +210,11 @@ end
212
210
 
213
211
  describe "has_many :through has_many and has_one :through belongs_to" do
214
212
  before do
213
+ INTEGRATION_DB.instance_variable_set(:@schemas, nil)
214
+ INTEGRATION_DB.create_table!(:firms) do
215
+ primary_key :id
216
+ end
215
217
  class ::Firm < Sequel::Model
216
- set_schema do
217
- primary_key :id
218
- end
219
- create_table!
220
218
  one_to_many :clients
221
219
  one_to_many :invoices, :read_only=>true, \
222
220
  :dataset=>proc{Invoice.eager_graph(:client).filter(:client__firm_id=>pk)}, \
@@ -237,22 +235,20 @@ describe "has_many :through has_many and has_one :through belongs_to" do
237
235
  end)
238
236
  end
239
237
 
238
+ INTEGRATION_DB.create_table!(:clients) do
239
+ primary_key :id
240
+ foreign_key :firm_id, :firms
241
+ end
240
242
  class ::Client < Sequel::Model
241
- set_schema do
242
- primary_key :id
243
- foreign_key :firm_id, :firms
244
- end
245
- create_table!
246
243
  many_to_one :firm
247
244
  one_to_many :invoices
248
245
  end
249
246
 
247
+ INTEGRATION_DB.create_table!(:invoices) do
248
+ primary_key :id
249
+ foreign_key :client_id, :clients
250
+ end
250
251
  class ::Invoice < Sequel::Model
251
- set_schema do
252
- primary_key :id
253
- foreign_key :client_id, :clients
254
- end
255
- create_table!
256
252
  many_to_one :client
257
253
  many_to_one :firm, :key=>nil, :read_only=>true, \
258
254
  :dataset=>proc{Firm.eager_graph(:clients).filter(:clients__id=>client_id)}, \
@@ -304,7 +300,7 @@ describe "has_many :through has_many and has_one :through belongs_to" do
304
300
 
305
301
  it "should return has_many :through has_many records for a single object" do
306
302
  invs = @firm1.invoices.sort_by{|x| x.pk}
307
- sqls_should_be('SELECT invoices.id, invoices.client_id, client.id AS client_id_0, client.firm_id FROM invoices LEFT OUTER JOIN clients AS client ON (client.id = invoices.client_id) WHERE (client.firm_id = 1)')
303
+ sqls_should_be("SELECT invoices.id, invoices.client_id, client.id AS 'client_id_0', client.firm_id FROM invoices LEFT OUTER JOIN clients AS 'client' ON (client.id = invoices.client_id) WHERE (client.firm_id = 1)")
308
304
  invs.should == [@invoice1, @invoice2, @invoice3]
309
305
  invs[0].client.should == @client1
310
306
  invs[1].client.should == @client1
@@ -316,7 +312,7 @@ describe "has_many :through has_many and has_one :through belongs_to" do
316
312
 
317
313
  it "should eagerly load has_many :through has_many records for multiple objects" do
318
314
  firms = Firm.order(:id).eager(:invoices).all
319
- sqls_should_be("SELECT * FROM firms ORDER BY id", "SELECT invoices.id, invoices.client_id, client.id AS client_id_0, client.firm_id FROM invoices LEFT OUTER JOIN clients AS client ON (client.id = invoices.client_id) WHERE (client.firm_id IN (1, 2))")
315
+ sqls_should_be("SELECT * FROM firms ORDER BY id", "SELECT invoices.id, invoices.client_id, client.id AS 'client_id_0', client.firm_id FROM invoices LEFT OUTER JOIN clients AS 'client' ON (client.id = invoices.client_id) WHERE (client.firm_id IN (1, 2))")
320
316
  firms.should == [@firm1, @firm2]
321
317
  firm1, firm2 = firms
322
318
  invs1 = firm1.invoices.sort_by{|x| x.pk}
@@ -337,7 +333,7 @@ describe "has_many :through has_many and has_one :through belongs_to" do
337
333
 
338
334
  it "should return has_one :through belongs_to records for a single object" do
339
335
  firm = @invoice1.firm
340
- sqls_should_be('SELECT firms.id, clients.id AS clients_id, clients.firm_id FROM firms LEFT OUTER JOIN clients ON (clients.firm_id = firms.id) WHERE (clients.id = 1)')
336
+ sqls_should_be("SELECT firms.id, clients.id AS 'clients_id', clients.firm_id FROM firms LEFT OUTER JOIN clients ON (clients.firm_id = firms.id) WHERE (clients.id = 1)")
341
337
  firm.should == @firm1
342
338
  @invoice1.client.should == @client1
343
339
  @invoice1.client.firm.should == @firm1
@@ -347,7 +343,7 @@ describe "has_many :through has_many and has_one :through belongs_to" do
347
343
 
348
344
  it "should eagerly load has_one :through belongs_to records for multiple objects" do
349
345
  invs = Invoice.order(:id).eager(:firm).all
350
- sqls_should_be("SELECT * FROM invoices ORDER BY id", "SELECT firms.id, clients.id AS clients_id, clients.firm_id FROM firms LEFT OUTER JOIN clients ON (clients.firm_id = firms.id) WHERE (clients.id IN (1, 2, 3))")
346
+ sqls_should_be("SELECT * FROM invoices ORDER BY id", "SELECT firms.id, clients.id AS 'clients_id', clients.firm_id FROM firms LEFT OUTER JOIN clients ON (clients.firm_id = firms.id) WHERE (clients.id IN (1, 2, 3))")
351
347
  invs.should == [@invoice1, @invoice2, @invoice3, @invoice4, @invoice5]
352
348
  invs[0].firm.should == @firm1
353
349
  invs[0].client.should == @client1
@@ -375,13 +371,13 @@ end
375
371
 
376
372
  describe "Polymorphic Associations" do
377
373
  before do
374
+ INTEGRATION_DB.instance_variable_set(:@schemas, nil)
375
+ INTEGRATION_DB.create_table!(:assets) do
376
+ primary_key :id
377
+ integer :attachable_id
378
+ text :attachable_type
379
+ end
378
380
  class ::Asset < Sequel::Model
379
- set_schema do
380
- primary_key :id
381
- integer :attachable_id
382
- text :attachable_type
383
- end
384
- create_table!
385
381
  many_to_one :attachable, :reciprocal=>:assets, \
386
382
  :dataset=>(proc do
387
383
  klass = attachable_type.constantize
@@ -411,11 +407,10 @@ describe "Polymorphic Associations" do
411
407
  end
412
408
  end
413
409
 
410
+ INTEGRATION_DB.create_table!(:posts) do
411
+ primary_key :id
412
+ end
414
413
  class ::Post < Sequel::Model
415
- set_schema do
416
- primary_key :id
417
- end
418
- create_table!
419
414
  one_to_many :assets, :key=>:attachable_id do |ds|
420
415
  ds.filter(:attachable_type=>'Post')
421
416
  end
@@ -438,11 +433,10 @@ describe "Polymorphic Associations" do
438
433
  end
439
434
  end
440
435
 
436
+ INTEGRATION_DB.create_table!(:notes) do
437
+ primary_key :id
438
+ end
441
439
  class ::Note < Sequel::Model
442
- set_schema do
443
- primary_key :id
444
- end
445
- create_table!
446
440
  one_to_many :assets, :key=>:attachable_id do |ds|
447
441
  ds.filter(:attachable_type=>'Note')
448
442
  end
@@ -550,12 +544,12 @@ end
550
544
 
551
545
  describe "many_to_one/one_to_many not referencing primary key" do
552
546
  before do
547
+ INTEGRATION_DB.instance_variable_set(:@schemas, nil)
548
+ INTEGRATION_DB.create_table!(:clients) do
549
+ primary_key :id
550
+ text :name
551
+ end
553
552
  class ::Client < Sequel::Model
554
- set_schema do
555
- primary_key :id
556
- text :name
557
- end
558
- create_table!
559
553
  one_to_many :invoices, :reciprocal=>:client, \
560
554
  :dataset=>proc{Invoice.filter(:client_name=>name)}, \
561
555
  :eager_loader=>(proc do |key_hash, clients, associations|
@@ -585,12 +579,11 @@ describe "many_to_one/one_to_many not referencing primary key" do
585
579
  end
586
580
  end
587
581
 
582
+ INTEGRATION_DB.create_table!(:invoices) do
583
+ primary_key :id
584
+ text :client_name
585
+ end
588
586
  class ::Invoice < Sequel::Model
589
- set_schema do
590
- primary_key :id
591
- text :client_name
592
- end
593
- create_table!
594
587
  many_to_one :client, :key=>:client_name, \
595
588
  :dataset=>proc{Client.filter(:name=>client_name)}, \
596
589
  :eager_loader=>(proc do |key_hash, invoices, associations|