Hibernate-reactive: Erreur de substitution de paramètre lors de la limitation des résultats sur MS SQL Server et Hibernate Reactive 1.0.0.CR9

Créé le 5 août 2021  ·  13Commentaires  ·  Source: hibernate/hibernate-reactive

Bonjour, je rencontre un problème avec les paramètres de requête MS SQL dans 1.0.0.CR9 lors de l'utilisation de la pagination ou des plages (exception SQL, message='Syntaxe incorrecte près de '?'.'), peut-être parce que le paramètre est dans la clause Select ?

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

À partir de ma ressource de repos, exécutez ceci :
[PanacheRepository].find("firstName LIKE ?1").page(Page.of(25)).list();

Avec la connexion SQL dans Hibernate, voici le SQL produit :

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>

Notez la substitution de paramètre correcte dans la clause where, mais la substitution ne s'est pas produite pour le "top(?)" dans la clause select.
Si je supprime la pagination et réexécute, la requête s'exécute avec succès.

Journalisation des traces illustrée ci-dessous, montrant HQL et SQL. Je suppose que Panache utilise setMaxResults() sur la requête. Heureux d'ouvrir un problème avec Quarkus ou Mutiny si c'est là qu'est le problème, et merci d'avance !

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

Tous les 13 commentaires

Il y a quelque chose de bizarre dans ce SQL.

Pourquoi utilisons-nous même top n au lieu de offset ... fetch ... . Et pourquoi les parenthèses redondantes ?

Je suppose que Panache utilise setMaxResults() sur la requête.

Eh bien, cela devrait certainement être le cas. Panache n'a pas à faire sa propre génération SQL, n'est-ce pas @FroMage ?

Eh bien, j'ai essayé ceci en simple RH, et j'obtiens :

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

Il me semble donc que ce doit être un problème lié à Panache.

Merci beaucoup pour votre évaluation et je vais contacter certains des projets de la pile. Étant donné que le seul HQL affiché dans les journaux de trace était FROM myproject.entities.Request WHERE requestor.firstName LIKE ?1 , j'ai supposé que l'ajout du top(?) provenait de l'implémentation MS SQL pour setMaxResults() , mais le SQL dans votre message montre qu'il utilise une syntaxe complètement différente.

Toujours confronté au problème, j'ai essayé d'ajouter une commande par, pour avoir ce 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

Cela a tenté d'exécuter le SQL suivant :

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

Donc... le même problème existe toujours en ce sens qu'il n'a pas effectué correctement la substitution de paramètres (maintenant pour le fetch next ? rows only ), mais cela a modifié la syntaxe SQL générée pour correspondre à votre exemple.

Étant donné que Quarkus n'inclut pas encore hibernate-reactive-core 1.0.0.CR9, j'ai spécifiquement remplacé la version dans mon pom pour cela et hibernate-core 5.5.5.Final. Pourrais-je manquer une dépendance?

J'ai regardé où je pense que la requête Hibernate est créée dans Panache io.quarkus.hibernate.reactive.panache.common.runtime.CommonPanache QueryImpl:L254 , il semble n'utiliser que setFirstResult et setMaxResults sur la requête. J'ai regardé l'objet de requête dans le débogueur et à la fin de cette méthode createQuery , le ReactiveQueryImpl sous-jacent a queryOptions contenant firstRow=0 et maxRows=25.

Appréciez l'aide! (Comprenez que j'utilise un aperçu)

Je pense avoir trouvé un bogue, probablement dans SQLServerParameters.processLimit()

Panache crée une requête d'hibernation avec firstRow=0.

Dans org.hibernate.reactive.loader.ReactiveLoader.executeReactiveQueryStatement , SQLServer2012LimitHandler.getOffsetFetch() est appelé :

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

Par conséquent, le SQL à ce stade se termine par offset 0 rows fetch next ? rows only . Notez le 0 au lieu du ?.
À partir de 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;

Le sql.indexOf( " offset ?") est le problème : comme le LimitHandler a déjà codé en dur le décalage à 0, la sous-chaîne offset ? n'est pas trouvée, il n'essaie donc pas de traiter d'autres paramètres.
La chaîne de caractères imbriquée à la recherche de fetch next ? être déplacée en dehors de la correspondance pour offset ?

@bharward Si vous pouviez créer un

Je suppose que nous en examinerons également un qui utilise Panache, mais ce référentiel n'est pas le bon endroit pour ce genre de problème

Je soupçonne qu'il nous manque un test qui définit uniquement les résultats maximum et non le premier résultat.

@gavinking et @DavideD - Merci. Il semble qu'un autre test, qui ferait _both_ setFirstResult(0) et setMaxResults(n) (n étant n'importe quelle valeur > 0), reproduirait l'erreur que je vois.

SQLServer2012LimitHandler.getOffsetFetch() crée un code SQL différent lorsque firstResult==0 ("offset 0 rows...") par rapport à lorsque firstResult >0 ("offset ? rows");

Si cela pouvait toujours être utile, dans quelques jours, je pourrais passer du temps à créer un petit exemple uniquement pour les RH à reproduire ou à modifier HQLQueryParameterNamedLimitTest pour ajouter un test comme je l'ai décrit.

Ah, tu as raison, ce test échouera dans 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() );
                                        } )
                        )
        );
    }

J'irais regarder

@DavideD _en

       <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 différence réside dans la suppression du « ordre par ». La raison en est que SQLServer2005LimitHandler écrit la requête sous la forme select top(?) ... chaque fois que la requête n'inclut pas de "classement par", mais lorsqu'elle le fait, elle utilise la syntaxe offset ? fetch next ? . Je pense donc que 2 cas de test sont nécessaires.

Très appréciée!!

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

blafond picture blafond  ·  7Commentaires

pqab picture pqab  ·  21Commentaires

DavideD picture DavideD  ·  37Commentaires

gavinking picture gavinking  ·  6Commentaires

Sanne picture Sanne  ·  12Commentaires