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

51 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-07 06:35 +0000

1from .. import util 

2from ..engine import Connection as _LegacyConnection 

3from ..engine import create_engine as _create_engine 

4from ..engine import Engine as _LegacyEngine 

5from ..engine.base import OptionEngineMixin 

6 

7NO_OPTIONS = util.immutabledict() 

8 

9 

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

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

12 

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

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

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

16 which has the 2.0 version of the API. 

17 

18 """ 

19 

20 kw["_future_engine_class"] = Engine 

21 return _create_engine(*arg, **kw) 

22 

23 

24class Connection(_LegacyConnection): 

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

26 

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

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

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

30 as transaction control. 

31 

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

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

34 following differences in behavior: 

35 

36 * The result object returned for results is the 

37 :class:`_engine.CursorResult` 

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

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

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

41 

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

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

44 the current transaction in progress, if any. 

45 

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

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

48 unconditionally start a 

49 transaction which can be controlled using the above mentioned 

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

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

52 

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

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

55 the transaction is explicitly committed, either via the 

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

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

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

59 

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

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

62 savepoint can be controlled by invoking 

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

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

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

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

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

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

69 effect that one desires). 

70 

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

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

73 refers to this connection as a parent. 

74 

75 

76 

77 """ 

78 

79 _is_future = True 

80 

81 def _branch(self): 

82 raise NotImplementedError( 

83 "sqlalchemy.future.Connection does not support " 

84 "'branching' of new connections." 

85 ) 

86 

87 def begin(self): 

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

89 

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

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

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

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

94 

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

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

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

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

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

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

101 

102 with engine.connect() as conn: 

103 with conn.begin(): 

104 conn.execute(...) 

105 conn.execute(...) 

106 

107 with conn.begin(): 

108 conn.execute(...) 

109 conn.execute(...) 

110 

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

112 the following code which does not use 

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

114 as "commit as you go" style:: 

115 

116 with engine.connect() as conn: 

117 conn.execute(...) 

118 conn.execute(...) 

119 conn.commit() 

120 

121 conn.execute(...) 

122 conn.execute(...) 

123 conn.commit() 

124 

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

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

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

128 concept of explicit transaction begin. 

129 

130 .. seealso:: 

131 

132 :ref:`tutorial_working_with_transactions` - in the 

133 :ref:`unified_tutorial` 

134 

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

136 

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

138 use a two phase /XID transaction 

139 

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

141 :class:`_future.Engine` 

142 

143 """ 

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

145 

146 def begin_nested(self): 

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

148 handle. 

149 

150 The returned object is an instance of 

151 :class:`_engine.NestedTransaction`. 

152 

153 Nested transactions require SAVEPOINT support in the 

154 underlying database. Any transaction in the hierarchy may 

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

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

157 transaction of a whole. 

158 

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

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

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

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

163 :meth:`_future.Connection.rollback`. 

164 

165 .. tip:: 

166 

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

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

169 :meth:`_engine.Connection.begin_nested`. 

170 

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

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

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

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

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

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

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

178 committed. 

179 

180 .. seealso:: 

181 

182 :meth:`_future.Connection.begin` 

183 

184 

185 """ 

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

187 

188 def commit(self): 

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

190 

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

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

193 the connection is in a non-invalidated state. 

194 

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

196 whenever a statement is first executed, or when the 

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

198 

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

200 the primary database transaction that is linked to the 

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

202 SAVEPOINT that would have been invoked from the 

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

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

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

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

207 

208 

209 """ 

210 if self._transaction: 

211 self._transaction.commit() 

212 

213 def rollback(self): 

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

215 

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

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

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

219 the transaction is cleared using this method. 

220 

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

222 whenever a statement is first executed, or when the 

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

224 

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

226 upon the primary database transaction that is linked to the 

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

228 SAVEPOINT that would have been invoked from the 

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

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

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

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

233 

234 

235 """ 

236 if self._transaction: 

237 self._transaction.rollback() 

238 

239 def close(self): 

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

241 

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

243 if any transaction is in place. 

244 

245 """ 

246 super(Connection, self).close() 

247 

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

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

250 :class:`_engine.Result`. 

251 

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

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

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

255 

256 * :class:`_expression.Select` 

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

258 :class:`_expression.Delete` 

259 * :class:`_expression.TextClause` and 

260 :class:`_expression.TextualSelect` 

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

262 :class:`_schema.DDLElement` 

263 

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

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

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

267 list of dictionaries is passed, the underlying statement execution 

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

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

270 method will be used. 

271 

272 :param execution_options: optional dictionary of execution options, 

273 which will be associated with the statement execution. This 

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

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

276 

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

278 

279 """ 

280 return self._execute_20( 

281 statement, parameters, execution_options or NO_OPTIONS 

282 ) 

283 

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

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

286 

287 This method is shorthand for invoking the 

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

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

290 

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

292 first row returned. 

293 

294 """ 

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

296 

297 

298class Engine(_LegacyEngine): 

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

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

301 source of database connectivity and behavior. 

302 

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

304 

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

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

307 

308 .. seealso:: 

309 

310 :doc:`/core/engines` 

311 

312 :ref:`connections_toplevel` 

313 

314 """ 

315 

316 _connection_cls = Connection 

317 _is_future = True 

318 

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

320 raise NotImplementedError( 

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

322 ) 

323 

324 transaction = ( 

325 run_callable 

326 ) = ( 

327 execute 

328 ) = ( 

329 scalar 

330 ) = ( 

331 _execute_clauseelement 

332 ) = _execute_compiled = table_names = has_table = _not_implemented 

333 

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

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

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

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

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

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

340 # single transaction rather than COMMIT for each statement. 

341 with self.begin() as conn: 

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

343 

344 @classmethod 

345 def _future_facade(self, legacy_engine): 

346 return Engine( 

347 legacy_engine.pool, 

348 legacy_engine.dialect, 

349 legacy_engine.url, 

350 logging_name=legacy_engine.logging_name, 

351 echo=legacy_engine.echo, 

352 hide_parameters=legacy_engine.hide_parameters, 

353 execution_options=legacy_engine._execution_options, 

354 ) 

355 

356 @util.contextmanager 

357 def begin(self): 

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

359 begun. 

360 

361 Use of this method is similar to that of 

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

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

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

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

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

367 

368 with engine.begin() as connection: 

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

370 

371 

372 .. seealso:: 

373 

374 :meth:`_future.Engine.connect` 

375 

376 :meth:`_future.Connection.begin` 

377 

378 """ 

379 with self.connect() as conn: 

380 with conn.begin(): 

381 yield conn 

382 

383 def connect(self): 

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

385 

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

387 the typical use of this method looks like:: 

388 

389 with engine.connect() as connection: 

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

391 connection.commit() 

392 

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

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

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

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

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

398 started and is still in progress. 

399 

400 .. seealso:: 

401 

402 :meth:`_future.Engine.begin` 

403 

404 

405 """ 

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

407 

408 

409class OptionEngine(OptionEngineMixin, Engine): 

410 pass 

411 

412 

413Engine._option_cls = OptionEngine