LINUX.ORG.RU

sqlite3 и обновление схемы таблицы

 , ,


0

1

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

как может быть такое: в консоли все запросы отрабатывают без ошибок, в приложении, собственно, тоже никаких ошибок не наблюдается, но в консоли изменения видно и после перезапуска sqlite3, а в приложении - только до выхода из него, а после повторного запуска таблицы, схему которой обновляли, вообще нет, равно как и временной.

Сталкивался кто-нибудь?

И да, я уже завернул всю цепочку запросов по обновлению структуры в транзакцию

Исходный код, который можно скомпилировать и запустить (самый простой) поможет вам в разы быстрее получить ответ на ваш вопрос.

trex6 ★★★★★
()
Последнее исправление: trex6 (всего исправлений: 1)
Ответ на: комментарий от trex6

эм... который можно скомпилировать и запустить вряд ли, но куски дам:

#база данных определена так:
        self.db = QSqlDatabase.addDatabase("QSQLITE")

#так запросы:
    def _query(self, querystr, qtype = SELECT, *qparams):
        if not self.db.isOpen():
            self.initdb()
        query = QSqlQuery()
        query.prepare(querystr)
        for qp in xrange(len(qparams)):
            query.bindValue(qp, qparams[qp])
        retval = False
        if query.exec_():
            if qtype == SELECT:
                return self._select_result_generator(query)
            elif qtype == INSERT:
                retval = self.get_insert_id()
            elif qtype == DDQ:
                retval = True
            elif qtype == UPDATE or qtype == DELETE:
                retval = query.numRowsAffected()
        else:
            self.lastErrorVal = Exception(query.lastError().text())
        query.finish()
        return retval

    def get_insert_id(self):
        query = QSqlQuery()
        query.exec_("SELECT last_insert_rowid()")
        query.next()
        val = query.value(0)
        query.finish()
        return val

    def _select_result_generator(self, queryObj):
        while queryObj.next():
            yield queryObj.record()
        queryObj.finish()

#кусок, который проводит обновление:
        res = self.db._query("SELECT value FROM dbversion LIMIT 1", SELECT)
        if not res:
            current_version = 0
        else:
            for r in res:
                current_version = int(r.value('value'))
        if self.db.begin():
            for update in DBUpdate.VERSIONQUERIES[current_version:]:
                for q in update:
                    self.db._query(*q)
            self.db._query("DELETE FROM dbversion", DELETE)
            self.db._query("INSERT INTO dbversion VALUES (?)", INSERT, i)
        if not self.db.commit():
            self.db.rollback()
            msg = QMessageBox()
            msg.setText(u'Не удалось обновить базу данных')
            msg.setInformativeText(u'Продолжение работы может привести к нарушению целостности данных. Обратитесь к разработчику за поддержкой')
            msg.exec_()

#собственно, запросы для обновляемой версии:
                      #V11
                      [
                       [
                        'ALTER TABLE templates ADD COLUMN state INTEGER DEFAULT 0', DDQ
                        ],
                       [
                        'ALTER TABLE templates ADD COLUMN manualmeasurelenstype INTEGER DEFAULT 0', DDQ
                        ],
                       [
                        'ALTER TABLE results ADD COLUMN pressure INTEGER DEFAULT 0', DDQ
                        ],
                       [
                        'ALTER TABLE results ADD COLUMN temperature INTEGER DEFAULT 0', DDQ
                        ],
                       [
                        'ALTER TABLE results ADD COLUMN humidity INTEGER DEFAULT 0', DDQ
                        ],
                       [
                        'CREATE TABLE operators (id INTEGER PRIMARY KEY, name TEXT)', DDQ
                        ],
                       [
                        '''CREATE TABLE tmp_results (
                            id INTEGER PRIMARY KEY,
                            template_id INTEGER,
                            date INTEGER,
                            measurer INTEGER,
                            device_id INTEGER,
                            certificate TEXT,
                            gms TEXT,
                            serial TEXT,
                            orderer_id INTEGER,
                            type INTEGER,
                            measurescountauto INTEGER,
                            measurescountmanual INTEGER,
                            state INTEGER,
                            owner_id INTEGER,
                            comment TEXT,
                            number TEXT,
                            pressure INTEGER DEFAULT 0,
                            temperature INTEGER DEFAULT 0,
                            humidity INTEGER DEFAULT 0
                           )
                        )''', DDQ
                        ],
                       [
                        '''
                        INSERT INTO operators (name) SELECT DISTINCT measurer FROM results
                        ''', INSERT
                        ],
                       [
                        '''
                        INSERT INTO tmp_results (
                            id,
                            template_id,
                            date,
                            measurer,
                            device_id,
                            certificate, 
                            gms,
                            serial,
                            orderer_id,
                            type,
                            measurescountauto,
                            measurescountmanual,
                            state,
                            owner_id,
                            comment,
                            number,
                            pressure,
                            temperature,
                            humidity
                        )
                        SELECT r.id, r.template_id, r.date,
                        o.id, r.device_id, r.certificate, r.gms,
                        r.serial, r.orderer_id, r.type,
                        r.measurescountauto, r.measurescountmanual,
                        r.finished, r.owner_id, r.comment, r.number,
                        0, 0, 0
                        FROM results as r, operators as o
                        WHERE r.measurer = o.name
                        ''', INSERT
                        ],
                       [
                        'DROP TABLE results', DDQ
                        ],
                       [
                        '''CREATE TABLE results (
                            id INTEGER PRIMARY KEY,
                            template_id INTEGER,
                            date INTEGER,
                            measurer INTEGER,
                            device_id INTEGER,
                            certificate TEXT,
                            gms TEXT,
                            serial TEXT,
                            orderer_id INTEGER,
                            type INTEGER,
                            measurescountauto INTEGER,
                            measurescountmanual INTEGER,
                            state INTEGER DEFAULT 0,
                            owner_id INTEGER,
                            comment TEXT,
                            number TEXT,
                            pressure INTEGER DEFAULT 0,
                            temperature INTEGER DEFAULT 0,
                            humidity INTEGER DEFAULT 0
                           )
                        )''', DDQ
                        ],
                       [
                        'INSERT INTO results SELECT * FROM tmp_results', INSERT
                        ],
                       [
                        'DROP TABLE tmp_results', DDQ
                        ]
                       ],

GateKeeper ★★
() автор топика

Подробности:

Запрос: CREATE TABLE operators (id INTEGER PRIMARY KEY, name TEXT)

Работа:

QSqlQuery().prepare(query).exec_()

Ошибка:

PySide.QtSql.QSqlError(-1, «Unable to fetch row», «No query»)

GateKeeper ★★
() автор топика

Такое бывало, когда я использовал временную таблицу или базу. В sqlite какое-то странное пространство имен, он иногда использовал таблицу из temp базы, хотя в main была такая же. Я не стал копать, использовал разные имена. У тебя я смотрю temp нигде не видно, наверное не тот случай.

anonymous
()

Чтобы отбросить вариант с тем, что новая таблица оказывается во временной базе, предлагаю перед выходом вывалить дамп схемы. SELECT * FROM sqlite_master; SELECT * FROM sqlite_temp_master;

anonymous
()
Ответ на: комментарий от GateKeeper

Понять, простить же. Проверь сначала. Такое у меня происходило, только когда у меня, скажем, была T1 и я явно создавал temp.T1 (или create temp table T1 ...). После этого нужно было всегда явно указывать базу в имени таблицы. main.T1 или temp.T1. Иначе он их путал и мог DROP'нуть не ту, или SELECT'нуть не по той.

anonymous
()
Ответ на: комментарий от GateKeeper

Ну и да, сам он во временную базу ничего класть не будет. Он просто по дефолту зачем-то кроме main открывает еще и :memory: базу и аттачит ее под именем temp. Если сам нигде не говоришь CREATE TEMP или CREATE temp.XXX, то это не твой случай.

anonymous
()
Ответ на: комментарий от anonymous

Оу, вот же момент, который я пропустил.

Tables in an attached database can be referred to using the syntax database-name.table-name. If the name of the table is unique across all attached databases and the main and temp databases, then the database-name prefix is not required. If two or more tables in different databases have the same name and the database-name prefix is not used on a table reference, then the table chosen is the one in the database that was least recently attached.

Похоже его temp база приаттачивается второй по счету, вот и выбирает оттуда. Так бы и не узнал никогда наверное, спасибо.

anonymous
()

Черт. В общем, единственный пока путь, который более-менее работает - это выгрузить данные из старого экземпляра, переименовать его, проинициализировать новый по старому пути и воткнуть данные из старого (соответственно, где нужно, использовать маппинг старых имен колонок в новые). Но блджад, там базулька на 400Кб, переливка через in-memory идет уже 15 минут, по размеру - 100Кб пока только скинуло.

GateKeeper ★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.