mirror of
https://github.com/SickGear/SickGear.git
synced 2024-12-13 14:43:37 +00:00
0d9fbc1ad7
This version of SickBeard uses both TVDB and TVRage to search and gather it's series data from allowing you to now have access to and download shows that you couldn't before because of being locked into only what TheTVDB had to offer. Also this edition is based off the code we used in our XEM editon so it does come with scene numbering support as well as all the other features our XEM edition has to offer. Please before using this with your existing database (sickbeard.db) please make a backup copy of it and delete any other database files such as cache.db and failed.db if present, we HIGHLY recommend starting out with no database files at all to make this a fresh start but the choice is at your own risk! Enjoy!
207 lines
7.6 KiB
Python
207 lines
7.6 KiB
Python
"""
|
|
parser.sql.objectadapter module (imdb.parser.sql package).
|
|
|
|
This module adapts the SQLObject ORM to the internal mechanism.
|
|
|
|
Copyright 2008-2010 Davide Alberani <da@erlug.linux.it>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
"""
|
|
|
|
import sys
|
|
import logging
|
|
|
|
from sqlobject import *
|
|
from sqlobject.sqlbuilder import ISNULL, ISNOTNULL, AND, OR, IN, CONTAINSSTRING
|
|
|
|
from dbschema import *
|
|
|
|
_object_logger = logging.getLogger('imdbpy.parser.sql.object')
|
|
|
|
|
|
# Maps our placeholders to SQLAlchemy's column types.
|
|
MAP_COLS = {
|
|
INTCOL: IntCol,
|
|
UNICODECOL: UnicodeCol,
|
|
STRINGCOL: StringCol
|
|
}
|
|
|
|
|
|
# Exception raised when Table.get(id) returns no value.
|
|
NotFoundError = SQLObjectNotFound
|
|
|
|
|
|
# class method to be added to the SQLObject class.
|
|
def addIndexes(cls, ifNotExists=True):
|
|
"""Create all required indexes."""
|
|
for col in cls._imdbpySchema.cols:
|
|
if col.index:
|
|
idxName = col.index
|
|
colToIdx = col.name
|
|
if col.indexLen:
|
|
colToIdx = {'column': col.name, 'length': col.indexLen}
|
|
if idxName in [i.name for i in cls.sqlmeta.indexes]:
|
|
# Check if the index is already present.
|
|
continue
|
|
idx = DatabaseIndex(colToIdx, name=idxName)
|
|
cls.sqlmeta.addIndex(idx)
|
|
try:
|
|
cls.createIndexes(ifNotExists)
|
|
except dberrors.OperationalError, e:
|
|
_object_logger.warn('Skipping creation of the %s.%s index: %s' %
|
|
(cls.sqlmeta.table, col.name, e))
|
|
addIndexes = classmethod(addIndexes)
|
|
|
|
|
|
# Global repository for "fake" tables with Foreign Keys - need to
|
|
# prevent troubles if addForeignKeys is called more than one time.
|
|
FAKE_TABLES_REPOSITORY = {}
|
|
|
|
def _buildFakeFKTable(cls, fakeTableName):
|
|
"""Return a "fake" table, with foreign keys where needed."""
|
|
countCols = 0
|
|
attrs = {}
|
|
for col in cls._imdbpySchema.cols:
|
|
countCols += 1
|
|
if col.name == 'id':
|
|
continue
|
|
if not col.foreignKey:
|
|
# A non-foreign key column - add it as usual.
|
|
attrs[col.name] = MAP_COLS[col.kind](**col.params)
|
|
continue
|
|
# XXX: Foreign Keys pointing to TableName.ColName not yet supported.
|
|
thisColName = col.name
|
|
if thisColName.endswith('ID'):
|
|
thisColName = thisColName[:-2]
|
|
|
|
fks = col.foreignKey.split('.', 1)
|
|
foreignTableName = fks[0]
|
|
if len(fks) == 2:
|
|
foreignColName = fks[1]
|
|
else:
|
|
foreignColName = 'id'
|
|
# Unused...
|
|
#fkName = 'fk_%s_%s_%d' % (foreignTableName, foreignColName,
|
|
# countCols)
|
|
# Create a Foreign Key column, with the correct references.
|
|
fk = ForeignKey(foreignTableName, name=thisColName, default=None)
|
|
attrs[thisColName] = fk
|
|
# Build a _NEW_ SQLObject subclass, with foreign keys, if needed.
|
|
newcls = type(fakeTableName, (SQLObject,), attrs)
|
|
return newcls
|
|
|
|
def addForeignKeys(cls, mapTables, ifNotExists=True):
|
|
"""Create all required foreign keys."""
|
|
# Do not even try, if there are no FK, in this table.
|
|
if not filter(None, [col.foreignKey for col in cls._imdbpySchema.cols]):
|
|
return
|
|
fakeTableName = 'myfaketable%s' % cls.sqlmeta.table
|
|
if fakeTableName in FAKE_TABLES_REPOSITORY:
|
|
newcls = FAKE_TABLES_REPOSITORY[fakeTableName]
|
|
else:
|
|
newcls = _buildFakeFKTable(cls, fakeTableName)
|
|
FAKE_TABLES_REPOSITORY[fakeTableName] = newcls
|
|
# Connect the class with foreign keys.
|
|
newcls.setConnection(cls._connection)
|
|
for col in cls._imdbpySchema.cols:
|
|
if col.name == 'id':
|
|
continue
|
|
if not col.foreignKey:
|
|
continue
|
|
# Get the SQL that _WOULD BE_ run, if we had to create
|
|
# this "fake" table.
|
|
fkQuery = newcls._connection.createReferenceConstraint(newcls,
|
|
newcls.sqlmeta.columns[col.name])
|
|
if not fkQuery:
|
|
# Probably the db doesn't support foreign keys (SQLite).
|
|
continue
|
|
# Remove "myfaketable" to get references to _real_ tables.
|
|
fkQuery = fkQuery.replace('myfaketable', '')
|
|
# Execute the query.
|
|
newcls._connection.query(fkQuery)
|
|
# Disconnect it.
|
|
newcls._connection.close()
|
|
addForeignKeys = classmethod(addForeignKeys)
|
|
|
|
|
|
# Module-level "cache" for SQLObject classes, to prevent
|
|
# "class TheClass is already in the registry" errors, when
|
|
# two or more connections to the database are made.
|
|
# XXX: is this the best way to act?
|
|
TABLES_REPOSITORY = {}
|
|
|
|
def getDBTables(uri=None):
|
|
"""Return a list of classes to be used to access the database
|
|
through the SQLObject ORM. The connection uri is optional, and
|
|
can be used to tailor the db schema to specific needs."""
|
|
DB_TABLES = []
|
|
for table in DB_SCHEMA:
|
|
if table.name in TABLES_REPOSITORY:
|
|
DB_TABLES.append(TABLES_REPOSITORY[table.name])
|
|
continue
|
|
attrs = {'_imdbpyName': table.name, '_imdbpySchema': table,
|
|
'addIndexes': addIndexes, 'addForeignKeys': addForeignKeys}
|
|
for col in table.cols:
|
|
if col.name == 'id':
|
|
continue
|
|
attrs[col.name] = MAP_COLS[col.kind](**col.params)
|
|
# Create a subclass of SQLObject.
|
|
# XXX: use a metaclass? I can't see any advantage.
|
|
cls = type(table.name, (SQLObject,), attrs)
|
|
DB_TABLES.append(cls)
|
|
TABLES_REPOSITORY[table.name] = cls
|
|
return DB_TABLES
|
|
|
|
|
|
def toUTF8(s):
|
|
"""For some strange reason, sometimes SQLObject wants utf8 strings
|
|
instead of unicode."""
|
|
return s.encode('utf_8')
|
|
|
|
|
|
def setConnection(uri, tables, encoding='utf8', debug=False):
|
|
"""Set connection for every table."""
|
|
kw = {}
|
|
# FIXME: it's absolutely unclear what we should do to correctly
|
|
# support unicode in MySQL; with some versions of SQLObject,
|
|
# it seems that setting use_unicode=1 is the _wrong_ thing to do.
|
|
_uriLower = uri.lower()
|
|
if _uriLower.startswith('mysql'):
|
|
kw['use_unicode'] = 1
|
|
#kw['sqlobject_encoding'] = encoding
|
|
kw['charset'] = encoding
|
|
conn = connectionForURI(uri, **kw)
|
|
conn.debug = debug
|
|
# XXX: doesn't work and a work-around was put in imdbpy2sql.py;
|
|
# is there any way to modify the text_factory parameter of
|
|
# a SQLite connection?
|
|
#if uri.startswith('sqlite'):
|
|
# major = sys.version_info[0]
|
|
# minor = sys.version_info[1]
|
|
# if major > 2 or (major == 2 and minor > 5):
|
|
# sqliteConn = conn.getConnection()
|
|
# sqliteConn.text_factory = str
|
|
for table in tables:
|
|
table.setConnection(conn)
|
|
#table.sqlmeta.cacheValues = False
|
|
# FIXME: is it safe to set table._cacheValue to False? Looks like
|
|
# we can't retrieve correct values after an update (I think
|
|
# it's never needed, but...) Anyway, these are set to False
|
|
# for performance reason at insert time (see imdbpy2sql.py).
|
|
table._cacheValue = False
|
|
# Required by imdbpy2sql.py.
|
|
conn.paramstyle = conn.module.paramstyle
|
|
return conn
|
|
|