summaryrefslogtreecommitdiff
blob: 22ae5f3f09672d35e94f3ebbd1438f1c6ea7ec10 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
#!/usr/bin/python
# -*- coding: utf-8 -*-
#################################################################################
# LAYMAN SQLite DB
#################################################################################
# File:       sqlite_db.py
#
#             Access SQLite overlay database(s).
#
# Copyright:
#             (c) 2015        Devan Franchini
#             Distributed under the terms of the GNU General Public License v2
#
# Author(s):
#             Devan Franchini <twitch153@gentoo.org>
#
'''Handler for sqlite overlay databases.'''

from __future__ import unicode_literals

__version__ = "$Id: sqlite_db.py 273 2015-08-03 20:17:30Z twitch153 $"

#===============================================================================
#
# Dependencies
#
#-------------------------------------------------------------------------------

import os
import sys
import sqlite3

from   layman.overlays.overlay   import Overlay

#py3.2+
if sys.hexversion >= 0x30200f0:
    _UNICODE = 'unicode'
else:
    _UNICODE = 'UTF-8'


#===============================================================================
#
# Class DBHandler
#
#-------------------------------------------------------------------------------

class DBHandler(object):
    '''
    Handle a SQLite overlay database.
    '''

    def __init__(self, config, overlays, paths=None, ignore=0,
                 ignore_init_read_errors=False):

        self.config = config
        self.ignore = ignore
        self.overlays = overlays
        self.paths = paths
        self.output = config['output']
        self.ignore_init_read_errors = ignore_init_read_errors

        self.output.debug('Initializing SQLite overlay list handler', 8)


    def __connect__(self, path):
        '''
        Establish connection with the SQLite database.
        '''
        if not os.path.exists(path):
            if not self.ignore_init_read_errors:
                msg = 'SQLite DBHandler warning; database previously '\
                      'non-existent.\nCreating database now...'
                self.output.warn(msg, 2)

            if not os.access(os.path.dirname(path), os.W_OK):
                msg = 'SQLite DBHandler error; cannot create database.\n'
                errmsg = 'Write permissions are not given in dir: "%(dir)s"'\
                      % {'dir': os.path.dirname(path)}
                self.output.error(msg + errmsg)
                
                raise Exception(errmsg)

        if os.path.exists(path) and not os.access(path, os.R_OK):
            msg = 'SQLite DBHandler error; database lacks read permissions'\
                  ' cannot continue.'
            self.output.error(msg)

            raise Exception(msg)

        self.__create_database__(path)

        return sqlite3.connect(path)


    def __create_database__(self, path):
        '''
        Create the LaymanOverlays database if it doesn't exist.
        '''
        with sqlite3.connect(path) as connection:
            cursor = connection.cursor()
            try:
                cursor.execute('''CREATE TABLE IF NOT EXISTS Overlay
                ( Overlay_ID INTEGER PRIMARY KEY AUTOINCREMENT, Name TEXT, 
                Priority TEXT, Status TEXT, Quality TEXT, Homepage 
                TEXT, IRC TEXT, License TEXT, UNIQUE (Name) ON CONFLICT IGNORE )
                ''')
                cursor.execute('''CREATE TABLE IF NOT EXISTS Owner ( Owner_ID
                INTEGER PRIMARY KEY AUTOINCREMENT, Owner_Name TEXT, 
                Owner_Email TEXT, UNIQUE (Owner_Name, Owner_Email) ON 
                CONFLICT IGNORE )''')
                cursor.execute('''CREATE TABLE IF NOT EXISTS Source ( Source_ID
                INTEGER PRIMARY KEY AUTOINCREMENT, Type TEXT, Branch TEXT, 
                URL TEXT, UNIQUE (Type, URL) ON CONFLICT IGNORE )''')
                cursor.execute('''CREATE TABLE IF NOT EXISTS Description 
                ( Description_ID INTEGER PRIMARY KEY AUTOINCREMENT, 
                Overlay_ID INTEGER, Description TEXT, FOREIGN 
                KEY(Overlay_ID) REFERENCES Overlay(Overlay_ID), 
                UNIQUE (Overlay_ID, Description) ON CONFLICT IGNORE )''')
                cursor.execute('''CREATE TABLE IF NOT EXISTS Feed ( Feed_ID 
                INTEGER PRIMARY KEY AUTOINCREMENT, Overlay_ID INTEGER, 
                Feed TEXT, FOREIGN KEY(Overlay_ID) REFERENCES 
                Overlay(Overlay_ID), UNIQUE (Overlay_ID, Feed) ON CONFLICT 
                IGNORE )''')
                cursor.execute('''CREATE TABLE IF NOT EXISTS Overlay_Source
                ( Overlay_Source_ID INTEGER PRIMARY KEY AUTOINCREMENT, 
                Overlay_ID INTEGER, Source_ID INTEGER, FOREIGN KEY(Overlay_ID) 
                REFERENCES Overlay(Overlay_ID), FOREIGN KEY(Source_ID) 
                REFERENCES Source(SourceID), UNIQUE (Overlay_ID, Source_ID) ON 
                CONFLICT IGNORE )''')
                cursor.execute('''CREATE TABLE IF NOT EXISTS Overlay_Owner
                ( Overlay_Owner_ID INTEGER PRIMARY KEY AUTOINCREMENT, 
                Overlay_ID INTEGER, Owner_ID INTEGER, FOREIGN KEY(Overlay_ID) 
                REFERENCES Overlay(Overlay_ID), FOREIGN KEY(Owner_ID) 
                REFERENCES Owner(Owner_ID), UNIQUE (Overlay_ID, Owner_ID) ON 
                CONFLICT IGNORE )''')

                connection.commit()
            except Exception as err:
                msg = 'SQLite DBHandler error; failed to create database.\n'\
                      'Error was: %(msg)s' % {'msg': err}
                self.output.error(msg)

                raise err


    def read_db(self, path, text=None):
        '''
        Read the overlay definitions from the database and generate overlays.
        '''
        cursor = None
        overlay_id = None
        overlay = {}

        with self.__connect__(path) as connection:
            cursor = connection.cursor()
            cursor.execute('''SELECT Overlay_ID, Name, Priority, Status, 
            Quality, Homepage, IRC, License FROM Overlay''')
            overlays_info = cursor.fetchall()
            connection.commit()

            for overlay_info in overlays_info:
                overlay = {}
                overlay_id = overlay_info[0]
                overlay['name'] = overlay_info[1]

                cursor.execute('''SELECT URL, Type, Branch FROM Overlay_Source 
                JOIN Overlay USING (Overlay_ID) JOIN Source USING (Source_ID) 
                WHERE Overlay_ID = ?''', (overlay_id,))
                overlay['source'] = cursor.fetchall()

                cursor.execute('''SELECT Owner_Email, Owner_Name FROM 
                Overlay_Owner JOIN Overlay USING (Overlay_ID) JOIN Owner USING 
                (Owner_ID) WHERE Overlay_ID = ?''', (overlay_id,))
                owner_info = cursor.fetchall()[0]

                overlay['owner_email'] = owner_info[0]

                if len(owner_info) > 1:
                    overlay['owner_name'] = owner_info[1]

                cursor.execute('''SELECT Description FROM Description JOIN 
                Overlay USING (Overlay_ID) WHERE Overlay_ID = ?''',
                (overlay_id,))
                overlay['description'] = cursor.fetchall()[0]

                overlay['status'] = overlay_info[3]
                overlay['quality'] = overlay_info[4]
                overlay['priority'] = overlay_info[2]
                overlay['license'] = overlay_info[7]
                overlay['homepage'] = overlay_info[5]
                overlay['IRC'] = overlay_info[6]

                cursor.execute('''SELECT Feed FROM Feed JOIN Overlay USING 
                (Overlay_ID) WHERE Overlay_ID = ?''', (overlay_id,))
                overlay['feed'] = cursor.fetchall()

                if len(overlay['feed']):
                    overlay['feed'] = overlay['feed'][0]

                self.overlays[overlay_info[1]] = Overlay(self.config,
                                                         ovl_dict=overlay,
                                                         ignore=self.ignore)


    def add_new(self, document=None, origin=None):
        '''
        Reads in provided sqlite text and generates overlays to populate
        database.
        '''
        if not document:
            msg = 'SQLite DBHandler - add_new() failed: can\'t add '\
                  'non-existent overlay(s).\nOrigin: %(path)s'\
                  % {'path': origin}
            self.output.warn(msg)

            return False

        return True


    def add_ovl(self, overlay, connection):
        '''
        Adds an overlay to the database.
        '''
        overlay_id = None
        owner_id = None
        source_ids = []
        cursor = None

        cursor = connection.cursor()
        cursor.execute('''INSERT INTO Overlay ( Name, Priority, Status, 
        Quality, Homepage, IRC, License ) VALUES ( ?, ?, ?, ?, ?, ?, ? )''',
        (overlay.name, overlay.priority, overlay.status, overlay.quality,
        overlay.homepage, overlay.irc, overlay.license,))
        connection.commit()

        cursor.execute('''SELECT Overlay_ID FROM Overlay WHERE Name = ?''',
        (overlay.name,))
        overlay_id = cursor.fetchone()[0]

        cursor.execute('''INSERT INTO Owner ( Owner_Name, Owner_Email ) 
        VALUES ( ?, ? )''', (overlay.owner_name, overlay.owner_email,))
        connection.commit()

        cursor.execute('''SELECT Owner_ID from Owner WHERE Owner_Email = ?;''',
        (overlay.owner_email,))
        owner_id = cursor.fetchone()[0]

        for source in overlay.sources:
            cursor.execute('''INSERT INTO Source ( Type, Branch, URL )
            VALUES ( ?, ?, ? )''', (source.type_key, source.branch,
            source.src,))
            connection.commit()
            cursor.execute('''SELECT Source_ID FROM Source WHERE URL = ?;''',
            (source.src,))
            source_ids.append(cursor.fetchone()[0])

        for description in overlay.descriptions:
            cursor.execute('''INSERT INTO Description ( Overlay_ID, 
            Description ) VALUES ( ?, ? )''', (overlay_id, description,))

        for feed in overlay.feeds:
            cursor.execute('''INSERT INTO Feed ( Overlay_ID, Feed ) VALUES ( ?,
             ? )''', (overlay_id, feed,))

        cursor.execute('''INSERT INTO Overlay_Owner ( Overlay_ID, Owner_ID ) 
        VALUES ( ?, ? )''', (overlay_id, owner_id,))

        for source_id in source_ids:
            cursor.execute('''INSERT INTO Overlay_Source ( Overlay_ID, 
            Source_ID ) VALUES ( ?, ? )''', (overlay_id, source_id,))

        connection.commit()


    def remove(self, overlay, path):
        '''
        Remove an overlay from the database.
        '''
        cursor = None
        overlay_id = 0
        owner_id = 0
        source_ids = []

        if overlay.name in self.overlays:
            del self.overlays[overlay.name]

        with self.__connect__(path) as connection:
            cursor = connection.cursor()
            
            cursor.execute('''SELECT Overlay_ID FROM Overlay WHERE Name = 
            ?''', (overlay.name,))
            overlay_id = cursor.fetchone()[0]

            cursor.execute('''SELECT Owner_ID FROM Overlay_Owner WHERE 
            Overlay_ID = ?''', (overlay_id,))
            owner_id = cursor.fetchone()[0]

            cursor.execute('''SELECT Source_ID FROM Overlay_Source WHERE 
            Overlay_ID = ?''', (overlay_id,))
            source_ids = cursor.fetchall()[0]

            cursor.execute('''DELETE FROM Feed WHERE Overlay_ID = ?''',
            (overlay_id,))
            cursor.execute('''DELETE FROM Description WHERE Overlay_ID = ?''',
            (overlay_id,))
            cursor.execute('''DELETE FROM Overlay_Source WHERE Overlay_ID = 
            ?''', (overlay_id,))
            cursor.execute('''DELETE FROM Overlay_Owner WHERE Overlay_ID = 
            ?''', (overlay_id,))

            for source_id in source_ids:
               cursor.execute('''DELETE FROM Source WHERE Source_ID = ?''', 
               (source_id,))

            cursor.execute('''DELETE FROM Owner WHERE Owner_ID = ?''',
            (owner_id,))
            cursor.execute('''DELETE FROM Overlay WHERE Overlay_ID = ?''',
            (overlay_id,))

            connection.commit()


    def write(self, path, remove=False):
        '''
        Write the list of overlays to the database.
        '''
        if remove:
            return

        try:
            with self.__connect__(path) as connection:
                for overlay in self.overlays:
                    self.add_ovl(self.overlays[overlay], connection)
        except Exception as err:
            msg = 'Failed to write to overlays database: %(path)s\nError was'\
                  ': %(err)s' % {'path': path, 'err': err}
            self.output.error(msg)
            raise err