sequel 3.1.0 → 3.2.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 (65) hide show
  1. data/CHANGELOG +76 -0
  2. data/Rakefile +2 -2
  3. data/bin/sequel +9 -4
  4. data/doc/opening_databases.rdoc +279 -0
  5. data/doc/release_notes/3.2.0.txt +268 -0
  6. data/doc/virtual_rows.rdoc +42 -51
  7. data/lib/sequel/adapters/ado.rb +2 -5
  8. data/lib/sequel/adapters/db2.rb +5 -0
  9. data/lib/sequel/adapters/do.rb +3 -0
  10. data/lib/sequel/adapters/firebird.rb +6 -4
  11. data/lib/sequel/adapters/informix.rb +5 -3
  12. data/lib/sequel/adapters/jdbc.rb +10 -8
  13. data/lib/sequel/adapters/jdbc/h2.rb +17 -4
  14. data/lib/sequel/adapters/mysql.rb +6 -19
  15. data/lib/sequel/adapters/odbc.rb +14 -18
  16. data/lib/sequel/adapters/openbase.rb +8 -0
  17. data/lib/sequel/adapters/shared/mssql.rb +14 -8
  18. data/lib/sequel/adapters/shared/mysql.rb +53 -28
  19. data/lib/sequel/adapters/shared/oracle.rb +21 -12
  20. data/lib/sequel/adapters/shared/postgres.rb +46 -26
  21. data/lib/sequel/adapters/shared/progress.rb +10 -5
  22. data/lib/sequel/adapters/shared/sqlite.rb +28 -12
  23. data/lib/sequel/adapters/sqlite.rb +4 -3
  24. data/lib/sequel/adapters/utils/stored_procedures.rb +18 -9
  25. data/lib/sequel/connection_pool.rb +4 -3
  26. data/lib/sequel/database.rb +110 -10
  27. data/lib/sequel/database/schema_sql.rb +12 -3
  28. data/lib/sequel/dataset.rb +40 -3
  29. data/lib/sequel/dataset/convenience.rb +0 -11
  30. data/lib/sequel/dataset/graph.rb +25 -11
  31. data/lib/sequel/dataset/sql.rb +176 -68
  32. data/lib/sequel/extensions/migration.rb +37 -21
  33. data/lib/sequel/extensions/schema_dumper.rb +8 -61
  34. data/lib/sequel/model.rb +3 -3
  35. data/lib/sequel/model/associations.rb +9 -1
  36. data/lib/sequel/model/base.rb +8 -1
  37. data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
  38. data/lib/sequel/sql.rb +125 -18
  39. data/lib/sequel/version.rb +1 -1
  40. data/spec/adapters/ado_spec.rb +1 -0
  41. data/spec/adapters/firebird_spec.rb +1 -0
  42. data/spec/adapters/informix_spec.rb +1 -0
  43. data/spec/adapters/mysql_spec.rb +23 -8
  44. data/spec/adapters/oracle_spec.rb +1 -0
  45. data/spec/adapters/postgres_spec.rb +52 -4
  46. data/spec/adapters/spec_helper.rb +2 -2
  47. data/spec/adapters/sqlite_spec.rb +2 -1
  48. data/spec/core/connection_pool_spec.rb +16 -0
  49. data/spec/core/database_spec.rb +174 -0
  50. data/spec/core/dataset_spec.rb +121 -26
  51. data/spec/core/expression_filters_spec.rb +156 -0
  52. data/spec/core/object_graph_spec.rb +20 -1
  53. data/spec/core/schema_spec.rb +5 -5
  54. data/spec/extensions/migration_spec.rb +140 -74
  55. data/spec/extensions/schema_dumper_spec.rb +3 -69
  56. data/spec/extensions/single_table_inheritance_spec.rb +6 -0
  57. data/spec/integration/dataset_test.rb +84 -2
  58. data/spec/integration/schema_test.rb +24 -5
  59. data/spec/integration/spec_helper.rb +8 -6
  60. data/spec/model/eager_loading_spec.rb +9 -0
  61. data/spec/model/record_spec.rb +35 -8
  62. metadata +8 -7
  63. data/lib/sequel/adapters/utils/date_format.rb +0 -21
  64. data/lib/sequel/adapters/utils/savepoint_transactions.rb +0 -80
  65. data/lib/sequel/adapters/utils/unsupported.rb +0 -50
@@ -189,11 +189,12 @@ module Sequel
189
189
  # Yield a hash for each row in the dataset.
190
190
  def fetch_rows(sql)
191
191
  execute(sql) do |result|
192
- @columns = result.columns.map{|c| output_identifier(c)}
193
- column_count = @columns.size
192
+ i = -1
193
+ cols = result.columns.map{|c| [output_identifier(c), i+=1]}
194
+ @columns = cols.map{|c| c.first}
194
195
  result.each do |values|
195
196
  row = {}
196
- column_count.times {|i| row[@columns[i]] = values[i]}
197
+ cols.each{|n,i| row[n] = values[i]}
197
198
  yield row
198
199
  end
199
200
  end
@@ -7,9 +7,24 @@ module Sequel
7
7
  # The name of the stored procedure to call
8
8
  attr_accessor :sproc_name
9
9
 
10
- # Call the prepared statement
10
+ # The name of the stored procedure to call
11
+ attr_writer :sproc_args
12
+
13
+ # Call the stored procedure with the given args
11
14
  def call(*args, &block)
12
- @sproc_args = args
15
+ sp = clone
16
+ sp.sproc_args = args
17
+ sp.run(&block)
18
+ end
19
+
20
+ # Programmer friendly string showing this is a stored procedure,
21
+ # showing the name of the procedure.
22
+ def inspect
23
+ "<#{self.class.name}/StoredProcedure name=#{@sproc_name}>"
24
+ end
25
+
26
+ # Run the stored procedure with the current args on the database
27
+ def run(&block)
13
28
  case @sproc_type
14
29
  when :select, :all
15
30
  all(&block)
@@ -24,13 +39,7 @@ module Sequel
24
39
  end
25
40
  end
26
41
 
27
- # Programmer friendly string showing this is a stored procedure,
28
- # showing the name of the procedure.
29
- def inspect
30
- "<#{self.class.name}/StoredProcedure name=#{@sproc_name}>"
31
- end
32
-
33
- # Set the type of the sproc and override the corresponding _sql
42
+ # Set the type of the stored procedure and override the corresponding _sql
34
43
  # method to return the empty string (since the result will be
35
44
  # ignored anyway).
36
45
  def sproc_type=(type)
@@ -41,7 +41,8 @@ class Sequel::ConnectionPool
41
41
  # present, will use a single :default server. The server name symbol will
42
42
  # be passed to the connection_proc.
43
43
  def initialize(opts = {}, &block)
44
- @max_size = opts[:max_connections] || 4
44
+ @max_size = Integer(opts[:max_connections] || 4)
45
+ raise(Sequel::Error, ':max_connections must be positive') if @max_size < 1
45
46
  @mutex = Mutex.new
46
47
  @connection_proc = block
47
48
  @disconnection_proc = opts[:disconnection_proc]
@@ -53,8 +54,8 @@ class Sequel::ConnectionPool
53
54
  @available_connections[s] = []
54
55
  @allocated[s] = {}
55
56
  end
56
- @timeout = opts[:pool_timeout] || 5
57
- @sleep_time = opts[:pool_sleep_time] || 0.001
57
+ @timeout = Integer(opts[:pool_timeout] || 5)
58
+ @sleep_time = Float(opts[:pool_sleep_time] || 0.001)
58
59
  @convert_exceptions = opts.include?(:pool_convert_exceptions) ? opts[:pool_convert_exceptions] : true
59
60
  end
60
61
 
@@ -21,11 +21,18 @@ module Sequel
21
21
 
22
22
  SQL_BEGIN = 'BEGIN'.freeze
23
23
  SQL_COMMIT = 'COMMIT'.freeze
24
+ SQL_RELEASE_SAVEPOINT = 'RELEASE SAVEPOINT autopoint_%d'.freeze
24
25
  SQL_ROLLBACK = 'ROLLBACK'.freeze
26
+ SQL_ROLLBACK_TO_SAVEPOINT = 'ROLLBACK TO SAVEPOINT autopoint_%d'.freeze
27
+ SQL_SAVEPOINT = 'SAVEPOINT autopoint_%d'.freeze
25
28
 
26
29
  TRANSACTION_BEGIN = 'Transaction.begin'.freeze
27
30
  TRANSACTION_COMMIT = 'Transaction.commit'.freeze
28
31
  TRANSACTION_ROLLBACK = 'Transaction.rollback'.freeze
32
+
33
+ POSTGRES_DEFAULT_RE = /\A(?:B?('.*')::[^']+|\((-?\d+(?:\.\d+)?)\))\z/
34
+ MYSQL_TIMESTAMP_RE = /\ACURRENT_(?:DATE|TIMESTAMP)?\z/
35
+ STRING_DEFAULT_RE = /\A'(.*)'\z/
29
36
 
30
37
  # The identifier input method to use by default
31
38
  @@identifier_input_method = nil
@@ -73,7 +80,7 @@ module Sequel
73
80
  def initialize(opts = {}, &block)
74
81
  @opts ||= opts
75
82
 
76
- @single_threaded = opts.include?(:single_threaded) ? opts[:single_threaded] : @@single_threaded
83
+ @single_threaded = opts.include?(:single_threaded) ? typecast_value_boolean(opts[:single_threaded]) : @@single_threaded
77
84
  @schemas = {}
78
85
  @default_schema = opts.include?(:default_schema) ? opts[:default_schema] : default_schema_default
79
86
  @prepared_statements = {}
@@ -120,7 +127,8 @@ module Sequel
120
127
 
121
128
  # Connects to a database. See Sequel.connect.
122
129
  def self.connect(conn_string, opts = {}, &block)
123
- if conn_string.is_a?(String)
130
+ case conn_string
131
+ when String
124
132
  if match = /\A(jdbc|do):/o.match(conn_string)
125
133
  c = adapter_class(match[1].to_sym)
126
134
  opts = {:uri=>conn_string}.merge(opts)
@@ -133,9 +141,11 @@ module Sequel
133
141
  uri.query.split('&').collect{|s| s.split('=')}.each{|k,v| uri_options[k.to_sym] = v} unless uri.query.to_s.strip.empty?
134
142
  opts = c.send(:uri_to_options, uri).merge(uri_options).merge(opts)
135
143
  end
136
- else
144
+ when Hash
137
145
  opts = conn_string.merge(opts)
138
146
  c = adapter_class(opts[:adapter] || opts['adapter'])
147
+ else
148
+ raise Error, "Sequel::Database.connect takes either a Hash or a String, given: #{conn_string.inspect}"
139
149
  end
140
150
  # process opts a bit
141
151
  opts = opts.inject({}) do |m, kv| k, v = *kv
@@ -447,6 +457,7 @@ module Sequel
447
457
 
448
458
  cols = schema_parse_table(table_name, opts)
449
459
  raise(Error, 'schema parsing returned no columns, table probably doesn\'t exist') if cols.nil? || cols.empty?
460
+ cols.each{|_,c| c[:ruby_default] = column_schema_to_ruby_default(c[:default], c[:type])}
450
461
  @schemas[quoted_name] = cols
451
462
  end
452
463
 
@@ -460,7 +471,7 @@ module Sequel
460
471
  @pool.hold(server || :default, &block)
461
472
  end
462
473
 
463
- # Whether the database and adapter support savepoints
474
+ # Whether the database and adapter support savepoints, false by default
464
475
  def supports_savepoints?
465
476
  false
466
477
  end
@@ -571,17 +582,38 @@ module Sequel
571
582
 
572
583
  # Add the current thread to the list of active transactions
573
584
  def add_transaction
574
- @transactions << Thread.current
585
+ th = Thread.current
586
+ if supports_savepoints?
587
+ unless @transactions.include?(th)
588
+ th[:sequel_transaction_depth] = 0
589
+ @transactions << th
590
+ end
591
+ else
592
+ @transactions << th
593
+ end
575
594
  end
576
595
 
577
596
  # Whether the current thread/connection is already inside a transaction
578
597
  def already_in_transaction?(conn, opts)
579
- @transactions.include?(Thread.current)
598
+ @transactions.include?(Thread.current) && (!supports_savepoints? || !opts[:savepoint])
580
599
  end
581
600
 
601
+ # SQL to start a new savepoint
602
+ def begin_savepoint_sql(depth)
603
+ SQL_SAVEPOINT % depth
604
+ end
605
+
582
606
  # Start a new database transaction on the given connection.
583
607
  def begin_transaction(conn)
584
- log_connection_execute(conn, begin_transaction_sql)
608
+ if supports_savepoints?
609
+ th = Thread.current
610
+ depth = th[:sequel_transaction_depth]
611
+ conn = transaction_statement_object(conn) if respond_to?(:transaction_statement_object, true)
612
+ log_connection_execute(conn, depth > 0 ? begin_savepoint_sql(depth) : begin_transaction_sql)
613
+ th[:sequel_transaction_depth] += 1
614
+ else
615
+ log_connection_execute(conn, begin_transaction_sql)
616
+ end
585
617
  conn
586
618
  end
587
619
 
@@ -608,9 +640,66 @@ module Sequel
608
640
  end
609
641
  end
610
642
 
643
+ # Convert the given default, which should be a database specific string, into
644
+ # a ruby object.
645
+ def column_schema_to_ruby_default(default, type)
646
+ return if default.nil?
647
+ orig_default = default
648
+ if database_type == :postgres and m = POSTGRES_DEFAULT_RE.match(default)
649
+ default = m[1] || m[2]
650
+ end
651
+ if [:string, :blob, :date, :datetime, :time].include?(type)
652
+ if database_type == :mysql
653
+ return if [:date, :datetime, :time].include?(type) && MYSQL_TIMESTAMP_RE.match(default)
654
+ orig_default = default = "'#{default.gsub("'", "''").gsub('\\', '\\\\')}'"
655
+ end
656
+ return unless m = STRING_DEFAULT_RE.match(default)
657
+ default = m[1].gsub("''", "'")
658
+ end
659
+ res = begin
660
+ case type
661
+ when :boolean
662
+ case default
663
+ when /[f0]/i
664
+ false
665
+ when /[t1]/i
666
+ true
667
+ end
668
+ when :string
669
+ default
670
+ when :blob
671
+ Sequel::SQL::Blob.new(default)
672
+ when :integer
673
+ Integer(default)
674
+ when :float
675
+ Float(default)
676
+ when :date
677
+ Sequel.string_to_date(default)
678
+ when :datetime
679
+ DateTime.parse(default)
680
+ when :time
681
+ Sequel.string_to_time(default)
682
+ when :decimal
683
+ BigDecimal.new(default)
684
+ end
685
+ rescue
686
+ nil
687
+ end
688
+ end
689
+
690
+ # SQL to commit a savepoint
691
+ def commit_savepoint_sql(depth)
692
+ SQL_RELEASE_SAVEPOINT % depth
693
+ end
694
+
611
695
  # Commit the active transaction on the connection
612
696
  def commit_transaction(conn)
613
- log_connection_execute(conn, commit_transaction_sql)
697
+ if supports_savepoints?
698
+ depth = Thread.current[:sequel_transaction_depth]
699
+ log_connection_execute(conn, depth > 1 ? commit_savepoint_sql(depth-1) : commit_transaction_sql)
700
+ else
701
+ log_connection_execute(conn, commit_transaction_sql)
702
+ end
614
703
  end
615
704
 
616
705
  # SQL to COMMIT a transaction.
@@ -717,7 +806,8 @@ module Sequel
717
806
 
718
807
  # Remove the current thread from the list of active transactions
719
808
  def remove_transaction(conn)
720
- @transactions.delete(Thread.current)
809
+ th = Thread.current
810
+ @transactions.delete(th) if !supports_savepoints? || ((th[:sequel_transaction_depth] -= 1) <= 0)
721
811
  end
722
812
 
723
813
  # Remove the cached schema_utility_dataset, because the identifier
@@ -726,9 +816,19 @@ module Sequel
726
816
  @schema_utility_dataset = nil
727
817
  end
728
818
 
819
+ # SQL to rollback to a savepoint
820
+ def rollback_savepoint_sql(depth)
821
+ SQL_ROLLBACK_TO_SAVEPOINT % depth
822
+ end
823
+
729
824
  # Rollback the active transaction on the connection
730
825
  def rollback_transaction(conn)
731
- log_connection_execute(conn, rollback_transaction_sql)
826
+ if supports_savepoints?
827
+ depth = Thread.current[:sequel_transaction_depth]
828
+ log_connection_execute(conn, depth > 1 ? rollback_savepoint_sql(depth-1) : rollback_transaction_sql)
829
+ else
830
+ log_connection_execute(conn, rollback_transaction_sql)
831
+ end
732
832
  end
733
833
 
734
834
  # Split the schema information from the table
@@ -74,7 +74,7 @@ module Sequel
74
74
  sql << " DEFAULT #{literal(column[:default])}" if column.include?(:default)
75
75
  sql << PRIMARY_KEY if column[:primary_key]
76
76
  sql << " #{auto_increment_sql}" if column[:auto_increment]
77
- sql << column_references_sql(column) if column[:table]
77
+ sql << column_references_column_constraint_sql(column) if column[:table]
78
78
  sql
79
79
  end
80
80
 
@@ -84,6 +84,11 @@ module Sequel
84
84
  (generator.columns.map{|c| column_definition_sql(c)} + generator.constraints.map{|c| constraint_definition_sql(c)}).join(COMMA_SEPARATOR)
85
85
  end
86
86
 
87
+ # SQL DDL fragment for column foreign key references (column constraints)
88
+ def column_references_column_constraint_sql(column)
89
+ column_references_sql(column)
90
+ end
91
+
87
92
  # SQL DDL fragment for column foreign key references
88
93
  def column_references_sql(column)
89
94
  sql = " REFERENCES #{quote_schema_table(column[:table])}"
@@ -93,6 +98,11 @@ module Sequel
93
98
  sql
94
99
  end
95
100
 
101
+ # SQL DDL fragment for table foreign key references (table constraints)
102
+ def column_references_table_constraint_sql(constraint)
103
+ "FOREIGN KEY #{literal(constraint[:columns])}#{column_references_sql(constraint)}"
104
+ end
105
+
96
106
  # SQL DDL fragment specifying a constraint on a table.
97
107
  def constraint_definition_sql(constraint)
98
108
  sql = constraint[:name] ? "CONSTRAINT #{quote_identifier(constraint[:name])} " : ""
@@ -103,8 +113,7 @@ module Sequel
103
113
  when :primary_key
104
114
  sql << "PRIMARY KEY #{literal(constraint[:columns])}"
105
115
  when :foreign_key
106
- sql << "FOREIGN KEY #{literal(constraint[:columns])}"
107
- sql << column_references_sql(constraint)
116
+ sql << column_references_table_constraint_sql(constraint)
108
117
  when :unique
109
118
  sql << "UNIQUE #{literal(constraint[:columns])}"
110
119
  else
@@ -45,12 +45,13 @@ module Sequel
45
45
  MUTATION_METHODS = %w'add_graph_aliases and distinct exclude exists
46
46
  filter from from_self full_outer_join graph
47
47
  group group_and_count group_by having inner_join intersect invert join
48
- left_outer_join limit naked or order order_by order_more paginate query reject
48
+ left_outer_join limit naked or order order_by order_more paginate qualify query reject
49
49
  reverse reverse_order right_outer_join select select_all select_more
50
50
  set_defaults set_graph_aliases set_overrides sort sort_by
51
- unfiltered union unordered where with_sql'.collect{|x| x.to_sym}
51
+ unfiltered ungraphed union unordered where with with_sql'.collect{|x| x.to_sym}
52
52
 
53
53
  NOTIMPL_MSG = "This method must be overridden in Sequel adapters".freeze
54
+ WITH_SUPPORTED='with'.freeze
54
55
 
55
56
  # The database that corresponds to this dataset
56
57
  attr_accessor :db
@@ -218,6 +219,12 @@ module Sequel
218
219
  @quote_identifiers
219
220
  end
220
221
 
222
+ # Whether the dataset requires SQL standard datetimes (false by default,
223
+ # as most allow strings with ISO 8601 format.
224
+ def requires_sql_standard_datetimes?
225
+ false
226
+ end
227
+
221
228
  # Set the server for this dataset to use. Used to pick a specific database
222
229
  # shard to run a query against, or to override the default (which is SELECT uses
223
230
  # :read_only database and all other queries use the :default database).
@@ -242,6 +249,36 @@ module Sequel
242
249
  def set_overrides(hash)
243
250
  clone(:overrides=>hash.merge(@opts[:overrides]||{}))
244
251
  end
252
+
253
+ # Whether the dataset supports common table expressions (the WITH clause).
254
+ def supports_cte?
255
+ select_clause_order.include?(WITH_SUPPORTED)
256
+ end
257
+
258
+ # Whether the dataset supports the DISTINCT ON clause, true by default.
259
+ def supports_distinct_on?
260
+ true
261
+ end
262
+
263
+ # Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
264
+ def supports_intersect_except?
265
+ true
266
+ end
267
+
268
+ # Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default.
269
+ def supports_intersect_except_all?
270
+ true
271
+ end
272
+
273
+ # Whether the dataset supports the IS TRUE syntax.
274
+ def supports_is_true?
275
+ true
276
+ end
277
+
278
+ # Whether the dataset supports window functions.
279
+ def supports_window_functions?
280
+ false
281
+ end
245
282
 
246
283
  # Updates values for the dataset. The returned value is generally the
247
284
  # number of rows updated, but that is adapter dependent.
@@ -262,7 +299,7 @@ module Sequel
262
299
  # Whether this dataset is a simple SELECT * FROM table.
263
300
  def simple_select_all?
264
301
  o = @opts.reject{|k,v| v.nil?}
265
- o.length == 1 && o[:from] && o[:from].length == 1
302
+ o.length == 1 && (f = o[:from]) && f.length == 1 && f.first.is_a?(Symbol)
266
303
  end
267
304
 
268
305
  private
@@ -210,17 +210,6 @@ module Sequel
210
210
  get{|o| o.sum(column)}
211
211
  end
212
212
 
213
- # Returns true if the table exists. Will raise an error
214
- # if the dataset has fixed SQL or selects from another dataset
215
- # or more than one table.
216
- def table_exists?
217
- raise(Sequel::Error, "this dataset has fixed SQL") if @opts[:sql]
218
- raise(Sequel::Error, "this dataset selects from multiple sources") if @opts[:from].size != 1
219
- t = @opts[:from].first
220
- raise(Sequel::Error, "this dataset selects from a sub query") if t.is_a?(Dataset)
221
- @db.table_exists?(t)
222
- end
223
-
224
213
  # Returns a string in CSV format containing the dataset records. By
225
214
  # default the CSV representation includes the column titles in the
226
215
  # first line. You can turn that off by passing false as the
@@ -1,5 +1,14 @@
1
1
  module Sequel
2
2
  class Dataset
3
+ # Adds the given graph aliases to the list of graph aliases to use,
4
+ # unlike #set_graph_aliases, which replaces the list. See
5
+ # #set_graph_aliases.
6
+ def add_graph_aliases(graph_aliases)
7
+ ds = select_more(*graph_alias_columns(graph_aliases))
8
+ ds.opts[:graph_aliases] = (ds.opts[:graph_aliases] || ds.opts[:graph][:column_aliases] || {}).merge(graph_aliases)
9
+ ds
10
+ end
11
+
3
12
  # Allows you to join multiple datasets/tables and have the result set
4
13
  # split into component tables.
5
14
  #
@@ -48,12 +57,20 @@ module Sequel
48
57
  # Allow the use of a model, dataset, or symbol as the first argument
49
58
  # Find the table name/dataset based on the argument
50
59
  dataset = dataset.dataset if dataset.respond_to?(:dataset)
60
+ table_alias = options[:table_alias]
51
61
  case dataset
52
62
  when Symbol
53
63
  table = dataset
54
64
  dataset = @db[dataset]
65
+ table_alias ||= table
55
66
  when ::Sequel::Dataset
56
- table = dataset.first_source
67
+ if dataset.simple_select_all?
68
+ table = dataset.opts[:from].first
69
+ table_alias ||= table
70
+ else
71
+ table = dataset
72
+ table_alias ||= dataset_alias((@opts[:num_dataset_sources] || 0)+1)
73
+ end
57
74
  else
58
75
  raise Error, "The dataset argument should be a symbol, dataset, or model"
59
76
  end
@@ -65,11 +82,10 @@ module Sequel
65
82
  end
66
83
 
67
84
  # Only allow table aliases that haven't been used
68
- table_alias = options[:table_alias] || table
69
85
  raise_alias_error.call if @opts[:graph] && @opts[:graph][:table_aliases] && @opts[:graph][:table_aliases].include?(table_alias)
70
86
 
71
87
  # Join the table early in order to avoid cloning the dataset twice
72
- ds = join_table(options[:join_type] || :left_outer, dataset.simple_select_all? ? table : dataset, join_conditions, :table_alias=>table_alias, :implicit_qualifier=>options[:implicit_qualifier], &block)
88
+ ds = join_table(options[:join_type] || :left_outer, table, join_conditions, :table_alias=>table_alias, :implicit_qualifier=>options[:implicit_qualifier], &block)
73
89
  opts = ds.opts
74
90
 
75
91
  # Whether to include the table in the result set
@@ -79,7 +95,7 @@ module Sequel
79
95
 
80
96
  # Setup the initial graph data structure if it doesn't exist
81
97
  unless graph = opts[:graph]
82
- master = ds.first_source
98
+ master = ds.first_source_alias
83
99
  raise_alias_error.call if master == table_alias
84
100
  # Master hash storing all .graph related information
85
101
  graph = opts[:graph] = {}
@@ -162,13 +178,11 @@ module Sequel
162
178
  ds
163
179
  end
164
180
 
165
- # Adds the give graph aliases to the list of graph aliases to use,
166
- # unlike #set_graph_aliases, which replaces the list. See
167
- # #set_graph_aliases.
168
- def add_graph_aliases(graph_aliases)
169
- ds = select_more(*graph_alias_columns(graph_aliases))
170
- ds.opts[:graph_aliases] = (ds.opts[:graph_aliases] || ds.opts[:graph][:column_aliases] || {}).merge(graph_aliases)
171
- ds
181
+ # Remove the splitting of results into subhashes. Also removes
182
+ # metadata related to graphing, so you should not call graph
183
+ # any tables to this dataset after calling this method.
184
+ def ungraphed
185
+ clone(:graph=>nil)
172
186
  end
173
187
 
174
188
  private