Changeset 808

Show
Ignore:
Timestamp:
03/08/07 14:43:44 (2 years ago)
Author:
mfenniak
Message:

work on adding bin/txt format detection support

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • pg8000/trunk/pg8000.py

    r807 r808  
    117117            if self._command_complete: 
    118118                return None 
    119             end_of_data, rows = self.c.fetch_rows(self.name, self.row_cache_size
     119            end_of_data, rows = self.c.fetch_rows(self.name, self.row_cache_size, self._row_desc
    120120            self._cached_rows = rows 
    121121            if end_of_data: 
     
    127127        row = self._cached_rows[0] 
    128128        del self._cached_rows[0] 
    129         return tuple([Types.py_value(row.fields[i], self._row_desc.fields[i]) for i in range(len(row.fields))]
     129        return tuple(row
    130130 
    131131    ## 
     
    254254 
    255255    class Parse(object): 
    256         def __init__(self, ps, qs, types): 
     256        def __init__(self, ps, qs, type_oids): 
    257257            self.ps = ps 
    258258            self.qs = qs 
    259             self.types = [Types.pg_type_id(x) for x in types] 
     259            self.type_oids = type_oids 
    260260 
    261261        def serialize(self): 
    262262            val = self.ps + "\x00" + self.qs + "\x00" 
    263             val = val + struct.pack("!h", len(self.types)) 
    264             for oid in self.types: 
     263            val = val + struct.pack("!h", len(self.type_oids)) 
     264            for oid in self.type_oids: 
    265265                val = val + struct.pack("!i", oid) 
    266266            val = struct.pack("!i", len(val) + 4) + val 
     
    281281                else: 
    282282                    fc = self.in_fc[i] 
    283                 self.params.append(Types.pg_value(params[i], fc, client_encoding)) 
     283                self.params.append(Types.pg_value(params[i], fc, client_encoding = client_encoding)) 
    284284            self.out_fc = out_fc 
    285285 
     
    615615        def extended_query(self, portal, statement, qs, params): 
    616616            self.verifyState("ready") 
    617             self._send(Protocol.Parse(statement, qs, [type(x) for x in params])) 
    618             self._send(Protocol.Bind(portal, statement, (0,), params, (0,), self._client_encoding)) 
     617            type_info = [Types.pg_type_info(type(x)) for x in params] 
     618            param_types, param_fc = [x[0] for x in type_info], [x[1] for x in type_info] # zip(*type_info) -- fails on empty arr 
     619            self._send(Protocol.Parse(statement, qs, param_types)) 
     620            # The Types functions have the ability to "prefer" a certain output 
     621            # format from the server, however... we need to know the output 
     622            # type before we can know the output format.  we don't get the 
     623            # types until we bind, which is where we need to specify the format 
     624            # codes.  So, we need to go with text format for all outputs from 
     625            # the server, even though it'd be nice to have the flexibility to 
     626            # get some return values in bin and some in txt. 
     627            self._send(Protocol.Bind(portal, statement, param_fc, params, (0,), self._client_encoding)) 
    619628            self._send(Protocol.DescribePortal(portal)) 
    620629            self._send(Protocol.Flush()) 
     
    628637                    pass 
    629638                elif isinstance(msg, Protocol.NoData): 
    630                     # no data means we should execute this command right away 
     639                    # No data means we should execute this command right away. 
     640                    # There's no need to rebind like we were planning to, since 
     641                    # there are no return format codes.  Just execute and sync. 
    631642                    self._send(Protocol.Execute(portal, 0)) 
    632643                    self._send(Protocol.Sync()) 
     
    650661                    raise InternalError("Unexpected response msg %r" % (msg)) 
    651662 
    652         def fetch_rows(self, portal, row_count): 
     663        def fetch_rows(self, portal, row_count, row_desc): 
    653664            self.verifyState("ready") 
    654665            self._send(Protocol.Execute(portal, row_count)) 
     
    659670                msg = self._read_message() 
    660671                if isinstance(msg, Protocol.DataRow): 
    661                     rows.append(msg) 
     672                    rows.append( 
     673                            [Types.py_value(msg.fields[i], row_desc.fields[i], client_encoding=self._client_encoding) 
     674                                for i in range(len(msg.fields))] 
     675                            ) 
    662676                elif isinstance(msg, Protocol.PortalSuspended): 
    663677                    # got all the rows we asked for, but not all that exist 
     
    727741 
    728742class Types(object): 
    729     def pg_type_id(typ): 
     743    def pg_type_info(typ): 
    730744        data = Types.py_types.get(typ) 
    731745        if data == None: 
    732746            raise NotSupportedError("type %r not mapped to pg type" % typ) 
    733         type_oid, func_txt, func_bin = data 
    734         return type_oid 
    735     pg_type_id = staticmethod(pg_type_id) 
    736  
    737     def pg_value(v, fc, client_encoding): 
     747        type_oid = data.get("tid") 
     748        if type_oid == None: 
     749            raise InternalError("type %r has no type_oid" % typ) 
     750        prefer = data.get("prefer") 
     751        if prefer != None: 
     752            if prefer == "bin": 
     753                if data.get("bin_out") == None: 
     754                    raise InternalError("bin format prefered but not avail for type %r" % typ) 
     755                format = 1 
     756            elif prefer == "txt": 
     757                if data.get("txt_out") == None: 
     758                    raise InternalError("txt format prefered but not avail for type %r" % typ) 
     759                format = 0 
     760            else: 
     761                raise InternalError("prefer flag not recognized for type %r" % typ) 
     762        else: 
     763            # by default, prefer bin, but go with whatever exists 
     764            if data.get("bin_out"): 
     765                format = 1 
     766            elif data.get("txt_out"): 
     767                format = 0 
     768            else: 
     769                raise InternalError("no conversion fuction for type %r" % typ) 
     770        return type_oid, format 
     771    pg_type_info = staticmethod(pg_type_info) 
     772 
     773    def pg_value(v, fc, **kwargs): 
    738774        typ = type(v) 
    739775        data = Types.py_types.get(typ) 
    740776        if data == None: 
    741777            raise NotSupportedError("type %r not mapped to pg type" % typ) 
    742         type_oid, func_txt, func_bin = data 
    743778        if fc == 0: 
    744             func = func_txt 
     779            func = data.get("txt_out") 
     780        elif fc == 1: 
     781            func = data.get("bin_out") 
    745782        else: 
    746             func = func_bin 
     783            raise InternalError("unrecognized format code %r" % fc) 
    747784        if func == None: 
    748             raise NotSupportedError("type %r, format code %r not converted" % (typ, fc)) 
    749         return func(v, client_encoding
     785            raise NotSupportedError("type %r, format code %r not supported" % (typ, fc)) 
     786        return func(v, **kwargs
    750787    pg_value = staticmethod(pg_value) 
    751788 
    752     def py_value(data, description): 
     789    def py_type_info(description): 
     790        type_oid = description['type_oid'] 
     791        data = Types.pg_types.get(typ) 
     792        if data == None: 
     793            raise NotSupportedError("type oid %r not mapped to py type" % type_oid) 
     794        prefer = data.get("prefer") 
     795        if prefer != None: 
     796            if prefer == "bin": 
     797                if data.get("bin_in") == None: 
     798                    raise InternalError("bin format prefered but not avail for type oid %r" % type_oid) 
     799                format = 1 
     800            elif prefer == "txt": 
     801                if data.get("txt_in") == None: 
     802                    raise InternalError("txt format prefered but not avail for type oid %r" % type_oid) 
     803                format = 0 
     804            else: 
     805                raise InternalError("prefer flag not recognized for type oid %r" % type_oid) 
     806        else: 
     807            # by default, prefer bin, but go with whatever exists 
     808            if data.get("bin_in"): 
     809                format = 1 
     810            elif data.get("txt_in"): 
     811                format = 0 
     812            else: 
     813                raise InternalError("no conversion fuction for type oid %r" % type_oid) 
     814        return type_oid 
     815    py_type_info = staticmethod(py_type_info) 
     816 
     817    def py_value(v, description, **kwargs): 
    753818        type_oid = description['type_oid'] 
    754819        format = description['format'] 
    755         funcs = Types.pg_types.get(type_oid) 
    756         if funcs == None: 
    757             raise NotSupportedError("data response type %r not supported" % (type_oid)) 
    758         func = funcs[format] 
     820        data = Types.pg_types.get(type_oid) 
     821        if data == None: 
     822            raise NotSupportedError("type oid %r not supported" % type_oid) 
     823        if format == 0: 
     824            func = data.get("txt_in") 
     825        elif format == 1: 
     826            func = data.get("bin_in") 
     827        else: 
     828            raise NotSupportedError("format code %r not supported" % format) 
    759829        if func == None: 
    760830            raise NotSupportedError("data response format %r, type %r not supported" % (format, type_oid)) 
    761         return func(data, description
     831        return func(v, **kwargs
    762832    py_value = staticmethod(py_value) 
    763833 
    764     def boolin(data, description): 
     834    def boolin(data, **kwargs): 
    765835        return data == 't' 
    766836 
    767     def boolrecv(data, description): 
     837    def boolrecv(data, **kwargs): 
    768838        return data == "\x01" 
    769839 
    770     def int2recv(data, description): 
     840    def int2recv(data, **kwargs): 
    771841        return struct.unpack("!h", data)[0] 
    772842 
    773     def int2in(data, description): 
     843    def int2in(data, **kwargs): 
    774844        return int(data) 
    775845 
    776     def int4recv(data, description): 
     846    def int4recv(data, **kwargs): 
    777847        return struct.unpack("!i", data)[0] 
    778848 
    779     def int4in(data, description): 
     849    def int4in(data, **kwargs): 
    780850        return int(data) 
    781851 
    782     def int8in(data, description): 
     852    def int8recv(data, **kwargs): 
     853        return struct.unpack("!q", data)[0] 
     854 
     855    def int8in(data, **kwargs): 
    783856        return int(data) 
    784857 
    785     def float4in(data, description): 
     858    def float4in(data, **kwargs): 
    786859        return float(data) 
    787860 
    788     def float8in(data, description): 
     861    def float4recv(data, **kwargs): 
     862        return struct.unpack("!f", data)[0] 
     863 
     864    def float8recv(data, **kwargs): 
     865        return struct.unpack("!d", data)[0] 
     866 
     867    def float8in(data, **kwargs): 
    789868        return float(data) 
    790869 
    791     def timestamp_recv(data, description): 
     870    def timestamp_recv(data, **kwargs): 
    792871        val = struct.unpack("!d", data)[0] 
    793872        return datetime.datetime(2000, 1, 1) + datetime.timedelta(seconds = val) 
    794873 
    795     def timestamp_in(data, description): 
     874    def timestamp_in(data, **kwargs): 
    796875        year = int(data[0:4]) 
    797876        month = int(data[5:7]) 
     
    802881        return datetime.datetime(year, month, day, hour, minute, int(sec), int((sec - int(sec)) * 1000000)) 
    803882 
    804     def numeric_in(data, description): 
     883    def numeric_in(data, **kwargs): 
    805884        if data.find(".") == -1: 
    806885            return int(data) 
     
    808887            return decimal.Decimal(data) 
    809888 
    810     def numeric_out(v, ce): 
     889    def numeric_out(v, **kwargs): 
    811890        return str(v) 
    812891 
    813     def varcharin(data, description): 
    814         return unicode(data, "utf-8"
    815  
    816     def textout(v, ce): 
    817         return v.encode(ce
     892    def varcharin(data, client_encoding, **kwargs): 
     893        return unicode(data, client_encoding
     894 
     895    def textout(v, client_encoding, **kwargs): 
     896        return v.encode(client_encoding
    818897 
    819898    def timestamptz_in(data, description): 
     
    844923            return datetime.timedelta(0) 
    845924 
    846     # interval req. new patch for binary-output format prefered. 
    847     #def interval_in(data, description): 
    848     #    print repr(data), repr(description) 
     925    def bytearecv(data, **kwargs): 
     926        return Bytea(data) 
     927 
     928    # interval support does not provide a Python-usable interval object yet 
     929    def interval_in(data, **kwargs): 
     930        return data 
    849931 
    850932    py_types = { 
    851         int: (1700, numeric_out, None)
    852         str: (25, textout, None)
    853         unicode: (25, textout, None)
     933        int: {"tid": 1700, "txt_out": numeric_out}
     934        str: {"tid": 25, "txt_out": textout}
     935        unicode: {"tid": 25, "txt_out": textout}
    854936    } 
    855937 
    856938    pg_types = { 
    857         16: (boolin, boolrecv), 
    858         17: (None, None), # bytea not supported yet 
    859         20: (int8in, None), 
    860         21: (int2in, int2recv), 
    861         23: (int4in, int4recv), 
    862         25: (varcharin, None),  # text 
    863         700: (float4in, None), 
    864         701: (float8in, None), 
    865         1042: (varcharin, None), # char 
    866         1043: (varcharin, None), # varchar 
    867         1114: (timestamp_in, timestamp_recv), 
    868         1184: (timestamptz_in, None), # timestamp w/ tz 
    869         1186: (interval_in, None), 
    870         1700: (numeric_in, None), 
     939        16: {"txt_in": boolin, "bin_in": boolrecv, "prefer": "bin"}, 
     940        17: {"bin_in": bytearecv}, 
     941        20: {"txt_in": int8in, "bin_in": int8recv, "prefer": "bin"}, 
     942        21: {"txt_in": int2in, "bin_in": int2recv, "prefer": "bin"}, 
     943        23: {"txt_in": int4in, "bin_in": int4recv, "prefer": "bin"}, 
     944        25: {"txt_in": varcharin}, # TEXT type 
     945        700: {"txt_in": float4in, "bin_in": float4recv, "prefer": "bin"}, 
     946        701: {"txt_in": float8in, "bin_in": float8recv, "prefer": "bin"}, 
     947        1042: {"txt_in": varcharin}, # CHAR type 
     948        1043: {"txt_in": varcharin}, # VARCHAR type 
     949        1114: {"txt_in": timestamp_in, "bin_in": timestamp_recv, "prefer": "bin"}, 
     950        1186: {"txt_in": interval_in}, 
     951        1700: {"txt_in": numeric_in}, 
    871952    } 
    872  
     953        #1184: (timestamptz_in, None), # timestamp w/ tz 
     954 
     955class Bytea(str): 
     956    pass 
     957 
     958 
  • pg8000/trunk/pg8000-test.py

    r807 r808  
    6868#assert tuple(cur1) == ({'timestamp': datetime.datetime(2001, 2, 3, 4, 5, 6, 170000, pg8000.Types.FixedOffsetTz("-07"))},) 
    6969 
    70 cur1.execute("SELECT '1 day'::interval") 
    71 print repr(tuple(cur1)
    72  
     70cur1.execute("SELECT '1 month'::interval") 
     71assert tuple(cur1) == ({'interval': '1 mon'},
     72#print repr(tuple(cur1)) 
    7373 
    7474print "Type checks complete."