001: /*
002: * Jython Database Specification API 2.0
003: *
004: * $Id: JDBC20DataHandler.java 2467 2005-05-12 02:38:59Z fwierzbicki $
005: *
006: * Copyright (c) 2001 brian zimmer <bzimmer@ziclix.com>
007: *
008: */
009: package com.ziclix.python.sql;
010:
011: import org.python.core.Py;
012: import org.python.core.PyFile;
013: import org.python.core.PyObject;
014: import org.python.core.PyString;
015:
016: import java.io.BufferedInputStream;
017: import java.io.BufferedReader;
018: import java.io.ByteArrayInputStream;
019: import java.io.InputStream;
020: import java.io.InputStreamReader;
021: import java.io.Reader;
022: import java.math.BigDecimal;
023: import java.sql.Blob;
024: import java.sql.PreparedStatement;
025: import java.sql.ResultSet;
026: import java.sql.SQLException;
027: import java.sql.Types;
028:
029: /**
030: * Support for JDBC 2.x type mappings, including Arrays, CLOBs and BLOBs.
031: *
032: * @author brian zimmer
033: * @author last revised by $Author: fwierzbicki $
034: * @version $Revision: 2467 $
035: */
036: public class JDBC20DataHandler extends FilterDataHandler {
037:
038: /**
039: * Handle JDBC 2.0 datatypes.
040: */
041: public JDBC20DataHandler(DataHandler datahandler) {
042: super (datahandler);
043: }
044:
045: /**
046: * Handle CLOBs and BLOBs.
047: *
048: * @param stmt
049: * @param index
050: * @param object
051: * @param type
052: * @throws SQLException
053: */
054: public void setJDBCObject(PreparedStatement stmt, int index,
055: PyObject object, int type) throws SQLException {
056:
057: if (DataHandler.checkNull(stmt, index, object, type)) {
058: return;
059: }
060:
061: switch (type) {
062:
063: case Types.CLOB:
064: if (object instanceof PyFile) {
065: object = new PyString(((PyFile) object).read());
066: }
067:
068: String clob = (String) object.__tojava__(String.class);
069: int length = clob.length();
070: InputStream stream = new ByteArrayInputStream(clob
071: .getBytes());
072:
073: stream = new BufferedInputStream(stream);
074:
075: stmt.setBinaryStream(index, stream, length);
076:
077: // Reader reader = new StringReader(clob);
078: // reader = new BufferedReader(reader);
079: // stmt.setCharacterStream(index, reader, length);
080: break;
081:
082: case Types.BLOB:
083: byte[] lob = null;
084: Object jobject = null;
085:
086: if (object instanceof PyFile) {
087: jobject = object.__tojava__(InputStream.class);
088: } else {
089: jobject = object.__tojava__(Object.class);
090: }
091:
092: // it really is unfortunate that I need to send the length of the stream
093: if (jobject instanceof InputStream) {
094: lob = DataHandler.read(new BufferedInputStream(
095: (InputStream) jobject));
096: } else if (jobject instanceof byte[]) {
097: lob = (byte[]) jobject;
098: }
099:
100: if (lob != null) {
101: stmt.setBytes(index, lob);
102:
103: break;
104: }
105: default:
106: super .setJDBCObject(stmt, index, object, type);
107: break;
108: }
109: }
110:
111: /**
112: * Get the object from the result set.
113: *
114: * @param set
115: * @param col
116: * @param type
117: * @return a Python object
118: * @throws SQLException
119: */
120: public PyObject getPyObject(ResultSet set, int col, int type)
121: throws SQLException {
122:
123: PyObject obj = Py.None;
124:
125: switch (type) {
126:
127: case Types.NUMERIC:
128: case Types.DECIMAL:
129:
130: // in JDBC 2.0, use of a scale is deprecated
131: try {
132: BigDecimal bd = set.getBigDecimal(col);
133:
134: obj = (bd == null) ? Py.None : Py.newFloat(bd
135: .doubleValue());
136: } catch (SQLException e) {
137: obj = super .getPyObject(set, col, type);
138: }
139: break;
140:
141: case Types.CLOB:
142:
143: /*
144: * It seems some drivers (well at least Informix) don't clean up after themselves
145: * if the Clob is requested. The engine keeps a handle to an open table for each
146: * row requested and cleans up fully only when the ResultSet or Connection is closed.
147: * While this generally will never be noticed because the number of CLOBs or BLOBs
148: * queried will likely be small in the event a large number are queried, it is a huge
149: * problem. So, handle it as low as possible by managing the stream directly. I've
150: * decided to leave this in the generic JDBC20 handler because it works for all engines
151: * I've tested and seems to perform quite well to boot.
152: */
153: Reader reader = null;
154:
155: try {
156: InputStream stream = set.getBinaryStream(col);
157:
158: if (stream == null) {
159: obj = Py.None;
160: } else {
161: reader = new InputStreamReader(stream);
162: reader = new BufferedReader(reader);
163: obj = Py.newString(DataHandler.read(reader));
164: }
165: } finally {
166: if (reader != null) {
167: try {
168: reader.close();
169: } catch (Exception e) {
170: }
171: }
172: }
173: break;
174:
175: case Types.BLOB:
176: Blob blob = set.getBlob(col);
177:
178: if (blob == null) {
179: obj = Py.None;
180: } else {
181: InputStream stream = null;
182:
183: try {
184: stream = blob.getBinaryStream();
185: stream = new BufferedInputStream(stream);
186: obj = Py.java2py(DataHandler.read(stream));
187: } finally {
188: if (stream != null) {
189: try {
190: stream.close();
191: } catch (Exception e) {
192: }
193: }
194: }
195: }
196: break;
197:
198: case Types.ARRAY:
199: obj = Py.java2py(set.getArray(col).getArray());
200: break;
201:
202: default:
203: return super.getPyObject(set, col, type);
204: }
205:
206: return (set.wasNull() || (obj == null)) ? Py.None : obj;
207: }
208: }
|