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.

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