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.

166 lines
5.8 KiB

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