Changeset 808
- Timestamp:
- 03/08/07 14:43:44 (2 years ago)
- Files:
-
- pg8000/trunk/pg8000.py (modified) (12 diffs)
- pg8000/trunk/pg8000-test.py (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
pg8000/trunk/pg8000.py
r807 r808 117 117 if self._command_complete: 118 118 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) 120 120 self._cached_rows = rows 121 121 if end_of_data: … … 127 127 row = self._cached_rows[0] 128 128 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) 130 130 131 131 ## … … 254 254 255 255 class Parse(object): 256 def __init__(self, ps, qs, type s):256 def __init__(self, ps, qs, type_oids): 257 257 self.ps = ps 258 258 self.qs = qs 259 self.type s = [Types.pg_type_id(x) for x in types]259 self.type_oids = type_oids 260 260 261 261 def serialize(self): 262 262 val = self.ps + "\x00" + self.qs + "\x00" 263 val = val + struct.pack("!h", len(self.type s))264 for oid in self.type s:263 val = val + struct.pack("!h", len(self.type_oids)) 264 for oid in self.type_oids: 265 265 val = val + struct.pack("!i", oid) 266 266 val = struct.pack("!i", len(val) + 4) + val … … 281 281 else: 282 282 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)) 284 284 self.out_fc = out_fc 285 285 … … 615 615 def extended_query(self, portal, statement, qs, params): 616 616 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)) 619 628 self._send(Protocol.DescribePortal(portal)) 620 629 self._send(Protocol.Flush()) … … 628 637 pass 629 638 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. 631 642 self._send(Protocol.Execute(portal, 0)) 632 643 self._send(Protocol.Sync()) … … 650 661 raise InternalError("Unexpected response msg %r" % (msg)) 651 662 652 def fetch_rows(self, portal, row_count ):663 def fetch_rows(self, portal, row_count, row_desc): 653 664 self.verifyState("ready") 654 665 self._send(Protocol.Execute(portal, row_count)) … … 659 670 msg = self._read_message() 660 671 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 ) 662 676 elif isinstance(msg, Protocol.PortalSuspended): 663 677 # got all the rows we asked for, but not all that exist … … 727 741 728 742 class Types(object): 729 def pg_type_i d(typ):743 def pg_type_info(typ): 730 744 data = Types.py_types.get(typ) 731 745 if data == None: 732 746 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): 738 774 typ = type(v) 739 775 data = Types.py_types.get(typ) 740 776 if data == None: 741 777 raise NotSupportedError("type %r not mapped to pg type" % typ) 742 type_oid, func_txt, func_bin = data743 778 if fc == 0: 744 func = func_txt 779 func = data.get("txt_out") 780 elif fc == 1: 781 func = data.get("bin_out") 745 782 else: 746 func = func_bin783 raise InternalError("unrecognized format code %r" % fc) 747 784 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) 750 787 pg_value = staticmethod(pg_value) 751 788 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): 753 818 type_oid = description['type_oid'] 754 819 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) 759 829 if func == None: 760 830 raise NotSupportedError("data response format %r, type %r not supported" % (format, type_oid)) 761 return func( data, description)831 return func(v, **kwargs) 762 832 py_value = staticmethod(py_value) 763 833 764 def boolin(data, description):834 def boolin(data, **kwargs): 765 835 return data == 't' 766 836 767 def boolrecv(data, description):837 def boolrecv(data, **kwargs): 768 838 return data == "\x01" 769 839 770 def int2recv(data, description):840 def int2recv(data, **kwargs): 771 841 return struct.unpack("!h", data)[0] 772 842 773 def int2in(data, description):843 def int2in(data, **kwargs): 774 844 return int(data) 775 845 776 def int4recv(data, description):846 def int4recv(data, **kwargs): 777 847 return struct.unpack("!i", data)[0] 778 848 779 def int4in(data, description):849 def int4in(data, **kwargs): 780 850 return int(data) 781 851 782 def int8in(data, description): 852 def int8recv(data, **kwargs): 853 return struct.unpack("!q", data)[0] 854 855 def int8in(data, **kwargs): 783 856 return int(data) 784 857 785 def float4in(data, description):858 def float4in(data, **kwargs): 786 859 return float(data) 787 860 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): 789 868 return float(data) 790 869 791 def timestamp_recv(data, description):870 def timestamp_recv(data, **kwargs): 792 871 val = struct.unpack("!d", data)[0] 793 872 return datetime.datetime(2000, 1, 1) + datetime.timedelta(seconds = val) 794 873 795 def timestamp_in(data, description):874 def timestamp_in(data, **kwargs): 796 875 year = int(data[0:4]) 797 876 month = int(data[5:7]) … … 802 881 return datetime.datetime(year, month, day, hour, minute, int(sec), int((sec - int(sec)) * 1000000)) 803 882 804 def numeric_in(data, description):883 def numeric_in(data, **kwargs): 805 884 if data.find(".") == -1: 806 885 return int(data) … … 808 887 return decimal.Decimal(data) 809 888 810 def numeric_out(v, ce):889 def numeric_out(v, **kwargs): 811 890 return str(v) 812 891 813 def varcharin(data, description):814 return unicode(data, "utf-8")815 816 def textout(v, c e):817 return v.encode(c e)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) 818 897 819 898 def timestamptz_in(data, description): … … 844 923 return datetime.timedelta(0) 845 924 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 849 931 850 932 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}, 854 936 } 855 937 856 938 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}, 871 952 } 872 953 #1184: (timestamptz_in, None), # timestamp w/ tz 954 955 class Bytea(str): 956 pass 957 958 pg8000/trunk/pg8000-test.py
r807 r808 68 68 #assert tuple(cur1) == ({'timestamp': datetime.datetime(2001, 2, 3, 4, 5, 6, 170000, pg8000.Types.FixedOffsetTz("-07"))},) 69 69 70 cur1.execute("SELECT '1 day'::interval")71 print repr(tuple(cur1))72 70 cur1.execute("SELECT '1 month'::interval") 71 assert tuple(cur1) == ({'interval': '1 mon'},) 72 #print repr(tuple(cur1)) 73 73 74 74 print "Type checks complete."
