i would like to create a list with data from ldap directory.
I want to be able to display this list and go through this list to select a user from the list.
I have already read about pipeline functions that can return a list (can I as well retrieve data from ldap with such a function?). Another function can then be used to start a search through the list.
I haven't had much to do with ldap so far and would be grateful for any hints and yes in our system we use ldap for authentication.
create object type:
create or replace type ldap_user_ot as object
(email varchar2(30),
username varchar2(30),
firstname varchar2(256),
lastname varchar2(256),
uuid number);
create nested table type:
create or replace type ldap_user_nt as table of ldap_user_ot;
create table function
create or replace function get_user_from_ldap (
p_container IN VARCHAR2 DEFAULT ',ou=users,dc=my-company,dc=en')---what to enter here???
return ldap_user_nt
PIPELINED
IS
l_retval PLS_INTEGER;
l_session dbms_ldap.SESSION;
l_attrs dbms_ldap.string_collection;
l_message dbms_ldap.message;
l_entry dbms_ldap.message;
l_attr_name VARCHAR2(256);
l_ber_element dbms_ldap.ber_element;
l_vals dbms_ldap.string_collection;
l_user ldap_user_ot:=ldap_user_ot(NULL,NULL,NULL,NULL,NULL);
begin
--IF p_ad_group IS NOT NULL THEN lv_retval := -1; ----what to enter here? do I need if statement since I want to return all users
-- Choose to raise exceptions.
dbms_ldap.use_exception := TRUE;
-- Connect to the LDAP server.
l_session := dbms_ldap.Init(hostname => l_ldap_host,
portnum => l_ldap_port)
;
l_retval := dbms_ldap.Simple_bind_s(ld => l_session, dn => v_un,
passwd => p_password);
-- Get all attributes
l_attrs(1) := '*';
l_retval := dbms_ldap.Search_s(ld => l_session, base => l_ldap_base, scope
=>
dbms_ldap.scope_subtree,
filter => '(&(objectclass=posixAccount)(uid='
||p_user_name
||'))', attrs => l_attrs, attronly => 0, res => l_message);
IF dbms_ldap.Count_entries(ld => l_session, msg => l_message) > 0 THEN
v_tableresult := 1;
-- Get all the entries returned by our search.
l_entry := dbms_ldap.First_entry(ld => l_session, msg => l_message);
<< entry_loop >>
WHILE l_entry IS NOT NULL LOOP
-- Get all the attributes for this entry.
dbms_output.Put_line('---------------------------------------');
l_attr_name := dbms_ldap.First_attribute(ld => l_session,
ldapentry => l_entry,
ber_elem => l_ber_element);
<< attributes_loop >>
WHILE l_attr_name IS NOT NULL LOOP
-- Get all the values for this attribute.
l_vals := dbms_ldap.Get_values (ld => l_session,
ldapentry => l_entry,
attr
=>
l_attr_name
);
<< values_loop >>
FOR i IN l_vals.first .. l_vals.last LOOP
CASE l_attr_name
WHEN 'mail' THEN
v_email := Substr(L_vals(i), 1, 200);
WHEN 'givenName' THEN
v_firstname := Substr(L_vals(i), 1, 200);
WHEN 'uid' THEN
v_username := Substr(L_vals(i), 1, 200);
WHEN 'sn' THEN
v_lastname := Substr(L_vals(i), 1, 200);
ELSE
dbms_output.Put_line('ATTIBUTE_NAME: '
|| l_attr_name
|| ' = '
|| Substr(L_vals(i), 1, 200));
END CASE;
END LOOP values_loop;
l_attr_name := dbms_ldap.Next_attribute(ld => l_session,
ldapentry => l_entry,
ber_elem => l_ber_element);
END LOOP attibutes_loop;
l_entry := dbms_ldap.Next_entry(ld => l_session, msg => l_entry);
PIPE ROW(l_user);
END LOOP entry_loop;
END IF;
l_retval := dbms_ldap.Unbind_s(ld => l_session);
end if;
return;
EXCEPTION WHEN no_data_needed THEN
-- unbind from the directory
l_retval := dbms_ldap.Unbind_s(ld => l_session);
return;
WHEN others THEN
IF l_session IS NOT NULL THEN
l_retval := dbms_ldap.Unbind_s(ld => l_session);
END IF;
RAISE;
END get_user_from_ldap;
Related
Note
I want to query page item name and send the corresponding items value into a procedure. I can get the item name but couldn't get the value of it. At first I use the following code:
begin
for j in (select item_name from UTLITMINF where service_id ='abc' ) loop
val := val || ':' || j.item_name; --items name
END LOOP;
/* exe := ' begin
dynamic_api_call(p_service => :ser,
p_par => :v_val, --items value to need to send
o_result_json => :v_l_response_text);
end; ';
execute immediate exe
using IN ser,
in val,
out l_response_text;*/
begin
dynamic_api_call(p_service => 'abc',
p_par => val, --items name from page queried from table and send its value to procedure
o_result_json => l_response_text);
end;
raise_application_error(-20001,l_response_text);
end;
In val parameter it contains P11_CUSTOMER. But the value of it did not pass through the procedure. How can I get the value of it? Suggest me if i need to improve my code.
You can use the V (short for value) and NV (short for numeric value) function for dynamic item names. Try something like this (you'll need to adjust on your end).
declare
l_response_text varchar2(255);
l_ser varchar2(255) := 'abc';
l_item_name varchar2(255);
begin
select item_name
into l_item_name
from UTLITMINF
where service_id = l_ser;
dynamic_api_call(
p_service => l_ser,
p_par => v(l_item_name),
o_result_json => l_response_text
);
end;
Try the dynamic sql like the below
begin
for j in (select item_name from UTLITMINF where service_id ='abc' ) loop
val := val || ':' || j.item_name; --items name
END LOOP;
exe := ' begin
dynamic_api_call(p_service => :ser,
p_par => :v_val, --items value to need to send
o_result_json => :v_l_response_text);
end ;';
execute immediate exe
using ser,
val,
OUT l_response_text;
raise_application_error(-20001,l_response_text);
I'm currently trying to implement a similar version of oracle's APEX_MAIL package. I have everything working, but I can't make the job work unless I modify it.
The job APEX_MAIL uses is called ORACLE_APEX_MAIL_QUEUE
BEGIN
DBMS_SCHEDULER.set_attribute( name => '"APEX_040000"."ORACLE_APEX_MAIL_QUEUE"', attribute => 'job_action', value => 'APEX_040000.WWV_FLOW_MAIL.PUSH_QUEUE');
DBMS_SCHEDULER.set_attribute( name => '"APEX_040000"."ORACLE_APEX_MAIL_QUEUE"', attribute => 'number_of_arguments', value => '2');
DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE(
job_name => '"APEX_040000"."ORACLE_APEX_MAIL_QUEUE"',
argument_position => 1,
argument_value => '');
DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE(
job_name => '"APEX_040000"."ORACLE_APEX_MAIL_QUEUE"',
argument_position => 2,
argument_value => '');
END;
/
So I go to the package to see what the code does. I'm was assuming push queue would send out emails in the queue. Instead, it calls the same job again!
PROCEDURE PUSH_QUEUE( P_SMTP_HOSTNAME IN VARCHAR2 DEFAULT NULL,
P_SMTP_PORTNO IN VARCHAR2 DEFAULT NULL )
IS
BEGIN
PUSH_QUEUE_BACKGROUND;
END PUSH_QUEUE;
PROCEDURE PUSH_QUEUE_BACKGROUND
IS
BEGIN
SYS.DBMS_SCHEDULER.RUN_JOB( JOB_NAME => 'ORACLE_APEX_MAIL_QUEUE', USE_CURRENT_SESSION => FALSE );
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE <> -27478 THEN
RAISE;
END IF;
END PUSH_QUEUE_BACKGROUND;
So basically this job does nothing, but I switch it to call PUSH_QUEUE_IMMEDIATE which does what I think it should do.
PROCEDURE PUSH_QUEUE_IMMEDIATE( P_FORCE_YN IN VARCHAR2 DEFAULT 'N')
IS
L_STATUS NUMBER;
L_LOCK_HDL VARCHAR2(128);
E_DB_SHUTDOWN EXCEPTION;
PRAGMA EXCEPTION_INIT(E_DB_SHUTDOWN, -1089);
BEGIN
WWV_FLOW_DEBUG.ENABLE_DBMS_OUTPUT;
SYS.DBMS_LOCK.ALLOCATE_UNIQUE( LOCKNAME => 'APEX_MAIL_QUEUE_LOCK', LOCKHANDLE => L_LOCK_HDL);
L_STATUS := SYS.DBMS_LOCK.REQUEST( LOCKHANDLE => L_LOCK_HDL,
LOCKMODE => SYS.DBMS_LOCK.X_MODE,
TIMEOUT => 0,
RELEASE_ON_COMMIT => FALSE );
WWV_FLOW_DEBUG.INFO('APEX Mail Lock status: ' || L_STATUS );
IF L_STATUS = 0 THEN
FOR C1 IN ( SELECT ID, MAIL_SEND_COUNT, LAST_UPDATED_ON
FROM WWV_FLOW_MAIL_QUEUE
ORDER BY MAIL_SEND_COUNT, LAST_UPDATED_ON) LOOP
BEGIN
WWV_FLOW_DEBUG.INFO( 'Pushing email: ' || C1.ID );
IF (C1.MAIL_SEND_COUNT = 0) OR (NVL(P_FORCE_YN,'N') = 'Y') OR
(C1.MAIL_SEND_COUNT > 0 AND (POWER(2,C1.MAIL_SEND_COUNT)/(60*24) + C1.LAST_UPDATED_ON) < SYSDATE) THEN
BACKGROUND( P_ID => C1.ID );
END IF;
WWV_FLOW_DEBUG.INFO( 'Pushed email: ' || C1.ID );
EXCEPTION
WHEN OTHERS THEN
WWV_FLOW_DEBUG.LOG_EXCEPTION;
IF L_LOCK_HDL IS NOT NULL THEN
L_STATUS := SYS.DBMS_LOCK.RELEASE( L_LOCK_HDL );
WWV_FLOW_DEBUG.INFO('APEX Mail released lock' );
END IF;
END;
END LOOP;
END IF;
IF L_LOCK_HDL IS NOT NULL THEN
L_STATUS := SYS.DBMS_LOCK.RELEASE( L_LOCK_HDL );
WWV_FLOW_DEBUG.INFO('APEX Mail released lock' );
END IF;
EXCEPTION WHEN E_DB_SHUTDOWN THEN
NULL;
END PUSH_QUEUE_IMMEDIATE;
I'm trying to copy APEX_MAIL to a point, but if I do, I won't have a working job. Can anyone point out if APEX_MAIL changes what the job does after an application setting change or any other change?
Thanks in advance!
APEX_MAIL.PUSH_QUEUE is usable in your own code to send your mail (in the queue) out immediate. The job normally calls PUSH_QUEUE_IMMEDIATE. I don't know if your setting ever was a bug in the installation or something wrong on your site.
Thus fact, it calls PUSH_QUEUE_IMMEDIATE in a separate session as APEX_040000 job.
Since everyone can request an immediate send of all the jobs in the queue, it makes sure via SYS.DBMS_LOCK.REQUEST only one session will actually do the transmit.
Just calling this directly from the editor (Toad). No idea why getting the above error having checked the function definition and variable types repeatedly. All of the information available online seems to be for stored procedures - which I don't believe are used here
DECLARE
TYPE attrs_type is VARRAY(10) of STRING(10);
l_ldap_host VARCHAR(255) := 'SERVERNAME';
l_ldap_port INT := 389;
l_ldap_user VARCHAR(255) := 'USERNAME';
l_ldap_passwd VARCHAR(255) := 'PASSWORD';
l_ldap_base VARCHAR(255) := 'l=something,dc=something';
l_session DBMS_LDAP.session;
l_retval NUMBER;
l_entry VARCHAR(255);
l_attrs attrs_type;
l_message VARCHAR(255) := null;
l_filter VARCHAR(255) := 'objectclass=*';
BEGIN
l_session := DBMS_LDAP.init(hostname => l_ldap_host,
portnum => l_ldap_port);
l_retval := DBMS_LDAP.simple_bind_s(ld => l_session,
dn => l_ldap_user,
passwd => l_ldap_passwd);
l_attrs(1) := '*'; -- retrieve all attributes
l_retval := DBMS_LDAP.search_s(
ld => l_session,
base => l_ldap_base,
scope => DBMS_LDAP.SCOPE_SUBTREE,
filter => l_filter,
attrs => l_attrs,
attronly => 0,
res => l_message);
DBMS_OUTPUT.put_line(l_message);
-- code to do stuff
EXCEPTION
WHEN OTHERS THEN DBMS_OUTPUT.put_line (SQLERRM);
END
You have to define l_attrs as dbms_ldap.string_collection, not your own type. Even if your type is defined in the same way, it will not be interchangeable with another apparently-similar type. To Oracle, your attrs_type is not the same as string_collection. Hence the error you're getting - you are indeed using the wrong type for that argument.
From the documentation:
A collection type defined in a package specification is incompatible with an identically defined local or standalone collection type.
I am using the following code block to update my front end segment.
But when i am compiling this
DECLARE
p_person_id NUMBER := NULL;
p_business_group_id NUMBER;
p_id_flex_num NUMBER;
------------------
p_analysis_criteria_id NUMBER := NULL;
p_person_analysis_id NUMBER := NULL;
p_per_object_version_number NUMBER := NULL;
v_err VARCHAR2 (1000) := NULL;
p_medical_id VARCHAR2 (100) := NULL;
BEGIN
FOR i IN (SELECT *
FROM xx_hr_upload_master_data_new
WHERE person_id IS NOT NULL
AND business_group_id IS NOT NULL
AND medical_id IS NOT NULL)
LOOP
p_id_flex_num := 50530;
BEGIN
SELECT sv.segment1, NVL (sit.object_version_number, 1),
sit.analysis_criteria_id, MAX (sit.person_analysis_id)
INTO p_medical_id, p_per_object_version_number,
p_analysis_criteria_id, p_person_analysis_id
FROM fnd_id_flex_structures_tl sttl,
fnd_id_flex_structures st,
per_person_analyses sit,
per_analysis_criteria sv
WHERE sttl.id_flex_structure_name = 'ISPs Medical Data'
AND sttl.LANGUAGE = USERENV ('LANG')
AND st.id_flex_code = sttl.id_flex_code
AND st.id_flex_num = sttl.id_flex_num
AND st.id_flex_num = sit.id_flex_num
AND st.id_flex_num = sv.id_flex_num
AND sit.analysis_criteria_id = sv.analysis_criteria_id
AND sit.person_id = i.person_id
--and sv.SEGMENT1 = '4602001140'
GROUP BY sv.segment1,
NVL (sit.object_version_number, 1),
sit.analysis_criteria_id;
EXCEPTION
WHEN OTHERS
THEN
p_medical_id := NULL;
p_per_object_version_number := 1;
p_person_analysis_id := NULL;
p_analysis_criteria_id := NULL;
p_person_analysis_id := NULL;
p_per_object_version_number := NULL;
p_person_id := NULL;
END;
BEGIN
------------------------------------------------
IF (p_medical_id IS NULL AND p_analysis_criteria_id IS NULL)
THEN
-- create a new record in the SIT (Special Information Type)table .
p_person_id := i.person_id;
p_medical_id := TO_CHAR (i.medical_id);
-----------------------------
hr_sit_api.create_sit
(p_validate => FALSE,
p_person_id => p_person_id,
p_business_group_id => i.business_group_id,
p_id_flex_num => p_id_flex_num,
p_effective_date => TRUNC (SYSDATE),
p_date_from => TRUNC (SYSDATE),
p_segment1 => p_medical_id,
p_analysis_criteria_id => p_analysis_criteria_id,
p_person_analysis_id => p_person_analysis_id,
p_pea_object_version_number => p_per_object_version_number
);
ELSE
-- employee has previous Billing_Acc_Num then update that number in the SIT table .
hr_sit_api.update_sit
(p_validate => FALSE,
p_person_analysis_id => p_person_analysis_id,
p_pea_object_version_number => p_per_object_version_number,
p_date_from => TRUNC (SYSDATE),
p_segment1 => p_medical_id,
p_analysis_criteria_id => p_analysis_criteria_id
);
END IF;
UPDATE xx_hr_upload_master_data_new xx
SET xx.error_msg = 'Done',
xx.emp_data = 'Done'
WHERE xx.business_group_id = i.business_group_id
AND xx.employee_number = i.employee_number;
EXCEPTION
WHEN OTHERS
THEN
p_analysis_criteria_id := NULL;
p_person_analysis_id := NULL;
p_per_object_version_number := NULL;
p_person_id := NULL;
p_medical_id := NULL;
v_err := NULL;
v_err := SQLERRM;
UPDATE xx_hr_upload_master_data_new xx
SET xx.error_msg = v_err
WHERE xx.business_group_id = i.business_group_id
AND xx.employee_number = i.employee_number;
END;
END LOOP;
COMMIT;
END;
Now when the select query is not fetching anything, the exception block is entered where all the variables are initiated to null.
I want that after this the begin where the create api is called inside the condition IF (p_medical_id IS NULL AND p_analysis_criteria_id IS NULL)
should be entered. But this is not happening and the program exits after this exception block after entering the exception block.
A hint might be:
The first select will not raise any exception if it gets no result. If it gets no result then the for-loop just won't be entered, so the only thing that will happen is the commit.
(The select inside the for-loop will raise an exception if it gets 0 rows or more than 1 row. The reason is 'select ... into ...': When using 'into' the select must return exact 1 row.)
In production every now and then we get an ORA-0001 error when dequeuing a message. We use:
Solaris 10
Oracle 10g
C++ application
PROC*C with dbms_aq package for queuing operations
Queues with XML payload
We handle a large volume of messages (1K/min).
Any clue why would a dequeue result in an ORA-0001 (unique constraint) error?
Update: adding code per request.
EXEC SQL EXECUTE
DECLARE
message_properties dbms_aq.message_properties_t;
dequeue_options dbms_aq.dequeue_options_t;
message_payload xmltype;
tmpclob clob;
dynamic_sql_string varchar2(512);
BEGIN
dequeue_options.wait := :iReadTimeout;
dequeue_options.dequeue_mode := dbms_aq.REMOVE;
dequeue_options.visibility := dbms_aq.ON_COMMIT;
IF :iBuffered = 1 then
dequeue_options.delivery_mode := dbms_aq.buffered;
dequeue_options.visibility := dbms_aq.immediate;
:iNavigationMode := 0;
END IF;
IF :iDequeueOnly = 1 and :iQueueType <> 1 THEN
dequeue_options.dequeue_mode := dbms_aq.REMOVE_NODATA;
dequeue_options.wait := dbms_aq.NO_WAIT;
dequeue_options.msgid := hextoraw(:pszDequeueMsgId);
ELSE
IF :iNavigationMode = 0 THEN
dequeue_options.navigation := dbms_aq.FIRST_MESSAGE;
ELSE
dequeue_options.navigation := dbms_aq.NEXT_MESSAGE;
END IF;
END IF;
dequeue_options.deq_condition := :pszDeqCondition;
dbms_aq.dequeue(queue_name => :pszQueueName,
message_properties => message_properties,
dequeue_options => dequeue_options,
payload => message_payload,
msgid => :msgid );
IF dequeue_options.dequeue_mode <> dbms_aq.REMOVE_NODATA THEN
EXECUTE IMMEDIATE dynamic_sql_string USING OUT tmpclob, IN message_payload;
:gpoXmlClob := tmpclob;
ELSE
:gpoXmlClob := message_payload.getclobval();
END IF;
END;