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.

180 lines
6.6 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. ##############################################################################
  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 odoo import models, fields, api, _
  25. from odoo.exceptions import Warning as UserError
  26. import odoo.tools as tools
  27. _logger = logging.getLogger(__name__)
  28. CONNECTORS = []
  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.info('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.info('MySQL not available. Please install "mysqldb"\
  45. python package.')
  46. except:
  47. _logger.info('SQL Alchemy not available. Please install "slqalchemy"\
  48. python package.')
  49. try:
  50. import pyodbc
  51. CONNECTORS.append(('pyodbc', 'ODBC'))
  52. except:
  53. _logger.info('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.info('Oracle libraries not available. Please install "cx_Oracle"\
  60. python package.')
  61. CONNECTORS.append(('postgresql', 'PostgreSQL'))
  62. class BaseExternalDbsource(models.Model):
  63. _name = "base.external.dbsource"
  64. _description = 'External Database Sources'
  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' \
  75. password=%s
  76. - SQLite: sqlite:///test.db
  77. """)
  78. password = fields.Char('Password', size=40)
  79. connector = fields.Selection(CONNECTORS, 'Connector', 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. @api.multi
  85. def conn_open(self):
  86. """The connection is open here."""
  87. self.ensure_one()
  88. # Get dbsource record
  89. # data = self.browse(cr, uid, id1)
  90. # Build the full connection string
  91. connStr = self.conn_string
  92. if self.password:
  93. if '%s' not in self.conn_string:
  94. connStr += ';PWD=%s'
  95. connStr = connStr % self.password
  96. # Try to connect
  97. if self.connector == 'cx_Oracle':
  98. os.environ['NLS_LANG'] = 'AMERICAN_AMERICA.UTF8'
  99. conn = cx_Oracle.connect(connStr)
  100. elif self.connector == 'pyodbc':
  101. conn = pyodbc.connect(connStr)
  102. elif self.connector in ('sqlite', 'mysql', 'mssql'):
  103. conn = sqlalchemy.create_engine(connStr).connect()
  104. elif self.connector == 'postgresql':
  105. conn = psycopg2.connect(connStr)
  106. return conn
  107. @api.multi
  108. def execute(self, sqlquery, sqlparams=None, metadata=False,
  109. context=None):
  110. """Executes SQL and returns a list of rows.
  111. "sqlparams" can be a dict of values, that can be referenced in
  112. the SQL statement using "%(key)s" or, in the case of Oracle,
  113. ":key".
  114. Example:
  115. sqlquery = "select * from mytable where city = %(city)s and
  116. date > %(dt)s"
  117. params = {'city': 'Lisbon',
  118. 'dt': datetime.datetime(2000, 12, 31)}
  119. If metadata=True, it will instead return a dict containing the
  120. rows list and the columns list, in the format:
  121. { 'cols': [ 'col_a', 'col_b', ...]
  122. , 'rows': [ (a0, b0, ...), (a1, b1, ...), ...] }
  123. """
  124. # data = self.browse(cr, uid, ids)
  125. rows, cols = list(), list()
  126. for obj in self:
  127. conn = obj.conn_open()
  128. if obj.connector in ["sqlite", "mysql", "mssql"]:
  129. # using sqlalchemy
  130. cur = conn.execute(sqlquery, sqlparams)
  131. if metadata:
  132. cols = cur.keys()
  133. rows = [r for r in cur]
  134. else:
  135. # using other db connectors
  136. cur = conn.cursor()
  137. cur.execute(sqlquery, sqlparams)
  138. if metadata:
  139. cols = [d[0] for d in cur.description]
  140. rows = cur.fetchall()
  141. conn.close()
  142. if metadata:
  143. return{'cols': cols, 'rows': rows}
  144. else:
  145. return rows
  146. @api.multi
  147. def connection_test(self):
  148. """Test of connection."""
  149. for obj in self:
  150. conn = False
  151. try:
  152. conn = self.conn_open()
  153. except Exception as e:
  154. raise UserError(_("Connection test failed: \
  155. Here is what we got instead:\n %s") % tools.ustr(e))
  156. finally:
  157. try:
  158. if conn:
  159. conn.close()
  160. except Exception:
  161. # ignored, just a consequence of the previous exception
  162. pass
  163. # TODO: if OK a (wizard) message box should be displayed
  164. raise UserError(_("Connection test succeeded: \
  165. Everything seems properly set up!"))