viking-sequel 3.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. data/CHANGELOG +3134 -0
  2. data/COPYING +19 -0
  3. data/README.rdoc +723 -0
  4. data/Rakefile +193 -0
  5. data/bin/sequel +196 -0
  6. data/doc/advanced_associations.rdoc +644 -0
  7. data/doc/cheat_sheet.rdoc +218 -0
  8. data/doc/dataset_basics.rdoc +106 -0
  9. data/doc/dataset_filtering.rdoc +158 -0
  10. data/doc/opening_databases.rdoc +296 -0
  11. data/doc/prepared_statements.rdoc +104 -0
  12. data/doc/reflection.rdoc +84 -0
  13. data/doc/release_notes/1.0.txt +38 -0
  14. data/doc/release_notes/1.1.txt +143 -0
  15. data/doc/release_notes/1.3.txt +101 -0
  16. data/doc/release_notes/1.4.0.txt +53 -0
  17. data/doc/release_notes/1.5.0.txt +155 -0
  18. data/doc/release_notes/2.0.0.txt +298 -0
  19. data/doc/release_notes/2.1.0.txt +271 -0
  20. data/doc/release_notes/2.10.0.txt +328 -0
  21. data/doc/release_notes/2.11.0.txt +215 -0
  22. data/doc/release_notes/2.12.0.txt +534 -0
  23. data/doc/release_notes/2.2.0.txt +253 -0
  24. data/doc/release_notes/2.3.0.txt +88 -0
  25. data/doc/release_notes/2.4.0.txt +106 -0
  26. data/doc/release_notes/2.5.0.txt +137 -0
  27. data/doc/release_notes/2.6.0.txt +157 -0
  28. data/doc/release_notes/2.7.0.txt +166 -0
  29. data/doc/release_notes/2.8.0.txt +171 -0
  30. data/doc/release_notes/2.9.0.txt +97 -0
  31. data/doc/release_notes/3.0.0.txt +221 -0
  32. data/doc/release_notes/3.1.0.txt +406 -0
  33. data/doc/release_notes/3.10.0.txt +286 -0
  34. data/doc/release_notes/3.2.0.txt +268 -0
  35. data/doc/release_notes/3.3.0.txt +192 -0
  36. data/doc/release_notes/3.4.0.txt +325 -0
  37. data/doc/release_notes/3.5.0.txt +510 -0
  38. data/doc/release_notes/3.6.0.txt +366 -0
  39. data/doc/release_notes/3.7.0.txt +179 -0
  40. data/doc/release_notes/3.8.0.txt +151 -0
  41. data/doc/release_notes/3.9.0.txt +233 -0
  42. data/doc/schema.rdoc +36 -0
  43. data/doc/sharding.rdoc +113 -0
  44. data/doc/virtual_rows.rdoc +205 -0
  45. data/lib/sequel.rb +1 -0
  46. data/lib/sequel/adapters/ado.rb +90 -0
  47. data/lib/sequel/adapters/ado/mssql.rb +30 -0
  48. data/lib/sequel/adapters/amalgalite.rb +176 -0
  49. data/lib/sequel/adapters/db2.rb +139 -0
  50. data/lib/sequel/adapters/dbi.rb +113 -0
  51. data/lib/sequel/adapters/do.rb +188 -0
  52. data/lib/sequel/adapters/do/mysql.rb +49 -0
  53. data/lib/sequel/adapters/do/postgres.rb +91 -0
  54. data/lib/sequel/adapters/do/sqlite.rb +40 -0
  55. data/lib/sequel/adapters/firebird.rb +283 -0
  56. data/lib/sequel/adapters/informix.rb +77 -0
  57. data/lib/sequel/adapters/jdbc.rb +587 -0
  58. data/lib/sequel/adapters/jdbc/as400.rb +58 -0
  59. data/lib/sequel/adapters/jdbc/h2.rb +133 -0
  60. data/lib/sequel/adapters/jdbc/mssql.rb +57 -0
  61. data/lib/sequel/adapters/jdbc/mysql.rb +78 -0
  62. data/lib/sequel/adapters/jdbc/oracle.rb +50 -0
  63. data/lib/sequel/adapters/jdbc/postgresql.rb +108 -0
  64. data/lib/sequel/adapters/jdbc/sqlite.rb +55 -0
  65. data/lib/sequel/adapters/mysql.rb +421 -0
  66. data/lib/sequel/adapters/odbc.rb +143 -0
  67. data/lib/sequel/adapters/odbc/mssql.rb +42 -0
  68. data/lib/sequel/adapters/openbase.rb +64 -0
  69. data/lib/sequel/adapters/oracle.rb +131 -0
  70. data/lib/sequel/adapters/postgres.rb +504 -0
  71. data/lib/sequel/adapters/shared/mssql.rb +490 -0
  72. data/lib/sequel/adapters/shared/mysql.rb +498 -0
  73. data/lib/sequel/adapters/shared/oracle.rb +195 -0
  74. data/lib/sequel/adapters/shared/postgres.rb +830 -0
  75. data/lib/sequel/adapters/shared/progress.rb +44 -0
  76. data/lib/sequel/adapters/shared/sqlite.rb +389 -0
  77. data/lib/sequel/adapters/sqlite.rb +224 -0
  78. data/lib/sequel/adapters/utils/stored_procedures.rb +84 -0
  79. data/lib/sequel/connection_pool.rb +99 -0
  80. data/lib/sequel/connection_pool/sharded_single.rb +84 -0
  81. data/lib/sequel/connection_pool/sharded_threaded.rb +211 -0
  82. data/lib/sequel/connection_pool/single.rb +29 -0
  83. data/lib/sequel/connection_pool/threaded.rb +150 -0
  84. data/lib/sequel/core.rb +293 -0
  85. data/lib/sequel/core_sql.rb +241 -0
  86. data/lib/sequel/database.rb +1079 -0
  87. data/lib/sequel/database/schema_generator.rb +327 -0
  88. data/lib/sequel/database/schema_methods.rb +203 -0
  89. data/lib/sequel/database/schema_sql.rb +320 -0
  90. data/lib/sequel/dataset.rb +32 -0
  91. data/lib/sequel/dataset/actions.rb +441 -0
  92. data/lib/sequel/dataset/features.rb +86 -0
  93. data/lib/sequel/dataset/graph.rb +254 -0
  94. data/lib/sequel/dataset/misc.rb +119 -0
  95. data/lib/sequel/dataset/mutation.rb +64 -0
  96. data/lib/sequel/dataset/prepared_statements.rb +227 -0
  97. data/lib/sequel/dataset/query.rb +709 -0
  98. data/lib/sequel/dataset/sql.rb +996 -0
  99. data/lib/sequel/exceptions.rb +51 -0
  100. data/lib/sequel/extensions/blank.rb +43 -0
  101. data/lib/sequel/extensions/inflector.rb +242 -0
  102. data/lib/sequel/extensions/looser_typecasting.rb +21 -0
  103. data/lib/sequel/extensions/migration.rb +239 -0
  104. data/lib/sequel/extensions/named_timezones.rb +61 -0
  105. data/lib/sequel/extensions/pagination.rb +100 -0
  106. data/lib/sequel/extensions/pretty_table.rb +82 -0
  107. data/lib/sequel/extensions/query.rb +52 -0
  108. data/lib/sequel/extensions/schema_dumper.rb +271 -0
  109. data/lib/sequel/extensions/sql_expr.rb +122 -0
  110. data/lib/sequel/extensions/string_date_time.rb +46 -0
  111. data/lib/sequel/extensions/thread_local_timezones.rb +48 -0
  112. data/lib/sequel/metaprogramming.rb +9 -0
  113. data/lib/sequel/model.rb +120 -0
  114. data/lib/sequel/model/associations.rb +1514 -0
  115. data/lib/sequel/model/base.rb +1069 -0
  116. data/lib/sequel/model/default_inflections.rb +45 -0
  117. data/lib/sequel/model/errors.rb +39 -0
  118. data/lib/sequel/model/exceptions.rb +21 -0
  119. data/lib/sequel/model/inflections.rb +162 -0
  120. data/lib/sequel/model/plugins.rb +70 -0
  121. data/lib/sequel/plugins/active_model.rb +59 -0
  122. data/lib/sequel/plugins/association_dependencies.rb +103 -0
  123. data/lib/sequel/plugins/association_proxies.rb +41 -0
  124. data/lib/sequel/plugins/boolean_readers.rb +53 -0
  125. data/lib/sequel/plugins/caching.rb +141 -0
  126. data/lib/sequel/plugins/class_table_inheritance.rb +214 -0
  127. data/lib/sequel/plugins/composition.rb +138 -0
  128. data/lib/sequel/plugins/force_encoding.rb +72 -0
  129. data/lib/sequel/plugins/hook_class_methods.rb +126 -0
  130. data/lib/sequel/plugins/identity_map.rb +116 -0
  131. data/lib/sequel/plugins/instance_filters.rb +98 -0
  132. data/lib/sequel/plugins/instance_hooks.rb +57 -0
  133. data/lib/sequel/plugins/lazy_attributes.rb +77 -0
  134. data/lib/sequel/plugins/many_through_many.rb +208 -0
  135. data/lib/sequel/plugins/nested_attributes.rb +206 -0
  136. data/lib/sequel/plugins/optimistic_locking.rb +81 -0
  137. data/lib/sequel/plugins/rcte_tree.rb +281 -0
  138. data/lib/sequel/plugins/schema.rb +66 -0
  139. data/lib/sequel/plugins/serialization.rb +166 -0
  140. data/lib/sequel/plugins/single_table_inheritance.rb +74 -0
  141. data/lib/sequel/plugins/subclasses.rb +45 -0
  142. data/lib/sequel/plugins/tactical_eager_loading.rb +61 -0
  143. data/lib/sequel/plugins/timestamps.rb +87 -0
  144. data/lib/sequel/plugins/touch.rb +118 -0
  145. data/lib/sequel/plugins/typecast_on_load.rb +72 -0
  146. data/lib/sequel/plugins/validation_class_methods.rb +405 -0
  147. data/lib/sequel/plugins/validation_helpers.rb +223 -0
  148. data/lib/sequel/sql.rb +1020 -0
  149. data/lib/sequel/timezones.rb +161 -0
  150. data/lib/sequel/version.rb +12 -0
  151. data/lib/sequel_core.rb +1 -0
  152. data/lib/sequel_model.rb +1 -0
  153. data/spec/adapters/firebird_spec.rb +407 -0
  154. data/spec/adapters/informix_spec.rb +97 -0
  155. data/spec/adapters/mssql_spec.rb +403 -0
  156. data/spec/adapters/mysql_spec.rb +1019 -0
  157. data/spec/adapters/oracle_spec.rb +286 -0
  158. data/spec/adapters/postgres_spec.rb +969 -0
  159. data/spec/adapters/spec_helper.rb +51 -0
  160. data/spec/adapters/sqlite_spec.rb +432 -0
  161. data/spec/core/connection_pool_spec.rb +808 -0
  162. data/spec/core/core_sql_spec.rb +417 -0
  163. data/spec/core/database_spec.rb +1662 -0
  164. data/spec/core/dataset_spec.rb +3827 -0
  165. data/spec/core/expression_filters_spec.rb +595 -0
  166. data/spec/core/object_graph_spec.rb +296 -0
  167. data/spec/core/schema_generator_spec.rb +159 -0
  168. data/spec/core/schema_spec.rb +830 -0
  169. data/spec/core/spec_helper.rb +56 -0
  170. data/spec/core/version_spec.rb +7 -0
  171. data/spec/extensions/active_model_spec.rb +76 -0
  172. data/spec/extensions/association_dependencies_spec.rb +127 -0
  173. data/spec/extensions/association_proxies_spec.rb +50 -0
  174. data/spec/extensions/blank_spec.rb +67 -0
  175. data/spec/extensions/boolean_readers_spec.rb +92 -0
  176. data/spec/extensions/caching_spec.rb +250 -0
  177. data/spec/extensions/class_table_inheritance_spec.rb +252 -0
  178. data/spec/extensions/composition_spec.rb +194 -0
  179. data/spec/extensions/force_encoding_spec.rb +117 -0
  180. data/spec/extensions/hook_class_methods_spec.rb +470 -0
  181. data/spec/extensions/identity_map_spec.rb +202 -0
  182. data/spec/extensions/inflector_spec.rb +181 -0
  183. data/spec/extensions/instance_filters_spec.rb +55 -0
  184. data/spec/extensions/instance_hooks_spec.rb +133 -0
  185. data/spec/extensions/lazy_attributes_spec.rb +153 -0
  186. data/spec/extensions/looser_typecasting_spec.rb +39 -0
  187. data/spec/extensions/many_through_many_spec.rb +884 -0
  188. data/spec/extensions/migration_spec.rb +332 -0
  189. data/spec/extensions/named_timezones_spec.rb +72 -0
  190. data/spec/extensions/nested_attributes_spec.rb +396 -0
  191. data/spec/extensions/optimistic_locking_spec.rb +100 -0
  192. data/spec/extensions/pagination_spec.rb +99 -0
  193. data/spec/extensions/pretty_table_spec.rb +91 -0
  194. data/spec/extensions/query_spec.rb +85 -0
  195. data/spec/extensions/rcte_tree_spec.rb +205 -0
  196. data/spec/extensions/schema_dumper_spec.rb +357 -0
  197. data/spec/extensions/schema_spec.rb +127 -0
  198. data/spec/extensions/serialization_spec.rb +209 -0
  199. data/spec/extensions/single_table_inheritance_spec.rb +96 -0
  200. data/spec/extensions/spec_helper.rb +91 -0
  201. data/spec/extensions/sql_expr_spec.rb +89 -0
  202. data/spec/extensions/string_date_time_spec.rb +93 -0
  203. data/spec/extensions/subclasses_spec.rb +52 -0
  204. data/spec/extensions/tactical_eager_loading_spec.rb +65 -0
  205. data/spec/extensions/thread_local_timezones_spec.rb +45 -0
  206. data/spec/extensions/timestamps_spec.rb +150 -0
  207. data/spec/extensions/touch_spec.rb +155 -0
  208. data/spec/extensions/typecast_on_load_spec.rb +69 -0
  209. data/spec/extensions/validation_class_methods_spec.rb +984 -0
  210. data/spec/extensions/validation_helpers_spec.rb +438 -0
  211. data/spec/integration/associations_test.rb +281 -0
  212. data/spec/integration/database_test.rb +26 -0
  213. data/spec/integration/dataset_test.rb +963 -0
  214. data/spec/integration/eager_loader_test.rb +734 -0
  215. data/spec/integration/model_test.rb +130 -0
  216. data/spec/integration/plugin_test.rb +814 -0
  217. data/spec/integration/prepared_statement_test.rb +213 -0
  218. data/spec/integration/schema_test.rb +361 -0
  219. data/spec/integration/spec_helper.rb +73 -0
  220. data/spec/integration/timezone_test.rb +55 -0
  221. data/spec/integration/transaction_test.rb +122 -0
  222. data/spec/integration/type_test.rb +96 -0
  223. data/spec/model/association_reflection_spec.rb +175 -0
  224. data/spec/model/associations_spec.rb +2633 -0
  225. data/spec/model/base_spec.rb +418 -0
  226. data/spec/model/dataset_methods_spec.rb +78 -0
  227. data/spec/model/eager_loading_spec.rb +1391 -0
  228. data/spec/model/hooks_spec.rb +240 -0
  229. data/spec/model/inflector_spec.rb +26 -0
  230. data/spec/model/model_spec.rb +593 -0
  231. data/spec/model/plugins_spec.rb +236 -0
  232. data/spec/model/record_spec.rb +1500 -0
  233. data/spec/model/spec_helper.rb +97 -0
  234. data/spec/model/validations_spec.rb +153 -0
  235. data/spec/rcov.opts +6 -0
  236. data/spec/spec_config.rb.example +10 -0
  237. metadata +346 -0
@@ -0,0 +1,86 @@
1
+ module Sequel
2
+ class Dataset
3
+ # ---------------------
4
+ # :section: Methods that describe what the dataset supports
5
+ # These methods all return booleans, with most describing whether or not the
6
+ # dataset supports a feature.
7
+ # ---------------------
8
+
9
+ # Method used to check if WITH is supported
10
+ WITH_SUPPORTED=:select_with_sql
11
+
12
+ # Whether this dataset quotes identifiers.
13
+ def quote_identifiers?
14
+ @quote_identifiers
15
+ end
16
+
17
+ # Whether this dataset will provide accurate number of rows matched for
18
+ # delete and update statements. Accurate in this case is the number of
19
+ # rows matched by the dataset's filter.
20
+ def provides_accurate_rows_matched?
21
+ true
22
+ end
23
+
24
+ # Whether the dataset requires SQL standard datetimes (false by default,
25
+ # as most allow strings with ISO 8601 format.
26
+ def requires_sql_standard_datetimes?
27
+ false
28
+ end
29
+
30
+ # Whether the dataset supports common table expressions (the WITH clause).
31
+ def supports_cte?
32
+ select_clause_methods.include?(WITH_SUPPORTED)
33
+ end
34
+
35
+ # Whether the dataset supports the DISTINCT ON clause, false by default.
36
+ def supports_distinct_on?
37
+ false
38
+ end
39
+
40
+ # Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
41
+ def supports_intersect_except?
42
+ true
43
+ end
44
+
45
+ # Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default.
46
+ def supports_intersect_except_all?
47
+ true
48
+ end
49
+
50
+ # Whether the dataset supports the IS TRUE syntax.
51
+ def supports_is_true?
52
+ true
53
+ end
54
+
55
+ # Whether the dataset supports the JOIN table USING (column1, ...) syntax.
56
+ def supports_join_using?
57
+ true
58
+ end
59
+
60
+ # Whether modifying joined datasets is supported.
61
+ def supports_modifying_joins?
62
+ false
63
+ end
64
+
65
+ # Whether the IN/NOT IN operators support multiple columns when an
66
+ # array of values is given.
67
+ def supports_multiple_column_in?
68
+ true
69
+ end
70
+
71
+ # Whether the dataset supports timezones in literal timestamps
72
+ def supports_timestamp_timezones?
73
+ false
74
+ end
75
+
76
+ # Whether the dataset supports fractional seconds in literal timestamps
77
+ def supports_timestamp_usecs?
78
+ true
79
+ end
80
+
81
+ # Whether the dataset supports window functions.
82
+ def supports_window_functions?
83
+ false
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,254 @@
1
+ module Sequel
2
+ class Dataset
3
+ # ---------------------
4
+ # :section: Methods related to dataset graphing
5
+ # Dataset graphing changes the dataset to yield hashes where keys are table
6
+ # name symbols and columns are hashes representing the values related to
7
+ # that table. All of these methods return modified copies of the receiver.
8
+ # ---------------------
9
+
10
+ # Adds the given graph aliases to the list of graph aliases to use,
11
+ # unlike #set_graph_aliases, which replaces the list. See
12
+ # #set_graph_aliases.
13
+ def add_graph_aliases(graph_aliases)
14
+ ds = select_more(*graph_alias_columns(graph_aliases))
15
+ ds.opts[:graph_aliases] = (ds.opts[:graph_aliases] || (ds.opts[:graph][:column_aliases] rescue {}) || {}).merge(graph_aliases)
16
+ ds
17
+ end
18
+
19
+ # Allows you to join multiple datasets/tables and have the result set
20
+ # split into component tables.
21
+ #
22
+ # This differs from the usual usage of join, which returns the result set
23
+ # as a single hash. For example:
24
+ #
25
+ # # CREATE TABLE artists (id INTEGER, name TEXT);
26
+ # # CREATE TABLE albums (id INTEGER, name TEXT, artist_id INTEGER);
27
+ # DB[:artists].left_outer_join(:albums, :artist_id=>:id).first
28
+ # => {:id=>albums.id, :name=>albums.name, :artist_id=>albums.artist_id}
29
+ # DB[:artists].graph(:albums, :artist_id=>:id).first
30
+ # => {:artists=>{:id=>artists.id, :name=>artists.name}, :albums=>{:id=>albums.id, :name=>albums.name, :artist_id=>albums.artist_id}}
31
+ #
32
+ # Using a join such as left_outer_join, the attribute names that are shared between
33
+ # the tables are combined in the single return hash. You can get around that by
34
+ # using .select with correct aliases for all of the columns, but it is simpler to
35
+ # use graph and have the result set split for you. In addition, graph respects
36
+ # any row_proc of the current dataset and the datasets you use with graph.
37
+ #
38
+ # If you are graphing a table and all columns for that table are nil, this
39
+ # indicates that no matching rows existed in the table, so graph will return nil
40
+ # instead of a hash with all nil values:
41
+ #
42
+ # # If the artist doesn't have any albums
43
+ # DB[:artists].graph(:albums, :artist_id=>:id).first
44
+ # => {:artists=>{:id=>artists.id, :name=>artists.name}, :albums=>nil}
45
+ #
46
+ # Arguments:
47
+ # * dataset - Can be a symbol (specifying a table), another dataset,
48
+ # or an object that responds to .dataset and return a symbol or a dataset
49
+ # * join_conditions - Any condition(s) allowed by join_table.
50
+ # * options - A hash of graph options. The following options are currently used:
51
+ # * :from_self_alias - The alias to use when the receiver is not a graphed
52
+ # dataset but it contains multiple FROM tables or a JOIN. In this case,
53
+ # the receiver is wrapped in a from_self before graphing, and this option
54
+ # determines the alias to use.
55
+ # * :implicit_qualifier - The qualifier of implicit conditions, see #join_table.
56
+ # * :join_type - The type of join to use (passed to join_table). Defaults to
57
+ # :left_outer.
58
+ # * :select - An array of columns to select. When not used, selects
59
+ # all columns in the given dataset. When set to false, selects no
60
+ # columns and is like simply joining the tables, though graph keeps
61
+ # some metadata about join that makes it important to use graph instead
62
+ # of join.
63
+ # * :table_alias - The alias to use for the table. If not specified, doesn't
64
+ # alias the table. You will get an error if the the alias (or table) name is
65
+ # used more than once.
66
+ # * block - A block that is passed to join_table.
67
+ def graph(dataset, join_conditions = nil, options = {}, &block)
68
+ # Allow the use of a model, dataset, or symbol as the first argument
69
+ # Find the table name/dataset based on the argument
70
+ dataset = dataset.dataset if dataset.respond_to?(:dataset)
71
+ table_alias = options[:table_alias]
72
+ case dataset
73
+ when Symbol
74
+ table = dataset
75
+ dataset = @db[dataset]
76
+ table_alias ||= table
77
+ when ::Sequel::Dataset
78
+ if dataset.simple_select_all?
79
+ table = dataset.opts[:from].first
80
+ table_alias ||= table
81
+ else
82
+ table = dataset
83
+ table_alias ||= dataset_alias((@opts[:num_dataset_sources] || 0)+1)
84
+ end
85
+ else
86
+ raise Error, "The dataset argument should be a symbol, dataset, or model"
87
+ end
88
+
89
+ # Raise Sequel::Error with explanation that the table alias has been used
90
+ raise_alias_error = lambda do
91
+ raise(Error, "this #{options[:table_alias] ? 'alias' : 'table'} has already been been used, please specify " \
92
+ "#{options[:table_alias] ? 'a different alias' : 'an alias via the :table_alias option'}")
93
+ end
94
+
95
+ # Only allow table aliases that haven't been used
96
+ raise_alias_error.call if @opts[:graph] && @opts[:graph][:table_aliases] && @opts[:graph][:table_aliases].include?(table_alias)
97
+
98
+ # Use a from_self if this is already a joined table
99
+ ds = (!@opts[:graph] && (@opts[:from].length > 1 || @opts[:join])) ? from_self(:alias=>options[:from_self_alias] || first_source) : self
100
+
101
+ # Join the table early in order to avoid cloning the dataset twice
102
+ ds = ds.join_table(options[:join_type] || :left_outer, table, join_conditions, :table_alias=>table_alias, :implicit_qualifier=>options[:implicit_qualifier], &block)
103
+ opts = ds.opts
104
+
105
+ # Whether to include the table in the result set
106
+ add_table = options[:select] == false ? false : true
107
+ # Whether to add the columns to the list of column aliases
108
+ add_columns = !ds.opts.include?(:graph_aliases)
109
+
110
+ # Setup the initial graph data structure if it doesn't exist
111
+ unless graph = opts[:graph]
112
+ master = alias_symbol(ds.first_source_alias)
113
+ raise_alias_error.call if master == table_alias
114
+ # Master hash storing all .graph related information
115
+ graph = opts[:graph] = {}
116
+ # Associates column aliases back to tables and columns
117
+ column_aliases = graph[:column_aliases] = {}
118
+ # Associates table alias (the master is never aliased)
119
+ table_aliases = graph[:table_aliases] = {master=>self}
120
+ # Keep track of the alias numbers used
121
+ ca_num = graph[:column_alias_num] = Hash.new(0)
122
+ # All columns in the master table are never
123
+ # aliased, but are not included if set_graph_aliases
124
+ # has been used.
125
+ if add_columns
126
+ select = opts[:select] = []
127
+ columns.each do |column|
128
+ column_aliases[column] = [master, column]
129
+ select.push(SQL::QualifiedIdentifier.new(master, column))
130
+ end
131
+ end
132
+ end
133
+
134
+ # Add the table alias to the list of aliases
135
+ # Even if it isn't been used in the result set,
136
+ # we add a key for it with a nil value so we can check if it
137
+ # is used more than once
138
+ table_aliases = graph[:table_aliases]
139
+ table_aliases[table_alias] = add_table ? dataset : nil
140
+
141
+ # Add the columns to the selection unless we are ignoring them
142
+ if add_table && add_columns
143
+ select = opts[:select]
144
+ column_aliases = graph[:column_aliases]
145
+ ca_num = graph[:column_alias_num]
146
+ # Which columns to add to the result set
147
+ cols = options[:select] || dataset.columns
148
+ # If the column hasn't been used yet, don't alias it.
149
+ # If it has been used, try table_column.
150
+ # If that has been used, try table_column_N
151
+ # using the next value of N that we know hasn't been
152
+ # used
153
+ cols.each do |column|
154
+ col_alias, identifier = if column_aliases[column]
155
+ column_alias = :"#{table_alias}_#{column}"
156
+ if column_aliases[column_alias]
157
+ column_alias_num = ca_num[column_alias]
158
+ column_alias = :"#{column_alias}_#{column_alias_num}"
159
+ ca_num[column_alias] += 1
160
+ end
161
+ [column_alias, SQL::QualifiedIdentifier.new(table_alias, column).as(column_alias)]
162
+ else
163
+ [column, SQL::QualifiedIdentifier.new(table_alias, column)]
164
+ end
165
+ column_aliases[col_alias] = [table_alias, column]
166
+ select.push(identifier)
167
+ end
168
+ end
169
+ ds
170
+ end
171
+
172
+ # This allows you to manually specify the graph aliases to use
173
+ # when using graph. You can use it to only select certain
174
+ # columns, and have those columns mapped to specific aliases
175
+ # in the result set. This is the equivalent of .select for a
176
+ # graphed dataset, and must be used instead of .select whenever
177
+ # graphing is used. Example:
178
+ #
179
+ # DB[:artists].graph(:albums, :artist_id=>:id).set_graph_aliases(:artist_name=>[:artists, :name], :album_name=>[:albums, :name], :forty_two=>[:albums, :fourtwo, 42]).first
180
+ # => {:artists=>{:name=>artists.name}, :albums=>{:name=>albums.name, :fourtwo=>42}}
181
+ #
182
+ # Arguments:
183
+ # * graph_aliases - Should be a hash with keys being symbols of
184
+ # column aliases, and values being arrays with two or three elements.
185
+ # The first element of the array should be the table alias symbol,
186
+ # and the second should be the actual column name symbol. If the array
187
+ # has a third element, it is used as the value returned, instead of
188
+ # table_alias.column_name.
189
+ def set_graph_aliases(graph_aliases)
190
+ ds = select(*graph_alias_columns(graph_aliases))
191
+ ds.opts[:graph_aliases] = graph_aliases
192
+ ds
193
+ end
194
+
195
+ # Remove the splitting of results into subhashes. Also removes
196
+ # metadata related to graphing, so you should not call graph
197
+ # any tables to this dataset after calling this method.
198
+ def ungraphed
199
+ clone(:graph=>nil)
200
+ end
201
+
202
+ private
203
+
204
+ # Transform the hash of graph aliases to an array of columns
205
+ def graph_alias_columns(graph_aliases)
206
+ graph_aliases.collect do |col_alias, tc|
207
+ identifier = tc[2] || SQL::QualifiedIdentifier.new(tc[0], tc[1])
208
+ identifier = SQL::AliasedExpression.new(identifier, col_alias) if tc[2] or tc[1] != col_alias
209
+ identifier
210
+ end
211
+ end
212
+
213
+ # Fetch the rows, split them into component table parts,
214
+ # tranform and run the row_proc on each part (if applicable),
215
+ # and yield a hash of the parts.
216
+ def graph_each
217
+ # Reject tables with nil datasets, as they are excluded from
218
+ # the result set
219
+ datasets = @opts[:graph][:table_aliases].to_a.reject{|ta,ds| ds.nil?}
220
+ # Get just the list of table aliases into a local variable, for speed
221
+ table_aliases = datasets.collect{|ta,ds| ta}
222
+ # Get an array of arrays, one for each dataset, with
223
+ # the necessary information about each dataset, for speed
224
+ datasets = datasets.collect{|ta, ds| [ta, ds, ds.row_proc]}
225
+ # Use the manually set graph aliases, if any, otherwise
226
+ # use the ones automatically created by .graph
227
+ column_aliases = @opts[:graph_aliases] || @opts[:graph][:column_aliases]
228
+ fetch_rows(select_sql) do |r|
229
+ graph = {}
230
+ # Create the sub hashes, one per table
231
+ table_aliases.each{|ta| graph[ta]={}}
232
+ # Split the result set based on the column aliases
233
+ # If there are columns in the result set that are
234
+ # not in column_aliases, they are ignored
235
+ column_aliases.each do |col_alias, tc|
236
+ ta, column = tc
237
+ graph[ta][column] = r[col_alias]
238
+ end
239
+ # For each dataset run the row_proc if applicable
240
+ datasets.each do |ta,ds,rp|
241
+ g = graph[ta]
242
+ graph[ta] = if g.values.any?{|x| !x.nil?}
243
+ rp ? rp.call(g) : g
244
+ else
245
+ nil
246
+ end
247
+ end
248
+
249
+ yield graph
250
+ end
251
+ self
252
+ end
253
+ end
254
+ end
@@ -0,0 +1,119 @@
1
+ module Sequel
2
+ class Dataset
3
+ # ---------------------
4
+ # :section: Miscellaneous methods
5
+ # These methods don't fit cleanly into another section.
6
+ # ---------------------
7
+
8
+ NOTIMPL_MSG = "This method must be overridden in Sequel adapters".freeze
9
+ ARRAY_ACCESS_ERROR_MSG = 'You cannot call Dataset#[] with an integer or with no arguments.'.freeze
10
+ ARG_BLOCK_ERROR_MSG = 'Must use either an argument or a block, not both'.freeze
11
+ IMPORT_ERROR_MSG = 'Using Sequel::Dataset#import an empty column array is not allowed'.freeze
12
+
13
+ # The database that corresponds to this dataset
14
+ attr_accessor :db
15
+
16
+ # The hash of options for this dataset, keys are symbols.
17
+ attr_accessor :opts
18
+
19
+ # Constructs a new Dataset instance with an associated database and
20
+ # options. Datasets are usually constructed by invoking the Database#[] method:
21
+ #
22
+ # DB[:posts]
23
+ #
24
+ # Sequel::Dataset is an abstract class that is not useful by itself. Each
25
+ # database adaptor should provide a subclass of Sequel::Dataset, and have
26
+ # the Database#dataset method return an instance of that class.
27
+ def initialize(db, opts = nil)
28
+ @db = db
29
+ @quote_identifiers = db.quote_identifiers? if db.respond_to?(:quote_identifiers?)
30
+ @identifier_input_method = db.identifier_input_method if db.respond_to?(:identifier_input_method)
31
+ @identifier_output_method = db.identifier_output_method if db.respond_to?(:identifier_output_method)
32
+ @opts = opts || {}
33
+ @row_proc = nil
34
+ end
35
+
36
+ # Return the dataset as an aliased expression with the given alias. You can
37
+ # use this as a FROM or JOIN dataset, or as a column if this dataset
38
+ # returns a single row and column.
39
+ def as(aliaz)
40
+ ::Sequel::SQL::AliasedExpression.new(self, aliaz)
41
+ end
42
+
43
+ # Yield a dataset for each server in the connection pool that is tied to that server.
44
+ # Intended for use in sharded environments where all servers need to be modified
45
+ # with the same data:
46
+ #
47
+ # DB[:configs].where(:key=>'setting').each_server{|ds| ds.update(:value=>'new_value')}
48
+ def each_server
49
+ db.servers.each{|s| yield server(s)}
50
+ end
51
+
52
+ # The first source (primary table) for this dataset. If the dataset doesn't
53
+ # have a table, raises an error. If the table is aliased, returns the aliased name.
54
+ def first_source_alias
55
+ source = @opts[:from]
56
+ if source.nil? || source.empty?
57
+ raise Error, 'No source specified for query'
58
+ end
59
+ case s = source.first
60
+ when SQL::AliasedExpression
61
+ s.aliaz
62
+ when Symbol
63
+ sch, table, aliaz = split_symbol(s)
64
+ aliaz ? aliaz.to_sym : s
65
+ else
66
+ s
67
+ end
68
+ end
69
+ alias first_source first_source_alias
70
+
71
+ # The first source (primary table) for this dataset. If the dataset doesn't
72
+ # have a table, raises an error. If the table is aliased, returns the original
73
+ # table, not the alias
74
+ def first_source_table
75
+ source = @opts[:from]
76
+ if source.nil? || source.empty?
77
+ raise Error, 'No source specified for query'
78
+ end
79
+ case s = source.first
80
+ when SQL::AliasedExpression
81
+ s.expression
82
+ when Symbol
83
+ sch, table, aliaz = split_symbol(s)
84
+ aliaz ? (sch ? SQL::QualifiedIdentifier.new(sch, table) : table.to_sym) : s
85
+ else
86
+ s
87
+ end
88
+ end
89
+
90
+ # Returns a string representation of the dataset including the class name
91
+ # and the corresponding SQL select statement.
92
+ def inspect
93
+ "#<#{self.class}: #{sql.inspect}>"
94
+ end
95
+
96
+ # Creates a unique table alias that hasn't already been used in the dataset.
97
+ # table_alias can be any type of object accepted by alias_symbol.
98
+ # The symbol returned will be the implicit alias in the argument,
99
+ # possibly appended with "_N" if the implicit alias has already been
100
+ # used, where N is an integer starting at 0 and increasing until an
101
+ # unused one is found.
102
+ def unused_table_alias(table_alias)
103
+ table_alias = alias_symbol(table_alias)
104
+ used_aliases = []
105
+ used_aliases += opts[:from].map{|t| alias_symbol(t)} if opts[:from]
106
+ used_aliases += opts[:join].map{|j| j.table_alias ? alias_alias_symbol(j.table_alias) : alias_symbol(j.table)} if opts[:join]
107
+ if used_aliases.include?(table_alias)
108
+ i = 0
109
+ loop do
110
+ ta = :"#{table_alias}_#{i}"
111
+ return ta unless used_aliases.include?(ta)
112
+ i += 1
113
+ end
114
+ else
115
+ table_alias
116
+ end
117
+ end
118
+ end
119
+ end