from sqlalchemy.types import *
import json
import pickle
import copy
import uuid
import binascii
from galaxy.util.bunch import Bunch
from galaxy.util.aliaspickler import AliasPickleModule
# For monkeypatching BIGINT
import sqlalchemy.dialects.sqlite
import sqlalchemy.dialects.postgresql
import sqlalchemy.dialects.mysql
import logging
log = logging.getLogger( __name__ )
# Default JSON encoder and decoder
json_encoder = json.JSONEncoder( sort_keys=True )
json_decoder = json.JSONDecoder( )
def _sniffnfix_pg9_hex(value):
"""
Sniff for and fix postgres 9 hex decoding issue
"""
try:
if value[0] == 'x':
return binascii.unhexlify(value[1:])
elif value.startswith( '\\x' ):
return binascii.unhexlify( value[2:] )
else:
return value
except Exception, ex:
return value
[docs]class JSONType( TypeDecorator ):
"""
Defines a JSONType for SQLAlchemy. Takes a primitive as input and
JSONifies it. This should replace PickleType throughout Galaxy.
"""
impl = LargeBinary
[docs] def process_bind_param( self, value, dialect ):
if value is None:
return None
return json_encoder.encode( value )
[docs] def process_result_value( self, value, dialect ):
if value is not None:
try:
return json_decoder.decode( str( _sniffnfix_pg9_hex( value ) ) )
except Exception, e:
log.error( 'Failed to decode JSON (%s): %s', value, e )
return None
[docs] def copy_value( self, value ):
# return json_decoder.decode( json_encoder.encode( value ) )
return copy.deepcopy( value )
[docs] def compare_values( self, x, y ):
# return json_encoder.encode( x ) == json_encoder.encode( y )
return ( x == y )
[docs] def is_mutable( self ):
return True
[docs] def load_dialect_impl(self, dialect):
if dialect.name == "mysql":
return dialect.type_descriptor(sqlalchemy.dialects.mysql.MEDIUMBLOB)
else:
return self.impl
metadata_pickler = AliasPickleModule( {
( "cookbook.patterns", "Bunch" ) : ( "galaxy.util.bunch" , "Bunch" )
} )
[docs]class UUIDType(TypeDecorator):
"""
Platform-independent UUID type.
Based on http://docs.sqlalchemy.org/en/rel_0_8/core/types.html#backend-agnostic-guid-type
Changed to remove sqlalchemy 0.8 specific code
CHAR(32), storing as stringified hex values.
"""
impl = CHAR
[docs] def load_dialect_impl(self, dialect):
return dialect.type_descriptor(CHAR(32))
[docs] def process_bind_param(self, value, dialect):
if value is None:
return value
else:
if not isinstance(value, uuid.UUID):
return "%.32x" % uuid.UUID(value)
else:
# hexstring
return "%.32x" % value
[docs] def process_result_value(self, value, dialect):
if value is None:
return value
else:
return uuid.UUID(value)
[docs]class TrimmedString( TypeDecorator ):
impl = String
[docs] def process_bind_param( self, value, dialect ):
"""Automatically truncate string values"""
if self.impl.length and value is not None:
value = value[0:self.impl.length]
return value
#class BigInteger( Integer ):
#"""
#A type for bigger ``int`` integers.
#Typically generates a ``BIGINT`` in DDL, and otherwise acts like
#a normal :class:`Integer` on the Python side.
#"""
#class BIGINT( BigInteger ):
#"""The SQL BIGINT type."""
#class SLBigInteger( BigInteger ):
#def get_col_spec( self ):
#return "BIGINT"
#sqlalchemy.dialects.sqlite.SLBigInteger = SLBigInteger
#sqlalchemy.dialects.sqlite.colspecs[BigInteger] = SLBigInteger
#sqlalchemy.dialects.sqlite.ischema_names['BIGINT'] = SLBigInteger
#sqlalchemy.dialects.postgres.colspecs[BigInteger] = sqlalchemy.dialects.postgres.PGBigInteger
#sqlalchemy.dialects.mysql.colspecs[BigInteger] = sqlalchemy.dialects.mysql.MSBigInteger