[Gvsig_desarrolladores] Uso de cursores en el driver de PostGIS

2010-10-21 Por tema Javier Estévez
Hola.

Trabajando en el desarrollo de una extensión me he encontrado con un
problema al recuperar datos de una capa de PostGIS de más de 3
elementos. Mi extensión recupera el valor de un campo concreto de la tabla
en una fila dada, para lo que uso un código que ya había utilizado en
múltiples otras ocasiones sin ningún problema:

idx = recordset.getFieldIndexByName(fieldName);
if (idx  -1) {
Value val = recordset.getFieldValue(rowPosition, idx);
.
}

Mientras la fila en cuestión esté entre las primeras 5000 no hay ningún
problema, pero a partir de ahí empiezan a surgir errores al obtener el dato
a través de getFieldValue, concretamente en la función setAbsolutePosition,
en la clase PostGisDriver de extJDBC. Tras indagaciones en el driver de
PostGIS he podido comprobar que se da cuando el dato solicitado no se
encuentra en el cursor que se está utilizando actualmente para obtener los
datos en formato binario y de forma más ágil, concretamente al tratar de
crear un nuevo cursor para obtener los datos.

Descubrí que los cursores se generan en dos puntos del código: en setData
(para los primeros 5000 elementos) y en la mencionada setAbsolutePosition
(para el resto):

En setData:

st =
((ConnectionJDBC)conn).getConnection().createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
// st.setFetchSize(FETCH_SIZE);
myCursorId++;
st.execute(declare  + getTableName() + myCursorId +
_wkb_cursor binary scroll cursor with hold for  + sqlAux);
rs = st.executeQuery(fetch forward  + FETCH_SIZE
+  in  + getTableName() + myCursorId + _wkb_cursor);
fetch_min = 0;
fetch_max = FETCH_SIZE - 1;

Donde las variables son atributos de la clase: st es un Statement, rs es un
ResultSet y fech_min, fech_max y myCursorId son enteros.

En setAbsolutePosition se utilizan esos mismos atributos:

// calculamos el intervalo correcto
fetch_min = (index / FETCH_SIZE) * FETCH_SIZE;
fetch_max = fetch_min +  FETCH_SIZE - 1;
// y cogemos ese cacho
rs.close();
myCursorId++;
st.execute(declare  + getTableName() + myCursorId +
_wkb_cursorAbsolutePosition binary scroll cursor with hold for  +
sqlTotal);
st.executeQuery(fetch absolute  + fetch_min
+  in  + getTableName() + myCursorId +
_wkb_cursorAbsolutePosition);

rs = st.executeQuery(fetch forward  + FETCH_SIZE
+  in  + getTableName() + myCursorId +
_wkb_cursorAbsolutePosition);


Al utilizar la misma variable de tipo Statement en toda la clase, me inclino
a pensar que en alguna parte se ha inutilizado por cualquier motivo, por lo
que he probado a reiniciarlo en setAbsolutePosition, metiendo la siguientes
líneas (marcadas con *) después del cierre del ResultSet:

...
rs.close();
st.close(); *
st =
((ConnectionJDBC)conn).getConnection().createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY); *
myCursorId++;
st.execute(declare  + getTableName() + myCursorId +
_wkb_cursorAbsolutePosition binary scroll cursor with hold for  +
sqlTotal);
...

Y así parece funcionar perfectamente. Pero como no sé muy bien qué
consecuencias tiene esto, planteo las siguientes preguntas:

- ¿Puede existir algún problema de sincronía con los datos de la BD?
- ¿Es excesivamente ineficiente?
- ¿Por qué se utiliza una única variable de tipo ResultSet y Statement para
(casi) todo?
- Si cuando hay cambio de cursor se desecha el anterior para no volverlo a
utilizar, ¿no se debería cerrar (sentencia SQL CLOSE)?

No sé si me he liado demasiado al tratar de contar el tema. Espero que
podáis arrojar un poco de luz a todo este lío que me he montado yo solo...

Un saludo.


-- 
Javier Estévez Valiñas
Grupo de Desarrollo
Cartolab - Laboratorio de Ingeniería Cartográfica
http://www.cartolab.es

ETS Ingeniería de Caminos, Canales y Puertos
Universidade da Coruña
Campus de Elviña - 15071 A Coruña (España)
(34)981167000 ext. 5493
___
gvSIG_desarrolladores mailing list
gvSIG_desarrolladores@listserv.gva.es
http://listserv.gva.es/cgi-bin/mailman/listinfo/gvsig_desarrolladores


Re: [Gvsig_desarrolladores] Uso de cursores en el driver de PostGIS

2010-10-21 Por tema Francisco José Peñarrubia

Hola Javier.

Yo pensaba que lo del absolutePosition funcionaba, aunque sí es cierto 
que a veces ha dado problemas. Imagino que antes de usar el recordset 
llamas al método start() y al final al método stop() (por si acaso).


En cualquier caso, si cierras y vuelves a crear el statement, lo veo 
bien. No creo que sea lento, y en cualquier caso dudo que se note.
Lo de cerrar los cursores binarios, recuerdo que lo intenté, pero 
fallaba más por que el cursor ya no era válido que porque se quedara 
abierto. Al cerrar la sesión, los cursores binarios se cierran 
automáticamente, así que opté por dejarlo abierto.


Si con lo que has hecho ya no aparecen errores, enhorabuena, creo que 
habrás resuelto uno de los bugs más escondidos!! :-)


Saludos, y pruebalo bien, a ver si para la 1.11 lo podemos meter en el 
trunk.


Fran.

El 21/10/2010 16:50, Javier Estévez escribió:

Hola.

Trabajando en el desarrollo de una extensión me he encontrado con un 
problema al recuperar datos de una capa de PostGIS de más de 3 
elementos. Mi extensión recupera el valor de un campo concreto de la 
tabla en una fila dada, para lo que uso un código que ya había 
utilizado en múltiples otras ocasiones sin ningún problema:


idx = recordset.getFieldIndexByName(fieldName);
if (idx  -1) {
Value val = recordset.getFieldValue(rowPosition, idx);
.
}

Mientras la fila en cuestión esté entre las primeras 5000 no hay 
ningún problema, pero a partir de ahí empiezan a surgir errores al 
obtener el dato a través de getFieldValue, concretamente en la función 
setAbsolutePosition, en la clase PostGisDriver de extJDBC. Tras 
indagaciones en el driver de PostGIS he podido comprobar que se da 
cuando el dato solicitado no se encuentra en el cursor que se está 
utilizando actualmente para obtener los datos en formato binario y de 
forma más ágil, concretamente al tratar de crear un nuevo cursor para 
obtener los datos.


Descubrí que los cursores se generan en dos puntos del código: en 
setData (para los primeros 5000 elementos) y en la mencionada 
setAbsolutePosition (para el resto):


En setData:

st = 
((ConnectionJDBC)conn).getConnection().createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,

ResultSet.CONCUR_READ_ONLY);
// st.setFetchSize(FETCH_SIZE);
myCursorId++;
st.execute(declare  + getTableName() + myCursorId + 
_wkb_cursor binary scroll cursor with hold for  + sqlAux);

rs = st.executeQuery(fetch forward  + FETCH_SIZE
+  in  + getTableName() + myCursorId + 
_wkb_cursor);

fetch_min = 0;
fetch_max = FETCH_SIZE - 1;

Donde las variables son atributos de la clase: st es un Statement, rs 
es un ResultSet y fech_min, fech_max y myCursorId son enteros.


En setAbsolutePosition se utilizan esos mismos atributos:

// calculamos el intervalo correcto
fetch_min = (index / FETCH_SIZE) * FETCH_SIZE;
fetch_max = fetch_min +  FETCH_SIZE - 1;
// y cogemos ese cacho
rs.close();
myCursorId++;
st.execute(declare  + getTableName() + myCursorId + 
_wkb_cursorAbsolutePosition binary scroll cursor with hold for  + 
sqlTotal);

st.executeQuery(fetch absolute  + fetch_min
+  in  + getTableName() + myCursorId + 
_wkb_cursorAbsolutePosition);


rs = st.executeQuery(fetch forward  + FETCH_SIZE
+  in  + getTableName() + myCursorId + 
_wkb_cursorAbsolutePosition);



Al utilizar la misma variable de tipo Statement en toda la clase, me 
inclino a pensar que en alguna parte se ha inutilizado por cualquier 
motivo, por lo que he probado a reiniciarlo en setAbsolutePosition, 
metiendo la siguientes líneas (marcadas con *) después del cierre del 
ResultSet:


...
rs.close();
st.close(); *
st = 
((ConnectionJDBC)conn).getConnection().createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,

ResultSet.CONCUR_READ_ONLY); *
myCursorId++;
st.execute(declare  + getTableName() + myCursorId + 
_wkb_cursorAbsolutePosition binary scroll cursor with hold for  + 
sqlTotal);

...

Y así parece funcionar perfectamente. Pero como no sé muy bien qué 
consecuencias tiene esto, planteo las siguientes preguntas:


- ¿Puede existir algún problema de sincronía con los datos de la BD?
- ¿Es excesivamente ineficiente?
- ¿Por qué se utiliza una única variable de tipo ResultSet y Statement 
para (casi) todo?
- Si cuando hay cambio de cursor se desecha el anterior para no 
volverlo a utilizar, ¿no se debería cerrar (sentencia SQL CLOSE)?


No sé si me he liado demasiado al tratar de contar el tema. Espero que 
podáis arrojar un poco de luz a todo este lío que me he montado yo solo...


Un saludo.


--
Javier Estévez Valiñas
Grupo de