Почему pymsql не вставляет запись в таблицу? - PullRequest
0 голосов
/ 03 ноября 2019

Я довольно новичок в разработке Python. У меня есть длинный скрипт на python, который «клонирует» базу данных и добавляет дополнительные хранимые функции и процедуры. Клонирование означает копирование только схемы БД. Эти шаги работают нормально.

Мой вопрос о pymsql вставьте исключение: мне нужно скопировать некоторое содержимое таблицы в новую БД. Я не получаю никакой ошибки sql. Если я отлаживаю или печатаю созданную команду INSERT INTO , это правильно (я проверял ее в редакторе / обработчике SQL). Выполнение вставки правильное, потому что результат содержит точный номер строки ... но все строки отсутствуют в таблице назначения в dest.DB ... (Определены переменные DB_ *!)

import pymysql

liveDbConn = pymysql.connect(DB_HOST, DB_USER, DB_PWD, LIVE_DB_NAME)
testDbConn = pymysql.connect(DB_HOST, DB_USER, DB_PWD, TEST_DB_NAME)

tablesForCopy = ['role', 'permission']
for table in tablesForCopy:
    with liveDbConn.cursor() as liveCursor:
        # Get name of columns
        liveCursor.execute("DESCRIBE `%s`;" % (table))
        columns = '';
        for column in liveCursor.fetchall():
            columns += '`' + column[0] + '`,'
        columns = columns.strip(',')

        # Get and convert values
        values = ''
        liveCursor.execute("SELECT * FROM `%s`;" % (table))
        for result in liveCursor.fetchall():
            data = []
            for item in result:
                if type(item)==type(None):
                    data.append('NULL')
                elif type(item)==type('str'):
                    data.append("'"+item+"'")
                elif type(item)==type(datetime.datetime.now()):
                    data.append("'"+str(item)+"'")
                else:  # for numeric values
                    data.append(str(item))
            v = '(' + ', '.join(data) + ')'
            values += v + ', '
    values = values.strip(', ')

    print("### table: %s" % (table))
    testDbCursor = testDbConn.cursor()
    testDbCursor.execute("INSERT INTO `" + TEST_DB_NAME + "`.`" + table + "` (" + columns + ") VALUES " + values + ";")
    print("Result: {}".format(testDbCursor._result.message))


liveDbConn.close()
testDbConn.close()

Результат:

### table: role 
Result: b"'Records: 16  Duplicates: 0  Warnings: 0"
### table: permission 
Result: b'(Records: 222  Duplicates: 0  Warnings: 0'

Что я делаю не так? Спасибо!

1 Ответ

2 голосов
/ 03 ноября 2019

У вас есть 2 основные проблемы:

  1. Вы не используете conn.commit() (здесь будет liveDbConn.commit() или testDbConn.commit()). Изменения в базе данных не будут отражены без фиксации этих изменений. Обратите внимание, что все изменения требуют фиксации, но, например, SELECT не требуется.
  2. Ваш запрос открыт для SQL-инъекции. Это серьезная проблема.

Имена таблиц не могут быть параметризованы, поэтому мы ничего не можем с этим поделать, но вы захотите параметризовать свои значения. Я сделал несколько исправлений в коде в отношении проверки типов, а также параметризации.

for table in tablesForCopy:
    with liveDbConn.cursor() as liveCursor:        
        liveCursor.execute("SELECT * FROM `%s`;" % (table))
        name_of_columns = [item[0] for item in liveCursor.description]
        insert_list = []
        for result in liveCursor.fetchall():
            data = []
            for item in result:
                if item is None: # test identity against the None singleton
                    data.append('NULL')
                elif isinstance(item, str): # Use isinstance to check type
                    data.append(item)
                elif isinstance(item, datetime.datetime):
                    data.append(item.strftime('%Y-%m-%d %H:%M:%S'))
                else:  # for numeric values
                    data.append(str(item))
            insert_list.append(data)

    testDbCursor = testDbConn.cursor()
    placeholders = ', '.join(['`%s`' for item in insert_list[0]])
    testDbCursor.executemany("INSERT INTO `{}.{}` ({}) VALUES ({})".format(
                                                                TEST_DB_NAME, 
                                                                table, 
                                                                name_of_columns, 
                                                                placeholders),
                             insert_list)
    testDbConn.commit()
...