001: /*
002: * Jython Database Specification API 2.0
003: *
004: * $Id: PyConnection.java 3248 2007-05-30 05:19:19Z cgroves $
005: *
006: * Copyright (c) 2001 brian zimmer <bzimmer@ziclix.com>
007: *
008: */
009: package com.ziclix.python.sql;
010:
011: import java.sql.Connection;
012: import java.sql.SQLException;
013: import java.util.Collections;
014: import java.util.HashSet;
015: import java.util.Iterator;
016: import java.util.Set;
017: import org.python.core.ClassDictInit;
018: import org.python.core.Py;
019: import org.python.core.PyBuiltinMethodSet;
020: import org.python.core.PyClass;
021: import org.python.core.PyInteger;
022: import org.python.core.PyList;
023: import org.python.core.PyObject;
024: import org.python.core.PyString;
025: import com.ziclix.python.sql.util.PyArgParser;
026:
027: /**
028: * A connection to the database.
029: *
030: * @author brian zimmer
031: * @author last revised by $Author: cgroves $
032: * @version $Revision: 3248 $
033: */
034: public class PyConnection extends PyObject implements ClassDictInit {
035:
036: /**
037: * Field closed
038: */
039: protected boolean closed;
040:
041: /**
042: * Field connection
043: */
044: protected Connection connection;
045:
046: /**
047: * Field supportsTransactions
048: */
049: protected boolean supportsTransactions;
050:
051: /**
052: * Field cursors
053: */
054: private Set cursors;
055:
056: /**
057: * Field statements
058: */
059: private Set statements;
060:
061: /**
062: * Field __class__
063: */
064: public static PyClass __class__;
065:
066: /**
067: * Method getPyClass
068: *
069: * @return PyClass
070: */
071: protected PyClass getPyClass() {
072: return __class__;
073: }
074:
075: /**
076: * Field __members__
077: */
078: protected static PyList __members__;
079:
080: /**
081: * Field __methods__
082: */
083: protected static PyList __methods__;
084:
085: static {
086: PyObject[] m = new PyObject[5];
087:
088: m[0] = new PyString("close");
089: m[1] = new PyString("commit");
090: m[2] = new PyString("cursor");
091: m[3] = new PyString("rollback");
092: m[4] = new PyString("nativesql");
093: __methods__ = new PyList(m);
094: m = new PyObject[10];
095: m[0] = new PyString("autocommit");
096: m[1] = new PyString("dbname");
097: m[2] = new PyString("dbversion");
098: m[3] = new PyString("drivername");
099: m[4] = new PyString("driverversion");
100: m[5] = new PyString("url");
101: m[6] = new PyString("__connection__");
102: m[7] = new PyString("__cursors__");
103: m[8] = new PyString("__statements__");
104: m[9] = new PyString("closed");
105: __members__ = new PyList(m);
106: }
107:
108: /**
109: * Create a PyConnection with the open connection.
110: *
111: * @param connection
112: * @throws SQLException
113: */
114: public PyConnection(Connection connection) throws SQLException {
115:
116: this .closed = false;
117: this .cursors = new HashSet();
118: this .connection = connection;
119: this .statements = new HashSet();
120: this .supportsTransactions = this .connection.getMetaData()
121: .supportsTransactions();
122:
123: if (this .supportsTransactions) {
124: this .connection.setAutoCommit(false);
125: }
126: }
127:
128: /**
129: * Produces a string representation of the object.
130: *
131: * @return string representation of the object.
132: */
133: public String toString() {
134:
135: try {
136: return "<PyConnection user='"
137: + this .connection.getMetaData().getUserName()
138: + "', url='"
139: + this .connection.getMetaData().getURL() + "'>";
140: } catch (SQLException e) {
141: return "<PyConnection at " + hashCode() + ">";
142: }
143: }
144:
145: /**
146: * Method classDictInit
147: *
148: * @param dict
149: */
150: static public void classDictInit(PyObject dict) {
151:
152: dict.__setitem__("autocommit", new PyInteger(0));
153: dict.__setitem__("__version__", Py.newString(
154: "$Revision: 3248 $").__getslice__(Py.newInteger(11),
155: Py.newInteger(-2), null));
156: dict.__setitem__("close", new ConnectionFunc("close", 0, 0, 0,
157: zxJDBC.getString("close")));
158: dict.__setitem__("commit", new ConnectionFunc("commit", 1, 0,
159: 0, zxJDBC.getString("commit")));
160: dict.__setitem__("cursor", new ConnectionFunc("cursor", 2, 0,
161: 4, zxJDBC.getString("cursor")));
162: dict.__setitem__("rollback", new ConnectionFunc("rollback", 3,
163: 0, 0, zxJDBC.getString("rollback")));
164: dict.__setitem__("nativesql", new ConnectionFunc("nativesql",
165: 4, 1, 1, zxJDBC.getString("nativesql")));
166:
167: // hide from python
168: dict.__setitem__("initModule", null);
169: dict.__setitem__("toString", null);
170: dict.__setitem__("setConnection", null);
171: dict.__setitem__("getPyClass", null);
172: dict.__setitem__("connection", null);
173: dict.__setitem__("classDictInit", null);
174: dict.__setitem__("cursors", null);
175: }
176:
177: /**
178: * Sets the attribute.
179: *
180: * @param name
181: * @param value
182: */
183: public void __setattr__(String name, PyObject value) {
184:
185: if ("autocommit".equals(name)) {
186: try {
187: if (this .supportsTransactions) {
188: this .connection.setAutoCommit(value.__nonzero__());
189: }
190: } catch (SQLException e) {
191: throw zxJDBC.makeException(zxJDBC.DatabaseError, e);
192: }
193:
194: return;
195: }
196:
197: super .__setattr__(name, value);
198: }
199:
200: /**
201: * Finds the attribute.
202: *
203: * @param name the name of the attribute of interest
204: * @return the value for the attribute of the specified name
205: */
206: public PyObject __findattr__(String name) {
207:
208: if ("autocommit".equals(name)) {
209: try {
210: return connection.getAutoCommit() ? Py.One : Py.Zero;
211: } catch (SQLException e) {
212: throw zxJDBC.makeException(zxJDBC.DatabaseError, e);
213: }
214: } else if ("dbname".equals(name)) {
215: try {
216: return Py.newString(this .connection.getMetaData()
217: .getDatabaseProductName());
218: } catch (SQLException e) {
219: throw zxJDBC.makeException(zxJDBC.DatabaseError, e);
220: }
221: } else if ("dbversion".equals(name)) {
222: try {
223: return Py.newString(this .connection.getMetaData()
224: .getDatabaseProductVersion());
225: } catch (SQLException e) {
226: throw zxJDBC.makeException(zxJDBC.DatabaseError, e);
227: }
228: } else if ("drivername".equals(name)) {
229: try {
230: return Py.newString(this .connection.getMetaData()
231: .getDriverName());
232: } catch (SQLException e) {
233: throw zxJDBC.makeException(zxJDBC.DatabaseError, e);
234: }
235: } else if ("driverversion".equals(name)) {
236: try {
237: return Py.newString(this .connection.getMetaData()
238: .getDriverVersion());
239: } catch (SQLException e) {
240: throw zxJDBC.makeException(zxJDBC.DatabaseError, e);
241: }
242: } else if ("url".equals(name)) {
243: try {
244: return Py.newString(this .connection.getMetaData()
245: .getURL());
246: } catch (SQLException e) {
247: throw zxJDBC.makeException(zxJDBC.DatabaseError, e);
248: }
249: } else if ("__connection__".equals(name)) {
250: return Py.java2py(this .connection);
251: } else if ("__cursors__".equals(name)) {
252: return Py
253: .java2py(Collections.unmodifiableSet(this .cursors));
254: } else if ("__statements__".equals(name)) {
255: return Py.java2py(Collections
256: .unmodifiableSet(this .statements));
257: } else if ("__methods__".equals(name)) {
258: return __methods__;
259: } else if ("__members__".equals(name)) {
260: return __members__;
261: } else if ("closed".equals(name)) {
262: return Py.newBoolean(closed);
263: }
264:
265: return super .__findattr__(name);
266: }
267:
268: /**
269: * Close the connection now (rather than whenever __del__ is called).
270: * The connection will be unusable from this point forward; an Error
271: * (or subclass) exception will be raised if any operation is attempted
272: * with the connection. The same applies to all cursor objects trying
273: * to use the connection.
274: */
275: public void close() {
276:
277: if (closed) {
278: throw zxJDBC.makeException(zxJDBC.ProgrammingError,
279: "connection is closed");
280: }
281:
282: // mark ourselves closed now so that any callbacks we
283: // get from closing down cursors and statements to not
284: // try and modify our internal sets
285: this .closed = true;
286:
287: synchronized (this .cursors) {
288:
289: // close the cursors
290: for (Iterator i = this .cursors.iterator(); i.hasNext();) {
291: ((PyCursor) i.next()).close();
292: }
293:
294: this .cursors.clear();
295: }
296:
297: synchronized (this .statements) {
298:
299: // close the cursors
300: for (Iterator i = this .statements.iterator(); i.hasNext();) {
301: ((PyStatement) i.next()).close();
302: }
303:
304: this .statements.clear();
305: }
306:
307: try {
308: this .connection.close();
309: } catch (SQLException e) {
310: throw zxJDBC.makeException(e);
311: }
312: }
313:
314: /**
315: * Commit any pending transaction to the database. Note that if the
316: * database supports an auto-commit feature, this must be initially
317: * off. An interface method may be provided to turn it back on.
318: * <p/>
319: * Database modules that do not support transactions should implement
320: * this method with void functionality.
321: */
322: public void commit() {
323:
324: if (closed) {
325: throw zxJDBC.makeException(zxJDBC.ProgrammingError,
326: "connection is closed");
327: }
328:
329: if (!this .supportsTransactions) {
330: return;
331: }
332:
333: try {
334: this .connection.commit();
335: } catch (SQLException e) {
336: throw zxJDBC.makeException(e);
337: }
338: }
339:
340: /**
341: * <i>This method is optional since not all databases provide transaction
342: * support.</i>
343: * <p/>
344: * In case a database does provide transactions this method causes the database
345: * to roll back to the start of any pending transaction. Closing a connection
346: * without committing the changes first will cause an implicit rollback to be
347: * performed.
348: */
349: public void rollback() {
350:
351: if (closed) {
352: throw zxJDBC.makeException(zxJDBC.ProgrammingError,
353: "connection is closed");
354: }
355:
356: if (!this .supportsTransactions) {
357: return;
358: }
359:
360: try {
361: this .connection.rollback();
362: } catch (SQLException e) {
363: throw zxJDBC.makeException(e);
364: }
365: }
366:
367: /**
368: * Converts the given SQL statement into the system's native SQL grammar. A
369: * driver may convert the JDBC sql grammar into its system's native SQL grammar
370: * prior to sending it; this method returns the native form of the statement
371: * that the driver would have sent.
372: *
373: * @param nativeSQL
374: * @return the native form of this statement
375: */
376: public PyObject nativesql(PyObject nativeSQL) {
377:
378: if (closed) {
379: throw zxJDBC.makeException(zxJDBC.ProgrammingError,
380: "connection is closed");
381: }
382:
383: if (nativeSQL == Py.None) {
384: return Py.None;
385: }
386:
387: try {
388: return Py.newString(this .connection.nativeSQL(nativeSQL
389: .__str__().toString()));
390: } catch (SQLException e) {
391: throw zxJDBC.makeException(e);
392: }
393: }
394:
395: /**
396: * Return a new Cursor Object using the connection. If the database does not
397: * provide a direct cursor concept, the module will have to emulate cursors
398: * using other means to the extent needed by this specification.
399: *
400: * @return a new cursor using this connection
401: */
402: public PyCursor cursor() {
403: return cursor(false);
404: }
405:
406: /**
407: * Return a new Cursor Object using the connection. If the database does not
408: * provide a direct cursor concept, the module will have to emulate cursors
409: * using other means to the extent needed by this specification.
410: *
411: * @param dynamicFetch if true, dynamically iterate the result
412: * @return a new cursor using this connection
413: */
414: public PyCursor cursor(boolean dynamicFetch) {
415: return this .cursor(dynamicFetch, Py.None, Py.None);
416: }
417:
418: /**
419: * Return a new Cursor Object using the connection. If the database does not
420: * provide a direct cursor concept, the module will have to emulate cursors
421: * using other means to the extent needed by this specification.
422: *
423: * @param dynamicFetch if true, dynamically iterate the result
424: * @param rsType the type of the underlying ResultSet
425: * @param rsConcur the concurrency of the underlying ResultSet
426: * @return a new cursor using this connection
427: */
428: public PyCursor cursor(boolean dynamicFetch, PyObject rsType,
429: PyObject rsConcur) {
430:
431: if (closed) {
432: throw zxJDBC.makeException(zxJDBC.ProgrammingError,
433: "connection is closed");
434: }
435:
436: PyCursor cursor = new PyExtendedCursor(this , dynamicFetch,
437: rsType, rsConcur);
438:
439: this .cursors.add(cursor);
440:
441: return cursor;
442: }
443:
444: /**
445: * Remove an open PyCursor.
446: *
447: * @param cursor
448: */
449: void remove(PyCursor cursor) {
450:
451: if (closed) {
452: return;
453: }
454:
455: this .cursors.remove(cursor);
456: }
457:
458: /**
459: * Method register
460: *
461: * @param statement statement
462: */
463: void add(PyStatement statement) {
464:
465: if (closed) {
466: return;
467: }
468:
469: this .statements.add(statement);
470: }
471:
472: /**
473: * Method contains
474: *
475: * @param statement statement
476: * @return boolean
477: */
478: boolean contains(PyStatement statement) {
479:
480: if (closed) {
481: return false;
482: }
483:
484: return this .statements.contains(statement);
485: }
486: }
487:
488: class ConnectionFunc extends PyBuiltinMethodSet {
489: ConnectionFunc(String name, int index, int minargs, int maxargs,
490: String doc) {
491: super (name, index, minargs, maxargs, doc, PyConnection.class);
492: }
493:
494: public PyObject __call__() {
495: PyConnection c = (PyConnection) __self__;
496: switch (index) {
497: case 0:
498: c.close();
499: return Py.None;
500: case 1:
501: c.commit();
502: return Py.None;
503: case 2:
504: return c.cursor();
505: case 3:
506: c.rollback();
507: return Py.None;
508: default:
509: throw info.unexpectedCall(0, false);
510: }
511: }
512:
513: public PyObject __call__(PyObject arg) {
514: PyConnection c = (PyConnection) __self__;
515: switch (index) {
516: case 2:
517: return c.cursor(arg.__nonzero__());
518: case 4:
519: return c.nativesql(arg);
520: default:
521: throw info.unexpectedCall(1, false);
522: }
523: }
524:
525: public PyObject __call__(PyObject arg1, PyObject arg2, PyObject arg3) {
526: PyConnection c = (PyConnection) __self__;
527: switch (index) {
528: case 2:
529: return c.cursor(arg1.__nonzero__(), arg2, arg3);
530: default:
531: throw info.unexpectedCall(3, false);
532: }
533: }
534:
535: public PyObject __call__(PyObject[] args, String[] keywords) {
536: PyConnection c = (PyConnection) __self__;
537: PyArgParser parser = new PyArgParser(args, keywords);
538: switch (index) {
539: case 2:
540: PyObject dynamic = parser.kw("dynamic", Py.None);
541: PyObject rstype = parser.kw("rstype", Py.None);
542: PyObject rsconcur = parser.kw("rsconcur", Py.None);
543:
544: dynamic = (parser.numArg() >= 1) ? parser.arg(0) : dynamic;
545: rstype = (parser.numArg() >= 2) ? parser.arg(1) : rstype;
546: rsconcur = (parser.numArg() >= 3) ? parser.arg(2)
547: : rsconcur;
548:
549: return c.cursor(dynamic.__nonzero__(), rstype, rsconcur);
550:
551: default:
552: throw info.unexpectedCall(args.length, true);
553: }
554: }
555: }
|