Contact: fumanchu@aminus.org

Log in as guest/geniusql to create tickets

Changeset 69

Show
Ignore:
Timestamp:
04/10/07 15:58:42
Author:
fumanchu
Message:

Firebird upgraded to latest adapter/dbtype syntax.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/geniusql/providers/firebird.py

    r68 r69  
    11# See http://www.firebirdsql.org/index.php?op=devel&sub=engine&id=SQL_conformance&nosb=1 
    22# for lots of conformance info. 
     3#  
     4# This module ALWAYS assumes dialect 3. 
    35 
    46import datetime 
     
    79 
    810import geniusql 
    9 from geniusql import adapters, conns, decompile, errors, select, typerefs 
     11from geniusql import adapters, conns, dbtypes, decompile, errors, select, typerefs 
    1012from geniusql import isolation as _isolation 
    1113 
     
    1618 
    1719 
    18 class AdapterToFireBirdSQL(adapters.AdapterToSQL): 
     20# ------------------------------- Adapters ------------------------------- # 
     21 
     22 
     23class Firebird_str_to_SQL92VARCHAR(adapters.str_to_SQL92VARCHAR): 
    1924     
    2025    # Notice these are ordered pairs. Escape \ before introducing new ones. 
    2126    # Values in these two lists should be strings encoded with self.encoding. 
    2227    escapes = [("'", "''")] 
    23      
    24     def coerce_bool_to_any(self, value): 
    25         if value: 
    26             return '1' 
    27         return '0' 
    28      
    29     def _to_BLOB(self, value): 
     28 
     29class Firebird_unicode_to_SQL92VARCHAR(adapters.unicode_to_SQL92VARCHAR): 
     30    escapes = [("'", "''")] 
     31 
     32class Firebird_Pickler(adapters.Pickler): 
     33    escapes = [("'", "''")] 
     34 
     35 
     36class Firebird_any_to_BLOB(adapters.Adapter): 
     37     
     38    def __init__(self, pytype): 
     39        self.pytype = pytype 
     40     
     41    def push(self, value, dbtype): 
     42        if value is None: 
     43            return 'NULL' 
    3044        return "'%s'" % str(value) 
    3145     
    32     coerce_decimal_to_BLOB = _to_BLOB 
    33     coerce_decimal_Decimal_to_BLOB = _to_BLOB 
    34     coerce_fixedpoint_FixedPoint_to_BLOB = _to_BLOB 
    35     coerce_float_to_BLOB = _to_BLOB 
    36     coerce_int_to_BLOB = _to_BLOB 
    37     coerce_long_to_BLOB = _to_BLOB 
    38      
    39     def coerce_datetime_datetime_to_any(self, value): 
     46    def pull(self, value, dbtype): 
     47        return self.pytype(value) 
     48 
     49 
     50 
     51class Firebird_datetime_adapter(adapters.datetime_to_SQL92TIMESTAMP): 
     52     
     53    def push(self, value, dbtype): 
    4054        # This isn't necessary for UPDATE, but it is for binary operations. 
    4155        return ("CAST('%04d-%02d-%02d %02d:%02d:%02d' AS TIMESTAMP)" % 
     
    4357                 value.hour, value.minute, value.second)) 
    4458     
    45     def coerce_datetime_date_to_any(self, value): 
     59    def binary_op(self, op1, op, sqlop, op2): 
     60        # See http://www.ibphoenix.com/main.nfs?a=ibphoenix&s=1154534295:6&page=ibp_60_sql_date_fs 
     61        if op2.pytype is datetime.datetime: 
     62            if op == "-": 
     63                return ("((%s - %s) * CAST(86400 AS DOUBLE PRECISION))" 
     64                        % (op1.sql, op2.sql)) 
     65        elif op2.pytype is datetime.timedelta: 
     66            return ("(%s %s (CAST(%s AS DOUBLE PRECISION) / 86400))" 
     67                    % (op1.sql, op, op2.sql)) 
     68        raise TypeError("unsupported operand type(s) for %s: " 
     69                        "%r and %r" % (op, op1.pytype, op2.pytype)) 
     70 
     71 
     72class Firebird_date_adapter(adapters.date_to_SQL92DATE): 
     73     
     74    def push(self, value, dbtype): 
    4675        # This isn't necessary for UPDATE, but it is for binary operations. 
    4776        return ("CAST('%04d-%02d-%02d' AS DATE)" % 
    4877                (value.year, value.month, value.day)) 
    4978     
    50     def coerce_datetime_time_to_any(self, value): 
     79    def binary_op(self, op1, op, sqlop, op2): 
     80        # See http://www.ibphoenix.com/main.nfs?a=ibphoenix&s=1154534295:6&page=ibp_60_sql_date_fs 
     81        if op2.pytype is datetime.date: 
     82            if op == "-": 
     83                return "((%s - %s) * 86400)" % (op1.sql, op2.sql) 
     84        elif op2.pytype is datetime.timedelta: 
     85            return "(%s %s (%s / 86400))" % (op1.sql, op, op2.sql) 
     86        raise TypeError("unsupported operand type(s) for %s: " 
     87                        "%r and %r" % (op, op1.pytype, op2.pytype)) 
     88 
     89 
     90class Firebird_timedelta_adapter(adapters.timedelta_to_SQL92DECIMAL): 
     91     
     92    def pull(self, value, dbtype): 
     93        # TIMESTAMP - TIMESTAMP => DECIMAL(18, 9) Days + Fraction of day 
     94        # DATE - DATE =>           DECIMAL(9, 0) representing # of Days 
     95        # However, in binary_op (below) we multiplied them all by 86400. 
     96        if isinstance(value, tuple): 
     97            value = normalize(value, float)[0] 
     98        days, seconds = divmod(value, 86400) 
     99        return datetime.timedelta(int(days), int(round(seconds))) 
     100     
     101    def binary_op(self, op1, op, sqlop, op2): 
     102        # See http://www.ibphoenix.com/main.nfs?a=ibphoenix&s=1154534295:6&page=ibp_60_sql_date_fs 
     103        if op2.pytype is datetime.timedelta: 
     104            return "(%s %s %s)" % (op1.sql, op, op2.sql) 
     105        elif op == "+": 
     106            if op2.pytype is datetime.date: 
     107                return "((%s / 86400) + %s)" % (op1.sql, op2.sql) 
     108            elif op2.pytype is datetime.datetime: 
     109                return ("((CAST(%s AS DOUBLE PRECISION) / 86400) + %s)" 
     110                        % (op1.sql, op2.sql)) 
     111        raise TypeError("unsupported operand type(s) for %s: " 
     112                        "%r and %r" % (op, op1.pytype, op2.pytype)) 
     113 
     114 
     115class Firebird_time_adapter(adapters.time_to_SQL92TIME): 
     116     
     117    def push(self, value, dbtype): 
    51118        # This isn't necessary for UPDATE, but it is for binary operations. 
    52119        return ("CAST('%02d:%02d:%02d' AS TIME)" % 
     
    57124del x 
    58125 
    59  
    60 class AdapterFromFirebirdDB(adapters.AdapterFromDB): 
    61      
    62     epoch = datetime.date(1898, 11, 17) 
    63      
    64     def normalize(dectuple, newtype): 
    65         """Normalize the given (val, scale) tuple into (newtype(val), scale).""" 
    66         value, scale = dectuple 
    67         if scale: 
    68             value = newtype(value) / _power_of_ten[-scale] 
    69         return value, scale 
    70     normalize = staticmethod(normalize) 
    71      
    72     def coerce_any_to_decimal(self, value): 
     126def normalize(dectuple, newtype): 
     127    """Normalize the given (val, scale) tuple into (newtype(val), scale).""" 
     128    value, scale = dectuple 
     129    if scale: 
     130        value = newtype(value) / _power_of_ten[-scale] 
     131    return value, scale 
     132 
     133 
     134class Firebird_decimal_adapter(adapters.decimal_to_SQL92DECIMAL): 
     135    def pull(self, value, dbtype): 
    73136        if isinstance(value, tuple): 
    74             return self.normalize(value, typerefs.decimal)[0] 
    75         return decimal(str(value)) 
    76      
    77     def coerce_any_to_decimal_Decimal(self, value): 
     137            return normalize(value, typerefs.decimal)[0] 
     138        return typerefs.decimal(str(value)) 
     139 
     140class Firebird_decimal_Decimal_adapter(adapters.decimal_to_SQL92DECIMAL): 
     141    def pull(self, value, dbtype): 
    78142        if isinstance(value, tuple): 
    79             return self.normalize(value, typerefs.decimal.Decimal)[0] 
     143            return normalize(value, typerefs.decimal.Decimal)[0] 
    80144        return typerefs.decimal.Decimal(str(value)) 
    81      
    82     def coerce_any_to_fixedpoint_FixedPoint(self, value): 
     145 
     146class Firebird_fixedpoint_adapter(adapters.fixedpoint_to_SQL92DECIMAL): 
     147    def pull(self, value, dbtype): 
     148        fp = typerefs.fixedpoint.FixedPoint 
    83149        if isinstance(value, tuple): 
    84             value, scale = self.normalize(value, float) 
    85             return typerefs.fixedpoint.FixedPoint(value, -scale) 
     150            value, scale = normalize(value, float) 
     151            return fp(value, -scale) 
    86152        elif isinstance(value, basestring): 
    87153            # Unicode really screws up fixedpoint; for example: 
     
    94160            if len(atoms) > 1: 
    95161                scale = len(atoms[-1]) 
    96             return typerefs.fixedpoint.FixedPoint(value, scale) 
     162            return fp(value, scale) 
    97163        else: 
    98             return typerefs.fixedpoint.FixedPoint(value) 
    99      
    100     def coerce_any_to_int(self, value): 
     164            return fp(value) 
     165 
     166class Firebird_int_adapter(adapters.int_to_SQL92INTEGER): 
     167    def pull(self, value, dbtype): 
    101168        if isinstance(value, tuple): 
    102             return self.normalize(value, int)[0] 
     169            return normalize(value, int)[0] 
    103170        return int(value) 
    104      
    105     def coerce_any_to_long(self, value): 
     171 
     172class Firebird_long_adapter(adapters.int_to_SQL92INTEGER): 
     173    def pull(self, value, dbtype): 
    106174        if isinstance(value, tuple): 
    107             return self.normalize(long)[0] 
     175            return normalize(long)[0] 
    108176        return long(value) 
    109      
    110     # !!?!??! kinterbasdb already converts to datetime? Rapture! 
    111     def coerce_any_to_datetime_datetime(self, value): 
    112         return value 
    113      
    114     def coerce_any_to_datetime_date(self, value): 
    115         return value 
    116      
    117     def coerce_any_to_datetime_time(self, value): 
    118         return value 
    119      
    120     def coerce_any_to_datetime_timedelta(self, value): 
    121         # TIMESTAMP - TIMESTAMP => DECIMAL(18, 9) Days + Fraction of day 
    122         # DATE - DATE =>           DECIMAL(9, 0) representing # of Days 
    123         # However, in binary_op (below) we multiplied them all by 86400. 
    124         if isinstance(value, tuple): 
    125             value = self.normalize(value, float)[0] 
    126         days, seconds = divmod(value, 86400) 
    127         return datetime.timedelta(int(days), int(round(seconds))) 
    128  
    129  
    130 class TypeAdapterFirebird(adapters.TypeAdapter): 
    131     """Return the SQL typename of a DB column.""" 
    132      
    133     # Max decimal precision for NUMERIC columns. 
    134     numeric_max_precision = 18 
    135     numeric_max_bytes = 9 
    136      
    137     numeric_text_type = "BLOB" 
    138      
    139     _reverse_types = adapters.TypeAdapter._reverse_types.copy() 
    140     _reverse_types.update({ 
    141         'LONG': int, 
    142         'SHORT': int, 
    143         'INT64': long, 
    144         'VARYING': str, 
    145         'NCHAR': unicode, 
    146         'NATIONAL': unicode, 
    147         }) 
    148      
    149     def coerce_str(self, hints): 
    150         # The bytes hint shall not reflect the usual 4-byte base for varchar. 
    151          
    152         # Although Firebird allows VARCHAR of 32765, 255 is usually the max 
    153         # for which an index can be created. 
    154         # TODO: this needs some serious work, so that the full size can be 
    155         # allowed while only allowing indices of 255. 
    156         default = 127 
    157          
    158         bytes = int(hints.get('bytes', default)) 
    159         if 1 <= bytes <= 32765: 
    160             return "VARCHAR(%s)" % bytes 
    161         return "BLOB" 
    162      
    163     def coerce_bool(self, hints): 
    164         return "SMALLINT" 
    165      
    166     def int_type(self, bytes): 
    167         """Return a datatype which can handle the given number of bytes.""" 
    168         if bytes <= 2: 
    169             return "SMALLINT" 
    170         elif bytes <= 4: 
    171             return "INTEGER" 
     177 
     178 
     179# ---------------------------- Database types ---------------------------- # 
     180 
     181 
     182# See http://www.ibexpert.info/documentation/%20%203.%20Database%20Objects/%20%203.%20Field/%20%203.%20Datatype/387.html 
     183# <data_type> = { 
     184#   {SMALLINT | INTEGER | FLOAT | DOUBLE PRECISION} [<array_dim>] 
     185# | {DECIMAL | NUMERIC} [(precision [, scale])] [<array_dim] 
     186# | DATE [<array_dim>] 
     187# | {CHAR | CHARACTER | CHARACTER VARYING | VARCHAR} 
     188#       [(int)] [<array_dim>] [CHARACTER SET charname] 
     189# | {NCHAR | NATIONAL CHARACTER | NATIONAL CHAR} 
     190#       [VARYING] [(int)] [<array_dim>] 
     191# | BLOB [SUB_TYPE {int | subtype_name}) (SEGMENT SIZE int] 
     192#       [CHARACTER SET charname] 
     193# | BLOB [(seglen [, subtype])] 
     194# } 
     195 
     196class SMALLINT(dbtypes.SQL92SMALLINT): 
     197    synonyms = ['SHORT'] 
     198    default_adapters = {int: Firebird_int_adapter(), 
     199                        long: Firebird_int_adapter(), 
     200                        bool: adapters.bool_to_SQL92BIT(), 
     201                        } 
     202 
     203class INTEGER(dbtypes.SQL92INTEGER): 
     204    synonyms = ['LONG'] 
     205    default_adapters = {int: Firebird_int_adapter(), 
     206                        long: Firebird_int_adapter(), 
     207                        bool: adapters.bool_to_SQL92BIT(), 
     208                        } 
     209 
     210class INT64(dbtypes.SQL92INTEGER): 
     211    _bytes = max_bytes = 8 
     212    default_adapters = {int: Firebird_int_adapter(), 
     213                        long: Firebird_int_adapter(), 
     214                        bool: adapters.bool_to_SQL92BIT(), 
     215                        } 
     216 
     217 
     218class FLOAT(dbtypes.SQL92REAL): 
     219    _precision = max_precision = 32 
     220 
     221class DOUBLE(dbtypes.SQL92DOUBLE): 
     222    """Base class for SQL 92 DOUBLE PRECISION types.""" 
     223    synonyms = ['DOUBLE PRECISION'] 
     224    _precision = max_precision = 64 
     225 
     226 
     227class NUMERIC(dbtypes.SQL92DECIMAL): 
     228    # Depending on the dialect, FB will convert NUMERIC to another type 
     229    # if the precision or scale are not specified. 
     230     
     231    # SQL dialect 1 only allows max_precision of 15, but dialect 3 is 18 
     232    max_precision = 18 
     233     
     234    default_adapters = {int: adapters.number_to_SQL92DECIMAL(int), 
     235                        long: adapters.number_to_SQL92DECIMAL(long), 
     236                        float: adapters.number_to_SQL92DECIMAL(float), 
     237                        datetime.timedelta: Firebird_timedelta_adapter(), 
     238                        } 
     239    if typerefs.fixedpoint: 
     240        default_adapters[typerefs.fixedpoint.FixedPoint] = Firebird_fixedpoint_adapter() 
     241    if typerefs.decimal: 
     242        if hasattr(typerefs.decimal, "Decimal"): 
     243            default_adapters[typerefs.decimal.Decimal] = Firebird_decimal_Decimal_adapter() 
    172244        else: 
    173             # Anything larger than 8 bytes, use decimal/numeric. 
    174             return "NUMERIC(%s, 0)" % (bytes * 2) 
     245            default_adapters[typerefs.decimal] = Firebird_decimal_adapter() 
     246 
     247 
     248 
     249class CHAR(dbtypes.SQL92CHAR): 
     250    synonyms = ['CHARACTER'] 
     251    max_bytes = 32767 
     252    default_adapters = dbtypes.SQL92CHAR.default_adapters.copy() 
     253    default_adapters.update({str: Firebird_str_to_SQL92VARCHAR(), 
     254                             unicode: Firebird_unicode_to_SQL92VARCHAR(), 
     255                             None: Firebird_Pickler, 
     256                             }) 
     257 
     258class VARCHAR(dbtypes.SQL92VARCHAR): 
     259    synonyms = ['CHARACTER VARYING', 'CHAR VARYING', 'VARYING'] 
     260    max_bytes = 32767 
     261    # Although Firebird allows VARCHAR of 32767, 255 is usually the max 
     262    # for which an index can be created. 
     263    # TODO: this needs some serious work, so that the full size can be 
     264    # allowed while only allowing indices of 255. 
     265    default_adapters = dbtypes.SQL92CHAR.default_adapters.copy() 
     266    default_adapters.update({str: Firebird_str_to_SQL92VARCHAR(), 
     267                             unicode: Firebird_unicode_to_SQL92VARCHAR(), 
     268                             None: Firebird_Pickler(), 
     269                             }) 
     270 
     271class NCHAR(dbtypes.SQL92CHAR): 
     272    # 'The only difference to the NCHAR/VARCHAR datatype is that 
     273    # NCHAR/VARCHAR automatically defines a special character set for 
     274    # this table column: "CHARACTER SET ISO8859_1"' 
     275    default_pytype = unicode 
     276    max_bytes = 32767 
     277    default_adapters = dbtypes.SQL92CHAR.default_adapters.copy() 
     278    default_adapters.update({str: Firebird_str_to_SQL92VARCHAR(), 
     279                             unicode: Firebird_unicode_to_SQL92VARCHAR(), 
     280                             None: Firebird_Pickler(), 
     281                             }) 
     282 
     283class NVARCHAR(dbtypes.SQL92VARCHAR): 
     284    default_pytype = unicode 
     285    max_bytes = 32767 
     286    default_adapters = dbtypes.SQL92CHAR.default_adapters.copy() 
     287    default_adapters.update({str: Firebird_str_to_SQL92VARCHAR(), 
     288                             unicode: Firebird_unicode_to_SQL92VARCHAR(), 
     289                             None: Firebird_Pickler(), 
     290                             }) 
     291 
     292 
     293class DATE(dbtypes.SQL92DATE): 
     294    # 'SQL dialect 1: DATE also includes a time slice 
     295    # (equivalent to TIMESTAMP in dialect 3).' 
     296     
     297    # 'Valid dates are from January 1, 100 AD through February 28, 32,767 AD. 
     298    # Note: for DATE arithmetic purposes, DATE 0 (the integer value of zero) 
     299    # as a DATE in InterBase/Firebird is November 17, 1898.' 
     300    _min = datetime.date(100, 1, 1) 
     301##    _max = datetime.date(32767, 2, 28) 
     302    _max = datetime.date(9999, 12, 31) 
     303    default_adapters = {datetime.date: Firebird_date_adapter()} 
     304 
     305class TIME(dbtypes.SQL92TIME): 
     306    default_adapters = {datetime.time: Firebird_time_adapter()} 
     307 
     308class TIMESTAMP(dbtypes.SQL92TIMESTAMP): 
     309    # 'The range is from January 1,100 AD to February 28, 32768 AD.' 
     310    _min = datetime.datetime(100, 1, 1) 
     311##    _max = datetime.datetime(32768, 2, 28, 23, 59, 59) 
     312    _max = datetime.datetime(9999, 12, 31, 23, 59, 59) 
     313    default_adapters = {datetime.datetime: Firebird_datetime_adapter()} 
     314 
     315 
     316 
     317class ComparableInfinity(object): 
     318     
     319    def __cmp__(self, other): 
     320        if isinstance(other, self.__class__): 
     321            return False 
     322        return True 
     323 
     324class BLOB(dbtypes.TEXT): 
     325    # 'It is important when using blobs in a database, to consider the 
     326    # database page size carefully. Blobs are created as part of a data 
     327    # row, but because a blob could be of unlimited length, what is 
     328    # actually stored with the data row is a BlobID, the data for the 
     329    # blob is stored separately on special blob pages elsewhere in the 
     330    # database.' 
     331    #  
     332    # Max Blob Size: 
     333    #  
     334    # 1Kb page size =>  64 Mb 
     335    # 2Kb page size => 512 Mb 
     336    # 4Kb page size =>   4 Gb 
     337    # 8Kb page size =>  32 Gb 
     338    # 16kb page size => Big enough :-). 
     339    # TEXT has no hard byte limit. 
     340    _bytes = max_bytes = ComparableInfinity() 
     341 
     342 
     343class FirebirdTypeSet(dbtypes.DatabaseTypeSet): 
     344     
     345    known_types = { 
     346                   'float': [FLOAT, DOUBLE], 
     347                   'varchar': [VARCHAR, BLOB], 
     348                   'char': [CHAR, BLOB], 
     349                   'int': [SMALLINT, INTEGER, INT64], 
     350                   'bool': [SMALLINT], 
     351                   'datetime': [TIMESTAMP], 
     352                   'date': [DATE], 
     353                   'time': [TIME], 
     354                   'timedelta': [], 
     355                   'numeric': [NUMERIC], 
     356                   'other': [], 
     357                   } 
    175358 
    176359 
     
    201384        else: 
    202385            # Looking for field in (a, b, c) 
    203             atoms = [self.adapter.coerce(x) for x in op2.value] 
     386            atoms = [] 
     387            for x in op2.value: 
     388                adapter = op1.dbtype.default_adapter(type(x)) 
     389                atoms.append(adapter.push(x, op1.dbtype)) 
    204390            if atoms: 
    205391                return self.get_expr(op1.sql + " IN (" + ", ".join(atoms) + ")", bool) 
     
    217403            # Looking for field in (a, b, c). 
    218404            # Force all args to uppercase for case-insensitive comparison. 
    219             atoms = [self.adapter.coerce(x).upper() for x in op2.value] 
     405            atoms = [] 
     406            for x in op2.value: 
     407                adapter = op1.dbtype.default_adapter(type(x)) 
     408                atoms.append(adapter.push(x.upper(), op1.dbtype)) 
    220409            return self.get_expr("UPPER(%s) IN (%s)" % 
    221410                                 (op1.sql, ", ".join(atoms)), bool) 
     
    254443    # Firebird 1.5 has no LENGTH function 
    255444    func__builtin___len = None 
    256      
    257     def binary_op(self, op): 
    258         op2, op1 = self.stack.pop(), self.stack.pop() 
    259         if op1 is decompile.cannot_represent or op2 is decompile.cannot_represent: 
    260             self.stack.append(decompile.cannot_represent) 
    261             return 
    262          
    263         t1, t2 = op1.pytype, op2.pytype 
    264          
    265         # See http://www.ibphoenix.com/main.nfs?a=ibphoenix&s=1154534295:6&page=ibp_60_sql_date_fs 
    266         newsql = None 
    267         if t1 is datetime.date: 
    268             if t2 is datetime.date: 
    269                 if op == "-": 
    270                     newsql = "((%s - %s) * 86400)" % (op1.sql, op2.sql) 
    271             elif t2 is datetime.timedelta: 
    272                 newsql = "(%s %s (%s / 86400))" % (op1.sql, op, op2.sql) 
    273         elif t1 is datetime.datetime: 
    274             if t2 is datetime.datetime: 
    275                 if op == "-": 
    276                     newsql = ("((%s - %s) * CAST(86400 AS DOUBLE PRECISION))" 
    277                               % (op1.sql, op2.sql)) 
    278             elif t2 is datetime.timedelta: 
    279                 newsql = ("(%s %s (CAST(%s AS DOUBLE PRECISION) / 86400))" 
    280                           % (op1.sql, op, op2.sql)) 
    281         elif t1 is datetime.timedelta: 
    282             if t2 is datetime.timedelta: 
    283                 newsql = "(%s %s %s)" % (op1.sql, op, op2.sql) 
    284             elif op == "+": 
    285                 if t2 is datetime.date: 
    286                     newsql = "((%s / 86400) + %s)" % (op1.sql, op2.sql) 
    287                 elif t2 is datetime.datetime: 
    288                     newsql = ("((CAST(%s AS DOUBLE PRECISION) / 86400) + %s)" 
    289                               % (op1.sql, op2.sql)) 
    290         else: 
    291             newsql = "(%s %s %s)" % (op1.sql, op, op2.sql) 
    292          
    293         if newsql is None: 
    294             raise TypeError("unsupported operand type(s) for %s: " 
    295                             "%r and %r" % (op, t1, t2)) 
    296          
    297         # re-use op1 
    298         op1.pytype = self.result_type[(t1, op, t2)] 
    299         op1.sql = newsql 
    300         if not op1.name.startswith("expr_"): 
    301             op1.name = "expr_%s" % op1.name 
    302         self.stack.append(op1) 
    303445 
    304446 
     
    366508    def insert(self, **inputs): 
    367509        """Insert a row and return {idcolkey: newid}.""" 
    368         coerce_out = self.schema.db.adaptertosql.coerce 
    369         coerce_in = self.schema.db.adapterfromdb.coerce 
    370          
    371510        newids = {} 
    372511        fields = [] 
     
    378517                       % col.sequence_name) 
    379518                data, _ = self.schema.db.fetch(sql) 
    380                 newid = coerce_in(data[0][0], col.dbtype, col.pytype) 
     519                newid = col.adapter.pull(data[0][0], col.dbtype) 
    381520                newids[key] = newid 
    382521                 
    383                 val = coerce_out(newid, col.dbtype) 
     522                val = col.adapter.push(newid, col.dbtype) 
    384523                fields.append(col.qname) 
    385524                values.append(val) 
    386525            elif key in inputs: 
    387                 val = coerce_out(inputs[key], col.dbtype) 
     526                val = col.adapter.push(inputs[key], col.dbtype) 
    388527                fields.append(col.qname) 
    389528                values.append(val) 
     
    551690            "T.RDB$FIELD_NAME='RDB$FIELD_TYPE';" % tablename, conn=conn) 
    552691        cols = [] 
    553         pytype = self.db.typeadapter.python_type 
    554692        for name, dbtype, fieldlen, default, prec, scale in data: 
    555             hints = {} 
    556             if prec: 
    557                 hints['precision'] = prec 
    558                 if scale: 
    559                     hints['scale'] = scale = abs(scale) 
    560             else: 
    561                 hints['bytes'] = fieldlen 
    562              
    563693            # FB pads name and type values to 31 chars. 
    564694            name, dbtype = name.rstrip(), dbtype.rstrip() 
    565695             
    566             # Grr. INT64 may actually have been declared as NUMERIC. 
    567             # See http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_60_exact_num_fs 
    568             if scale and (dbtype in ('SMALLINT', 'INTEGER', 'INT64')): 
    569                 dbtype = "NUMERIC" 
     696            dbtype = self.db.typeset.canonicalize(dbtype)() 
     697            if prec: 
     698                dbtype.precision = prec 
     699                if scale: 
     700                    dbtype.scale = abs(scale) 
     701            else: 
     702                dbtype.bytes = fieldlen 
     703             
     704##            # Grr. INT64 may actually have been declared as NUMERIC. 
     705##            # See http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_60_exact_num_fs 
     706##            if scale and (dbtype in ('SMALLINT', 'INTEGER', 'INT64')): 
     707##                dbtype = "NUMERIC" 
    570708             
    571709            key = (name in pks) 
     710             
     711            # Column(pytype, dbtype, default=None, key=False, name, qname) 
     712            col = geniusql.Column(dbtype.default_pytype, dbtype, None, 
     713                                  key, name, self.db.quote(name)) 
     714            col.adapter = dbtype.default_adapter(col.pytype) 
     715            cols.append(col) 
    572716             
    573717            # RDB$RELATION_FIELDS.RDB$DEFAULT_SOURCE = None | "DEFAULT x" 
    574718            if default: 
    575719                default = default[len("DEFAULT "):] 
    576                 if dbtype in ("SHORT", "LONG"): 
    577                     default = int(default) 
    578                 elif dbtype in ("FLOAT",): 
    579                     default = float(default) 
    580              
    581             # Column(pytype, dbtype, default=None, hints=None, key=False, name, qname) 
    582             col = geniusql.Column(pytype(dbtype), dbtype, default, 
    583                                   hints, key, name, self.db.quote(name)) 
    584             cols.append(col) 
     720                default = col.adapter.pull(default, col.dbtype) 
    585721        return cols 
    586722     
     
    611747        Firebird needs the sequence created in a separate SQL statement. 
    612748        """ 
    613         dbtype = column.dbtype 
    614          
    615749        default = column.default or "" 
    616750        if default: 
    617             default = self.db.adaptertosql.coerce(default, dbtype) 
     751            default = column.adapter.push(default, column.dbtype) 
    618752            default = " DEFAULT %s" % default 
    619753         
     
    623757            notnull = " NOT NULL" 
    624758         
    625         return '%s %s%s%s' % (column.qname, dbtype, default, notnull) 
     759        return '%s %s%s%s' % (column.qname, column.dbtype.ddl(), default, notnull) 
    626760     
    627761    def create_sequence(self, table, column): 
     
    671805    decompiler = FirebirdSQLDecompiler 
    672806     
    673     adaptertosql = AdapterToFireBirdSQL() 
    674     adapterfromdb = AdapterFromFirebirdDB() 
    675     typeadapter = TypeAdapterFirebird() 
     807    typeset = FirebirdTypeSet() 
    676808    connectionmanager = FirebirdConnectionManager 
    677809     
     
    695827                conn = self.connections.get() 
    696828            if isinstance(query, unicode): 
    697                 query = query.encode(self.adaptertosql.encoding) 
     829                query = query.encode(self.encoding) 
    698830            self.log(query) 
    699831            cur = conn.cursor() 
     
    733865                conn = self.connections.get() 
    734866            if isinstance(query, unicode): 
    735                 query = query.encode(self.adaptertosql.encoding) 
     867                query = query.encode(self.encoding) 
    736868            self.log(query) 
    737869            cur = conn.cursor() 
  • trunk/geniusql/test/zoo_fixture.py

    r68 r69  
    113113        Exhibit = schema.table('Exhibit') 
    114114        # Make this a string to help test vs unicode. 
    115         Exhibit['Name'] = schema.column(str, key=True) 
     115        # Also, use a smaller field size so Firebird doesn't choke 
     116        # when forming the primary key. 
     117        # See http://www.volny.cz/iprenosil/interbase/ip_ib_indexcalculator.htm 
     118        Exhibit['Name'] = schema.column(str, key=True, hints={'bytes': 192}) 
    116119        Exhibit.add_index('Name') 
    117120        Exhibit['ZooID'] = schema.column(int, key=True)