postgres x psycopg2 踩坑:連接中文名稱數(shù)據(jù)庫

-1 為什么要強(qiáng)調(diào)「在 Mac OS X」下

據(jù)江湖傳言,在 Linux 下沒有這個問題……沒想到 Mac 也會入這種坑,大跌眼鏡

0 背景

目前 Python 社區(qū)中最流行的用于和 Postgres 打交道的模塊非 psycopg2 莫屬了,連 Postgres 官方 wiki 都在介紹該模塊

猜:psycopg2 = Python + SYstem + COnnection + PostGres + 2

順便安利一下好友珂皓專門寫的一個中間層 pgdb 用它來和 Postgres 打交道可以省點(diǎn)力氣。

為了方便配置,我在連接數(shù)據(jù)庫一般是在配置文件中這么寫:

[conn01_name]
database = conn01_name
host = 192.168.0.123
port = 5678
user = user_sssj
password = 

然后在需要連接數(shù)據(jù)庫的文件中這么寫

# db.py
import pgdb

conn_01_name = pgdb.Connection(
    database=config.get('conn_01_name', 'database'),
    user=config.get('conn_01_name', 'user'),
    password=config.get('conn_01_name', 'password'),
    host=config.get('conn_01_name', 'host'),
    port=config.get('conn_01_name', 'port')
)

當(dāng)數(shù)據(jù)庫名即 conn_01_name 為英文名如 zoo 時,這樣做沒什么問題。問題就在數(shù)據(jù)庫名是中文的時候:

下面是在交互式shell中。這里使用的數(shù)據(jù)庫名為:大象系統(tǒng)

>>> from db import conn_01_name
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/path/to/db.py", line 52, in <module>
    port=config.get('conn_01_name', 'port')
  File "/path/to/py35venv/lib/python3.5/site-packages/pgdb/pgdb.py", line 28, in __init__
    self._reconnect()
  File "/path/to/py35venv/lib/python3.5/site-packages/pgdb/pgdb.py", line 63, in _reconnect
    self.connection = psycopg2.connect(*self.db_args, **self.db_kwargs)
  File "/path/to/py35venv/lib/python3.5/site-packages/psycopg2/__init__.py", line 129, in connect
    dsn = _ext.make_dsn(dsn, **kwargs)
  File "/path/to/py35venv/lib/python3.5/site-packages/psycopg2/extensions.py", line 177, in make_dsn
    parse_dsn(dsn)
psycopg2.ProgrammingError: invalid dsn: missing "=" after "大象系統(tǒng)" in connection info string

報錯啦!

01 嘗試

作為一個面向 StackOverFlow 和面向 Google 編程的程序猿,我當(dāng)然是把錯誤碼(最下方那行)復(fù)制起來丟到 Google 上去搜索。可惜無論我如何修改英文關(guān)鍵字,結(jié)果寥寥。遂想:

這類坑可能也只有我們中文圈比較有可能趟上吧……?

干脆加點(diǎn)中文關(guān)鍵字,結(jié)果依舊少得可憐……嗯其實(shí)是幾乎就沒有結(jié)果,不過好歹找到一篇中文博客,大意是說這可能是編碼類型造成的?唔這我之前也有想過,但怎么設(shè)置呢?是哪里的編碼類型?文章中說的是要去設(shè)置 connection.extensions.set_client_encoding,顧名思義是客戶端的編碼類型,設(shè)為 "UTF8" 即可。

我當(dāng)時就像看到了救命稻草,也沒再仔細(xì)看,嘗試了一下

conn_01_name = pgdb.Connection(
    database=config.get('conn_01_name', 'database'),
    user=config.get('conn_01_name', 'user'),
    password=config.get('conn_01_name', 'password'),
    host=config.get('conn_01_name', 'host'),
    port=config.get('conn_01_name', 'port')
)
conn_01_name.connection.set_client_encoding('UTF-8')

(注:這里用 Pgdb 實(shí)驗(yàn)其實(shí)沒有太大影響,因?yàn)?Connection 用于包裝的中間層非常簡單,見下可知)

# pgdb.py
import psycopg2

class Connection:
      def __init__(self, *args, **kwargs):
          self.connection = None
          self.db_args = args
          self.db_kwargs = kwargs
          self._reconnect()
      
      def _reconnect(self):
          self._close()
          self.connection = psycopg2.connect(*self.db_args, **self.db_kwargs)
          self.autocommit = True

呃,還是連不上。我想了一下就知道自己思路不對:

是因?yàn)槟莻€方法要執(zhí)行,必須要建立連接【后】才有用?而我的連接都還沒建立就報錯了,自然無法到執(zhí)行 set_client_encoding 的那步就報錯了

要不我能不能看看建立連接【時】有沒有相應(yīng)參數(shù)來初始化?又掃了一遍那篇中文博客,文中確實(shí)提到的是「取數(shù)據(jù)時」,情況和我不同,也側(cè)面驗(yàn)證了一下我的猜測。查了一下官方文檔,是有這個參數(shù)的:即在初始化 psycopg2.connect 類時,可使用 client_encoding 來指定編碼類型

client_encoding

This sets the client_encoding configuration parameter for this connection. In addition to the values accepted by the corresponding server option, you can use auto to determine the right encoding from the current locale in the client (LC_CTYPE environment variable on Unix systems).

(注意到下面這份代碼直接改用 psycopg2 來連接了,這樣比 pgdb 實(shí)驗(yàn)更快一點(diǎn))

import psycopg2
c = psycopg2.connect(dbname="大象系統(tǒng)", user="ssj", password="",
        port="5678", host="192.168.0.123", client_encoding="UTF-8")

結(jié)果也是失敗的:仍然和上面是同樣的報錯。我甚至考慮了會不會是參數(shù)的位置導(dǎo)致的?當(dāng)然事實(shí)證明也沒有用。

02 轉(zhuǎn)機(jī)

不死心,繼續(xù)找答案,一邊看文檔。我想了一下,報錯一直說 dsn:

psycopg2.ProgrammingError: invalid dsn: missing "=" after "大象系統(tǒng)"
in connection info string

上面強(qiáng)行換行,方便看

dsn是啥?可能不是很重要,但是上面這,以及剛才在看官方文檔時的線索似乎給了我提示:(先看文檔,我當(dāng)時注意到了這部分)

# psycopg2 初始化連接的 2 種方式
conn = psycopg2.connect("dbname=test user=postgres password=secret")

# 或者
conn = psycopg2.connect(dbname="test", user="postgres", password="secret")

結(jié)合報錯提示,它是說實(shí)際上 psycopg2 的解析過程是把關(guān)鍵字參數(shù)轉(zhuǎn)換成字符串什么的一種叫 dsn 的東西,然后用 parse_dsn (見上面的報錯提示)解析時報錯是嗎?于是我嘗試重新連接:

import psycopg2
conn = psycopg2.connect("dbname=大象系統(tǒng) user=ssj password='' port=5678 host=192.168.0.123")

仍是失敗的。與此同時,我看到了一個提示

import psycopg2
conn_string = "host='localhost' dbname='my_database' user='postgres' password='secret'"
conn = psycopg2.connect(conn_string)

啊!引號嵌套,我可能就是少了這個。那么其實(shí)我可以這樣寫

import psycopg2
conn = psycopg2.connect("dbname='%(dbname)s' user='%(user)s' password='%(password)s'\
port='%(port)s' host='%(host)s'"
% {'dbname': '大象系統(tǒng)', 'user': 'ssj', 'password': '', 'port': '5678',
   'host': '192.168.0.123'})

這樣嘗試之后就成功了。對應(yīng)的 pgdb 寫法是

import pgdb
conn = pgdb.Connection("dbname='%(dbname)s' user='%(user)s' password='%(password)s'\
port='%(port)s' host='%(host)s'"
% {'dbname': '大象系統(tǒng)', 'user': 'ssj', 'password': '', 'port': '5678',
   'host': '192.168.0.123'})

03 尾聲和風(fēng)波

終于能夠成功連接中文名稱的數(shù)據(jù)庫了,別提有多高興。這當(dāng)然馬上要查詢一下某些表看看效果啦:

>>> from db import conn
>>> sql = "SELECT animalcode FROM 動物園"
>>> ret = conn.query(sql,)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/path/to/py35venv/lib/python3.5/site-packages/pgdb/pgdb.py", line 76, in query
    raise e
  File "/path/to/py35venv/lib/python3.5/site-packages/pgdb/pgdb.py", line 69, in query
    cursor.execute(*args, **kwargs)
psycopg2.ProgrammingError: relation "動物園" does not exist
LINE 1: SELECT animalcode FROM 動物園

啥?這也行?我還以為是中文表名又出錯了。嘗試了一些英文表名,結(jié)果也是 relation xxx does not exist

難道是我被「騙」了嗎?我沒有實(shí)際建立連接但繞開了報錯?于是我用了 conn_zoo.ensure_connected 和 cursor 等方法查詢了一下,好像是有建立起來呀……我索性用同樣的連接法(字符串初始化連接法)去嘗試連接其他數(shù)據(jù)庫(非中文名),連上了且成功查詢到其他英文表名的記錄。

這說明我這種初始化方法應(yīng)該是沒有大問題的。但到底是哪里出錯我一下子也想不起來。后來我看了一下配置文件,突然想到這兩天配服務(wù)器時看各種文檔,專門說到一些數(shù)據(jù)庫的端口值會設(shè)為非默認(rèn)值(有時候可能是為了安全,有時候是為了方便隧道訪問什么的)——會不會是端口值錯了?啊!那么,可能用戶名之類的也有問題。

然后我去核對了 DataGrip 中可以正確連接數(shù)據(jù)庫的配置,和我寫到程序配置文件中的數(shù)據(jù)庫配置……就知道是我大意了:確實(shí)是配置文件寫錯了。

改完配置文件后,就能正常連接中文名稱的數(shù)據(jù)庫,查詢 SQL 語句啦!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,247評論 6 543
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,520評論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,362評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,805評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,541評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,896評論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,887評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,062評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,608評論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,356評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,555評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,077評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,769評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,175評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,489評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,289評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,516評論 2 379