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