viking-sequel 3.10.0

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