Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/sqlalchemy/future/engine.py: 61%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

51 statements  

1# future/engine.py 

2# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors 

3# <see AUTHORS file> 

4# 

5# This module is part of SQLAlchemy and is released under 

6# the MIT License: https://www.opensource.org/licenses/mit-license.php 

7from .. import util 

8from ..engine import Connection as _LegacyConnection 

9from ..engine import create_engine as _create_engine 

10from ..engine import Engine as _LegacyEngine 

11from ..engine.base import OptionEngineMixin 

12 

13NO_OPTIONS = util.immutabledict() 

14 

15 

16def create_engine(*arg, **kw): 

17 """Create a new :class:`_future.Engine` instance. 

18 

19 Arguments passed to :func:`_future.create_engine` are mostly identical 

20 to those passed to the 1.x :func:`_sa.create_engine` function. 

21 The difference is that the object returned is the :class:`._future.Engine` 

22 which has the 2.0 version of the API. 

23 

24 """ 

25 

26 kw["_future_engine_class"] = Engine 

27 return _create_engine(*arg, **kw) 

28 

29 

30class Connection(_LegacyConnection): 

31 """Provides high-level functionality for a wrapped DB-API connection. 

32 

33 The :class:`_future.Connection` object is procured by calling 

34 the :meth:`_future.Engine.connect` method of the :class:`_future.Engine` 

35 object, and provides services for execution of SQL statements as well 

36 as transaction control. 

37 

38 **This is the SQLAlchemy 2.0 version** of the :class:`_engine.Connection` 

39 class. The API and behavior of this object is largely the same, with the 

40 following differences in behavior: 

41 

42 * The result object returned for results is the 

43 :class:`_engine.CursorResult` 

44 object, which is a subclass of the :class:`_engine.Result`. 

45 This object has a slightly different API and behavior than the 

46 :class:`_engine.LegacyCursorResult` returned for 1.x style usage. 

47 

48 * The object has :meth:`_future.Connection.commit` and 

49 :meth:`_future.Connection.rollback` methods which commit or roll back 

50 the current transaction in progress, if any. 

51 

52 * The object features "autobegin" behavior, such that any call to 

53 :meth:`_future.Connection.execute` will 

54 unconditionally start a 

55 transaction which can be controlled using the above mentioned 

56 :meth:`_future.Connection.commit` and 

57 :meth:`_future.Connection.rollback` methods. 

58 

59 * The object does not have any "autocommit" functionality. Any SQL 

60 statement or DDL statement will not be followed by any COMMIT until 

61 the transaction is explicitly committed, either via the 

62 :meth:`_future.Connection.commit` method, or if the connection is 

63 being used in a context manager that commits such as the one 

64 returned by :meth:`_future.Engine.begin`. 

65 

66 * The SAVEPOINT method :meth:`_future.Connection.begin_nested` returns 

67 a :class:`_engine.NestedTransaction` as was always the case, and the 

68 savepoint can be controlled by invoking 

69 :meth:`_engine.NestedTransaction.commit` or 

70 :meth:`_engine.NestedTransaction.rollback` as was the case before. 

71 However, this savepoint "transaction" is not associated with the 

72 transaction that is controlled by the connection itself; the overall 

73 transaction can be committed or rolled back directly which will not emit 

74 any special instructions for the SAVEPOINT (this will typically have the 

75 effect that one desires). 

76 

77 * The :class:`_future.Connection` object does not support "branching", 

78 which was a pattern by which a sub "connection" would be used that 

79 refers to this connection as a parent. 

80 

81 

82 

83 """ 

84 

85 _is_future = True 

86 

87 def _branch(self): 

88 raise NotImplementedError( 

89 "sqlalchemy.future.Connection does not support " 

90 "'branching' of new connections." 

91 ) 

92 

93 def begin(self): 

94 """Begin a transaction prior to autobegin occurring. 

95 

96 The returned object is an instance of :class:`_engine.RootTransaction`. 

97 This object represents the "scope" of the transaction, 

98 which completes when either the :meth:`_engine.Transaction.rollback` 

99 or :meth:`_engine.Transaction.commit` method is called. 

100 

101 The :meth:`_future.Connection.begin` method in SQLAlchemy 2.0 begins a 

102 transaction that normally will be begun in any case when the connection 

103 is first used to execute a statement. The reason this method might be 

104 used would be to invoke the :meth:`_events.ConnectionEvents.begin` 

105 event at a specific time, or to organize code within the scope of a 

106 connection checkout in terms of context managed blocks, such as:: 

107 

108 with engine.connect() as conn: 

109 with conn.begin(): 

110 conn.execute(...) 

111 conn.execute(...) 

112 

113 with conn.begin(): 

114 conn.execute(...) 

115 conn.execute(...) 

116 

117 The above code is not fundamentally any different in its behavior than 

118 the following code which does not use 

119 :meth:`_future.Connection.begin`; the below style is referred towards 

120 as "commit as you go" style:: 

121 

122 with engine.connect() as conn: 

123 conn.execute(...) 

124 conn.execute(...) 

125 conn.commit() 

126 

127 conn.execute(...) 

128 conn.execute(...) 

129 conn.commit() 

130 

131 From a database point of view, the :meth:`_future.Connection.begin` 

132 method does not emit any SQL or change the state of the underlying 

133 DBAPI connection in any way; the Python DBAPI does not have any 

134 concept of explicit transaction begin. 

135 

136 .. seealso:: 

137 

138 :ref:`tutorial_working_with_transactions` - in the 

139 :ref:`unified_tutorial` 

140 

141 :meth:`_future.Connection.begin_nested` - use a SAVEPOINT 

142 

143 :meth:`_engine.Connection.begin_twophase` - 

144 use a two phase /XID transaction 

145 

146 :meth:`_future.Engine.begin` - context manager available from 

147 :class:`_future.Engine` 

148 

149 """ 

150 return super(Connection, self).begin() 

151 

152 def begin_nested(self): 

153 """Begin a nested transaction (i.e. SAVEPOINT) and return a transaction 

154 handle. 

155 

156 The returned object is an instance of 

157 :class:`_engine.NestedTransaction`. 

158 

159 Nested transactions require SAVEPOINT support in the 

160 underlying database. Any transaction in the hierarchy may 

161 ``commit`` and ``rollback``, however the outermost transaction 

162 still controls the overall ``commit`` or ``rollback`` of the 

163 transaction of a whole. 

164 

165 If an outer :class:`.RootTransaction` is not present on this 

166 :class:`_future.Connection`, a new one is created using "autobegin". 

167 This outer transaction may be completed using "commit-as-you-go" style 

168 usage, by calling upon :meth:`_future.Connection.commit` or 

169 :meth:`_future.Connection.rollback`. 

170 

171 .. tip:: 

172 

173 The "autobegin" behavior of :meth:`_future.Connection.begin_nested` 

174 is specific to :term:`2.0 style` use; for legacy behaviors, see 

175 :meth:`_engine.Connection.begin_nested`. 

176 

177 The :class:`_engine.NestedTransaction` remains independent of the 

178 :class:`_future.Connection` object itself. Calling the 

179 :meth:`_future.Connection.commit` or 

180 :meth:`_future.Connection.rollback` will always affect the actual 

181 containing database transaction itself, and not the SAVEPOINT itself. 

182 When a database transaction is committed, any SAVEPOINTs that have been 

183 established are cleared and the data changes within their scope is also 

184 committed. 

185 

186 .. seealso:: 

187 

188 :meth:`_future.Connection.begin` 

189 

190 

191 """ 

192 return super(Connection, self).begin_nested() 

193 

194 def commit(self): 

195 """Commit the transaction that is currently in progress. 

196 

197 This method commits the current transaction if one has been started. 

198 If no transaction was started, the method has no effect, assuming 

199 the connection is in a non-invalidated state. 

200 

201 A transaction is begun on a :class:`_future.Connection` automatically 

202 whenever a statement is first executed, or when the 

203 :meth:`_future.Connection.begin` method is called. 

204 

205 .. note:: The :meth:`_future.Connection.commit` method only acts upon 

206 the primary database transaction that is linked to the 

207 :class:`_future.Connection` object. It does not operate upon a 

208 SAVEPOINT that would have been invoked from the 

209 :meth:`_future.Connection.begin_nested` method; for control of a 

210 SAVEPOINT, call :meth:`_engine.NestedTransaction.commit` on the 

211 :class:`_engine.NestedTransaction` that is returned by the 

212 :meth:`_future.Connection.begin_nested` method itself. 

213 

214 

215 """ 

216 if self._transaction: 

217 self._transaction.commit() 

218 

219 def rollback(self): 

220 """Roll back the transaction that is currently in progress. 

221 

222 This method rolls back the current transaction if one has been started. 

223 If no transaction was started, the method has no effect. If a 

224 transaction was started and the connection is in an invalidated state, 

225 the transaction is cleared using this method. 

226 

227 A transaction is begun on a :class:`_future.Connection` automatically 

228 whenever a statement is first executed, or when the 

229 :meth:`_future.Connection.begin` method is called. 

230 

231 .. note:: The :meth:`_future.Connection.rollback` method only acts 

232 upon the primary database transaction that is linked to the 

233 :class:`_future.Connection` object. It does not operate upon a 

234 SAVEPOINT that would have been invoked from the 

235 :meth:`_future.Connection.begin_nested` method; for control of a 

236 SAVEPOINT, call :meth:`_engine.NestedTransaction.rollback` on the 

237 :class:`_engine.NestedTransaction` that is returned by the 

238 :meth:`_future.Connection.begin_nested` method itself. 

239 

240 

241 """ 

242 if self._transaction: 

243 self._transaction.rollback() 

244 

245 def close(self): 

246 """Close this :class:`_future.Connection`. 

247 

248 This has the effect of also calling :meth:`_future.Connection.rollback` 

249 if any transaction is in place. 

250 

251 """ 

252 super(Connection, self).close() 

253 

254 def execute(self, statement, parameters=None, execution_options=None): 

255 r"""Executes a SQL statement construct and returns a 

256 :class:`_engine.Result`. 

257 

258 :param statement: The statement to be executed. This is always 

259 an object that is in both the :class:`_expression.ClauseElement` and 

260 :class:`_expression.Executable` hierarchies, including: 

261 

262 * :class:`_expression.Select` 

263 * :class:`_expression.Insert`, :class:`_expression.Update`, 

264 :class:`_expression.Delete` 

265 * :class:`_expression.TextClause` and 

266 :class:`_expression.TextualSelect` 

267 * :class:`_schema.DDL` and objects which inherit from 

268 :class:`_schema.DDLElement` 

269 

270 :param parameters: parameters which will be bound into the statement. 

271 This may be either a dictionary of parameter names to values, 

272 or a mutable sequence (e.g. a list) of dictionaries. When a 

273 list of dictionaries is passed, the underlying statement execution 

274 will make use of the DBAPI ``cursor.executemany()`` method. 

275 When a single dictionary is passed, the DBAPI ``cursor.execute()`` 

276 method will be used. 

277 

278 :param execution_options: optional dictionary of execution options, 

279 which will be associated with the statement execution. This 

280 dictionary can provide a subset of the options that are accepted 

281 by :meth:`_future.Connection.execution_options`. 

282 

283 :return: a :class:`_engine.Result` object. 

284 

285 """ 

286 return self._execute_20( 

287 statement, parameters, execution_options or NO_OPTIONS 

288 ) 

289 

290 def scalar(self, statement, parameters=None, execution_options=None): 

291 r"""Executes a SQL statement construct and returns a scalar object. 

292 

293 This method is shorthand for invoking the 

294 :meth:`_engine.Result.scalar` method after invoking the 

295 :meth:`_future.Connection.execute` method. Parameters are equivalent. 

296 

297 :return: a scalar Python value representing the first column of the 

298 first row returned. 

299 

300 """ 

301 return self.execute(statement, parameters, execution_options).scalar() 

302 

303 

304class Engine(_LegacyEngine): 

305 """Connects a :class:`_pool.Pool` and 

306 :class:`_engine.Dialect` together to provide a 

307 source of database connectivity and behavior. 

308 

309 **This is the SQLAlchemy 2.0 version** of the :class:`~.engine.Engine`. 

310 

311 An :class:`.future.Engine` object is instantiated publicly using the 

312 :func:`~sqlalchemy.future.create_engine` function. 

313 

314 .. seealso:: 

315 

316 :doc:`/core/engines` 

317 

318 :ref:`connections_toplevel` 

319 

320 """ 

321 

322 _connection_cls = Connection 

323 _is_future = True 

324 

325 def _not_implemented(self, *arg, **kw): 

326 raise NotImplementedError( 

327 "This method is not implemented for SQLAlchemy 2.0." 

328 ) 

329 

330 transaction = ( 

331 run_callable 

332 ) = ( 

333 execute 

334 ) = ( 

335 scalar 

336 ) = ( 

337 _execute_clauseelement 

338 ) = _execute_compiled = table_names = has_table = _not_implemented 

339 

340 def _run_ddl_visitor(self, visitorcallable, element, **kwargs): 

341 # TODO: this is for create_all support etc. not clear if we 

342 # want to provide this in 2.0, that is, a way to execute SQL where 

343 # they aren't calling "engine.begin()" explicitly, however, DDL 

344 # may be a special case for which we want to continue doing it this 

345 # way. A big win here is that the full DDL sequence is inside of a 

346 # single transaction rather than COMMIT for each statement. 

347 with self.begin() as conn: 

348 conn._run_ddl_visitor(visitorcallable, element, **kwargs) 

349 

350 @classmethod 

351 def _future_facade(self, legacy_engine): 

352 return Engine( 

353 legacy_engine.pool, 

354 legacy_engine.dialect, 

355 legacy_engine.url, 

356 logging_name=legacy_engine.logging_name, 

357 echo=legacy_engine.echo, 

358 hide_parameters=legacy_engine.hide_parameters, 

359 execution_options=legacy_engine._execution_options, 

360 ) 

361 

362 @util.contextmanager 

363 def begin(self): 

364 """Return a :class:`_future.Connection` object with a transaction 

365 begun. 

366 

367 Use of this method is similar to that of 

368 :meth:`_future.Engine.connect`, typically as a context manager, which 

369 will automatically maintain the state of the transaction when the block 

370 ends, either by calling :meth:`_future.Connection.commit` when the 

371 block succeeds normally, or :meth:`_future.Connection.rollback` when an 

372 exception is raised, before propagating the exception outwards:: 

373 

374 with engine.begin() as connection: 

375 connection.execute(text("insert into table values ('foo')")) 

376 

377 

378 .. seealso:: 

379 

380 :meth:`_future.Engine.connect` 

381 

382 :meth:`_future.Connection.begin` 

383 

384 """ 

385 with self.connect() as conn: 

386 with conn.begin(): 

387 yield conn 

388 

389 def connect(self): 

390 """Return a new :class:`_future.Connection` object. 

391 

392 The :class:`_future.Connection` acts as a Python context manager, so 

393 the typical use of this method looks like:: 

394 

395 with engine.connect() as connection: 

396 connection.execute(text("insert into table values ('foo')")) 

397 connection.commit() 

398 

399 Where above, after the block is completed, the connection is "closed" 

400 and its underlying DBAPI resources are returned to the connection pool. 

401 This also has the effect of rolling back any transaction that 

402 was explicitly begun or was begun via autobegin, and will 

403 emit the :meth:`_events.ConnectionEvents.rollback` event if one was 

404 started and is still in progress. 

405 

406 .. seealso:: 

407 

408 :meth:`_future.Engine.begin` 

409 

410 

411 """ 

412 return super(Engine, self).connect() 

413 

414 

415class OptionEngine(OptionEngineMixin, Engine): 

416 pass 

417 

418 

419Engine._option_cls = OptionEngine