martes, 9 de junio de 2009

Oracle Functions Decode y Sign

Estas funciones son bastante útiles cuando se necesita seleccionar un valor u otro dependiendo de cierta condición.
La función Decode trabaja como un IF-THEN-ELSE o como un IF-THEN-ELSEIF-ELSEIF-...-ELSE. Su sintaxis es:
decode( expression , search , result [, search , result]... [, default] )
En los parámetros, expression es el valor que queremos comparar y cada par search-result, funciona como un if, es decir, IF search THEN result. Mejor escribo un ejemplo:
SELECT decode( codigo_empresa, 100, 'Yahoo!', 200, 'Google', 300, 'Sun') FROM empresa;
Esto es como si hicieramos algo como:
if ( codigo_empresa == 100 ){
  echo 'Yahoo!';
}
else if( codigo_empresa == 200 ) {
  echo 'Google';
}
else if( codigo_empresa == 300 ) {
  echo 'Sun';
}
La función Sign nos devuelve un valor para indicar si un número es positivo, negativo o cero (0). Ejemplos:
SELECT sign(-5) from dual;   -- Devuelve -1
SELECT sign(1000) from dual;   -- Devuelve 1
SELECT sign(0) from dual;   -- Devuelve 0
SELECT sign(0.0001) from dual;   -- Devuelve 1
Ahora, cómo usar juntas estas funciones para obtener un gran provecho? Supongamos que tenemos dos tablas con estructuras similares, donde manejamos unos valores para diario, semanal y mensual. En una de las tablas manejamos los detalles y en la otra manejamos los "topes" máximos que se van a obtener al consultar.
SELECT
decode( sign( det.diario - tope.diario ), -1, det.diario, 0, det.diario, tope.diario ) AS diario
decode( sign( det.semanal - tope.semanal ), -1, det.semanal, 0, det.semanal, tope.semanal ) AS semanal
decode( sign( det.mensual - tope.mensual ), -1, det.mensual, 0, det.mensual, tope.mensual ) AS mensual
FROM detalle det, tope;
Este query nos va a traer el valor más bajo de las columnas diario, semanal y mensual entre la tabla Detalle y la tabla Tope.

viernes, 5 de junio de 2009

Control de cache en conexiones seguras

Hace poco me salía el siguiente error cuando descargaba un archivo por medio de un servlet desde un sitio seguro a través de Internet Explorer.



Extrañamente, en Firefox este error no se presentaba.
Después de buscar un rato (creo que fue más de un rato) a qué se debía el error, encontré que el problema parece ser un bug de IE (que raro!) y se genera por el uso de una etiqueta que había puesto en el encabezado de la respuesta. La etiqueta que yo tenía es:

Pragma: null

Internet Explorer tiene problemas con este encabezado cuando el sitio es seguro. La solución fue demasiado sencilla, eliminar la etiqueta en cuestión y utilizar la siguiente:

Cache-Control: private

En mi caso puse las cabeceras así :
response.setHeader("Cache-Control", "private");
response.setHeader("Pragma", null);
response.setHeader("Expires", null);
Y ahora sí funciona.

martes, 16 de diciembre de 2008

ORA-00942 La tabla o vista no existe (pero sí existe)

Este error nos ha salido a muchos y generalmente se presenta cuando realmente la tabla o vista no existe en el esquema en el que la queremos usar.

Pero otras veces se genera porque no se han dado los permisos necesarios hacia la tabla o vista, o porque estamos intentando referenciar la tabla sin su esquema (cuando pertenece a otro) y el motor de base de datos se confunde si hay otro objeto en la base de datos con el mismo nombre.

Sin embargo, aunque la tabla exista y aunque se tengan los permisos necesarios para acceder a ella y se use el nombre del esquema al que pertenece, todavía existe la posibilidad de que nos salga el error ORA-00942. Resulta que si los permisos sobre la tabla o vista son concedidos a un rol al que pertenece el usuario con el que necesitamos accederla, es posible ejecutar query's normalmente. Pero si estos query's se van a ejecutar dentro de un procedimiento almacenado, nos vamos a encontrar con ORA-00942.

Qué pasa?
Pues no sé si sea un bug en el RDBMS, o esa sea su forma de administrar los privilegios o tal vez es por razones de seguridad, pero para poder acceder a una tabla o vista desde un procedimiento almacenado, el usuario debe haber adquirido los privilegios directamente y no a través de un rol.

miércoles, 15 de octubre de 2008

Cómo ordenar colecciones de objetos en java

Ordenar colecciones de objetos en java es muy sencillo, basta con hacer un cambio en la clase de los objetos que se van a agregar en la colección.
El cambio consiste en implementar la interface java.lang.Comparable, la cuál nos obliga a implementar únicamente el método compareTo(Object objeto2) de forma que devuelva un entero. El valor devuelto por este método debe ser el equivalente a this - objeto2.

Ejemplo:
public class Articulo implements Comparable {

private String nombre;
private Double valor;

public String getNombre() {
  return nombre;
}

public void setNombre(String nombre) {
  this.nombre = nombre;
}

public Double getValor() {
  return valor;
}

public void setValor(Double valor) {
  this.valor = valor;
}

public int compareTo(Object objeto2) {
  Articulo articulo2 = (Articulo) objeto2;
  return this.valor.compareTo(articulo2.getValor());
}

}
En este ejemplo se hace uso del método ya implementado por parte de la clase Double, pero la implementación la pudimos haber hecho de la manera en que más nos convenga.

Después, suponiendo que articulos es una colección de Articulos, podemos hacer:
Collections.sort(articulos);
Y nuestra lista de artículos quedará ordenada.

martes, 7 de octubre de 2008

Internet Explorer y cookies remotas

Una vez más, problemas por culpa de Internet Explorer...

Resulta que a los "genios" desarrolladores del navegador (?) se les ocurrió una grandiosa idea: tratar a las cookies de un sitio cargado dentro de un iframe, como cookies de terceros.

Por ejemplo, tenemos el sitio www.sitiouno.com y dentro de este queremos cargar un iframe con el sitio www.sitiodos.com. El sitio sitiodos envía una cookie al navegador y cuando se ingresa de forma normal a él, el sitio tiene un funcionamiento normal. Al entrar a sitiouno con Internet Explorer se recibe la cookie, pero ésta es tratada como una cookie de terceros.

El problema de que la cookie sea tratada de esa manera es que regularmente los usuarios tienen configurado su IE en un nivel de privacidad medio, el cual "Bloquea cookies de terceros que no tienen una directiva de seguridad sólida". Este bloqueo consiste en que la cookie es eliminada, es decir, no es tomada en cuenta por el maravilloso navegador y generar una experiencia no muy grata para el usuario.

Para corregir este problema de forma rápida, basta con agregar una política de privacidad compacta en las cabeceras de la respuesta antes de intentar crear la cookie. La forma de hacer esto es:

En PHP:
<?php
header('P3P: CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT"');
?>
En Java:
httpservletresponse.setHeader("P3P","CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\"");
Cada conjunto de letras tiene un significado y uno lo puede adaptar según a la forma en la que quiera definir su política de privacidad. El anterior ejemplo funcionó muy bien para el problema que tuvimos nosotros, pero en http://www.w3.org/P3P/implementations.html se encuentra software para crear, editar y probar estas políticas a la medida de las necesidades. También está este formulario http://www.dmaresponsibility.org/PPG/, que genera la política según las respuestas que demos.

El problema se presenta de la versión 6 en adelante.

Bueno, ese fue el camino corto. El camino largo requiere permiso de cambiar la instalación de Apache para poder instalar mod_header y acceso a los archivos de configuración. Aunque la solución larga no la probamos, si encontramos bastante información:

Los seis pasos para crear y publicar la P3P:
http://www.w3.org/P3P/details.html

Estos son más detallados:
http://www.windley.com/archives/2008/08/p3p_and_internet_explorer.shtml
http://www.builderau.com.au/architect/webservices/soa/Implementing-privacy-preference-policies-with-P3P/0,339024590,320264989,00.htm

Acá les dejo más información:
http://www.w3.org/P3P/
http://www.webmasterworld.com/forum21/788.htm

miércoles, 24 de septiembre de 2008

Cómo incrementar el valor de una secuencia

El siguiente PL/SQL es útil para incrementar el valor de una secuencia al valor que deseemos:
DECLARE
i NUMBER(10);

BEGIN

  SELECT SQCUENTA.NEXTVAL INTO i FROM dual;

  WHILE i < 1000000 LOOP 
    SELECT SQCUENTA.NEXTVAL INTO i FROM dual; 
  END LOOP;
END;
Esto me ha funcionado en una BD Oracle 10g.

En PostgreSQL existe la función setval que permite hacer lo mismo.
SELECT setval('SQCUENTA', 1000000);

miércoles, 18 de abril de 2007

Esquema de autenticación de MySQL

Desde la versión 4.1.1 de MySQL el esquema de autenticación cambió para hacerlo más seguro. Desafortunadamente, este nuevo esquema no es compatible con las versiones anteriores (clientes anteriores a la versión 4.1.1) y cuando intentamos loguearnos nos dará el siguiente error:

Client does not support authentication protocol requested by server; consider upgrading MySQL client.

Si no es posible actualizar la versión del cliente con la que nos estamos conectando, podemos hacer una de las dos cosas siguientes, tener en cuenta que esto reducirá la seguridad de nuestro servidor de bases de datos:
mysql> SET PASSWORD FOR 'username'@'hostname' = OLD_PASSWORD('password');
ó
mysql> use mysql;
mysql> UPDATE user SET password = OLD_PASSWORD('password') WHERE user = 'username' AND host = 'hostname';
Se debe tener en cuenta que los cambios no harán efecto hasta que se haga
mysql> flush privileges;