Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1hbVtd-0007Oz-VW for psycopg@arkaria.postgresql.org; Thu, 13 Jun 2019 20:00:30 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.89) (envelope-from ) id 1hbVtc-0001TX-S6 for psycopg@arkaria.postgresql.org; Thu, 13 Jun 2019 20:00:28 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1hbVtc-0001TQ-F1 for psycopg@lists.postgresql.org; Thu, 13 Jun 2019 20:00:28 +0000 Received: from smtp109.iad3b.emailsrvr.com ([146.20.161.109]) by makus.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1hbVtZ-0007Z7-8G for psycopg@postgresql.org; Thu, 13 Jun 2019 20:00:27 +0000 X-Auth-ID: xof@thebuild.com Received: by smtp22.relay.iad3b.emailsrvr.com (Authenticated sender: xof-AT-thebuild.com) with ESMTPSA id 7C89060168; Thu, 13 Jun 2019 16:00:23 -0400 (EDT) X-Sender-Id: xof@thebuild.com Received: from [10.1.1.139] (sfosj0215mups301.wiline.com [67.207.108.210]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384) by 0.0.0.0:25 (trex/5.7.12); Thu, 13 Jun 2019 16:00:23 -0400 Content-Type: text/plain; charset=us-ascii Mime-Version: 1.0 (Mac OS X Mail 11.5 \(3445.9.1\)) Subject: Re: Creating dynamically-typed tables using psycopg2's built-in formatting From: Christophe Pettus In-Reply-To: Date: Thu, 13 Jun 2019 13:00:20 -0700 Cc: psycopg@postgresql.org Content-Transfer-Encoding: quoted-printable Message-Id: <33071EF2-86DE-4698-BFAA-6F084024A826@thebuild.com> References: To: Daniel Cohen X-Mailer: Apple Mail (2.3445.9.1) List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Precedence: bulk Hi, Daniel, First, tbl and "tbl" aren't "totally different": > xof=3D# create table tbl (i integer); > CREATE TABLE > xof=3D# create table "tbl" (i integer); > ERROR: relation "tbl" already exists The difference is that putting double quotes around an SQL identifier = makes the comparison type-sensitive, and allows for characters not = otherwise allowed in identifiers: > xof=3D# select * from Tbl; > i=20 > --- > (0 rows) >=20 > xof=3D# select * from "Tbl"; > ERROR: relation "Tbl" does not exist > LINE 1: select * from "Tbl"; > ^ You can use SQL.identifier, but you need to make sure you are getting = the case right; in general, PostgreSQL types are all lower-case, and = it's only the lack of double quotes that makes this work: xof=3D# create table x (i VARCHAR); CREATE TABLE xof=3D# create table y (i "VARCHAR"); ERROR: type "VARCHAR" does not exist LINE 1: create table y (i "VARCHAR"); ^ xof=3D# create table y (i "varchar"); CREATE TABLE > On Jun 13, 2019, at 12:28, Daniel Cohen = wrote: >=20 > Hi! >=20 > I'm working on a project in Python that interacts with a PostgreSQL = data warehouse, and I'm using the psycopg2 API. I am looking to create = dynamically-typed tables. >=20 > For example, I would like to be able to execute the following code: >=20 > from psycopg2 import connect, > sql >=20 > connection=20 > =3D connect(host=3D"host", port=3D"port", database=3D"database", = user=3D"user", password=3D"pw") >=20 >=20 >=20 > def create_table(tbl_name, col_name, col_type): >=20 > query=20 > =3D sql.SQL("CREATE TABLE {} ({} {})".format(sql.Identifier(tbl_name), = sql.Identifier(col_name), sql.Identifier(column_type))) >=20 > connection > .execute(query) >=20 >=20 > create_table > ('animals', 'name', 'VARCHAR') > and end up with a table named "animals" that contains a column "name" = of type VARCHAR. However, when I attempt to run this, I get an error: = 'type "VARCHAR" does not exist'. I assume psycopg2's built-in formatter = is putting double quotes around the VARCHAR type when there should not = be any. Normally, I would just work around this myself, but the = documentation is very clear that Python string concatenation should = never be used for fear of SQL injection attacks. Security is a concern = for this project, so I would like to know if it's possible to create = dynamically-typed tables in this fashion using pyscopg2, and if not, = whether there exists another third-party API that can do so securely.=20 >=20 > A second issue I've had is that when creating tables with a similar = methodology, the sql.Identifier() function does not perform as I expect = it to. When I use it to dynamically feed in table names, for example, I = get varying results. See below: >=20 > CREATE TABLE tbl AS SELECT * FROM other_tbl; > in raw SQL creates a table called tbl, whereas >=20 > cursor.execute(sql.SQL("CREATE TABLE {} AS SELECT * FROM = other_tbl").format(sql.Identifier(tbl)) > creates a table called "tbl". The two are different, and=20 >=20 > SELECT * FROM tbl; >=20 > returns a totally different table than >=20 > SELECT * FROM "tbl"; > Please let me know if I can fix either of these problems; I want to be = able to dynamically feed types into SQL queries, and I want the tables = created to be of the form tbl not "tbl". Thank you! >=20 > Danny=20 >=20 >=20 -- -- Christophe Pettus xof@thebuild.com