Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/sqlalchemy/dialects/postgresql/pg8000.py: 49%

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

301 statements  

1# dialects/postgresql/pg8000.py 

2# Copyright (C) 2005-2026 the SQLAlchemy authors and contributors <see AUTHORS 

3# 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 

7# mypy: ignore-errors 

8 

9r""" 

10.. dialect:: postgresql+pg8000 

11 :name: pg8000 

12 :dbapi: pg8000 

13 :connectstring: postgresql+pg8000://user:password@host:port/dbname[?key=value&key=value...] 

14 :url: https://pypi.org/project/pg8000/ 

15 

16.. versionchanged:: 1.4 The pg8000 dialect has been updated for version 

17 1.16.6 and higher, and is again part of SQLAlchemy's continuous integration 

18 with full feature support. 

19 

20.. _pg8000_unicode: 

21 

22Unicode 

23------- 

24 

25pg8000 will encode / decode string values between it and the server using the 

26PostgreSQL ``client_encoding`` parameter; by default this is the value in 

27the ``postgresql.conf`` file, which often defaults to ``SQL_ASCII``. 

28Typically, this can be changed to ``utf-8``, as a more useful default:: 

29 

30 # client_encoding = sql_ascii # actually, defaults to database encoding 

31 client_encoding = utf8 

32 

33The ``client_encoding`` can be overridden for a session by executing the SQL: 

34 

35.. sourcecode:: sql 

36 

37 SET CLIENT_ENCODING TO 'utf8'; 

38 

39SQLAlchemy will execute this SQL on all new connections based on the value 

40passed to :func:`_sa.create_engine` using the ``client_encoding`` parameter:: 

41 

42 engine = create_engine( 

43 "postgresql+pg8000://user:pass@host/dbname", client_encoding="utf8" 

44 ) 

45 

46.. _pg8000_ssl: 

47 

48SSL Connections 

49--------------- 

50 

51pg8000 accepts a Python ``SSLContext`` object which may be specified using the 

52:paramref:`_sa.create_engine.connect_args` dictionary:: 

53 

54 import ssl 

55 

56 ssl_context = ssl.create_default_context() 

57 engine = sa.create_engine( 

58 "postgresql+pg8000://scott:tiger@192.168.0.199/test", 

59 connect_args={"ssl_context": ssl_context}, 

60 ) 

61 

62If the server uses an automatically-generated certificate that is self-signed 

63or does not match the host name (as seen from the client), it may also be 

64necessary to disable hostname checking:: 

65 

66 import ssl 

67 

68 ssl_context = ssl.create_default_context() 

69 ssl_context.check_hostname = False 

70 ssl_context.verify_mode = ssl.CERT_NONE 

71 engine = sa.create_engine( 

72 "postgresql+pg8000://scott:tiger@192.168.0.199/test", 

73 connect_args={"ssl_context": ssl_context}, 

74 ) 

75 

76.. _pg8000_isolation_level: 

77 

78pg8000 Transaction Isolation Level 

79------------------------------------- 

80 

81The pg8000 dialect offers the same isolation level settings as that 

82of the :ref:`psycopg2 <psycopg2_isolation_level>` dialect: 

83 

84* ``READ COMMITTED`` 

85* ``READ UNCOMMITTED`` 

86* ``REPEATABLE READ`` 

87* ``SERIALIZABLE`` 

88* ``AUTOCOMMIT`` 

89 

90.. seealso:: 

91 

92 :ref:`postgresql_isolation_level` 

93 

94 :ref:`psycopg2_isolation_level` 

95 

96 

97""" # noqa 

98 

99import decimal 

100 

101from . import ranges 

102from .array import ARRAY as PGARRAY 

103from .base import _DECIMAL_TYPES 

104from .base import _FLOAT_TYPES 

105from .base import _INT_TYPES 

106from .base import ENUM 

107from .base import INTERVAL 

108from .base import PGCompiler 

109from .base import PGDialect 

110from .base import PGExecutionContext 

111from .base import PGIdentifierPreparer 

112from .json import JSON 

113from .json import JSONB 

114from .json import JSONPathType 

115from .pg_catalog import _SpaceVector 

116from .pg_catalog import OIDVECTOR 

117from .types import CITEXT 

118from ... import exc 

119from ... import util 

120from ...engine import processors 

121from ...sql import sqltypes 

122from ...sql.elements import quoted_name 

123 

124 

125class _PGString(sqltypes.String): 

126 render_bind_cast = True 

127 

128 

129class _PGNumericCommon(sqltypes.NumericCommon): 

130 render_bind_cast = True 

131 

132 def result_processor(self, dialect, coltype): 

133 if self.asdecimal: 

134 if coltype in _FLOAT_TYPES: 

135 return processors.to_decimal_processor_factory( 

136 decimal.Decimal, self._effective_decimal_return_scale 

137 ) 

138 elif coltype in _DECIMAL_TYPES or coltype in _INT_TYPES: 

139 # pg8000 returns Decimal natively for 1700 

140 return None 

141 else: 

142 raise exc.InvalidRequestError( 

143 "Unknown PG numeric type: %d" % coltype 

144 ) 

145 else: 

146 if coltype in _FLOAT_TYPES: 

147 # pg8000 returns float natively for 701 

148 return None 

149 elif coltype in _DECIMAL_TYPES or coltype in _INT_TYPES: 

150 return processors.to_float 

151 else: 

152 raise exc.InvalidRequestError( 

153 "Unknown PG numeric type: %d" % coltype 

154 ) 

155 

156 

157class _PGNumeric(_PGNumericCommon, sqltypes.Numeric): 

158 pass 

159 

160 

161class _PGFloat(_PGNumericCommon, sqltypes.Float): 

162 pass 

163 

164 

165class _PGNumericNoBind(_PGNumeric): 

166 def bind_processor(self, dialect): 

167 return None 

168 

169 

170class _PGJSON(JSON): 

171 render_bind_cast = True 

172 

173 

174class _PGJSONB(JSONB): 

175 render_bind_cast = True 

176 

177 

178class _PGJSONIndexType(sqltypes.JSON.JSONIndexType): 

179 def get_dbapi_type(self, dbapi): 

180 raise NotImplementedError("should not be here") 

181 

182 

183class _PGJSONIntIndexType(sqltypes.JSON.JSONIntIndexType): 

184 __visit_name__ = "json_int_index" 

185 

186 render_bind_cast = True 

187 

188 

189class _PGJSONStrIndexType(sqltypes.JSON.JSONStrIndexType): 

190 __visit_name__ = "json_str_index" 

191 

192 render_bind_cast = True 

193 

194 

195class _PGJSONPathType(JSONPathType): 

196 pass 

197 

198 # DBAPI type 1009 

199 

200 

201class _PGEnum(ENUM): 

202 def get_dbapi_type(self, dbapi): 

203 return dbapi.UNKNOWN 

204 

205 

206class _PGInterval(INTERVAL): 

207 render_bind_cast = True 

208 

209 def get_dbapi_type(self, dbapi): 

210 return dbapi.INTERVAL 

211 

212 @classmethod 

213 def adapt_emulated_to_native(cls, interval, **kw): 

214 return _PGInterval(precision=interval.second_precision) 

215 

216 

217class _PGTimeStamp(sqltypes.DateTime): 

218 render_bind_cast = True 

219 

220 

221class _PGDate(sqltypes.Date): 

222 render_bind_cast = True 

223 

224 

225class _PGTime(sqltypes.Time): 

226 render_bind_cast = True 

227 

228 

229class _PGInteger(sqltypes.Integer): 

230 render_bind_cast = True 

231 

232 

233class _PGSmallInteger(sqltypes.SmallInteger): 

234 render_bind_cast = True 

235 

236 

237class _PGNullType(sqltypes.NullType): 

238 pass 

239 

240 

241class _PGBigInteger(sqltypes.BigInteger): 

242 render_bind_cast = True 

243 

244 

245class _PGBoolean(sqltypes.Boolean): 

246 render_bind_cast = True 

247 

248 

249class _PGARRAY(PGARRAY): 

250 render_bind_cast = True 

251 

252 

253class _PGOIDVECTOR(_SpaceVector, OIDVECTOR): 

254 pass 

255 

256 

257class _Pg8000Range(ranges.AbstractSingleRangeImpl): 

258 def bind_processor(self, dialect): 

259 pg8000_Range = dialect.dbapi.Range 

260 

261 def to_range(value): 

262 if isinstance(value, ranges.Range): 

263 value = pg8000_Range( 

264 value.lower, value.upper, value.bounds, value.empty 

265 ) 

266 return value 

267 

268 return to_range 

269 

270 def result_processor(self, dialect, coltype): 

271 def to_range(value): 

272 if value is not None: 

273 value = ranges.Range( 

274 value.lower, 

275 value.upper, 

276 bounds=value.bounds, 

277 empty=value.is_empty, 

278 ) 

279 return value 

280 

281 return to_range 

282 

283 

284class _Pg8000MultiRange(ranges.AbstractMultiRangeImpl): 

285 def bind_processor(self, dialect): 

286 pg8000_Range = dialect.dbapi.Range 

287 

288 def to_multirange(value): 

289 if isinstance(value, list): 

290 mr = [] 

291 for v in value: 

292 if isinstance(v, ranges.Range): 

293 mr.append( 

294 pg8000_Range(v.lower, v.upper, v.bounds, v.empty) 

295 ) 

296 else: 

297 mr.append(v) 

298 return mr 

299 else: 

300 return value 

301 

302 return to_multirange 

303 

304 def result_processor(self, dialect, coltype): 

305 def to_multirange(value): 

306 if value is None: 

307 return None 

308 else: 

309 return ranges.MultiRange( 

310 ranges.Range( 

311 v.lower, v.upper, bounds=v.bounds, empty=v.is_empty 

312 ) 

313 for v in value 

314 ) 

315 

316 return to_multirange 

317 

318 

319_server_side_id = util.counter() 

320 

321 

322class PGExecutionContext_pg8000(PGExecutionContext): 

323 def create_server_side_cursor(self): 

324 ident = "c_%s_%s" % (hex(id(self))[2:], hex(_server_side_id())[2:]) 

325 return ServerSideCursor(self._dbapi_connection.cursor(), ident) 

326 

327 def pre_exec(self): 

328 if not self.compiled: 

329 return 

330 

331 

332class ServerSideCursor: 

333 server_side = True 

334 

335 def __init__(self, cursor, ident): 

336 self.ident = ident 

337 self.cursor = cursor 

338 

339 @property 

340 def connection(self): 

341 return self.cursor.connection 

342 

343 @property 

344 def rowcount(self): 

345 return self.cursor.rowcount 

346 

347 @property 

348 def description(self): 

349 return self.cursor.description 

350 

351 def execute(self, operation, args=(), stream=None): 

352 op = "DECLARE " + self.ident + " NO SCROLL CURSOR FOR " + operation 

353 self.cursor.execute(op, args, stream=stream) 

354 return self 

355 

356 def executemany(self, operation, param_sets): 

357 self.cursor.executemany(operation, param_sets) 

358 return self 

359 

360 def fetchone(self): 

361 self.cursor.execute("FETCH FORWARD 1 FROM " + self.ident) 

362 return self.cursor.fetchone() 

363 

364 def fetchmany(self, num=None): 

365 if num is None: 

366 return self.fetchall() 

367 else: 

368 self.cursor.execute( 

369 "FETCH FORWARD " + str(int(num)) + " FROM " + self.ident 

370 ) 

371 return self.cursor.fetchall() 

372 

373 def fetchall(self): 

374 self.cursor.execute("FETCH FORWARD ALL FROM " + self.ident) 

375 return self.cursor.fetchall() 

376 

377 def close(self): 

378 self.cursor.execute("CLOSE " + self.ident) 

379 self.cursor.close() 

380 

381 def setinputsizes(self, *sizes): 

382 self.cursor.setinputsizes(*sizes) 

383 

384 def setoutputsize(self, size, column=None): 

385 pass 

386 

387 

388class PGCompiler_pg8000(PGCompiler): 

389 def visit_mod_binary(self, binary, operator, **kw): 

390 return ( 

391 self.process(binary.left, **kw) 

392 + " %% " 

393 + self.process(binary.right, **kw) 

394 ) 

395 

396 

397class PGIdentifierPreparer_pg8000(PGIdentifierPreparer): 

398 def __init__(self, *args, **kwargs): 

399 PGIdentifierPreparer.__init__(self, *args, **kwargs) 

400 self._double_percents = False 

401 

402 

403class PGDialect_pg8000(PGDialect): 

404 driver = "pg8000" 

405 supports_statement_cache = True 

406 

407 minimum_dbapi_version = util.VersionInfo((1, 16, 6)) 

408 

409 supports_unicode_statements = True 

410 

411 supports_unicode_binds = True 

412 

413 default_paramstyle = "format" 

414 supports_sane_multi_rowcount = True 

415 execution_ctx_cls = PGExecutionContext_pg8000 

416 statement_compiler = PGCompiler_pg8000 

417 preparer = PGIdentifierPreparer_pg8000 

418 supports_server_side_cursors = True 

419 

420 supports_native_json_serialization = False 

421 supports_native_json_deserialization = True 

422 dialect_injects_custom_json_deserializer = True 

423 

424 render_bind_cast = True 

425 

426 # reversed as of pg8000 1.16.6. 1.16.5 and lower 

427 # are no longer compatible 

428 description_encoding = None 

429 # description_encoding = "use_encoding" 

430 

431 colspecs = util.update_copy( 

432 PGDialect.colspecs, 

433 { 

434 sqltypes.String: _PGString, 

435 sqltypes.Numeric: _PGNumericNoBind, 

436 sqltypes.Float: _PGFloat, 

437 sqltypes.JSON: _PGJSON, 

438 sqltypes.Boolean: _PGBoolean, 

439 sqltypes.NullType: _PGNullType, 

440 JSONB: _PGJSONB, 

441 CITEXT: CITEXT, 

442 sqltypes.JSON.JSONPathType: _PGJSONPathType, 

443 sqltypes.JSON.JSONIndexType: _PGJSONIndexType, 

444 sqltypes.JSON.JSONIntIndexType: _PGJSONIntIndexType, 

445 sqltypes.JSON.JSONStrIndexType: _PGJSONStrIndexType, 

446 sqltypes.Interval: _PGInterval, 

447 INTERVAL: _PGInterval, 

448 sqltypes.DateTime: _PGTimeStamp, 

449 sqltypes.DateTime: _PGTimeStamp, 

450 sqltypes.Date: _PGDate, 

451 sqltypes.Time: _PGTime, 

452 sqltypes.Integer: _PGInteger, 

453 sqltypes.SmallInteger: _PGSmallInteger, 

454 sqltypes.BigInteger: _PGBigInteger, 

455 sqltypes.Enum: _PGEnum, 

456 sqltypes.ARRAY: _PGARRAY, 

457 OIDVECTOR: _PGOIDVECTOR, 

458 ranges.INT4RANGE: _Pg8000Range, 

459 ranges.INT8RANGE: _Pg8000Range, 

460 ranges.NUMRANGE: _Pg8000Range, 

461 ranges.DATERANGE: _Pg8000Range, 

462 ranges.TSRANGE: _Pg8000Range, 

463 ranges.TSTZRANGE: _Pg8000Range, 

464 ranges.INT4MULTIRANGE: _Pg8000MultiRange, 

465 ranges.INT8MULTIRANGE: _Pg8000MultiRange, 

466 ranges.NUMMULTIRANGE: _Pg8000MultiRange, 

467 ranges.DATEMULTIRANGE: _Pg8000MultiRange, 

468 ranges.TSMULTIRANGE: _Pg8000MultiRange, 

469 ranges.TSTZMULTIRANGE: _Pg8000MultiRange, 

470 }, 

471 ) 

472 

473 def __init__(self, client_encoding=None, **kwargs): 

474 PGDialect.__init__(self, **kwargs) 

475 self.client_encoding = client_encoding 

476 

477 if self._native_inet_types: 

478 raise NotImplementedError( 

479 "The pg8000 dialect does not fully implement " 

480 "ipaddress type handling; INET is supported by default, " 

481 "CIDR is not" 

482 ) 

483 

484 def retrieve_dbapi_version(self, dbapi): 

485 return util.parse_version_string(getattr(dbapi, "__version__", None)) 

486 

487 @classmethod 

488 def import_dbapi(cls): 

489 return __import__("pg8000") 

490 

491 def create_connect_args(self, url): 

492 opts = url.translate_connect_args(username="user") 

493 if "port" in opts: 

494 opts["port"] = int(opts["port"]) 

495 opts.update(url.query) 

496 return ([], opts) 

497 

498 def is_disconnect(self, e, connection, cursor): 

499 if isinstance(e, self.dbapi.InterfaceError) and "network error" in str( 

500 e 

501 ): 

502 # new as of pg8000 1.19.0 for broken connections 

503 return True 

504 

505 # connection was closed normally 

506 return "connection is closed" in str(e) 

507 

508 def get_isolation_level_values(self, dbapi_connection): 

509 return ( 

510 "AUTOCOMMIT", 

511 "READ COMMITTED", 

512 "READ UNCOMMITTED", 

513 "REPEATABLE READ", 

514 "SERIALIZABLE", 

515 ) 

516 

517 def set_isolation_level(self, dbapi_connection, level): 

518 level = level.replace("_", " ") 

519 

520 if level == "AUTOCOMMIT": 

521 dbapi_connection.autocommit = True 

522 else: 

523 dbapi_connection.autocommit = False 

524 cursor = dbapi_connection.cursor() 

525 cursor.execute( 

526 "SET SESSION CHARACTERISTICS AS TRANSACTION " 

527 f"ISOLATION LEVEL {level}" 

528 ) 

529 cursor.execute("COMMIT") 

530 cursor.close() 

531 

532 def detect_autocommit_setting(self, dbapi_conn) -> bool: 

533 return bool(dbapi_conn.autocommit) 

534 

535 def set_readonly(self, connection, value): 

536 cursor = connection.cursor() 

537 try: 

538 cursor.execute( 

539 "SET SESSION CHARACTERISTICS AS TRANSACTION %s" 

540 % ("READ ONLY" if value else "READ WRITE") 

541 ) 

542 cursor.execute("COMMIT") 

543 finally: 

544 cursor.close() 

545 

546 def get_readonly(self, connection): 

547 cursor = connection.cursor() 

548 try: 

549 cursor.execute("show transaction_read_only") 

550 val = cursor.fetchone()[0] 

551 finally: 

552 cursor.close() 

553 

554 return val == "on" 

555 

556 def set_deferrable(self, connection, value): 

557 cursor = connection.cursor() 

558 try: 

559 cursor.execute( 

560 "SET SESSION CHARACTERISTICS AS TRANSACTION %s" 

561 % ("DEFERRABLE" if value else "NOT DEFERRABLE") 

562 ) 

563 cursor.execute("COMMIT") 

564 finally: 

565 cursor.close() 

566 

567 def get_deferrable(self, connection): 

568 cursor = connection.cursor() 

569 try: 

570 cursor.execute("show transaction_deferrable") 

571 val = cursor.fetchone()[0] 

572 finally: 

573 cursor.close() 

574 

575 return val == "on" 

576 

577 def _set_client_encoding(self, dbapi_connection, client_encoding): 

578 cursor = dbapi_connection.cursor() 

579 cursor.execute(f""" 

580 SET CLIENT_ENCODING TO '{client_encoding.replace("'", "''")}'""") 

581 cursor.execute("COMMIT") 

582 cursor.close() 

583 

584 def do_begin_twophase(self, connection, xid): 

585 connection.connection.tpc_begin((0, xid, "")) 

586 

587 def do_prepare_twophase(self, connection, xid): 

588 connection.connection.tpc_prepare() 

589 

590 def do_rollback_twophase( 

591 self, connection, xid, is_prepared=True, recover=False 

592 ): 

593 connection.connection.tpc_rollback((0, xid, "")) 

594 

595 def do_commit_twophase( 

596 self, connection, xid, is_prepared=True, recover=False 

597 ): 

598 connection.connection.tpc_commit((0, xid, "")) 

599 

600 def do_recover_twophase(self, connection): 

601 return [row[1] for row in connection.connection.tpc_recover()] 

602 

603 def on_connect(self): 

604 fns = [] 

605 

606 def on_connect(conn): 

607 conn.py_types[quoted_name] = conn.py_types[str] 

608 

609 fns.append(on_connect) 

610 

611 if self.client_encoding is not None: 

612 

613 def on_connect(conn): 

614 self._set_client_encoding(conn, self.client_encoding) 

615 

616 fns.append(on_connect) 

617 

618 if self._native_inet_types is False: 

619 

620 def on_connect(conn): 

621 # inet 

622 conn.register_in_adapter(869, lambda s: s) 

623 

624 # cidr 

625 conn.register_in_adapter(650, lambda s: s) 

626 

627 fns.append(on_connect) 

628 

629 if self._json_deserializer: 

630 

631 def on_connect(conn): 

632 # json 

633 conn.register_in_adapter(114, self._json_deserializer) 

634 

635 # jsonb 

636 conn.register_in_adapter(3802, self._json_deserializer) 

637 

638 fns.append(on_connect) 

639 

640 if len(fns) > 0: 

641 

642 def on_connect(conn): 

643 for fn in fns: 

644 fn(conn) 

645 

646 return on_connect 

647 else: 

648 return None 

649 

650 @util.memoized_property 

651 def _dialect_specific_select_one(self): 

652 return ";" 

653 

654 

655dialect = PGDialect_pg8000