mysql.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. #============================================================================
  2. # This file is part of Pwman3.
  3. #
  4. # Pwman3 is free software; you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License, version 2
  6. # as published by the Free Software Foundation;
  7. #
  8. # Pwman3 is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. # GNU General Public License for more details.
  12. #
  13. # You should have received a copy of the GNU General Public License
  14. # along with Pwman3; if not, write to the Free Software
  15. # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  16. #============================================================================
  17. # Copyright (C) 2006 Ivan Kelly <ivan@ivankelly.net>
  18. #============================================================================
  19. """MySQL Database implementation."""
  20. from pwman.data.database import Database, DatabaseException
  21. from pwman.data.nodes import Node
  22. from pwman.data.tags import Tag
  23. import MySQLdb
  24. import pwman.util.config as config
  25. import cPickle
  26. class MySQLDatabase(Database):
  27. """MySQL Database implementation"""
  28. def __init__(self):
  29. """Initialise MySQLDatabase instance."""
  30. Database.__init__(self)
  31. self._tagidcache = {}
  32. config.add_defaults({"Database" : {"server": "localhost",
  33. "port" : "3306",
  34. "database" : "pwman",
  35. "table_prefix" : "pwman_"}})
  36. try:
  37. self._server = config.get_value('Database', 'server')
  38. self._port = config.get_value('Database', 'port')
  39. self._user = config.get_value('Database', 'user')
  40. self._password = config.get_value('Database', 'password')
  41. self._database = config.get_value('Database', 'database')
  42. self._prefix = config.get_value('Database', 'table_prefix')
  43. except KeyError, e:
  44. raise DatabaseException(
  45. "MySQL: missing parameter [%s]" % (e))
  46. def _open(self):
  47. try:
  48. self._con = None
  49. # server = "%s:%s" % (self._server, self._port)
  50. # self._con = pgdb.connect(host = server,
  51. # database = self._database,
  52. # user = self._user,
  53. # password = self._password)
  54. # self._cur = self._con.cursor()
  55. self._checktables()
  56. except MySQLdb.DatabaseError, e:
  57. raise DatabaseException("MySQL: %s" % (e))
  58. def _get_cur(self):
  59. try:
  60. if (self._con != None):
  61. return self._con.cursor()
  62. except MySQLdb.DatabaseError, e:
  63. pass
  64. self._con = MySQLdb.connect(host = self._server,
  65. port = int(self._port),
  66. db = self._database,
  67. user = self._user,
  68. passwd = self._password)
  69. self._cur = self._con.cursor()
  70. return self._cur
  71. def close(self):
  72. self._cur.close()
  73. self._con.close()
  74. def listtags(self, all=False):
  75. sql = ''
  76. params = []
  77. i = 0
  78. if len(self._filtertags) == 0 or all:
  79. sql = "SELECT DATA FROM %sTAGS ORDER BY DATA ASC" % (self._prefix)
  80. else:
  81. sql = ("SELECT %sTAGS.DATA FROM %sLOOKUP"
  82. +" INNER JOIN %sTAGS ON %sLOOKUP.TAG = %sTAGS.ID"
  83. +" WHERE NODE IN (") % (self._prefix, self._prefix, self._prefix,
  84. self._prefix, self._prefix)
  85. first = True
  86. i += 1
  87. for t in self._filtertags:
  88. if not first:
  89. sql += " INTERSECT "
  90. else:
  91. first = False
  92. sql += ("SELECT NODE FROM %sLOOKUP LEFT JOIN %sTAGS ON TAG = %sTAGS.ID "
  93. + " WHERE %sTAGS.DATA = %%s") % (self._prefix, self._prefix,
  94. self._prefix, self._prefix)
  95. params.append(cPickle.dumps(t))
  96. sql += ") EXCEPT SELECT DATA FROM %sTAGS WHERE " %(self._prefix)
  97. first = True
  98. for t in self._filtertags:
  99. if not first:
  100. sql += " OR "
  101. else:
  102. first = False
  103. sql += "%sTAGS.DATA = %%s" % (self._prefix)
  104. params.append(cPickle.dumps(t))
  105. try:
  106. cursor = self._get_cur()
  107. cursor.execute(sql, params)
  108. tags = []
  109. row = cursor.fetchone()
  110. while (row != None):
  111. tag = cPickle.loads(str(row[0]))
  112. tags.append(tag)
  113. row = cursor.fetchone()
  114. return tags
  115. except MySQLdb.DatabaseError, e:
  116. raise DatabaseException("MySQL: %s" % (e))
  117. def getnodes(self, ids):
  118. nodes = []
  119. idstr = ""
  120. first = True
  121. if len(ids) == 0:
  122. idstr = "-1"
  123. for i in ids:
  124. if first:
  125. idstr += "%d" % (i)
  126. first = False
  127. else:
  128. idstr += ", %d" % (i)
  129. sql = "SELECT ID,DATA FROM %sNODES WHERE ID IN (%s)" % (self._prefix,
  130. MySQLdb.escape_string(idstr))
  131. try:
  132. cursor = self._get_cur()
  133. cursor.execute(sql)
  134. row = cursor.fetchone()
  135. while row != None:
  136. node = cPickle.loads(str(row[1]))
  137. node.set_id(row[0])
  138. nodes.append(node)
  139. row = cursor.fetchone()
  140. except MySQLdb.DatabaseError, e:
  141. raise DatabaseException("MySQL: %s" % (e))
  142. return nodes
  143. def editnode(self, id, node):
  144. if not isinstance(node, Node): raise DatabaseException(
  145. "Tried to insert foreign object into database [%s]" % node)
  146. try:
  147. cursor = self._get_cur()
  148. sql = "UPDATE %sNODES SET DATA = %%s WHERE ID = %%s" % (self._prefix)
  149. cursor.execute(sql, (cPickle.dumps(node), id))
  150. except MySQL.DatabaseError, e:
  151. raise DatabaseException("MySQL: %s" % (e))
  152. self._setnodetags(node)
  153. self._checktags()
  154. self._commit()
  155. def addnodes(self, nodes):
  156. cursor = self._get_cur()
  157. for n in nodes:
  158. sql = "INSERT INTO %sNODES(DATA) VALUES(%%s)" % (self._prefix)
  159. if not isinstance(n, Node): raise DatabaseException(
  160. "Tried to insert foreign object into database [%s]", n)
  161. values = [cPickle.dumps(n)]
  162. try:
  163. cursor.execute(sql, values)
  164. except MySQLdb.DatabaseError, e:
  165. raise DatabaseException("MySQL: %s" % (e))
  166. id = cursor.lastrowid
  167. print "id: %d" % (id)
  168. n.set_id(id)
  169. self._setnodetags(n)
  170. self._commit()
  171. def removenodes(self, nodes):
  172. cursor = self._get_cur()
  173. for n in nodes:
  174. if not isinstance(n, Node): raise DatabaseException(
  175. "Tried to delete foreign object from database [%s]", n)
  176. try:
  177. sql = "DELETE FROM %sNODES WHERE ID = %%s" % (self._prefix)
  178. cursor.execute(sql, [n.get_id()])
  179. except MySQLdb.DatabaseError, e:
  180. raise DatabaseException("MySQL: %s" % (e))
  181. self._deletenodetags(n)
  182. self._checktags()
  183. self._commit()
  184. def listnodes(self):
  185. sql = ''
  186. params = []
  187. i = 0
  188. cursor = self._get_cur()
  189. if len(self._filtertags) == 0:
  190. sql = "SELECT ID FROM %sNODES ORDER BY ID ASC" % (self._prefix)
  191. else:
  192. first = True
  193. for t in self._filtertags:
  194. if not first:
  195. sql += " INTERSECT "
  196. else:
  197. first = False
  198. i += 1
  199. sql += (("SELECT NODE FROM %sLOOKUP LEFT JOIN %sTAGS ON TAG = %sTAGS.ID"
  200. + " WHERE %sTAGS.DATA = %%s ") % (self._prefix, self._prefix,
  201. self._prefix, self._prefix))
  202. params.append(cPickle.dumps(t))
  203. try:
  204. print sql
  205. cursor.execute(sql, params)
  206. ids = []
  207. row = cursor.fetchone()
  208. while (row != None):
  209. ids.append(row[0])
  210. row = cursor.fetchone()
  211. return ids
  212. except MySQLdb.DatabaseError, e:
  213. raise DatabaseException("MySQL: %s" % (e))
  214. def _commit(self):
  215. try:
  216. self._con.commit()
  217. except MySQLdb.DatabaseError, e:
  218. self._con.rollback()
  219. raise DatabaseException(
  220. "MySQL: Error commiting data to db [%s]" % (e))
  221. def _tagids(self, tags):
  222. ids = []
  223. cursor = self._get_cur()
  224. for t in tags:
  225. pickled = cPickle.dumps(t)
  226. try:
  227. ids.append(self._tagidcache[pickled])
  228. continue
  229. except KeyError, e:
  230. pass # not in cache
  231. sql = "SELECT ID FROM %sTAGS WHERE DATA = %%s" % (self._prefix)
  232. if not isinstance(t, Tag): raise DatabaseException(
  233. "Tried to insert foreign object into database [%s]", t)
  234. data = [ pickled ]
  235. try:
  236. cursor.execute(sql, data)
  237. row = cursor.fetchone()
  238. if (row != None):
  239. ids.append(row[0])
  240. self._tagidcache[pickled] = row[0]
  241. else:
  242. sql = "INSERT INTO %sTAGS(DATA) VALUES(%%s)" % (self._prefix)
  243. cursor.execute(sql, data)
  244. id = cursor.lastrowid
  245. ids.append(id)
  246. self._tagidcache[pickled] = id
  247. except MySQLdb.DatabaseError, e:
  248. raise DatabaseException("MySQLdb: %s" % (e))
  249. return ids
  250. def _deletenodetags(self, node):
  251. try:
  252. cursor = self._get_cur()
  253. sql = "DELETE FROM %sLOOKUP WHERE NODE = %%s" % (self._prefix)
  254. cursor.execute(sql, [node.get_id()])
  255. except MySQLdb.DatabaseError, e:
  256. raise DatabaseException("MySQLdb: %s" % (e))
  257. def _setnodetags(self, node):
  258. self._deletenodetags(node)
  259. ids = self._tagids(node.get_tags())
  260. for i in ids:
  261. sql = "INSERT INTO %sLOOKUP VALUES(%%s, %%s)" % (self._prefix)
  262. params = [ node.get_id(), i ]
  263. try:
  264. cursor = self._get_cur()
  265. cursor.execute(sql, params)
  266. except MySQLdb.DatabaseError, e:
  267. raise DatabaseException("MySQLdb: %s" % (e))
  268. def _checktags(self):
  269. self._tagidcache.clear()
  270. try:
  271. cursor = self._get_cur()
  272. sql = ("DELETE FROM %sTAGS WHERE ID NOT IN "
  273. + "(SELECT TAG FROM %sLOOKUP GROUP BY TAG)") % (self._prefix,
  274. self._prefix)
  275. cursor.execute(sql)
  276. except MySQLdb.DatabaseError, e:
  277. raise DatabaseException("MySQL: %s" % (e))
  278. self._commit()
  279. def _checktables(self):
  280. """ Check if the Pwman tables exist """
  281. cursor = self._get_cur()
  282. cursor.execute(
  283. "SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = '%sNODES'"
  284. % (self._prefix))
  285. if (cursor.fetchone() == None):
  286. # table doesn't exist, create it
  287. cursor.execute(("CREATE TABLE %sNODES"
  288. +"(ID INT AUTO_INCREMENT PRIMARY KEY, DATA TEXT NOT NULL)")
  289. % (self._prefix))
  290. cursor.execute(("CREATE TABLE %sTAGS"
  291. + "(ID INT AUTO_INCREMENT PRIMARY KEY,"
  292. + "DATA VARCHAR(255) NOT NULL UNIQUE)")
  293. % (self._prefix))
  294. cursor.execute(("CREATE TABLE %sLOOKUP"
  295. + "(NODE INTEGER NOT NULL, TAG INTEGER NOT NULL,"
  296. + " PRIMARY KEY(NODE, TAG))")
  297. % (self._prefix))
  298. cursor.execute(("CREATE TABLE %sKEY"
  299. + "(THEKEY TEXT(1024) NOT NULL DEFAULT '')")
  300. % (self._prefix));
  301. cursor.execute("INSERT INTO %sKEY VALUES('')" % (self._prefix));
  302. try:
  303. self._con.commit()
  304. except MySQLdb.DatabaseError, e:
  305. self._con.rollback()
  306. raise e
  307. def savekey(self, key):
  308. sql = "UPDATE %sKEY SET THEKEY = %%s" % (self._prefix)
  309. values = ( key )
  310. cursor = self._get_cur()
  311. cursor.execute(sql, values)
  312. try:
  313. self._con.commit()
  314. except MySQLdb.DatabaseError, e:
  315. self._con.rollback()
  316. raise DatabaseException(
  317. "MySQL: Error saving key [%s]" % (e))
  318. def loadkey(self):
  319. cursor = self._get_cur()
  320. cursor.execute("SELECT THEKEY FROM %sKEY" % (self._prefix));
  321. keyrow = cursor.fetchone()
  322. if (keyrow[0] == ''):
  323. return None
  324. else:
  325. return keyrow[0]