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.
- data/CHANGELOG +76 -0
- data/Rakefile +2 -2
- data/bin/sequel +9 -4
- data/doc/opening_databases.rdoc +279 -0
- data/doc/release_notes/3.2.0.txt +268 -0
- data/doc/virtual_rows.rdoc +42 -51
- data/lib/sequel/adapters/ado.rb +2 -5
- data/lib/sequel/adapters/db2.rb +5 -0
- data/lib/sequel/adapters/do.rb +3 -0
- data/lib/sequel/adapters/firebird.rb +6 -4
- data/lib/sequel/adapters/informix.rb +5 -3
- data/lib/sequel/adapters/jdbc.rb +10 -8
- data/lib/sequel/adapters/jdbc/h2.rb +17 -4
- data/lib/sequel/adapters/mysql.rb +6 -19
- data/lib/sequel/adapters/odbc.rb +14 -18
- data/lib/sequel/adapters/openbase.rb +8 -0
- data/lib/sequel/adapters/shared/mssql.rb +14 -8
- data/lib/sequel/adapters/shared/mysql.rb +53 -28
- data/lib/sequel/adapters/shared/oracle.rb +21 -12
- data/lib/sequel/adapters/shared/postgres.rb +46 -26
- data/lib/sequel/adapters/shared/progress.rb +10 -5
- data/lib/sequel/adapters/shared/sqlite.rb +28 -12
- data/lib/sequel/adapters/sqlite.rb +4 -3
- data/lib/sequel/adapters/utils/stored_procedures.rb +18 -9
- data/lib/sequel/connection_pool.rb +4 -3
- data/lib/sequel/database.rb +110 -10
- data/lib/sequel/database/schema_sql.rb +12 -3
- data/lib/sequel/dataset.rb +40 -3
- data/lib/sequel/dataset/convenience.rb +0 -11
- data/lib/sequel/dataset/graph.rb +25 -11
- data/lib/sequel/dataset/sql.rb +176 -68
- data/lib/sequel/extensions/migration.rb +37 -21
- data/lib/sequel/extensions/schema_dumper.rb +8 -61
- data/lib/sequel/model.rb +3 -3
- data/lib/sequel/model/associations.rb +9 -1
- data/lib/sequel/model/base.rb +8 -1
- data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
- data/lib/sequel/sql.rb +125 -18
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/ado_spec.rb +1 -0
- data/spec/adapters/firebird_spec.rb +1 -0
- data/spec/adapters/informix_spec.rb +1 -0
- data/spec/adapters/mysql_spec.rb +23 -8
- data/spec/adapters/oracle_spec.rb +1 -0
- data/spec/adapters/postgres_spec.rb +52 -4
- data/spec/adapters/spec_helper.rb +2 -2
- data/spec/adapters/sqlite_spec.rb +2 -1
- data/spec/core/connection_pool_spec.rb +16 -0
- data/spec/core/database_spec.rb +174 -0
- data/spec/core/dataset_spec.rb +121 -26
- data/spec/core/expression_filters_spec.rb +156 -0
- data/spec/core/object_graph_spec.rb +20 -1
- data/spec/core/schema_spec.rb +5 -5
- data/spec/extensions/migration_spec.rb +140 -74
- data/spec/extensions/schema_dumper_spec.rb +3 -69
- data/spec/extensions/single_table_inheritance_spec.rb +6 -0
- data/spec/integration/dataset_test.rb +84 -2
- data/spec/integration/schema_test.rb +24 -5
- data/spec/integration/spec_helper.rb +8 -6
- data/spec/model/eager_loading_spec.rb +9 -0
- data/spec/model/record_spec.rb +35 -8
- metadata +8 -7
- data/lib/sequel/adapters/utils/date_format.rb +0 -21
- data/lib/sequel/adapters/utils/savepoint_transactions.rb +0 -80
- 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
|
-
|
193
|
-
|
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
|
-
|
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
|
-
#
|
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
|
-
|
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
|
-
#
|
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
|
|
data/lib/sequel/database.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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 <<
|
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 <<
|
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
|
data/lib/sequel/dataset.rb
CHANGED
@@ -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] &&
|
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
|
data/lib/sequel/dataset/graph.rb
CHANGED
@@ -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
|
-
|
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,
|
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.
|
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
|
-
#
|
166
|
-
#
|
167
|
-
#
|
168
|
-
def
|
169
|
-
|
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
|