0001: /*
0002: * Jython Database Specification API 2.0
0003: *
0004: * $Id: PyCursor.java 3248 2007-05-30 05:19:19Z cgroves $
0005: *
0006: * Copyright (c) 2001 brian zimmer <bzimmer@ziclix.com>
0007: *
0008: */
0009: package com.ziclix.python.sql;
0010:
0011: import java.sql.DatabaseMetaData;
0012: import java.sql.SQLException;
0013: import java.sql.SQLWarning;
0014: import java.sql.Statement;
0015: import java.util.List;
0016: import org.python.core.ClassDictInit;
0017: import org.python.core.Py;
0018: import org.python.core.PyBuiltinMethodSet;
0019: import org.python.core.PyClass;
0020: import org.python.core.PyDictionary;
0021: import org.python.core.PyException;
0022: import org.python.core.PyInteger;
0023: import org.python.core.PyList;
0024: import org.python.core.PyObject;
0025: import org.python.core.PyString;
0026: import org.python.core.PyTuple;
0027: import com.ziclix.python.sql.util.PyArgParser;
0028:
0029: /**
0030: * These objects represent a database cursor, which is used to manage the
0031: * context of a fetch operation.
0032: *
0033: * @author brian zimmer
0034: * @author last revised by $Author: cgroves $
0035: * @version $Revision: 3248 $
0036: */
0037: public class PyCursor extends PyObject implements ClassDictInit,
0038: WarningListener {
0039:
0040: /** Field fetch */
0041: protected Fetch fetch;
0042:
0043: /** Field closed */
0044: private boolean closed;
0045:
0046: /** Field arraysize */
0047: protected int arraysize;
0048:
0049: /** Field softspace */
0050: protected int softspace;
0051:
0052: /** Field rsType */
0053: protected PyObject rsType;
0054:
0055: /** Field rsConcur */
0056: protected PyObject rsConcur;
0057:
0058: /** Field warnings */
0059: protected PyObject warnings;
0060:
0061: /** Field warnings */
0062: protected PyObject lastrowid;
0063:
0064: /** Field updatecount */
0065: protected PyObject updatecount;
0066:
0067: /** Field dynamicFetch */
0068: protected boolean dynamicFetch;
0069:
0070: /** Field connection */
0071: protected PyConnection connection;
0072:
0073: /** Field datahandler */
0074: protected DataHandler datahandler;
0075:
0076: /** Field statement */
0077: protected PyStatement statement;
0078:
0079: // they are stateless instances, so we only need to instantiate it once
0080: private static final DataHandler DATAHANDLER = DataHandler
0081: .getSystemDataHandler();
0082:
0083: /**
0084: * Create the cursor with a static fetch.
0085: *
0086: * @param connection
0087: */
0088: PyCursor(PyConnection connection) {
0089: this (connection, false);
0090: }
0091:
0092: /**
0093: * Create the cursor, optionally choosing the type of fetch (static or dynamic).
0094: * If dynamicFetch is true, then use a dynamic fetch.
0095: *
0096: * @param connection
0097: * @param dynamicFetch
0098: */
0099: PyCursor(PyConnection connection, boolean dynamicFetch) {
0100:
0101: this .arraysize = 1;
0102: this .softspace = 0;
0103: this .closed = false;
0104: this .rsType = Py.None;
0105: this .rsConcur = Py.None;
0106: this .connection = connection;
0107: this .datahandler = DATAHANDLER;
0108: this .dynamicFetch = dynamicFetch;
0109:
0110: // constructs the appropriate Fetch among other things
0111: this .clear();
0112: }
0113:
0114: /**
0115: * Create the cursor, optionally choosing the type of fetch (static or dynamic).
0116: * If dynamicFetch is true, then use a dynamic fetch.
0117: * rsType and rsConcur are used to create the Statement if both are non-None
0118: *
0119: * @param connection
0120: * @param dynamicFetch
0121: * @param rsType
0122: * @param rsConcur
0123: */
0124: PyCursor(PyConnection connection, boolean dynamicFetch,
0125: PyObject rsType, PyObject rsConcur) {
0126:
0127: this (connection, dynamicFetch);
0128:
0129: this .rsType = rsType;
0130: this .rsConcur = rsConcur;
0131: }
0132:
0133: /** Field __class__ */
0134: public static PyClass __class__;
0135:
0136: /**
0137: * Method getPyClass
0138: *
0139: * @return PyClass
0140: *
0141: */
0142: protected PyClass getPyClass() {
0143: return __class__;
0144: }
0145:
0146: /** Field __methods__ */
0147: protected static PyList __methods__;
0148:
0149: /** Field __members__ */
0150: protected static PyList __members__;
0151:
0152: static {
0153: PyObject[] m = new PyObject[9];
0154:
0155: m[0] = new PyString("close");
0156: m[1] = new PyString("execute");
0157: m[2] = new PyString("executemany");
0158: m[3] = new PyString("fetchone");
0159: m[4] = new PyString("fetchall");
0160: m[5] = new PyString("fetchmany");
0161: m[6] = new PyString("callproc");
0162: m[7] = new PyString("next");
0163: m[8] = new PyString("write");
0164: __methods__ = new PyList(m);
0165: m = new PyObject[11];
0166: m[0] = new PyString("arraysize");
0167: m[1] = new PyString("rowcount");
0168: m[2] = new PyString("rownumber");
0169: m[3] = new PyString("description");
0170: m[4] = new PyString("datahandler");
0171: m[5] = new PyString("warnings");
0172: m[6] = new PyString("lastrowid");
0173: m[7] = new PyString("updatecount");
0174: m[8] = new PyString("softspace");
0175: m[9] = new PyString("closed");
0176: m[10] = new PyString("connection");
0177: __members__ = new PyList(m);
0178: }
0179:
0180: /**
0181: * String representation of the object.
0182: *
0183: * @return a string representation of the object.
0184: */
0185: public String toString() {
0186: return "<PyCursor object instance at " + Py.id(this ) + ">";
0187: }
0188:
0189: /**
0190: * Sets the attribute name to value.
0191: *
0192: * @param name
0193: * @param value
0194: */
0195: public void __setattr__(String name, PyObject value) {
0196:
0197: if ("arraysize".equals(name)) {
0198: this .arraysize = ((PyInteger) value.__int__()).getValue();
0199: } else if ("softspace".equals(name)) {
0200: this .softspace = ((PyInteger) value.__int__()).getValue();
0201: } else if ("datahandler".equals(name)) {
0202: this .datahandler = (DataHandler) value
0203: .__tojava__(DataHandler.class);
0204: } else {
0205: super .__setattr__(name, value);
0206: }
0207: }
0208:
0209: /**
0210: * Gets the value of the attribute name.
0211: *
0212: * @param name
0213: * @return the attribute for the given name
0214: */
0215: public PyObject __findattr__(String name) {
0216:
0217: if ("arraysize".equals(name)) {
0218: return Py.newInteger(arraysize);
0219: } else if ("softspace".equals(name)) {
0220: return Py.newInteger(softspace);
0221: } else if ("__methods__".equals(name)) {
0222: return __methods__;
0223: } else if ("__members__".equals(name)) {
0224: return __members__;
0225: } else if ("description".equals(name)) {
0226: return this .fetch.description;
0227: } else if ("rowcount".equals(name)) {
0228: return Py.newInteger(this .fetch.rowcount);
0229: } else if ("rownumber".equals(name)) {
0230: int rn = this .fetch.rownumber;
0231: return (rn < 0) ? Py.None : Py.newInteger(rn);
0232: } else if ("warnings".equals(name)) {
0233: return warnings;
0234: } else if ("lastrowid".equals(name)) {
0235: return lastrowid;
0236: } else if ("updatecount".equals(name)) {
0237: return updatecount;
0238: } else if ("datahandler".equals(name)) {
0239: return Py.java2py(this .datahandler);
0240: } else if ("dynamic".equals(name)) {
0241: return this .dynamicFetch ? Py.One : Py.Zero;
0242: } else if ("connection".equals(name)) {
0243: return this .connection;
0244: } else if ("closed".equals(name)) {
0245: return Py.newBoolean(closed);
0246: } else if ("callproc".equals(name)) {
0247: try {
0248: // dynamically decide on the the attribute based on the driver
0249: if (!getMetaData().supportsStoredProcedures()) {
0250: return null;
0251: }
0252: } catch (Throwable t) {
0253: }
0254: }
0255:
0256: return super .__findattr__(name);
0257: }
0258:
0259: /**
0260: * Initializes the object's namespace.
0261: *
0262: * @param dict
0263: */
0264: static public void classDictInit(PyObject dict) {
0265:
0266: dict.__setitem__("__version__", Py.newString(
0267: "$Revision: 3248 $").__getslice__(Py.newInteger(11),
0268: Py.newInteger(-2), null));
0269: dict.__setitem__("fetchmany", new CursorFunc("fetchmany", 0, 0,
0270: 1, "fetch specified number of rows"));
0271: dict.__setitem__("close", new CursorFunc("close", 1, 0,
0272: "close the cursor"));
0273: dict.__setitem__("fetchall", new CursorFunc("fetchall", 2, 0,
0274: "fetch all results"));
0275: dict.__setitem__("fetchone", new CursorFunc("fetchone", 3, 0,
0276: "fetch the next result"));
0277: dict.__setitem__("nextset", new CursorFunc("nextset", 4, 0,
0278: "return next set or None"));
0279: dict.__setitem__("execute", new CursorFunc("execute", 5, 1, 4,
0280: "execute the sql expression"));
0281: dict.__setitem__("setinputsizes", new CursorFunc(
0282: "setinputsizes", 6, 1, "not implemented"));
0283: dict.__setitem__("setoutputsize", new CursorFunc(
0284: "setoutputsize", 7, 1, 2, "not implemented"));
0285: dict.__setitem__("callproc", new CursorFunc("callproc", 8, 1,
0286: 4, "executes a stored procedure"));
0287: dict.__setitem__("executemany", new CursorFunc("executemany",
0288: 9, 1, 3, "execute sql with the parameter list"));
0289: dict
0290: .__setitem__(
0291: "scroll",
0292: new CursorFunc("scroll", 10, 1, 2,
0293: "scroll the cursor in the result set to a new position according to mode"));
0294: dict.__setitem__("write", new CursorFunc("write", 11, 1,
0295: "execute the sql written to this file-like object"));
0296: dict.__setitem__("prepare", new CursorFunc("prepare", 12, 1,
0297: "prepare the sql statement for later execution"));
0298:
0299: // hide from python
0300: dict.__setitem__("classDictInit", null);
0301: dict.__setitem__("toString", null);
0302: dict.__setitem__("getDataHandler", null);
0303: dict.__setitem__("warning", null);
0304: dict.__setitem__("fetch", null);
0305: dict.__setitem__("statement", null);
0306: dict.__setitem__("dynamicFetch", null);
0307: dict.__setitem__("getPyClass", null);
0308: dict.__setitem__("rsConcur", null);
0309: dict.__setitem__("rsType", null);
0310: }
0311:
0312: /**
0313: * Delete the cursor.
0314: *
0315: */
0316: public void __del__() {
0317: close();
0318: }
0319:
0320: /**
0321: * Close the cursor now (rather than whenever __del__ is called).
0322: * The cursor will be unusable from this point forward; an Error
0323: * (or subclass) exception will be raised if any operation is
0324: * attempted with the cursor.
0325: *
0326: */
0327: public void close() {
0328:
0329: try {
0330: this .clear();
0331: this .connection.remove(this );
0332: } finally {
0333: this .closed = true;
0334: }
0335: }
0336:
0337: /**
0338: * Returns an iteratable object.
0339: *
0340: * @return PyObject
0341: *
0342: * @since Jython 2.2, DB API 2.0+
0343: */
0344: public PyObject __iter__() {
0345: return this ;
0346: }
0347:
0348: /**
0349: * Returns the next row from the currently executing SQL statement
0350: * using the same semantics as .fetchone(). A StopIteration
0351: * exception is raised when the result set is exhausted for Python
0352: * versions 2.2 and later.
0353: *
0354: * @return PyObject
0355: *
0356: * @since Jython 2.2, DB API 2.0+
0357: */
0358: public PyObject next() {
0359: PyObject row = __iternext__();
0360: if (row == null) {
0361: throw Py.StopIteration("");
0362: }
0363: return row;
0364: }
0365:
0366: /**
0367: * Return the next element of the sequence that this is an iterator
0368: * for. Returns null when the end of the sequence is reached.
0369: *
0370: * @since Jython 2.2
0371: *
0372: * @return PyObject
0373: */
0374: public PyObject __iternext__() {
0375: PyObject row = fetchone();
0376: return row.__nonzero__() ? row : null;
0377: }
0378:
0379: /**
0380: * Return ths DatabaseMetaData for the current connection.
0381: *
0382: * @return DatabaseMetaData
0383: *
0384: * @throws SQLException
0385: */
0386: protected DatabaseMetaData getMetaData() throws SQLException {
0387: return this .connection.connection.getMetaData();
0388: }
0389:
0390: /**
0391: * Return the currently bound DataHandler.
0392: *
0393: * @return DataHandler
0394: */
0395: public DataHandler getDataHandler() {
0396: return this .datahandler;
0397: }
0398:
0399: /**
0400: * Prepare a statement ready for executing.
0401: *
0402: * @param sql the sql to execute or a prepared statement
0403: * @param maxRows max number of rows to be returned
0404: * @param prepared if true, prepare the statement, otherwise create a normal statement
0405: *
0406: * @return PyStatement
0407: */
0408: private PyStatement prepareStatement(PyObject sql,
0409: PyObject maxRows, boolean prepared) {
0410:
0411: PyStatement stmt = null;
0412:
0413: if (sql == Py.None) {
0414: return null;
0415: }
0416:
0417: try {
0418: if (sql instanceof PyStatement) {
0419: stmt = (PyStatement) sql;
0420: } else {
0421: Statement sqlStatement = null;
0422: String sqlString = sql.__str__().toString();
0423:
0424: if (sqlString.trim().length() == 0) {
0425: return null;
0426: }
0427:
0428: boolean normal = ((this .rsType == Py.None) && (this .rsConcur == Py.None));
0429:
0430: if (normal) {
0431: if (prepared) {
0432: sqlStatement = this .connection.connection
0433: .prepareStatement(sqlString);
0434: } else {
0435: sqlStatement = this .connection.connection
0436: .createStatement();
0437: }
0438: } else {
0439: int t = ((PyInteger) this .rsType.__int__())
0440: .getValue();
0441: int c = ((PyInteger) this .rsConcur.__int__())
0442: .getValue();
0443:
0444: if (prepared) {
0445: sqlStatement = this .connection.connection
0446: .prepareStatement(sqlString, t, c);
0447: } else {
0448: sqlStatement = this .connection.connection
0449: .createStatement(t, c);
0450: }
0451: }
0452:
0453: int style = prepared ? PyStatement.STATEMENT_PREPARED
0454: : PyStatement.STATEMENT_STATIC;
0455:
0456: stmt = new PyStatement(sqlStatement, sqlString, style);
0457: }
0458:
0459: if (maxRows != Py.None) {
0460: stmt.statement.setMaxRows(((PyInteger) maxRows
0461: .__int__()).getValue());
0462: }
0463: } catch (AbstractMethodError e) {
0464: throw zxJDBC.makeException(zxJDBC.NotSupportedError, zxJDBC
0465: .getString("nodynamiccursors"));
0466: } catch (PyException e) {
0467: throw e;
0468: } catch (Throwable e) {
0469: throw zxJDBC.makeException(e);
0470: }
0471:
0472: return stmt;
0473: }
0474:
0475: /**
0476: * This method is optional since not all databases provide stored procedures.
0477: *
0478: * Call a stored database procedure with the given name. The sequence of parameters
0479: * must contain one entry for each argument that the procedure expects. The result of
0480: * the call is returned as modified copy of the input sequence. Input parameters are
0481: * left untouched, output and input/output parameters replaced with possibly new values.
0482: *
0483: * The procedure may also provide a result set as output. This must then be made available
0484: * through the standard fetchXXX() methods.
0485: *
0486: * @param name
0487: * @param params
0488: * @param bindings
0489: * @param maxRows
0490: */
0491: public void callproc(PyObject name, final PyObject params,
0492: PyObject bindings, PyObject maxRows) {
0493:
0494: this .clear();
0495:
0496: try {
0497: if (getMetaData().supportsStoredProcedures()) {
0498: if (isSeqSeq(params)) {
0499: throw zxJDBC.makeException(
0500: zxJDBC.NotSupportedError,
0501: "sequence of sequences is not supported");
0502: }
0503:
0504: final Procedure procedure = datahandler.getProcedure(
0505: this , name);
0506: Statement stmt = procedure.prepareCall(this .rsType,
0507: this .rsConcur);
0508:
0509: if (maxRows != Py.None) {
0510: stmt.setMaxRows(((PyInteger) maxRows.__int__())
0511: .getValue());
0512: }
0513:
0514: // get the bindings per the stored proc spec
0515: PyDictionary callableBindings = new PyDictionary();
0516:
0517: procedure.normalizeInput(params, callableBindings);
0518:
0519: // overwrite with any user specific bindings
0520: if (bindings instanceof PyDictionary) {
0521: callableBindings.update((PyDictionary) bindings);
0522: }
0523:
0524: this .statement = new PyStatement(stmt, procedure);
0525:
0526: this .execute(params, callableBindings);
0527: } else {
0528: throw zxJDBC.makeException(zxJDBC.NotSupportedError,
0529: zxJDBC.getString("noStoredProc"));
0530: }
0531: } catch (PyException e) {
0532: throw e;
0533: } catch (Throwable e) {
0534: throw zxJDBC.makeException(e);
0535: } finally {
0536: if (this .statement != null) {
0537:
0538: // close what we opened
0539: this .statement.close();
0540: }
0541: }
0542: }
0543:
0544: /**
0545: * Prepare a database operation (query or command) and then execute it against all
0546: * parameter sequences or mappings found in the sequence seq_of_parameters.
0547: * Modules are free to implement this method using multiple calls to the execute()
0548: * method or by using array operations to have the database process the sequence as
0549: * a whole in one call.
0550: *
0551: * The same comments as for execute() also apply accordingly to this method.
0552: *
0553: * Return values are not defined.
0554: *
0555: * @param sql
0556: * @param params
0557: * @param bindings
0558: * @param maxRows
0559: */
0560: public void executemany(PyObject sql, PyObject params,
0561: PyObject bindings, PyObject maxRows) {
0562: execute(sql, params, bindings, maxRows);
0563: }
0564:
0565: /**
0566: * Prepare and execute a database operation (query or command).
0567: * Parameters may be provided as sequence or mapping and will
0568: * be bound to variables in the operation. Variables are specified
0569: * in a database-specific notation (see the module's paramstyle
0570: * attribute for details).
0571: *
0572: * A reference to the operation will be retained by the cursor.
0573: * If the same operation object is passed in again, then the cursor
0574: * can optimize its behavior. This is most effective for algorithms
0575: * where the same operation is used, but different parameters are
0576: * bound to it (many times).
0577: *
0578: * For maximum efficiency when reusing an operation, it is best to
0579: * use the setinputsizes() method to specify the parameter types and
0580: * sizes ahead of time. It is legal for a parameter to not match the
0581: * predefined information; the implementation should compensate, possibly
0582: * with a loss of efficiency.
0583: *
0584: * The parameters may also be specified as list of tuples to e.g. insert
0585: * multiple rows in a single operation, but this kind of usage is
0586: * deprecated: executemany() should be used instead.
0587: *
0588: * Return values are not defined.
0589: *
0590: * @param sql sql string or prepared statement
0591: * @param params params for a prepared statement
0592: * @param bindings dictionary of (param index : SQLType binding)
0593: * @param maxRows integer value of max rows
0594: */
0595: public void execute(final PyObject sql, PyObject params,
0596: PyObject bindings, PyObject maxRows) {
0597:
0598: this .clear();
0599:
0600: boolean hasParams = hasParams(params);
0601: PyStatement stmt = this .prepareStatement(sql, maxRows,
0602: hasParams);
0603:
0604: if (stmt == null) {
0605: return;
0606: }
0607:
0608: this .statement = stmt;
0609:
0610: try {
0611: synchronized (this .statement) {
0612: if (hasParams) {
0613:
0614: // if we have a sequence of sequences, let's run through them and finish
0615: if (isSeqSeq(params)) {
0616:
0617: // [(3, 4)] or [(3, 4), (5, 6)]
0618: for (int i = 0, len = params.__len__(); i < len; i++) {
0619: PyObject param = params.__getitem__(i);
0620:
0621: this .execute(param, bindings);
0622: }
0623: } else {
0624: this .execute(params, bindings);
0625: }
0626: } else {
0627:
0628: // execute the sql string straight up
0629: this .execute(Py.None, Py.None);
0630: }
0631: }
0632: } catch (PyException e) {
0633: throw e;
0634: } catch (Throwable e) {
0635: throw zxJDBC.makeException(e);
0636: } finally {
0637: if (this .statement != null) {
0638:
0639: // only close static, single-use statements
0640: if (!(sql instanceof PyStatement)
0641: && (!this .dynamicFetch)) {
0642: this .statement.close();
0643: }
0644: }
0645: }
0646: }
0647:
0648: /**
0649: * Execute the current sql statement. Some generic functionality such
0650: * as updating the lastrowid and updatecount occur as well.
0651: */
0652: protected void execute(PyObject params, PyObject bindings) {
0653:
0654: try {
0655: Statement stmt = this .statement.statement;
0656:
0657: this .datahandler.preExecute(stmt);
0658:
0659: // this performs the SQL execution and fetch per the Statement type
0660: this .statement.execute(this , params, bindings);
0661:
0662: this .lastrowid = this .datahandler.getRowId(stmt);
0663:
0664: int uc = stmt.getUpdateCount();
0665:
0666: this .updatecount = (uc < 0) ? Py.None : Py.newInteger(uc);
0667:
0668: warning(new WarningEvent(this , stmt.getWarnings()));
0669: this .datahandler.postExecute(stmt);
0670: } catch (PyException e) {
0671: throw e;
0672: } catch (Throwable e) {
0673: throw zxJDBC.makeException(e);
0674: }
0675: }
0676:
0677: /**
0678: * Fetch the next row of a query result set, returning a single sequence,
0679: * or None when no more data is available.
0680: *
0681: * An Error (or subclass) exception is raised if the previous call to
0682: * executeXXX() did not produce any result set or no call was issued yet.
0683: *
0684: * @return a single sequence from the result set, or None when no more data is available
0685: */
0686: public PyObject fetchone() {
0687: return this .fetch.fetchone();
0688: }
0689:
0690: /**
0691: * Fetch all (remaining) rows of a query result, returning them as a sequence
0692: * of sequences (e.g. a list of tuples). Note that the cursor's arraysize attribute
0693: * can affect the performance of this operation.
0694: *
0695: * An Error (or subclass) exception is raised if the previous call to executeXXX()
0696: * did not produce any result set or no call was issued yet.
0697: *
0698: * @return a sequence of sequences from the result set, or an empty sequence when
0699: * no more data is available
0700: */
0701: public PyObject fetchall() {
0702: return this .fetch.fetchall();
0703: }
0704:
0705: /**
0706: * Fetch the next set of rows of a query result, returning a sequence of
0707: * sequences (e.g. a list of tuples). An empty sequence is returned when
0708: * no more rows are available.
0709: *
0710: * The number of rows to fetch per call is specified by the parameter. If
0711: * it is not given, the cursor's arraysize determines the number of rows
0712: * to be fetched. The method should try to fetch as many rows as indicated
0713: * by the size parameter. If this is not possible due to the specified number
0714: * of rows not being available, fewer rows may be returned.
0715: *
0716: * An Error (or subclass) exception is raised if the previous call to executeXXX()
0717: * did not produce any result set or no call was issued yet.
0718: *
0719: * Note there are performance considerations involved with the size parameter.
0720: * For optimal performance, it is usually best to use the arraysize attribute.
0721: * If the size parameter is used, then it is best for it to retain the same value
0722: * from one fetchmany() call to the next.
0723: *
0724: * @param size
0725: * @return a sequence of sequences from the result set, or an empty sequence when
0726: * no more data is available
0727: */
0728: public PyObject fetchmany(int size) {
0729: return this .fetch.fetchmany(size);
0730: }
0731:
0732: /**
0733: * Move the result pointer to the next set if available.
0734: *
0735: * @return true if more sets exist, else None
0736: */
0737: public PyObject nextset() {
0738: return this .fetch.nextset();
0739: }
0740:
0741: /**
0742: * Prepare a sql statement for later execution.
0743: *
0744: * @param sql The sql string to be prepared.
0745: *
0746: * @return A prepared statement usable with .executeXXX()
0747: */
0748: public PyStatement prepare(PyObject sql) {
0749:
0750: PyStatement s = this .prepareStatement(sql, Py.None, true);
0751:
0752: // add to the set of statements which are leaving our control
0753: this .connection.add(s);
0754:
0755: return s;
0756: }
0757:
0758: /**
0759: * Scroll the cursor in the result set to a new position according
0760: * to mode.
0761: *
0762: * If mode is 'relative' (default), value is taken as offset to
0763: * the current position in the result set, if set to 'absolute',
0764: * value states an absolute target position.
0765: *
0766: * An IndexError should be raised in case a scroll operation would
0767: * leave the result set. In this case, the cursor position is left
0768: * undefined (ideal would be to not move the cursor at all).
0769: *
0770: * Note: This method should use native scrollable cursors, if
0771: * available, or revert to an emulation for forward-only
0772: * scrollable cursors. The method may raise NotSupportedErrors to
0773: * signal that a specific operation is not supported by the
0774: * database (e.g. backward scrolling).
0775: *
0776: *
0777: * @param value
0778: * @param mode
0779: *
0780: */
0781: public void scroll(int value, String mode) {
0782: this .fetch.scroll(value, mode);
0783: }
0784:
0785: /**
0786: * Adds a warning to the tuple and will follow the chain as necessary.
0787: *
0788: * @param event
0789: */
0790: public void warning(WarningEvent event) {
0791:
0792: if (this .warnings == Py.None) {
0793: this .warnings = new PyList();
0794: }
0795:
0796: SQLWarning warning = event.getWarning();
0797: while (warning != null) {
0798:
0799: PyObject[] warn = new PyObject[] {
0800: // there are three parts: (reason, state, vendorCode)
0801: Py.java2py(warning.getMessage()),
0802: Py.java2py(warning.getSQLState()),
0803: Py.newInteger(warning.getErrorCode()) };
0804:
0805: // add the warning to the list
0806: ((PyList) this .warnings).append(new PyTuple(warn));
0807:
0808: warning = warning.getNextWarning();
0809: }
0810: }
0811:
0812: /**
0813: * Resets the cursor state. This includes flushing the warnings
0814: * and any previous results.
0815: *
0816: */
0817: protected void clear() {
0818:
0819: if (closed) {
0820: throw zxJDBC.makeException(zxJDBC.ProgrammingError,
0821: "cursor is closed");
0822: }
0823:
0824: this .warnings = Py.None;
0825: this .lastrowid = Py.None;
0826: this .updatecount = Py.newInteger(-1);
0827:
0828: try {
0829: this .fetch.close();
0830: } catch (Throwable e) {
0831: } finally {
0832: this .fetch = Fetch.newFetch(this .datahandler,
0833: this .dynamicFetch);
0834:
0835: this .fetch.addWarningListener(this );
0836: }
0837:
0838: if (this .statement != null) {
0839:
0840: // we can't close a dynamic fetch statement until everything has been
0841: // consumed so the only time we can clean up is now
0842: // but if this is a previously prepared statement we don't want to close
0843: // it underneath someone; we can check this by looking in the set
0844: try {
0845: if (this .dynamicFetch
0846: && (!this .connection.contains(this .statement))) {
0847: this .statement.close();
0848: }
0849: } finally {
0850: this .statement = null;
0851: }
0852: }
0853: }
0854:
0855: /**
0856: * Method isSeq
0857: *
0858: * @param object
0859: *
0860: * @return true for any PyList, PyTuple or java.util.List
0861: *
0862: */
0863: public static boolean isSeq(PyObject object) {
0864:
0865: if ((object == null) || (object == Py.None)) {
0866: return false;
0867: }
0868:
0869: if (object.__tojava__(List.class) != Py.NoConversion) {
0870: return true;
0871: }
0872:
0873: // originally checked for __getitem__ and __len__, but this is true for PyString
0874: // and we don't want to insert one character at a time
0875: return (object instanceof PyList)
0876: || (object instanceof PyTuple);
0877: }
0878:
0879: /**
0880: * Method hasParams
0881: *
0882: * @param params
0883: *
0884: * @return boolean
0885: *
0886: */
0887: public static boolean hasParams(PyObject params) {
0888: if (Py.None == params) {
0889: return false;
0890: }
0891:
0892: boolean isSeq = isSeq(params);
0893: // the optional argument better be a sequence
0894: if (!isSeq) {
0895: throw zxJDBC.makeException(zxJDBC.ProgrammingError, zxJDBC
0896: .getString("optionalSecond"));
0897: }
0898: return params.__len__() > 0;
0899: }
0900:
0901: /**
0902: * Method isSeqSeq
0903: *
0904: * @param object
0905: *
0906: * @return true is a sequence of sequences
0907: *
0908: */
0909: public static boolean isSeqSeq(PyObject object) {
0910:
0911: if (isSeq(object) && (object.__len__() > 0)) {
0912: for (int i = 0; i < object.__len__(); i++) {
0913: if (!isSeq(object.__finditem__(i))) {
0914: return false;
0915: }
0916: }
0917: return true;
0918: }
0919: return false;
0920: }
0921: }
0922:
0923: class CursorFunc extends PyBuiltinMethodSet {
0924: CursorFunc(String name, int index, int argcount, String doc) {
0925: this (name, index, argcount, argcount, doc);
0926: }
0927:
0928: CursorFunc(String name, int index, int minargs, int maxargs,
0929: String doc) {
0930: super (name, index, minargs, maxargs, doc, PyCursor.class);
0931: }
0932:
0933: public PyObject __call__() {
0934: PyCursor cursor = (PyCursor) __self__;
0935: switch (index) {
0936: case 0:
0937: return cursor.fetchmany(cursor.arraysize);
0938: case 1:
0939: cursor.close();
0940: return Py.None;
0941: case 2:
0942: return cursor.fetchall();
0943: case 3:
0944: return cursor.fetchone();
0945: case 4:
0946: return cursor.nextset();
0947: default:
0948: throw info.unexpectedCall(0, false);
0949: }
0950: }
0951:
0952: public PyObject __call__(PyObject arg) {
0953: PyCursor cursor = (PyCursor) __self__;
0954: switch (index) {
0955: case 0:
0956: return cursor.fetchmany(((PyInteger) arg.__int__())
0957: .getValue());
0958: case 5:
0959: cursor.execute(arg, Py.None, Py.None, Py.None);
0960: return Py.None;
0961: case 6:
0962: case 7:
0963: return Py.None;
0964: case 8:
0965: cursor.callproc(arg, Py.None, Py.None, Py.None);
0966: return Py.None;
0967: case 9:
0968: cursor.executemany(arg, Py.None, Py.None, Py.None);
0969: return Py.None;
0970: case 10:
0971: cursor.scroll(((PyInteger) arg.__int__()).getValue(),
0972: "relative");
0973: return Py.None;
0974: case 11:
0975: cursor.execute(arg, Py.None, Py.None, Py.None);
0976: return Py.None;
0977: case 12:
0978: return cursor.prepare(arg);
0979: default:
0980: throw info.unexpectedCall(1, false);
0981: }
0982: }
0983:
0984: public PyObject __call__(PyObject arga, PyObject argb) {
0985: PyCursor cursor = (PyCursor) __self__;
0986: switch (index) {
0987: case 5:
0988: cursor.execute(arga, argb, Py.None, Py.None);
0989: return Py.None;
0990: case 7:
0991: return Py.None;
0992: case 8:
0993: cursor.callproc(arga, argb, Py.None, Py.None);
0994: return Py.None;
0995: case 9:
0996: cursor.executemany(arga, argb, Py.None, Py.None);
0997: return Py.None;
0998: case 10:
0999: cursor.scroll(((PyInteger) arga.__int__()).getValue(), argb
1000: .toString());
1001: return Py.None;
1002: default:
1003: throw info.unexpectedCall(2, false);
1004: }
1005: }
1006:
1007: public PyObject __call__(PyObject arga, PyObject argb, PyObject argc) {
1008: PyCursor cursor = (PyCursor) __self__;
1009: switch (index) {
1010: case 5:
1011: cursor.execute(arga, argb, argc, Py.None);
1012: return Py.None;
1013: case 8:
1014: cursor.callproc(arga, argb, argc, Py.None);
1015: return Py.None;
1016: case 9:
1017: cursor.executemany(arga, argb, argc, Py.None);
1018: return Py.None;
1019: default:
1020: throw info.unexpectedCall(3, false);
1021: }
1022: }
1023:
1024: public PyObject __call__(PyObject[] args, String[] keywords) {
1025:
1026: PyCursor cursor = (PyCursor) __self__;
1027: PyArgParser parser = new PyArgParser(args, keywords);
1028: PyObject sql = parser.arg(0);
1029: PyObject params = parser.kw("params", Py.None);
1030: PyObject bindings = parser.kw("bindings", Py.None);
1031: PyObject maxrows = parser.kw("maxrows", Py.None);
1032:
1033: params = (parser.numArg() >= 2) ? parser.arg(1) : params;
1034: bindings = (parser.numArg() >= 3) ? parser.arg(2) : bindings;
1035: maxrows = (parser.numArg() >= 4) ? parser.arg(3) : maxrows;
1036:
1037: switch (index) {
1038: case 5:
1039: cursor.execute(sql, params, bindings, maxrows);
1040: return Py.None;
1041: case 8:
1042: cursor.callproc(sql, params, bindings, maxrows);
1043: return Py.None;
1044: case 9:
1045: cursor.executemany(sql, params, bindings, maxrows);
1046: return Py.None;
1047: default:
1048: throw info.unexpectedCall(args.length, true);
1049: }
1050: }
1051: }
|