SQLalchemy не фиксирует изменения при установке роли - PullRequest
2 голосов
/ 15 мая 2019

Я создаю таблицы с использованием механизма sqlalchemy, но, хотя мои операторы create выполняются без ошибок, таблицы не отображаются в базе данных, когда я пытаюсь установить роль заранее.

url = 'postgresql://{}:{}@{}:{}/{}'
url = url.format(user, password, host, port, db)

engine = sqlalchemy.create_engine(url)

# works fine
engine.execute("CREATE TABLE testpublic (id int, val text); \n\nINSERT INTO testpublic VALUES (1,'foo'), (2,'bar'), (3,'baz');")
r = engine.execute("select * from testpublic")
r.fetchall() # returns expected tuples
engine.execute("DROP TABLE testpublic;")

# appears to succeed/does NOT throw any error
engine.execute("SET ROLE read_write; CREATE table testpublic (id int, val text);")

# throws error "relation testpublic does not exist"
engine.execute("select * FROM testpublic")

Для контекста, я на Python 3.6, sqlalchemy версии 1.2.17 и postgres 11.1, и роль "read_write" абсолютно существует и имеет все необходимые разрешения для создания публичной таблицы (у меня нет проблем).выполняя точную последовательность выше в pgadmin).

Кто-нибудь знает, почему это так и как исправить?

1 Ответ

1 голос
/ 16 мая 2019

Проблема здесь в том, как sqlalchemy решает выдать коммит после каждого оператора.

если текст передан engine.execute, sqlalchemy попытается определить, является ли текст DML или DDL, используя следующее регулярное выражение. Вы можете найти его в источниках здесь

AUTOCOMMIT_REGEXP = re.compile(
    r"\s*(?:UPDATE|INSERT|CREATE|DELETE|DROP|ALTER)", re.I | re.UNICODE
)

Это только обнаруживает слова, если они находятся в начале текста, игнорируя любые начальные пробелы. Таким образом, хотя ваша первая попытка # works fine, во втором примере не удается распознать, что необходимо выполнить коммит после выполнения оператора, поскольку первое слово - SET.

Вместо этого sqlalchemy выдает откат, поэтому он # appears to succeed/does NOT throw any error.

самое простое решение - зафиксировать вручную.

пример:

engine.execute("SET ROLE read_write; CREATE table testpublic (id int, val text); COMMIT;")

или, оберните sql в text и установите autocommit=True, , как показано в документации

stmt = text('set role read_write; create table testpublic (id int, val text);').execution_options(autocommit=True)
e.execute(stmt)
...