PageRenderTime 26ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb

https://github.com/Tho85/rails
Ruby | 394 lines | 201 code | 52 blank | 141 comment | 14 complexity | 7d0fccc9ce476ba89a0b893ca849d4cb MD5 | raw file
  1. module ActiveRecord
  2. module ConnectionAdapters # :nodoc:
  3. module DatabaseStatements
  4. def initialize
  5. super
  6. reset_transaction
  7. end
  8. # Converts an arel AST to SQL
  9. def to_sql(arel, binds = [])
  10. if arel.respond_to?(:ast)
  11. binds = binds.dup
  12. visitor.accept(arel.ast) do
  13. quote(*binds.shift.reverse)
  14. end
  15. else
  16. arel
  17. end
  18. end
  19. # Returns an ActiveRecord::Result instance.
  20. def select_all(arel, name = nil, binds = [])
  21. if arel.is_a?(Relation)
  22. relation = arel
  23. arel = relation.arel
  24. if !binds || binds.empty?
  25. binds = relation.bind_values
  26. end
  27. end
  28. select(to_sql(arel, binds), name, binds)
  29. end
  30. # Returns a record hash with the column names as keys and column values
  31. # as values.
  32. def select_one(arel, name = nil, binds = [])
  33. select_all(arel, name, binds).first
  34. end
  35. # Returns a single value from a record
  36. def select_value(arel, name = nil, binds = [])
  37. if result = select_one(arel, name, binds)
  38. result.values.first
  39. end
  40. end
  41. # Returns an array of the values of the first column in a select:
  42. # select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
  43. def select_values(arel, name = nil)
  44. binds = []
  45. if arel.is_a?(Relation)
  46. arel, binds = arel.arel, arel.bind_values
  47. end
  48. select_rows(to_sql(arel, binds), name, binds).map(&:first)
  49. end
  50. # Returns an array of arrays containing the field values.
  51. # Order is the same as that returned by +columns+.
  52. def select_rows(sql, name = nil, binds = [])
  53. end
  54. undef_method :select_rows
  55. # Executes the SQL statement in the context of this connection.
  56. def execute(sql, name = nil)
  57. end
  58. undef_method :execute
  59. # Executes +sql+ statement in the context of this connection using
  60. # +binds+ as the bind substitutes. +name+ is logged along with
  61. # the executed +sql+ statement.
  62. def exec_query(sql, name = 'SQL', binds = [])
  63. end
  64. # Executes insert +sql+ statement in the context of this connection using
  65. # +binds+ as the bind substitutes. +name+ is logged along with
  66. # the executed +sql+ statement.
  67. def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
  68. exec_query(sql, name, binds)
  69. end
  70. # Executes delete +sql+ statement in the context of this connection using
  71. # +binds+ as the bind substitutes. +name+ is logged along with
  72. # the executed +sql+ statement.
  73. def exec_delete(sql, name, binds)
  74. exec_query(sql, name, binds)
  75. end
  76. # Executes update +sql+ statement in the context of this connection using
  77. # +binds+ as the bind substitutes. +name+ is logged along with
  78. # the executed +sql+ statement.
  79. def exec_update(sql, name, binds)
  80. exec_query(sql, name, binds)
  81. end
  82. # Returns the last auto-generated ID from the affected table.
  83. #
  84. # +id_value+ will be returned unless the value is nil, in
  85. # which case the database will attempt to calculate the last inserted
  86. # id and return that value.
  87. #
  88. # If the next id was calculated in advance (as in Oracle), it should be
  89. # passed in as +id_value+.
  90. def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
  91. sql, binds = sql_for_insert(to_sql(arel, binds), pk, id_value, sequence_name, binds)
  92. value = exec_insert(sql, name, binds, pk, sequence_name)
  93. id_value || last_inserted_id(value)
  94. end
  95. # Executes the update statement and returns the number of rows affected.
  96. def update(arel, name = nil, binds = [])
  97. exec_update(to_sql(arel, binds), name, binds)
  98. end
  99. # Executes the delete statement and returns the number of rows affected.
  100. def delete(arel, name = nil, binds = [])
  101. exec_delete(to_sql(arel, binds), name, binds)
  102. end
  103. # Returns +true+ when the connection adapter supports prepared statement
  104. # caching, otherwise returns +false+
  105. def supports_statement_cache?
  106. false
  107. end
  108. # Runs the given block in a database transaction, and returns the result
  109. # of the block.
  110. #
  111. # == Nested transactions support
  112. #
  113. # Most databases don't support true nested transactions. At the time of
  114. # writing, the only database that supports true nested transactions that
  115. # we're aware of, is MS-SQL.
  116. #
  117. # In order to get around this problem, #transaction will emulate the effect
  118. # of nested transactions, by using savepoints:
  119. # http://dev.mysql.com/doc/refman/5.0/en/savepoint.html
  120. # Savepoints are supported by MySQL and PostgreSQL. SQLite3 version >= '3.6.8'
  121. # supports savepoints.
  122. #
  123. # It is safe to call this method if a database transaction is already open,
  124. # i.e. if #transaction is called within another #transaction block. In case
  125. # of a nested call, #transaction will behave as follows:
  126. #
  127. # - The block will be run without doing anything. All database statements
  128. # that happen within the block are effectively appended to the already
  129. # open database transaction.
  130. # - However, if +:requires_new+ is set, the block will be wrapped in a
  131. # database savepoint acting as a sub-transaction.
  132. #
  133. # === Caveats
  134. #
  135. # MySQL doesn't support DDL transactions. If you perform a DDL operation,
  136. # then any created savepoints will be automatically released. For example,
  137. # if you've created a savepoint, then you execute a CREATE TABLE statement,
  138. # then the savepoint that was created will be automatically released.
  139. #
  140. # This means that, on MySQL, you shouldn't execute DDL operations inside
  141. # a #transaction call that you know might create a savepoint. Otherwise,
  142. # #transaction will raise exceptions when it tries to release the
  143. # already-automatically-released savepoints:
  144. #
  145. # Model.connection.transaction do # BEGIN
  146. # Model.connection.transaction(requires_new: true) do # CREATE SAVEPOINT active_record_1
  147. # Model.connection.create_table(...)
  148. # # active_record_1 now automatically released
  149. # end # RELEASE SAVEPOINT active_record_1 <--- BOOM! database error!
  150. # end
  151. #
  152. # == Transaction isolation
  153. #
  154. # If your database supports setting the isolation level for a transaction, you can set
  155. # it like so:
  156. #
  157. # Post.transaction(isolation: :serializable) do
  158. # # ...
  159. # end
  160. #
  161. # Valid isolation levels are:
  162. #
  163. # * <tt>:read_uncommitted</tt>
  164. # * <tt>:read_committed</tt>
  165. # * <tt>:repeatable_read</tt>
  166. # * <tt>:serializable</tt>
  167. #
  168. # You should consult the documentation for your database to understand the
  169. # semantics of these different levels:
  170. #
  171. # * http://www.postgresql.org/docs/9.1/static/transaction-iso.html
  172. # * https://dev.mysql.com/doc/refman/5.0/en/set-transaction.html
  173. #
  174. # An <tt>ActiveRecord::TransactionIsolationError</tt> will be raised if:
  175. #
  176. # * The adapter does not support setting the isolation level
  177. # * You are joining an existing open transaction
  178. # * You are creating a nested (savepoint) transaction
  179. #
  180. # The mysql, mysql2 and postgresql adapters support setting the transaction
  181. # isolation level. However, support is disabled for mysql versions below 5,
  182. # because they are affected by a bug[http://bugs.mysql.com/bug.php?id=39170]
  183. # which means the isolation level gets persisted outside the transaction.
  184. def transaction(options = {})
  185. options.assert_valid_keys :requires_new, :joinable, :isolation
  186. if !options[:requires_new] && current_transaction.joinable?
  187. if options[:isolation]
  188. raise ActiveRecord::TransactionIsolationError, "cannot set isolation when joining a transaction"
  189. end
  190. yield
  191. else
  192. within_new_transaction(options) { yield }
  193. end
  194. rescue ActiveRecord::Rollback
  195. # rollbacks are silently swallowed
  196. end
  197. def within_new_transaction(options = {}) #:nodoc:
  198. transaction = begin_transaction(options)
  199. yield
  200. rescue Exception => error
  201. rollback_transaction if transaction
  202. raise
  203. ensure
  204. begin
  205. commit_transaction unless error
  206. rescue Exception
  207. rollback_transaction
  208. raise
  209. end
  210. end
  211. def current_transaction #:nodoc:
  212. @transaction
  213. end
  214. def transaction_open?
  215. @transaction.open?
  216. end
  217. def begin_transaction(options = {}) #:nodoc:
  218. @transaction = @transaction.begin(options)
  219. end
  220. def commit_transaction #:nodoc:
  221. @transaction = @transaction.commit
  222. end
  223. def rollback_transaction #:nodoc:
  224. @transaction = @transaction.rollback
  225. end
  226. def reset_transaction #:nodoc:
  227. @transaction = ClosedTransaction.new(self)
  228. end
  229. # Register a record with the current transaction so that its after_commit and after_rollback callbacks
  230. # can be called.
  231. def add_transaction_record(record)
  232. @transaction.add_record(record)
  233. end
  234. # Begins the transaction (and turns off auto-committing).
  235. def begin_db_transaction() end
  236. def transaction_isolation_levels
  237. {
  238. read_uncommitted: "READ UNCOMMITTED",
  239. read_committed: "READ COMMITTED",
  240. repeatable_read: "REPEATABLE READ",
  241. serializable: "SERIALIZABLE"
  242. }
  243. end
  244. # Begins the transaction with the isolation level set. Raises an error by
  245. # default; adapters that support setting the isolation level should implement
  246. # this method.
  247. def begin_isolated_db_transaction(isolation)
  248. raise ActiveRecord::TransactionIsolationError, "adapter does not support setting transaction isolation"
  249. end
  250. # Commits the transaction (and turns on auto-committing).
  251. def commit_db_transaction() end
  252. # Rolls back the transaction (and turns on auto-committing). Must be
  253. # done if the transaction block raises an exception or returns false.
  254. def rollback_db_transaction() end
  255. def default_sequence_name(table, column)
  256. nil
  257. end
  258. # Set the sequence to the max value of the table's column.
  259. def reset_sequence!(table, column, sequence = nil)
  260. # Do nothing by default. Implement for PostgreSQL, Oracle, ...
  261. end
  262. # Inserts the given fixture into the table. Overridden in adapters that require
  263. # something beyond a simple insert (eg. Oracle).
  264. def insert_fixture(fixture, table_name)
  265. columns = schema_cache.columns_hash(table_name)
  266. key_list = []
  267. value_list = fixture.map do |name, value|
  268. key_list << quote_column_name(name)
  269. quote(value, columns[name])
  270. end
  271. execute "INSERT INTO #{quote_table_name(table_name)} (#{key_list.join(', ')}) VALUES (#{value_list.join(', ')})", 'Fixture Insert'
  272. end
  273. def empty_insert_statement_value
  274. "DEFAULT VALUES"
  275. end
  276. def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
  277. "WHERE #{quoted_primary_key} IN (SELECT #{quoted_primary_key} FROM #{quoted_table_name} #{where_sql})"
  278. end
  279. # Sanitizes the given LIMIT parameter in order to prevent SQL injection.
  280. #
  281. # The +limit+ may be anything that can evaluate to a string via #to_s. It
  282. # should look like an integer, or a comma-delimited list of integers, or
  283. # an Arel SQL literal.
  284. #
  285. # Returns Integer and Arel::Nodes::SqlLiteral limits as is.
  286. # Returns the sanitized limit parameter, either as an integer, or as a
  287. # string which contains a comma-delimited list of integers.
  288. def sanitize_limit(limit)
  289. if limit.is_a?(Integer) || limit.is_a?(Arel::Nodes::SqlLiteral)
  290. limit
  291. elsif limit.to_s =~ /,/
  292. Arel.sql limit.to_s.split(',').map{ |i| Integer(i) }.join(',')
  293. else
  294. Integer(limit)
  295. end
  296. end
  297. # The default strategy for an UPDATE with joins is to use a subquery. This doesn't work
  298. # on mysql (even when aliasing the tables), but mysql allows using JOIN directly in
  299. # an UPDATE statement, so in the mysql adapters we redefine this to do that.
  300. def join_to_update(update, select) #:nodoc:
  301. key = update.key
  302. subselect = subquery_for(key, select)
  303. update.where key.in(subselect)
  304. end
  305. def join_to_delete(delete, select, key) #:nodoc:
  306. subselect = subquery_for(key, select)
  307. delete.where key.in(subselect)
  308. end
  309. protected
  310. # Returns a subquery for the given key using the join information.
  311. def subquery_for(key, select)
  312. subselect = select.clone
  313. subselect.projections = [key]
  314. subselect
  315. end
  316. # Returns an ActiveRecord::Result instance.
  317. def select(sql, name = nil, binds = [])
  318. end
  319. undef_method :select
  320. # Returns the last auto-generated ID from the affected table.
  321. def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
  322. execute(sql, name)
  323. id_value
  324. end
  325. # Executes the update statement and returns the number of rows affected.
  326. def update_sql(sql, name = nil)
  327. execute(sql, name)
  328. end
  329. # Executes the delete statement and returns the number of rows affected.
  330. def delete_sql(sql, name = nil)
  331. update_sql(sql, name)
  332. end
  333. def sql_for_insert(sql, pk, id_value, sequence_name, binds)
  334. [sql, binds]
  335. end
  336. def last_inserted_id(result)
  337. row = result.rows.first
  338. row && row.first
  339. end
  340. end
  341. end
  342. end