Browse Source

Merge pull request #351 from akretion/8.0-sql_export

[8.0] Add sql export module (migration from v7)
pull/777/head
Sylvain LE GAL 8 years ago
committed by GitHub
parent
commit
a6e5ad3785
  1. 61
      sql_export/README.rst
  2. 2
      sql_export/__init__.py
  3. 43
      sql_export/__openerp__.py
  4. 17
      sql_export/demo/sql_export.xml
  5. 232
      sql_export/i18n/fr.po
  6. 230
      sql_export/i18n/sql_export.pot
  7. 3
      sql_export/security/ir.model.access.csv
  8. 16
      sql_export/security/sql_export_security.xml
  9. 62
      sql_export/sql_export.py
  10. 106
      sql_export/sql_export_view.xml
  11. BIN
      sql_export/static/description/icon.png
  12. 2
      sql_export/tests/__init__.py
  13. 74
      sql_export/tests/test_sql_query.py
  14. 1
      sql_export/wizard/__init__.py
  15. 106
      sql_export/wizard/wizard_file.py
  16. 27
      sql_export/wizard/wizard_file_view.xml
  17. 93
      sql_request_abstract/README.rst
  18. 3
      sql_request_abstract/__init__.py
  19. 23
      sql_request_abstract/__openerp__.py
  20. 145
      sql_request_abstract/i18n/fr.po
  21. 140
      sql_request_abstract/i18n/sql_export_abstract.pot
  22. 3
      sql_request_abstract/models/__init__.py
  23. 255
      sql_request_abstract/models/sql_request_mixin.py
  24. 4
      sql_request_abstract/security/ir.model.access.csv
  25. 9
      sql_request_abstract/security/ir_module_category.xml
  26. 23
      sql_request_abstract/security/res_groups.xml
  27. BIN
      sql_request_abstract/static/description/icon.png

61
sql_export/README.rst

@ -0,0 +1,61 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:alt: License: AGPL-3
SQL Export
==========
Allow to export data in csv files FROM sql requests.
There are some restrictions in the sql sql request, you can only read datas.
No update, deletion or creation are possible.
A new menu named Export is created.
Known issues / Roadmap
======================
* Some words are prohibeted and can't be used is the query in anyways, even in a select query :
* delete
* drop
* insert
* alter
* truncate
* execute
* create
* update
See sql_request_abstract module to fix this issue.
* checking SQL request by execution and rollback is disabled in this module
since variables features has been introduced. This can be fixed by
overloading _prepare_request_check_execution() function.
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/server-tools/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed feedback
`here <https://github.com/OCA/server-tools/issues/new?body=module:%20sql_export%0Aversion:%208.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Credits
=======
Contributors
------------
* Florian da Costa <florian.dacosta@akretion.com>
Maintainer
----------
.. image:: http://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: http://odoo-community.org
This module is maintained by the OCA.
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
To contribute to this module, please visit http://odoo-community.org.

2
sql_export/__init__.py

@ -0,0 +1,2 @@
from . import sql_export
from . import wizard

43
sql_export/__openerp__.py

@ -0,0 +1,43 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2015 Akretion (<http://www.akretion.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
{
'name': 'SQL Export',
'version': '8.0.1.0.0',
'author': 'Akretion,Odoo Community Association (OCA)',
'website': 'http://www.akretion.com',
'license': 'AGPL-3',
'category': 'Generic Modules/Others',
'summary': 'Export data in csv file with SQL requests',
'depends': [
'sql_request_abstract',
],
'data': [
'sql_export_view.xml',
'wizard/wizard_file_view.xml',
'security/sql_export_security.xml',
'security/ir.model.access.csv',
],
'demo': [
'demo/sql_export.xml',
],
'installable': True,
}

17
sql_export/demo/sql_export.xml

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (C) 2017 - Today: GRAP (http://www.grap.coop)
@author Sylvain LE GAL (https://twitter.com/legalsylvain)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-->
<openerp><data>
<record id="sql_export_partner" model="sql.export">
<field name="name">Export Partners (Demo Data)</field>
<field name="query">SELECT name, street FROM res_partner;</field>
</record>
<function model="sql.export" name="button_clean_check_request" eval="([ref('sql_export.sql_export_partner')])"/>
</data></openerp>

232
sql_export/i18n/fr.po

@ -0,0 +1,232 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * sql_export
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 8.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-02-27 12:18+0000\n"
"PO-Revision-Date: 2017-02-27 12:18+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: sql_export
#: model:ir.model,name:sql_export.model_sql_file_wizard
msgid "Allow the user to save the file with sql request's data"
msgstr "Permet à l'utilisateur de sauvegarder le fichier contenant les données de la requête SQL"
#. module: sql_export
#: view:sql.export:sql_export.sql_export_view_form
#: field:sql.export,group_ids:0
msgid "Allowed Groups"
msgstr "Groupes Autorisés"
#. module: sql_export
#: view:sql.export:sql_export.sql_export_view_form
#: field:sql.export,user_ids:0
msgid "Allowed Users"
msgstr "Utilisateurs Autorisés"
#. module: sql_export
#: view:sql.file.wizard:sql_export.sql_file_wizard_view_form
msgid "Cancel"
msgstr "Annuler"
#. module: sql_export
#: view:sql.export:sql_export.sql_export_view_form
msgid "Clean and Check Request"
msgstr "Corriger et vérifier la requête"
#. module: sql_export
#: field:sql.export,copy_options:0
msgid "Copy Options"
msgstr "Options de copie"
#. module: sql_export
#: field:sql.export,create_uid:0
#: field:sql.file.wizard,create_uid:0
msgid "Created by"
msgstr "Créé par"
#. module: sql_export
#: field:sql.export,create_date:0
#: field:sql.file.wizard,create_date:0
msgid "Created on"
msgstr "Créé le"
#. module: sql_export
#: view:sql.file.wizard:sql_export.sql_file_wizard_view_form
msgid "Csv File"
msgstr "Fichier CSV"
#. module: sql_export
#: field:sql.export,display_name:0
#: field:sql.file.wizard,display_name:0
msgid "Display Name"
msgstr "Nom affiché"
#. module: sql_export
#: selection:sql.export,state:0
msgid "Draft"
msgstr "En brouillon"
#. module: sql_export
#: view:sql.export:sql_export.sql_export_view_form
#: view:sql.export:sql_export.sql_export_view_tree
msgid "Execute Query"
msgstr "Execute la requête"
#. module: sql_export
#: view:sql.file.wizard:sql_export.sql_file_wizard_view_form
msgid "Export"
msgstr "Exporter"
#. module: sql_export
#: view:sql.file.wizard:sql_export.sql_file_wizard_view_form
msgid "Export file"
msgstr "Fichier d'export"
#. module: sql_export
#: field:sql.file.wizard,binary_file:0
msgid "File"
msgstr "Fichier"
#. module: sql_export
#: field:sql.file.wizard,file_name:0
msgid "File Name"
msgstr "Nom de fichier"
#. module: sql_export
#: field:sql.export,id:0
#: field:sql.file.wizard,id:0
msgid "ID"
msgstr "ID"
#. module: sql_export
#: field:sql.export,__last_update:0
#: field:sql.file.wizard,__last_update:0
msgid "Last Modified on"
msgstr "Dernière modification le"
#. module: sql_export
#: field:sql.export,write_uid:0
#: field:sql.file.wizard,write_uid:0
msgid "Last Updated by"
msgstr "Dernière mise à jour par"
#. module: sql_export
#: field:sql.export,write_date:0
#: field:sql.file.wizard,write_date:0
msgid "Last Updated on"
msgstr "Dernière mise à jour le"
#. module: sql_export
#: field:sql.export,name:0
msgid "Name"
msgstr "Nom"
#. module: sql_export
#: view:sql.export:sql_export.sql_export_view_form
#: field:sql.export,field_ids:0
msgid "Parameters"
msgstr "Paramètres"
#. module: sql_export
#: field:sql.export,query:0
msgid "Query"
msgstr "Requête"
#. module: sql_export
#: view:sql.export:sql_export.sql_export_view_form
msgid "Request Name"
msgstr "Nom de la requête"
#. module: sql_export
#: model:ir.actions.act_window,name:sql_export.sql_export_tree_action
#: view:sql.export:sql_export.sql_export_view_tree
msgid "SQL Export"
msgstr "Export SQL"
#. module: sql_export
#: model:ir.actions.act_window,name:sql_export.sql_parameter_tree_action
#: view:ir.model.fields:sql_export.sql_parameter_view_tree
msgid "SQL Parameter"
msgstr "Paramètre SQL"
#. module: sql_export
#: view:sql.export:sql_export.sql_export_view_form
msgid "SQL Request"
msgstr "Requête SQL"
#. module: sql_export
#: selection:sql.export,state:0
msgid "SQL Valid"
msgstr "SQL Validé"
#. module: sql_export
#: model:ir.model,name:sql_export.model_sql_export
#: view:ir.model.fields:sql_export.sql_parameter_view_form
#: view:sql.export:sql_export.sql_export_view_form
msgid "SQL export"
msgstr "export SQL"
#. module: sql_export
#: view:sql.export:sql_export.sql_export_view_form
msgid "Set to Draft"
msgstr "Remettre en brouillon"
#. module: sql_export
#: model:ir.ui.menu,name:sql_export.sql_export_menu
#: model:ir.ui.menu,name:sql_export.sql_export_menu_view
msgid "Sql Export"
msgstr "Export SQL"
#. module: sql_export
#: model:ir.ui.menu,name:sql_export.sql_parameter_menu_view
msgid "Sql Export Variables"
msgstr "Variables d'export SQL"
#. module: sql_export
#: field:sql.file.wizard,sql_export_id:0
msgid "Sql export id"
msgstr "Sql export id"
#. module: sql_export
#: field:sql.export,state:0
msgid "State"
msgstr "Etat"
#. module: sql_export
#: help:sql.export,state:0
msgid "State of the Request:\n"
" * 'Draft': Not tested\n"
" * 'SQL Valid': SQL Request has been checked and is valid"
msgstr "Etat de la requête:\n"
" * 'En brouillon': non testée\n"
" * 'SQL Validé': La requête SQL a été vérifiée et est valide"
#. module: sql_export
#: help:sql.export,query:0
msgid "You can't use the following words: DELETE, DROP, CREATE, INSERT, ALTER, TRUNCATE, EXECUTE, UPDATE"
msgstr "Vous ne pouvez pas utiliser les termes suivants : DELETE, DROP, CREATE, INSERT, ALTER, TRUNCATE, EXECUTE, UPDATE"
#. module: sql_export
#: view:sql.file.wizard:sql_export.sql_file_wizard_view_form
msgid "or"
msgstr "ou"
#. module: sql_export
#: view:sql.export:sql_export.sql_export_view_form
msgid "select * from res_partner"
msgstr "select * from res_partner"
#. module: sql_export
#: view:sql.file.wizard:sql_export.sql_file_wizard_view_form
msgid "variables_placeholder"
msgstr "variables_placeholder"

230
sql_export/i18n/sql_export.pot

@ -0,0 +1,230 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * sql_export
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 8.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-02-27 12:24+0000\n"
"PO-Revision-Date: 2017-02-27 12:24+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: sql_export
#: model:ir.model,name:sql_export.model_sql_file_wizard
msgid "Allow the user to save the file with sql request's data"
msgstr ""
#. module: sql_export
#: view:sql.export:sql_export.sql_export_view_form
#: field:sql.export,group_ids:0
msgid "Allowed Groups"
msgstr ""
#. module: sql_export
#: view:sql.export:sql_export.sql_export_view_form
#: field:sql.export,user_ids:0
msgid "Allowed Users"
msgstr ""
#. module: sql_export
#: view:sql.file.wizard:sql_export.sql_file_wizard_view_form
msgid "Cancel"
msgstr ""
#. module: sql_export
#: view:sql.export:sql_export.sql_export_view_form
msgid "Clean and Check Request"
msgstr ""
#. module: sql_export
#: field:sql.export,copy_options:0
msgid "Copy Options"
msgstr ""
#. module: sql_export
#: field:sql.export,create_uid:0
#: field:sql.file.wizard,create_uid:0
msgid "Created by"
msgstr ""
#. module: sql_export
#: field:sql.export,create_date:0
#: field:sql.file.wizard,create_date:0
msgid "Created on"
msgstr ""
#. module: sql_export
#: view:sql.file.wizard:sql_export.sql_file_wizard_view_form
msgid "Csv File"
msgstr ""
#. module: sql_export
#: field:sql.export,display_name:0
#: field:sql.file.wizard,display_name:0
msgid "Display Name"
msgstr ""
#. module: sql_export
#: selection:sql.export,state:0
msgid "Draft"
msgstr ""
#. module: sql_export
#: view:sql.export:sql_export.sql_export_view_form
#: view:sql.export:sql_export.sql_export_view_tree
msgid "Execute Query"
msgstr ""
#. module: sql_export
#: view:sql.file.wizard:sql_export.sql_file_wizard_view_form
msgid "Export"
msgstr ""
#. module: sql_export
#: view:sql.file.wizard:sql_export.sql_file_wizard_view_form
msgid "Export file"
msgstr ""
#. module: sql_export
#: field:sql.file.wizard,binary_file:0
msgid "File"
msgstr ""
#. module: sql_export
#: field:sql.file.wizard,file_name:0
msgid "File Name"
msgstr ""
#. module: sql_export
#: field:sql.export,id:0
#: field:sql.file.wizard,id:0
msgid "ID"
msgstr ""
#. module: sql_export
#: field:sql.export,__last_update:0
#: field:sql.file.wizard,__last_update:0
msgid "Last Modified on"
msgstr ""
#. module: sql_export
#: field:sql.export,write_uid:0
#: field:sql.file.wizard,write_uid:0
msgid "Last Updated by"
msgstr ""
#. module: sql_export
#: field:sql.export,write_date:0
#: field:sql.file.wizard,write_date:0
msgid "Last Updated on"
msgstr ""
#. module: sql_export
#: field:sql.export,name:0
msgid "Name"
msgstr ""
#. module: sql_export
#: view:sql.export:sql_export.sql_export_view_form
#: field:sql.export,field_ids:0
msgid "Parameters"
msgstr ""
#. module: sql_export
#: field:sql.export,query:0
msgid "Query"
msgstr ""
#. module: sql_export
#: view:sql.export:sql_export.sql_export_view_form
msgid "Request Name"
msgstr ""
#. module: sql_export
#: model:ir.actions.act_window,name:sql_export.sql_export_tree_action
#: view:sql.export:sql_export.sql_export_view_tree
msgid "SQL Export"
msgstr ""
#. module: sql_export
#: model:ir.actions.act_window,name:sql_export.sql_parameter_tree_action
#: view:ir.model.fields:sql_export.sql_parameter_view_tree
msgid "SQL Parameter"
msgstr ""
#. module: sql_export
#: view:sql.export:sql_export.sql_export_view_form
msgid "SQL Request"
msgstr ""
#. module: sql_export
#: selection:sql.export,state:0
msgid "SQL Valid"
msgstr ""
#. module: sql_export
#: model:ir.model,name:sql_export.model_sql_export
#: view:ir.model.fields:sql_export.sql_parameter_view_form
#: view:sql.export:sql_export.sql_export_view_form
msgid "SQL export"
msgstr ""
#. module: sql_export
#: view:sql.export:sql_export.sql_export_view_form
msgid "Set to Draft"
msgstr ""
#. module: sql_export
#: model:ir.ui.menu,name:sql_export.sql_export_menu
#: model:ir.ui.menu,name:sql_export.sql_export_menu_view
msgid "Sql Export"
msgstr ""
#. module: sql_export
#: model:ir.ui.menu,name:sql_export.sql_parameter_menu_view
msgid "Sql Export Variables"
msgstr ""
#. module: sql_export
#: field:sql.file.wizard,sql_export_id:0
msgid "Sql export id"
msgstr ""
#. module: sql_export
#: field:sql.export,state:0
msgid "State"
msgstr ""
#. module: sql_export
#: help:sql.export,state:0
msgid "State of the Request:\n"
" * 'Draft': Not tested\n"
" * 'SQL Valid': SQL Request has been checked and is valid"
msgstr ""
#. module: sql_export
#: help:sql.export,query:0
msgid "You can't use the following words: DELETE, DROP, CREATE, INSERT, ALTER, TRUNCATE, EXECUTE, UPDATE"
msgstr ""
#. module: sql_export
#: view:sql.file.wizard:sql_export.sql_file_wizard_view_form
msgid "or"
msgstr ""
#. module: sql_export
#: view:sql.export:sql_export.sql_export_view_form
msgid "select * from res_partner"
msgstr ""
#. module: sql_export
#: view:sql.file.wizard:sql_export.sql_file_wizard_view_form
msgid "variables_placeholder"
msgstr ""

3
sql_export/security/ir.model.access.csv

@ -0,0 +1,3 @@
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
"access_sql_export_all","access_sql_export_all","model_sql_export",,1,0,0,0
"access_sql_export_editor","access_sql_export_editor","model_sql_export",sql_request_abstract.group_sql_request_manager,1,1,1,1

16
sql_export/security/sql_export_security.xml

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="0">
<record model="ir.rule" id="sql_export_restric_access_user_or_group">
<field name="name" >SQL Export users and groups rules</field>
<field name="model_id" ref="model_sql_export"/>
<field eval="1" name="perm_read"/>
<field eval="0" name="perm_create"/>
<field eval="0" name="perm_write"/>
<field eval="0" name="perm_unlink"/>
<field name="domain_force">['|', ('user_ids','=',user.id), ('group_ids','in', [x.id for x in user.groups_id])]</field>
</record>
</data>
</openerp>

62
sql_export/sql_export.py

@ -0,0 +1,62 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2015 Akretion (<http://www.akretion.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp import models, fields, api
class SqlExport(models.Model):
_name = "sql.export"
_inherit = ['sql.request.mixin']
_description = "SQL export"
_sql_request_groups_relation = 'groups_sqlquery_rel'
_sql_request_users_relation = 'users_sqlquery_rel'
_check_execution_enabled = False
copy_options = fields.Char(
string='Copy Options', required=True,
default="CSV HEADER DELIMITER ';'")
field_ids = fields.Many2many(
'ir.model.fields',
'fields_sqlquery_rel',
'sql_id',
'field_id',
'Parameters',
domain=[('model', '=', 'sql.file.wizard')])
@api.multi
def export_sql_query(self):
self.ensure_one()
wiz = self.env['sql.file.wizard'].create({
'sql_export_id': self.id})
return {
'view_type': 'form',
'view_mode': 'form',
'res_model': 'sql.file.wizard',
'res_id': wiz.id,
'type': 'ir.actions.act_window',
'target': 'new',
'context': self._context,
'nodestroy': True,
}

106
sql_export/sql_export_view.xml

@ -0,0 +1,106 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="sql_export_view_form" model="ir.ui.view">
<field name="name">Sql_export_form_view</field>
<field name="model">sql.export</field>
<field name="arch" type="xml">
<form string="SQL export">
<sheet>
<header>
<button name="button_clean_check_request" type="object" states="draft"
string="Clean and Check Request" class="oe_highlight"/>
<button name="button_set_draft" type="object" states="sql_valid"
string="Set to Draft" groups="sql_request_abstract.group_sql_request_manager"/>
<button name="export_sql_query" string="Execute Query" states="sql_valid" type="object" class="oe_highlight"
icon="gtk-execute"/>
<field name="state" widget="statusbar" />
</header>
<group>
<h1>
<field name="name" nolabel="1" placeholder="Request Name"/>
</h1>
</group>
<group name="option" groups="sql_request_abstract.group_sql_request_user">
<field name="copy_options"/>
</group>
<group name="request" string="SQL Request" groups="sql_request_abstract.group_sql_request_user">
<field name="query" nolabel="1" placeholder="select * from res_partner"/>
</group>
<group string="Parameters" groups="sql_request_abstract.group_sql_request_user">
<field name="field_ids" nolabel="1"/>
</group>
<group groups="sql_request_abstract.group_sql_request_manager">
<group string="Allowed Users">
<field name="user_ids" nolabel="1"/>
</group>
<group string="Allowed Groups">
<field name="group_ids" nolabel="1"/>
</group>
</group>
</sheet>
</form>
</field>
</record>
<record id="sql_export_view_tree" model="ir.ui.view">
<field name="name">Sql_export_tree_view</field>
<field name="model">sql.export</field>
<field name="arch" type="xml">
<tree string="SQL Export" colors="blue:state == 'draft'">
<field name="name"/>
<field name="state"/>
<button name="export_sql_query" string="Execute Query" states="sql_valid" type="object"
icon="gtk-execute"/>
</tree>
</field>
</record>
<record id="sql_export_tree_action" model="ir.actions.act_window">
<field name="name">SQL Export</field>
<field name="res_model">sql.export</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem id="sql_export_menu" name="Sql Export" parent="base.menu_reporting" sequence="80"/>
<menuitem id="sql_export_menu_view" name="Sql Export" parent="sql_export_menu" action="sql_export_tree_action" sequence="1"/>
<record id="sql_parameter_view_form" model="ir.ui.view">
<field name="name">Sql_parameter_form_view</field>
<field name="model">ir.model.fields</field>
<field name="arch" type="xml">
<form string="SQL export">
</form>
</field>
</record>
<record id="sql_parameter_view_tree" model="ir.ui.view">
<field name="name">Sql_parameter_tree_view</field>
<field name="model">ir.model.fields</field>
<field name="arch" type="xml">
<tree string="SQL Parameter">
<field name="name"/>
</tree>
</field>
</record>
<record id="sql_parameter_tree_action" model="ir.actions.act_window">
<field name="name">SQL Parameter</field>
<field name="res_model">ir.model.fields</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="context" eval="{'default_model_id': ref('sql_export.model_sql_file_wizard'), 'default_size': 64, 'search_default_state': 'manual'}"/>
<field name="domain">[('model','=','sql.file.wizard')]</field>
</record>
<menuitem id="sql_parameter_menu_view" name="Sql Export Variables" parent="sql_export_menu" action="sql_parameter_tree_action" sequence="5" groups="sql_request_abstract.group_sql_request_manager"/>
</data>
</openerp>

BIN
sql_export/static/description/icon.png

After

Width: 128  |  Height: 128  |  Size: 9.2 KiB

2
sql_export/tests/__init__.py

@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
from . import test_sql_query

74
sql_export/tests/test_sql_query.py

@ -0,0 +1,74 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Florian da Costa
# Copyright 2015 Akretion
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distnaributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import base64
from openerp.tests.common import TransactionCase
from openerp.exceptions import Warning as UserError
class TestExportSqlQuery(TransactionCase):
def setUp(self):
super(TestExportSqlQuery, self).setUp()
self.sql_export_obj = self.env['sql.export']
self.wizard_obj = self.env['sql.file.wizard']
self.sql_report_demo = self.env.ref('sql_export.sql_export_partner')
def test_sql_query(self):
wizard = self.wizard_obj.create({
'sql_export_id': self.sql_report_demo.id,
})
wizard.export_sql()
export = base64.b64decode(wizard.binary_file)
self.assertEqual(export.split(';')[0], 'name')
self.assertTrue(len(export.split(';')) > 6)
def test_prohibited_queries(self):
prohibited_queries = [
"upDaTe res_partner SET name = 'test' WHERE id = 1",
"DELETE FROM sql_export WHERE name = 'test';",
" DELETE FROM sql_export WHERE name = 'test' ;",
"""DELETE
FROM
sql_export
WHERE name = 'test'
""",
"SELECT id FROM sql_export;DELETE FROM sql_export",
]
for query in prohibited_queries:
with self.assertRaises(UserError):
sql_export = self.sql_export_obj.create({
'name': 'test_prohibited',
'query': query})
sql_export.button_clean_check_request()
def test_authorized_queries(self):
authorized_queries = [
"SELECT create_date FROM res_partner",
]
for query in authorized_queries:
sql_export = self.sql_export_obj.create({
'name': 'test_authorized',
'query': query})
sql_export.button_clean_check_request()
self.assertEqual(
sql_export.state, 'sql_valid',
"%s is a valid request" % (query))

1
sql_export/wizard/__init__.py

@ -0,0 +1 @@
from . import wizard_file

106
sql_export/wizard/wizard_file.py

@ -0,0 +1,106 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2015 Akretion (<http://www.akretion.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import datetime
from lxml import etree
from openerp import models, fields, api, osv
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
class SqlFileWizard(models.TransientModel):
_name = "sql.file.wizard"
_description = "Allow the user to save the file with sql request's data"
binary_file = fields.Binary('File', readonly=True)
file_name = fields.Char('File Name', readonly=True)
sql_export_id = fields.Many2one(comodel_name='sql.export', required=True)
@api.model
def fields_view_get(self, view_id=None, view_type='form',
toolbar=False, submenu=False):
"""
Display dinamicaly parameter fields depending on the sql_export.
"""
res = super(SqlFileWizard, self).fields_view_get(
view_id=view_id, view_type=view_type, toolbar=toolbar,
submenu=submenu)
export_obj = self.env['sql.export']
if view_type == 'form':
sql_export = export_obj.browse(self._context.get('active_id'))
if sql_export.field_ids:
eview = etree.fromstring(res['arch'])
group = etree.Element(
'group', name="variables_group", colspan="4")
toupdate_fields = []
for field in sql_export.field_ids:
kwargs = {'name': "%s" % field.name}
toupdate_fields.append(field.name)
view_field = etree.SubElement(group, 'field', **kwargs)
osv.orm.setup_modifiers(
view_field, self.fields_get(field.name))
res['fields'].update(self.fields_get(toupdate_fields))
placeholder = eview.xpath(
"//separator[@string='variables_placeholder']")[0]
placeholder.getparent().replace(
placeholder, group)
res['arch'] = etree.tostring(eview, pretty_print=True)
return res
@api.multi
def export_sql(self):
self.ensure_one()
sql_export = self.sql_export_id
# Manage Params
variable_dict = {}
today = datetime.datetime.now()
today_tz = fields.Datetime.context_timestamp(
sql_export, today)
date = today_tz.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
if sql_export.field_ids:
for field in sql_export.field_ids:
variable_dict[field.name] = self[field.name]
if "%(company_id)s" in sql_export.query:
variable_dict['company_id'] = self.env.user.company_id.id
if "%(user_id)s" in sql_export.query:
variable_dict['user_id'] = self._uid
# Execute Request
res = sql_export._execute_sql_request(
params=variable_dict, mode='stdout',
copy_options=sql_export.copy_options)
self.write({
'binary_file': res,
'file_name': sql_export.name + '_' + date + '.csv'
})
return {
'view_type': 'form',
'view_mode': 'form',
'res_model': 'sql.file.wizard',
'res_id': self.id,
'type': 'ir.actions.act_window',
'target': 'new',
'context': self._context,
'nodestroy': True,
}

27
sql_export/wizard/wizard_file_view.xml

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="sql_file_wizard_view_form" model="ir.ui.view">
<field name="name">sql.file.wizard.view.form</field>
<field name="model">sql.file.wizard</field>
<field name="arch" type="xml">
<form string="Csv File">
<separator string="variables_placeholder" colspan="4" invisible="1"/>
<separator string="Export file" colspan="4"
attrs="{'invisible': [('binary_file', '=', False)]}"/>
<field name="binary_file" filename="file_name"/>
<field name="file_name" invisible="1"/>
<footer>
<button name="export_sql" string="Export" type="object"
icon="gtk-apply" />
or
<button special="cancel" string="Cancel" type="object"
icon="gtk-cancel" />
</footer>
</form>
</field>
</record>
</data>
</openerp>

93
sql_request_abstract/README.rst

@ -0,0 +1,93 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
=====================================
Abstract Model to manage SQL Requests
=====================================
This module provide an abstract model to manage SQL Select request on database.
It is not usefull for itself. You can see an exemple of implementation in the
'sql_export' module. (same repository).
Implemented features
--------------------
* Add some restrictions in the sql request:
* you can only read datas. No update, deletion or creation are possible.
* some tables are not allowed, because they could contains clear password
or keys. For the time being ('ir_config_parameter').
* The request can be in a 'draft' or a 'SQL Valid' status. To be valid,
the request has to be cleaned, checked and tested. All of this operations
can be disabled in the inherited modules.
* This module two new groups:
* SQL Request / User : Can see all the sql requests by default and execute
them, if they are valid.
* SQL Request / Manager : has full access on sql requests.
Usage
=====
Inherit the model:
from openerp import models
class MyModel(models.model)
_name = 'my.model'
_inherit = ['sql.request.mixin']
_sql_request_groups_relation = 'my_model_groups_rel'
_sql_request_users_relation = 'my_model_users_rel'
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/149/8.0
Bug Tracker
===========
Bugs are tracked on `GitHub Issues
<https://github.com/OCA/server-tools/issues>`_. In case of trouble, please
check there if your issue has already been reported. If you spotted it first,
help us smash it by providing detailed and welcomed feedback.
Credits
=======
Images
------
* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.
Contributors
------------
* Florian da Costa <florian.dacosta@akretion.com>
* Sylvain LE GAL (https://twitter.com/legalsylvain)
Funders
-------
The development of this module has been financially supported by:
* Akretion (<http://www.akretion.com>)
* GRAP, Groupement Régional Alimentaire de Proximité (<http://www.grap.coop>)
Maintainer
----------
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
This module is maintained by the OCA.
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
To contribute to this module, please visit https://odoo-community.org.

3
sql_request_abstract/__init__.py

@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import models

23
sql_request_abstract/__openerp__.py

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017 - Today: GRAP (http://www.grap.coop)
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
{
'name': 'SQL Request Abstract',
'version': '8.0.1.0.0',
'author': 'GRAP,Akretion,Odoo Community Association (OCA)',
'website': 'https://www.odoo-community.org',
'license': 'AGPL-3',
'category': 'Tools',
'summary': 'Abstract Model to manage SQL Requests',
'depends': [
'base',
],
'data': [
'security/ir_module_category.xml',
'security/res_groups.xml',
'security/ir.model.access.csv',
],
'installable': True,
}

145
sql_request_abstract/i18n/fr.po

@ -0,0 +1,145 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * sql_request_abstract
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 8.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-02-27 12:11+0000\n"
"PO-Revision-Date: 2017-02-27 12:11+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: sql_request_abstract
#: field:sql.request.mixin,group_ids:0
msgid "Allowed Groups"
msgstr "Groupes autorisés"
#. module: sql_request_abstract
#: field:sql.request.mixin,user_ids:0
msgid "Allowed Users"
msgstr "Utilisateurs Autorisés"
#. module: sql_request_abstract
#: field:sql.request.mixin,create_uid:0
msgid "Created by"
msgstr "Créé par"
#. module: sql_request_abstract
#: field:sql.request.mixin,create_date:0
msgid "Created on"
msgstr "Créé le"
#. module: sql_request_abstract
#: field:sql.request.mixin,display_name:0
msgid "Display Name"
msgstr "Nom affiché"
#. module: sql_request_abstract
#: selection:sql.request.mixin,state:0
msgid "Draft"
msgstr "En brouillon"
#. module: sql_request_abstract
#: field:sql.request.mixin,id:0
msgid "ID"
msgstr "ID"
#. module: sql_request_abstract
#: code:addons/sql_request_abstract/models/sql_request_mixin.py:135
#, python-format
msgid "It is not allowed to execute a not checked request."
msgstr "Il n'est pas autorisé d'exécuter une requête non vérifiée."
#. module: sql_request_abstract
#: field:sql.request.mixin,__last_update:0
msgid "Last Modified on"
msgstr "Dernière modification le"
#. module: sql_request_abstract
#: field:sql.request.mixin,write_uid:0
msgid "Last Updated by"
msgstr "Dernière mise à jour par"
#. module: sql_request_abstract
#: field:sql.request.mixin,write_date:0
msgid "Last Updated on"
msgstr "Dernière mise à jour le"
#. module: sql_request_abstract
#: model:res.groups,name:sql_request_abstract.group_sql_request_manager
msgid "Manager"
msgstr "Responsable"
#. module: sql_request_abstract
#: field:sql.request.mixin,name:0
msgid "Name"
msgstr "Nom"
#. module: sql_request_abstract
#: field:sql.request.mixin,query:0
msgid "Query"
msgstr "Requête"
#. module: sql_request_abstract
#: selection:sql.request.mixin,state:0
msgid "SQL Valid"
msgstr "SQL Validé"
#. module: sql_request_abstract
#: model:ir.module.category,name:sql_request_abstract.category_sql_abstract
msgid "Sql Request"
msgstr "Request SQL"
#. module: sql_request_abstract
#: field:sql.request.mixin,state:0
msgid "State"
msgstr "Etat"
#. module: sql_request_abstract
#: help:sql.request.mixin,state:0
msgid "State of the Request:\n"
" * 'Draft': Not tested\n"
" * 'SQL Valid': SQL Request has been checked and is valid"
msgstr "Etat de la requête:\n"
" * 'En brouillon': non testée\n"
" * 'SQL Validé': La requête SQL a été vérifiée et est valide"
#. module: sql_request_abstract
#: code:addons/sql_request_abstract/models/sql_request_mixin.py:248
#, python-format
msgid "The SQL query is not valid:\n"
"\n"
" %s"
msgstr "La requête SQL n'est pas valide:\n"
"\n"
" %s"
#. module: sql_request_abstract
#: code:addons/sql_request_abstract/models/sql_request_mixin.py:217
#, python-format
msgid "The query is not allowed because it contains unsafe word '%s'"
msgstr "La requête n'est pas autorisée car elle contient un terme non sécurisé '%s'"
#. module: sql_request_abstract
#: code:addons/sql_request_abstract/models/sql_request_mixin.py:156
#, python-format
msgid "Unimplemented mode : '%s'"
msgstr "Mode non implémenté : '%s'"
#. module: sql_request_abstract
#: model:res.groups,name:sql_request_abstract.group_sql_request_user
msgid "User"
msgstr "Utilisateur"
#. module: sql_request_abstract
#: help:sql.request.mixin,query:0
msgid "You can't use the following words: DELETE, DROP, CREATE, INSERT, ALTER, TRUNCATE, EXECUTE, UPDATE"
msgstr "Vous ne pouvez pas utiliser les termes suivants : DELETE, DROP, CREATE, INSERT, ALTER, TRUNCATE, EXECUTE, UPDATE"

140
sql_request_abstract/i18n/sql_export_abstract.pot

@ -0,0 +1,140 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * sql_request_abstract
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 8.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-02-27 12:11+0000\n"
"PO-Revision-Date: 2017-02-27 12:11+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: sql_request_abstract
#: field:sql.request.mixin,group_ids:0
msgid "Allowed Groups"
msgstr ""
#. module: sql_request_abstract
#: field:sql.request.mixin,user_ids:0
msgid "Allowed Users"
msgstr ""
#. module: sql_request_abstract
#: field:sql.request.mixin,create_uid:0
msgid "Created by"
msgstr ""
#. module: sql_request_abstract
#: field:sql.request.mixin,create_date:0
msgid "Created on"
msgstr ""
#. module: sql_request_abstract
#: field:sql.request.mixin,display_name:0
msgid "Display Name"
msgstr ""
#. module: sql_request_abstract
#: selection:sql.request.mixin,state:0
msgid "Draft"
msgstr ""
#. module: sql_request_abstract
#: field:sql.request.mixin,id:0
msgid "ID"
msgstr ""
#. module: sql_request_abstract
#: code:addons/sql_request_abstract/models/sql_request_mixin.py:135
#, python-format
msgid "It is not allowed to execute a not checked request."
msgstr ""
#. module: sql_request_abstract
#: field:sql.request.mixin,__last_update:0
msgid "Last Modified on"
msgstr ""
#. module: sql_request_abstract
#: field:sql.request.mixin,write_uid:0
msgid "Last Updated by"
msgstr ""
#. module: sql_request_abstract
#: field:sql.request.mixin,write_date:0
msgid "Last Updated on"
msgstr ""
#. module: sql_request_abstract
#: model:res.groups,name:sql_request_abstract.group_sql_abstract_mixin_manager
msgid "Manager"
msgstr ""
#. module: sql_request_abstract
#: field:sql.request.mixin,name:0
msgid "Name"
msgstr ""
#. module: sql_request_abstract
#: field:sql.request.mixin,query:0
msgid "Query"
msgstr ""
#. module: sql_request_abstract
#: selection:sql.request.mixin,state:0
msgid "SQL Valid"
msgstr ""
#. module: sql_request_abstract
#: model:ir.module.category,name:sql_request_abstract.category_sql_abstract
msgid "Sql Request"
msgstr ""
#. module: sql_request_abstract
#: field:sql.request.mixin,state:0
msgid "State"
msgstr ""
#. module: sql_request_abstract
#: help:sql.request.mixin,state:0
msgid "State of the Request:\n"
" * 'Draft': Not tested\n"
" * 'SQL Valid': SQL Request has been checked and is valid"
msgstr ""
#. module: sql_request_abstract
#: code:addons/sql_request_abstract/models/sql_request_mixin.py:248
#, python-format
msgid "The SQL query is not valid:\n"
"\n"
" %s"
msgstr ""
#. module: sql_request_abstract
#: code:addons/sql_request_abstract/models/sql_request_mixin.py:217
#, python-format
msgid "The query is not allowed because it contains unsafe word '%s'"
msgstr ""
#. module: sql_request_abstract
#: code:addons/sql_request_abstract/models/sql_request_mixin.py:156
#, python-format
msgid "Unimplemented mode : '%s'"
msgstr ""
#. module: sql_request_abstract
#: model:res.groups,name:sql_request_abstract.group_sql_abstract_mixin_user
msgid "User"
msgstr ""
#. module: sql_request_abstract
#: help:sql.request.mixin,query:0
msgid "You can't use the following words: DELETE, DROP, CREATE, INSERT, ALTER, TRUNCATE, EXECUTE, UPDATE"
msgstr ""

3
sql_request_abstract/models/__init__.py

@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import sql_request_mixin

255
sql_request_abstract/models/sql_request_mixin.py

@ -0,0 +1,255 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2015 Akretion (<http://www.akretion.com>)
# Copyright (C) 2017 - Today: GRAP (http://www.grap.coop)
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import re
import uuid
import StringIO
import base64
from psycopg2 import ProgrammingError
from openerp import _, api, fields, models
from openerp.exceptions import Warning as UserError
class SQLRequestMixin(models.Model):
_name = 'sql.request.mixin'
_clean_query_enabled = True
_check_prohibited_words_enabled = True
_check_execution_enabled = True
_sql_request_groups_relation = False
_sql_request_users_relation = False
STATE_SELECTION = [
('draft', 'Draft'),
('sql_valid', 'SQL Valid'),
]
PROHIBITED_WORDS = [
'delete',
'drop',
'insert',
'alter',
'truncate',
'execute',
'create',
'update',
'ir_config_parameter',
]
# Default Section
@api.model
def _default_group_ids(self):
ir_model_obj = self.env['ir.model.data']
return [ir_model_obj.xmlid_to_res_id(
'sql_request_abstract.group_sql_request_user')]
@api.model
def _default_user_ids(self):
return []
# Columns Section
name = fields.Char('Name', required=True)
query = fields.Text(
string='Query', required=True, help="You can't use the following words"
": DELETE, DROP, CREATE, INSERT, ALTER, TRUNCATE, EXECUTE, UPDATE")
state = fields.Selection(
string='State', selection=STATE_SELECTION, default='draft',
help="State of the Request:\n"
" * 'Draft': Not tested\n"
" * 'SQL Valid': SQL Request has been checked and is valid")
group_ids = fields.Many2many(
comodel_name='res.groups', string='Allowed Groups',
relation=_sql_request_groups_relation,
column1='sql_id', column2='group_id',
default=_default_group_ids)
user_ids = fields.Many2many(
comodel_name='res.users', string='Allowed Users',
relation=_sql_request_users_relation,
column1='sql_id', column2='user_id',
default=_default_user_ids)
# Action Section
@api.multi
def button_clean_check_request(self):
for item in self:
if item._clean_query_enabled:
item._clean_query()
if item._check_prohibited_words_enabled:
item._check_prohibited_words()
if item._check_execution_enabled:
item._check_execution()
item.state = 'sql_valid'
@api.multi
def button_set_draft(self):
self.write({'state': 'draft'})
# API Section
@api.multi
def _execute_sql_request(
self, params=None, mode='fetchall', rollback=True,
view_name=False, copy_options="CSV HEADER DELIMITER ';'"):
"""Execute a SQL request on the current database.
??? This function checks before if the user has the
right to execute the request.
:param params: (dict) of keys / values that will be replaced in
the sql query, before executing it.
:param mode: (str) result type expected. Available settings :
* 'view': create a view with the select query. Extra param
required 'view_name'.
* 'materialized_view': create a MATERIALIZED VIEW with the
select query. Extra parameter required 'view_name'.
* 'fetchall': execute the select request, and return the
result of 'cr.fetchall()'.
* 'fetchone' : execute the select request, and return the
result of 'cr.fetchone()'
:param rollback: (boolean) mention if a rollback should be played after
the execution of the query. Please keep this feature enabled
for security reason, except if necessary.
(Ignored if @mode in ('view', 'materialized_view'))
:param view_name: (str) name of the view.
(Ignored if @mode not in ('view', 'materialized_view'))
:param copy_options: (str) mentions extra options for
"COPY request STDOUT WITH xxx" request.
(Ignored if @mode != 'stdout')
..note:: The following exceptions could be raised:
psycopg2.ProgrammingError: Error in the SQL Request.
openerp.exceptions.Warning:
* 'mode' is not implemented.
* materialized view is not supported by the Postgresql Server.
"""
self.ensure_one()
res = False
# Check if the request is in a valid state
if self.state == 'draft':
raise UserError(_(
"It is not allowed to execute a not checked request."))
# Disable rollback if a creation of a view is asked
if mode in ('view', 'materialized_view'):
rollback = False
params = params and params or {}
query = self.env.cr.mogrify(self.query, params).decode('utf-8')
if mode in ('fetchone', 'fetchall'):
pass
elif mode == 'stdout':
query = "COPY (%s) TO STDOUT WITH %s" % (query, copy_options)
elif mode in 'view':
query = "CREATE VIEW %s AS (%s);" % (query, view_name)
elif mode in 'materialized_view':
self._check_materialized_view_available()
query = "CREATE MATERIALIZED VIEW %s AS (%s);" % (query, view_name)
else:
raise UserError(_("Unimplemented mode : '%s'" % mode))
if rollback:
rollback_name = self._create_savepoint()
try:
if mode == 'stdout':
output = StringIO.StringIO()
self.env.cr.copy_expert(query, output)
output.getvalue()
res = base64.b64encode(output.getvalue())
output.close()
else:
self.env.cr.execute(query)
if mode == 'fetchall':
res = self.env.cr.fetchall()
elif mode == 'fetchone':
res = self.env.cr.fetchone()
finally:
self._rollback_savepoint(rollback_name)
return res
# Private Section
@api.model
def _create_savepoint(self):
rollback_name = '%s_%s' % (
self._name.replace('.', '_'), uuid.uuid1().hex)
req = "SAVEPOINT %s" % (rollback_name)
self.env.cr.execute(req)
return rollback_name
@api.model
def _rollback_savepoint(self, rollback_name):
req = "ROLLBACK TO SAVEPOINT %s" % (rollback_name)
self.env.cr.execute(req)
@api.model
def _check_materialized_view_available(self):
self.env.cr.execute("SHOW server_version;")
res = self.env.cr.fetchone()[0].split('.')
minor_version = float('.'.join(res[:2]))
return minor_version >= 9.3
@api.multi
def _clean_query(self):
self.ensure_one()
query = self.query.strip()
while query[-1] == ';':
query = query[:-1]
self.query = query
@api.multi
def _check_prohibited_words(self):
"""Check if the query contains prohibited words, to avoid maliscious
SQL requests"""
self.ensure_one()
query = self.query.lower()
for word in self.PROHIBITED_WORDS:
expr = r'\b%s\b' % word
is_not_safe = re.search(expr, query)
if is_not_safe:
raise UserError(_(
"The query is not allowed because it contains unsafe word"
" '%s'") % (word))
@api.multi
def _check_execution(self):
"""Ensure that the query is valid, trying to execute it. A rollback
is done after."""
self.ensure_one()
query = self._prepare_request_check_execution()
rollback_name = self._create_savepoint()
res = False
try:
self.env.cr.execute(query)
res = self._hook_executed_request()
except ProgrammingError as e:
raise UserError(
_("The SQL query is not valid:\n\n %s") % e.message)
finally:
self._rollback_savepoint(rollback_name)
return res
@api.multi
def _prepare_request_check_execution(self):
"""Overload me to replace some part of the query, if it contains
parameters"""
self.ensure_one()
return self.query
def _hook_executed_request(self):
"""Overload me to insert custom code, when the SQL request has
been executed, before the rollback.
"""
self.ensure_one()
return False

4
sql_request_abstract/security/ir.model.access.csv

@ -0,0 +1,4 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_sql_request_mixin_all,access_sql_request_mixin_all,model_sql_request_mixin,,0,0,0,0
access_sql_request_mixin_user,access_sql_request_mixin_user,model_sql_request_mixin,sql_request_abstract.group_sql_request_user,1,0,0,0
access_sql_request_mixin_manager,access_sql_request_mixin_manager,model_sql_request_mixin,sql_request_abstract.group_sql_request_manager,1,1,1,1

9
sql_request_abstract/security/ir_module_category.xml

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<openerp><data>
<record model="ir.module.category" id="category_sql_abstract">
<field name="name">Sql Request</field>
</record>
</data></openerp>

23
sql_request_abstract/security/res_groups.xml

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (C) 2017 - Today: GRAP (http://www.grap.coop)
@author Sylvain LE GAL (https://twitter.com/legalsylvain)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-->
<openerp><data>
<record model="res.groups" id="group_sql_request_user">
<field name="name">User</field>
<field name="category_id" ref="category_sql_abstract" />
</record>
<record model="res.groups" id="group_sql_request_manager">
<field name="name">Manager</field>
<field name="category_id" ref="category_sql_abstract" />
<field name="users" eval="[(4, ref('base.user_root'))]"/>
<field name="implied_ids" eval="[(4, ref('group_sql_request_user'))]" />
</record>
</data></openerp>

BIN
sql_request_abstract/static/description/icon.png

After

Width: 128  |  Height: 128  |  Size: 9.2 KiB

Loading…
Cancel
Save