PageRenderTime 56ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/src/com/ziclix/python/sql/PyConnection.java

http://github.com/nriley/jython
Java | 513 lines | 297 code | 72 blank | 144 comment | 44 complexity | 11783891932af7bf4e1b12768648f2b1 MD5 | raw file
  1. /*
  2. * Jython Database Specification API 2.0
  3. *
  4. * $Id$
  5. *
  6. * Copyright (c) 2001 brian zimmer <bzimmer@ziclix.com>
  7. *
  8. */
  9. package com.ziclix.python.sql;
  10. import java.sql.Connection;
  11. import java.sql.SQLException;
  12. import java.util.Collections;
  13. import java.util.HashSet;
  14. import java.util.Iterator;
  15. import java.util.Set;
  16. import org.python.core.ClassDictInit;
  17. import org.python.core.Py;
  18. import org.python.core.PyBuiltinMethodSet;
  19. import org.python.core.PyClass;
  20. import org.python.core.PyInteger;
  21. import org.python.core.PyList;
  22. import org.python.core.PyObject;
  23. import org.python.core.PyString;
  24. import com.ziclix.python.sql.util.PyArgParser;
  25. /**
  26. * A connection to the database.
  27. *
  28. * @author brian zimmer
  29. * @author last revised by $Author$
  30. * @version $Revision$
  31. */
  32. public class PyConnection extends PyObject implements ClassDictInit {
  33. /**
  34. * Field closed
  35. */
  36. protected boolean closed;
  37. /**
  38. * Field connection
  39. */
  40. protected Connection connection;
  41. /**
  42. * Field supportsTransactions
  43. */
  44. protected boolean supportsTransactions;
  45. /**
  46. * Field cursors
  47. */
  48. private Set cursors;
  49. /**
  50. * Field statements
  51. */
  52. private Set statements;
  53. /**
  54. * Field __members__
  55. */
  56. protected static PyList __members__;
  57. /**
  58. * Field __methods__
  59. */
  60. protected static PyList __methods__;
  61. static {
  62. PyObject[] m = new PyObject[5];
  63. m[0] = new PyString("close");
  64. m[1] = new PyString("commit");
  65. m[2] = new PyString("cursor");
  66. m[3] = new PyString("rollback");
  67. m[4] = new PyString("nativesql");
  68. __methods__ = new PyList(m);
  69. m = new PyObject[10];
  70. m[0] = new PyString("autocommit");
  71. m[1] = new PyString("dbname");
  72. m[2] = new PyString("dbversion");
  73. m[3] = new PyString("drivername");
  74. m[4] = new PyString("driverversion");
  75. m[5] = new PyString("url");
  76. m[6] = new PyString("__connection__");
  77. m[7] = new PyString("__cursors__");
  78. m[8] = new PyString("__statements__");
  79. m[9] = new PyString("closed");
  80. __members__ = new PyList(m);
  81. }
  82. /**
  83. * Create a PyConnection with the open connection.
  84. *
  85. * @param connection
  86. * @throws SQLException
  87. */
  88. public PyConnection(Connection connection) throws SQLException {
  89. this.closed = false;
  90. this.cursors = new HashSet();
  91. this.connection = connection;
  92. this.statements = new HashSet();
  93. this.supportsTransactions = this.connection.getMetaData().supportsTransactions();
  94. if (this.supportsTransactions) {
  95. this.connection.setAutoCommit(false);
  96. }
  97. }
  98. /**
  99. * Produces a string representation of the object.
  100. *
  101. * @return string representation of the object.
  102. */
  103. public String toString() {
  104. try {
  105. return "<PyConnection user='" + this.connection.getMetaData().getUserName() + "', url='" + this.connection.getMetaData().getURL() + "'>";
  106. } catch (SQLException e) {
  107. return "<PyConnection at " + hashCode() + ">";
  108. }
  109. }
  110. /**
  111. * Method classDictInit
  112. *
  113. * @param dict
  114. */
  115. static public void classDictInit(PyObject dict) {
  116. dict.__setitem__("autocommit", new PyInteger(0));
  117. dict.__setitem__("__version__", Py.newString("$Revision$").__getslice__(Py.newInteger(11), Py.newInteger(-2), null));
  118. dict.__setitem__("close", new ConnectionFunc("close", 0, 0, 0, zxJDBC.getString("close")));
  119. dict.__setitem__("commit", new ConnectionFunc("commit", 1, 0, 0, zxJDBC.getString("commit")));
  120. dict.__setitem__("cursor", new ConnectionFunc("cursor", 2, 0, 4, zxJDBC.getString("cursor")));
  121. dict.__setitem__("rollback", new ConnectionFunc("rollback", 3, 0, 0, zxJDBC.getString("rollback")));
  122. dict.__setitem__("nativesql", new ConnectionFunc("nativesql", 4, 1, 1, zxJDBC.getString("nativesql")));
  123. // hide from python
  124. dict.__setitem__("initModule", null);
  125. dict.__setitem__("toString", null);
  126. dict.__setitem__("setConnection", null);
  127. dict.__setitem__("getPyClass", null);
  128. dict.__setitem__("connection", null);
  129. dict.__setitem__("classDictInit", null);
  130. dict.__setitem__("cursors", null);
  131. }
  132. /**
  133. * Sets the attribute.
  134. *
  135. * @param name
  136. * @param value
  137. */
  138. public void __setattr__(String name, PyObject value) {
  139. if ("autocommit".equals(name)) {
  140. try {
  141. if (this.supportsTransactions) {
  142. this.connection.setAutoCommit(value.__nonzero__());
  143. }
  144. } catch (SQLException e) {
  145. throw zxJDBC.makeException(zxJDBC.DatabaseError, e);
  146. }
  147. return;
  148. }
  149. super.__setattr__(name, value);
  150. }
  151. /**
  152. * Finds the attribute.
  153. *
  154. * @param name the name of the attribute of interest
  155. * @return the value for the attribute of the specified name
  156. */
  157. public PyObject __findattr_ex__(String name) {
  158. if ("autocommit".equals(name)) {
  159. try {
  160. return connection.getAutoCommit() ? Py.One : Py.Zero;
  161. } catch (SQLException e) {
  162. throw zxJDBC.makeException(zxJDBC.DatabaseError, e);
  163. }
  164. } else if ("dbname".equals(name)) {
  165. try {
  166. return Py.newString(this.connection.getMetaData().getDatabaseProductName());
  167. } catch (SQLException e) {
  168. throw zxJDBC.makeException(zxJDBC.DatabaseError, e);
  169. }
  170. } else if ("dbversion".equals(name)) {
  171. try {
  172. return Py.newString(this.connection.getMetaData().getDatabaseProductVersion());
  173. } catch (SQLException e) {
  174. throw zxJDBC.makeException(zxJDBC.DatabaseError, e);
  175. }
  176. } else if ("drivername".equals(name)) {
  177. try {
  178. return Py.newString(this.connection.getMetaData().getDriverName());
  179. } catch (SQLException e) {
  180. throw zxJDBC.makeException(zxJDBC.DatabaseError, e);
  181. }
  182. } else if ("driverversion".equals(name)) {
  183. try {
  184. return Py.newString(this.connection.getMetaData().getDriverVersion());
  185. } catch (SQLException e) {
  186. throw zxJDBC.makeException(zxJDBC.DatabaseError, e);
  187. }
  188. } else if ("url".equals(name)) {
  189. try {
  190. return Py.newString(this.connection.getMetaData().getURL());
  191. } catch (SQLException e) {
  192. throw zxJDBC.makeException(zxJDBC.DatabaseError, e);
  193. }
  194. } else if ("__connection__".equals(name)) {
  195. return Py.java2py(this.connection);
  196. } else if ("__cursors__".equals(name)) {
  197. return Py.java2py(Collections.unmodifiableSet(this.cursors));
  198. } else if ("__statements__".equals(name)) {
  199. return Py.java2py(Collections.unmodifiableSet(this.statements));
  200. } else if ("__methods__".equals(name)) {
  201. return __methods__;
  202. } else if ("__members__".equals(name)) {
  203. return __members__;
  204. } else if ("closed".equals(name)) {
  205. return Py.newBoolean(closed);
  206. }
  207. return super.__findattr_ex__(name);
  208. }
  209. /**
  210. * Close the connection now (rather than whenever __del__ is called).
  211. * The connection will be unusable from this point forward; an Error
  212. * (or subclass) exception will be raised if any operation is attempted
  213. * with the connection. The same applies to all cursor objects trying
  214. * to use the connection.
  215. */
  216. public void close() {
  217. if (closed) {
  218. throw zxJDBC.makeException(zxJDBC.ProgrammingError, "connection is closed");
  219. }
  220. // mark ourselves closed now so that any callbacks we
  221. // get from closing down cursors and statements to not
  222. // try and modify our internal sets
  223. this.closed = true;
  224. synchronized (this.cursors) {
  225. // close the cursors
  226. for (Iterator i = this.cursors.iterator(); i.hasNext();) {
  227. ((PyCursor) i.next()).close();
  228. }
  229. this.cursors.clear();
  230. }
  231. synchronized (this.statements) {
  232. // close the cursors
  233. for (Iterator i = this.statements.iterator(); i.hasNext();) {
  234. ((PyStatement) i.next()).close();
  235. }
  236. this.statements.clear();
  237. }
  238. try {
  239. this.connection.close();
  240. } catch (SQLException e) {
  241. throw zxJDBC.makeException(e);
  242. }
  243. }
  244. /**
  245. * Commit any pending transaction to the database. Note that if the
  246. * database supports an auto-commit feature, this must be initially
  247. * off. An interface method may be provided to turn it back on.
  248. * <p/>
  249. * Database modules that do not support transactions should implement
  250. * this method with void functionality.
  251. */
  252. public void commit() {
  253. if (closed) {
  254. throw zxJDBC.makeException(zxJDBC.ProgrammingError, "connection is closed");
  255. }
  256. if (!this.supportsTransactions) {
  257. return;
  258. }
  259. try {
  260. this.connection.commit();
  261. } catch (SQLException e) {
  262. throw zxJDBC.makeException(e);
  263. }
  264. }
  265. /**
  266. * <i>This method is optional since not all databases provide transaction
  267. * support.</i>
  268. * <p/>
  269. * In case a database does provide transactions this method causes the database
  270. * to roll back to the start of any pending transaction. Closing a connection
  271. * without committing the changes first will cause an implicit rollback to be
  272. * performed.
  273. */
  274. public void rollback() {
  275. if (closed) {
  276. throw zxJDBC.makeException(zxJDBC.ProgrammingError, "connection is closed");
  277. }
  278. if (!this.supportsTransactions) {
  279. return;
  280. }
  281. try {
  282. this.connection.rollback();
  283. } catch (SQLException e) {
  284. throw zxJDBC.makeException(e);
  285. }
  286. }
  287. /**
  288. * Converts the given SQL statement into the system's native SQL grammar. A
  289. * driver may convert the JDBC sql grammar into its system's native SQL grammar
  290. * prior to sending it; this method returns the native form of the statement
  291. * that the driver would have sent.
  292. *
  293. * @param nativeSQL
  294. * @return the native form of this statement
  295. */
  296. public PyObject nativesql(PyObject nativeSQL) {
  297. if (closed) {
  298. throw zxJDBC.makeException(zxJDBC.ProgrammingError, "connection is closed");
  299. }
  300. if (nativeSQL == Py.None) {
  301. return Py.None;
  302. }
  303. try {
  304. return Py.newString(this.connection.nativeSQL(nativeSQL.__str__().toString()));
  305. } catch (SQLException e) {
  306. throw zxJDBC.makeException(e);
  307. }
  308. }
  309. /**
  310. * Return a new Cursor Object using the connection. If the database does not
  311. * provide a direct cursor concept, the module will have to emulate cursors
  312. * using other means to the extent needed by this specification.
  313. *
  314. * @return a new cursor using this connection
  315. */
  316. public PyCursor cursor() {
  317. return cursor(false);
  318. }
  319. /**
  320. * Return a new Cursor Object using the connection. If the database does not
  321. * provide a direct cursor concept, the module will have to emulate cursors
  322. * using other means to the extent needed by this specification.
  323. *
  324. * @param dynamicFetch if true, dynamically iterate the result
  325. * @return a new cursor using this connection
  326. */
  327. public PyCursor cursor(boolean dynamicFetch) {
  328. return this.cursor(dynamicFetch, Py.None, Py.None);
  329. }
  330. /**
  331. * Return a new Cursor Object using the connection. If the database does not
  332. * provide a direct cursor concept, the module will have to emulate cursors
  333. * using other means to the extent needed by this specification.
  334. *
  335. * @param dynamicFetch if true, dynamically iterate the result
  336. * @param rsType the type of the underlying ResultSet
  337. * @param rsConcur the concurrency of the underlying ResultSet
  338. * @return a new cursor using this connection
  339. */
  340. public PyCursor cursor(boolean dynamicFetch, PyObject rsType, PyObject rsConcur) {
  341. if (closed) {
  342. throw zxJDBC.makeException(zxJDBC.ProgrammingError, "connection is closed");
  343. }
  344. PyCursor cursor = new PyExtendedCursor(this, dynamicFetch, rsType, rsConcur);
  345. this.cursors.add(cursor);
  346. return cursor;
  347. }
  348. /**
  349. * Remove an open PyCursor.
  350. *
  351. * @param cursor
  352. */
  353. void remove(PyCursor cursor) {
  354. if (closed) {
  355. return;
  356. }
  357. this.cursors.remove(cursor);
  358. }
  359. /**
  360. * Method register
  361. *
  362. * @param statement statement
  363. */
  364. void add(PyStatement statement) {
  365. if (closed) {
  366. return;
  367. }
  368. this.statements.add(statement);
  369. }
  370. /**
  371. * Method contains
  372. *
  373. * @param statement statement
  374. * @return boolean
  375. */
  376. boolean contains(PyStatement statement) {
  377. if (closed) {
  378. return false;
  379. }
  380. return this.statements.contains(statement);
  381. }
  382. }
  383. class ConnectionFunc extends PyBuiltinMethodSet {
  384. ConnectionFunc(String name, int index, int minargs, int maxargs, String doc) {
  385. super(name, index, minargs, maxargs, doc, PyConnection.class);
  386. }
  387. public PyObject __call__() {
  388. PyConnection c = (PyConnection) __self__;
  389. switch (index) {
  390. case 0:
  391. c.close();
  392. return Py.None;
  393. case 1:
  394. c.commit();
  395. return Py.None;
  396. case 2:
  397. return c.cursor();
  398. case 3:
  399. c.rollback();
  400. return Py.None;
  401. default :
  402. throw info.unexpectedCall(0, false);
  403. }
  404. }
  405. public PyObject __call__(PyObject arg) {
  406. PyConnection c = (PyConnection) __self__;
  407. switch (index) {
  408. case 2:
  409. return c.cursor(arg.__nonzero__());
  410. case 4:
  411. return c.nativesql(arg);
  412. default :
  413. throw info.unexpectedCall(1, false);
  414. }
  415. }
  416. public PyObject __call__(PyObject arg1, PyObject arg2, PyObject arg3) {
  417. PyConnection c = (PyConnection) __self__;
  418. switch (index) {
  419. case 2:
  420. return c.cursor(arg1.__nonzero__(), arg2, arg3);
  421. default :
  422. throw info.unexpectedCall(3, false);
  423. }
  424. }
  425. public PyObject __call__(PyObject[] args, String[] keywords) {
  426. PyConnection c = (PyConnection) __self__;
  427. PyArgParser parser = new PyArgParser(args, keywords);
  428. switch (index) {
  429. case 2:
  430. PyObject dynamic = parser.kw("dynamic", Py.None);
  431. PyObject rstype = parser.kw("rstype", Py.None);
  432. PyObject rsconcur = parser.kw("rsconcur", Py.None);
  433. dynamic = (parser.numArg() >= 1) ? parser.arg(0) : dynamic;
  434. rstype = (parser.numArg() >= 2) ? parser.arg(1) : rstype;
  435. rsconcur = (parser.numArg() >= 3) ? parser.arg(2) : rsconcur;
  436. return c.cursor(dynamic.__nonzero__(), rstype, rsconcur);
  437. default :
  438. throw info.unexpectedCall(args.length, true);
  439. }
  440. }
  441. }