2012-11-08 20 views
2

我有一些問題,設計出使用psycopg2庫的說明書中所描述here構建SQL動態查詢使用好轉換型工具

我想建立等於該字符串動態查詢一個很好的算法:

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() 
+0

什麼是從你的程序生成的查詢文本?什麼是錯誤信息? –

+0

我更新了簡單的例子和​​錯誤跟蹤:) – reyman64

回答

4

您正試圖傳遞一個表名作爲參數。如果你只是看了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的另一個參數一起用作查詢參數。

我沒有測試過這個,但它應該給你正確的想法並幫助解釋什麼是錯的。

BE EXTEMELY CAREFUL這樣做字符串插值時,這是一個簡單的途徑SQL injection。我在上面顯示的代碼中做了非常粗略的引用,但是如果你的客戶端庫提供了一個引用函數,我想要使用正確的標識符引用函數。

+0

謝謝!根據您的回答,我使用完整的工作解決方案/程序更新我的問題並創建數據庫。 – reyman64

1

正確的方法是使用psycopg2 2.7的新sql module其中包括一個Identifier對象。這允許您以安全的方式動態指定SQL標識符。

不幸的是,2.7還沒有在PyPi上(2.6.2截至撰寫)。

在此之前,psycopg2在「如何將字段/表名稱傳遞給查詢?」標題下覆蓋此內容。 http://initd.org/psycopg/docs/faq.html#problems-with-type-conversions

通過使用AsIs函數,可以將SQL標識符和數據值一起傳遞到execute函數。

注意:這提供了沒有安全。它和使用格式字符串一樣好,這是不推薦的。 這個唯一真正的優點是你鼓勵未來的代碼遵循execute +數據風格。您以後也可以輕鬆搜索AsIs

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

既然2.7是在PyPi這裏是一個動態查詢的例子。

在這個例子中,我將假設多邊形爲csv文件中的字典。如上所述,鍵可以是名稱,url,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