Pythonでpg8000を使用してPostgreSQLに接続&Lambdaで使う

# 概要
PythonでPostgreSQLで接続するには`psycopg2`がよく使われますが
AWSのLambdaにserverless frameworkを使用してデプロイして実行する際にpsycopg2でハマってしまったので`Pure Python`の`pg8000`を使用しました

https://github.com/mfenniak/pg8000

# インストール
```
pip install pg8000
```

# 接続

```python
import pg8000

conn = pg8000.connect(
    user='<user>',
    host='<host>',
    database='<db>',
    password='<password>'
)

# こんな感じもok
conf = {
    user='<user>',
    host='<host>',
    database='<db>',
    password='<password>'
}
conn = pg8000.connect(**conf)
```


# SELECT

とりあえずテーブルはこんな感じで

|id|name|age|from|language|
|---|---|---|---|---|
|1|shimizu|23|nagano|python|
|2|kondo|22|kanagawa|java|
|3|saito|23|kanagawa|javascript|

```python
cur = conn.cursor()

sql = 'SELECT * FROM user'

cur.execute(sql)
results = cur.fetchall()

for row in results:
    print(row)
'''
[1, 'shimizu', 23, 'nagano', 'python']
[2, 'kondo', 22, 'kanagawa', 'java']
[3, 'saito', 23, 'kanagawa', 'javascript']
'''

cur.close()
conn.close()
```
クエリの実行結果は`list`を格納した`tuple`として渡されます

# INSERT
```python
cur = conn.cursor()

kojima = (4, 'kojima', 23, 'aichi', 'python')
users = (
    (5, 'kobayashi', 24, 'kanagawa', 'java'),
    (5, 'maeda', 24, 'tokyo', 'swift'),
)

sql = '''
INSERT INTO user(id, name, age, from, language)
    VALUES(%s, %s, %s, %s, %s)
'''

try:
    cur.execute(sql, kojima)
    cur.executemany(sql, users)
    conn.commit()
except Exception as e:
    print(e)

cur.close()
conn.close()
```
プレースホルダ(`%s`)を利用して`tuple`をINSERTしています
ちなみに以下のようにするとオートコミットになります

```python
conn.autocommit = True
```

# プレースホルダの種類
`execute()`する前に以下のように設定すると、プレースホルダのパラメータスタイルを変更できます。

```python
pg8000.paramstyle = 'named'
```

## format(デフォルト): `%s`
おそらく何も設定していない状態だと`%s`が適用されます
`execute()`の第2引数の`tuple`から順番に適用されます

## qmark: `?`
`%s`が`?`に変わっただけです

```python
sql = '''
INSERT INTO user(id, name, age, from, language)
    VALUES(?, ?, ?, ?, ?)
'''

try:
    pg8000.paramstyle = 'qmark'
    cur.execute(sql, (4, 'kojima', 23, 'aichi', 'python'))
    conn.commit()
except Exception as e:
    print(e)
```

## named: `:hoge`
phpの`pdo`を使っている人は馴染み深いかなと思います

`execute()`の第2引数として`dict`を渡すことができます
`:<dictのkey名>`とdictのkeyが対応して、keyに紐づいたvalueが適応されます

```python
sql = '''
INSERT INTO user(id, name, age, from, language)
    VALUES(:id, :name, :age, :from, :language)
'''

user = {
    'id': 4,
    'name': 'kojima',
    'age': 23,
    'from': 'aichi',
    'language': 'python'
}

try:
    pg8000.paramstyle = 'named'
    cur.execute(sql, user)
    conn.commit()
except Exception as e:
    print(e)
```

## pyformat: `%(hoge)s`
`:named`とそんなに変わらないです


```python
sql = '''
INSERT INTO user(id, name, age, from, language)
    VALUES(%(id)s, %(name)s, %(age)s, %(from)s, %(language)s)
'''

user = {
    'id': 4,
    'name': 'kojima',
    'age': 23,
    'from': 'aichi',
    'language': 'python'
}

try:
    pg8000.paramstyle = 'pyformat'
    cur.execute(sql, user)
    conn.commit()
except Exception as e:
    print(e)
```

## その他
`paramstyle`は上記で挙げた以外にもあります [参考(github)](paramstyle)
もちろん`execute()`だけでなく`executemany()`にも対応しています

# UPSERT
今回記事を書いたきっかけとして`UPSERT`をしたかったので`UPSERT`も載せときます

```python
sql = '''
INSERT INTO user(id, name, age, from, language)
    VALUES(:id, :name, :age, :from, :language)
    ON CONFLICT ON CONSTRAINT user_pkey 
    DO UPDATE SET
        id=:id, name=:name, age=:age, from=:from, language=:language
'''
```

# serverless frameworkについて
今回はパッケージ管理は`pipenv`を使用しています

```
pipenv install pg8000
```
`pipenv`で作成した`Pipfile`を一緒にデプロイするツールとして[serverless-python-requirements](https://github.com/UnitedIncome/serverless-python-requirements)を使用しています

# 最後に
今回は`pg8000`を使用しましたが、`serverless-python-requirements`の`dockerizePip: non-linux`を使用すればAWSのLambda用にDockerイメージからビルドしてくれるようなので、`psycopg2`使用できるかもしれません