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.

204 lines
7.1 KiB

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. try:
  62. import fdb
  63. CONNECTORS.append(('fdb', 'Firebird'))
  64. except:
  65. _logger.info('Firebird libraries not available. Please install "fdb"\
  66. python package.')
  67. CONNECTORS.append(('postgresql', 'PostgreSQL'))
  68. class BaseExternalDbsource(models.Model):
  69. _name = "base.external.dbsource"
  70. _description = 'External Database Sources'
  71. name = fields.Char('Datasource name', required=True, size=64)
  72. conn_string = fields.Text('Connection string', help="""
  73. Sample connection strings:
  74. - Microsoft SQL Server:
  75. mssql+pymssql://username:%s@server:port/dbname?charset=utf8
  76. - MySQL: mysql://user:%s@server:port/dbname
  77. - ODBC: DRIVER={FreeTDS};SERVER=server.address;Database=mydb;UID=sa
  78. - ORACLE: username/%s@//server.address:port/instance
  79. - FireBird: host=localhost;database=mydatabase.gdb;user=sysdba;password=%s;
  80. port=3050;charset=utf8
  81. - PostgreSQL:
  82. dbname='template1' user='dbuser' host='localhost' port='5432' \
  83. password=%s
  84. - SQLite: sqlite:///test.db
  85. """)
  86. password = fields.Char('Password', size=40)
  87. connector = fields.Selection(CONNECTORS, 'Connector', required=True,
  88. help="If a connector is missing from the\
  89. list, check the server log to confirm\
  90. that the required components were\
  91. detected.")
  92. @api.multi
  93. def conn_open(self):
  94. """The connection is open here."""
  95. self.ensure_one()
  96. # Get dbsource record
  97. # Build the full connection string
  98. connStr = self.conn_string
  99. if self.password:
  100. if '%s' not in self.conn_string:
  101. connStr += ';PWD=%s'
  102. connStr = connStr % self.password
  103. # Try to connect
  104. if self.connector == 'cx_Oracle':
  105. os.environ['NLS_LANG'] = 'AMERICAN_AMERICA.UTF8'
  106. conn = cx_Oracle.connect(connStr)
  107. elif self.connector == 'pyodbc':
  108. conn = pyodbc.connect(connStr)
  109. elif self.connector in ('sqlite', 'mysql', 'mssql'):
  110. conn = sqlalchemy.create_engine(connStr).connect()
  111. elif self.connector == 'fdb':
  112. kwargs = dict([x.split('=') for x in connStr.split(';')])
  113. conn = fdb.connect(**kwargs)
  114. elif self.connector == 'postgresql':
  115. conn = psycopg2.connect(connStr)
  116. return conn
  117. @api.multi
  118. def execute(self, sqlquery, sqlparams=None, metadata=False,
  119. context=None):
  120. """Executes SQL and returns a list of rows.
  121. "sqlparams" can be a dict of values, that can be referenced in
  122. the SQL statement using "%(key)s" or, in the case of Oracle,
  123. ":key".
  124. Example:
  125. sqlquery = "select * from mytable where city = %(city)s and
  126. date > %(dt)s"
  127. params = {'city': 'Lisbon',
  128. 'dt': datetime.datetime(2000, 12, 31)}
  129. If metadata=True, it will instead return a dict containing the
  130. rows list and the columns list, in the format:
  131. { 'cols': [ 'col_a', 'col_b', ...]
  132. , 'rows': [ (a0, b0, ...), (a1, b1, ...), ...] }
  133. """
  134. rows, cols = list(), list()
  135. for obj in self:
  136. conn = obj.conn_open()
  137. if obj.connector in ["sqlite", "mysql", "mssql"]:
  138. # using sqlalchemy
  139. cur = conn.execute(sqlquery, sqlparams)
  140. if metadata:
  141. cols = cur.keys()
  142. rows = [r for r in cur]
  143. elif obj.connector in ["fdb"]:
  144. # using other db connectors
  145. cur = conn.cursor()
  146. for key in sqlparams:
  147. sqlquery = sqlquery.replace('%%(%s)s' % key,
  148. str(sqlparams[key]))
  149. cur.execute(sqlquery)
  150. rows = cur.fetchall()
  151. else:
  152. # using other db connectors
  153. cur = conn.cursor()
  154. cur.execute(sqlquery, sqlparams)
  155. if metadata:
  156. cols = [d[0] for d in cur.description]
  157. rows = cur.fetchall()
  158. conn.close()
  159. if metadata:
  160. return{'cols': cols, 'rows': rows}
  161. else:
  162. return rows
  163. @api.multi
  164. def connection_test(self):
  165. """Test of connection."""
  166. self.ensure_one()
  167. conn = False
  168. try:
  169. conn = self.conn_open()
  170. except Exception as e:
  171. raise UserError(_("Connection test failed: \
  172. Here is what we got instead:\n %s") % tools.ustr(e))
  173. finally:
  174. if conn:
  175. conn.close()
  176. # TODO: if OK a (wizard) message box should be displayed
  177. raise UserError(_("Connection test succeeded: \
  178. Everything seems properly set up!"))