建立与psycopg2 Python库,并使用良好的转化型工具SQL动态查询(build SQL d

2019-07-03 16:18发布

我有一些问题,设计出使用说明psycopg2库的规范一个好的算法在这里

我想建立等于该字符串动态查询:

SELECT ST_GeomFromText('POLYGON((0.0 0.0,20.0 0.0,20.0 20.0,0.0 20.0,0.0 0.0))');

正如你所看到的,我的Polygon对象包含多个点,其中包含一个简单的CSV文件some.csv阅读:

0.0;0.0
20.0;0.0
20.0;20.0
0.0;20.0
0.0;0.0

所以我构建查询动态,线路/数据的在CSV数的函数。

在这里我的程序生成的SQL查询字符串来执行:

import psycopg2
import csv 

# list of points
lXy = []

DSN= "dbname='testS' user='postgres' password='postgres' host='localhost'"
conn = psycopg2.connect(DSN)

curs = conn.cursor()

def genPointText(curs,x,y):
    generatedPoint = "%s %s" % (x,y)
    return generatedPoint

#Lecture fichier csv
polygonFile = open('some.csv', 'rb')
readerCSV = csv.reader(polygonFile,delimiter = ';')

for coordinates in readerCSV:
    lXy.append(genPointText(curs,float(coordinates[0]),float(coordinates[1])))

# function of list concatenation by separator
def convert(myList,separator):
    return separator.join([str(i) for i in myList])

# construct simple query with psycopg
def genPolygonText(curs,l):
    # http://initd.org/psycopg/docs/usage.html#python-types-adaptation
    generatedPolygon = "POLYGON((%s))" % convert(l, ",")
    return generatedPolygon

def executeWKT(curs,geomObject,srid):
    try:
            # geometry ST_GeomFromText(text WKT, integer srid);
        finalWKT = "SELECT ST_GeomFromText('%s');" % (geomObject) 
        print finalWKT
        curs.execute(finalWKT)
    except psycopg2.ProgrammingError,err:
        print "ERROR = " , err

polygonQuery = genPolygonText(curs,lXy)
executeWKT(curs,polygonQuery,4326)

正如你所看到的,这是工作,但这种方式是不是因为Python对象和SQL PostgreSQL的对象之间的转换问题是正确的。

在文档中,我只看到例如饲料和数据转换为静态查询。 你知道一个“优雅”的方式在动态建立查询以创建正确的字符串与正确的类型?

更新1:

正如你所看到的,当我在这个简单的例子使用psycopg类型的转换功能,我有这样的错误:

query = "ST_GeomFromText('POLYGON(( 52.146542 19.050557, 52.148430 19.045527, 52.149525 19.045831, 52.147400 19.050780, 52.147400 19.050780, 52.146542 19.050557))',4326)"
name = "my_table"

try:
    curs.execute('INSERT INTO %s(name, url, id, point_geom, poly_geom) VALUES (%s);', (name,query))
except psycopg2.ProgrammingError,err:
    print "ERROR = " , err

错误等于:

ERROR =  ERREUR:  erreur de syntaxe sur ou près de « E'my_table' »
LINE 1: INSERT INTO E'my_table'(name, poly_geom) VALUES (E'ST_GeomFr...

更新2:

最终的代码工作,感谢StackOverflow的用户!

#info lib : http://www.initd.org/psycopg/docs/
import psycopg2
# info lib : http://docs.python.org/2/library/csv.html
import csv 

# list of points
lXy = []

DSN= "dbname='testS' user='postgres' password='postgres' host='localhost'"

print "Opening connection using dns:", DSN
conn = psycopg2.connect(DSN)

curs = conn.cursor()

def genPointText(curs,x,y):
    generatedPoint = "%s %s" % (x,y)
    return generatedPoint

#Lecture fichier csv
polygonFile = open('some.csv', 'rb')
readerCSV = csv.reader(polygonFile,delimiter = ';')

for coordinates in readerCSV:
    lXy.append(genPointText(curs,float(coordinates[0]),float(coordinates[1])))

# function of list concatenation by separator
def convert(myList,separator):
    return separator.join([str(i) for i in myList])

# construct simple query with psycopg
def genPolygonText(l):
    # http://initd.org/psycopg/docs/usage.html#python-types-adaptation
    generatedPolygon = "POLYGON((%s))" % convert(l, ",")
    return generatedPolygon

def generateInsert(curs,tableName,name,geomObject):
    curs.execute('INSERT INTO binome1(name,geom) VALUES (%s, %s);' , (name,geomObject))


def create_db_binome(conn,name):

    curs = conn.cursor()

    SQL = (
        "CREATE TABLE %s"
        " ("
        " polyname character varying(15),"
        " geom geometry,"
        " id serial NOT NULL,"
        " CONSTRAINT id_key PRIMARY KEY (id)"
        " )" 
        " WITH ("
        " OIDS=FALSE"
        " );"
        " ALTER TABLE %s OWNER TO postgres;"
        ) %(name,name)
    try:
      #print SQL
      curs.execute(SQL)

    except psycopg2.ProgrammingError,err:
      conn.rollback()
      dropQuery = "ALTER TABLE %s DROP CONSTRAINT id_key; DROP TABLE %s;" % (name,name)
      curs.execute(dropQuery)
      curs.execute(SQL)

    conn.commit()

def insert_geometry(polyname,tablename,geometry):

    escaped_name = tablename.replace('""','""')

    try:
        test = 'INSERT INTO %s(polyname, geom) VALUES(%%s, ST_GeomFromText(%%s,%%s))' % (escaped_name)
        curs.execute(test, (tablename, geometry, 4326))
        conn.commit()
    except psycopg2.ProgrammingError,err:
        print "ERROR = " , err

################
# PROGRAM MAIN #
################

polygonQuery = genPolygonText(lXy)
srid = 4326
table = "binome1"

create_db_binome(conn,table)
insert_geometry("Berlin",table,polygonQuery)
insert_geometry("Paris",table,polygonQuery)

polygonFile.close()
conn.close()

Answer 1:

您正在试图通过一个表名作为参数。 你可能如果你只是看着PostgreSQL的错误日志可能已经立即看到了这一点。

你想通过psycopg2作为参数表名被转义,产生类似的查询:

INSERT INTO E'my_table'(name, url, id, point_geom, poly_geom) VALUES (E'ST_GeomFromText(''POLYGON(( 52.146542 19.050557, 52.148430 19.045527, 52.149525 19.045831, 52.147400 19.050780, 52.147400 19.050780, 52.146542 19.050557))'',4326)');'

这是不是你所预期的,并且将无法正常工作; 你无法逃避像文字表名。 您必须使用正常的Python字符串插值来构建动态SQL,你只能使用参数化的语句占位符的实际文本值。

params = ('POLYGON(( 52.146542 19.050557, 52.148430 19.045527, 52.149525 19.045831, 52.147400 19.050780, 52.147400 19.050780, 52.146542 19.050557))',4326)
escaped_name = name.replace('"",'""')
curs.execute('INSERT INTO "%s"(name, url, id, point_geom, poly_geom) VALUES (ST_GeomFromText(%%s,%%s));' % escaped_name, params)

怎么看我插的名义直接生成查询字符串:

INSERT INTO my_table(name, url, id, point_geom, poly_geom) VALUES (ST_GeomFromText(%s,%s));

%%被转换为纯%由%取代)。 然后,我使用的是定义该字符串查询POLYGON和其他参数ST_GeomFromText作为查询参数。

我没有测试过这一点,但它应该给你正确的想法,并有助于解释什么是错的。

做字符串插值像这样时候要EXTEMELY小心 ,这是一种简单的途径SQL注入 。 我已经做了非常粗略上面所示的代码引用,但我想用正确的ID引用功能,如果您的客户端库提供了一个。



Answer 2:

现在2.7是PyPI上这里是一个动态查询的例子。

在这个例子中,我假定多边形从您的CSV文件的字典。 按键可以像上面提到的名称,网址,ID,point_geom,poly_geom但他们不会真的不管,只要表结构包含相同的密钥。

有可能的方式来缩短这个,但我希望这澄清了使用的SQL函数,即sql.SQLsql.Identifiersql.Placeholder以及如何连接字符串列表sql.SQL('..').join(list())

from psycopg2 import sql
table = 'my_table'
polygon = Polyogon.from_file()  # or something
column_list = list()
value_list = list()

# Convert the dictionary to lists
for column, value in polygon.items():
    column_list.append(sql.Identifier(column))  # Convert to identifiers
    value_list.append(value)

# Build the query, values will be inserted later
query = sql.SQL("INSERT INTO {} ({}) VALUES ({}) ON CONFLICT DO NOTHING").format(
                sql.Identifier(table),
                sql.SQL(', ').join(column_list),  # already sql.Identifier
                sql.SQL(', ').join([sql.Placeholder()] * len(value_list)))

# Execute the cursor
with postgres.cursor() as p_cursor:
    # execute requires tuples and not a list
    p_cursor.execute(insert_query, tuple(value_list))  

参考: http://initd.org/psycopg/docs/sql.html



Answer 3:

正确的方法是psycopg2 2.7的新的使用sql模块 ,其中包括一个Identifier对象。 这使您可以动态地在一个安全的方式指定SQL标识符。

不幸的是2.7是不是PyPI上,但(2.6.2写作的)。

在此之前,psycopg2盖这个标题下的“我怎样才能通过现场/表名查询?” http://initd.org/psycopg/docs/faq.html#problems-with-type-conversions

你可以在沿有数据值通过使用SQL传递标识符来执行功能AsIs功能。

注意:此提供安全 。 这是因为使用格式字符串,这是不推荐为好。 这样做的唯一真正的好处是你鼓励未来的代码遵循执行+数据的风格。 您也可以轻松地搜索AsIs在未来。

from psycopg2.extensions import AsIs
<snip>
with transaction() as cur:
    # WARNING: not secure
    cur.execute('SELECT * from %(table)s', {'table': AsIs('mytable')})


文章来源: build SQL dynamic query with psycopg2 python library and using good conversion type tools