Psycopg2/PostgreSQL 11.9: Синтаксическая ошибка в или около «::» при выполнении приведения типа строки->дата

#python #sql #postgresql #psycopg2

Вопрос:

Я использую psycopg2 для создания раздела таблицы и вставки некоторых строк в этот недавно созданный раздел. Таблица разделена на диапазоны по столбцу типа даты.

Код Psycopg2:

 conn = connect_db()
cursor = conn.cursor()
sysdate = datetime.now().date()
sysdate_str = sysdate.strftime('%Y%m%d')
schema_name = "schema_name"
table_name = "transaction_log"

# Add partition if not exists for current day
sql_add_partition = sql.SQL("""
    CREATE TABLE IF NOT EXISTS {table_partition}
    PARTITION of {table}
    FOR VALUES FROM (%(sysdate)s) TO (maxvalue);
""").format(table = sql.Identifier(schema_name, table_name), table_partition = sql.Identifier(schema_name, f'{table_name}_{sysdate_str}'))
print(cursor.mogrify(sql_add_partition, {'sysdate': dt.date(2015,6,30)}))
cursor.execute(sql_add_partition, {'sysdate': sysdate})
 

Форматированный вывод курсора.mogrify():

 CREATE TABLE IF NOT EXISTS "schema_name"."transaction_log_20211001"
PARTITION of "schema_name"."transaction_log"
FOR VALUES FROM ('2021-10-01'::date) TO (maxvalue);
 

Получена ошибка:

 ERROR:  syntax error at or near "::"
LINE 3: for values FROM ('2021-10-01'::date) TO (maxvalue);
 

Интересно, что psycopg2, похоже, пытается привести строку ‘2021-10-01’ к объекту даты с синтаксисом «::дата», и, согласно документации PostgreSQL, это кажется допустимым (хотя в документах нет явных примеров), однако выполнение инструкции как с помощью pyscopg2, так и в редакторе запросов PostgreSQL приводит к этой синтаксической ошибке. Однако выполнение следующей инструкции в редакторе SQL PostgreSQL успешно:

 CREATE TABLE IF NOT EXISTS "schema_name"."transaction_log_20211001"
PARTITION of "schema_name"."transaction_log"
FOR VALUES FROM ('2021-10-01') TO (maxvalue);
 

Есть какие-нибудь идеи о том, как заставить psycopg2 правильно отформатировать запрос?

Комментарии:

1. Попробуйте преобразовать его в строку на Python и интерполировать ее.

2. @LaurenzAlbe Это должно сработать. Хотя мне любопытно знать, почему документы psycopg2 и документы postgresql указывают, что синтаксис допустим, несмотря на эту синтаксическую ошибку. Я пока оставлю этот вопрос открытым.

3. Синтаксис недопустим. Вам нужно указать константу, а не выражение, такое как приведение типа.

4. Причина в таблице Create : «Каждое из значений, указанных в параметре partition_bound_spec, является литеральным, НУЛЕВЫМ, МИНИМАЛЬНЫМ или МАКСИМАЛЬНЫМ ЗНАЧЕНИЕМ. Каждое литеральное значение должно быть либо числовой константой, которая сопоставима с типом соответствующего столбца ключа раздела, либо строковым литералом, который является допустимым вводом для этого типа. » Добавил это в качестве обновления к моему ответу.

Ответ №1:

Чтобы следить за комментарием @LaurenzAlbe:

 
sql_add_partition = sql.SQL("""
    CREATE TABLE IF NOT EXISTS {table_partition}
    PARTITION of {table}
    FOR VALUES FROM (%(sysdate)s) TO (maxvalue);
""").format(table = sql.Identifier(schema_name, table_name), table_partition = sql.Identifier(schema_name, f'{table_name}_{sysdate_str}'))
print(cursor.mogrify(sql_add_partition, {'sysdate': '2021-10-01'}))

#OR

sql_add_partition = sql.SQL("""
    CREATE TABLE IF NOT EXISTS {table_partition}
    PARTITION of {table}
    FOR VALUES FROM ({sysdate}) TO (maxvalue);
""").format(table = sql.Identifier(schema_name, table_name), 
table_partition = sql.Identifier(schema_name, f'{table_name}_{sysdate_str}'),
sysdate=sql.Literal('2021-10-01'))
print(cursor.mogrify(sql_add_partition))

#Formatted as

CREATE TABLE IF NOT EXISTS "schema_name"."transaction_log_20211001"
    PARTITION of "schema_name"."transaction_log"
    FOR VALUES FROM ('2021-10-01') TO (maxvalue);
 

Передайте дату в качестве буквального значения вместо объекта даты. psycopg2 выполняет автоматическую адаптацию объектов даты(времени) к типам даты/метки времени Postgres(адаптация даты и времени), что вас раздражает.

Обновить

Согласно моему комментарию, причина, по которой это должен быть литерал, объясняется здесь Создать таблицу:

Каждое из значений, указанных в параметре partition_bound_spec, является литеральным, НУЛЕВЫМ, МИНИМАЛЬНЫМ или МАКСИМАЛЬНЫМ ЗНАЧЕНИЕМ. Каждое литеральное значение должно быть либо числовой константой, приведенной к типу соответствующего столбца ключа раздела, либо строковым литералом, который является допустимым вводом для этого типа.