Separate cursors for every transaction
This commit is contained in:
		
							parent
							
								
									13ae208a06
								
							
						
					
					
						commit
						109d5feef1
					
				
					 4 changed files with 132 additions and 85 deletions
				
			
		|  | @ -1,4 +1,5 @@ | ||||||
| import converter | import converter | ||||||
|  | import logging | ||||||
| import string | import string | ||||||
| import traceback | import traceback | ||||||
| 
 | 
 | ||||||
|  | @ -117,6 +118,8 @@ ADDITIONAL_MIMETYPES = { | ||||||
| DEFAULT_DATADIR = '.\\_etiquette' | DEFAULT_DATADIR = '.\\_etiquette' | ||||||
| 
 | 
 | ||||||
| DEFAULT_CONFIGURATION = { | DEFAULT_CONFIGURATION = { | ||||||
|  |     'log_level': logging.DEBUG, | ||||||
|  | 
 | ||||||
|     'min_tag_name_length': 1, |     'min_tag_name_length': 1, | ||||||
|     'max_tag_name_length': 32, |     'max_tag_name_length': 32, | ||||||
|     'valid_tag_chars': string.ascii_lowercase + string.digits + '_', |     'valid_tag_chars': string.ascii_lowercase + string.digits + '_', | ||||||
|  |  | ||||||
|  | @ -13,14 +13,14 @@ else: | ||||||
| 
 | 
 | ||||||
| if port == 443: | if port == 443: | ||||||
|     http = gevent.pywsgi.WSGIServer( |     http = gevent.pywsgi.WSGIServer( | ||||||
|         listener=('', port), |         listener=('0.0.0.0', port), | ||||||
|         application=etiquette.site, |         application=etiquette.site, | ||||||
|         keyfile='C:\\git\\etiquette\\etiquette\\https\\etiquette.key', |         keyfile='C:\\git\\etiquette\\etiquette\\https\\etiquette.key', | ||||||
|         certfile='C:\\git\\etiquette\\etiquette\\https\\etiquette.crt', |         certfile='C:\\git\\etiquette\\etiquette\\https\\etiquette.crt', | ||||||
|     ) |     ) | ||||||
| else: | else: | ||||||
|     http = gevent.pywsgi.WSGIServer( |     http = gevent.pywsgi.WSGIServer( | ||||||
|         listener=('', port), |         listener=('0.0.0.0', port), | ||||||
|         application=etiquette.site, |         application=etiquette.site, | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										123
									
								
								objects.py
									
									
									
									
									
								
							
							
						
						
									
										123
									
								
								objects.py
									
									
									
									
									
								
							|  | @ -43,8 +43,9 @@ class GroupableMixin: | ||||||
| 
 | 
 | ||||||
|         # Groupables are only allowed to have 1 parent. |         # Groupables are only allowed to have 1 parent. | ||||||
|         # Unlike photos which can exist in multiple albums. |         # Unlike photos which can exist in multiple albums. | ||||||
|         self.photodb.cur.execute('SELECT * FROM tag_group_rel WHERE memberid == ?', [member.id]) |         cur = self.photodb.sql.cursor() | ||||||
|         fetch = self.photodb.cur.fetchone() |         cur.execute('SELECT * FROM tag_group_rel WHERE memberid == ?', [member.id]) | ||||||
|  |         fetch = cur.fetchone() | ||||||
|         if fetch is not None: |         if fetch is not None: | ||||||
|             parent_id = fetch[constants.SQL_TAGGROUP['parentid']] |             parent_id = fetch[constants.SQL_TAGGROUP['parentid']] | ||||||
|             if parent_id == self.id: |             if parent_id == self.id: | ||||||
|  | @ -58,14 +59,15 @@ class GroupableMixin: | ||||||
|                 raise exceptions.RecursiveGrouping('%s is an ancestor of %s' % (member.name, self.name)) |                 raise exceptions.RecursiveGrouping('%s is an ancestor of %s' % (member.name, self.name)) | ||||||
| 
 | 
 | ||||||
|         self.photodb._cached_frozen_children = None |         self.photodb._cached_frozen_children = None | ||||||
|         self.photodb.cur.execute('INSERT INTO tag_group_rel VALUES(?, ?)', [self.id, member.id]) |         cur.execute('INSERT INTO tag_group_rel VALUES(?, ?)', [self.id, member.id]) | ||||||
|         if commit: |         if commit: | ||||||
|             self.photodb.log.debug('Commiting - add to group') |             self.photodb.log.debug('Commiting - add to group') | ||||||
|             self.photodb.commit() |             self.photodb.commit() | ||||||
| 
 | 
 | ||||||
|     def children(self): |     def children(self): | ||||||
|         self.photodb.cur.execute('SELECT * FROM tag_group_rel WHERE parentid == ?', [self.id]) |         cur = self.photodb.sql.cursor() | ||||||
|         fetch = self.photodb.cur.fetchall() |         cur.execute('SELECT * FROM tag_group_rel WHERE parentid == ?', [self.id]) | ||||||
|  |         fetch = cur.fetchall() | ||||||
|         results = [] |         results = [] | ||||||
|         for f in fetch: |         for f in fetch: | ||||||
|             memberid = f[constants.SQL_TAGGROUP['memberid']] |             memberid = f[constants.SQL_TAGGROUP['memberid']] | ||||||
|  | @ -91,6 +93,7 @@ class GroupableMixin: | ||||||
|             Otherwise they'll just be raised up one level. |             Otherwise they'll just be raised up one level. | ||||||
|         ''' |         ''' | ||||||
|         self.photodb._cached_frozen_children = None |         self.photodb._cached_frozen_children = None | ||||||
|  |         cur = self.photodb.sql.cursor() | ||||||
|         if delete_children: |         if delete_children: | ||||||
|             for child in self.children(): |             for child in self.children(): | ||||||
|                 child.delete(delete_children=delete_children, commit=False) |                 child.delete(delete_children=delete_children, commit=False) | ||||||
|  | @ -99,15 +102,15 @@ class GroupableMixin: | ||||||
|             parent = self.parent() |             parent = self.parent() | ||||||
|             if parent is None: |             if parent is None: | ||||||
|                 # Since this group was a root, children become roots by removing the row. |                 # Since this group was a root, children become roots by removing the row. | ||||||
|                 self.photodb.cur.execute('DELETE FROM tag_group_rel WHERE parentid == ?', [self.id]) |                 cur.execute('DELETE FROM tag_group_rel WHERE parentid == ?', [self.id]) | ||||||
|             else: |             else: | ||||||
|                 # Since this group was a child, its parent adopts all its children. |                 # Since this group was a child, its parent adopts all its children. | ||||||
|                 self.photodb.cur.execute( |                 cur.execute( | ||||||
|                     'UPDATE tag_group_rel SET parentid == ? WHERE parentid == ?', |                     'UPDATE tag_group_rel SET parentid == ? WHERE parentid == ?', | ||||||
|                     [parent.id, self.id] |                     [parent.id, self.id] | ||||||
|                 ) |                 ) | ||||||
|         # Note that this part comes after the deletion of children to prevent issues of recursion. |         # Note that this part comes after the deletion of children to prevent issues of recursion. | ||||||
|         self.photodb.cur.execute('DELETE FROM tag_group_rel WHERE memberid == ?', [self.id]) |         cur.execute('DELETE FROM tag_group_rel WHERE memberid == ?', [self.id]) | ||||||
|         if commit: |         if commit: | ||||||
|             self.photodb.log.debug('Committing - delete tag') |             self.photodb.log.debug('Committing - delete tag') | ||||||
|             self.photodb.commit() |             self.photodb.commit() | ||||||
|  | @ -117,8 +120,9 @@ class GroupableMixin: | ||||||
|         Return the group of which this is a member, or None. |         Return the group of which this is a member, or None. | ||||||
|         Returned object will be of the same type as calling object. |         Returned object will be of the same type as calling object. | ||||||
|         ''' |         ''' | ||||||
|         self.photodb.cur.execute('SELECT * FROM tag_group_rel WHERE memberid == ?', [self.id]) |         cur = self.photodb.sql.cursor() | ||||||
|         fetch = self.photodb.cur.fetchone() |         cur.execute('SELECT * FROM tag_group_rel WHERE memberid == ?', [self.id]) | ||||||
|  |         fetch = cur.fetchone() | ||||||
|         if fetch is None: |         if fetch is None: | ||||||
|             return None |             return None | ||||||
| 
 | 
 | ||||||
|  | @ -144,8 +148,9 @@ class GroupableMixin: | ||||||
|         ''' |         ''' | ||||||
|         Leave the current group and become independent. |         Leave the current group and become independent. | ||||||
|         ''' |         ''' | ||||||
|  |         cur = self.photodb.sql.cursor() | ||||||
|         self.photodb._cached_frozen_children = None |         self.photodb._cached_frozen_children = None | ||||||
|         self.photodb.cur.execute('DELETE FROM tag_group_rel WHERE memberid == ?', [self.id]) |         cur.execute('DELETE FROM tag_group_rel WHERE memberid == ?', [self.id]) | ||||||
|         if commit: |         if commit: | ||||||
|             self.photodb.log.debug('Committing - leave group') |             self.photodb.log.debug('Committing - leave group') | ||||||
|             self.photodb.commit() |             self.photodb.commit() | ||||||
|  | @ -184,7 +189,8 @@ class Album(ObjectBase, GroupableMixin): | ||||||
|             raise ValueError('Not the same PhotoDB') |             raise ValueError('Not the same PhotoDB') | ||||||
|         if self.has_photo(photo): |         if self.has_photo(photo): | ||||||
|             return |             return | ||||||
|         self.photodb.cur.execute('INSERT INTO album_photo_rel VALUES(?, ?)', [self.id, photo.id]) |         cur = self.photodb.sql.cursor() | ||||||
|  |         cur.execute('INSERT INTO album_photo_rel VALUES(?, ?)', [self.id, photo.id]) | ||||||
|         if commit: |         if commit: | ||||||
|             self.photodb.log.debug('Committing - add photo to album') |             self.photodb.log.debug('Committing - add photo to album') | ||||||
|             self.photodb.commit() |             self.photodb.commit() | ||||||
|  | @ -205,8 +211,9 @@ class Album(ObjectBase, GroupableMixin): | ||||||
|     def delete(self, *, delete_children=False, commit=True): |     def delete(self, *, delete_children=False, commit=True): | ||||||
|         self.photodb.log.debug('Deleting album {album:r}'.format(album=self)) |         self.photodb.log.debug('Deleting album {album:r}'.format(album=self)) | ||||||
|         GroupableMixin.delete(self, delete_children=delete_children, commit=False) |         GroupableMixin.delete(self, delete_children=delete_children, commit=False) | ||||||
|         self.photodb.cur.execute('DELETE FROM albums WHERE id == ?', [self.id]) |         cur = self.photodb.sql.cursor() | ||||||
|         self.photodb.cur.execute('DELETE FROM album_photo_rel WHERE albumid == ?', [self.id]) |         cur.execute('DELETE FROM albums WHERE id == ?', [self.id]) | ||||||
|  |         cur.execute('DELETE FROM album_photo_rel WHERE albumid == ?', [self.id]) | ||||||
|         if commit: |         if commit: | ||||||
|             self.photodb.log.debug('Committing - delete album') |             self.photodb.log.debug('Committing - delete album') | ||||||
|             self.photodb.commit() |             self.photodb.commit() | ||||||
|  | @ -216,7 +223,7 @@ class Album(ObjectBase, GroupableMixin): | ||||||
|             title = self.title |             title = self.title | ||||||
|         if description is None: |         if description is None: | ||||||
|             description = self.description |             description = self.description | ||||||
|         self.photodb.cur.execute( |         cur.execute( | ||||||
|             'UPDATE albums SET title=?, description=? WHERE id == ?', |             'UPDATE albums SET title=?, description=? WHERE id == ?', | ||||||
|             [title, description, self.id] |             [title, description, self.id] | ||||||
|         ) |         ) | ||||||
|  | @ -229,11 +236,12 @@ class Album(ObjectBase, GroupableMixin): | ||||||
|     def has_photo(self, photo): |     def has_photo(self, photo): | ||||||
|         if not isinstance(photo, Photo): |         if not isinstance(photo, Photo): | ||||||
|             raise TypeError('Must be a %s' % Photo) |             raise TypeError('Must be a %s' % Photo) | ||||||
|         self.photodb.cur.execute( |         cur = self.photodb.sql.cursor() | ||||||
|  |         cur.execute( | ||||||
|             'SELECT * FROM album_photo_rel WHERE albumid == ? AND photoid == ?', |             'SELECT * FROM album_photo_rel WHERE albumid == ? AND photoid == ?', | ||||||
|             [self.id, photo.id] |             [self.id, photo.id] | ||||||
|         ) |         ) | ||||||
|         return self.photodb.cur.fetchone() is not None |         return cur.fetchone() is not None | ||||||
| 
 | 
 | ||||||
|     def photos(self): |     def photos(self): | ||||||
|         photos = [] |         photos = [] | ||||||
|  | @ -252,7 +260,8 @@ class Album(ObjectBase, GroupableMixin): | ||||||
|     def remove_photo(self, photo, *, commit=True): |     def remove_photo(self, photo, *, commit=True): | ||||||
|         if not self.has_photo(photo): |         if not self.has_photo(photo): | ||||||
|             return |             return | ||||||
|         self.photodb.cur.execute( |         cur = self.photodb.sql.cursor() | ||||||
|  |         cur.execute( | ||||||
|             'DELETE FROM album_photo_rel WHERE albumid == ? AND photoid == ?', |             'DELETE FROM album_photo_rel WHERE albumid == ? AND photoid == ?', | ||||||
|             [self.id, photo.id] |             [self.id, photo.id] | ||||||
|         ) |         ) | ||||||
|  | @ -309,8 +318,9 @@ class Photo(ObjectBase): | ||||||
|         ''' |         ''' | ||||||
|         Reload the row from the database and do __init__ with them. |         Reload the row from the database and do __init__ with them. | ||||||
|         ''' |         ''' | ||||||
|         self.photodb.cur.execute('SELECT * FROM photos WHERE id == ?', [self.id]) |         cur = self.photodb.sql.cursor() | ||||||
|         row = self.photodb.cur.fetchone() |         cur.execute('SELECT * FROM photos WHERE id == ?', [self.id]) | ||||||
|  |         row = cur.fetchone() | ||||||
|         self.__init__(self.photodb, row) |         self.__init__(self.photodb, row) | ||||||
| 
 | 
 | ||||||
|     def __repr__(self): |     def __repr__(self): | ||||||
|  | @ -338,8 +348,9 @@ class Photo(ObjectBase): | ||||||
| 
 | 
 | ||||||
|         self.photodb.log.debug('Applying tag {tag:s} to photo {pho:s}'.format(tag=tag, pho=self)) |         self.photodb.log.debug('Applying tag {tag:s} to photo {pho:s}'.format(tag=tag, pho=self)) | ||||||
|         now = int(helpers.now()) |         now = int(helpers.now()) | ||||||
|         self.photodb.cur.execute('INSERT INTO photo_tag_rel VALUES(?, ?)', [self.id, tag.id]) |         cur = self.photodb.sql.cursor() | ||||||
|         self.photodb.cur.execute('UPDATE photos SET tagged_at = ? WHERE id == ?', [now, self.id]) |         cur.execute('INSERT INTO photo_tag_rel VALUES(?, ?)', [self.id, tag.id]) | ||||||
|  |         cur.execute('UPDATE photos SET tagged_at = ? WHERE id == ?', [now, self.id]) | ||||||
|         if commit: |         if commit: | ||||||
|             self.photodb.log.debug('Committing - add photo tag') |             self.photodb.log.debug('Committing - add photo tag') | ||||||
|             self.photodb.commit() |             self.photodb.commit() | ||||||
|  | @ -348,8 +359,9 @@ class Photo(ObjectBase): | ||||||
|         ''' |         ''' | ||||||
|         Return the albums of which this photo is a member. |         Return the albums of which this photo is a member. | ||||||
|         ''' |         ''' | ||||||
|         self.photodb.cur.execute('SELECT albumid FROM album_photo_rel WHERE photoid == ?', [self.id]) |         cur = self.photodb.sql.cursor() | ||||||
|         fetch = self.photodb.cur.fetchall() |         cur.execute('SELECT albumid FROM album_photo_rel WHERE photoid == ?', [self.id]) | ||||||
|  |         fetch = cur.fetchall() | ||||||
|         albums = [self.photodb.get_album(f[0]) for f in fetch] |         albums = [self.photodb.get_album(f[0]) for f in fetch] | ||||||
|         return albums |         return albums | ||||||
| 
 | 
 | ||||||
|  | @ -368,9 +380,10 @@ class Photo(ObjectBase): | ||||||
|         Delete the Photo and its relation to any tags and albums. |         Delete the Photo and its relation to any tags and albums. | ||||||
|         ''' |         ''' | ||||||
|         self.photodb.log.debug('Deleting photo {photo:r}'.format(photo=self)) |         self.photodb.log.debug('Deleting photo {photo:r}'.format(photo=self)) | ||||||
|         self.photodb.cur.execute('DELETE FROM photos WHERE id == ?', [self.id]) |         cur = self.photodb.sql.cursor() | ||||||
|         self.photodb.cur.execute('DELETE FROM photo_tag_rel WHERE photoid == ?', [self.id]) |         cur.execute('DELETE FROM photos WHERE id == ?', [self.id]) | ||||||
|         self.photodb.cur.execute('DELETE FROM album_photo_rel WHERE photoid == ?', [self.id]) |         cur.execute('DELETE FROM photo_tag_rel WHERE photoid == ?', [self.id]) | ||||||
|  |         cur.execute('DELETE FROM album_photo_rel WHERE photoid == ?', [self.id]) | ||||||
| 
 | 
 | ||||||
|         if delete_file: |         if delete_file: | ||||||
|             path = self.real_path.absolute_path |             path = self.real_path.absolute_path | ||||||
|  | @ -453,7 +466,8 @@ class Photo(ObjectBase): | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|         if return_filepath != self.thumbnail: |         if return_filepath != self.thumbnail: | ||||||
|             self.photodb.cur.execute( |             cur = self.photodb.sql.cursor() | ||||||
|  |             cur.execute( | ||||||
|                 'UPDATE photos SET thumbnail = ? WHERE id == ?', |                 'UPDATE photos SET thumbnail = ? WHERE id == ?', | ||||||
|                 [return_filepath, self.id] |                 [return_filepath, self.id] | ||||||
|             ) |             ) | ||||||
|  | @ -480,12 +494,13 @@ class Photo(ObjectBase): | ||||||
|         else: |         else: | ||||||
|             tags = [tag] |             tags = [tag] | ||||||
| 
 | 
 | ||||||
|  |         cur = self.photodb.sql.cursor() | ||||||
|         for tag in tags: |         for tag in tags: | ||||||
|             self.photodb.cur.execute( |             cur.execute( | ||||||
|                 'SELECT * FROM photo_tag_rel WHERE photoid == ? AND tagid == ?', |                 'SELECT * FROM photo_tag_rel WHERE photoid == ? AND tagid == ?', | ||||||
|                 [self.id, tag.id] |                 [self.id, tag.id] | ||||||
|             ) |             ) | ||||||
|             if self.photodb.cur.fetchone() is not None: |             if cur.fetchone() is not None: | ||||||
|                 return tag |                 return tag | ||||||
| 
 | 
 | ||||||
|         return False |         return False | ||||||
|  | @ -545,7 +560,8 @@ class Photo(ObjectBase): | ||||||
|             self.area = self.width * self.height |             self.area = self.width * self.height | ||||||
|             self.ratio = round(self.width / self.height, 2) |             self.ratio = round(self.width / self.height, 2) | ||||||
| 
 | 
 | ||||||
|         self.photodb.cur.execute( |         cur = self.photodb.sql.cursor() | ||||||
|  |         cur.execute( | ||||||
|             'UPDATE photos SET width=?, height=?, area=?, ratio=?, duration=?, bytes=? WHERE id==?', |             'UPDATE photos SET width=?, height=?, area=?, ratio=?, duration=?, bytes=? WHERE id==?', | ||||||
|             [self.width, self.height, self.area, self.ratio, self.duration, self.bytes, self.id], |             [self.width, self.height, self.area, self.ratio, self.duration, self.bytes, self.id], | ||||||
|         ) |         ) | ||||||
|  | @ -558,13 +574,15 @@ class Photo(ObjectBase): | ||||||
| 
 | 
 | ||||||
|         self.photodb.log.debug('Removing tag {t} from photo {p}'.format(t=repr(tag), p=repr(self))) |         self.photodb.log.debug('Removing tag {t} from photo {p}'.format(t=repr(tag), p=repr(self))) | ||||||
|         tags = list(tag.walk_children()) |         tags = list(tag.walk_children()) | ||||||
|  | 
 | ||||||
|  |         cur = self.photodb.sql.cursor() | ||||||
|         for tag in tags: |         for tag in tags: | ||||||
|             self.photodb.cur.execute( |             cur.execute( | ||||||
|                 'DELETE FROM photo_tag_rel WHERE photoid == ? AND tagid == ?', |                 'DELETE FROM photo_tag_rel WHERE photoid == ? AND tagid == ?', | ||||||
|                 [self.id, tag.id] |                 [self.id, tag.id] | ||||||
|             ) |             ) | ||||||
|         now = int(helpers.now()) |         now = int(helpers.now()) | ||||||
|         self.photodb.cur.execute('UPDATE photos SET tagged_at = ? WHERE id == ?', [now, self.id]) |         cur.execute('UPDATE photos SET tagged_at = ? WHERE id == ?', [now, self.id]) | ||||||
|         if commit: |         if commit: | ||||||
|             self.photodb.log.debug('Committing - remove photo tag') |             self.photodb.log.debug('Committing - remove photo tag') | ||||||
|             self.photodb.commit() |             self.photodb.commit() | ||||||
|  | @ -604,7 +622,8 @@ class Photo(ObjectBase): | ||||||
|             except OSError: |             except OSError: | ||||||
|                 spinal.copy_file(old_path, new_path) |                 spinal.copy_file(old_path, new_path) | ||||||
| 
 | 
 | ||||||
|         self.photodb.cur.execute( |         cur = self.photodb.sql.cursor() | ||||||
|  |         cur.execute( | ||||||
|             'UPDATE photos SET filepath = ? WHERE filepath == ?', |             'UPDATE photos SET filepath = ? WHERE filepath == ?', | ||||||
|             [new_path.absolute_path, old_path.absolute_path] |             [new_path.absolute_path, old_path.absolute_path] | ||||||
|         ) |         ) | ||||||
|  | @ -687,7 +706,8 @@ class Tag(ObjectBase, GroupableMixin): | ||||||
|             raise exceptions.TagExists(synname) |             raise exceptions.TagExists(synname) | ||||||
| 
 | 
 | ||||||
|         self.photodb._cached_frozen_children = None |         self.photodb._cached_frozen_children = None | ||||||
|         self.photodb.cur.execute('INSERT INTO tag_synonyms VALUES(?, ?)', [synname, self.name]) |         cur = self.photodb.sql.cursor() | ||||||
|  |         cur.execute('INSERT INTO tag_synonyms VALUES(?, ?)', [synname, self.name]) | ||||||
| 
 | 
 | ||||||
|         if commit: |         if commit: | ||||||
|             self.photodb.log.debug('Committing - add synonym') |             self.photodb.log.debug('Committing - add synonym') | ||||||
|  | @ -707,7 +727,8 @@ class Tag(ObjectBase, GroupableMixin): | ||||||
|         # Migrate the old tag's synonyms to the new one |         # Migrate the old tag's synonyms to the new one | ||||||
|         # UPDATE is safe for this operation because there is no chance of duplicates. |         # UPDATE is safe for this operation because there is no chance of duplicates. | ||||||
|         self.photodb._cached_frozen_children = None |         self.photodb._cached_frozen_children = None | ||||||
|         self.photodb.cur.execute( |         cur = self.photodb.sql.cursor() | ||||||
|  |         cur.execute( | ||||||
|             'UPDATE tag_synonyms SET mastername = ? WHERE mastername == ?', |             'UPDATE tag_synonyms SET mastername = ? WHERE mastername == ?', | ||||||
|             [mastertag.name, self.name] |             [mastertag.name, self.name] | ||||||
|         ) |         ) | ||||||
|  | @ -722,10 +743,10 @@ class Tag(ObjectBase, GroupableMixin): | ||||||
|         for relationship in generator: |         for relationship in generator: | ||||||
|             photoid = relationship[constants.SQL_PHOTOTAG['photoid']] |             photoid = relationship[constants.SQL_PHOTOTAG['photoid']] | ||||||
|             query = 'SELECT * FROM photo_tag_rel WHERE photoid == ? AND tagid == ?' |             query = 'SELECT * FROM photo_tag_rel WHERE photoid == ? AND tagid == ?' | ||||||
|             self.photodb.cur.execute(query, [photoid, mastertag.id]) |             cur.execute(query, [photoid, mastertag.id]) | ||||||
|             if self.photodb.cur.fetchone() is None: |             if cur.fetchone() is None: | ||||||
|                 query = 'INSERT INTO photo_tag_rel VALUES(?, ?)' |                 query = 'INSERT INTO photo_tag_rel VALUES(?, ?)' | ||||||
|                 self.photodb.cur.execute(query, [photoid, mastertag.id]) |                 cur.execute(query, [photoid, mastertag.id]) | ||||||
| 
 | 
 | ||||||
|         # Then delete the relationships with the old tag |         # Then delete the relationships with the old tag | ||||||
|         self.delete() |         self.delete() | ||||||
|  | @ -740,9 +761,10 @@ class Tag(ObjectBase, GroupableMixin): | ||||||
|         self.photodb.log.debug('Deleting tag {tag:r}'.format(tag=self)) |         self.photodb.log.debug('Deleting tag {tag:r}'.format(tag=self)) | ||||||
|         self.photodb._cached_frozen_children = None |         self.photodb._cached_frozen_children = None | ||||||
|         GroupableMixin.delete(self, delete_children=delete_children, commit=False) |         GroupableMixin.delete(self, delete_children=delete_children, commit=False) | ||||||
|         self.photodb.cur.execute('DELETE FROM tags WHERE id == ?', [self.id]) |         cur = self.photodb.sql.cursor() | ||||||
|         self.photodb.cur.execute('DELETE FROM photo_tag_rel WHERE tagid == ?', [self.id]) |         cur.execute('DELETE FROM tags WHERE id == ?', [self.id]) | ||||||
|         self.photodb.cur.execute('DELETE FROM tag_synonyms WHERE mastername == ?', [self.name]) |         cur.execute('DELETE FROM photo_tag_rel WHERE tagid == ?', [self.id]) | ||||||
|  |         cur.execute('DELETE FROM tag_synonyms WHERE mastername == ?', [self.name]) | ||||||
|         if commit: |         if commit: | ||||||
|             self.photodb.log.debug('Committing - delete tag') |             self.photodb.log.debug('Committing - delete tag') | ||||||
|             self.photodb.commit() |             self.photodb.commit() | ||||||
|  | @ -766,13 +788,14 @@ class Tag(ObjectBase, GroupableMixin): | ||||||
|         they always resolve to the master tag before application. |         they always resolve to the master tag before application. | ||||||
|         ''' |         ''' | ||||||
|         synname = self.photodb.normalize_tagname(synname) |         synname = self.photodb.normalize_tagname(synname) | ||||||
|         self.photodb.cur.execute('SELECT * FROM tag_synonyms WHERE name == ?', [synname]) |         cur = self.photodb.sql.cursor() | ||||||
|         fetch = self.photodb.cur.fetchone() |         cur.execute('SELECT * FROM tag_synonyms WHERE name == ?', [synname]) | ||||||
|  |         fetch = cur.fetchone() | ||||||
|         if fetch is None: |         if fetch is None: | ||||||
|             raise exceptions.NoSuchSynonym(synname) |             raise exceptions.NoSuchSynonym(synname) | ||||||
| 
 | 
 | ||||||
|         self.photodb._cached_frozen_children = None |         self.photodb._cached_frozen_children = None | ||||||
|         self.photodb.cur.execute('DELETE FROM tag_synonyms WHERE name == ?', [synname]) |         cur.execute('DELETE FROM tag_synonyms WHERE name == ?', [synname]) | ||||||
|         if commit: |         if commit: | ||||||
|             self.photodb.log.debug('Committing - remove synonym') |             self.photodb.log.debug('Committing - remove synonym') | ||||||
|             self.photodb.commit() |             self.photodb.commit() | ||||||
|  | @ -794,9 +817,10 @@ class Tag(ObjectBase, GroupableMixin): | ||||||
| 
 | 
 | ||||||
|         self._cached_qualified_name = None |         self._cached_qualified_name = None | ||||||
|         self.photodb._cached_frozen_children = None |         self.photodb._cached_frozen_children = None | ||||||
|         self.photodb.cur.execute('UPDATE tags SET name = ? WHERE id == ?', [new_name, self.id]) |         cur = self.photodb.sql.cursor() | ||||||
|  |         cur.execute('UPDATE tags SET name = ? WHERE id == ?', [new_name, self.id]) | ||||||
|         if apply_to_synonyms: |         if apply_to_synonyms: | ||||||
|             self.photodb.cur.execute( |             cur.execute( | ||||||
|                 'UPDATE tag_synonyms SET mastername = ? WHERE mastername = ?', |                 'UPDATE tag_synonyms SET mastername = ? WHERE mastername = ?', | ||||||
|                 [new_name, self.name] |                 [new_name, self.name] | ||||||
|             ) |             ) | ||||||
|  | @ -807,8 +831,9 @@ class Tag(ObjectBase, GroupableMixin): | ||||||
|             self.photodb.commit() |             self.photodb.commit() | ||||||
| 
 | 
 | ||||||
|     def synonyms(self): |     def synonyms(self): | ||||||
|         self.photodb.cur.execute('SELECT name FROM tag_synonyms WHERE mastername == ?', [self.name]) |         cur = self.photodb.sql.cursor() | ||||||
|         fetch = self.photodb.cur.fetchall() |         cur.execute('SELECT name FROM tag_synonyms WHERE mastername == ?', [self.name]) | ||||||
|  |         fetch = cur.fetchall() | ||||||
|         fetch = [f[0] for f in fetch] |         fetch = [f[0] for f in fetch] | ||||||
|         fetch.sort() |         fetch.sort() | ||||||
|         return fetch |         return fetch | ||||||
|  |  | ||||||
|  | @ -320,8 +320,9 @@ class PDBAlbumMixin: | ||||||
|         Return the album with the `associated_directory` of this value, NOT case-sensitive. |         Return the album with the `associated_directory` of this value, NOT case-sensitive. | ||||||
|         ''' |         ''' | ||||||
|         filepath = os.path.abspath(filepath) |         filepath = os.path.abspath(filepath) | ||||||
|         self.cur.execute('SELECT * FROM albums WHERE associated_directory == ?', [filepath]) |         cur = self.sql.cursor() | ||||||
|         fetch = self.cur.fetchone() |         cur.execute('SELECT * FROM albums WHERE associated_directory == ?', [filepath]) | ||||||
|  |         fetch = cur.fetchone() | ||||||
|         if fetch is None: |         if fetch is None: | ||||||
|             raise exceptions.NoSuchAlbum(filepath) |             raise exceptions.NoSuchAlbum(filepath) | ||||||
|         return self.get_album(fetch[constants.SQL_ALBUM['id']]) |         return self.get_album(fetch[constants.SQL_ALBUM['id']]) | ||||||
|  | @ -363,7 +364,8 @@ class PDBAlbumMixin: | ||||||
| 
 | 
 | ||||||
|         (qmarks, bindings) = helpers.binding_filler(constants.SQL_ALBUM_COLUMNS, data) |         (qmarks, bindings) = helpers.binding_filler(constants.SQL_ALBUM_COLUMNS, data) | ||||||
|         query = 'INSERT INTO albums VALUES(%s)' % qmarks |         query = 'INSERT INTO albums VALUES(%s)' % qmarks | ||||||
|         self.cur.execute(query, bindings) |         cur = self.sql.cursor() | ||||||
|  |         cur.execute(query, bindings) | ||||||
| 
 | 
 | ||||||
|         album = objects.Album(self, data) |         album = objects.Album(self, data) | ||||||
|         if photos: |         if photos: | ||||||
|  | @ -383,8 +385,9 @@ class PDBPhotoMixin: | ||||||
| 
 | 
 | ||||||
|     def get_photo_by_path(self, filepath): |     def get_photo_by_path(self, filepath): | ||||||
|         filepath = os.path.abspath(filepath) |         filepath = os.path.abspath(filepath) | ||||||
|         self.cur.execute('SELECT * FROM photos WHERE filepath == ?', [filepath]) |         cur = self.sql.cursor() | ||||||
|         fetch = self.cur.fetchone() |         cur.execute('SELECT * FROM photos WHERE filepath == ?', [filepath]) | ||||||
|  |         fetch = cur.fetchone() | ||||||
|         if fetch is None: |         if fetch is None: | ||||||
|             raise_no_such_thing(exceptions.NoSuchPhoto, thing_name=filepath) |             raise_no_such_thing(exceptions.NoSuchPhoto, thing_name=filepath) | ||||||
|         photo = objects.Photo(self, fetch) |         photo = objects.Photo(self, fetch) | ||||||
|  | @ -398,10 +401,10 @@ class PDBPhotoMixin: | ||||||
|             return |             return | ||||||
|         # We're going to use a second cursor because the first one may |         # We're going to use a second cursor because the first one may | ||||||
|         # get used for something else, deactivating this query. |         # get used for something else, deactivating this query. | ||||||
|         temp_cur = self.sql.cursor() |         cur = self.sql.cursor() | ||||||
|         temp_cur.execute('SELECT * FROM photos ORDER BY created DESC') |         cur.execute('SELECT * FROM photos ORDER BY created DESC') | ||||||
|         while True: |         while True: | ||||||
|             fetch = temp_cur.fetchone() |             fetch = cur.fetchone() | ||||||
|             if fetch is None: |             if fetch is None: | ||||||
|                 break |                 break | ||||||
|             photo = objects.Photo(self, fetch) |             photo = objects.Photo(self, fetch) | ||||||
|  | @ -485,7 +488,8 @@ class PDBPhotoMixin: | ||||||
| 
 | 
 | ||||||
|         (qmarks, bindings) = helpers.binding_filler(constants.SQL_PHOTO_COLUMNS, data) |         (qmarks, bindings) = helpers.binding_filler(constants.SQL_PHOTO_COLUMNS, data) | ||||||
|         query = 'INSERT INTO photos VALUES(%s)' % qmarks |         query = 'INSERT INTO photos VALUES(%s)' % qmarks | ||||||
|         self.cur.execute(query, bindings) |         cur = self.sql.cursor() | ||||||
|  |         cur.execute(query, bindings) | ||||||
|         photo = objects.Photo(self, data) |         photo = objects.Photo(self, data) | ||||||
| 
 | 
 | ||||||
|         if do_metadata: |         if do_metadata: | ||||||
|  | @ -673,6 +677,10 @@ class PDBPhotoMixin: | ||||||
|         print(query) |         print(query) | ||||||
|         generator = helpers.select_generator(self.sql, query) |         generator = helpers.select_generator(self.sql, query) | ||||||
| 
 | 
 | ||||||
|  |         if orderby is None: | ||||||
|  |             giveback_orderby = None | ||||||
|  |         else: | ||||||
|  |             giveback_orderby = [term.replace('RANDOM()', 'random') for term in orderby] | ||||||
|         if give_back_parameters: |         if give_back_parameters: | ||||||
|             parameters = { |             parameters = { | ||||||
|                 'area': area, |                 'area': area, | ||||||
|  | @ -694,7 +702,7 @@ class PDBPhotoMixin: | ||||||
|                 'tag_expression': tag_expression, |                 'tag_expression': tag_expression, | ||||||
|                 'limit': limit, |                 'limit': limit, | ||||||
|                 'offset': offset, |                 'offset': offset, | ||||||
|                 'orderby': [term.replace('RANDOM()', 'random') for term in orderby], |                 'orderby': giveback_orderby, | ||||||
|             } |             } | ||||||
|             yield parameters |             yield parameters | ||||||
| 
 | 
 | ||||||
|  | @ -851,15 +859,16 @@ class PDBTagMixin: | ||||||
|         tagname = tagname.split('.')[-1].split('+')[0] |         tagname = tagname.split('.')[-1].split('+')[0] | ||||||
|         tagname = self.normalize_tagname(tagname) |         tagname = self.normalize_tagname(tagname) | ||||||
| 
 | 
 | ||||||
|  |         cur = self.sql.cursor() | ||||||
|         while True: |         while True: | ||||||
|             # Return if it's a toplevel, or resolve the synonym and try that. |             # Return if it's a toplevel, or resolve the synonym and try that. | ||||||
|             self.cur.execute('SELECT * FROM tags WHERE name == ?', [tagname]) |             cur.execute('SELECT * FROM tags WHERE name == ?', [tagname]) | ||||||
|             fetch = self.cur.fetchone() |             fetch = cur.fetchone() | ||||||
|             if fetch is not None: |             if fetch is not None: | ||||||
|                 return objects.Tag(self, fetch) |                 return objects.Tag(self, fetch) | ||||||
| 
 | 
 | ||||||
|             self.cur.execute('SELECT * FROM tag_synonyms WHERE name == ?', [tagname]) |             cur.execute('SELECT * FROM tag_synonyms WHERE name == ?', [tagname]) | ||||||
|             fetch = self.cur.fetchone() |             fetch = cur.fetchone() | ||||||
|             if fetch is None: |             if fetch is None: | ||||||
|                 # was not a top tag or synonym |                 # was not a top tag or synonym | ||||||
|                 raise_no_such_thing(exceptions.NoSuchTag, thing_name=tagname) |                 raise_no_such_thing(exceptions.NoSuchTag, thing_name=tagname) | ||||||
|  | @ -882,7 +891,8 @@ class PDBTagMixin: | ||||||
| 
 | 
 | ||||||
|         tagid = self.generate_id('tags') |         tagid = self.generate_id('tags') | ||||||
|         self._cached_frozen_children = None |         self._cached_frozen_children = None | ||||||
|         self.cur.execute('INSERT INTO tags VALUES(?, ?)', [tagid, tagname]) |         cur = self.sql.cursor() | ||||||
|  |         cur.execute('INSERT INTO tags VALUES(?, ?)', [tagid, tagname]) | ||||||
|         if commit: |         if commit: | ||||||
|             self.log.debug('Commiting - new_tag') |             self.log.debug('Commiting - new_tag') | ||||||
|             self.commit() |             self.commit() | ||||||
|  | @ -916,12 +926,13 @@ class PDBUserMixin: | ||||||
|         so they get their own method. |         so they get their own method. | ||||||
|         ''' |         ''' | ||||||
|         possible = string.digits + string.ascii_uppercase |         possible = string.digits + string.ascii_uppercase | ||||||
|  |         cur = self.sql.cursor() | ||||||
|         for retry in range(20): |         for retry in range(20): | ||||||
|             user_id = [random.choice(possible) for x in range(self.config['id_length'])] |             user_id = [random.choice(possible) for x in range(self.config['id_length'])] | ||||||
|             user_id = ''.join(user_id) |             user_id = ''.join(user_id) | ||||||
| 
 | 
 | ||||||
|             self.cur.execute('SELECT * FROM users WHERE id == ?', [user_id]) |             cur.execute('SELECT * FROM users WHERE id == ?', [user_id]) | ||||||
|             if self.cur.fetchone() is None: |             if cur.fetchone() is None: | ||||||
|                 break |                 break | ||||||
|         else: |         else: | ||||||
|             raise Exception('Failed to create user id after 20 tries.') |             raise Exception('Failed to create user id after 20 tries.') | ||||||
|  | @ -932,20 +943,22 @@ class PDBUserMixin: | ||||||
|         if not helpers.is_xor(id, username): |         if not helpers.is_xor(id, username): | ||||||
|             raise exceptions.NotExclusive('One and only one of `id`, `username` must be passed.') |             raise exceptions.NotExclusive('One and only one of `id`, `username` must be passed.') | ||||||
| 
 | 
 | ||||||
|  |         cur = self.sql.cursor() | ||||||
|         if username is not None: |         if username is not None: | ||||||
|             self.cur.execute('SELECT * FROM users WHERE username == ?', [username]) |             cur.execute('SELECT * FROM users WHERE username == ?', [username]) | ||||||
|         else: |         else: | ||||||
|             self.cur.execute('SELECT * FROM users WHERE id == ?', [id]) |             cur.execute('SELECT * FROM users WHERE id == ?', [id]) | ||||||
| 
 | 
 | ||||||
|         fetch = self.cur.fetchone() |         fetch = cur.fetchone() | ||||||
|         if fetch is not None: |         if fetch is not None: | ||||||
|             return objects.User(self, fetch) |             return objects.User(self, fetch) | ||||||
|         else: |         else: | ||||||
|             raise exceptions.NoSuchUser(username) |             raise exceptions.NoSuchUser(username) | ||||||
| 
 | 
 | ||||||
|     def login(self, user_id, password): |     def login(self, user_id, password): | ||||||
|         self.cur.execute('SELECT * FROM users WHERE id == ?', [user_id]) |         cur = self.sql.cursor() | ||||||
|         fetch = self.cur.fetchone() |         cur.execute('SELECT * FROM users WHERE id == ?', [user_id]) | ||||||
|  |         fetch = cur.fetchone() | ||||||
| 
 | 
 | ||||||
|         if fetch is None: |         if fetch is None: | ||||||
|             raise exceptions.WrongLogin() |             raise exceptions.WrongLogin() | ||||||
|  | @ -978,8 +991,9 @@ class PDBUserMixin: | ||||||
|         if len(password) < self.config['min_password_length']: |         if len(password) < self.config['min_password_length']: | ||||||
|             raise exceptions.PasswordTooShort |             raise exceptions.PasswordTooShort | ||||||
| 
 | 
 | ||||||
|         self.cur.execute('SELECT * FROM users WHERE username == ?', [username]) |         cur = self.sql.cursor() | ||||||
|         if self.cur.fetchone() is not None: |         cur.execute('SELECT * FROM users WHERE username == ?', [username]) | ||||||
|  |         if cur.fetchone() is not None: | ||||||
|             raise exceptions.UserExists(username) |             raise exceptions.UserExists(username) | ||||||
| 
 | 
 | ||||||
|         user_id = self.generate_user_id() |         user_id = self.generate_user_id() | ||||||
|  | @ -995,7 +1009,7 @@ class PDBUserMixin: | ||||||
| 
 | 
 | ||||||
|         (qmarks, bindings) = helpers.binding_filler(constants.SQL_USER_COLUMNS, data) |         (qmarks, bindings) = helpers.binding_filler(constants.SQL_USER_COLUMNS, data) | ||||||
|         query = 'INSERT INTO users VALUES(%s)' % qmarks |         query = 'INSERT INTO users VALUES(%s)' % qmarks | ||||||
|         self.cur.execute(query, bindings) |         cur.execute(query, bindings) | ||||||
| 
 | 
 | ||||||
|         if commit: |         if commit: | ||||||
|             self.log.debug('Committing - register user') |             self.log.debug('Committing - register user') | ||||||
|  | @ -1069,6 +1083,7 @@ class PhotoDB(PDBAlbumMixin, PDBPhotoMixin, PDBTagMixin, PDBUserMixin): | ||||||
|         statements = DB_INIT.split(';') |         statements = DB_INIT.split(';') | ||||||
|         for statement in statements: |         for statement in statements: | ||||||
|             self.cur.execute(statement) |             self.cur.execute(statement) | ||||||
|  |         self.sql.commit() | ||||||
| 
 | 
 | ||||||
|         # CONFIG |         # CONFIG | ||||||
|         self.config_file = self.data_directory.with_child('config.json') |         self.config_file = self.data_directory.with_child('config.json') | ||||||
|  | @ -1088,6 +1103,7 @@ class PhotoDB(PDBAlbumMixin, PDBPhotoMixin, PDBTagMixin, PDBUserMixin): | ||||||
| 
 | 
 | ||||||
|         # OTHER |         # OTHER | ||||||
|         self.log = logging.getLogger(__name__) |         self.log = logging.getLogger(__name__) | ||||||
|  |         self.log.setLevel(self.config['log_level']) | ||||||
|         self.on_commit_queue = [] |         self.on_commit_queue = [] | ||||||
|         self._cached_frozen_children = None |         self._cached_frozen_children = None | ||||||
| 
 | 
 | ||||||
|  | @ -1305,8 +1321,9 @@ class PhotoDB(PDBAlbumMixin, PDBPhotoMixin, PDBTagMixin, PDBUserMixin): | ||||||
|         if table not in ['photos', 'tags', 'groups']: |         if table not in ['photos', 'tags', 'groups']: | ||||||
|             raise ValueError('Invalid table requested: %s.', table) |             raise ValueError('Invalid table requested: %s.', table) | ||||||
| 
 | 
 | ||||||
|         self.cur.execute('SELECT * FROM id_numbers WHERE tab == ?', [table]) |         cur = self.sql.cursor() | ||||||
|         fetch = self.cur.fetchone() |         cur.execute('SELECT * FROM id_numbers WHERE tab == ?', [table]) | ||||||
|  |         fetch = cur.fetchone() | ||||||
|         if fetch is None: |         if fetch is None: | ||||||
|             # Register new value |             # Register new value | ||||||
|             new_id_int = 1 |             new_id_int = 1 | ||||||
|  | @ -1318,9 +1335,9 @@ class PhotoDB(PDBAlbumMixin, PDBPhotoMixin, PDBTagMixin, PDBUserMixin): | ||||||
| 
 | 
 | ||||||
|         new_id = str(new_id_int).rjust(self.config['id_length'], '0') |         new_id = str(new_id_int).rjust(self.config['id_length'], '0') | ||||||
|         if do_insert: |         if do_insert: | ||||||
|             self.cur.execute('INSERT INTO id_numbers VALUES(?, ?)', [table, new_id]) |             cur.execute('INSERT INTO id_numbers VALUES(?, ?)', [table, new_id]) | ||||||
|         else: |         else: | ||||||
|             self.cur.execute('UPDATE id_numbers SET last_id = ? WHERE tab == ?', [new_id, table]) |             cur.execute('UPDATE id_numbers SET last_id = ? WHERE tab == ?', [new_id, table]) | ||||||
|         return new_id |         return new_id | ||||||
| 
 | 
 | ||||||
|     def get_thing_by_id(self, thing_type, thing_id): |     def get_thing_by_id(self, thing_type, thing_id): | ||||||
|  | @ -1330,8 +1347,9 @@ class PhotoDB(PDBAlbumMixin, PDBPhotoMixin, PDBTagMixin, PDBUserMixin): | ||||||
|             thing_id = thing_id.id |             thing_id = thing_id.id | ||||||
| 
 | 
 | ||||||
|         query = 'SELECT * FROM %s WHERE id == ?' % thing_map['table'] |         query = 'SELECT * FROM %s WHERE id == ?' % thing_map['table'] | ||||||
|         self.cur.execute(query, [thing_id]) |         cur = self.sql.cursor() | ||||||
|         thing = self.cur.fetchone() |         cur.execute(query, [thing_id]) | ||||||
|  |         thing = cur.fetchone() | ||||||
|         if thing is None: |         if thing is None: | ||||||
|             return raise_no_such_thing(thing_map['exception'], thing_id=thing_id) |             return raise_no_such_thing(thing_map['exception'], thing_id=thing_id) | ||||||
|         thing = thing_map['class'](self, thing) |         thing = thing_map['class'](self, thing) | ||||||
|  | @ -1340,12 +1358,13 @@ class PhotoDB(PDBAlbumMixin, PDBPhotoMixin, PDBTagMixin, PDBUserMixin): | ||||||
|     def get_things(self, thing_type, orderby=None): |     def get_things(self, thing_type, orderby=None): | ||||||
|         thing_map = _THING_CLASSES[thing_type] |         thing_map = _THING_CLASSES[thing_type] | ||||||
| 
 | 
 | ||||||
|  |         cur = self.sql.cursor() | ||||||
|         if orderby: |         if orderby: | ||||||
|             self.cur.execute('SELECT * FROM %s ORDER BY %s' % (thing_map['table'], orderby)) |             cur.execute('SELECT * FROM %s ORDER BY %s' % (thing_map['table'], orderby)) | ||||||
|         else: |         else: | ||||||
|             self.cur.execute('SELECT * FROM %s' % thing_map['table']) |             cur.execute('SELECT * FROM %s' % thing_map['table']) | ||||||
| 
 | 
 | ||||||
|         things = self.cur.fetchall() |         things = cur.fetchall() | ||||||
|         for thing in things: |         for thing in things: | ||||||
|             thing = thing_map['class'](self, row_tuple=thing) |             thing = thing_map['class'](self, row_tuple=thing) | ||||||
|             yield thing |             yield thing | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue