epugh-sequel 0.0.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 (134) hide show
  1. data/README.rdoc +652 -0
  2. data/VERSION.yml +4 -0
  3. data/bin/sequel +104 -0
  4. data/lib/sequel.rb +1 -0
  5. data/lib/sequel/adapters/ado.rb +85 -0
  6. data/lib/sequel/adapters/db2.rb +132 -0
  7. data/lib/sequel/adapters/dbi.rb +101 -0
  8. data/lib/sequel/adapters/do.rb +197 -0
  9. data/lib/sequel/adapters/do/mysql.rb +38 -0
  10. data/lib/sequel/adapters/do/postgres.rb +92 -0
  11. data/lib/sequel/adapters/do/sqlite.rb +31 -0
  12. data/lib/sequel/adapters/firebird.rb +307 -0
  13. data/lib/sequel/adapters/informix.rb +75 -0
  14. data/lib/sequel/adapters/jdbc.rb +485 -0
  15. data/lib/sequel/adapters/jdbc/h2.rb +62 -0
  16. data/lib/sequel/adapters/jdbc/mysql.rb +56 -0
  17. data/lib/sequel/adapters/jdbc/oracle.rb +23 -0
  18. data/lib/sequel/adapters/jdbc/postgresql.rb +101 -0
  19. data/lib/sequel/adapters/jdbc/sqlite.rb +43 -0
  20. data/lib/sequel/adapters/mysql.rb +370 -0
  21. data/lib/sequel/adapters/odbc.rb +184 -0
  22. data/lib/sequel/adapters/openbase.rb +57 -0
  23. data/lib/sequel/adapters/oracle.rb +140 -0
  24. data/lib/sequel/adapters/postgres.rb +453 -0
  25. data/lib/sequel/adapters/shared/mssql.rb +93 -0
  26. data/lib/sequel/adapters/shared/mysql.rb +341 -0
  27. data/lib/sequel/adapters/shared/oracle.rb +62 -0
  28. data/lib/sequel/adapters/shared/postgres.rb +743 -0
  29. data/lib/sequel/adapters/shared/progress.rb +34 -0
  30. data/lib/sequel/adapters/shared/sqlite.rb +263 -0
  31. data/lib/sequel/adapters/sqlite.rb +243 -0
  32. data/lib/sequel/adapters/utils/date_format.rb +21 -0
  33. data/lib/sequel/adapters/utils/stored_procedures.rb +75 -0
  34. data/lib/sequel/adapters/utils/unsupported.rb +62 -0
  35. data/lib/sequel/connection_pool.rb +258 -0
  36. data/lib/sequel/core.rb +204 -0
  37. data/lib/sequel/core_sql.rb +185 -0
  38. data/lib/sequel/database.rb +687 -0
  39. data/lib/sequel/database/schema_generator.rb +324 -0
  40. data/lib/sequel/database/schema_methods.rb +164 -0
  41. data/lib/sequel/database/schema_sql.rb +324 -0
  42. data/lib/sequel/dataset.rb +422 -0
  43. data/lib/sequel/dataset/convenience.rb +237 -0
  44. data/lib/sequel/dataset/prepared_statements.rb +220 -0
  45. data/lib/sequel/dataset/sql.rb +1105 -0
  46. data/lib/sequel/deprecated.rb +529 -0
  47. data/lib/sequel/exceptions.rb +44 -0
  48. data/lib/sequel/extensions/blank.rb +42 -0
  49. data/lib/sequel/extensions/inflector.rb +288 -0
  50. data/lib/sequel/extensions/pagination.rb +96 -0
  51. data/lib/sequel/extensions/pretty_table.rb +78 -0
  52. data/lib/sequel/extensions/query.rb +48 -0
  53. data/lib/sequel/extensions/string_date_time.rb +47 -0
  54. data/lib/sequel/metaprogramming.rb +44 -0
  55. data/lib/sequel/migration.rb +212 -0
  56. data/lib/sequel/model.rb +142 -0
  57. data/lib/sequel/model/association_reflection.rb +263 -0
  58. data/lib/sequel/model/associations.rb +1024 -0
  59. data/lib/sequel/model/base.rb +911 -0
  60. data/lib/sequel/model/deprecated.rb +188 -0
  61. data/lib/sequel/model/deprecated_hooks.rb +103 -0
  62. data/lib/sequel/model/deprecated_inflector.rb +335 -0
  63. data/lib/sequel/model/deprecated_validations.rb +384 -0
  64. data/lib/sequel/model/errors.rb +37 -0
  65. data/lib/sequel/model/exceptions.rb +7 -0
  66. data/lib/sequel/model/inflections.rb +230 -0
  67. data/lib/sequel/model/plugins.rb +74 -0
  68. data/lib/sequel/object_graph.rb +230 -0
  69. data/lib/sequel/plugins/caching.rb +122 -0
  70. data/lib/sequel/plugins/hook_class_methods.rb +122 -0
  71. data/lib/sequel/plugins/schema.rb +53 -0
  72. data/lib/sequel/plugins/single_table_inheritance.rb +63 -0
  73. data/lib/sequel/plugins/validation_class_methods.rb +373 -0
  74. data/lib/sequel/sql.rb +854 -0
  75. data/lib/sequel/version.rb +11 -0
  76. data/lib/sequel_core.rb +1 -0
  77. data/lib/sequel_model.rb +1 -0
  78. data/spec/adapters/ado_spec.rb +46 -0
  79. data/spec/adapters/firebird_spec.rb +376 -0
  80. data/spec/adapters/informix_spec.rb +96 -0
  81. data/spec/adapters/mysql_spec.rb +875 -0
  82. data/spec/adapters/oracle_spec.rb +272 -0
  83. data/spec/adapters/postgres_spec.rb +692 -0
  84. data/spec/adapters/spec_helper.rb +10 -0
  85. data/spec/adapters/sqlite_spec.rb +550 -0
  86. data/spec/core/connection_pool_spec.rb +526 -0
  87. data/spec/core/core_ext_spec.rb +156 -0
  88. data/spec/core/core_sql_spec.rb +528 -0
  89. data/spec/core/database_spec.rb +1214 -0
  90. data/spec/core/dataset_spec.rb +3513 -0
  91. data/spec/core/expression_filters_spec.rb +363 -0
  92. data/spec/core/migration_spec.rb +261 -0
  93. data/spec/core/object_graph_spec.rb +280 -0
  94. data/spec/core/pretty_table_spec.rb +58 -0
  95. data/spec/core/schema_generator_spec.rb +167 -0
  96. data/spec/core/schema_spec.rb +778 -0
  97. data/spec/core/spec_helper.rb +82 -0
  98. data/spec/core/version_spec.rb +7 -0
  99. data/spec/extensions/blank_spec.rb +67 -0
  100. data/spec/extensions/caching_spec.rb +201 -0
  101. data/spec/extensions/hook_class_methods_spec.rb +470 -0
  102. data/spec/extensions/inflector_spec.rb +122 -0
  103. data/spec/extensions/pagination_spec.rb +99 -0
  104. data/spec/extensions/pretty_table_spec.rb +91 -0
  105. data/spec/extensions/query_spec.rb +85 -0
  106. data/spec/extensions/schema_spec.rb +111 -0
  107. data/spec/extensions/single_table_inheritance_spec.rb +53 -0
  108. data/spec/extensions/spec_helper.rb +90 -0
  109. data/spec/extensions/string_date_time_spec.rb +93 -0
  110. data/spec/extensions/validation_class_methods_spec.rb +1054 -0
  111. data/spec/integration/dataset_test.rb +160 -0
  112. data/spec/integration/eager_loader_test.rb +683 -0
  113. data/spec/integration/prepared_statement_test.rb +130 -0
  114. data/spec/integration/schema_test.rb +183 -0
  115. data/spec/integration/spec_helper.rb +75 -0
  116. data/spec/integration/type_test.rb +96 -0
  117. data/spec/model/association_reflection_spec.rb +93 -0
  118. data/spec/model/associations_spec.rb +1780 -0
  119. data/spec/model/base_spec.rb +494 -0
  120. data/spec/model/caching_spec.rb +217 -0
  121. data/spec/model/dataset_methods_spec.rb +78 -0
  122. data/spec/model/eager_loading_spec.rb +1165 -0
  123. data/spec/model/hooks_spec.rb +472 -0
  124. data/spec/model/inflector_spec.rb +126 -0
  125. data/spec/model/model_spec.rb +588 -0
  126. data/spec/model/plugins_spec.rb +142 -0
  127. data/spec/model/record_spec.rb +1243 -0
  128. data/spec/model/schema_spec.rb +92 -0
  129. data/spec/model/spec_helper.rb +124 -0
  130. data/spec/model/validations_spec.rb +1080 -0
  131. data/spec/rcov.opts +6 -0
  132. data/spec/spec.opts +0 -0
  133. data/spec/spec_config.rb.example +10 -0
  134. metadata +202 -0
@@ -0,0 +1,185 @@
1
+ class Array
2
+ # Return a Sequel::SQL::BooleanExpression created from this array, not matching any of the
3
+ # conditions.
4
+ def ~
5
+ sql_expr_if_all_two_pairs(:OR, true)
6
+ end
7
+
8
+ # True if the array is not empty and all of its elements are
9
+ # arrays of size 2. This is used to determine if the array
10
+ # could be a specifier of conditions, used similarly to a hash
11
+ # but allowing for duplicate keys.
12
+ #
13
+ # hash.to_a.all_two_pairs? # => true unless hash is empty
14
+ def all_two_pairs?
15
+ !empty? && all?{|i| (Array === i) && (i.length == 2)}
16
+ end
17
+
18
+ # Return a Sequel::SQL::CaseExpression with this array as the conditions and the given
19
+ # default value.
20
+ def case(default, expression = nil)
21
+ ::Sequel::SQL::CaseExpression.new(self, default, expression)
22
+ end
23
+
24
+ # Return a Sequel::SQL::Array created from this array. Used if this array contains
25
+ # all two pairs and you want it treated as an SQL array instead of a ordered hash-like
26
+ # conditions.
27
+ def sql_array
28
+ ::Sequel::SQL::SQLArray.new(self)
29
+ end
30
+
31
+ # Return a Sequel::SQL::BooleanExpression created from this array, matching all of the
32
+ # conditions.
33
+ def sql_expr
34
+ sql_expr_if_all_two_pairs
35
+ end
36
+
37
+ # Return a Sequel::SQL::BooleanExpression created from this array, matching none
38
+ # of the conditions.
39
+ def sql_negate
40
+ sql_expr_if_all_two_pairs(:AND, true)
41
+ end
42
+
43
+ # Return a Sequel::SQL::BooleanExpression created from this array, matching any of the
44
+ # conditions.
45
+ def sql_or
46
+ sql_expr_if_all_two_pairs(:OR)
47
+ end
48
+
49
+ # Return a Sequel::SQL::BooleanExpression representing an SQL string made up of the
50
+ # concatenation of this array's elements. If an argument is passed
51
+ # it is used in between each element of the array in the SQL
52
+ # concatenation.
53
+ def sql_string_join(joiner=nil)
54
+ if joiner
55
+ args = self.inject([]) do |m, a|
56
+ m << a
57
+ m << joiner
58
+ end
59
+ args.pop
60
+ else
61
+ args = self
62
+ end
63
+ args = args.collect{|a| [Symbol, ::Sequel::SQL::Expression, ::Sequel::LiteralString, TrueClass, FalseClass, NilClass].any?{|c| a.is_a?(c)} ? a : a.to_s}
64
+ ::Sequel::SQL::StringExpression.new(:'||', *args)
65
+ end
66
+
67
+ private
68
+
69
+ # Raise an error if this array is not made up of all two pairs, otherwise create a Sequel::SQL::BooleanExpression from this array.
70
+ def sql_expr_if_all_two_pairs(*args)
71
+ raise(Sequel::Error, 'Not all elements of the array are arrays of size 2, so it cannot be converted to an SQL expression') unless all_two_pairs?
72
+ ::Sequel::SQL::BooleanExpression.from_value_pairs(self, *args)
73
+ end
74
+ end
75
+
76
+ class Hash
77
+ # Return a Sequel::SQL::BooleanExpression created from this hash, matching
78
+ # all of the conditions in this hash and the condition specified by
79
+ # the given argument.
80
+ def &(ce)
81
+ ::Sequel::SQL::BooleanExpression.new(:AND, self, ce)
82
+ end
83
+
84
+ # Return a Sequel::SQL::BooleanExpression created from this hash, matching
85
+ # all of the conditions in this hash or the condition specified by
86
+ # the given argument.
87
+ def |(ce)
88
+ ::Sequel::SQL::BooleanExpression.new(:OR, self, ce)
89
+ end
90
+
91
+ # Return a Sequel::SQL::BooleanExpression created from this hash, not matching any of the
92
+ # conditions.
93
+ def ~
94
+ ::Sequel::SQL::BooleanExpression.from_value_pairs(self, :OR, true)
95
+ end
96
+
97
+ # Return a Sequel::SQL::CaseExpression with this hash as the conditions and the given
98
+ # default value. Note that the order of the conditions will be arbitrary, so all
99
+ # conditions should be orthogonal.
100
+ def case(default, expression = nil)
101
+ ::Sequel::SQL::CaseExpression.new(to_a, default, expression)
102
+ end
103
+
104
+ # Return a Sequel::SQL::BooleanExpression created from this hash, matching all of the
105
+ # conditions.
106
+ def sql_expr
107
+ ::Sequel::SQL::BooleanExpression.from_value_pairs(self)
108
+ end
109
+
110
+ # Return a Sequel::SQL::BooleanExpression created from this hash, matching none
111
+ # of the conditions.
112
+ def sql_negate
113
+ ::Sequel::SQL::BooleanExpression.from_value_pairs(self, :AND, true)
114
+ end
115
+
116
+ # Return a Sequel::SQL::BooleanExpression created from this hash, matching any of the
117
+ # conditions.
118
+ def sql_or
119
+ ::Sequel::SQL::BooleanExpression.from_value_pairs(self, :OR)
120
+ end
121
+ end
122
+
123
+ class String
124
+ include Sequel::SQL::AliasMethods
125
+ include Sequel::SQL::CastMethods
126
+
127
+ # Converts a string into a Sequel::LiteralString, in order to override string
128
+ # literalization, e.g.:
129
+ #
130
+ # DB[:items].filter(:abc => 'def').sql #=>
131
+ # "SELECT * FROM items WHERE (abc = 'def')"
132
+ #
133
+ # DB[:items].filter(:abc => 'def'.lit).sql #=>
134
+ # "SELECT * FROM items WHERE (abc = def)"
135
+ #
136
+ # You can also provide arguments, to create a Sequel::SQL::PlaceholderLiteralString:
137
+ #
138
+ # DB[:items].select{|o| o.count('DISTINCT ?'.lit(:a))}.sql #=>
139
+ # "SELECT count(DISTINCT a) FROM items"
140
+ def lit(*args)
141
+ args.empty? ? Sequel::LiteralString.new(self) : Sequel::SQL::PlaceholderLiteralString.new(self, args)
142
+ end
143
+
144
+ # Returns a Blob that holds the same data as this string. Blobs provide proper
145
+ # escaping of binary data.
146
+ def to_sequel_blob
147
+ ::Sequel::SQL::Blob.new(self)
148
+ end
149
+ end
150
+
151
+ class Symbol
152
+ include Sequel::SQL::QualifyingMethods
153
+ include Sequel::SQL::IdentifierMethods
154
+ include Sequel::SQL::AliasMethods
155
+ include Sequel::SQL::CastMethods
156
+ include Sequel::SQL::OrderMethods
157
+ include Sequel::SQL::BooleanMethods
158
+ include Sequel::SQL::NumericMethods
159
+ include Sequel::SQL::StringMethods
160
+ include Sequel::SQL::ComplexExpressionMethods
161
+ include Sequel::SQL::InequalityMethods if RUBY_VERSION < '1.9.0'
162
+
163
+ # If no argument is given, returns a Sequel::SQL::ColumnAll object specifying all
164
+ # columns for this table.
165
+ # If an argument is given, returns a Sequel::SQL::NumericExpression using the *
166
+ # (multiplication) operator with this and the given argument.
167
+ def *(ce=(arg=false;nil))
168
+ return super(ce) unless arg == false
169
+ Sequel::SQL::ColumnAll.new(self);
170
+ end
171
+
172
+ # Returns a Sequel::SQL::Function with this as the function name,
173
+ # and the given arguments. This is aliased as Symbol#[] if ruby 1.9
174
+ # is not being used. ruby 1.9 includes Symbol#[], and Sequel
175
+ # doesn't override methods defined by ruby itself.
176
+ def sql_function(*args)
177
+ Sequel::SQL::Function.new(self, *args)
178
+ end
179
+ alias_method(:[], :sql_function) if RUBY_VERSION < '1.9.0'
180
+
181
+ # Return an SQL array subscript with the given arguments.
182
+ def sql_subscript(*sub)
183
+ Sequel::SQL::Subscript.new(self, sub.flatten)
184
+ end
185
+ end
@@ -0,0 +1,687 @@
1
+ module Sequel
2
+ # Array of all databases to which Sequel has connected. If you are
3
+ # developing an application that can connect to an arbitrary number of
4
+ # databases, delete the database objects from this or they will not get
5
+ # garbage collected.
6
+ DATABASES = []
7
+
8
+ # A Database object represents a virtual connection to a database.
9
+ # The Database class is meant to be subclassed by database adapters in order
10
+ # to provide the functionality needed for executing queries.
11
+ class Database
12
+ extend Metaprogramming
13
+ include Metaprogramming
14
+
15
+ # Array of supported database adapters
16
+ ADAPTERS = %w'ado db2 dbi do firebird informix jdbc mysql odbc openbase oracle postgres sqlite'.collect{|x| x.to_sym}
17
+
18
+ SQL_BEGIN = 'BEGIN'.freeze
19
+ SQL_COMMIT = 'COMMIT'.freeze
20
+ SQL_ROLLBACK = 'ROLLBACK'.freeze
21
+
22
+ # Hash of adapters that have been used
23
+ @@adapters = Hash.new
24
+
25
+ # The identifier input method to use by default
26
+ @@identifier_input_method = nil
27
+
28
+ # The identifier output method to use by default
29
+ @@identifier_output_method = nil
30
+
31
+ # Whether to use the single threaded connection pool by default
32
+ @@single_threaded = false
33
+
34
+ # Whether to quote identifiers (columns and tables) by default
35
+ @@quote_identifiers = nil
36
+
37
+ # The default schema to use
38
+ attr_accessor :default_schema
39
+
40
+ # Array of SQL loggers to use for this database
41
+ attr_accessor :loggers
42
+
43
+ # The options for this database
44
+ attr_reader :opts
45
+
46
+ # The connection pool for this database
47
+ attr_reader :pool
48
+
49
+ # The prepared statement objects for this database, keyed by name
50
+ attr_reader :prepared_statements
51
+
52
+ # Constructs a new instance of a database connection with the specified
53
+ # options hash.
54
+ #
55
+ # Sequel::Database is an abstract class that is not useful by itself.
56
+ #
57
+ # Takes the following options:
58
+ # * :default_schema : The default schema to use, should generally be nil
59
+ # * :disconnection_proc: A proc used to disconnect the connection.
60
+ # * :identifier_input_method: A string method symbol to call on identifiers going into the database
61
+ # * :identifier_output_method: A string method symbol to call on identifiers coming from the database
62
+ # * :loggers : An array of loggers to use.
63
+ # * :quote_identifiers : Whether to quote identifiers
64
+ # * :single_threaded : Whether to use a single-threaded connection pool
65
+ #
66
+ # All options given are also passed to the ConnectionPool. If a block
67
+ # is given, it is used as the connection_proc for the ConnectionPool.
68
+ def initialize(opts = {}, &block)
69
+ @opts ||= opts
70
+
71
+ @single_threaded = opts.include?(:single_threaded) ? opts[:single_threaded] : @@single_threaded
72
+ @schemas = nil
73
+ @default_schema = opts.include?(:default_schema) ? opts[:default_schema] : default_schema_default
74
+ @prepared_statements = {}
75
+ @transactions = []
76
+ @identifier_input_method = nil
77
+ @identifier_output_method = nil
78
+ @quote_identifiers = nil
79
+ if opts.include?(:upcase_identifiers)
80
+ Deprecation.deprecate('The :upcase_identifiers Database option', 'Use the :identifier_input_method => :upcase option instead')
81
+ @identifier_input_method = opts[:upcase_identifiers] ? :upcase : ""
82
+ end
83
+ @pool = (@single_threaded ? SingleThreadedPool : ConnectionPool).new(connection_pool_default_options.merge(opts), &block)
84
+ @pool.connection_proc = proc{|server| connect(server)} unless block
85
+ @pool.disconnection_proc = proc{|conn| disconnect_connection(conn)} unless opts[:disconnection_proc]
86
+
87
+ @loggers = Array(opts[:logger]) + Array(opts[:loggers])
88
+ ::Sequel::DATABASES.push(self)
89
+ end
90
+
91
+ ### Class Methods ###
92
+
93
+ # The Database subclass for the given adapter scheme.
94
+ # Raises Sequel::Error::AdapterNotFound if the adapter
95
+ # could not be loaded.
96
+ def self.adapter_class(scheme)
97
+ scheme = scheme.to_s.gsub('-', '_').to_sym
98
+
99
+ if (klass = @@adapters[scheme]).nil?
100
+ # attempt to load the adapter file
101
+ begin
102
+ Sequel.require "adapters/#{scheme}"
103
+ rescue LoadError => e
104
+ raise Error::AdapterNotFound, "Could not load #{scheme} adapter:\n #{e.message}"
105
+ end
106
+
107
+ # make sure we actually loaded the adapter
108
+ if (klass = @@adapters[scheme]).nil?
109
+ raise Error::AdapterNotFound, "Could not load #{scheme} adapter"
110
+ end
111
+ end
112
+ return klass
113
+ end
114
+
115
+ # Returns the scheme for the Database class.
116
+ def self.adapter_scheme
117
+ @scheme
118
+ end
119
+
120
+ # Connects to a database. See Sequel.connect.
121
+ def self.connect(conn_string, opts = {}, &block)
122
+ if conn_string.is_a?(String)
123
+ if conn_string =~ /\Ajdbc:/
124
+ c = adapter_class(:jdbc)
125
+ opts = {:uri=>conn_string}.merge(opts)
126
+ elsif conn_string =~ /\Ado:/
127
+ c = adapter_class(:do)
128
+ opts = {:uri=>conn_string}.merge(opts)
129
+ else
130
+ uri = URI.parse(conn_string)
131
+ scheme = uri.scheme
132
+ scheme = :dbi if scheme =~ /\Adbi-/
133
+ c = adapter_class(scheme)
134
+ uri_options = {}
135
+ uri.query.split('&').collect{|s| s.split('=')}.each{|k,v| uri_options[k.to_sym] = v} unless uri.query.to_s.strip.empty?
136
+ opts = c.send(:uri_to_options, uri).merge(uri_options).merge(opts)
137
+ end
138
+ else
139
+ opts = conn_string.merge(opts)
140
+ c = adapter_class(opts[:adapter] || opts['adapter'])
141
+ end
142
+ # process opts a bit
143
+ opts = opts.inject({}) do |m, kv| k, v = *kv
144
+ k = :user if k.to_s == 'username'
145
+ m[k.to_sym] = v
146
+ m
147
+ end
148
+ if block
149
+ begin
150
+ yield(db = c.new(opts))
151
+ ensure
152
+ db.disconnect if db
153
+ ::Sequel::DATABASES.delete(db)
154
+ end
155
+ nil
156
+ else
157
+ c.new(opts)
158
+ end
159
+ end
160
+
161
+ # The method to call on identifiers going into the database
162
+ def self.identifier_input_method
163
+ @@identifier_input_method
164
+ end
165
+
166
+ # Set the method to call on identifiers going into the database
167
+ def self.identifier_input_method=(v)
168
+ @@identifier_input_method = v || ""
169
+ end
170
+
171
+ # The method to call on identifiers coming from the database
172
+ def self.identifier_output_method
173
+ @@identifier_output_method
174
+ end
175
+
176
+ # Set the method to call on identifiers coming from the database
177
+ def self.identifier_output_method=(v)
178
+ @@identifier_output_method = v || ""
179
+ end
180
+
181
+ # Sets the default quote_identifiers mode for new databases.
182
+ # See Sequel.quote_identifiers=.
183
+ def self.quote_identifiers=(value)
184
+ @@quote_identifiers = value
185
+ end
186
+
187
+ # Sets the default single_threaded mode for new databases.
188
+ # See Sequel.single_threaded=.
189
+ def self.single_threaded=(value)
190
+ @@single_threaded = value
191
+ end
192
+
193
+ ### Private Class Methods ###
194
+
195
+ # Sets the adapter scheme for the Database class. Call this method in
196
+ # descendnants of Database to allow connection using a URL. For example the
197
+ # following:
198
+ #
199
+ # class Sequel::MyDB::Database < Sequel::Database
200
+ # set_adapter_scheme :mydb
201
+ # ...
202
+ # end
203
+ #
204
+ # would allow connection using:
205
+ #
206
+ # Sequel.connect('mydb://user:password@dbserver/mydb')
207
+ def self.set_adapter_scheme(scheme) # :nodoc:
208
+ @scheme = scheme
209
+ @@adapters[scheme.to_sym] = self
210
+ end
211
+
212
+ # Converts a uri to an options hash. These options are then passed
213
+ # to a newly created database object.
214
+ def self.uri_to_options(uri) # :nodoc:
215
+ { :user => uri.user,
216
+ :password => uri.password,
217
+ :host => uri.host,
218
+ :port => uri.port,
219
+ :database => (m = /\/(.*)/.match(uri.path)) && (m[1]) }
220
+ end
221
+
222
+ private_class_method :set_adapter_scheme, :uri_to_options
223
+
224
+ ### Instance Methods ###
225
+
226
+ # Executes the supplied SQL statement string.
227
+ def <<(sql)
228
+ Deprecation.deprecate('Passing an array argument to Database#<<', 'Use array.each{|x| database << x}') if Array === sql
229
+ execute_ddl((Array === sql) ? sql.to_sql : sql)
230
+ end
231
+
232
+ # Returns a dataset from the database. If the first argument is a string,
233
+ # the method acts as an alias for Database#fetch, returning a dataset for
234
+ # arbitrary SQL:
235
+ #
236
+ # DB['SELECT * FROM items WHERE name = ?', my_name].print
237
+ #
238
+ # Otherwise, acts as an alias for Database#from, setting the primary
239
+ # table for the dataset:
240
+ #
241
+ # DB[:items].sql #=> "SELECT * FROM items"
242
+ def [](*args, &block)
243
+ (String === args.first) ? fetch(*args, &block) : from(*args, &block)
244
+ end
245
+
246
+ # Call the prepared statement with the given name with the given hash
247
+ # of arguments.
248
+ def call(ps_name, hash={})
249
+ prepared_statements[ps_name].call(hash)
250
+ end
251
+
252
+ # Connects to the database. This method should be overridden by descendants.
253
+ def connect
254
+ raise NotImplementedError, "#connect should be overridden by adapters"
255
+ end
256
+
257
+ # Returns a blank dataset
258
+ def dataset
259
+ ds = Sequel::Dataset.new(self)
260
+ end
261
+
262
+ # Disconnects all available connections from the connection pool. If any
263
+ # connections are currently in use, they will not be disconnected.
264
+ def disconnect
265
+ pool.disconnect
266
+ end
267
+
268
+ # Executes the given SQL. This method should be overridden in descendants.
269
+ def execute(sql, opts={})
270
+ raise NotImplementedError, "#execute should be overridden by adapters"
271
+ end
272
+
273
+ # Method that should be used when submitting any DDL (Data Definition
274
+ # Language) SQL. By default, calls execute_dui.
275
+ def execute_ddl(sql, opts={}, &block)
276
+ execute_dui(sql, opts, &block)
277
+ end
278
+
279
+ # Method that should be used when issuing a DELETE, UPDATE, or INSERT
280
+ # statement. By default, calls execute.
281
+ def execute_dui(sql, opts={}, &block)
282
+ execute(sql, opts, &block)
283
+ end
284
+
285
+ # Method that should be used when issuing a INSERT
286
+ # statement. By default, calls execute_dui.
287
+ def execute_insert(sql, opts={}, &block)
288
+ execute_dui(sql, opts, &block)
289
+ end
290
+
291
+ # Fetches records for an arbitrary SQL statement. If a block is given,
292
+ # it is used to iterate over the records:
293
+ #
294
+ # DB.fetch('SELECT * FROM items'){|r| p r}
295
+ #
296
+ # The method returns a dataset instance:
297
+ #
298
+ # DB.fetch('SELECT * FROM items').print
299
+ #
300
+ # Fetch can also perform parameterized queries for protection against SQL
301
+ # injection:
302
+ #
303
+ # DB.fetch('SELECT * FROM items WHERE name = ?', my_name).print
304
+ def fetch(sql, *args, &block)
305
+ ds = dataset.with_sql(sql, *args)
306
+ ds.each(&block) if block
307
+ ds
308
+ end
309
+
310
+ # Returns a new dataset with the from method invoked. If a block is given,
311
+ # it is used as a filter on the dataset.
312
+ def from(*args, &block)
313
+ ds = dataset.from(*args)
314
+ block ? ds.filter(&block) : ds
315
+ end
316
+
317
+ # Returns a single value from the database, e.g.:
318
+ #
319
+ # # SELECT 1
320
+ # DB.get(1) #=> 1
321
+ #
322
+ # # SELECT version()
323
+ # DB.get(:version.sql_function) #=> ...
324
+ def get(*args, &block)
325
+ dataset.get(*args, &block)
326
+ end
327
+
328
+ # The method to call on identifiers going into the database
329
+ def identifier_input_method
330
+ case @identifier_input_method
331
+ when nil
332
+ @identifier_input_method = @opts.include?(:identifier_input_method) ? @opts[:identifier_input_method] : (@@identifier_input_method.nil? ? identifier_input_method_default : @@identifier_input_method)
333
+ @identifier_input_method == "" ? nil : @identifier_input_method
334
+ when ""
335
+ nil
336
+ else
337
+ @identifier_input_method
338
+ end
339
+ end
340
+
341
+ # Set the method to call on identifiers going into the database
342
+ def identifier_input_method=(v)
343
+ reset_schema_utility_dataset
344
+ @identifier_input_method = v || ""
345
+ end
346
+
347
+ # The method to call on identifiers coming from the database
348
+ def identifier_output_method
349
+ case @identifier_output_method
350
+ when nil
351
+ @identifier_output_method = @opts.include?(:identifier_output_method) ? @opts[:identifier_output_method] : (@@identifier_output_method.nil? ? identifier_output_method_default : @@identifier_output_method)
352
+ @identifier_output_method == "" ? nil : @identifier_output_method
353
+ when ""
354
+ nil
355
+ else
356
+ @identifier_output_method
357
+ end
358
+ end
359
+
360
+ # Set the method to call on identifiers coming from the database
361
+ def identifier_output_method=(v)
362
+ reset_schema_utility_dataset
363
+ @identifier_output_method = v || ""
364
+ end
365
+
366
+ # Returns a string representation of the database object including the
367
+ # class name and the connection URI (or the opts if the URI
368
+ # cannot be constructed).
369
+ def inspect
370
+ "#<#{self.class}: #{(uri rescue opts).inspect}>"
371
+ end
372
+
373
+ # Log a message at level info to all loggers. All SQL logging
374
+ # goes through this method.
375
+ def log_info(message, args=nil)
376
+ message = "#{message}; #{args.inspect}" if args
377
+ @loggers.each{|logger| logger.info(message)}
378
+ end
379
+
380
+ # Return the first logger or nil if no loggers are being used.
381
+ # Should only be used for backwards compatibility.
382
+ def logger
383
+ @loggers.first
384
+ end
385
+
386
+ # Replace the array of loggers with the given logger(s).
387
+ def logger=(logger)
388
+ @loggers = Array(logger)
389
+ end
390
+
391
+ # Returns true unless the database is using a single-threaded connection pool.
392
+ def multi_threaded?
393
+ !@single_threaded
394
+ end
395
+
396
+ # Whether to quote identifiers (columns and tables) for this database
397
+ def quote_identifiers=(v)
398
+ reset_schema_utility_dataset
399
+ @quote_identifiers = v
400
+ end
401
+
402
+ # Returns true if the database quotes identifiers.
403
+ def quote_identifiers?
404
+ return @quote_identifiers unless @quote_identifiers.nil?
405
+ @quote_identifiers = @opts.include?(:quote_identifiers) ? @opts[:quote_identifiers] : (@@quote_identifiers.nil? ? quote_identifiers_default : @@quote_identifiers)
406
+ end
407
+
408
+ # Returns a new dataset with the select method invoked.
409
+ def select(*args, &block)
410
+ dataset.select(*args, &block)
411
+ end
412
+
413
+ # Default serial primary key options.
414
+ def serial_primary_key_options
415
+ {:primary_key => true, :type => Integer, :auto_increment => true}
416
+ end
417
+
418
+ # Returns true if the database is using a single-threaded connection pool.
419
+ def single_threaded?
420
+ @single_threaded
421
+ end
422
+
423
+ # Acquires a database connection, yielding it to the passed block.
424
+ def synchronize(server=nil, &block)
425
+ @pool.hold(server || :default, &block)
426
+ end
427
+
428
+ # Returns true if a table with the given name exists. This requires a query
429
+ # to the database unless this database object already has the schema for
430
+ # the given table name.
431
+ def table_exists?(name)
432
+ if @schemas && @schemas[name]
433
+ true
434
+ else
435
+ begin
436
+ from(name).first
437
+ true
438
+ rescue
439
+ false
440
+ end
441
+ end
442
+ end
443
+
444
+ # Attempts to acquire a database connection. Returns true if successful.
445
+ # Will probably raise an error if unsuccessful.
446
+ def test_connection(server=nil)
447
+ synchronize(server){|conn|}
448
+ true
449
+ end
450
+
451
+ # A simple implementation of SQL transactions. Nested transactions are not
452
+ # supported - calling #transaction within a transaction will reuse the
453
+ # current transaction. Should be overridden for databases that support nested
454
+ # transactions.
455
+ def transaction(opts={})
456
+ unless opts.is_a?(Hash)
457
+ Deprecation.deprecate('Passing an argument other than a Hash to Database#transaction', "Use DB.transaction(:server=>#{opts.inspect})")
458
+ opts = {:server=>opts}
459
+ end
460
+ synchronize(opts[:server]) do |conn|
461
+ return yield(conn) if @transactions.include?(Thread.current)
462
+ log_info(begin_transaction_sql)
463
+ conn.execute(begin_transaction_sql)
464
+ begin
465
+ @transactions << Thread.current
466
+ yield(conn)
467
+ rescue Exception => e
468
+ log_info(rollback_transaction_sql)
469
+ conn.execute(rollback_transaction_sql)
470
+ transaction_error(e)
471
+ ensure
472
+ unless e
473
+ log_info(commit_transaction_sql)
474
+ conn.execute(commit_transaction_sql)
475
+ end
476
+ @transactions.delete(Thread.current)
477
+ end
478
+ end
479
+ end
480
+
481
+ # Typecast the value to the given column_type. Can be overridden in
482
+ # adapters to support database specific column types.
483
+ # This method should raise Sequel::Error::InvalidValue if assigned value
484
+ # is invalid.
485
+ def typecast_value(column_type, value)
486
+ return nil if value.nil?
487
+ begin
488
+ case column_type
489
+ when :integer
490
+ Integer(value)
491
+ when :string
492
+ value.to_s
493
+ when :float
494
+ Float(value)
495
+ when :decimal
496
+ case value
497
+ when BigDecimal
498
+ value
499
+ when String, Float
500
+ value.to_d
501
+ when Integer
502
+ value.to_s.to_d
503
+ else
504
+ raise Sequel::Error::InvalidValue, "invalid value for BigDecimal: #{value.inspect}"
505
+ end
506
+ when :boolean
507
+ case value
508
+ when false, 0, "0", /\Af(alse)?\z/i
509
+ false
510
+ else
511
+ blank_object?(value) ? nil : true
512
+ end
513
+ when :date
514
+ case value
515
+ when Date
516
+ value
517
+ when DateTime, Time
518
+ Date.new(value.year, value.month, value.day)
519
+ when String
520
+ Sequel.string_to_date(value)
521
+ else
522
+ raise Sequel::Error::InvalidValue, "invalid value for Date: #{value.inspect}"
523
+ end
524
+ when :time
525
+ case value
526
+ when Time
527
+ value
528
+ when String
529
+ Sequel.string_to_time(value)
530
+ else
531
+ raise Sequel::Error::InvalidValue, "invalid value for Time: #{value.inspect}"
532
+ end
533
+ when :datetime
534
+ raise(Sequel::Error::InvalidValue, "invalid value for Datetime: #{value.inspect}") unless [DateTime, Date, Time, String].any?{|c| value.is_a?(c)}
535
+ if Sequel.datetime_class === value
536
+ # Already the correct class, no need to convert
537
+ value
538
+ else
539
+ # First convert it to standard ISO 8601 time, then
540
+ # parse that string using the time class.
541
+ Sequel.string_to_datetime(Time === value ? value.iso8601 : value.to_s)
542
+ end
543
+ when :blob
544
+ ::Sequel::SQL::Blob.new(value)
545
+ else
546
+ value
547
+ end
548
+ rescue ArgumentError => exp
549
+ e = Sequel::Error::InvalidValue.new("#{exp.class} #{exp.message}")
550
+ e.set_backtrace(exp.backtrace)
551
+ raise e
552
+ end
553
+ end
554
+
555
+ # Returns the URI identifying the database.
556
+ # This method can raise an error if the database used options
557
+ # instead of a connection string.
558
+ def uri
559
+ uri = URI::Generic.new(
560
+ self.class.adapter_scheme.to_s,
561
+ nil,
562
+ @opts[:host],
563
+ @opts[:port],
564
+ nil,
565
+ "/#{@opts[:database]}",
566
+ nil,
567
+ nil,
568
+ nil
569
+ )
570
+ uri.user = @opts[:user]
571
+ uri.password = @opts[:password] if uri.user
572
+ uri.to_s
573
+ end
574
+
575
+ # Explicit alias of uri for easier subclassing.
576
+ def url
577
+ uri
578
+ end
579
+
580
+ private
581
+
582
+ # SQL to BEGIN a transaction.
583
+ def begin_transaction_sql
584
+ SQL_BEGIN
585
+ end
586
+
587
+ # Returns true when the object is considered blank.
588
+ # The only objects that are blank are nil, false,
589
+ # strings with all whitespace, and ones that respond
590
+ # true to empty?
591
+ def blank_object?(obj)
592
+ case obj
593
+ when NilClass, FalseClass
594
+ true
595
+ when Numeric, TrueClass
596
+ false
597
+ when String
598
+ obj.strip.empty?
599
+ else
600
+ !obj.respond_to?(:empty?) || obj.empty?
601
+ end
602
+ end
603
+
604
+ # SQL to COMMIT a transaction.
605
+ def commit_transaction_sql
606
+ SQL_COMMIT
607
+ end
608
+
609
+ # The default options for the connection pool.
610
+ def connection_pool_default_options
611
+ {}
612
+ end
613
+
614
+ # The default value for default_schema.
615
+ def default_schema_default
616
+ nil
617
+ end
618
+
619
+ # The method to apply to identifiers going into the database by default.
620
+ # Should be overridden in subclasses for databases that fold unquoted
621
+ # identifiers to lower case instead of uppercase, such as
622
+ # MySQL, PostgreSQL, and SQLite.
623
+ def identifier_input_method_default
624
+ :upcase
625
+ end
626
+
627
+ # The method to apply to identifiers coming the database by default.
628
+ # Should be overridden in subclasses for databases that fold unquoted
629
+ # identifiers to lower case instead of uppercase, such as
630
+ # MySQL, PostgreSQL, and SQLite.
631
+ def identifier_output_method_default
632
+ :downcase
633
+ end
634
+
635
+ def quote_identifiers_default
636
+ true
637
+ end
638
+
639
+ # SQL to ROLLBACK a transaction.
640
+ def rollback_transaction_sql
641
+ SQL_ROLLBACK
642
+ end
643
+
644
+ # Convert the given exception to a DatabaseError, keeping message
645
+ # and traceback.
646
+ def raise_error(exception, opts={})
647
+ if !opts[:classes] || Array(opts[:classes]).any?{|c| exception.is_a?(c)}
648
+ e = DatabaseError.new("#{exception.class} #{exception.message}")
649
+ e.set_backtrace(exception.backtrace)
650
+ raise e
651
+ else
652
+ raise exception
653
+ end
654
+ end
655
+
656
+ # Split the schema information from the table
657
+ def schema_and_table(table_name)
658
+ schema_utility_dataset.schema_and_table(table_name)
659
+ end
660
+
661
+ # Return the options for the given server by merging the generic
662
+ # options for all server with the specific options for the given
663
+ # server specified in the :servers option.
664
+ def server_opts(server)
665
+ opts = if @opts[:servers] && server_options = @opts[:servers][server]
666
+ case server_options
667
+ when Hash
668
+ @opts.merge(server_options)
669
+ when Proc
670
+ @opts.merge(server_options.call(self))
671
+ else
672
+ raise Error, 'Server opts should be a hash or proc'
673
+ end
674
+ else
675
+ @opts.dup
676
+ end
677
+ opts.delete(:servers)
678
+ opts
679
+ end
680
+
681
+ # Raise a database error unless the exception is an Error::Rollback.
682
+ def transaction_error(e, *classes)
683
+ raise_error(e, :classes=>classes) unless Error::Rollback === e
684
+ end
685
+ end
686
+ end
687
+