SQLAlchemy - Connection Select

 

Пример 1 - прочитать из базы значение

комманда состояние соединения последняя SQL комментарии
engine = create_engine(f'postgresql://...') нет соединения    
conn = engine.connect() idle ROLLBACK  
result = conn.execute("SELECT name FROM host LIMIT 10") idle in transaction SELECT name FROM host LIMIT 10 данные уже в result
for row in result: print("name:", row['name']) idle in transaction SELECT name FROM host LIMIT 10  
conn.close() idle ROLLBACK соединение вернулось в пул

первая особенность: соединение не закрывается. Иногда его таки нужно закрыть. Способы:

вызвать engine.dispose() при этом pool пересоздастся, закрыв существующие соединения, engine можно будет использовать далее, не происходит уничтожение объекта. Более того, видимо engine запоминает информацию о базе, и при следующем соединении не делает несколько служебных SELECT, как при самом первом соединении.

engine = create_engine(f'postgresql://...') нет соединения    
conn = engine.connect() idle ROLLBACK  
result = conn.execute("SELECT name FROM host LIMIT 10") idle in transaction SELECT name FROM host LIMIT 10 данные уже в result
for row in result: print("name:", row['name']) idle in transaction SELECT name FROM host LIMIT 10  
conn.close() idle ROLLBACK соединение вернулось в пул
engine.dispose() нет соединения    

использовать другой pool, например create_engine(f'postgresql://user:password@localhost:5432/db', poolclass=sqlalchemy.pool.NullPool)

engine = create_engine(f'postgresql://...', poolclass=NullPool) нет соединения    
conn = engine.connect() idle ROLLBACK  
result = conn.execute("SELECT name FROM host LIMIT 10") idle in transaction SELECT name FROM host LIMIT 10 данные уже в result
for row in result: print("name:", row['name']) idle in transaction SELECT name FROM host LIMIT 10  
conn.close() нет соединения    

добраться до соединения psycopg2 и закрыть его. это не правильно, при выходе из программы пул захочет закрыть соединение, соединение уже закрыто - исключение.

engine = create_engine(f'postgresql://...') нет соединения    
conn = engine.connect() idle ROLLBACK  
result = conn.execute("SELECT name FROM host LIMIT 10") idle in transaction SELECT name FROM host LIMIT 10 данные уже в result
for row in result: print("name:", row['name']) idle in transaction SELECT name FROM host LIMIT 10  
conn.connection.connection.close() нет соединения   так делать неправильно

отсоединить соединение от пула, тогда оно реально закроется

engine = create_engine(f'postgresql://...') нет соединения    
conn = engine.connect() idle ROLLBACK  
result = conn.execute("SELECT name FROM host LIMIT 10") idle in transaction SELECT name FROM host LIMIT 10 данные уже в result
for row in result: print("name:", row['name']) idle in transaction SELECT name FROM host LIMIT 10  
conn.detach() idle in transaction SELECT name FROM host LIMIT 10 отсоединяем от пула
conn.close() нет соединения    

вторая особенность: после показа всех значений состояние остается idle in transaction.

В документации сказано, что по умолчанию самый базовый уровень DBAPI всегда работает в режиме транзакции (в документации постгрес отмечено что это дефолтный режим https://www.postgresql.org/docs/current/ecpg-sql-set-autocommit.html). но у Connection, Engine, Executable есть настройки execution_options(), и там установленно autocommit=true, это настройка alchemy, не постгреса, работает таким образом: sqlalchemy пытается определить тип команды (даже с помощью регулярок) и если команда INSERT, UPDATE, DELETE и прочие DDL, то sqlalchemy посылает автоматически COMMIT, очевидно для SELECT COMMIT не посылается, и мы находимся в транзакции. решения 2:

вызвать commit() не на чем, так как транзакцию не получали, зато можно послать это слово прямым текстом: conn.execute("COMMIT"), причем можно послать эту команду подрят несколько раз, исключения не будет.

engine = create_engine(f'postgresql://...') нет соединения    
conn = engine.connect() idle ROLLBACK  
result = conn.execute("SELECT name FROM host LIMIT 10") idle in transaction SELECT name FROM host LIMIT 10 данные уже в result
for row in result: print("name:", row['name']) idle in transaction SELECT name FROM host LIMIT 10  
conn.execute("COMMIT") idle COMMIT добились конца транзакции
conn.close() idle ROLLBACK соединение вернулось в пул

совет из док.: conn.execute(text("select name from host LIMIT 1").execution_options(autocommit=True))

engine = create_engine(f'postgresql://...') нет соединения    
conn = engine.connect() idle ROLLBACK  
conn.execute(text("select name from host LIMIT 10").execution_options(autocommit=True)) idle COMMIT закончили транзакцию
for row in result: print("name:", row['name']) idle COMMIT  
conn.close() idle COMMIT соединение вернулось в пул

третья особенность: все данные сразу попадают из execute в result.

(в этом можно убедиться выбрав большое кол-во данные и померяв память программы) при этом если закрыть соединение, то данные из result будет получить нельзя. Чтоб получить данные из базы по частям нужно создать курсор postgres, в терминах psycopg это серверный курсор или именованный курсор.

 

Яндекс.Метрика