sequel 3.21.0 → 3.31.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 +485 -3
- data/README.rdoc +20 -6
- data/Rakefile +37 -19
- data/bin/sequel +1 -5
- data/doc/association_basics.rdoc +249 -43
- data/doc/dataset_basics.rdoc +4 -4
- data/doc/dataset_filtering.rdoc +8 -0
- data/doc/mass_assignment.rdoc +54 -0
- data/doc/migration.rdoc +15 -538
- data/doc/model_hooks.rdoc +72 -27
- data/doc/opening_databases.rdoc +89 -50
- data/doc/prepared_statements.rdoc +40 -13
- data/doc/reflection.rdoc +8 -2
- data/doc/release_notes/3.22.0.txt +39 -0
- data/doc/release_notes/3.23.0.txt +172 -0
- data/doc/release_notes/3.24.0.txt +420 -0
- data/doc/release_notes/3.25.0.txt +88 -0
- data/doc/release_notes/3.26.0.txt +88 -0
- data/doc/release_notes/3.27.0.txt +82 -0
- data/doc/release_notes/3.28.0.txt +304 -0
- data/doc/release_notes/3.29.0.txt +459 -0
- data/doc/release_notes/3.30.0.txt +135 -0
- data/doc/release_notes/3.31.0.txt +146 -0
- data/doc/schema_modification.rdoc +547 -0
- data/doc/sharding.rdoc +7 -1
- data/doc/testing.rdoc +129 -0
- data/doc/transactions.rdoc +137 -0
- data/doc/virtual_rows.rdoc +2 -2
- data/lib/sequel/adapters/ado/mssql.rb +18 -6
- data/lib/sequel/adapters/ado.rb +40 -18
- data/lib/sequel/adapters/amalgalite.rb +17 -9
- data/lib/sequel/adapters/db2.rb +175 -86
- data/lib/sequel/adapters/dbi.rb +15 -15
- data/lib/sequel/adapters/do/mysql.rb +5 -7
- data/lib/sequel/adapters/do/postgres.rb +0 -5
- data/lib/sequel/adapters/do/sqlite.rb +0 -5
- data/lib/sequel/adapters/do.rb +17 -36
- data/lib/sequel/adapters/firebird.rb +27 -208
- data/lib/sequel/adapters/ibmdb.rb +448 -0
- data/lib/sequel/adapters/informix.rb +6 -23
- data/lib/sequel/adapters/jdbc/as400.rb +16 -34
- data/lib/sequel/adapters/jdbc/db2.rb +45 -0
- data/lib/sequel/adapters/jdbc/derby.rb +247 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
- data/lib/sequel/adapters/jdbc/h2.rb +56 -29
- data/lib/sequel/adapters/jdbc/hsqldb.rb +170 -0
- data/lib/sequel/adapters/jdbc/informix.rb +26 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
- data/lib/sequel/adapters/jdbc/mysql.rb +11 -11
- data/lib/sequel/adapters/jdbc/oracle.rb +81 -25
- data/lib/sequel/adapters/jdbc/postgresql.rb +9 -13
- data/lib/sequel/adapters/jdbc/sqlite.rb +2 -6
- data/lib/sequel/adapters/jdbc/sqlserver.rb +71 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
- data/lib/sequel/adapters/jdbc.rb +231 -78
- data/lib/sequel/adapters/mock.rb +335 -0
- data/lib/sequel/adapters/mysql.rb +82 -184
- data/lib/sequel/adapters/mysql2.rb +26 -25
- data/lib/sequel/adapters/odbc/db2.rb +17 -0
- data/lib/sequel/adapters/odbc/mssql.rb +1 -6
- data/lib/sequel/adapters/odbc.rb +28 -9
- data/lib/sequel/adapters/openbase.rb +3 -5
- data/lib/sequel/adapters/oracle.rb +351 -53
- data/lib/sequel/adapters/postgres.rb +194 -33
- data/lib/sequel/adapters/shared/access.rb +32 -9
- data/lib/sequel/adapters/shared/db2.rb +321 -0
- data/lib/sequel/adapters/shared/firebird.rb +221 -0
- data/lib/sequel/adapters/shared/informix.rb +53 -0
- data/lib/sequel/adapters/shared/mssql.rb +231 -106
- data/lib/sequel/adapters/shared/mysql.rb +181 -63
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
- data/lib/sequel/adapters/shared/oracle.rb +231 -41
- data/lib/sequel/adapters/shared/postgres.rb +214 -82
- data/lib/sequel/adapters/shared/progress.rb +4 -7
- data/lib/sequel/adapters/shared/sqlite.rb +201 -57
- data/lib/sequel/adapters/sqlite.rb +29 -21
- data/lib/sequel/adapters/swift/mysql.rb +12 -7
- data/lib/sequel/adapters/swift/postgres.rb +0 -5
- data/lib/sequel/adapters/swift/sqlite.rb +8 -6
- data/lib/sequel/adapters/swift.rb +5 -5
- data/lib/sequel/adapters/tinytds.rb +160 -19
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
- data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
- data/lib/sequel/ast_transformer.rb +190 -0
- data/lib/sequel/connection_pool/threaded.rb +3 -2
- data/lib/sequel/connection_pool.rb +1 -1
- data/lib/sequel/core.rb +46 -5
- data/lib/sequel/database/connecting.rb +5 -6
- data/lib/sequel/database/dataset.rb +4 -4
- data/lib/sequel/database/dataset_defaults.rb +59 -1
- data/lib/sequel/database/logging.rb +1 -1
- data/lib/sequel/database/misc.rb +104 -19
- data/lib/sequel/database/query.rb +153 -58
- data/lib/sequel/database/schema_generator.rb +17 -7
- data/lib/sequel/database/schema_methods.rb +59 -29
- data/lib/sequel/dataset/actions.rb +231 -76
- data/lib/sequel/dataset/features.rb +91 -8
- data/lib/sequel/dataset/graph.rb +38 -11
- data/lib/sequel/dataset/misc.rb +50 -23
- data/lib/sequel/dataset/mutation.rb +5 -6
- data/lib/sequel/dataset/prepared_statements.rb +48 -27
- data/lib/sequel/dataset/query.rb +236 -43
- data/lib/sequel/dataset/sql.rb +665 -253
- data/lib/sequel/dataset.rb +8 -0
- data/lib/sequel/exceptions.rb +4 -0
- data/lib/sequel/extensions/columns_introspection.rb +61 -0
- data/lib/sequel/extensions/migration.rb +6 -4
- data/lib/sequel/extensions/named_timezones.rb +5 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
- data/lib/sequel/extensions/to_dot.rb +95 -83
- data/lib/sequel/model/associations.rb +1020 -337
- data/lib/sequel/model/base.rb +414 -132
- data/lib/sequel/model/errors.rb +1 -1
- data/lib/sequel/model/exceptions.rb +5 -1
- data/lib/sequel/model.rb +15 -8
- data/lib/sequel/plugins/association_pks.rb +22 -4
- data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
- data/lib/sequel/plugins/dataset_associations.rb +100 -0
- data/lib/sequel/plugins/defaults_setter.rb +58 -0
- data/lib/sequel/plugins/force_encoding.rb +6 -6
- data/lib/sequel/plugins/identity_map.rb +114 -7
- data/lib/sequel/plugins/many_through_many.rb +65 -7
- data/lib/sequel/plugins/prepared_statements.rb +151 -0
- data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
- data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
- data/lib/sequel/plugins/rcte_tree.rb +29 -15
- data/lib/sequel/plugins/serialization.rb +88 -44
- data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
- data/lib/sequel/plugins/sharding.rb +21 -54
- data/lib/sequel/plugins/single_table_inheritance.rb +3 -1
- data/lib/sequel/plugins/typecast_on_load.rb +9 -12
- data/lib/sequel/plugins/update_primary_key.rb +2 -2
- data/lib/sequel/plugins/xml_serializer.rb +3 -3
- data/lib/sequel/sql.rb +109 -52
- data/lib/sequel/timezones.rb +57 -38
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/db2_spec.rb +146 -0
- data/spec/adapters/mssql_spec.rb +111 -29
- data/spec/adapters/mysql_spec.rb +135 -123
- data/spec/adapters/oracle_spec.rb +49 -77
- data/spec/adapters/postgres_spec.rb +267 -78
- data/spec/adapters/spec_helper.rb +6 -5
- data/spec/adapters/sqlite_spec.rb +35 -21
- data/spec/core/connection_pool_spec.rb +71 -92
- data/spec/core/core_sql_spec.rb +21 -30
- data/spec/core/database_spec.rb +788 -512
- data/spec/core/dataset_spec.rb +1073 -1053
- data/spec/core/expression_filters_spec.rb +159 -42
- data/spec/core/mock_adapter_spec.rb +419 -0
- data/spec/core/object_graph_spec.rb +86 -114
- data/spec/core/schema_generator_spec.rb +3 -3
- data/spec/core/schema_spec.rb +298 -38
- data/spec/core/spec_helper.rb +6 -48
- data/spec/extensions/association_autoreloading_spec.rb +1 -10
- data/spec/extensions/association_dependencies_spec.rb +2 -12
- data/spec/extensions/association_pks_spec.rb +66 -32
- data/spec/extensions/caching_spec.rb +23 -50
- data/spec/extensions/class_table_inheritance_spec.rb +31 -83
- data/spec/extensions/columns_introspection_spec.rb +91 -0
- data/spec/extensions/composition_spec.rb +18 -13
- data/spec/extensions/dataset_associations_spec.rb +199 -0
- data/spec/extensions/defaults_setter_spec.rb +64 -0
- data/spec/extensions/hook_class_methods_spec.rb +65 -91
- data/spec/extensions/identity_map_spec.rb +114 -22
- data/spec/extensions/instance_filters_spec.rb +11 -21
- data/spec/extensions/instance_hooks_spec.rb +53 -0
- data/spec/extensions/json_serializer_spec.rb +4 -5
- data/spec/extensions/lazy_attributes_spec.rb +16 -20
- data/spec/extensions/list_spec.rb +17 -39
- data/spec/extensions/many_through_many_spec.rb +283 -250
- data/spec/extensions/migration_spec.rb +35 -32
- data/spec/extensions/named_timezones_spec.rb +23 -3
- data/spec/extensions/nested_attributes_spec.rb +97 -88
- data/spec/extensions/optimistic_locking_spec.rb +9 -20
- data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
- data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
- data/spec/extensions/prepared_statements_spec.rb +53 -0
- data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
- data/spec/extensions/pretty_table_spec.rb +1 -6
- data/spec/extensions/rcte_tree_spec.rb +41 -43
- data/spec/extensions/schema_dumper_spec.rb +5 -8
- data/spec/extensions/schema_spec.rb +13 -21
- data/spec/extensions/serialization_modification_detection_spec.rb +37 -0
- data/spec/extensions/serialization_spec.rb +72 -43
- data/spec/extensions/sharding_spec.rb +66 -140
- data/spec/extensions/single_table_inheritance_spec.rb +18 -29
- data/spec/extensions/spec_helper.rb +13 -61
- data/spec/extensions/sql_expr_spec.rb +20 -60
- data/spec/extensions/tactical_eager_loading_spec.rb +9 -19
- data/spec/extensions/thread_local_timezones_spec.rb +22 -2
- data/spec/extensions/timestamps_spec.rb +6 -6
- data/spec/extensions/to_dot_spec.rb +4 -7
- data/spec/extensions/touch_spec.rb +13 -14
- data/spec/extensions/tree_spec.rb +11 -26
- data/spec/extensions/typecast_on_load_spec.rb +1 -6
- data/spec/extensions/update_primary_key_spec.rb +30 -24
- data/spec/extensions/validation_class_methods_spec.rb +30 -51
- data/spec/extensions/validation_helpers_spec.rb +16 -35
- data/spec/extensions/xml_serializer_spec.rb +16 -4
- data/spec/integration/associations_test.rb +537 -21
- data/spec/integration/database_test.rb +4 -2
- data/spec/integration/dataset_test.rb +461 -65
- data/spec/integration/eager_loader_test.rb +19 -21
- data/spec/integration/model_test.rb +80 -1
- data/spec/integration/plugin_test.rb +304 -116
- data/spec/integration/prepared_statement_test.rb +212 -122
- data/spec/integration/schema_test.rb +161 -30
- data/spec/integration/spec_helper.rb +39 -30
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +172 -5
- data/spec/integration/type_test.rb +17 -3
- data/spec/model/association_reflection_spec.rb +201 -7
- data/spec/model/associations_spec.rb +865 -662
- data/spec/model/base_spec.rb +275 -95
- data/spec/model/dataset_methods_spec.rb +7 -27
- data/spec/model/eager_loading_spec.rb +677 -676
- data/spec/model/hooks_spec.rb +309 -67
- data/spec/model/model_spec.rb +222 -168
- data/spec/model/plugins_spec.rb +24 -13
- data/spec/model/record_spec.rb +381 -218
- data/spec/model/spec_helper.rb +13 -71
- data/spec/model/validations_spec.rb +11 -0
- metadata +115 -73
data/lib/sequel/dataset.rb
CHANGED
|
@@ -26,6 +26,14 @@ module Sequel
|
|
|
26
26
|
extend Metaprogramming
|
|
27
27
|
include Metaprogramming
|
|
28
28
|
include Enumerable
|
|
29
|
+
include SQL::AliasMethods
|
|
30
|
+
include SQL::BooleanMethods
|
|
31
|
+
include SQL::CastMethods
|
|
32
|
+
include SQL::ComplexExpressionMethods
|
|
33
|
+
include SQL::InequalityMethods
|
|
34
|
+
include SQL::NumericMethods
|
|
35
|
+
include SQL::OrderMethods
|
|
36
|
+
include SQL::StringMethods
|
|
29
37
|
end
|
|
30
38
|
|
|
31
39
|
require(%w"query actions features graph prepared_statements misc mutation sql", 'dataset')
|
data/lib/sequel/exceptions.rb
CHANGED
|
@@ -44,6 +44,10 @@ module Sequel
|
|
|
44
44
|
# and won't reraise it.
|
|
45
45
|
class Rollback < Error ; end
|
|
46
46
|
|
|
47
|
+
# Exception that occurs when unbinding a dataset that has multiple different values
|
|
48
|
+
# for a given variable.
|
|
49
|
+
class UnbindDuplicate < Error; end
|
|
50
|
+
|
|
47
51
|
class Error
|
|
48
52
|
AdapterNotFound = Sequel::AdapterNotFound
|
|
49
53
|
InvalidOperation = Sequel::InvalidOperation
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# The columns_introspection extension attempts to introspect the
|
|
2
|
+
# selected columns for a dataset before issuing a query. If it
|
|
3
|
+
# thinks it can guess correctly at the columns the query will use,
|
|
4
|
+
# it will return the columns without issuing a database query.
|
|
5
|
+
# This method is not fool-proof, it's possible that some databases
|
|
6
|
+
# will use column names that Sequel does not expect.
|
|
7
|
+
#
|
|
8
|
+
# To enable this for a single dataset, extend the dataset with
|
|
9
|
+
# Sequel::ColumnIntrospection. To enable this for all datasets, run:
|
|
10
|
+
#
|
|
11
|
+
# Sequel::Dataset.introspect_all_columns
|
|
12
|
+
|
|
13
|
+
module Sequel
|
|
14
|
+
module ColumnsIntrospection
|
|
15
|
+
# Attempt to guess the columns that will be returned
|
|
16
|
+
# if there are columns selected, in order to skip a database
|
|
17
|
+
# query to retrieve the columns. This should work with
|
|
18
|
+
# Symbols, SQL::Identifiers, SQL::QualifiedIdentifiers, and
|
|
19
|
+
# SQL::AliasedExpressions.
|
|
20
|
+
def columns
|
|
21
|
+
return @columns if @columns
|
|
22
|
+
return columns_without_introspection unless cols = opts[:select] and !cols.empty?
|
|
23
|
+
probable_columns = cols.map{|c| probable_column_name(c)}
|
|
24
|
+
if probable_columns.all?
|
|
25
|
+
@columns = probable_columns
|
|
26
|
+
else
|
|
27
|
+
columns_without_introspection
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
# Return the probable name of the column, or nil if one
|
|
34
|
+
# cannot be determined.
|
|
35
|
+
def probable_column_name(c)
|
|
36
|
+
case c
|
|
37
|
+
when Symbol
|
|
38
|
+
_, c, a = split_symbol(c)
|
|
39
|
+
(a || c).to_sym
|
|
40
|
+
when SQL::Identifier
|
|
41
|
+
c.value.to_sym
|
|
42
|
+
when SQL::QualifiedIdentifier
|
|
43
|
+
col = c.column
|
|
44
|
+
col.is_a?(SQL::Identifier) ? col.value.to_sym : col.to_sym
|
|
45
|
+
when SQL::AliasedExpression
|
|
46
|
+
a = c.aliaz
|
|
47
|
+
a.is_a?(SQL::Identifier) ? a.value.to_sym : a.to_sym
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
class Dataset
|
|
53
|
+
alias columns_without_introspection columns
|
|
54
|
+
|
|
55
|
+
# Enable column introspection for every dataset.
|
|
56
|
+
def self.introspect_all_columns
|
|
57
|
+
include ColumnsIntrospection
|
|
58
|
+
remove_method(:columns) if instance_methods(false).map{|x| x.to_s}.include?('columns')
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
# the user to easily group schema changes and migrate the database
|
|
3
3
|
# to a newer version or revert to a previous version.
|
|
4
4
|
|
|
5
|
+
#
|
|
5
6
|
module Sequel
|
|
6
7
|
# Sequel's older migration class, available for backward compatibility.
|
|
7
8
|
# Uses subclasses with up and down instance methods for each migration:
|
|
@@ -302,7 +303,7 @@ module Sequel
|
|
|
302
303
|
#
|
|
303
304
|
# Part of the +migration+ extension.
|
|
304
305
|
class Migrator
|
|
305
|
-
MIGRATION_FILE_PATTERN = /\A\d+_.+\.rb\z/i.freeze
|
|
306
|
+
MIGRATION_FILE_PATTERN = /\A(\d+)_.+\.rb\z/i.freeze
|
|
306
307
|
MIGRATION_SPLITTER = '_'.freeze
|
|
307
308
|
MINIMUM_TIMESTAMP = 20000101
|
|
308
309
|
|
|
@@ -375,7 +376,8 @@ module Sequel
|
|
|
375
376
|
@db = db
|
|
376
377
|
@directory = directory
|
|
377
378
|
@files = get_migration_files
|
|
378
|
-
|
|
379
|
+
schema, table = @db.send(:schema_and_table, opts[:table] || self.class.const_get(:DEFAULT_SCHEMA_TABLE))
|
|
380
|
+
@table = schema ? Sequel::SQL::QualifiedIdentifier.new(schema, table) : table
|
|
379
381
|
@column = opts[:column] || self.class.const_get(:DEFAULT_SCHEMA_COLUMN)
|
|
380
382
|
@ds = schema_dataset
|
|
381
383
|
end
|
|
@@ -423,7 +425,7 @@ module Sequel
|
|
|
423
425
|
@current = opts[:current] || current_migration_version
|
|
424
426
|
|
|
425
427
|
raise(Error, "No current version available") unless current
|
|
426
|
-
raise(Error, "No target version available") unless target
|
|
428
|
+
raise(Error, "No target version available, probably because no migration files found or filenames don't follow the migration filename convention") unless target
|
|
427
429
|
|
|
428
430
|
@direction = current < target ? :up : :down
|
|
429
431
|
@migrations = get_migrations
|
|
@@ -588,7 +590,7 @@ module Sequel
|
|
|
588
590
|
next unless MIGRATION_FILE_PATTERN.match(file)
|
|
589
591
|
files << File.join(directory, file)
|
|
590
592
|
end
|
|
591
|
-
files.
|
|
593
|
+
files.sort_by{|f| MIGRATION_FILE_PATTERN.match(File.basename(f))[1].to_i}
|
|
592
594
|
end
|
|
593
595
|
|
|
594
596
|
# Returns tuples of migration, filename, and direction
|
|
@@ -21,6 +21,11 @@
|
|
|
21
21
|
# Then, before data is stored in the database, it is converted to New
|
|
22
22
|
# York time. When data is retrieved from the database, it is
|
|
23
23
|
# converted to Los Angeles time.
|
|
24
|
+
#
|
|
25
|
+
# Note that typecasting from the database timezone to the application
|
|
26
|
+
# timezone when fetching rows is dependent on the database adapter,
|
|
27
|
+
# and only works on adapters where Sequel itself does the conversion.
|
|
28
|
+
# It should work on mysql, postgres, sqlite, ibmdb, and jdbc.
|
|
24
29
|
|
|
25
30
|
require 'tzinfo'
|
|
26
31
|
|
|
@@ -32,7 +32,7 @@ module Sequel
|
|
|
32
32
|
module ThreadLocalTimezones
|
|
33
33
|
%w'application database typecast'.each do |t|
|
|
34
34
|
class_eval("def thread_#{t}_timezone=(tz); Thread.current[:#{t}_timezone] = convert_timezone_setter_arg(tz); end", __FILE__, __LINE__)
|
|
35
|
-
class_eval(<<END, __FILE__, __LINE__)
|
|
35
|
+
class_eval(<<END, __FILE__, __LINE__ + 1)
|
|
36
36
|
def #{t}_timezone
|
|
37
37
|
if tz = Thread.current[:#{t}_timezone]
|
|
38
38
|
tz unless tz == :nil
|
|
@@ -4,109 +4,111 @@
|
|
|
4
4
|
# of the dataset's abstract syntax tree.
|
|
5
5
|
|
|
6
6
|
module Sequel
|
|
7
|
-
class
|
|
7
|
+
class ToDot
|
|
8
8
|
# The option keys that should be included in the dot output.
|
|
9
9
|
TO_DOT_OPTIONS = [:with, :distinct, :select, :from, :join, :where, :group, :having, :compounds, :order, :limit, :offset, :lock].freeze
|
|
10
10
|
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
11
|
+
# Given a +Dataset+, return a string in +dot+ format that will
|
|
12
|
+
# generate a visualization of the dataset.
|
|
13
|
+
def self.output(ds)
|
|
14
|
+
new(ds).output
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Given a +Dataset+, parse the internal structure to generate
|
|
18
|
+
# a dataset visualization.
|
|
19
|
+
def initialize(ds)
|
|
20
|
+
@i = 0
|
|
21
|
+
@stack = [@i]
|
|
22
|
+
@dot = ["digraph G {", "0 [label=\"self\"];"]
|
|
23
|
+
v(ds, "")
|
|
24
|
+
@dot << "}"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Output the dataset visualization as a string in +dot+ format.
|
|
28
|
+
def output
|
|
29
|
+
@dot.join("\n")
|
|
20
30
|
end
|
|
21
31
|
|
|
22
32
|
private
|
|
23
33
|
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
# its children are then added by recursively calling this method. The
|
|
39
|
-
# return value is the integer representing the last created node.
|
|
40
|
-
def _to_dot(dot, l, c, e, i)
|
|
41
|
-
i += 1
|
|
42
|
-
dot << "#{c} -> #{i} [label=\"#{l}\"];" if l
|
|
43
|
-
c = i
|
|
34
|
+
# Add an entry to the +dot+ output with the given label. If +j+
|
|
35
|
+
# is given, it is used directly as the node or transition. Otherwise
|
|
36
|
+
# a node is created for the current object.
|
|
37
|
+
def dot(label, j=nil)
|
|
38
|
+
@dot << "#{j||@i} [label=#{label.to_s.inspect}];"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Recursive method that parses all of Sequel's internal datastructures,
|
|
42
|
+
# adding the appropriate nodes and transitions to the internal +dot+
|
|
43
|
+
# structure.
|
|
44
|
+
def v(e, l)
|
|
45
|
+
@i += 1
|
|
46
|
+
dot(l, "#{@stack.last} -> #{@i}") if l
|
|
47
|
+
@stack.push(@i)
|
|
44
48
|
case e
|
|
45
49
|
when LiteralString
|
|
46
|
-
dot
|
|
47
|
-
i
|
|
50
|
+
dot "#{e.inspect}.lit"
|
|
48
51
|
when Symbol, Numeric, String, Class, TrueClass, FalseClass, NilClass
|
|
49
|
-
dot
|
|
50
|
-
i
|
|
52
|
+
dot e.inspect
|
|
51
53
|
when Array
|
|
52
|
-
dot
|
|
53
|
-
e.each_with_index do |
|
|
54
|
-
|
|
54
|
+
dot "Array"
|
|
55
|
+
e.each_with_index do |val, j|
|
|
56
|
+
v(val, j)
|
|
55
57
|
end
|
|
56
58
|
when Hash
|
|
57
|
-
dot
|
|
58
|
-
e.each do |k,
|
|
59
|
-
|
|
59
|
+
dot "Hash"
|
|
60
|
+
e.each do |k, val|
|
|
61
|
+
v(val, k)
|
|
60
62
|
end
|
|
61
63
|
when SQL::ComplexExpression
|
|
62
|
-
dot
|
|
63
|
-
e.args.each_with_index do |
|
|
64
|
-
|
|
64
|
+
dot "ComplexExpression: #{e.op}"
|
|
65
|
+
e.args.each_with_index do |val, j|
|
|
66
|
+
v(val, j)
|
|
65
67
|
end
|
|
66
68
|
when SQL::Identifier
|
|
67
|
-
dot
|
|
68
|
-
|
|
69
|
+
dot "Identifier"
|
|
70
|
+
v(e.value, :value)
|
|
69
71
|
when SQL::QualifiedIdentifier
|
|
70
|
-
dot
|
|
71
|
-
|
|
72
|
-
|
|
72
|
+
dot "QualifiedIdentifier"
|
|
73
|
+
v(e.table, :table)
|
|
74
|
+
v(e.column, :column)
|
|
73
75
|
when SQL::OrderedExpression
|
|
74
|
-
dot
|
|
75
|
-
|
|
76
|
+
dot "OrderedExpression: #{e.descending ? :DESC : :ASC}#{" NULLS #{e.nulls.to_s.upcase}" if e.nulls}"
|
|
77
|
+
v(e.expression, :expression)
|
|
76
78
|
when SQL::AliasedExpression
|
|
77
|
-
dot
|
|
78
|
-
|
|
79
|
-
|
|
79
|
+
dot "AliasedExpression"
|
|
80
|
+
v(e.expression, :expression)
|
|
81
|
+
v(e.aliaz, :alias)
|
|
80
82
|
when SQL::CaseExpression
|
|
81
|
-
dot
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
dot "CaseExpression"
|
|
84
|
+
v(e.expression, :expression) if e.expression
|
|
85
|
+
v(e.conditions, :conditions)
|
|
86
|
+
v(e.default, :default)
|
|
85
87
|
when SQL::Cast
|
|
86
|
-
dot
|
|
87
|
-
|
|
88
|
-
|
|
88
|
+
dot "Cast"
|
|
89
|
+
v(e.expr, :expr)
|
|
90
|
+
v(e.type, :type)
|
|
89
91
|
when SQL::Function
|
|
90
|
-
dot
|
|
91
|
-
e.args.each_with_index do |
|
|
92
|
-
|
|
92
|
+
dot "Function: #{e.f}"
|
|
93
|
+
e.args.each_with_index do |val, j|
|
|
94
|
+
v(val, j)
|
|
93
95
|
end
|
|
94
96
|
when SQL::Subscript
|
|
95
|
-
dot
|
|
96
|
-
|
|
97
|
-
|
|
97
|
+
dot "Subscript"
|
|
98
|
+
v(e.f, :f)
|
|
99
|
+
v(e.sub, :sub)
|
|
98
100
|
when SQL::WindowFunction
|
|
99
|
-
dot
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
dot "WindowFunction"
|
|
102
|
+
v(e.function, :function)
|
|
103
|
+
v(e.window, :window)
|
|
102
104
|
when SQL::Window
|
|
103
|
-
dot
|
|
104
|
-
|
|
105
|
+
dot "Window"
|
|
106
|
+
v(e.opts, :opts)
|
|
105
107
|
when SQL::PlaceholderLiteralString
|
|
106
108
|
str = e.str
|
|
107
109
|
str = "(#{str})" if e.parens
|
|
108
|
-
dot
|
|
109
|
-
|
|
110
|
+
dot "PlaceholderLiteralString: #{str.inspect}"
|
|
111
|
+
v(e.args, :args)
|
|
110
112
|
when SQL::JoinClause
|
|
111
113
|
str = "#{e.join_type.to_s.upcase} JOIN"
|
|
112
114
|
if e.is_a?(SQL::JoinOnClause)
|
|
@@ -114,24 +116,34 @@ module Sequel
|
|
|
114
116
|
elsif e.is_a?(SQL::JoinUsingClause)
|
|
115
117
|
str << " USING"
|
|
116
118
|
end
|
|
117
|
-
dot
|
|
118
|
-
|
|
119
|
-
|
|
119
|
+
dot str
|
|
120
|
+
v(e.table, :table)
|
|
121
|
+
v(e.table_alias, :alias) if e.table_alias
|
|
120
122
|
if e.is_a?(SQL::JoinOnClause)
|
|
121
|
-
|
|
123
|
+
v(e.on, :on)
|
|
122
124
|
elsif e.is_a?(SQL::JoinUsingClause)
|
|
123
|
-
|
|
125
|
+
v(e.using, :using)
|
|
124
126
|
end
|
|
125
127
|
when Dataset
|
|
126
|
-
dot
|
|
128
|
+
dot "Dataset"
|
|
127
129
|
TO_DOT_OPTIONS.each do |k|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
+
if val = e.opts[k]
|
|
131
|
+
v(val, k.to_s)
|
|
132
|
+
end
|
|
130
133
|
end
|
|
131
134
|
else
|
|
132
|
-
dot
|
|
135
|
+
dot "Unhandled: #{e.inspect}"
|
|
133
136
|
end
|
|
134
|
-
|
|
137
|
+
@stack.pop
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
class Dataset
|
|
142
|
+
# Return a string that can be processed by the +dot+ program (included
|
|
143
|
+
# with graphviz) in order to see a visualization of the dataset's
|
|
144
|
+
# abstract syntax tree.
|
|
145
|
+
def to_dot
|
|
146
|
+
ToDot.output(self)
|
|
135
147
|
end
|
|
136
148
|
end
|
|
137
149
|
end
|