Re: [sqlalchemy] sqlalchemy session in same transaction as existing psycopg2 connection

2018-08-29 Thread 'Brian DeRocher' via sqlalchemy
Beautiful.  Skipping the psycopg2 initialization prevents that rollback and 
allows SQLAlchemy to use the same transaction.

FWIW, I don't think pool_reset_on_return=None is needed, at least for my 
purposes.

Thanks for the help and thanks for the advice about raw_connection().  I'll 
get that into place, at least for the testing suite.

Brian

-- 
SQLAlchemy - 
The Python SQL Toolkit and Object Relational Mapper

http://www.sqlalchemy.org/

To post example code, please provide an MCVE: Minimal, Complete, and Verifiable 
Example.  See  http://stackoverflow.com/help/mcve for a full description.
--- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.


Re: [sqlalchemy] sqlalchemy session in same transaction as existing psycopg2 connection

2018-08-28 Thread 'Brian DeRocher' via sqlalchemy


On Tuesday, August 28, 2018 at 2:51:47 PM UTC-4, Mike Bayer wrote:
>
> On Tue, Aug 28, 2018 at 11:32 AM, 'Brian DeRocher' via sqlalchemy 
> > wrote: 
> > Hey all, 
> > 
> > I'm writing some automated tests for some legacy python code using a 
> > psycopg2 connection.  I'd like to check data in the database by using 
> > SQLAlchemy.  I rollback the database transaction after each test in 
> > tearDown(). 
> > 
> > The problem is my SQLAlchemy connection doesn't see the database 
> updates. 
> > At first I thought they weren't using the same connection, but they are. 
> > I'm using create_engine(..., creator=get_conn). 
> > 
> > The problem appears to be that DefaultDialect is rolling back the 
> > transaction.  See sqlalchemy/engine/default.py line 167.  I had to 
> review PG 
> > logs to spot this as it's not logged. 
> > 
> > self.do_rollback(connection.connection) 
> > 
> > Is this line really needed? 
>
> yup (in general) 
>

I ask because it doesn't seem natural to me that the job so the 
DefaultDialect is to manage a transaction. 

>
> > What would it be rolling back? 
>
> all of the snapshots and/or locks that are accumulated by the 
> Postgresql database as commands on the connection proceed.   This 
> means literally versions of the database that don't exist outside of 
> the transaction, as well as simple things like table/ row locks.   See 
> https://www.postgresql.org/docs/9.1/static/mvcc.html .   As far as why 
> there is a transaction in the first place, the pep-249 DBAPI drivers 
> like psycopg2 are required for a connection to be in a transaction by 
> default, and "autocommit" systems are not standard per spec. 
>
> > Can it be 
> > avoided? 
>
> the rollback you are seeing is likely the connection-pool level 
> "reset", which is not strictly necessary if the code that makes use of 
> the pool is absolutely disciplined about ensuring transactions are 
> cleaned up before returning a connection to the pool, or as in your 
> case you are linking it to some larger state and don't actually want 
> the connection pool to be managing connection lifecycle.  You can set 
> this with pool_reset_on_return=None, see 
>
> http://docs.sqlalchemy.org/en/latest/core/engines.html?highlight=reset_on_return#sqlalchemy.create_engine.params.pool_reset_on_return,
>  
>
> however that docstring seems a little typo-ish and the link is broken 
> so the actual list of values you can see at 
>
> http://docs.sqlalchemy.org/en/latest/core/pooling.html?highlight=reset_on_return#sqlalchemy.pool.Pool.params.reset_on_return
>  
>
> let me fix the formatting on that docstring 
>

Unfortunately setting pool_reset_on_return=None did not help.  There's 
still a path of code down to this do_rollback().   I insert a 
traceback.format_stack() near the rollback, and this is the stack at that 
point in time (with pool_reset_on_return=None):

DEBUG:root:  File "iso.py", line 72, in 
test_transaction()
  File "iso.py", line 66, in test_transaction
user = session.query(User).get(uid)
  File 
"/home/brian/fr/venv2/local/lib/python2.7/site-packages/sqlalchemy/orm/query.py",
 
line 864, in get
return self._get_impl(ident, loading.load_on_ident)
  File 
"/home/brian/fr/venv2/local/lib/python2.7/site-packages/sqlalchemy/orm/query.py",
 
line 897, in _get_impl
return fallback_fn(self, key)
  File 
"/home/brian/fr/venv2/local/lib/python2.7/site-packages/sqlalchemy/orm/loading.py",
 
line 223, in load_on_ident
return q.one()
  File 
"/home/brian/fr/venv2/local/lib/python2.7/site-packages/sqlalchemy/orm/query.py",
 
line 2814, in one
ret = self.one_or_none()
  File 
"/home/brian/fr/venv2/local/lib/python2.7/site-packages/sqlalchemy/orm/query.py",
 
line 2784, in one_or_none
ret = list(self)
  File 
"/home/brian/fr/venv2/local/lib/python2.7/site-packages/sqlalchemy/orm/query.py",
 
line 2855, in __iter__
return self._execute_and_instances(context)
  File 
"/home/brian/fr/venv2/local/lib/python2.7/site-packages/sqlalchemy/orm/query.py",
 
line 2876, in _execute_and_instances
close_with_result=True)
  File 
"/home/brian/fr/venv2/local/lib/python2.7/site-packages/sqlalchemy/orm/query.py",
 
line 2885, in _get_bind_args
**kw
  File 
"/home/brian/fr/venv2/local/lib/python2.7/site-packages/sqlalchemy/orm/query.py",
 
line 2867, in _connection_from_session
conn = self.session.connection(**kw)
  File 
"/home/brian/fr/venv2/local/lib/python2.7/site-packages/sqlalchemy/orm/session.py",
 
line 1013, in connection
execution_options=execution_options)
  File 
"/home/brian/fr/venv2/local/lib/python2.7/site-packages/sqlal

[sqlalchemy] sqlalchemy session in same transaction as existing psycopg2 connection

2018-08-28 Thread 'Brian DeRocher' via sqlalchemy
Hey all,

I'm writing some automated tests for some legacy python code using a 
psycopg2 connection.  I'd like to check data in the database by using 
SQLAlchemy.  I rollback the database transaction after each test in 
tearDown().

The problem is my SQLAlchemy connection doesn't see the database updates.  
At first I thought they weren't using the same connection, but they are.  
I'm using create_engine(..., creator=get_conn).

The problem appears to be that DefaultDialect is rolling back the 
transaction.  See sqlalchemy/engine/default.py line 167.  I had to review 
PG logs to spot this as it's not logged.

self.do_rollback(connection.connection)

Is this line really needed?  What would it be rolling back?  Can it be 
avoided?  When I disable this line of code, the transaction continues and 
sqlalchemy can see the updates from psyopg2.

I've attached a demo file.

Thanks,
Brian

-- 
SQLAlchemy - 
The Python SQL Toolkit and Object Relational Mapper

http://www.sqlalchemy.org/

To post example code, please provide an MCVE: Minimal, Complete, and Verifiable 
Example.  See  http://stackoverflow.com/help/mcve for a full description.
--- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.
import os
import logging
from dotenv import load_dotenv
import psycopg2
from psycopg2.extras import LoggingConnection
from sqlalchemy import Column, create_engine, Integer, MetaData, Text
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base


dotenv_path = os.path.join(os.path.dirname(__file__), '.env')
load_dotenv(dotenv_path)

logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.DEBUG)

db_conn = None

def get_conn():
global db_conn
logging.debug('get_conn()')
if db_conn is None:
logging.debug('  theres no db_conn, must create one')
conn_string = "dbname='{0}' host='localhost' port=5432 user='{1}' password='{2}'".format(
os.getenv("DB_NAME"),
os.getenv("DB_USER"),
os.getenv("DB_PASSWORD"))
db_conn = psycopg2.connect(conn_string, connection_factory=LoggingConnection)
db_conn.initialize(logger)
else:
logging.debug('  using exising db_conn')
return db_conn


BaseCi = declarative_base(metadata=MetaData(schema='config'))

class User(BaseCi):
__tablename__ = 'user'
user_id = Column(Integer, primary_key=True)
user_status = Column(Text)


def legacy_psycopg_code(uid, status):
conn = get_conn()
cur = conn.cursor()
query = 'UPDATE config.user SET user_status = %s WHERE user_id = %s '
cur.execute(query, (status, uid,))

# Confirm it's there.
query = 'select user_status from config.user where user_id = %s'
cur.execute(query, (uid,))
uc = cur.fetchone()[0]
logging.debug("user_status is %s" % uc)


def test_transaction():
uid = 78
status = 'ex-parrot'
legacy_psycopg_code(uid, status)

# Use existing connection so we can see inside the transaction.
engine = create_engine('postgresql+psycopg2://', creator=get_conn)
Session = sessionmaker(bind=engine)
session = Session()

user = session.query(User).get(uid)
logging.debug(user.user_status)
#   assertEquals(user.user_status, status)


if __name__ == "__main__":
test_transaction()
DEBUG:root:get_conn()
DEBUG:root:  theres no db_conn, must create one
DEBUG:__main__:UPDATE config.user SET user_status = 'ex-parrot' WHERE user_id = 
78 
DEBUG:__main__:select user_status from config.user where user_id = 78
DEBUG:root:user_status is ex-parrot
DEBUG:root:get_conn()
DEBUG:root:  using exising db_conn
DEBUG:__main__:select version()
DEBUG:__main__:select current_schema()
DEBUG:__main__:show transaction isolation level
DEBUG:__main__:SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1
DEBUG:__main__:SELECT CAST('test unicode returns' AS VARCHAR(60)) AS anon_1
DEBUG:__main__:SELECT 'x' AS some_label
DEBUG:root:DefaultDialect::initialize() calls do_rollback() here.
<-
DEBUG:__main__:show standard_conforming_strings
DEBUG:__main__:SELECT config."user".user_id AS config_user_user_id, 
config."user".user_status AS config_user_user_status 
FROM config."user" 
WHERE config."user".user_id = 78
DEBUG:root:Active


feedback request on support for sequences with --autogenerate

2018-01-08 Thread Brian DeRocher
[23:13]  I just started using Alembic/SA in a project that had a well 
established database.
[23:13]  I'm so glad there's support for relationships.  Thanks!
[23:13]  But there was no support for sequences, when it came to 
--autogen.
[23:14]  So I took a stab at adding that and I got the happy path 
working.
[23:14]  I'd like someone to take a look at my patches and give me some 
feedback.
[23:15]  So what I have now are forks of SA and Alembic.
[23:16]  I'm not too familiar with Bitbucket.  What's the next step?

# Concerns

So some of the issues I have are the following:

* There's a lot of support in the codebase for schemas.  I just want to make 
sure I got it right with regard to metadata.
* I want to make sure that these sequences can be used independent of any 
column.
* I want to make sure that column server_default works correctly.
* I need to make sure downgrade() / drop sequence is working.  Not to mention 
ALTER SEQUENCE.
* I need to make sure dropping a schema will remove it as a  default from any 
column that uses it.

Surely I'm forgetting many things, so please fill me in.

# Transactions in PG

One thing I've noticed is that calls to sequences in PostgreSQL don't rollback 
in a transaction.  I'm not sure if that causes an issue for alembic.

# Testing

I'm new to Python.  I've run automated tests using python setup.py test.  Is 
there any way to run a subset of tests or label tests with @WIP to just run 
those?  And is it me, or are there a lot of failures?  Is there anyway someone 
can create the tests, so I know I got this working correctly?  Also, since 
sqlite doesn't support sequences, how do I get automated testing to use PG.

A pull request is coming soon.

Thanks,
Brian

-- 
Brian DeRocher

Noblis | noblis.org | ☎ 703.610.1589 | ✉ brian.deroc...@noblis.org


-- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy-alembic" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy-alembic+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.