"org.jdbi.v3.core.statement.UnableToCreateStatementException: Undefined attribute for token '<endif>' - jdbi

Method in Dao Interface
#RegisterRowMapper(MapMapper.class)
#SqlQuery(
"SELECT Table1.tenantId,Table1.sacTenantId, sacLogId,currentStep,status from Table1 inner join Table2 on Table1.tenantId = Table2.tenantId where <if(tenantId)>Table1.tenantId = :tenantId and<endif> Table2.status = 'FAILED'")
List<Map<String, Object>> getTenantFailedJobDetails(#Define("tenantId") #Bind("tenantId") String tenantId);
Error trace:
"level":"ERROR","categories":[],"msg":"Servlet.service() for servlet
[dispatcherServlet] in context with path [] threw exception [Request
processing failed; nested exception is
org.jdbi.v3.core.statement.UnableToCreateStatementException: Error
rendering SQL template: 'SELECT Table1.tenantId,Table1.sacTenantId,
sacLogId,currentStep,status from Table1 inner join Table2 on
Table1.tenantId = Table2.tenantId where <if(tenantId)>Table1.tenantId
= :tenantId and Table2.status = 'FAILED'' [statement:"null", arguments:{positional:{0:DUMMY-TENANT}, named:{tenantId:DUMMY-TENANT},
finder:[]}]] with root
cause","stacktrace":["org.jdbi.v3.core.statement.UnableToCreateStatementException:
Undefined attribute for token '' [statement:"null",
arguments:{positional:{0:DUMMY-TENANT}, named:{tenantId:DUMMY-TENANT},
finder:[]}]"
What could be wrong with the if condition?

To make if condition in jdbi query work I added annotation #UseStringTemplateEngine of package org.jdbi.v3.stringtemplate4 to the Dao method
If tenandId is not null then where clause will be
Table1.tenantId = :tenantId and Table2.status = 'FAILED'
else where clause will be just
Table2.status = 'FAILED'
One more information to add, for the else part annotation #AllowUnusedBindings package org.jdbi.v3.sqlobject.customizer is required

The syntax you posted is stringtemplate4, so you need to use the stringtemplate 4 engine (which you select with the annotation that you posted). Otherwise you end up with the default engine which does support only very simply substitutions (and not st4 syntax).

Related

Doctrine and quoting reserved word of MySql

I have in my Symfony 4.3 project entity named Group and table group in MySql database. Group is a reserved word. I have 3 entities : User, Group, UserGroup. They have relations: User ->OneToMany-> UserGroup and Group -> OneToMany -> UserGroup ( UserGroup has additional attributes, so I can't use ManyToMany relation). When I try to get group names for user with id=1:
$entityManager = $this->getDoctrine()->getManager();
$user = $entityManager->getRepository(User::class)
->find($id);
$groups = $user->getUserGroups();
foreach ($groups as $group) {
$group_name = $group->getGroupId()->getName();
echo $group_name, '<br>';
}
I get error message :
You have an error: An exception occurred while executing 'SELECT t0.id AS id_1, t0.name AS name_2 FROM group t0 WHERE t0.id = ?' with params [1]: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'group t0 WHERE t0.id = 1' at line 1 with code: 0
There is a recipe in documentation (http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/basic-mapping.html#quoting-reserved-words) how to enable quoting of reserved words:
<?php
use Doctrine\ORM\Mapping\AnsiQuoteStrategy;
$configuration->setQuoteStrategy(new AnsiQuoteStrategy());
I don't understand where I have to use this code - in EntityManager, in Controller? And how I can get $configuration?
I think the better solution is to use ticks in the definition of your entity as mentioned in Doctrine Documentation.
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\GroupRepository")
* #ORM\Table(name="`group`")
*/
class Group
{
// ...
}
Note, that this approach will work only if you are using the default quote strategy. So, delete quote_strategy parameter or set it as doctrine.orm.quote_strategy.default.
# config/packages/doctrine.yml
doctrine:
# ...
orm:
quote_strategy: doctrine.orm.quote_strategy.default

Error: Expected Literal, got 'SELECT'

I'm trying to add a method to a doctrine repository
public function getOpenedImport(Uftts $fattura){
$dql = "SELECT F.importoFattura*C.segnoNumerico - (SELECT SUM(I.importo*C2.segnoNumerico)
FROM MyAppDomain:Incas I
INNER JOIN MyAppDomain:Cjcau AS C2 ON C2.codice = I.causale
WHERE I.annoDocumento = F.annoDocumento AND I.numeroDocumento = F.numeroDocument)
FROM MyAppDomain:Uftts F
INNER JOIN MyAppDomain:Cjcau C ON C.codice = F.causale
WHERE F.annoDocumento = :annoDocumento AND F.numeroDocumento = :numeroDocumento";
$query = $this->getEntityManager()->createQuery($dql);
$query->setParameters(['annoDocumento' => $fattura->getAnnoDocumento(),
'numeroDocumento' => $fattura->getNumeroDocumento()]);
return $query->getSingleScalarResult();
}
This query if executed on my Sql Server 2008 returns the expected result, but using doctrine i get
Error: Expected Literal, got 'SELECT'
I can't find out what's wrong.
$dql variable is not valid DQL. I am not sure how to make it valid also.
You can write plain SQL and run
$this->getEntityManager()->getConnection()->executeQuery($query, $params)
it's a wrapper over PDO, so you will get result like you were performing simple PDO query.

'Invalid schema name' error thrown by Doctrine, but the raw SQL seems to work

I'm using some custom DQL functions to filter rows by some JSONB fields in PostgreSQL. Here's my query function:
private function findTopLevelResources(): array {
return $this->createQueryBuilder('r')
->where("JSON_EXISTS(r.contents, '-1') = FALSE")
->getQuery()
->getResult();
}
Running this code results in DriverException from AbstractPostgreSQLDriver:
An exception occurred while executing 'SELECT r0_.id AS id_0, r0_.marking AS marking_1, r0_.contents AS contents_2, r0_.kind_id AS kind_id_3 FROM resource r0_ WHERE r0_.contents?'-1' = false':
SQLSTATE[3F000]: Invalid schema name: 7 ERROR: schema "r0_" does not exist
LINE 1: ... r0_.kind_id AS kind_id_3 FROM resource r0_ WHERE r0_.conten...
^
I tried to execute the raw SQL query manually from PHPStorm and it worked, no errors.
How do I get this to work in Doctrine?
Why doesn't this query work with Doctrine, but does when I test it manually?
Here's JSON_EXISTS: (based on syslogic/doctrine-json-functions)
class JsonExists extends FunctionNode
{
const FUNCTION_NAME = 'JSON_EXISTS';
const OPERATOR = '?';
public $jsonData;
public $jsonPath;
public function getSql(SqlWalker $sqlWalker)
{
$jsonData = $sqlWalker->walkStringPrimary($this->jsonData);
$jsonPath = $this->jsonPath->value;
return $jsonData . self::OPERATOR . "'$jsonPath'";
}
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->jsonData = $parser->StringPrimary();
$parser->match(Lexer::T_COMMA);
$this->jsonPath = $parser->StringPrimary();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
}
Registered via Symfony's YAML config like this:
doctrine:
orm:
dql:
numeric_functions:
json_exists: Syslogic\DoctrineJsonFunctions\Query\AST\Functions\Postgresql\JsonExists
Versions of stuff:
PHP 7.1.1
doctrine/dbal v2.6.1
doctrine/orm dev-master e3ecec3 (== 2.6.x-dev)
symfony/symfony v3.3.4
The error message is a false clue.
Actual problem is caused by PDO (this is why it works from PHPStorm). When it sees a query like this:
SELECT * FROM foo WHERE contents?'bar'
It treats it like a parametrized query and the question mark ? as a parameter. For some reason it sometimes results in nonsensical error messages. This specific case could be solved by adding a space after the question mark, but it won't work for operators ?| and ?& which can't have a space in the middle.
The solution is to use functions corresponding to operators (this question saved the day). One can find out how they are called using queries like this one:
SELECT oprname, oprcode FROM pg_operator WHERE oprname IN ('?', '?|', '?&')
Here's the part of result related to JSON:
? → jsonb_exists
?| → jsonb_exists_any
?& → jsonb_exists_all
So instead of previous query which causes problems via PDO, one can use this equivalent one:
SELECT * FROM foo WHERE jsonb_exists(contents, 'bar')

Symfony 2.1 - FPNTagBundle - add a Tag bug?

I have installed FPNTagBundle via composer.
$tagArray=array('uno','due');
$tagManager = $this->get('fpn_tag.tag_manager');
$tagsObj = $tagManager->loadOrCreateTags($tagArray);
$tagManager->replaceTags($tagsObj,$entity);
$em->persist($entity);
$em->flush();
I get this error that avoid tag to persist:
An exception occurred while executing 'INSERT INTO Tag (name, slug,
created_at, updated_at) VALUES (?, ?, ?, ?)' with params
{"1":null,"2":"uno","3":null,"4":null}:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'name'
cannot be null 500 Internal Server Error - DBALException
It's seems a problem calling $tag = parent::createTag($name); in line35 of TagManager.php
Any ideas?
v.
I came across the same issue in my project.
The problem was that the Tag entity prevented the execution of the constructor in the BaseTag (FPN\TagBundle\Entity\Tag) class. I am pretty sure that your Tag class has its own constructor and you do not call the parent one, which sets the name attribute missing in your entity.
Here is an example custom Tag class which calls the parent constructor to set the name:
use FPN\TagBundle\Entity\Tag as BaseTag;
class Tag extends BaseTag
{
protected $tagging;
public function __construct($name = null)
{
parent::__construct($name);
$this->tagging = new ArrayCollection();
}
}

doctrine2: different way of querying, different results

Can someone explain, why I get different results?
$user = new UserEn();
$user->setName("test");
$em->persist($user);
$result1 = $em->find('UserEn', 'test');
$result2 = $em->getRepository('UserEn')->findBy(array('name'=>'test'));
$q = $em->createQuery('select u from UserEn u where u.name = :name');
$q->setParameter('name', 'test');
$result3 = $q->getResult();
Only $result1 holds $user, which is what I expected, and the others are null. What's wrong? (Please don't say that I need to call $em->flush(); )
Because Doctrine can't figure out that you are specifically requesting an User object which has name property set to test from the query, it queries the DB(ignoring caching mechanism), map resultset to entity object, load them in entity manager and return the array of entity object[s] if any data found. So there is no involvement of entity manager here. Things would be different if you used find($id) instead of findBy() because now Doctrine will check entity manager first, query DB if not found.
$result2
This returns an array that holds all entities with test as name:
$result2 = $em->getRepository('UserEn')->findBy(array('name'=>'test'));
In order to get only one record you should use findOneBy instead of findBy:
$result2 = $em->getRepository('UserEn')->findOneBy(array('name'=>'test'));
$result3
In the $result3 you should call $q->getSingleResult() instead of $q->getResult():
$result3 = $q->getSingleResult();