You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

177 lines
6.6 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. # -*- coding: utf-8 -*-
  2. ##############################################################################
  3. #
  4. # Daniel Reis
  5. # 2011
  6. #
  7. # This program is free software: you can redistribute it and/or modify
  8. # it under the terms of the GNU Affero General Public License as
  9. # published by the Free Software Foundation, either version 3 of the
  10. # License, or (at your option) any later version.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU Affero General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU Affero General Public License
  18. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. #
  20. ##############################################################################
  21. import os
  22. import logging
  23. import psycopg2
  24. from openerp.osv import orm, fields
  25. from openerp.tools.translate import _
  26. import openerp.tools as tools
  27. _logger = logging.getLogger(__name__)
  28. CONNECTORS = [('postgresql', 'PostgreSQL')]
  29. try:
  30. import sqlalchemy
  31. CONNECTORS.append(('sqlite', 'SQLite'))
  32. try:
  33. import pymssql
  34. CONNECTORS.append(('mssql', 'Microsoft SQL Server'))
  35. assert pymssql
  36. except (ImportError, AssertionError):
  37. _logger.debug('MS SQL Server not available. Please install "pymssql"\
  38. python package.')
  39. try:
  40. import MySQLdb
  41. CONNECTORS.append(('mysql', 'MySQL'))
  42. assert MySQLdb
  43. except (ImportError, AssertionError):
  44. _logger.debug('MySQL not available. Please install "mysqldb"\
  45. python package.')
  46. except:
  47. _logger.debug('SQL Alchemy not available. Please install "slqalchemy"\
  48. python package.')
  49. try:
  50. import pyodbc
  51. CONNECTORS.append(('pyodbc', 'ODBC'))
  52. except:
  53. _logger.debug('ODBC libraries not available. Please install "unixodbc"\
  54. and "python-pyodbc" packages.')
  55. try:
  56. import cx_Oracle
  57. CONNECTORS.append(('cx_Oracle', 'Oracle'))
  58. except:
  59. _logger.debug('Oracle libraries not available. Please install "cx_Oracle"\
  60. python package.')
  61. class base_external_dbsource(orm.Model):
  62. _name = "base.external.dbsource"
  63. _description = 'External Database Sources'
  64. _columns = {
  65. 'name': fields.char('Datasource name', required=True, size=64),
  66. 'conn_string': fields.text('Connection string', help="""
  67. Sample connection strings:
  68. - Microsoft SQL Server:
  69. mssql+pymssql://username:%s@server:port/dbname?charset=utf8
  70. - MySQL: mysql://user:%s@server:port/dbname
  71. - ODBC: DRIVER={FreeTDS};SERVER=server.address;Database=mydb;UID=sa
  72. - ORACLE: username/%s@//server.address:port/instance
  73. - PostgreSQL:
  74. dbname='template1' user='dbuser' host='localhost' port='5432' password=%s
  75. - SQLite: sqlite:///test.db
  76. """),
  77. 'password': fields.char('Password', size=40),
  78. 'connector': fields.selection(CONNECTORS, 'Connector',
  79. required=True,
  80. help="If a connector is missing from the\
  81. list, check the server log to confirm\
  82. that the required components were\
  83. detected."),
  84. }
  85. def conn_open(self, cr, uid, id1):
  86. # Get dbsource record
  87. data = self.browse(cr, uid, id1)
  88. # Build the full connection string
  89. connStr = data.conn_string
  90. if data.password:
  91. if '%s' not in data.conn_string:
  92. connStr += ';PWD=%s'
  93. connStr = connStr % data.password
  94. # Try to connect
  95. if data.connector == 'cx_Oracle':
  96. os.environ['NLS_LANG'] = 'AMERICAN_AMERICA.UTF8'
  97. conn = cx_Oracle.connect(connStr)
  98. elif data.connector == 'pyodbc':
  99. conn = pyodbc.connect(connStr)
  100. elif data.connector in ('sqlite', 'mysql', 'mssql'):
  101. conn = sqlalchemy.create_engine(connStr).connect()
  102. elif data.connector == 'postgresql':
  103. conn = psycopg2.connect(connStr)
  104. return conn
  105. def execute(self, cr, uid, ids, sqlquery, sqlparams=None, metadata=False,
  106. context=None):
  107. """Executes SQL and returns a list of rows.
  108. "sqlparams" can be a dict of values, that can be referenced in
  109. the SQL statement using "%(key)s" or, in the case of Oracle,
  110. ":key".
  111. Example:
  112. sqlquery = "select * from mytable where city = %(city)s and
  113. date > %(dt)s"
  114. params = {'city': 'Lisbon',
  115. 'dt': datetime.datetime(2000, 12, 31)}
  116. If metadata=True, it will instead return a dict containing the
  117. rows list and the columns list, in the format:
  118. { 'cols': [ 'col_a', 'col_b', ...]
  119. , 'rows': [ (a0, b0, ...), (a1, b1, ...), ...] }
  120. """
  121. data = self.browse(cr, uid, ids)
  122. rows, cols = list(), list()
  123. for obj in data:
  124. conn = self.conn_open(cr, uid, obj.id)
  125. if obj.connector in ["sqlite", "mysql", "mssql"]:
  126. # using sqlalchemy
  127. cur = conn.execute(sqlquery, sqlparams)
  128. if metadata:
  129. cols = cur.keys()
  130. rows = [r for r in cur]
  131. else:
  132. # using other db connectors
  133. cur = conn.cursor()
  134. cur.execute(sqlquery, sqlparams)
  135. if metadata:
  136. cols = [d[0] for d in cur.description]
  137. rows = cur.fetchall()
  138. conn.close()
  139. if metadata:
  140. return{'cols': cols, 'rows': rows}
  141. else:
  142. return rows
  143. def connection_test(self, cr, uid, ids, context=None):
  144. for obj in self.browse(cr, uid, ids, context):
  145. conn = False
  146. try:
  147. conn = self.conn_open(cr, uid, obj.id)
  148. except Exception as e:
  149. raise orm.except_orm(_("Connection test failed!"),
  150. _("Here is what we got instead:\n %s")
  151. % tools.ustr(e))
  152. finally:
  153. try:
  154. if conn:
  155. conn.close()
  156. except Exception:
  157. # ignored, just a consequence of the previous exception
  158. pass
  159. # TODO: if OK a (wizard) message box should be displayed
  160. raise orm.except_orm(_("Connection test succeeded!"),
  161. _("Everything seems properly set up!"))