refactore sql thread and add test cases for versioning

This commit is contained in:
Muzahid 2021-04-30 01:20:18 +05:30
parent bb144d78f6
commit dedefbcfc3
Signed by untrusted user: cis-muzahid
GPG Key ID: 1DC85E7D3AB613EA
27 changed files with 1000 additions and 246 deletions

View File

@ -18,7 +18,6 @@ if sys.version_info[0] == 3:
from . import tr from . import tr
from .bmconfigparser import BMConfigParser from .bmconfigparser import BMConfigParser
from .debug import logger from .debug import logger
# pylint: disable=attribute-defined-outside-init,protected-access
from .addresses import encodeAddress from .addresses import encodeAddress
else: else:
import helper_sql import helper_sql
@ -29,11 +28,224 @@ else:
import tr import tr
from bmconfigparser import BMConfigParser from bmconfigparser import BMConfigParser
from debug import logger from debug import logger
# pylint: disable=attribute-defined-outside-init,protected-access
from addresses import encodeAddress from addresses import encodeAddress
class sqlThread(threading.Thread): # pylint: disable=attribute-defined-outside-init,protected-access
root_path = os.path.dirname(os.path.dirname(__file__))
def connection_build():
conn = sqlite3.connect(state.appdata + 'messages.dat')
conn.text_factory = str
cur = conn.cursor()
return conn, cur
class UpgradeDB():
"""Upgrade Db with respect to versions"""
# cur = None
parameters = None
current_level = None
max_level = 11
conn = None
conn, cur = connection_build()
def get_current_level(self):
# Upgrade Db with respect to their versions
item = '''SELECT value FROM settings WHERE key='version';'''
parameters = ''
self.cur.execute(item, parameters)
return int(self.cur.fetchall()[0][0])
def upgrade_one_level(self, level):
""" Apply switcher to call methods accordingly """
if level != self.get_current_level():
return None
# Migrate Db with level
method_name = 'upgrade_schema_data_' + str(level)
method = getattr(self, method_name, lambda: "Invalid version")
return method()
def run_migrations(self, file):
try:
with open(os.path.join(root_path, "src/sql/init_version_{}.sql".format(file))) as sql_file:
sql_as_string = sql_file.read()
self.cur.executescript(sql_as_string)
# self.cur.executescript(sql_as_string)
except Exception as err:
if str(err) == 'table inbox already exists':
return "table inbox already exists"
else:
sys.stderr.write(
'ERROR trying to create database file (message.dat). Error message: %s\n' % str(err))
os._exit(0)
def versioning(func):
def wrapper(*args):
self = args[0]
func_name = func.__name__
version = func_name.rsplit('_', 1)[-1]
self.run_migrations(version)
ret = func(*args)
return ret # <-- use (self, ...)
return wrapper
def upgrade_to_latest(self, cur, conn):
"""
Initialise upgrade level
"""
# Declare variables
self.conn = conn
self.cur = cur
self.current_level = self.get_current_level()
self.max_level = 11
# call upgrading level in loop
for l in range(self.current_level, self.max_level):
self.upgrade_one_level(l)
self.upgrade_schema_data_level(l)
def upgrade_schema_data_level(self, level):
item = '''update settings set value=? WHERE key='version';'''
parameters = (level + 1,)
self.cur.execute(item, parameters)
@versioning
def upgrade_schema_data_1(self):
"""inventory
For version 1 and 3
Add a new column to the inventory table to store tags.
"""
logger.debug(
'In messages.dat database, adding tag field to'
' the inventory table.')
@versioning
def upgrade_schema_data_2(self):
"""
For version 2
Let's get rid of the first20bytesofencryptedmessage field in the inventory table.
"""
logger.debug(
'In messages.dat database, removing an obsolete field from'
' the inventory table.')
def upgrade_schema_data_3(self):
"""
For version 3
Call method for version 1
"""
self.upgrade_schema_data_1()
@versioning
def upgrade_schema_data_4(self):
"""
For version 4
Add a new column to the pubkeys table to store the address version.
We're going to trash all of our pubkeys and let them be redownloaded.
"""
@versioning
def upgrade_schema_data_5(self):
"""
For version 5
Add a new table: objectprocessorqueue with which to hold objects
That have yet to be processed if the user shuts down Bitmessage.
"""
@versioning
def upgrade_schema_data_6(self):
"""
For version 6
Changes related to protocol v3
In table inventory and objectprocessorqueue, objecttype is now
an integer (it was a human-friendly string previously)
"""
logger.debug(
'In messages.dat database, dropping and recreating'
' the inventory table.')
logger.debug(
'Finished dropping and recreating the inventory table.')
@versioning
def upgrade_schema_data_7(self):
"""
For version 7
The format of data stored in the pubkeys table has changed. Let's
clear it, and the pubkeys from inventory, so that they'll
be re-downloaded.
"""
logger.debug(
'In messages.dat database, clearing pubkeys table'
' because the data format has been updated.')
logger.debug('Finished clearing currently held pubkeys.')
@versioning
def upgrade_schema_data_8(self):
"""
For version 8
Add a new column to the inbox table to store the hash of
the message signature. We'll use this as temporary message UUID
in order to detect duplicates.
"""
logger.debug(
'In messages.dat database, adding sighash field to'
' the inbox table.')
@versioning
def upgrade_schema_data_9(self):
"""
For version 9
We'll also need a `sleeptill` field and a `ttl` field. Also we
can combine the pubkeyretrynumber and msgretrynumber into one.
"""
logger.info(
'In messages.dat database, making TTL-related changes:'
' combining the pubkeyretrynumber and msgretrynumber'
' fields into the retrynumber field and adding the'
' sleeptill and ttl fields...')
logger.info('In messages.dat database, finished making TTL-related changes.')
logger.debug('In messages.dat database, adding address field to the pubkeys table.')
# We're going to have to calculate the address for each row in the pubkeys
# table. Then we can take out the hash field.
self.cur.execute('''ALTER TABLE pubkeys ADD address text DEFAULT '' ''')
# replica for loop to update hashed address
self.cur.execute('''UPDATE pubkeys SET address=(enaddr(pubkeys.addressversion, 1, hash)); ''')
self.run_migrations("9_1")
logger.debug(
'In messages.dat database, done adding address field to the pubkeys table'
' and removing the hash field.')
@versioning
def upgrade_schema_data_10(self):
"""
For version 10
Update the address colunm to unique in addressbook table
"""
logger.debug(
'In messages.dat database, updating address column to UNIQUE'
' in the addressbook table.')
class sqlThread(threading.Thread, UpgradeDB):
"""A thread for all SQL operations""" """A thread for all SQL operations"""
def __init__(self): def __init__(self):
@ -42,9 +254,7 @@ class sqlThread(threading.Thread):
def run(self): # pylint: disable=too-many-locals, too-many-branches, too-many-statements def run(self): # pylint: disable=too-many-locals, too-many-branches, too-many-statements
"""Process SQL queries from `.helper_sql.sqlSubmitQueue`""" """Process SQL queries from `.helper_sql.sqlSubmitQueue`"""
helper_sql.sql_available = True helper_sql.sql_available = True
self.conn = sqlite3.connect(state.appdata + 'messages.dat') self.conn, self.cur = connection_build()
self.conn.text_factory = str
self.cur = self.conn.cursor()
self.cur.execute('PRAGMA secure_delete = true') self.cur.execute('PRAGMA secure_delete = true')
@ -194,237 +404,20 @@ class sqlThread(threading.Thread):
'''update sent set status='broadcastqueued' where status='broadcastpending' ''') '''update sent set status='broadcastqueued' where status='broadcastpending' ''')
self.conn.commit() self.conn.commit()
# Let's get rid of the first20bytesofencryptedmessage field in self.upgrade_to_latest(self.cur, self.conn)
# the inventory table.
item = '''SELECT value FROM settings WHERE key='version';'''
parameters = ''
self.cur.execute(item, parameters)
if int(self.cur.fetchall()[0][0]) == 2:
logger.debug(
'In messages.dat database, removing an obsolete field from'
' the inventory table.')
self.cur.execute(
'''CREATE TEMPORARY TABLE inventory_backup'''
'''(hash blob, objecttype text, streamnumber int, payload blob,'''
''' receivedtime integer, UNIQUE(hash) ON CONFLICT REPLACE);''')
self.cur.execute(
'''INSERT INTO inventory_backup SELECT hash, objecttype, streamnumber, payload, receivedtime'''
''' FROM inventory;''')
self.cur.execute('''DROP TABLE inventory''')
self.cur.execute(
'''CREATE TABLE inventory'''
''' (hash blob, objecttype text, streamnumber int, payload blob, receivedtime integer,'''
''' UNIQUE(hash) ON CONFLICT REPLACE)''')
self.cur.execute(
'''INSERT INTO inventory SELECT hash, objecttype, streamnumber, payload, receivedtime'''
''' FROM inventory_backup;''')
self.cur.execute('''DROP TABLE inventory_backup;''')
item = '''update settings set value=? WHERE key='version';'''
parameters = (3,)
self.cur.execute(item, parameters)
# Add a new column to the inventory table to store tags.
item = '''SELECT value FROM settings WHERE key='version';'''
parameters = ''
self.cur.execute(item, parameters)
currentVersion = int(self.cur.fetchall()[0][0])
if currentVersion == 1 or currentVersion == 3:
logger.debug(
'In messages.dat database, adding tag field to'
' the inventory table.')
item = '''ALTER TABLE inventory ADD tag blob DEFAULT '' '''
parameters = ''
self.cur.execute(item, parameters)
item = '''update settings set value=? WHERE key='version';'''
parameters = (4,)
self.cur.execute(item, parameters)
# Add a new column to the pubkeys table to store the address version.
# We're going to trash all of our pubkeys and let them be redownloaded.
item = '''SELECT value FROM settings WHERE key='version';'''
parameters = ''
self.cur.execute(item, parameters)
currentVersion = int(self.cur.fetchall()[0][0])
if currentVersion == 4:
self.cur.execute('''DROP TABLE pubkeys''')
self.cur.execute(
'''CREATE TABLE pubkeys (hash blob, addressversion int, transmitdata blob, time int,'''
'''usedpersonally text, UNIQUE(hash, addressversion) ON CONFLICT REPLACE)''')
self.cur.execute(
'''delete from inventory where objecttype = 'pubkey';''')
item = '''update settings set value=? WHERE key='version';'''
parameters = (5,)
self.cur.execute(item, parameters)
# Add a new table: objectprocessorqueue with which to hold objects
# that have yet to be processed if the user shuts down Bitmessage.
item = '''SELECT value FROM settings WHERE key='version';'''
parameters = ''
self.cur.execute(item, parameters)
currentVersion = int(self.cur.fetchall()[0][0])
if currentVersion == 5:
self.cur.execute('''DROP TABLE knownnodes''')
self.cur.execute(
'''CREATE TABLE objectprocessorqueue'''
''' (objecttype text, data blob, UNIQUE(objecttype, data) ON CONFLICT REPLACE)''')
item = '''update settings set value=? WHERE key='version';'''
parameters = (6,)
self.cur.execute(item, parameters)
# changes related to protocol v3
# In table inventory and objectprocessorqueue, objecttype is now
# an integer (it was a human-friendly string previously)
item = '''SELECT value FROM settings WHERE key='version';'''
parameters = ''
self.cur.execute(item, parameters)
currentVersion = int(self.cur.fetchall()[0][0])
if currentVersion == 6:
logger.debug(
'In messages.dat database, dropping and recreating'
' the inventory table.')
self.cur.execute('''DROP TABLE inventory''')
self.cur.execute(
'''CREATE TABLE inventory'''
''' (hash blob, objecttype int, streamnumber int, payload blob, expirestime integer,'''
''' tag blob, UNIQUE(hash) ON CONFLICT REPLACE)''')
self.cur.execute('''DROP TABLE objectprocessorqueue''')
self.cur.execute(
'''CREATE TABLE objectprocessorqueue'''
''' (objecttype int, data blob, UNIQUE(objecttype, data) ON CONFLICT REPLACE)''')
item = '''update settings set value=? WHERE key='version';'''
parameters = (7,)
self.cur.execute(item, parameters)
logger.debug(
'Finished dropping and recreating the inventory table.')
# The format of data stored in the pubkeys table has changed. Let's
# clear it, and the pubkeys from inventory, so that they'll
# be re-downloaded.
item = '''SELECT value FROM settings WHERE key='version';'''
parameters = ''
self.cur.execute(item, parameters)
currentVersion = int(self.cur.fetchall()[0][0])
if currentVersion == 7:
logger.debug(
'In messages.dat database, clearing pubkeys table'
' because the data format has been updated.')
self.cur.execute(
'''delete from inventory where objecttype = 1;''')
self.cur.execute(
'''delete from pubkeys;''')
# Any sending messages for which we *thought* that we had
# the pubkey must be rechecked.
self.cur.execute(
'''UPDATE sent SET status='msgqueued' WHERE status='doingmsgpow' or status='badkey';''')
query = '''update settings set value=? WHERE key='version';'''
parameters = (8,)
self.cur.execute(query, parameters)
logger.debug('Finished clearing currently held pubkeys.')
# Add a new column to the inbox table to store the hash of
# the message signature. We'll use this as temporary message UUID
# in order to detect duplicates.
item = '''SELECT value FROM settings WHERE key='version';'''
parameters = ''
self.cur.execute(item, parameters)
currentVersion = int(self.cur.fetchall()[0][0])
if currentVersion == 8:
logger.debug(
'In messages.dat database, adding sighash field to'
' the inbox table.')
item = '''ALTER TABLE inbox ADD sighash blob DEFAULT '' '''
parameters = ''
self.cur.execute(item, parameters)
item = '''update settings set value=? WHERE key='version';'''
parameters = (9,)
self.cur.execute(item, parameters)
# We'll also need a `sleeptill` field and a `ttl` field. Also we
# can combine the pubkeyretrynumber and msgretrynumber into one.
item = '''SELECT value FROM settings WHERE key='version';'''
parameters = ''
self.cur.execute(item, parameters)
currentVersion = int(self.cur.fetchall()[0][0])
if currentVersion == 9:
logger.info(
'In messages.dat database, making TTL-related changes:'
' combining the pubkeyretrynumber and msgretrynumber'
' fields into the retrynumber field and adding the'
' sleeptill and ttl fields...')
self.cur.execute(
'''CREATE TEMPORARY TABLE sent_backup'''
''' (msgid blob, toaddress text, toripe blob, fromaddress text, subject text, message text,'''
''' ackdata blob, lastactiontime integer, status text, retrynumber integer,'''
''' folder text, encodingtype int)''')
self.cur.execute(
'''INSERT INTO sent_backup SELECT msgid, toaddress, toripe, fromaddress,'''
''' subject, message, ackdata, lastactiontime,'''
''' status, 0, folder, encodingtype FROM sent;''')
self.cur.execute('''DROP TABLE sent''')
self.cur.execute(
'''CREATE TABLE sent'''
''' (msgid blob, toaddress text, toripe blob, fromaddress text, subject text, message text,'''
''' ackdata blob, senttime integer, lastactiontime integer, sleeptill int, status text,'''
''' retrynumber integer, folder text, encodingtype int, ttl int)''')
self.cur.execute(
'''INSERT INTO sent SELECT msgid, toaddress, toripe, fromaddress, subject, message, ackdata,'''
''' lastactiontime, lastactiontime, 0, status, 0, folder, encodingtype, 216000 FROM sent_backup;''')
self.cur.execute('''DROP TABLE sent_backup''')
logger.info('In messages.dat database, finished making TTL-related changes.')
logger.debug('In messages.dat database, adding address field to the pubkeys table.')
# We're going to have to calculate the address for each row in the pubkeys
# table. Then we can take out the hash field.
self.cur.execute('''ALTER TABLE pubkeys ADD address text DEFAULT '' ;''')
# replica for loop to update hashed address
self.cur.execute('''UPDATE pubkeys SET address=(enaddr(pubkeys.addressversion, 1, hash)); ''')
# Now we can remove the hash field from the pubkeys table.
self.cur.execute(
'''CREATE TEMPORARY TABLE pubkeys_backup'''
''' (address text, addressversion int, transmitdata blob, time int,'''
''' usedpersonally text, UNIQUE(address) ON CONFLICT REPLACE)''')
self.cur.execute(
'''INSERT INTO pubkeys_backup'''
''' SELECT address, addressversion, transmitdata, time, usedpersonally FROM pubkeys;''')
self.cur.execute('''DROP TABLE pubkeys''')
self.cur.execute(
'''CREATE TABLE pubkeys'''
''' (address text, addressversion int, transmitdata blob, time int, usedpersonally text,'''
''' UNIQUE(address) ON CONFLICT REPLACE)''')
self.cur.execute(
'''INSERT INTO pubkeys SELECT'''
''' address, addressversion, transmitdata, time, usedpersonally FROM pubkeys_backup;''')
self.cur.execute('''DROP TABLE pubkeys_backup''')
logger.debug(
'In messages.dat database, done adding address field to the pubkeys table'
' and removing the hash field.')
self.cur.execute('''update settings set value=10 WHERE key='version';''')
# Update the address colunm to unique in addressbook table
item = '''SELECT value FROM settings WHERE key='version';'''
parameters = ''
self.cur.execute(item, parameters)
currentVersion = int(self.cur.fetchall()[0][0])
if currentVersion == 10:
logger.debug(
'In messages.dat database, updating address column to UNIQUE'
' in the addressbook table.')
self.cur.execute(
'''ALTER TABLE addressbook RENAME TO old_addressbook''')
self.cur.execute(
'''CREATE TABLE addressbook'''
''' (label text, address text, UNIQUE(address) ON CONFLICT IGNORE)''')
self.cur.execute(
'''INSERT INTO addressbook SELECT label, address FROM old_addressbook;''')
self.cur.execute('''DROP TABLE old_addressbook''')
self.cur.execute('''update settings set value=11 WHERE key='version';''')
# Are you hoping to add a new option to the keys.dat file of existing # Are you hoping to add a new option to the keys.dat file of existing
# Bitmessage users or modify the SQLite database? Add it right # Bitmessage users or modify the SQLite database? Add it right
# above this line! # above this line!
self.add_new_option()
# Let us check to see the last time we vaccumed the messages.dat file.
# If it has been more than a month let's do it now.
self.check_vaccumed()
def add_new_option(self):
try: try:
testpayload = '\x00\x00' testpayload = '\x00\x00'
t = ('1234', 1, testpayload, '12345678', 'no') t = ('1234', 1, testpayload, '12345678', 'no')
@ -465,8 +458,7 @@ class sqlThread(threading.Thread):
else: else:
logger.error(err) logger.error(err)
# Let us check to see the last time we vaccumed the messages.dat file. def check_vaccumed(self):
# If it has been more than a month let's do it now.
item = '''SELECT value FROM settings WHERE key='lastvacuumtime';''' item = '''SELECT value FROM settings WHERE key='lastvacuumtime';'''
parameters = '' parameters = ''
self.cur.execute(item, parameters) self.cur.execute(item, parameters)

View File

@ -1,15 +1,11 @@
""" """
Startup operations. Startup operations.
""" """
# pylint: disable=too-many-branches,too-many-statements
import logging import logging
import os import os
import platform import platform
import sys
import time import time
from distutils.version import StrictVersion from distutils.version import StrictVersion
import sys import sys
if sys.version_info[0] == 3: if sys.version_info[0] == 3:
from . import defaults from . import defaults
@ -29,6 +25,8 @@ try:
except ImportError: except ImportError:
get_plugin = None get_plugin = None
# pylint: disable=too-many-branches,too-many-statements
logger = logging.getLogger('default') logger = logging.getLogger('default')

0
src/sql/__init__.py Normal file
View File

View File

@ -0,0 +1,6 @@
--
-- Alter table `inventory`
--
ALTER TABLE inventory ADD tag blob DEFAULT '';

View File

@ -0,0 +1,12 @@
ALTER TABLE addressbook RENAME TO old_addressbook;
CREATE TABLE `addressbook` (
`label` text NOT NULL,
`address` text NOT NULL,
UNIQUE(address) ON CONFLICT IGNORE
) ;
INSERT INTO addressbook SELECT label, address FROM old_addressbook;
DROP TABLE old_addressbook;

View File

@ -0,0 +1,55 @@
--
-- Temp Table structure for table `inventory_backup`
--
CREATE TEMP TABLE `inventory_backup` (
`hash` blob NOT NULL,
`objecttype` text DEFAULT NULL,
`streamnumber` int NOT NULL,
`receivedtime` int NOT NULL,
`payload` blob DEFAULT NULL,
-- `integer` integer NOT NULL,
-- `tag` blob DEFAULT NULL,
UNIQUE(hash) ON CONFLICT REPLACE
) ;
--
-- Dumping data for table `inventory_backup`
--
INSERT INTO `inventory_backup` SELECT hash, objecttype, streamnumber, payload, receivedtime FROM inventory;
--
-- Drop table `inventory`
--
DROP TABLE inventory;
--
-- Table structure for table `inventory`
--
CREATE TABLE `inventory` (
`hash` blob NOT NULL,
`objecttype` text DEFAULT NULL,
`streamnumber` int NOT NULL,
`receivedtime` int NOT NULL,
`payload` blob DEFAULT NULL,
UNIQUE(hash) ON CONFLICT REPLACE
) ;
--
-- Dumping data for table `inventory`
--
INSERT INTO inventory SELECT hash, objecttype, streamnumber, payload, receivedtime FROM inventory_backup;
--
-- Drop data for table `inventory_backup`
--
DROP TABLE inventory_backup;

View File

@ -0,0 +1,26 @@
--
-- Drop Table `pubkeys`
--
DROP TABLE pubkeys;
--
-- Table structure for table `pubkeys`
--
CREATE TABLE `pubkeys` (
`hash` blob NOT NULL,
`addressversion` int DEFAULT NULL,
`transmitdata` blob NOT NULL,
`time` int NOT NULL,
`usedpersonally` text DEFAULT NULL,
UNIQUE(hash, addressversion) ON CONFLICT REPLACE
) ;
--
-- Drop from Table `pubkeys`
--
DELETE FROM inventory WHERE objecttype = 'pubkey';

View File

@ -0,0 +1,17 @@
--
-- Drop Table `knownnodes`
--
DROP TABLE knownnodes;
--
-- Table structure for table `objectprocessorqueue`
--
CREATE TABLE `objectprocessorqueue` (
`objecttype` text DEFAULT NULL,
`data` blob DEFAULT NULL,
UNIQUE(objecttype, data) ON CONFLICT REPLACE
) ;

View File

@ -0,0 +1,39 @@
-- --
-- -- Drop table `inventory`
-- --
DROP TABLE inventory;
-- --
-- -- Table structure for table `inventory`
-- --
CREATE TABLE `inventory` (
`hash` blob NOT NULL,
`objecttype` int DEFAULT NULL,
`streamnumber` int NOT NULL,
`payload` blob NOT NULL,
`expirestime` integer DEFAULT NULL,
`tag` blob DEFAULT NULL,
UNIQUE(hash) ON CONFLICT REPLACE
) ;
-- --
-- -- Drop table `inventory`
-- --
DROP TABLE objectprocessorqueue;
-- --
-- -- Table structure for table `objectprocessorqueue`
-- --
CREATE TABLE `objectprocessorqueue` (
`objecttype` int DEFAULT NULL,
`data` blob DEFAULT NULL,
UNIQUE(objecttype, data) ON CONFLICT REPLACE
) ;

View File

@ -0,0 +1,18 @@
-- --
-- -- Drop table `inventory`
-- --
DELETE FROM inventory WHERE objecttype = 1;
-- --
-- -- Drop table `pubkeys`
-- --
DELETE FROM pubkeys;
-- --
-- -- Update table `pubkeys`
-- --
UPDATE sent SET status='msgqueued' WHERE status='doingmsgpow' or status='badkey';

View File

@ -0,0 +1,5 @@
-- --
-- -- Alter table `inbox`
-- --
ALTER TABLE inbox ADD sighash blob DEFAULT '';

View File

@ -0,0 +1,74 @@
-- --
-- -- Table structure for table `sent_backup`
-- --
CREATE TEMPORARY TABLE `sent_backup` (
`msgid` blob DEFAULT NULL,
`toaddress` text DEFAULT NULL,
`toripe` blob DEFAULT NULL,
`fromaddress` text DEFAULT NULL,
`subject` text DEFAULT NULL,
`message` text DEFAULT NULL,
`ackdata` blob DEFAULT NULL,
`lastactiontime` integer DEFAULT NULL,
`status` text DEFAULT NULL,
`retrynumber` integer DEFAULT NULL,
`folder` text DEFAULT NULL,
`encodingtype` int DEFAULT NULL
) ;
-- --
-- -- Dumping data for table `sent_backup`
-- --
INSERT INTO sent_backup SELECT msgid, toaddress, toripe, fromaddress, subject, message, ackdata, lastactiontime, status, 0, folder, encodingtype FROM sent;
-- --
-- -- Drope table `sent`
-- --
DROP TABLE sent;
-- --
-- -- Table structure for table `sent_backup`
-- --
CREATE TABLE `sent` (
`msgid` blob DEFAULT NULL,
`toaddress` text DEFAULT NULL,
`toripe` blob DEFAULT NULL,
`fromaddress` text DEFAULT NULL,
`subject` text DEFAULT NULL,
`message` text DEFAULT NULL,
`ackdata` blob DEFAULT NULL,
`senttime` integer DEFAULT NULL,
`lastactiontime` integer DEFAULT NULL,
`sleeptill` int DEFAULT NULL,
`status` text DEFAULT NULL,
`retrynumber` integer DEFAULT NULL,
`folder` text DEFAULT NULL,
`encodingtype` int DEFAULT NULL,
`ttl` int DEFAULT NULL
) ;
-- --
-- -- Dumping data for table `sent`
-- --
INSERT INTO sent SELECT msgid, toaddress, toripe, fromaddress, subject, message, ackdata, lastactiontime, lastactiontime, 0, status, 0, folder, encodingtype, 216000 FROM sent_backup;
--UPDATE pubkeys SET address= (select enaddr(?, ?, ?)", (addressVersion, 1, addressHash)) WHERE hash=?
-- --
-- -- Drop table `sent`
-- --
DROP TABLE sent_backup;

View File

@ -0,0 +1,57 @@
-- --
-- -- Table structure for table `pubkeys_backup`
-- --
CREATE TEMPORARY TABLE `pubkeys_backup` (
`address` text DEFAULT NULL,
`addressversion` int DEFAULT NULL,
`transmitdata` blob DEFAULT NULL,
`time` int DEFAULT NULL,
`usedpersonally` text DEFAULT NULL,
UNIQUE(address) ON CONFLICT REPLACE
) ;
-- --
-- -- Dumping data for table `pubkeys_backup`
-- --
INSERT INTO pubkeys_backup SELECT address, addressversion, transmitdata, time, usedpersonally FROM pubkeys;
-- --
-- -- Drope table `pubkeys`
-- --
DROP TABLE pubkeys;
-- --
-- -- Table structure for table `pubkeys`
-- --
CREATE TABLE `pubkeys` (
`address` text DEFAULT NULL,
`addressversion` int DEFAULT NULL,
`transmitdata` blob DEFAULT NULL,
`time` int DEFAULT NULL,
`usedpersonally` text DEFAULT NULL,
UNIQUE(address) ON CONFLICT REPLACE
) ;
-- --
-- -- Dumping data for table `pubkeys`
-- --
INSERT INTO pubkeys SELECT address, addressversion, transmitdata, time, usedpersonally FROM pubkeys_backup;
-- --
-- -- Dropping table `pubkeys_backup`
-- --
DROP TABLE pubkeys_backup;

126
src/sql/run.sql Normal file
View File

@ -0,0 +1,126 @@
--
-- Table structure for table `inbox`
--
CREATE TABLE `inbox` (
`msgid` blob DEFAULT NULL,
`toaddress` text DEFAULT NULL,
`fromaddress` text DEFAULT NULL,
`subject` text DEFAULT NULL,
`received` text DEFAULT NULL,
`message` text DEFAULT NULL,
`folder` text DEFAULT NULL,
`encodingtype` int DEFAULT NULL,
`read` bool DEFAULT NULL,
`sighash` blob DEFAULT NULL,
UNIQUE(msgid) ON CONFLICT REPLACE
) ;
--
-- Table structure for table `sent`
--
CREATE TABLE `sent` (
`msgid` blob DEFAULT NULL,
`toaddress` text DEFAULT NULL,
`toripe` blob DEFAULT NULL,
`fromaddress` text DEFAULT NULL,
`subject` text DEFAULT NULL,
`message` text DEFAULT NULL,
`ackdata` blob DEFAULT NULL,
`senttime` integer DEFAULT NULL,
`lastactiontime` integer DEFAULT NULL,
`sleeptill` integer DEFAULT NULL,
`status` text DEFAULT NULL,
`retrynumber` integer DEFAULT NULL,
`folder` text DEFAULT NULL,
`encodingtype` int DEFAULT NULL,
`ttl` int DEFAULT NULL
) ;
--
-- Table structure for table `subscriptions`
--
CREATE TABLE `subscriptions` (
`label` text DEFAULT NULL,
`address` text DEFAULT NULL,
`enabled` bool DEFAULT NULL
) ;
--
-- Table structure for table `addressbook`
--
CREATE TABLE `addressbook` (
`label` text DEFAULT NULL,
`address` text DEFAULT NULL,
UNIQUE(address) ON CONFLICT IGNORE
) ;
--
-- Table structure for table `blacklist`
--
CREATE TABLE `blacklist` (
`label` text DEFAULT NULL,
`address` text DEFAULT NULL,
`enabled` bool DEFAULT NULL
) ;
--
-- Table structure for table `whitelist`
--
CREATE TABLE `whitelist` (
`label` text DEFAULT NULL,
`address` text DEFAULT NULL,
`enabled` bool DEFAULT NULL
) ;
--
-- Table structure for table `pubkeys`
--
CREATE TABLE `pubkeys` (
`address` text DEFAULT NULL,
`addressversion` int DEFAULT NULL,
`transmitdata` blob DEFAULT NULL,
`time` int DEFAULT NULL,
`usedpersonally` text DEFAULT NULL,
UNIQUE(address) ON CONFLICT REPLACE
) ;
--
-- Table structure for table `inventory`
--
CREATE TABLE `inventory` (
`hash` blob DEFAULT NULL,
`objecttype` int DEFAULT NULL,
`streamnumber` int DEFAULT NULL,
`payload` blob DEFAULT NULL,
`expirestime` integer DEFAULT NULL,
`tag` blob DEFAULT NULL,
UNIQUE(hash) ON CONFLICT REPLACE
) ;
--
-- Insert data for table `subscriptions`
--
INSERT INTO subscriptions VALUES ('Bitmessage new releases/announcements','BM-GtovgYdgs7qXPkoYaRgrLFuFKz1SFpsw',1);
--
-- Table structure for table `settings`
--
CREATE TABLE `settings` (
`key` blob DEFAULT NULL,
`value` blob DEFAULT NULL,
UNIQUE(key) ON CONFLICT REPLACE
) ;

View File

View File

@ -1,4 +1,4 @@
CREATE TABLE `testhash` ( CREATE TABLE `testhash` (
`addressversion` int DEFAULT NULL, `addressversion` int DEFAULT NULL,
`hash` blob DEFAULT NULL, `hash` blob DEFAULT NULL,
`address` text DEFAULT NULL, `address` text DEFAULT NULL,

View File

@ -0,0 +1,16 @@
CREATE TABLE IF NOT EXISTS `settings` (
`key` blob NOT NULL,
`value` text DEFAULT NULL,
UNIQUE(key) ON CONFLICT REPLACE
) ;
INSERT INTO `settings` VALUES ('version','1');
CREATE TABLE IF NOT EXISTS `inventory` (
`hash` blob NOT NULL,
`objecttype` int DEFAULT NULL,
`streamnumber` int NOT NULL,
`payload` blob DEFAULT NULL,
`integer` integer NOT NULL,
UNIQUE(hash) ON CONFLICT REPLACE
) ;

View File

@ -0,0 +1,17 @@
CREATE TABLE IF NOT EXISTS `addressbook` (
`label` blob NOT NULL,
`address` text DEFAULT NULL,
UNIQUE(address) ON CONFLICT IGNORE
) ;
ALTER TABLE addressbook RENAME TO old_addressbook;
CREATE TABLE IF NOT EXISTS `addressbook` (
`label` text NOT NULL,
`address` text DEFAULT NULL,
UNIQUE(address) ON CONFLICT IGNORE
) ;
INSERT INTO addressbook SELECT label, address FROM old_addressbook;
DROP TABLE old_addressbook;

View File

@ -0,0 +1,11 @@
CREATE TABLE IF NOT EXISTS `inventory` (
`hash` blob NOT NULL,
`objecttype` int DEFAULT NULL,
`streamnumber` int NOT NULL,
`receivedtime` int NOT NULL,
`payload` blob DEFAULT NULL,
`integer` integer NOT NULL,
UNIQUE(hash) ON CONFLICT REPLACE
) ;
INSERT INTO `inventory` VALUES ('hash', 1, 1,1, 1,'test');

View File

@ -0,0 +1,15 @@
CREATE TABLE IF NOT EXISTS `inventory` (
`hash` blob NOT NULL,
`objecttype` text DEFAULT NULL,
`streamnumber` int NOT NULL,
`payload` blob DEFAULT NULL,
`integer` integer NOT NULL,
UNIQUE(hash) ON CONFLICT REPLACE
);
INSERT INTO `inventory` VALUES ('hash', "pubkey", 1, 1,'test');
CREATE TABLE IF NOT EXISTS `pubkeys` (
`objecttype` int,
UNIQUE(objecttype) ON CONFLICT REPLACE
) ;

View File

@ -0,0 +1,8 @@
CREATE TABLE IF NOT EXISTS `knownnodes` (
`hash` blob NOT NULL,
`objecttype` int DEFAULT NULL,
`streamnumber` int NOT NULL,
`payload` blob DEFAULT NULL,
`integer` integer NOT NULL,
UNIQUE(hash) ON CONFLICT REPLACE
) ;

View File

@ -0,0 +1,15 @@
CREATE TABLE IF NOT EXISTS `inventory` (
`hash` blob NOT NULL,
`objecttype` int DEFAULT NULL,
`streamnumber` int NOT NULL,
`payload` blob DEFAULT NULL,
`integer` integer NOT NULL,
UNIQUE(hash) ON CONFLICT REPLACE
) ;
INSERT INTO `inventory` VALUES ('hash', 1, 1, 1,'test');
CREATE TABLE IF NOT EXISTS `objectprocessorqueue` (
`objecttype` int,
UNIQUE(objecttype) ON CONFLICT REPLACE
) ;

View File

@ -0,0 +1,43 @@
CREATE TABLE IF NOT EXISTS `inventory` (
`hash` blob NOT NULL,
`objecttype` int DEFAULT NULL,
`streamnumber` int NOT NULL,
`payload` blob DEFAULT NULL,
`integer` integer NOT NULL,
UNIQUE(hash) ON CONFLICT REPLACE
) ;
INSERT INTO `inventory` VALUES ('hash', 1, 1, 1,'test');
CREATE TABLE IF NOT EXISTS `pubkeys` (
`hash` text,
`addressversion` int,
`transmitdata` blob,
`time` int,
`usedpersonally` text,
UNIQUE(hash) ON CONFLICT REPLACE
) ;
INSERT INTO `pubkeys` VALUES ('hash','1','1','1','test');
CREATE TABLE IF NOT EXISTS `sent` (
`msgid` blob DEFAULT NULL,
`toaddress` text DEFAULT NULL,
`toripe` blob DEFAULT NULL,
`fromaddress` text DEFAULT NULL,
`subject` text DEFAULT NULL,
`message` text DEFAULT NULL,
`ackdata` blob DEFAULT NULL,
`senttime` integer DEFAULT NULL,
`lastactiontime` integer DEFAULT NULL,
`sleeptill` integer DEFAULT NULL,
`status` text DEFAULT NULL,
`retrynumber` integer DEFAULT NULL,
`folder` text DEFAULT NULL,
`encodingtype` int DEFAULT NULL,
`ttl` int DEFAULT NULL
) ;
INSERT INTO `sent` VALUES
('msgid','toaddress','toripe','fromaddress','subject','message','ackdata','senttime','lastactiontime','sleeptill','doingmsgpow','retrynumber','folder','encodingtype','ttl'),
('msgid','toaddress','toripe','fromaddress','subject','message','ackdata','senttime','lastactiontime','sleeptill','badkey','retrynumber','folder','encodingtype','ttl');

View File

@ -0,0 +1,12 @@
CREATE TABLE IF NOT EXISTS `inbox` (
`msgid` blob NOT NULL,
`toaddress` text DEFAULT NULL,
`fromaddress` text DEFAULT NULL,
`subject` text DEFAULT NULL,
`received` text DEFAULT NULL,
`message` text DEFAULT NULL,
`folder` text DEFAULT NULL,
`encodingtype` int DEFAULT NULL,
`read` bool DEFAULT NULL,
UNIQUE(msgid) ON CONFLICT REPLACE
) ;

View File

@ -0,0 +1,27 @@
CREATE TABLE IF NOT EXISTS `sent` (
`msgid` blob NOT NULL,
`toaddress` text DEFAULT NULL,
`toripe` blob DEFAULT NULL,
`fromaddress` text DEFAULT NULL,
`subject` text DEFAULT NULL,
`message` text DEFAULT NULL,
`ackdata` blob DEFAULT NULL,
`senttime` integer DEFAULT NULL,
`lastactiontime` integer DEFAULT NULL,
`sleeptill` integer DEFAULT NULL,
`status` text DEFAULT NULL,
`retrynumber` integer DEFAULT NULL,
`folder` text DEFAULT NULL,
`encodingtype` int DEFAULT NULL,
`ttl` int DEFAULT NULL,
UNIQUE(msgid) ON CONFLICT REPLACE
) ;
CREATE TABLE IF NOT EXISTS `pubkeys` (
`hash` text,
`addressversion` int,
`transmitdata` blob,
`time` int,
`usedpersonally` text,
UNIQUE(hash) ON CONFLICT REPLACE
) ;

View File

@ -0,0 +1,25 @@
CREATE TEMPORARY TABLE `pubkeys_backup` (
`address` text DEFAULT NULL,
`addressversion` int DEFAULT NULL,
`transmitdata` blob DEFAULT NULL,
`time` int DEFAULT NULL,
`usedpersonally` text DEFAULT NULL,
UNIQUE(address) ON CONFLICT REPLACE
) ;
INSERT INTO pubkeys_backup SELECT address, addressversion, transmitdata, time, usedpersonally FROM pubkeys;
DROP TABLE pubkeys;
CREATE TABLE `pubkeys` (
`address` text DEFAULT NULL,
`addressversion` int DEFAULT NULL,
`transmitdata` blob DEFAULT NULL,
`time` int DEFAULT NULL,
`usedpersonally` text DEFAULT NULL,
UNIQUE(address) ON CONFLICT REPLACE
);
INSERT INTO pubkeys SELECT address, addressversion, transmitdata, time, usedpersonally FROM pubkeys_backup;
DROP TABLE pubkeys_backup;

View File

@ -5,9 +5,8 @@
import os import os
import unittest import unittest
from ..helper_sql import sqlStoredProcedure, sql_ready, sqlExecute, SqlBulkExecute, sqlQuery, sqlExecuteScript from ..helper_sql import sqlStoredProcedure, sql_ready, sqlExecute, SqlBulkExecute, sqlQuery, sqlExecuteScript
from ..class_sqlThread import (sqlThread) from ..class_sqlThread import (sqlThread, UpgradeDB)
from ..addresses import encodeAddress from ..addresses import encodeAddress
import threading import threading
class TestSqlThread(unittest.TestCase): class TestSqlThread(unittest.TestCase):
@ -18,8 +17,6 @@ class TestSqlThread(unittest.TestCase):
# query file path # query file path
root_path = os.path.dirname(os.path.dirname(__file__)) root_path = os.path.dirname(os.path.dirname(__file__))
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
# Start SQL thread # Start SQL thread
@ -28,7 +25,6 @@ class TestSqlThread(unittest.TestCase):
sqlLookup.start() sqlLookup.start()
sql_ready.wait() sql_ready.wait()
@classmethod @classmethod
def setUp(cls): def setUp(cls):
tables = list(sqlQuery("select name from sqlite_master where type is 'table'")) tables = list(sqlQuery("select name from sqlite_master where type is 'table'"))
@ -52,10 +48,35 @@ class TestSqlThread(unittest.TestCase):
Initialise DB Initialise DB
""" """
with open(os.path.join(self.root_path, "tests/sql/{}.sql".format(file)), 'r') as sql_as_string: with open(os.path.join(self.root_path, "tests/sql/{}.sql".format(file)), 'r') as sql_as_string:
# sql_as_string = open(os.path.join(self.root_path, "tests/sql/{}.sql".format(file))).read()
sql_as_string = sql_as_string.read() sql_as_string = sql_as_string.read()
sqlExecuteScript(sql_as_string) sqlExecuteScript(sql_as_string)
def versioning(func):
def wrapper(*args):
self = args[0]
func_name = func.__name__
version = func_name.rsplit('_', 1)[-1]
# Update versions DB mocking
self.initialise_database("init_version_{}".format(version))
if int(version) == 9:
sqlThread().create_function()
# Test versions
upgrade_db = UpgradeDB()
getattr(upgrade_db, "upgrade_schema_data_{}".format(version))()
ret = func(*args)
return ret # <-- use (self, ...)
return wrapper
def filter_table_column(self, schema, column):
for x in schema:
for y in x:
if y == column:
yield y
def test_create_function(self): def test_create_function(self):
# call create function # call create function
encoded_str = encodeAddress(4, 1, "21122112211221122112") encoded_str = encodeAddress(4, 1, "21122112211221122112")
@ -66,10 +87,129 @@ class TestSqlThread(unittest.TestCase):
sqlExecute('''INSERT INTO testhash (addressversion, hash) VALUES(4, "21122112211221122112")''') sqlExecute('''INSERT INTO testhash (addressversion, hash) VALUES(4, "21122112211221122112")''')
# call function in query # call function in query
# sqlExecute('''UPDATE testhash SET address=(enaddr(testhash.addressversion, 1, hash)) WHERE hash=testhash.hash''')
sqlExecute('''UPDATE testhash SET address=(enaddr(testhash.addressversion, 1, hash));''') sqlExecute('''UPDATE testhash SET address=(enaddr(testhash.addressversion, 1, hash));''')
# Assertion # Assertion
query = sqlQuery('''select * from testhash;''') query = sqlQuery('''select * from testhash;''')
self.assertEqual(query[0][-1], encoded_str, "test case fail for create_function") self.assertEqual(query[0][-1], encoded_str, "test case fail for create_function")
sqlExecute('''DROP TABLE testhash''') sqlExecute('''DROP TABLE testhash''')
@versioning
def test_sql_thread_version_1(self):
"""
Test with version 1
Version 1 and 3 are same so will skip 3
"""
# Assertion after versioning
res = sqlQuery('''PRAGMA table_info('inventory');''')
result = list(self.filter_table_column(res, "tag"))
res = [tup for tup in res if any(i in tup for i in ["tag"])]
self.assertEqual(result, ['tag'], "Data not migrated for version 1")
self.assertEqual(res, [(5, 'tag', 'blob', 0, "''", 0)], "Data not migrated for version 1")
@versioning
def test_sql_thread_version_2(self):
"""
Test with version 2
"""
# Assertion
res = sqlQuery(''' SELECT count(name) FROM sqlite_master WHERE type='table' AND name='inventory_backup' ''')
self.assertNotEqual(res[0][0], 1, "Table inventory_backup not deleted in versioning 2")
@versioning
def test_sql_thread_version_4(self):
"""
Test with version 4
"""
# Assertion
res = sqlQuery('''select * from inventory where objecttype = 'pubkey';''')
self.assertNotEqual(len(res), 1, "Table inventory not deleted in versioning 4")
@versioning
def test_sql_thread_version_5(self):
"""
Test with version 5
"""
# Assertion
res = sqlQuery(''' SELECT count(name) FROM sqlite_master WHERE type='table' AND name='knownnodes' ''')
self.assertNotEqual(res[0][0], 1, "Table knownnodes not deleted in versioning 5")
res = sqlQuery(''' SELECT count(name) FROM sqlite_master WHERE type='table' AND name='objectprocessorqueue'; ''')
self.assertNotEqual(len(res), 0, "Table objectprocessorqueue not created in versioning 5")
@versioning
def test_sql_thread_version_6(self):
"""
Test with version 6
"""
# Assertion
inventory = sqlQuery('''PRAGMA table_info('inventory');''')
inventory = list(self.filter_table_column(inventory, "expirestime"))
self.assertEqual(inventory, ['expirestime'], "Data not migrated for version 6")
objectprocessorqueue = sqlQuery('''PRAGMA table_info('inventory');''')
objectprocessorqueue = list(self.filter_table_column(objectprocessorqueue, "objecttype"))
self.assertEqual(objectprocessorqueue, ['objecttype'], "Data not migrated for version 6")
@versioning
def test_sql_thread_version_7(self):
"""
Test with version 7
"""
# Assertion
pubkeys = sqlQuery('''SELECT * FROM pubkeys ''')
self.assertEqual(pubkeys, [], "Data not migrated for version 7")
inventory = sqlQuery('''SELECT * FROM inventory ''')
self.assertEqual(inventory, [], "Data not migrated for version 7")
sent = sqlQuery('''SELECT status FROM sent ''')
self.assertEqual(sent, [('msgqueued',), ('msgqueued',)], "Data not migrated for version 7")
@versioning
def test_sql_thread_version_8(self):
"""
Test with version 8
"""
# Assertion
res = sqlQuery('''PRAGMA table_info('inbox');''')
result = list(self.filter_table_column(res, "sighash"))
self.assertEqual(result, ['sighash'], "Data not migrated for version 8")
@versioning
def test_sql_thread_version_9(self):
"""
Test with version 9
"""
# Assertion
res = sqlQuery(''' SELECT count(name) FROM sqlite_master WHERE type='table' AND name='pubkeys_backup' ''')
self.assertNotEqual(res[0][0], 1, "Table pubkeys_backup not deleted")
res = sqlQuery('''PRAGMA table_info('pubkeys');''')
# res = res.fetchall()
result = list(self.filter_table_column(res, "address"))
self.assertEqual(result, ['address'], "Data not migrated for version 9")
@versioning
def test_sql_thread_version_10(self):
"""
Test with version 10
"""
# Assertion
res = sqlQuery(''' SELECT count(name) FROM sqlite_master WHERE type='table' AND name='old_addressbook' ''')
self.assertNotEqual(res[0][0], 1, "Table old_addressbook not deleted")
self.assertEqual(len(res), 1, "Table old_addressbook not deleted")
res = sqlQuery('''PRAGMA table_info('addressbook');''')
result = list(self.filter_table_column(res, "address"))
self.assertEqual(result, ['address'], "Data not migrated for version 10")