How to read user defined types from Oracle using cx_Oracle in Python - cx-oracle

This code works...
Its making a call to oracle function dbms_comparison.compare
With in/out/return parameters as such...
DBMS_COMPARISON.COMPARE(
comparison_name IN VARCHAR2,
scan_info OUT COMPARISON_TYPE,
min_value IN VARCHAR2 DEFAULT NULL,
max_value IN VARCHAR2 DEFAULT NULL,
RETURN BOOLEAN
The call succeeds
try:
with cx_Oracle.connect(
config.username,
config.password,
config.dsn,
encoding=config.encoding) as connection:
with connection.cursor() as cursor:
comparison_type = connection.gettype( "DBMS_COMPARISON.COMPARISON_TYPE")
scan_info = cursor.var(comparison_type)
result = cursor.callfunc("dbms_comparison.compare", bool, [comparison_name, scan_info, None, None, True])
except cx_Oracle.Error as error:
print(error)
I am doing this below to tell cx_oracle about the shape of the out parameter scan_info
comparison_type = connection.gettype( "DBMS_COMPARISON.COMPARISON_TYPE")
scan_info = cursor.var(comparison_type)
scan_info has a value, and all looks good, see image below.
BUT, how do I retrieve the individual values from the scan_info return value shapes as...
TYPE COMPARISON_TYPE IS RECORD(
scan_id NUMBER,
loc_rows_merged NUMBER,
rmt_rows_merged NUMBER,
loc_rows_deleted NUMBER,
rmt_rows_deleted NUMBER)
I searched for too long for this and could not find the answer.
Strange surely this is run of the mill with database function taking/returning user defined types?!
You'd expect the result to be accessed as:
scan_info.scan_id
scan_info.loc_rows_merged
But apparently its not as simple as that and the Oracle docs do not mention how to do this.
So, if you my friend has done something similar before, please enlighten me.

In order to access the attributes you need to do this:
scan_info_inst = scan_info.getvalue()
print("Scan ID:", scan_info_inst.SCAN_ID)
As Chris mentioned, the case matters!

Related

Odoo 13 API how to save null search results as a string

I am using the Odoo 13 api as such.
wanted_ids= models.execute_kw(db, uid, password,
'res.partner', 'search_read',
[[['name', '=',False]]],
)
print(wanted_ids)
as expected nothing is returned.
[]
What I want instead of null
['Nothing found']
I am not sure how to approach this. I have tried to raise exceptions but this does not save to the wanted_ids variable.
if not wanted_ids:
wanted_ids = ['Nothing found']
This is pure Python, I think so

Odoo 12 : How to prevent default field method to be executed

I scheduled an cron that execute every 1st of the month, the purpose is to allocate leave for all employee according to their tags. here is sample of my code :
for leave in leave_type_ids:
for employee_tag in employee_tag_ids:
values = {
'name': 'Allocation mensuelle %s %s' % (now.strftime('%B'), now.strftime('%Y')),
'holiday_status_id': leave.id,
'number_of_days': employee_tag.allocation,
'holiday_type': 'category',
'category_id': employee_tag.id,
}
try:
self.create(values).action_approve()
except Exception as e:
_logger.critical(e)
I want to point out that self is instance of 'hr.leave.allocation'.
The problem is when I create the record, the field employee_id is automatically fill with the user/employee OdooBot (the one who executed the program in the cron) and that is not all, the employee OdooBot was allocated a leaves.
This behavior is due to those codes in odoo native modules :
def _default_employee(self):
return self.env.context.get('default_employee_id') or self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1)
employee_id = fields.Many2one(
'hr.employee', string='Employee', index=True, readonly=True,
states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, default=_default_employee, track_visibility='onchange')
So my question is how to prevent this when it's the cron and set it to normal when it's in Form view?
The field "employé" should be empty here (in image below), because it is an allocation by tag.
You must loop over hr.employee because then you can do either of the following:
self.with_context({'default_employee_id': employee.id}).create(...)
OR
self.sudo(employee.id).create(...)

Inserting NULL in python is giving an error

I am trying to insert a null value via python and mysql connector.
update_query = ''' update people.names set fname=%s, lname=%s,address=%s where person_id = %s;'''
params = ['Bob', 'Jones', None, 507]
mysql_db.update_statement(update_query,params)
Gives the following error.
File "/usr/lib/python3/dist-packages/mysql/connector/connection.py", line 1536, in cmd_stmt_execute
result = self._handle_binary_result(packet)
File "/usr/lib/python3/dist-packages/mysql/connector/connection.py", line 1475, in _handle_binary_result
raise errors.get_exception(packet)
mysql.connector.errors.ProgrammingError: 1210 (HY000): Incorrect arguments to mysqld_stmt_execute
I have also tried params = ['Bob', 'Jones', 'Null', 507] But the 'Null' is a string and this is not what I need. I am trying to add NULL as NULL not as a string.
This isn't technically correct but it works for me. ["bob","jones","",507]when retrieving from the database python will recognise it at None which is Null. However in mysql it is an empty string.

Python-MySQL : removing single quotes around variable values in query while running db.execute(str, vars)

I am running this code
def details(self, dbsettings, payload):
res = None
with UseDatabase(dbsettings) as db:
sql = "select * from %(tablename)s where userid = %(userid)s"
result = db.run_query_vals(sql, payload)
res = result.fetchall()
return res
but get an error
SQLError: 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''statuser' where userid = '14'' at line 1
The arguments being passed are :
sql = "select * from %(tablename)s where userid = %(userid)s"
payload = {'tablename' : 'statuser', 'userid' : 14}
As far as I understand, the query being passed to MySQL is along the lines of
select * from 'statuser' where userid = '14'
which is where I get the error; the tablename isnt supposed to be enclosed in quotes. How do I have the name included without the quotes/make them backquotes?
(I don't want to hard-code the table name - this is a variable and is initialised according to different parameters during class creation). Any help here?
You can use the .format() from string in python:
def details(self, dbsettings, payload):
res = None
with UseDatabase(dbsettings) as db:
sql = "select * from {tablename} where userid = {userid}"
sql = sql.format(**payload)
# result = db.run_query_vals(sql, payload) # Method to run query
res = result.fetchall()
return res
I encountered the same problem in pymysql and have figured out a solution:
rewrite the escape method in class 'pymysql.connections.Connection', which obviously adds "'" arround your string.
don't know whether it will help in your case, just sharing a possible way
similiar question: How to remove extra quotes in pymysql
Here's my code:
from pymysql.connections import Connection, converters
class MyConnect(Connection):
def escape(self, obj, mapping=None):
"""Escape whatever value you pass to it.
Non-standard, for internal use; do not use this in your applications.
"""
if isinstance(obj, str):
return self.escape_string(obj) # by default, it is :return "'" + self.escape_string(obj) + "'"
if isinstance(obj, (bytes, bytearray)):
ret = self._quote_bytes(obj)
if self._binary_prefix:
ret = "_binary" + ret
return ret
return converters.escape_item(obj, self.charset, mapping=mapping)
config = {'host':'', 'user':'', ...}
conn = MyConnect(**config)
cur = conn.cursor()

cx_Oracle gives OCI-22062 invalid input string when calling function with bool parameter

I have a strange problem. When I try to call an oracle stored function using cursor.call_func method I receive an OCI-22062 exception. I'm using Python 3.2 and cx_Oracle 5.1.2. It generally looks like this:
This is the function header for package pkg_planista:
FUNCTION zatwierdz_plan (p_pl_id IN dz_plany.ID%TYPE,
p_czy_nowy_algorytm IN BOOLEAN DEFAULT FALSE,
p_czy_prz_grup IN BOOLEAN DEFAULT FALSE
) RETURN NUMBER;
This is what I do:
print(proc_name, retType, params, keyword_params)
res = self.cursor.callfunc(proc_name, retType, params, keyword_params)
This print above prints:
pkg_planista.zatwierdz_plan <class 'int'> [83, False] {}
And when the callfunc is executed, the OCI-22062 error is raised: cx_Oracle.DatabaseError: OCI-22062: invalid input string [False]
How should I pass the boolean parameter to make it work?
I had to find a workaround for this problem, because I couldn't find a way to make the callfunc work as it should.
In this workaround I use one temporary table that was created for some other purpose, but it's just what I need.
I changed callfunc to execute and made it like this:
self.cursor.execute("""
declare
result number;
begin
result := pkg_planista.zatwierdz_plan(:pid, :alg = 1);
INSERT INTO tmp_dz_v_planista_pracownicy values (result);
end;
""", {'pid': pl_id, 'alg': int(new_algorithm)})
ret = self.cursor.execute("SELECT * FROM tmp_dz_v_planista_pracownicy")
This way I can call the function and receive it's return value. The tmp_dz_v_planista_pracownicy table is just a temporary table with one column of type NUMBER.

Resources