SQL * Plus, @ y rutas relativas

9

De alguna manera, parece que SQL * Plus (al menos en Windows) no puede ubicar un script con una ruta relativa cuando se llama con @@y cuando la ruta comienza con un punto simple o doble.

Por ejemplo, en x:\some\whereTengo la siguiente estructura de directorios:

script.sql
main-dir\main-sub-dir
              call-script.sql
              script.sql

Es decir: dos script.sqlpero en diferentes lugares.

El contenido de script.sqljusto debajo x:\some\wherees simplemente

prompt SCRIPT root

mientras que el script.sqlcontenido del otro es

prompt SCRIPT main-dir/main-subdir

call-script.sql lee

@@script.sql
@ script.sql

Rendimiento esperado

Si inicio SQL * Plus desde x:\some\wherey luego hago un

@main-dir/main-sub-dir/call-scripts

La salida será

SCRIPT main-dir/main-subdir
SCRIPT root 

Esto se espera, ya que @se supone que el single busca rutas desde donde se inició SQL * Plus y @@se supone que busca rutas desde el directorio del script que lo contiene.

salida inesperada

Ahora , si cambio call-scripts.sqlasí:

@@./script.sql
@ ./script.sql

el doble @@parece cambiar su comportamiento, ya que busca rutas desde donde se inició SQL * Plus, y la salida ahora será

SCRIPT root
SCRIPT root

que no es lo que esperaba


¿Está este comportamiento documentado en alguna parte y, lo que es más importante, cómo debo cambiar call-scripts.sqlpara que llame @@../../other-dir/other-sub-dir/scriptcorrectamente a las rutas relativas ( )?

René Nyffenegger
fuente
¿Cuál es su variable de entorno SQLPATH establecida? Eso afecta a qué directorios se buscan.
Philᵀᴹ
Mismo comportamiento bajo Linux, FWIW. (Y un ampersand es &, no @; que no parece tener un nombre real ). Parece ser un error, ya que es inconsistente. Lo único que viene a la mente es establecer una variable en el script de nivel superior con la ruta completa y hacer todo en función de eso, pero eso no es muy conveniente a menos que la estructura de directorios a continuación sea fija.
Alex Poole
Gracias por señalar lo de @ vs ampersands ... Debería haberlo sabido, pero cuando escribí la publicación realmente no presté atención. Ahora está arreglado en el título.
René Nyffenegger
2
Acabo de atacar sqlplus con strace. Estas son las llamadas relevantes: pastebin.com/cVK1QQu4 Tenga en cuenta que no intentó realizar estadísticas ni acceder a los archivos "script.sql" en ningún otro directorio antes de intentar abrir los que se ven en la salida de pastebin.
Philᵀᴹ

Respuestas:

7

Sí, este es el error 2391334 que ha existido durante mucho tiempo, y probablemente no se solucionará en el futuro cercano.

Una forma de evitar esto es "conocer" la ruta de los scripts sin codificar realmente esa ruta. Hacer esto en SQLPlus requiere un truco: si intenta ejecutar un archivo inexistente, recibirá un mensaje de error que incluye el nombre de la ruta.

Así que aquí hay una demostración de eso en acción. Para imitar su escenario, tengo:

c:\temp\demo
   script.sql
   maindir
      subdir
         call_script.sql
         script.sql

Lo que podemos hacer es agregar algunos comandos al frente de call_script.sql que tomarán la ruta. Parece un poco extraño, pero no deberías necesitar cambiarlo, es solo una cosa fija que pegas

set termout off
spool _path_finder.sql
@@_nonexistent_script.sql
spool off;

var path varchar2(100);
set serverout on
declare
  output varchar2(1000) := regexp_replace(replace(q'{
@_path_finder.sql
}',chr(10)),'.*"(.*)".*','\1');
begin 
  :path:=substr(output,1,length(output)-24);
end;
/
col path new_val path
select :path path from dual;
set termout on

Lo que está sucediendo aquí, es que estamos ejecutando un script inexistente, que devuelve:

"SP2-0310: no se puede abrir el archivo" ruta \ _nonexistent_script.sql "

así que con un poco de expresión regular podemos extraer la ruta, almacenarla en una variable SQLPlus y luego usarla a partir de ese momento.

Entonces la versión final de su call_script.sql se vería así

set termout off
spool _path_finder.sql
@@_nonexistent_script.sql
spool off;

var path varchar2(100);
set serverout on
declare
  output varchar2(1000) := regexp_replace(replace(q'{
@_path_finder.sql
}',chr(10)),'.*"(.*)".*','\1');
begin 
  :path:=substr(output,1,length(output)-24);
end;
/
col path new_val path
select :path path from dual;
set termout on
prompt path was &path      

@@&path\script.sql
@&path\script.sql

y cuando corremos eso, obtenemos lo siguiente

SQL> @maindir\mainsubdir\call_script
path was maindir\mainsubdir
script in subdir
script in subdir

y ahí tienes :-)

Connor McDonald
fuente