Hibernate-reactive: Error de sustitución de parámetros al limitar los resultados en MS SQL Server e Hibernate Reactive 1.0.0.CR9

Creado en 5 ago. 2021  ·  13Comentarios  ·  Fuente: hibernate/hibernate-reactive

Hola, me encuentro con un problema con los parámetros de consulta de MS SQL en 1.0.0.CR9 cuando uso Paginación o Rangos (Excepción SQL, mensaje = 'Sintaxis incorrecta cerca de'? '.'), Posiblemente porque el parámetro está en la cláusula Select?

Pila: Quarkus 2.1.0.Final / Panache Reactive / Mutiny 4.1.2 / Hibernate Reactive 1.0.0.CR9 / Hibernate Core 5.5.5.Final

Desde mi recurso de descanso, ejecutando esto:
[PanacheRepository].find("firstName LIKE ?1").page(Page.of(25)).list();

Con SQL iniciando sesión en Hibernate, este es el SQL producido:

Hibernate:
    select
        top(?) request0_.id as id1_1_,
        request0_.caseId as caseid2_1_,
        request0_.caseNumber as casenumb3_1_,
        request0_.expires as expires4_1_,
        request0_.addressLine1 as addressl5_1_,
        request0_.addressLine2 as addressl6_1_,
        request0_.agency as agency7_1_,
        request0_.city as city8_1_,
        request0_.email as email9_1_,
        request0_.firstName as firstna10_1_,
        request0_.lastName as lastnam11_1_,
        request0_.phone as phone12_1_,
        request0_.relationship as relatio13_1_,
        request0_.state as state14_1_,
        request0_.zipcode as zipcode15_1_,
        request0_.submitted as submitt16_1_
    from
        Request request0_
    where
        request0_.firstName like <strong i="11">@P1</strong>

Tenga en cuenta la sustitución correcta del parámetro en la cláusula where, pero no se produjo la sustitución del "top (?)" En la cláusula select.
Si elimino la paginación y vuelvo a ejecutar, la consulta se ejecuta correctamente.

El registro de seguimiento se muestra a continuación, mostrando HQL y SQL. Supongo que Panache está usando setMaxResults() en la consulta. Feliz de abrir un problema con Quarkus o Mutiny si ahí es donde está el problema, ¡y gracias de antemano!

2021-08-05 12:55:57,342 DEBUG [org.hib.hql.int.ast.QueryTranslatorImpl] (vert.x-eventloop-thread-1) HQL: FROM myproject.entities.Request WHERE requestor.firstName LIKE ?1
2021-08-05 12:55:57,342 DEBUG [org.hib.hql.int.ast.QueryTranslatorImpl] (vert.x-eventloop-thread-1) SQL: select request0_.id as id1_1_, request0_.caseId as caseid2_1_, request0_.caseNumber as casenumb3_1_, request0_.expires as expires4_1_, request0_.addressLine1 as addressl5_1_, request0_.addressLine2 as addressl6_1_, request0_.agency as agency7_1_, request0_.city as city8_1_, request0_.email as email9_1_, request0_.firstName as firstna10_1_, request0_.lastName as lastnam11_1_, request0_.phone as phone12_1_, request0_.relationship as relatio13_1_, request0_.state as state14_1_, request0_.zipcode as zipcode15_1_, request0_.submitted as submitt16_1_ from Request request0_ where request0_.firstName like ?
2021-08-05 12:55:57,342 DEBUG [org.hib.hql.int.ast.ErrorTracker] (vert.x-eventloop-thread-1) throwQueryException() : no errors
2021-08-05 12:55:57,342 TRACE [org.hib.rea.ses.imp.ReactiveHQLQueryPlan] (vert.x-eventloop-thread-1) Find: FROM myproject.entities.Request WHERE requestor.firstName LIKE ?1
2021-08-05 12:55:57,344 TRACE [org.hib.eng.spi.QueryParameters] (vert.x-eventloop-thread-1) Named parameters: {1=%j%}
2021-08-05 12:55:57,345 TRACE [org.hib.typ.des.sql.BasicBinder] (vert.x-eventloop-thread-1) binding parameter [2] as [VARCHAR] - [%j%]
2021-08-05 12:55:57,345 TRACE [org.hib.loa.Loader] (vert.x-eventloop-thread-1) Bound [3] parameters total
2021-08-05 12:55:57,355 DEBUG [org.hib.SQL] (vert.x-eventloop-thread-1)
    select
        top(?) request0_.id as id1_1_,
        request0_.caseId as caseid2_1_,
        request0_.caseNumber as casenumb3_1_,
        request0_.expires as expires4_1_,
        request0_.addressLine1 as addressl5_1_,
        request0_.addressLine2 as addressl6_1_,
        request0_.agency as agency7_1_,
        request0_.city as city8_1_,
        request0_.email as email9_1_,
        request0_.firstName as firstna10_1_,
        request0_.lastName as lastnam11_1_,
        request0_.phone as phone12_1_,
        request0_.relationship as relatio13_1_,
        request0_.state as state14_1_,
        request0_.zipcode as zipcode15_1_,
        request0_.submitted as submitt16_1_
    from
        Request request0_
    where
        request0_.firstName like <strong i="18">@P1</strong>
2021-08-05 12:55:57,358 ERROR [org.hib.rea.errors] (vert.x-eventloop-thread-1) HR000057: Failed to execute statement [$1select request0_.id as id1_1_, request0_.caseId as caseid2_1_, request0_.caseNumber as casenumb3_1_, request0_.expires as expires4_1_, request0_.addressLine1 as addressl5_1_, request0_.addressLine2 as addressl6_1_, request0_.agency as agency7_1_, request0_.city as city8_1_, request0_.email as email9_1_, request0_.firstName as firstna10_1_, request0_.lastName as lastnam11_1_, request0_.phone as phone12_1_, request0_.relationship as relatio13_1_, request0_.state as state14_1_, request0_.zipcode as zipcode15_1_, request0_.submitted as submitt16_1_ from Request request0_ where request0_.firstName like @P1]: $2could not execute query: java.util.concurrent.CompletionException: io.vertx.mssqlclient.MSSQLException: {number=102, state=1, severity=15, message='Incorrect syntax near '?'.', serverName='aa3ae4a0fd3c', lineNumber=1, additional=[io.vertx.mssqlclient.MSSQLException: {number=8180, state=1, severity=16, message='Statement(s) could not be prepared.', serverName='aa3ae4a0fd3c', lineNumber=1}]}
bug

Todos 13 comentarios

Hay algo extraño en ese SQL.

¿Por qué usamos top n lugar de offset ... fetch ... ? ¿Y por qué los paréntesis redundantes?

Supongo que Panache está usando setMaxResults() en la consulta.

Bueno, ciertamente debería serlo. Panache no tiene nada que hacer y hacer su propia generación de SQL, ¿verdad @FroMage?

Bueno, probé esto en HR simple, y obtengo:

select hqlquerypa0_.id as id1_0_, hqlquerypa0_.description as descript2_0_, hqlquerypa0_.name as name3_0_, hqlquerypa0_.type as type4_0_ from Flour hqlquerypa0_ order by hqlquerypa0_.id offset <strong i="6">@P1</strong> rows fetch next <strong i="7">@P2</strong> rows only

Entonces me parece que este debe ser un problema relacionado con Panache.

Muchas gracias por revisar y me comunicaré con algunos de los proyectos en la pila. Dado que el único HQL que se muestra en los registros de seguimiento era FROM myproject.entities.Request WHERE requestor.firstName LIKE ?1 , asumí que la adición de top(?) provenía de la implementación de MS SQL por setMaxResults() , sin embargo, el SQL en su publicación muestra que está usando una sintaxis completamente diferente.

Aún encontrándome con el problema, intenté agregar un pedido para tener este HQL: FROM myproject.entities.Request WHERE requestor.firstName LIKE ?1 ORDER BY submitted

2021-08-05 16:20:37,992 DEBUG [org.hib.hql.int.ast.QueryTranslatorImpl] (vert.x-eventloop-thread-19) HQL: FROM myproject.entities.Request WHERE requestor.firstName LIKE ?1 ORDER BY submitted
2021-08-05 16:20:37,992 DEBUG [org.hib.hql.int.ast.QueryTranslatorImpl] (vert.x-eventloop-thread-19) SQL: select request0_.id as id1_1_, request0_.caseId as caseid2_1_, request0_.caseNumber as casenumb3_1_, request0_.expires as expires4_1_, request0_.addressLine1 as addressl5_1_, request0_.addressLine2 as addressl6_1_, request0_.agency as agency7_1_, request0_.city as city8_1_, request0_.email as email9_1_, request0_.firstName as firstna10_1_, request0_.lastName as lastnam11_1_, request0_.phone as phone12_1_, request0_.relationship as relatio13_1_, request0_.state as state14_1_, request0_.zipcode as zipcode15_1_, request0_.submitted as submitt16_1_ from Request request0_ where request0_.firstName like ? order by request0_.submitted

Esto intentó ejecutar el siguiente SQL:

2021-08-05 16:20:38,022 DEBUG [org.hib.SQL] (vert.x-eventloop-thread-19)
    select
        request0_.id as id1_1_,
        request0_.caseId as caseid2_1_,
        request0_.caseNumber as casenumb3_1_,
        request0_.expires as expires4_1_,
        request0_.addressLine1 as addressl5_1_,
        request0_.addressLine2 as addressl6_1_,
        request0_.agency as agency7_1_,
        request0_.city as city8_1_,
        request0_.email as email9_1_,
        request0_.firstName as firstna10_1_,
        request0_.lastName as lastnam11_1_,
        request0_.phone as phone12_1_,
        request0_.relationship as relatio13_1_,
        request0_.state as state14_1_,
        request0_.zipcode as zipcode15_1_,
        request0_.submitted as submitt16_1_
    from
        Request request0_
    where
        request0_.firstName like <strong i="10">@P1</strong>
    order by
        request0_.submitted offset 0 rows fetch next ? rows only

Entonces ... el mismo problema todavía existe en el sentido de que no realizó la sustitución de parámetros correctamente (ahora para fetch next ? rows only ), pero eso cambió la sintaxis SQL generada para que coincida con su ejemplo.

Dado que Quarkus aún no incluye hibernate-reactive-core 1.0.0.CR9, anulé específicamente la versión en mi pom para esto y hibernate-core 5.5.5.Final. ¿Podría faltar una dependencia?

Miré dónde creo que se crea la consulta de Hibernate en Panache io.quarkus.hibernate.reactive.panache.common.runtime.CommonPanache QueryImpl: L254 , parece usar solo setFirstResult y setMaxResults en la consulta. Miré el objeto de consulta en el depurador y al final de ese método createQuery , el ReactiveQueryImpl subyacente tiene queryOptions que contienen firstRow = 0 y maxRows = 25.

¡Agradezco la ayuda! (Entiende que estoy usando una vista previa)

Creo que encontré un error, probablemente en SQLServerParameters.processLimit ()

Panache está creando una consulta de hibernación con firstRow = 0.

Dentro de org.hibernate.reactive.loader.ReactiveLoader.executeReactiveQueryStatement , SQLServer2012LimitHandler.getOffsetFetch() se llama:

if ( !LimitHelper.hasFirstRow( selection ) ) {
    return " offset 0 rows fetch next ? rows only";
}
return " offset ? rows fetch next ? rows only";

Por lo tanto, el SQL en este punto termina con offset 0 rows fetch next ? rows only . Tenga en cuenta el 0 en lugar del?.
Desde SQLServerParameters.processLimit() :

        if (isProcessingNotRequired(sql)) {
            return sql;
        }

        // Replace 'offset ? fetch next ? rows only' with the <strong i="17">@P</strong> style parameters for Sql Server
        int index = hasOffset ? parameterArray.length - 1 : parameterArray.length;
        int pos = sql.indexOf( " offset ?" );
        if ( pos > -1 ) {
            String sqlProcessed = sql.substring( 0, pos ) + " offset @P" + index++ + " rows";
            if ( sql.indexOf( " fetch next ?" ) > -1 ) {
                sqlProcessed += " fetch next @P" + index + " rows only ";
            }
            return sqlProcessed;
        }

        return sql;

El sql.indexOf( " offset ?") es el problema: debido a que LimitHandler ya codificó el desplazamiento en 0, la subcadena offset ? no se encuentra, por lo que no intenta procesar más parámetros.
¿Debería la cadena anidada coincidir buscando fetch next ? moverse fuera de la coincidencia por offset ?

@bharward Si pudiera crear un caso de prueba que use solo Hibernate Reactive, sería muy apreciado

Supongo que también veremos uno que usa Panache, pero este repositorio no es el lugar adecuado para ese tipo de problema.

Sospecho que nos falta una prueba que establece solo los resultados máximos y no el primer resultado.

@gavinking y @DavideD - Gracias. Parece que otra prueba, una que haría _ ambos_ setFirstResult(0) y setMaxResults(n) (siendo n cualquier valor> 0), reproduciría el error que estoy viendo.

SQLServer2012LimitHandler.getOffsetFetch() crea un SQL diferente cuando firstResult == 0 ("compensar 0 filas ...") en comparación con cuando firstResult> 0 ("compensar? Filas");

Si aún fuera útil, en unos días podría dedicar algo de tiempo a crear un pequeño ejemplo solo de HR para reproducir, o modificar HQLQueryParameterNamedLimitTest para agregar una prueba como describí.

Ah, tienes razón, esta prueba fallará en HQLQueryParameterNamedLimitTest :

    <strong i="7">@Test</strong>
    public void testFirstResultZeroAndMaxResults(TestContext context) {
        test(
                context,
                openSession()
                        .thenCompose( s ->
                                s.createQuery( "from Flour order by id" )
                                        .setFirstResult( 0 )
                                        .setMaxResults( 10 )
                                        .getResultList()
                                        .thenAccept( result -> {
                                            context.assertEquals( 3, result.size() );
                                        } )
                        )
        );
    }

Echaré un vistazo

@DavideD _ además_ de la prueba que propuso, también puedo sugerir una prueba adicional como esta:

       <strong i="7">@Test</strong>
    public void testFirstResultZeroAndMaxResultsWithoutOrderBy(TestContext context) {
        test(
                context,
                openSession()
                        .thenCompose( s ->
                                s.createQuery( "from Flour" )
                                        .setFirstResult( 0 )
                                        .setMaxResults( 10 )
                                        .getResultList()
                                        .thenAccept( result -> {
                                            context.assertEquals( 3, result.size() );
                                        } )
                        )
        );
    }

La diferencia está en eliminar el "orden por". La razón es que SQLServer2005LimitHandler escribe la consulta como select top(?) ... siempre que la consulta no incluye un "orden por", pero cuando lo hace utiliza la sintaxis offset ? fetch next ? . Entonces creo que se necesitan 2 casos de prueba.

¡¡Muy apreciado!!

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

arifpratama398 picture arifpratama398  ·  10Comentarios

yaakov-berkovitch picture yaakov-berkovitch  ·  7Comentarios

markusdlugi picture markusdlugi  ·  30Comentarios

gavinking picture gavinking  ·  16Comentarios

DavideD picture DavideD  ·  17Comentarios