解析 CSV 文件,循环并将行插入 PostGreSQL 数据库

Parse a CSV file, loop and insert rows into a PostGreSQL database

我使用 Python psycopg2 模块将 csv 文件(用户列表)的内容复制到 PostGreSQL 数据库中。
所以我开始用 Python pandas 模块解析 CSV。然后使用 for 循环,我尝试将我的数据插入到我的 SQL 查询中。
我有两个问题:

a) 当我执行角色查询(查询 2 - 见下文)以在数据库中创建新角色时,我得到 'user' 而不是 用户 。我该怎么做才能使用正确的语法插入角色?
b) 查询 3 4(见下文) 给出以下错误:

TypeError: not all arguments converted during string formatting 

这个问题究竟是什么以及如何解决?

完整代码如下:

import csv, psycopg2
import pandas as pd

conn = psycopg2.connect("host=localhost dbname=vmap user=postgres password=postgres port=5432")
c = conn.cursor()
# Import_CSV
data = pd.read_csv (r'users.csv', sep=';')   
df = pd.DataFrame(data, columns= ['id','login','mdp','mail','date'])
print(df)

for row in df.itertuples():
    print (row.login)
    c.execute("INSERT INTO users (user_id, login, email) VALUES(%s, %s, %s);", (row.id, row.login, row.mail))
    # query2
    c.execute('create role "%s" with encrypted password %s',(row.login, row.mdp))
    # query3 
    c.execute('grant vitis_user, vmap_user to "%s"',(row.login))    
    # query4
    c.execute('grant connect on vmap to "%s"',(row.login))

我要解析的 DataFrame(CSV 文件的内容)是这个:

   id              login  ...                             mail        date
0  10  ldeschamps-diallo  ...  ldeschamps-diallo@monwebsig.com  2022-01-31
1  11            pmarion  ...            pmarion@monwebsig.com  2022-01-31
2  12             cleroy  ...             cleroy@monwebsig.com  2022-01-31
3  13          lcourtois  ...          lcourtois@monwebsig.com  2022-01-31
4  14      rpaul-monnier  ...      rpaul-monnier@monwebsig.com  2022-01-31

正在读取 CSV

首先,这里可能不需要pandas,因为你只需要打开csv文件并解析它的内容。使用 built-in csv 模块应该足够了。

您可以这样阅读文件:

import csv
with open("users.csv", "r", encoding="utf-8", newline="") as fid:
    reader = csv.reader(fid, delimiter=";")

正在加载

正如 Adrian Klaver 所建议的,您可以使用 .copy_from 方法加载数据,但这只会帮助您解决问题的第一部分,即插入用户。您将从查询编号 2 开始处理您现在面临的相同问题。

说明

另一件事是,psycopg2 为您提供参数化查询以保护您免受 SQL injection 的影响,但设计为在您尝试使用它时转义 'values' 时工作'identifier'。当您第一次将用户插入 table 时,用户名是一个要插入的值,一切都按预期进行。在第二个查询中,您指的是数据库中的实际用户(请注意,您使用 " 而不是 ' 来转义用户名)。因为 psycopg2 用户名是一个值,它用额外的 ' 转义它,你最终得到这样的查询:

create role "'cleroy'" with encrypted password 'password'

如果您想传递用户名,您可能需要使用字符串格式:

c.execute(f'create role "{row.login}" with encrypted password %s',(row.mdp,))

但这样一来,您就可以接受 sql 注入。想象一下有一个用户名 tom"; DROP DATABASE mysuperdatabase;。这样,您将得到查询: create role "tom"; DROP DATABASE mysuperdatabase; 并且您面临丢失宝贵数据的潜在风险。

安全的方法

所以你首先需要做的是确保标识符被正确转义。您可以使用 psycopg2.sql 模块来执行此操作。

from psycopg2 import sql
login = sql.Identifier(row.login)
query = sql.SQL(f"create role {login} with encrypted password %s")
c.execute(query, (row.mdp,))