sequel 2.5.0 → 2.6.0

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