Re:Re:Ejecución arbitraria de sentencias sql en bloque

Inicio Foros Foro principal Desarrolladores Ejecucion arbitraria de sentencias sql en bloque Re:Re:Ejecución arbitraria de sentencias sql en bloque

#34341
Javier AderJavier Ader
Participante

Bueno, intente probar usando CStatement en lugar de CPreparedStatement en PluginXMLUpdater.executeUpdate y con un par de modificaciones previas anduvo la ejecución en blogue de cualquier sentencia sql (incluso con referencias “internas”).
Lo que hice fue lo siguiente:
PluginXMLUpdater.java:

Code:
public static void executeUpdate(String sql, String trxName) throws Exception
{
if (sql != null && sql.length() > 0)
{
//anterior
//CPreparedStatement cs = new CPreparedStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE, sql, trxName, true);
//cs.executeUpdate();
//cs.close();

//MIO: agregado para probar CStatement en vez de CPreparedStatement
CStatement cs = new CStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE,trxName);
cs.executeUpdate(sql,true);
cs.close();

}

Agregue el método executeUpate(string,boolean) en CStatement para que no llame a DB.getDatabaseConvert (no se bien que significa este convert, pero cuando se usa CPreparedStatement se saltea pasando true a noConvert)
CStatement.java:

Code:
//Copy and past de executeUpdtae(sql)
public int executeUpdate( String sql0, boolean noConvert ) throws SQLException {

//MIO: Convert?
p_vo.setSql(noConvert?sql0: DB.getDatabase().convertStatement( sql0 ));
if( p_stmt != null ) { // local
return p_stmt.executeUpdate( p_vo.getSql());
}

//TODO LO demas identico a CStatement.excuteUpdate(string)

y finalmente hice el fix para los finales de linea en la instalación de componentes (poniendo n como separador de linea
en readFromJar)
VPluginInstallerUtils.java:

Code:
public static void doPreInstall(Properties ctx, String trxName, String jarURL, String fileURL) throws Exception
{
/* Toma el archivo SQL correspondiente e impacta en la base de datos */
//String sql = readFromJar(jarURL, fileURL, “”);
//MIO:para que agregue este LF entre sentencias SQL
String sql = readFromJar(jarURL, fileURL, “n”);

if (sql != null && sql.length() > 0)
PluginXMLUpdater.executeUpdate(sql, trxName);
}

Despues instale este componente: http://www.eltita.com.ar/libertya/testCStatement/TestCStatement.zip
Que tiene un preinstall.sql basado en el componente PSP1.0 que postee en otro thread pero que ademas crea una tabla , inserta una fila en la misma, y despues crea un vista basada en esa tabla (e.d referencias internas dentro del mismo conjunto de sentencias a una tabla creada por el mismo archivo).
Pongo el código del preinstall.sql:

Code:
———————————————————————-
———- Nuevas Tablas y/o Vistas
———————————————————————-

CREATE VIEW v_product_not_in_plv AS
SELECT plv.ad_client_id, plv.ad_org_id, plv.m_pricelist_version_id, p.m_product_id, NULL::timestamp without time zone AS created, 0 AS createdby, NULL::timestamp without time zone AS updated, 0 AS updatedby FROM (SELECT m_pricelist_version.m_pricelist_version_id, m_pricelist_version.ad_client_id, m_pricelist_version.ad_org_id FROM m_pricelist_version) plv, (SELECT m_product.m_product_id, m_product.ad_client_id FROM m_product WHERE ((m_product.producttype = ‘I’::bpchar) OR (m_product.producttype = ‘R’::bpchar))) p WHERE ((plv.ad_client_id = p.ad_client_id) AND (NOT (EXISTS (SELECT 1 FROM m_productprice pp WHERE ((pp.m_product_id = p.m_product_id) AND (pp.m_pricelist_version_id = plv.m_pricelist_version_id))))));

–agregado para probar el chequeo de referencias dentro del preinstall

create table ejTabla2(
id integer not null,
name character varying(40)

);
— insert en la tabla creada justo antes
insert into ejTabla2(id,name) values (10,’nombre’);
create view ejView2 as select * from ejTabla2;

———————————————————————-
———- Nuevas columnas en tablas y/o vistas
———————————————————————-

———————————————————————-
———- Modificación de tablas y/o vistas
———————————————————————-

(este preinstall.sql tiene algunos finales de linea con LF y otros con CRLF; no importa al parecer ya que después del readFromJar con n como separador todas las lineas pasan a terminar con este delimitador)
Lo instalo lo mas bien, y me creo la tabla y las dos vistas (la tabla ejTabla2 tenia ademas la fila insertada).
Me dije: Éxito!
Pero… ahora dije: bueno, vamos a recrear el tema de las referencias a tablas inexistentes usando el anterior CPreparedStatement, así que volví al anterior PluginXMLUpdater.executeUpdate (e.d, volvi a usar CPreparedStatement en vez de CStatement). Cree otra base de datos y repetí la instalación suponiendo que me iba a tirar un error de “tabla inexistente” al ejecutar el preinstall.sql…. pero no; lo ejecuto lo mas bien!!!! E.d, a CPreparedStatement tampoco le importan los comentarios, los finales de linea, las referencias internas!!!! Anda lo más bien! Esto “El problema es en el caso en que dichas sentencias hagan referencia a tablas todavía no existentes (escenario en el que te comentaba previamente).” no me ocurrió (todo el problema se solucionaba para el caso de los componentes llamado a readFromJar con n y CPreparedStatement no tenia nada que ver).
Por qué te puede haber ocurrido a vos tal vez provenga de la nota en este link http://jdbc.postgresql.org/documentation/83/server-prepare.html ; ahí dice que la versiones anteriores del driver JDBC para Postgress lleva a cabo la implementación de PreparedStatemet usaban las sentencias PREPARE a nivel SQL; pero estas sentencias NO soportan múltiples sentencias separadas por “;” http://www.postgresql.org/docs/8.1/static/sql-prepare.html ya que solo la primer sentencia va a quedar dentro del PREPARE (el primer “;” que encuentra finaliza la sentencia PREPARE en si misma) y las demas van a ser sentencias normales despues del prepare; bajo estas condiciones es claro que van a ocurrir problemas de referencias si el prepare define algo que es referenciado por las demás sentencias (el PREPARE no ejecuta la sentencia, solo la planifica…). Supongo que para esto haya ocurrido se tiene que estar ejecutando contra un versión del server menor o igual a 7.3 o explícitamente usando el protocolo ‘V2’ en vez de ‘V3’. Vendrá por este lado tu comentario acerca de las referencias a tablas inexistentes?

PD : Bueno, en cualquier caso saco 2 conclusiones
1) mi solución usando funciones temporales no parece para nada necesaria jajaja…
2) usar CStatement en vez de CPreparedStatement no soluciona nada ya que no hay ningún problema! (ok, salvo en situaciones en que se tenga que usar versiones anteriores del server o el protocolo V2; ahí CPreparedStatement debería fallar). De cualquier manera en este caso en particular creo que de todas formas se debería usar CStatement (con la modificación que saltea la conversión del sql por DB), pero esta vez por una cuestión de performance, no de correctitud: el instalador de componentes crea un PreparedStatement sin parámetros por cada uno de los inserts,deletes y modificaciones cuando procesa el Changelog via el install.xml y el postinstall.sml y los descarta después de usarlos UNA sola vez! (esto debe estar creando una cantidad enorme de objetos del lado del servidor de manera innecesaria y ademas generando el doble de “round-trips” a la base de datos ya que ejecución de un PreparedStatment requiere dos pasos). Me dio la sensación de que al ejecutar la instalación usando CStatement tardo menos (pero bueno, muchos tests no hice).
3) usar CStatement en general en lugar de CPreparedStatement debe traer mejoras de performance en otras lugares, pero el tema es que se pierde los setsXXXX y se tiene que modificar muchísimas referencias a esta clase. La otra opción que se me ocurre es que se podría modificar CPreparedStatement para bajo ciertas circunstancias sea un wrapper de un Statement e implementar a ‘mano’ cada una de los setXXX manipulando las string sql y usando las APIs de “escapes” dadas por el driver de postgress para no reinventar la rueda. CPreparedStatement pasaría a ser un wrapper de un PreparedStatement o de un Statement; en el primer lugar delegaría los setXXX al PreparedStatement interno tal como lo hace ahora; en el segundo debería manipular la string sql dada en su constructor de manera manual. Después investigo un poco mas acerca de esto (se me ocurre sniffear un poco la red para ver cuantos accesos genera un PreparedStatement vs un Statement… tal vez no haya mejoras de peformance después de todo…)