Получить postgres типы столбцов из курсора psycopg2 (или более эффективно отобразить postgres oids на типы) - PullRequest
0 голосов
/ 01 февраля 2020

Итак, я пишу скрипт, который запускает несколько различных postgres sql запросов, хранящихся в файлах, и записывает результаты (с некоторыми дополнительными вычислениями) обратно в выходную таблицу. Поскольку запросы могут быть добавлены или изменены для включения большего числа столбцов, сценарий попытается изменить выходную таблицу для включения соответствующих новых столбцов.

Я знаю, что могу извлечь postgres oid для столбцов в запросе, используя описание курсора, а затем выполнить запрос к postgres, чтобы преобразовать oid в строковую форму postgres тип данных:

colnames = [desc[0] for desc in cur.description]
coloids = [desc[1] for desc in cur.description]
coltypes = [get_pg_type_from_oid(x) for x in coloids]
...
oid_catalog = {
    20:  'BIGINT',
    23 : 'INTEGER',
    1043: 'TEXT',
    1700: 'REAL',
}
def get_pg_type_from_oid(oid):
    if oid in oid_catalog:
        log.debug("converting oid type {} into {}".format(oid, oid_catalog[oid]))
        return oid_catalog[oid]
    q = "select {}::oid::regtype".format(oid)
    with conn, conn.cursor() as cur:
        cur.execute(q)
        newtype = cur.fetchone()[0]
        log.debug("converting oid type {} into {}".format(oid, newtype))
        oid_catalog[oid] = newtype
        return newtype

Однако строки типа данных, возвращаемые моим get_pg_type_from_oid, по-видимому, имеют другой формат, чем те, которые я обычно использовал бы для создания новой таблицы или добавления столбцов (например, «изменяющийся символ» "вместо" VARCHAR (X) "и т. д.).

Есть ли более простой способ получить это отображение? Я только начал вручную жестко задавать c сопоставления с сохраненным oid_catalog, где мне нужно, но мне бы хотелось более общее и элегантное решение.

Кстати, есть ли способ получить результаты из cursor.fetchall () представляют себя в виде строки с необходимым форматированием для прямого включения в запрос?

В настоящее время я делаю это вручную (см. код ниже), но моя интуиция заключается в том, что должно быть лучший способ.

def stringify(l):
    newl = []
    for x in l:
        if isinstance(x,float) or isinstance(x,Decimal):
            newl.append("{0:.2f}".format(round(float(x),2)))
        elif isinstance(x, datetime.datetime):
            newl.append("(TIMESTAMP '{}')".format(x.isoformat()))
        elif isinstance(x, datetime.date):
            newl.append("'{}'".format(str(x)))
        elif type(x) is str:
            newl.append("'"+x+"'")
        else:  # type(x) == 'int':
            #log.info("type else used for {}".format(x))
            newl.append(str(x))
    #log.debug("new string = {}".format(newl))
    return list(newl)

1 Ответ

0 голосов
/ 01 февраля 2020
  • character varying является совершенно допустимым типом:
$ psql

=# select pg_typeof(''::varchar);
     pg_typeof     
-------------------
 character varying

=# select 1::character varying(10);
 varchar 
---------
 1

однако вы можете получить другие имена (которые могут быть каноническими) из таблицы pg_type, используя oid скрытое поле.

=# select pg_typeof(''::varchar)::oid;
 pg_typeof 
-----------
      1043

=# select typname from pg_type where oid = pg_typeof(''::varchar)::oid;
 typname 
---------
 varchar
...