sequel 3.1.0 → 3.2.0

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