Oracle

Oracle 的 UTL_FILE 在本地連接或通過偵聽器連接時具有不同的行為

  • November 8, 2014

我們有一個使用 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。

引用自:https://dba.stackexchange.com/questions/50965