[IMP] Make possible to make sql queries on an external database

This commit is contained in:
Florian da Costa
2022-02-16 16:18:33 +01:00
committed by thien
parent 464033c2c8
commit 154f37664d
4 changed files with 75 additions and 12 deletions

View File

@@ -1 +1,2 @@
from . import models
from . import sql_db

View File

@@ -12,8 +12,10 @@ from io import BytesIO
from psycopg2 import ProgrammingError
from psycopg2.sql import SQL
from odoo import _, api, fields, models
from odoo.exceptions import UserError
from odoo import _, api, fields, models, tools
from odoo.exceptions import UserError, ValidationError
from ..sql_db import get_external_cursor
logger = logging.getLogger(__name__)
@@ -96,6 +98,26 @@ class SQLRequestMixin(models.AbstractModel):
column2="user_id",
default=_default_user_ids,
)
use_external_database = fields.Boolean(
help=(
"If filled, the query will be executed against an external "
"database, configured in Odoo main configuration file. "
)
)
@api.constrains("use_external_database")
def check_external_config(self):
external_db_records = self.filtered(lambda rec: rec.use_external_database)
if external_db_records:
external_db_name = tools.config.get("external_db_name")
if not external_db_name:
raise ValidationError(
_(
"You can't use an external database as there are no such "
"configuration about this. Please contact your Odoo administrator"
" to solve this issue."
)
)
has_group_changed = fields.Boolean(
copy=False,
@@ -202,42 +224,54 @@ class SQLRequestMixin(models.AbstractModel):
else:
raise UserError(_("Unimplemented mode : '%s'") % mode)
query_cr = self._get_cr_for_query()
if rollback:
rollback_name = self._create_savepoint()
rollback_name = self._create_savepoint(query_cr)
try:
if mode == "stdout":
output = BytesIO()
self.env.cr.copy_expert(query, output)
query_cr.copy_expert(query, output)
res = base64.b64encode(output.getvalue())
output.close()
else:
self.env.cr.execute(query)
query_cr.execute(query)
if mode == "fetchall":
res = self.env.cr.fetchall()
res = query_cr.fetchall()
if header:
colnames = [desc[0] for desc in self.env.cr.description]
res.insert(0, colnames)
elif mode == "fetchone":
res = self.env.cr.fetchone()
res = query_cr.fetchone()
finally:
self._rollback_savepoint(rollback_name)
self._rollback_savepoint(rollback_name, query_cr)
return res
# Private Section
def _get_cr_for_query(self):
self.ensure_one()
if self.use_external_database:
return get_external_cursor()
else:
return self.env.cr
@api.model
def _create_savepoint(self):
def _create_savepoint(self, cr):
rollback_name = "{}_{}".format(self._name.replace(".", "_"), uuid.uuid1().hex)
# pylint: disable=sql-injection
req = "SAVEPOINT %s" % (rollback_name)
self.env.cr.execute(req)
cr.execute(req)
return rollback_name
@api.model
def _rollback_savepoint(self, rollback_name):
def _rollback_savepoint(self, rollback_name, cr):
# pylint: disable=sql-injection
req = "ROLLBACK TO SAVEPOINT %s" % (rollback_name)
self.env.cr.execute(req)
cr.execute(req)
# close external database cursor
if self.env.cr != cr:
cr.close()
@api.model
def _check_materialized_view_available(self):

View File

@@ -0,0 +1,6 @@
To configure the use of an external database, you need to edit the main configuration file of your instance and add the external database configuration with following keys :
* external_db_user
* external_db_password
* external_db_name
* external_db_host
* external_db_port

View File

@@ -0,0 +1,22 @@
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
from odoo import tools
from odoo.sql_db import Connection, ConnectionPool, _Pool # noqa
def connection_info_for_external_database():
db_name = tools.config.get("external_db_name")
connection_info = {"database": db_name}
for p in ("host", "port", "user", "password"):
cfg = tools.config.get("external_db_" + p)
if cfg:
connection_info[p] = cfg
return db_name, connection_info
def get_external_cursor():
global _Pool
if _Pool is None:
_Pool = ConnectionPool(int(tools.config["db_maxconn"]))
db, info = connection_info_for_external_database()
return Connection(_Pool, db, info).cursor()