【问题标题】:Python - Postgresql Invalid Byte Sequence for varchar - Weird BehaviorPython - Postgresql varchar 的无效字节序列 - 奇怪的行为
【发布时间】:2026-02-15 01:30:01
【问题描述】:

我编写了一个使用 python 线程函数的脚本。我认为问题与线程有关,因为当我从工作线程外部运行查询时,它工作正常。我正在尝试将一些东西插入数据库,但遇到了一些非常时髦的行为。

让我简化一下:

运行这个工程:

cmd = "INSERT INTO cstanley_temp (device, host, ux, units) VALUES (%s, %s, %s, %s);"
data = ("solaris-cdc", resultHOST[0], "UX10", 1,)
sql.execute(cmd, data)

运行这个不起作用:

cmd = "INSERT INTO cstanley_temp (device, host, ux, units) VALUES (%s, %s, %s, %s);"
data = ("solaris-cdc", resultHOST[0], "sdsdsdsdsdsd", 1,)
sql.execute(cmd, data)

以下是字段类型: device = varchar host = varchar ux = varchar units = int

这是我收到的错误:

Exception in thread Thread-1:
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/threading.py", line 801, in __bootstrap_inner
    self.run()
  File "/usr/local/lib/python2.7/threading.py", line 754, in run
    self.__target(*self.__args, **self.__kwargs)
  File "/home/cstanley/scripts/vip/sun_audit.py", line 37, in workon
    sql.execute(cmd, data)
DataError: invalid byte sequence for encoding "UTF8": 0x86
HINT:  This error can also happen if the byte sequence does not match the encoding expected by the server, which is controlled by "client_encoding".

Exception in thread Thread-2:
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/threading.py", line 801, in __bootstrap_inner
    self.run()
  File "/usr/local/lib/python2.7/threading.py", line 754, in run
    self.__target(*self.__args, **self.__kwargs)
  File "/home/cstanley/scripts/vip/sun_audit.py", line 37, in workon
    sql.execute(cmd, data)
InternalError: current transaction is aborted, commands ignored until end of transaction block

这里是完整的代码:

#!/usr/local/bin/python2.7
import sys, os, string, threading
import paramiko
import psycopg2
import time

#paramiko.util.log_to_file("sun_audit.log")

getCPU = "/usr/sbin/psrinfo -p"
getMEM = "/usr/sbin/prtconf | grep \"Memory\" | awk '{ print $3 }'"
getHOST = "hostname"

class bcolors:
    MAGENTA = '\033[95m'
    YELLOW = '\033[93m'
    ENDC = '\033[0m'

def workon(host,sql):

    #Connect to each host
    ssh = paramiko.SSHClient()
    key = paramiko.RSAKey.from_private_key_file("/home/cstanley/scripts/vip/cstanley")
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(host, username='cstanley', pkey=key)

    #Run Commands
    stdinHOST, stdoutHOST, stderrHOST = ssh.exec_command(getHOST)
    stdinCPU, stdoutCPU, stderrCPU = ssh.exec_command(getCPU)
    stdinMEM, stdoutMEM, stderrMEM = ssh.exec_command(getMEM)

    with threading.Lock():

        resultHOST = stdoutHOST.readlines()
        #print "{0} {0} UX10 1".format(resultHOST[0].rstrip())
        cmd = "INSERT INTO cstanley_temp (device, host, ux, units) VALUES (%s, %s, %s, %s);"
        data = ("solaris-cdc", resultHOST[0], "sdsdsdsdsdsd", 1,)
        sql.execute(cmd, data)

        resultCPU = stdoutCPU.readlines()
        ux40 = (int(resultCPU[0].rstrip()) - 1)
        if ux40 != 0:
            #print "{0} {0} UX40 {1}".format(resultHOST[0].rstrip(),ux40)
            cmd = "INSERT INTO cstanley_temp (device, host, ux, units) VALUES (%s, %s, %s, %s);"
            data = ("solaris-cdc", resultHOST[0], "UX40", ux40,)
            sql.execute(cmd, data)

        resultMEM = stdoutMEM.readlines()
        ux30 = (int(resultMEM[0].rstrip()) / 1024 - 2) / 2
        #print "{0} {0} UX30 {1}".format(resultHOST[0].rstrip(),ux30)
        cmd = "INSERT INTO cstanley_temp (device, host, ux, units) VALUES (%s, %s, %s, %s);"
        data = ("solaris-cdc", resultHOST[0], "UX30", ux30,)
        sql.execute(cmd, data)

        ssh.close()

def main():

    #date = (time.strftime("%Y-%m-%d"))

    #Define our connection string
    conn_string = "host='REMOVED' dbname='REMOVED' user='REMOVED' password='REMOVED' connect_timeout=3"

    # print the connection string we will use to connect
    #print bcolors.MAGENTA + 'Connecting to database\n    ->%s' % (conn_string) + bcolors.ENDC + "\n"

    # get a connection, if a connect cannot be made an exception will be raised here
    conn = psycopg2.connect(conn_string)

    # conn.cursor will return a cursor object, you can use this cursor to perform queries
    sql = conn.cursor()
    print bcolors.YELLOW + "Inserting Solaris information into table.\n" + bcolors.ENDC

    with open('/home/cstanley/scripts/vip/sun_ip') as ip:
        hosts = ip.read().splitlines()

    threads = []
    for h in hosts:
        t = threading.Thread(target=workon, args=(h,sql,))
        t.start()
        threads.append(t)
    for t in threads:
        t.join()

    conn.commit()
    sql.close()
    conn.close()

if __name__ == "__main__":
    main()

试图弄清楚这里发生了什么。为什么当我输入UX10 时它可以工作,而当我输入sdsdsdsdsdsd 时它不起作用?我什至尝试用solaris-cdc 替换它,就像它在查询的第一部分一样,但这也失败了。这到底是怎么回事!?

【问题讨论】:

    标签: python postgresql


    【解决方案1】:

    错误似乎是在说:当 PostgreSQL 期望给出正确的 UTF-8 编码的 unicode 时,您正试图将一些带有二进制的 Python 2 strs 插入 varchar 列。要么它间歇性地来自resultHOST[0],要么你的代码中的"sdsdsdsdsdsd" 字符串中有一些不可见的控制字符:

    >>> print u"here be ma\x86gic"
    here be magic
    

    但真正的原因可能是根据psycopg2 documentation

    游标不是线程安全的:多线程应用程序可以从同一个连接创建多个游标,并且应该使用单个线程中的每个游标。详情请见Thread and process safety

    因此,您应该在每个线程上创建一个新光标。不要将sql 作为参数传递给workon,只需在workon 方法中使用语句创建一个新游标

    sql = conn.cursor()
    

    此外,应该不需要锁定,因为使用具有多个游标的单个连接是线程安全的。

    【讨论】:

    • “UX10”如何正常而“sdsdsdsdsd”不正常?此外,“solaris-cdc”如何在一个字段上正常,而另一个字段“solaris-cdc”不起作用?这两个字段的类型完全相同。显然这是问题所在,但解决方案是什么?在我看来,线程以某种方式将二进制数据放在不应该的地方。当字段为“UX10”与“sdsdsdsdsd”时,您是否看到运行两个 EXACT 查询会导致不同的问题 - 问题很明显,是的 postgres 期待 UTF-8 并接收其他内容。这不是我的问题 - 我的问题是为什么我的脚本发送二进制文件。
    • 我手动输入了 sdsdsdsdsd - 当它改回 UX10 时它可以工作。因此,它来自 resultHOST[0] 我希望当字段更改回UX10 时它仍然会抛出错误
    • 你能得到这个的 MVCE 吗?这个问题肯定没有一个最小、完整和可验证的例子——远非如此
    • 问题在于代码的原样。当我尝试制作 MVCE(尽可能多地剥离)时,它可以工作。也许它与从连接到服务器(通过 ssh)获得的结果有关。我无论如何都不是开发人员,所以我的代码写得不好。我在执行之前将 SQL 语句写入文件并得到了我的预期:INSERT INTO cstanley_temp (device, host, ux, units) VALUES (solaris-cdc, REMOVED , solaris-cdc, 1);INSERT INTO cstanley_temp (device, host, ux, units) VALUES (solaris-cdc, REMOVED , solaris-cdc, 1);
    • 或者它可能与与单独的线程共享单个光标有关。如果你只有 1 个线程在运行,你能复制它吗?顺便说一句,with threading.Lock(): 也不正确 - 它创建一个新锁,将其锁定(立即成功,因为这是它唯一见过的地方),然后将其丢弃