Create a Postgres database using python
Asked Answered
H

3

81

I want to create Postgres database using Python.

con = psql.connect(dbname='postgres',
      user=self.user_name, host='',
      password=self.password)

cur = con.cursor()
cur.execute("CREATE DATABASE %s  ;" % self.db_name)

I am getting the following error:

InternalError: CREATE DATABASE cannot run inside a transaction block

I am using psycopg2 to connect. I don't understand what's the problem. What am I trying to do is to connect to database (Postgres):

psql -postgres -U UserName

And then create another database:

create database test;

This is what I usually do and I want to automate this by creating Python script.

Hewett answered 27/12, 2015 at 19:34 Comment(1)
From the doc: Warning - Never, never, NEVER use Python string concatenation (+) or string parameters interpolation (%) to pass variables to a SQL query string. Not even at gunpoint.Onus
C
116

Use ISOLATION_LEVEL_AUTOCOMMIT, a psycopg2 extensions:

No transaction is started when command are issued and no commit() or rollback() is required.

import psycopg2
from psycopg2 import sql
from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT # <-- ADD THIS LINE

con = psycopg2.connect(dbname='postgres',
      user=self.user_name, host='',
      password=self.password)

con.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT) # <-- ADD THIS LINE

cur = con.cursor()

# Use the psycopg2.sql module instead of string concatenation 
# in order to avoid sql injection attacks.
cur.execute(sql.SQL("CREATE DATABASE {}").format(
        sql.Identifier(self.db_name))
    )
Cung answered 27/12, 2015 at 19:47 Comment(2)
worked for me. just FYI all that you are importing is 0 the integer. so could just pass it in without bothering w extensions. and also FYI you need a separate execute call if you have to DROP DATABASE first.Freeboot
@JLPeyret "all that you are importing is 0 the integer. so could just pass it in without bothering w extensions" -- true, but one can never be sure that this stays so forever. Code such as con.set_isolation_level(0) is less readable and less future-proof as named constants. Cf. the issue of C's NULL pointer. It almost always has the numerical value 0, but it would be very bad practice to say int *x = 0;. In C++11 and above, there's even a keyword nullptr for this. Although everyone "knows" that it's just 0 in all likelihood... :-)Lindbom
T
50

As shown in the other answer the connection must be in autocommit mode. Another way of setting it using psycopg2 is through the autocommit attribute:

import psycopg2
from psycopg2 import sql

con = psycopg2.connect(...)
con.autocommit = True

cur = con.cursor()
# sql.SQL and sql.Identifier are needed to avoid SQL injection attacks.
cur.execute(sql.SQL('CREATE DATABASE {};').format(
    sql.Identifier(self.db_name)))
Tai answered 26/4, 2017 at 12:58 Comment(2)
Do we just see another SQL injection through the value of self.db_name? self.db_name = "arbitrary; DROP TABLE admin"Tiliaceous
con.autocommit = True. fixedElvinelvina
B
3

A better and simple solution:

import psycopg # this uses psycopg version 3

def conection()
    config = {'user':'postgres',
          'password':'password_string',
              'host':'127.0.0.1',
              'port':'5432',
            'dbname':'postgres',
        'autocommit':True} #this resolve the problem "InternalError: CREATE DATABASE cannot run inside a transaction block"
    try:
        cnx = psycopg.connect(**config)
    except psycopg.Error as err:
        print(err)
        exit(1)
    else:
        return cnx
Bellbottoms answered 11/11, 2022 at 14:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.