lunes, 29 de noviembre de 2010

"Run As -> m2 Maven build" no funciona en Eclipse Helios

Puede que hayas llegado hasta aquí buscando alguna de las siguientes expresiones en un buscador:
  • No puedo ejecutar Maven en Eclipse Helios
  • No funciona maven en Eclipse Helios
  • Maven no hace nada en Eclipse Helios
  • Unable to start any of the Maven goals from Eclipse Helios
  • org.eclipse.core.runtime.CoreException: Local configuration cannot be nested in a directory
En efecto, el síntoma es que no se ejecuta "Maven build", "Maven package", "Maven clean" ni ningún otro goal del menú contextual "Run As" de m2e. Tras seleccionar la opción, no sucede nada ni se ve nada por consola. La ventana "Error Log" muestra: org.eclipse.core.runtime.CoreException: Local configuration cannot be nested in a directory.

Es un bug conocido de m2eclipse: https://issues.sonatype.org/browse/MNGECLIPSE-2191

La única solución que he encontrado hasta el momento es crear una configuración específica para cada proyecto y operación que quiero hacer. Estas configuraciones se pueden llamar desde el icono "Run As" de la barra de herramientas (el símbolo "play")


martes, 9 de noviembre de 2010

Firefox cumple seis años



Hoy, 9 de Noviembre, Firefox, el segundo navegador más usado, cumple seis años. Con una importante lista de premios a la espalda, es sin duda el navegador más personalizable que existe, el más seguro, y el menos "contaminado" por intereses comerciales, ya que detrás del mismo está la Fundación Mozilla, una fundación sin ánimo de lucro dedicada al software libre e innovación en internet.

El año que viene, veremos en su próxima versión (nº 4), importantes mejoras. Y además, ya está disponible (en versión beta) una versión para ciertos móviles.

Si no conoces la familia de productos Mozilla, te invito desde aquí a probar alguno de sus productos (todos gratuitos), especialmente Thunderbird con Lightning, la combinación de cliente de correo con calendario mejor que existe, con todas las ventajas de firefox: rapidez, seguridad, personalización (extremo a destacar especialmente)... y por supuesto, también gratis.

Firefox está disponible en más de 70 idiomas para Linux, Mac OS, y Windows.


Referencias y más información:

sábado, 16 de octubre de 2010

Control del nivel de aislamiento transaccional en JPA

La única ventaja de jugar con fuego es que aprende uno a no quemarse.

- Oscar Wilde (1854-1900)

Breve introducción al aislamiento

El aislamiento es una de las propiedades fundamentales ACID que definen las transacciones como tales en sistemas de gestión de bases de datos. El aislamiento define cómo y cuándo se ven los cambios realizados por una determinada operación por otras operaciones concurrentes.

El estándar ANSI/ISO SQL define cuatro niveles de aislamiento transaccional en función de tres hechos problemáticos que deben ser tenidos en cuenta entre transacciones concurrentes. Estos hechos no deseados son:

lectura "sucia"
Una transacción lee datos escritos por una transacción no confirmada [1]. Es decir, se leen datos temporales que no existirán finalmente porque la transacción que los creó se canceló finalmente tras la lectura.
lectura no repetible
Una transacción vuelve a leer datos que previamente había leído y encuentra que han sido modificados por una transacción confirmada.
lectura "fantasma"
Una transacción vuelve a ejecutar una consulta, devolviendo un conjunto de filas que satisfacen una condición de búsqueda y encuentra que otras filas que satisfacen la condición han sido insertadas o borradas por otra transacción cursada. La inconsistencia ocurre cuando una segunda transacción accede repetidas veces a una fila y lee datos diferentes cada vez.

Como comentaba, los niveles de aislamiento se definen en función de los efectos no deseados que evita. Así, el estándar ANSI/SQL define los siguientes cuatro niveles de aislamiento, de menor aislamiento a mayor:


lectura sucia
(dirty reads)
lectura no repetible / doble actualización
(non-repeatable reads / lost-update)
lectura fantasma
(phantom reads)
lectura no confirmada
READ_UNCOMMITTED
Se produce efecto
Se produce efecto
Se produce efecto
lectura confirmada
READ_COMMITTED
No se produce efecto
Se produce efecto
Se produce efecto
Iectura repetible
REPEATABLE_READ
No se produce efecto
No se produce efecto
Se produce efecto
secuenciable
SERIALIZABLE
No se produce efecto
No se produce efecto
No se produce efecto

Las transacciones tienen aislamiento cuando no interfieren entre sí, especialmente aquellas que aún no han concluído y están incompletas. El nivel de aislamiento es inversamente proporcional al rendimiento final en la medida en que que cuanto mayor es el aislamiento mayores son los recursos del sistema utilizados para garantizarlo. Esto se hace patente con un alto grado de concurrencia: a un nivel de aislamiento mayor, menor rendimiento.

La mayoría de las bases de datos usan un el nivel de aislamiento READ_COMMITED por defecto. Para accesos clásicos a la base de datos usando un patrón DAO y gestionando las conexiones directamente (sin usar JPA) esto es prácticamente lo único que necesitamos, ya que podemos seleccionar un nivel de aislamiento mayor en caso que lo necesitemos en la conexión usando setTransactionIsolation().


Con JPA las cosas son bastante diferentes

Hay que tener en cuenta que las implementaciones de JPA incluyen una caché de objetos que introduce elementos nuevos y desconcertantes frente las aplicaciones clásicas.

Otras aplicaciones acceden a los datos
Nuestra aplicación puede convivir con otras aplicaciones JPA que no usen la misma Unidad o Contexto de Persistencia (otro módulo, otro servidor) o que accedan a los datos directamente sin un framework JPA de por medio. Esto causará lecturas no repetibles, es decir, que ambas aplicaciones sobreescriban los mismos datos simultáneamente. En definitiva, el framework no realiza un refresco automático de un objeto antes de realizar un merge() (si lo hiciese, sería costoso ya que ejecutaría una sentencia SELECT anterior a cualquier otra). El tema del "refresco" de la caché es un tema aparte que merecerá un articulo aparte, en su momento.

Objetos caducados
Incluso aunque todas las aplicaciones usen el mismo contexto de persistencia y la misma caché de objetos, es frecuente que dos threads modifiquen el mismo objeto (incluso aunque sean propiedades diferentes) y realicen una transacción simultánea sobreescribiendo los cambios de otra, con lo que podemos tener que "se pierden" cambios de una propiedad. Por ejemplo:
  1. La transacción A lee la fila x.
  2. La transacción B lee la misma fila x.
  3. La transacción A escribe la fila x.
  4. La transacción B escribe la fila x (y sobreescribe los cambios realizados por A).
  5. Ambos confirman la transacción con éxito.
Este efecto se conoce típicamente como actualización perdida (Lost update). Con un nivel de aislamiento SERIALIZABLE esto no ocurriría, ya que la transacción B se quedaría bloqueada esperando hasta que la transacción A realizara la confirmación (commit) o no esperaría y simplemente fallaría (dependiendo de si se emplea un NO_WAIT en el tipo de lock). Sin embargo, en una aplicación web típica, esto seguiría generando un conflicto aún con un nivel SERIALIZABLE, ya que una aplicación web leería (y presentaría) primero los datos en una transacción y los actualizaría en otra).

Otro casos parecidos a éste y con consecuencias similares son:
  • un thread borra un objeto (delete()) y otro realiza un persist() sobre él. En ambos casos tendremos información inconsistente: o no se ha borrado, y tenemos dos, o no se ha grabado y no tenemos ninguno.
  • un objeto tiene relaciones con otros objetos hijos. Las modificaciones en las listas de sus hijos relacionados puede causar igualmente información inconsistente con respecto a los hijos.

Mecanismos de control de concurrencia
Para evitar estos problemas existen los mecanismos de control de concurrencia, que se encargan de mantener el aislamiento transaccional. Existen múltiples métodos de control de concurrencia que se pueden agrupar en tres categorías:
  • Optimistic. No realiza bloqueos, cancelando la confirmación y revertiendo los cambios (rollback) en caso de que se detecte un conflicto con otras transacciones. Como su propio nombre indica, asume que las transacciones pueden avanzar y realiza las comprobaciones de modificación al final, justo antes de realizar la confirmación de la transacción, asegurándose que los datos no han cambiado desde que se leyeron. Previene "lost updates".
  • Pessimistic. Se adquieren bloqueos sobre los objetos que se van a editar (típicamente las implementaciones lo realizan meidante sentencias SELECT ... FOR UPDATE). Este enfoque equivale a un nivel de aislamiento SERIALIZABLE.
  • Semi-optimistic. Es una mezcla de ambos realizando bloqueos sólo en determinadas situaciones.

Mecanismos de control de concurrencia en JPA

Por defecto, las implementaciones (persistence provider) de JPA asumen que la aplicación es responsable de la consistencia de datos y, por tanto, no realizan ningún comportamiento por defecto relativo a bloqueos. Como he comentado, trabajando directamente con la conexión de base de datos, podemos establecer un nivel de aislamiento y controlar la concurrencia, pero en JPA no manejamos directamente la conexión, sino que trabajamos con un gestor de entidades (EntityManager). Entonces, ¿cómo hacemos para gestionar la concurrencia si no podemos establecer un nivel de aislamiento?. Y aunque pudiéramos, ¿como resolvemos los problemas añadidos inherentes a JPA como los objetos caducados?. Vamos a ello.

Optimistic locking
JPA soporta optimistic locking mediante un campo versionado de bloqueo, definido por la anotación @Version. Dicho campo se actualiza automáticamente por la implementación JPA en cada actualización y debe ser conservado tal cual por la aplicación. En el momento de la realización del merge(), si se detecta un bloqueo o cambio, se lanza una excepción OptimisticLockException.
@Entity
public class Debt {
    @Id
    private long id;
    @Version
    private long version;
    //...
}

Bloqueos específicos de lectura y escritura
Algunas veces es deseable bloquear algo que no vas a cambiar. Normalmente se hace cuando vas a realizar un cambio sobre un objeto que se base en el estado de otro, y deseas asegurar que éste último no cambia mientras dura la transacción. JPA soporta bloqueos de lectura y escritura a través del método EntityManager.lock(entity, lockMode). El argumento lockMode puede ser READ o WRITE.

Si una transacción llama a lock(entity,LockModeType.READ) sobre un objeto versionado nos aseguraremos de que no se realizará ninguna lectura sucia ni lectura no repetible. Es decir, se asegura de que el objeto no ha cambiado antes de hacer el commit. En caso contrario, se lanzará una OptimisticLockException. Por ejemplo, en un método evaluamos en una condición un atributo de un
objeto y, en función del valor, realizamos una modificación de otro objeto.
    ut.begin();
    Debt d = em.find(Debt.class,5);
    em.lock(d,LockModeType.READ);
    if ( d.getAmount() < 10 ) 
        throw new MinAmountExceedException();
    ut.commit();

Si una transacción llama a lock(entity,LockModeType.WRITE) nos aseguraremos de que no se realizará ninguna lectura sucia ni lectura no repetible, ni otro objeto está realizando un lock. En caso contrario, se lanzará una OptimisticLockException. El bloqueo WRITE puede usarse también para proporcionar bloqueos a nivel de objeto y sus objetos dependientes, es decir, bloquear (aunque deberíamos decir "detectar") cambios en relaciones, de forma que un cambio en una lista de objetos hijos fuerce el incremento del número de versión del objeto padre.


En definitiva, el bloqueo READ comprueba el optimistic version field (campo de versionado), y el bloqueo WRITE lo comprueba y lo incrementa. Este tipo de bloqueos es justo lo que se obtiene con un nivel de aislamiento serializable pero de forma optimista, es decir, sin riesgos de deadlock o bloqueos abiertos, ya que estamos hablando de comprobaciones" no de bloqueos efectivos como tales.


Pessimistic locking
Pessimistic locking significa adquirir un bloqueo sobre el objeto antes de comenzar a editarlo y equivale a un nivel de aislamiento SERIALIZABLE. Es realmente bloquear, no es una simple comprobación como en el bloqueo optimista. Se implementa típicamente con una sentencia SELECT ... FOR UPDATE. El bloqueo pesimista no está incluido en JPA 1.0, aunque algunas implementaciones sí lo hacen. Si usamos JPA 1.0 (por ejemplo usando la implementación por defecto de Glassfish 2.x), las alternativas para realizar este bloqueo serían las siguientes:
Por ejemplo:
@Entity
@Table(name="mailing_package")
@NamedQueries( {
    @NamedQuery(name = "MailingPackage.findByIdLocked",
        query = "SELECT s FROM MailingPackage s WHERE s.id = :id",
        hints={ @QueryHint(name = "toplink.pessimistic-lock", value = "LockNoWait")} })
public class MailingPackage implements Serializable {
...
}

El bloqueo pesimista hay que manejarlo con cuidado porque puede causar problemas de concurrencia, rendimiento o bloqueos de la aplicación por deadlocks. Típicamente no es deseable para aplicaciones web interactivas, ya que requiere mantener la transacción (y por tanto la conexión) activa durante la edición. El uso típico es cuando se quiere que la edición tendrá éxito en un momento que sabemos que la transacción durará lo menos posible.


Bloqueos en JPA 2.0
JPA 2.0 añade soporte específico para bloqueo pesimista además de otras opciones de bloqueo en el propio API. Un bloqueo se puede adquirir usando adquiere usando el método EntityManager.lock(entity, lockMode), pasando un argumento LockModeType a los nuevos métodos sobrecargados find() y refresh(), o estableciendo un lockMode en una Query ( setLockMode() ) o NamedQuery (lockMode).

JPA 2.0 amplia/redefine los modos de bloqueo de JPA 1.0 en el enum LockModeType:
  • OPTIMISTIC: Es el READ de JPA 1.0
  • OPTIMISTIC_FORCE_INCREMENT: Es el WRITE de JPA 1.0
  • PESSIMISTIC_READ: Bloquea y evita que otra transacción adquiera un bloqueo PESSIMISTIC_WRITE.
  • PESSIMISTIC_WRITE: Bloquea y evita que otra transacción adquiera bloqueos PESSIMISTIC_READ o PESSIMISTIC_WRITE.
  • PESSIMISTIC_FORCE_INCREMENT: es una suma de PESSIMISTIC_WRITE y OPTIMISTIC_FORCE_INCREMENT.
  • NONE: No hay bloqueo ni comprobación. Equivalente a omitir cualquier lockMode.
Adicionalmente, JPA 2.0 añade dos hits estándar que pueden pasarse a qualquier Query o NamedQuery y a qualquier operación find(), lock() o refresh():
  • "javax.persistence.lock.timeout": Número de milisegundos a esperar la liberación del bloqueo antes de lanzar una PessimisticLockException.
  • "javax.persistence.lock.scope": Los alcances válidos se definen en PessimisticLockScope (NORMAL or EXTENDED).  EXTENDED bloqueará adicionalmente las tablas relacionadas.

Conclusiones

Con el nivel por defecto de la base de datos en READ_COMMITED y usando un bloqueo optimista para detectar lost-updates, sólo necesitaríamos realizar un bloqueo pesimista en muy pocos casos. No obstante, estos casos existen: casos como un objeto que deba tener una numeración "sin huecos" (como el clásico ejemplo de los números de factura) o un repartidor de objetos que no deba dar el mismo objeto a dos threads para su proceso, podría requierir un bloqueo pesimista o secuenciable que impida de dos threads lean simultáneamente el mismo valor y lo incrementen.

Recomiendo echar un vistazo a las referencias al final del artículo para profundizar más en el tema. Aprovecho la ocasión para felicitar aquí a los autores de "Java Persistence", de en.wikibooks.org, que han hecho un trabajo impecable el cual me ha sido de enorme ayuda para comprender el complejo mundo de JPA.


NOTAS:
[1] Creo que la traducción de "commited" como "cursado" o "confirmado" es más correcta en este contexto.


Referencias y más información:

jueves, 9 de septiembre de 2010

Eclipse Helios e integración con SVN, Maven y Glassfish

Desde el año 2006, la fundación Eclipse produce a finales de Junio una versión coordinada simultánea de decenas de proyectos de código abierto consolidados en una herramienta de desarrollo conocida comúnmente como Eclipse IDE, de la que se ponen disponibles 12 empaquetados distintos según el propósito de desarrollo, plataforma tecnológica y necesidades del desarrollador. Desde el 23 de Junio de este año está disponible la versión 3.6 denominada Eclipse Helios.

El modo de distribución de Eclipse es análogo al de una distribución de Linux como Ubuntu y, por tanto, con la misma potencia, comodidad y efectividad. Los distintos paquetes son, en definitiva, distintas combinaciones de proyectos con sus dependencias debidamente resueltas contra los repositorios de Eclipse. Las actualizaciones, en línea, también son similares a las que se realizan con Ubuntu, con lo que la integridad de dependencias está asegurada. En definitiva, una buena idea extendida a las herramientas de desarrollo.


La comunidad de Eclipse tiene cientos de plugins, tanto de código abierto como comerciales, no todos hospedados eclipse.org, que pueden ser de interés. El sistema de repositorios de Eclipse, si bien resuelve correctamente dependencias, requiere que añadamos manualmente las url's de las fuentes de software que queremos instalar. Una de las novedades de Eclipse Helios se acerca aún más a la analogía comentada añadiendo al sistema de repositorios de Eclipse una herramienta de alto nivel llamada Eclipse Marketplace Client (MPC). MPC funciona como un almacén de aplicaciones y plugins (app store) centralizado permitiendo la descarga e instalación de forma cómoda, automática e integrada en nuestro Eclipse.




Para trabajar con un proyecto JEE me gusta que mi Eclipse tenga los plugins de integración con Subversion (SVN), Maven y Glassfish (o el servidor de aplicaciones con el que vaya a trabajar). Por alguna razón que desconozco, Helios aún no trae "de serie" la integración con Maven y SVN. Es posible que sea por mantener escrupulosamente la libertad del desarrollador ya que existen varios plugins disponibles, de los cuales, los más conocidos son:
  • Maven
    • m2eclipse (m2e), el "oficial" de los chicos de Maven (Sonatype) y que, por cierto, se está trasladando de codehaus.org a eclipse.org
    • Eclipse IAM, antiguo q4e de los chicos de Apache
  • SVN
    • subclipse, el "oficial" de los chicos de SVN
    • subversion, el "oficial" de los chicos de Eclipse

En mi caso, yo instalo "Maven Integration for Eclipse" (m2eclipse), "Subversive - SVN Team provider" para SVN y "Glassfish Java EE Application Server Plugin for Eclipse".




Referencias:

lunes, 5 de julio de 2010

Actualizar el firmware de la BIOS vía USB con linux

Denme un punto de apoyo y moveré el mundo.

- Arquímedes de Siracusa. (c. 287 a. C. – c. 212 a. C.)


Recientemente tuve que actualizar el firmware de la BIOS de mi placa base y me encontré con la desagradable sorpresa de que las opciones del fabricante eran exclusivamente para Windows y DOS. Para los que usamos linux esto es un grave inconveniente, porque no tenemos ni lo uno ni lo otro, ni mucho menos ganas de adquirir una licencia sólo para esa simpleza. Incluso en el caso que pudiese adquirir una licencia, está el problema de no disponer de disquetera, de la cual carecen todos los equipos recientes.

Afortunadamente, el disgusto no me duró mucho porque el mundo del software libre ofrece muchas y variadas soluciones para esta tarea. Durante la búsqueda de soluciones para este problema me reencontré con algunos viejos proyectos que siguen felizmente muy activos y están actualmente en un estado muy interesante, como FreeDOS (un sistema operativo libre compatible con MS-DOS) y ReactOS (el renacimiento de aquel digno pero malogrado Freewin95)... ufff... uno ya va teniendo una edad...

De todas las soluciones posibles, traigo a este artículo la que más me gustó y me pareció más sencilla y sólida: UNetbootin.


UNetbootin te permite crear unidades de arranque USB de forma automática y transparente de numerosas distribuciones de Linux, utilidades diversas (reparación, recuperación, bootloaders, etc) y FreeDOS.

Adicionalmente, es capaz de crear un disco de arranque a partir de cualquier imagen ISO (o disquete) de arranque, kernel o ficheros intrd, que queramos, con lo que las posibilidades se multiplican.

El software están disponible también, a su vez, en los repositorios y/o paquetes de para las distribuciones de Linux más conocidas, y también para Windows. Por supuesto, como software libre que es, está disponible el código para cualquier otro caso raro no contemplado.

En el caso que nos ocupa, en apenas unos minutos pude descargar del repositorio el software (sudo apt-get install unetbootin) crear una unidad de arranque en un lápiz USB con FreeDOS y ejecutar la utilidad DOS para actualizar la flash de la BIOS de mi sistema. Todo con software libre (¡y gratuito!).


Referencias:

jueves, 24 de junio de 2010

XML con PostgreSQL

En el artículo Alternativas EAV con XML expliqué cómo se podía implementar una mejora del modelo Entity Attribute Value (Entidad-Atributo-Valor) usando XML. Este artículo es, en alguna medida, una continuación de aquél, cubriendo ciertos aspectos importantes sobre la consulta a éstos campos o cualquier otro campo que contenga xml. El uso de las características de este artículo requiere que la instalación de postgresql se haya realizado con el soporte xml (configure --with-libxml).

Serialización/deserialización XML de campos varchar
Hay ocasiones en que necesitamos que los campos que contienen XML sean de tipo caracter y no de tipo xml nativo. Una razón para hacer eso, por ejemplo, es que estemos usando un ORM. En algunos casos, pongamos por ejemplo PostgreSQL con Toplink Essentials (incluído de serie en Glassfish 2.1), no hay forma (al menos yo no la he encontrado) de que el ORM haga mapping de campos de tipo xml. Para estos casos, en primer lugar, debemos producir un valor de tipo xml a partir de datos carácter, para lo que podemos usar la función xmlparse o realizar un type cast a xml usando la sintaxis tradicional de PostgreSQL expression::type o bien usando la sintaxis SQL-92 estándar CAST ( expression AS type ), así:

SELECT XMLPARSE( DOCUMENT campo)
FROM tabla
WHERE campo  is not null
<=>
SELECT cast(campo as xml)
FROM tabla
WHERE campo is not null
<=>
SELECT campo::xml
FROM tabla
WHERE campo is not null



Consultas: la función xpath

Para procesar valores de tipo xml, PostgreSQL ofrece la función xpath, que evalúa expresiones XPath 1.0.

xpath(xpath_expr, xml_value[, nsarray])

La función xpath evalúa la expresión XPath xpath_expr contra el valor XML xml_value (debe ser un documento XML bien formado), devolviendo un array de valores XML correspondiente al conjunto de nodos producidos por la expresión XPath. El tercer argumento, opcional, es el array bidimensional de espacios de nombress (nombre espacio de nombres,URI espacio de nombres) que use el documento XML xml_value.


Por ejemplo, dado un campo campo de la tabla tabla, de tipo varchar(2048), podríamos consultar el contenido así (a partir de este momento usaremos la sintaxis del último ejemplo, la tradicional de PostgreSQL, por ser la más sencilla):

Consulta
Resultado
SELECT campo::xml
FROM tabla
where campo is not null
campo
----------------------------------------------------------
<xmlData><data name="incidencia">
<data  value="tiempo"  name="motivo"/></data></xmlData>
<xmlData><data name="incidencia">
<data value="hardware"  name="motivo"/></data></xmlData>
<xmlData><data name="incidencia">
<data value="hardware"  name="motivo"/></data></xmlData>
<xmlData><data name="cantidad" value="2"></xmlData>
<xmlData><data name="cantidad" value="3"></xmlData> 



Es importante recordar que la función xpath devuelve un array de valores XML. Por ejemplo, si deseamos consultar sólo el contenido de aquellos valores de elementos data cuyo name es "motivo", por eso la siguiente consulta nos devuelve 4 filas. Es decir, de las cuatro filas en las que campo tiene valores, sólo dos de ellas tiene un elemento data con name igual a 'motivo', pero como xpath devuelve un array de valores, en las los dos últimas filas se devuelve un array vacio.


Consulta
Resultado
SELECT  xpath('//data[@name=''motivo'']/@value',campo::xml) as motivos
FROM  tabla
where campo is not null
motivos
----------
{tiempo} 
{hardware}
{}
{}


Para evitar lo anterior, podemos hacer:


ConsultaResultado
SELECT  xpath('//data[@name=''motivo'']/@value',campo::xml) as motivos
FROM tabla
where campo is not null
and  array_upper(xpath('//data[@name=''motivo'']',campo::xml),1) is not null
motivos
------------
{tiempo}
{hardware}



La consulta anterior elimina aquellas filas con array vacío. No obstante, xpath() nos sigue devolviendo un array que tendremos que procesar posteriormente. Para que nos devuelva valores de tipo xml (u no un array) tendremos que tratar la respuesta de xpath como array y pedir el primer elemento del mismo. Así:



ConsultaResultado

SELECT  (xpath('//data[@name=''motivo'']/@value',campo::xml))[1] as motivos
FROM tabla
where campo is not null
and  array_upper(xpath('//data[@name=''motivo'']',campo::xml),1) is not null
motivos
-----------
tiempo
hardware



Obviamente, podemos usar la función xpath no sólo para seleccionar valores (uso en la select list de la sentencia SELECT) sino también en la cláusula WHERE, para filtrar filas. En este último caso, tendremos que realizar una conversión de tipo para poder realizar ciertas comparaciones (comparación de valores enteros, de fechas, etc...). La conversión a realizar debe ser un poco especial, ya que deberemos convertir de xml a varchar y de éste al tipo deseado. Por ejemplo:

SELECT (xpath('//data[@name=''cantidad'']/@value',campo::xml))[1]::varchar::int4
FROM step
where campo is not null
and array_upper(xpath('//data[@name=''cantidad'']',campo::xml),1) is not null
and (xpath('//data[@name=''cantidad'']/@value',campo::xml))[1]::varchar::int4 > 2

Con lo que conseguiríamos todas aquellas filas con xml que tengan un elemento data con nombre "cantidad" cuyo valor sea superior a 2.


Con esto, podemos usar un modelo más flexible (especialmente para valores de poca densidad) al modelo EAV y con consultas más asequibles.

Referencias:
Related Posts Plugin for WordPress, Blogger...
cookieassistant.com