Oracle 的 UTL_FILE 在本地連接或通過偵聽器連接時具有不同的行為
我們有一個使用 UTL_FILE 創建 CSV 文件的過程。(env 是 Oracle 11.2.0.1 和 RHEL 6.4)到目前為止,我們將該 CSV 文件儲存到系統
oracle
(oracle 所有者)使用者主目錄 (/home/oracle/csv/) 中。它工作正常,但現在我們需要將 CSV 文件儲存到不同的系統使用者主目錄(例如reports
係統使用者主目錄 (/home/reports/csv/))因此,我們將
oracle
使用者添加到該組reports
作為其次要組,然後將reports
首頁權限更改為他和他的所有組都可以訪問。# id oracle uid=500(oracle) gid=500(oracle) grupos=500(oracle),502(reports) # id reports uid=502(reports) gid=502(reports) grupos=502(reports) # chmod 770 /home/reports # ls -la /home/reports/ total 52 drwxrwx--- 8 reports reports 4096 oct 3 12:58 . drwxr-xr-x. 5 root root 4096 oct 2 11:05 .. drwxrwxrwx 2 reports reports 4096 oct 3 12:59 csv
有了這個,登錄到系統,
oracle
我可以寫入,讀取和執行文件到reports
’s home。# su - oracle oracle ~$ touch /home/reports/csv/test.txt oracle ~$ ls -la /home/reports/csv/test.txt total 8 -rw-rw-r-- 1 oracle oracle 0 oct 3 17:51 test.txt
現在,據我所知(甲骨文的文件說),這應該有效,但它沒有……根本沒有。 如果我用 sqlplus 在本地連接,它可以工作。但是如果我從遠端機器或通過監聽器連接,我不會!
我將向您展示: 與 sqlplus 的本地連接:
oracle ~$ export ORACLE_SID=MYDB oracle ~$ sqlplus -S informes Introduzca la contraseña: select a.directory_name, a.directory_path, b.grantee, b.privilege from all_directories a, all_tab_privs b where a.directory_name = b.table_name and DIRECTORY_NAME='CSVFOLDER'; DIRECTORY_NAME DIRECTORY_PATH GRANTEE PRIVILEGE ------------------------------ -------------------- --------- --------- CSVFOLDER /home/reports/csv INFORMES READ CSVFOLDER /home/reports/csv INFORMES WRITE show user USER es "INFORMES" declare output_fich utl_file.file_type; begin output_fich := utl_file.fopen('CSVFOLDER','testfile.csv','W'); utl_file.put_line (output_fich, 'test line'); utl_file.fclose(output_fich); end; / Procedimiento PL/SQL terminado correctamente. host ls -la /home/reports/csv/testfile.csv -rw-rw-r-- 1 oracle oracle 10 oct 3 18:20 /home/informesestados/tmp/testfile.csv host rm /home/reports/csv/testfile.csv
現在,讓我們再次嘗試通過偵聽器連接 讓我們看看我的 tnsnames 指向哪裡:
oracle ~$ cat $ORACLE_HOME/network/admin/tnsnames.ora | grep MYDB MYDB = (DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = FR-BD1-tmp)(PORT = 1522))) (CONNECT_DATA = (SERVICE_NAME = MYDB))) oracle ~$ ping FR-BD1-tmp PING fr-bd1-tmp (192.168.78.3) 56(84) bytes of data. 64 bytes from fr-bd1-tmp (192.168.78.3): icmp_seq=1 ttl=64 time=0.047 ms 64 bytes from fr-bd1-tmp (192.168.78.3): icmp_seq=2 ttl=64 time=0.025 ms ^C --- fr-bd1-tmp ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 1386ms rtt min/avg/max/mdev = 0.025/0.036/0.047/0.011 ms oracle ~$ /sbin/ifconfig | grep "inet addr" inet addr:192.168.78.3 Bcast:192.168.78.255 Mask:255.255.255.0 oracle ~$ lsnrctl services LISTENER_MYBD LSNRCTL for Linux: Version 11.2.0.1.0 - Production on 03-OCT-2013 18:33:04 Copyright (c) 1991, 2009, Oracle. All rights reserved. Conectándose a (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=fr-bd1-tmp)(PORT=1522))) Resumen de Servicios... El servicio "mydb" tiene 1 instancia(s). La instancia "mydb", con estado READY, tiene 1 manejador(es) para este servicio... Manejador(es): "DEDICATED" establecido:45 rechazado:0 LOCAL SERVER El servicio "mydb_node1" tiene 1 instancia(s). La instancia "mydb", con estado READY, tiene 1 manejador(es) para este servicio... Manejador(es): "DEDICATED" establecido:3 rechazado:0 estado:ready LOCAL SERVER El comando ha terminado correctamente oracle ~$ sqlplus -S informes@mydb Introduzca la contraseña: select a.directory_name, a.directory_path, b.grantee, b.privilege from all_directories a, all_tab_privs b where a.directory_name = b.table_name and DIRECTORY_NAME='CSVFOLDER'; DIRECTORY_NAME DIRECTORY_PATH GRANTEE PRIVILEGE ------------------------------ -------------------- --------- --------- CSVFOLDER /home/reports/csv INFORMES READ CSVFOLDER /home/reports/csv INFORMES WRITE show user USER es "INFORMES" declare output_fich utl_file.file_type; begin output_fich := utl_file.fopen('INFORMES','testfile.csv','W'); utl_file.put_line (output_fich, 'test line'); utl_file.fclose(output_fich); end; / declare * ERROR en línea 1: ORA-29283: operación de archivo no válida ORA-06512: en "SYS.UTL_FILE", línea 536 ORA-29283: operación de archivo no válida ORA-06512: en línea 4
現在,如果我將
reports
’s home 權限更改為所有人都可以訪問,則通過偵聽器連接的 UTL_FILE 過程可以正常工作!# chmod 777 /home/reports # ls -la /home/reports/ total 52 drwxrwxrwx 8 reports reports 4096 oct 3 12:58 . drwxr-xr-x. 5 root root 4096 oct 2 11:05 .. drwxrwxrwx 2 reports reports 4096 oct 3 12:59 csv # su - oracle oracle ~$ sqlplus -S informes@mydb Introduzca la contraseña: declare output_fich utl_file.file_type; begin output_fich := utl_file.fopen('CSVFOLDER','testfile.csv','W'); utl_file.put_line (output_fich, 'test line'); utl_file.fclose(output_fich); end; / Procedimiento PL/SQL terminado correctamente. host ls -la /home/reports/csv/testfile.csv -rw-rw-r-- 1 oracle oracle 10 oct 3 18:59 /home/informesestados/tmp/testfile.csv
???!!
我不明白為什麼會有這種行為。正如甲骨文的文件所說:
( http://docs.oracle.com/cd/B28359_01/appdev.111/b28419/u_file.htm )
在 UNIX 系統上,由 FOPEN 函式創建的文件的所有者是執行實例的影子程序的所有者
影子程序的所有者在兩種方法中都相同(即“oracle”系統使用者),所以,.. 為什麼會這樣?
有人有線索嗎?我錯過了什麼嗎?
正如 Colin ’t Hart 和 Phil 在評論中建議的那樣,我不得不重新啟動監聽器。這就是問題所在!
通過監聽器的新數據庫連接繼承了監聽器啟動時使用的環境變數、組等,因為新的 Oracle 程序是從監聽器派生的。組成員在 Linux 上的程序啟動時被記憶體
非常感謝,我整天都被困在這個問題上。
問候
更新
最近,我在使用調度程序執行的過程中遇到了類似的情況。在這種情況下,影子程序由作業協調程序 (CJQ0) 生成。在這裡您將需要重新啟動整個數據庫(您可以殺死 CJQ0,但隨後它是由程序監視器產生的,有同樣的問題)。
作為一種解決方法,您可以使用 linux 訪問控制列表
setfacl
。有了它,該過程無需重新啟動即可重新載入組成員資格。
這是 Unix 系統上的預期行為。程序的有效使用者 ID 允許讀/寫文件或其有效的主要或有效的輔助組 ID 允許讀/寫文件
如果您登錄到 unix 系統,則有效使用者 id、有效組 id 和有效輔助組 id 是為您在該系統上的登錄使用者定義的 id(在 /etc/passwd 或任何地方)。如果一個unix程序創建了一個新程序,那麼新程序會從創建程序繼承這個id。
Oracle 影子程序是對應於 oracle 會話的 unix 程序。如果您使用網路連接登錄數據庫,則此影子程序由偵聽器程序創建。如果您向 oracle unix 使用者添加新的輔助組 ID,這不會更改正在執行的偵聽器程序的有效 ID,因此所有新創建的程序都具有與添加新輔助之前創建的程序相同的有效使用者/組 ID團體。
但是,如果您在向 oracle 使用者添加新的輔助組 id 後登錄系統,您的登錄會話將有這個額外的有效輔助組 id,如果您停止並啟動偵聽器程序,它也會有額外的有效輔助組 id . 新的偵聽器程序創建的影子程序也是如此。
如果偵聽器程序在添加新的輔助組 id 後被另一個程序停止,但在添加新的輔助組 id 後該其他程序沒有重新啟動,則偵聽器不會將新的輔助組 id 作為有效的輔助組 id,即使他被重新啟動,因為啟動偵聽器程序的程序沒有新的輔助 id 作為有效的輔助 id。如果您通過某個集群程序重新啟動偵聽器程序,則可能會發生這種情況。在這種情況下,必須先重新啟動此集群程序,然後才能重新啟動偵聽器程序,以使新添加的輔助組 id 生效。
如果您使用 sqlplus 進行本地連接,則影子程序由 sqlplus 生成,而不是由偵聽器程序生成。但是 sqlplus 程序具有由您的登錄會話繼承的有效使用者/組 ID。那麼您在添加新的輔助組後是否登錄,那麼影子程序將具有此輔助組 ID。如果在將輔助組添加到 oracle 使用者之前已經建立了登錄,那麼您的 unix 會話的所有新本地 sqlplus 連接(=影子程序)也不會將此附加輔助組 ID 作為有效輔助組 ID。