Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using nonInsertable/nonUpdateable columns with JOINED inheritance type triggers SQL error #9467

Closed
bordgab opened this issue Feb 3, 2022 · 6 comments
Labels

Comments

@bordgab
Copy link

bordgab commented Feb 3, 2022

Bug report

JoinedSubclassPersister::getInsertColumnList() does not take care of notInsertable/notUpdateable property of field mapping in parent class, therefore the column(s) appears in INSERT/UPDATE statement.

This wrong behaviour generates a DriverException with the following message, because BasicEntityPersister::prepareUpdateData() (correctly) does not provide the appropriate changeset:

An exception occurred while executing a query: SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens

ORM version: 2.11

Follow up to #9118

@derrabus
Copy link
Member

derrabus commented Feb 4, 2022

ORM 2.9 is not maintained anymore. Can you please try to reproduce your problem with a recent release? If this issue still occurs, please provide steps to reproduce your problem.

@beberlei
Copy link
Member

beberlei commented Feb 4, 2022

This must be 2.11 since it uses a new feature

@bordgab
Copy link
Author

bordgab commented Feb 4, 2022

This must be 2.11 since it uses a new feature

Thanks for the version fix, I overlooked the version number....

gtgt pushed a commit to gtgt/doctrine-orm that referenced this issue Feb 7, 2022
There wasn't any check for fields with notInsertable mapping at JoinedSubclassPersister's getInsertColumnList. This causes more column in the resulting SQL Insert query than expected when insertable=false is set on a field of joined subclass.
A test is created for this case.
gtgt pushed a commit to gtgt/doctrine-orm that referenced this issue Feb 8, 2022
phpcbf is applied to the code
@curry684
Copy link

curry684 commented Oct 14, 2022

I'm experiencing a related issue in ORM 2.13.3, I think it's strongly related based op superficial debugging.

I have the following field in an abstract entity used in single table inheritance:

    #[Column(type: Types::DATETIME_MUTABLE, insertable: false, updatable: false,
        columnDefinition: 'datetime GENERATED ALWAYS AS (DATE_ADD(Start, INTERVAL Hours * 60 MINUTE))',
        generated: 'ALWAYS')]
    private \DateTimeInterface $end;

The insertable property is properly processed, the field is not added to the INSERT when flushing. However the generated: 'ALWAYS' should trigger a SELECT right after, to fill the generated field. This is not happening, causing the field to remain uninitialized until the next request.

I suspect it's caused by the derived class not properly being aware that its base class contains the generated field.

@hakai
Copy link

hakai commented Feb 7, 2023

I'm getting the same issue in doctrine-bundle:2.8.2 and orm 2.14.0, using a MySQL database. Apparently the code that picks up the parameters respects the insertable flags, but the code that creates the SQL query does not. Minimal example:

src/Entity/AbstractUser.php:

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
#[ORM\Table(name: 'user_common')]
#[ORM\InheritanceType('JOINED')]
#[ORM\DiscriminatorColumn(name: 'entity', type: 'string')]
#[ORM\DiscriminatorMap(['customer' => 'Customer'])]
abstract class AbstractUser
{
    #[ORM\Id]
    #[ORM\Column(type: 'integer')]
    #[ORM\GeneratedValue]
    public int $id = 0;

    #[ORM\Column(type: 'datetime', insertable: false, updatable: false, options: ['default' => 'CURRENT_TIMESTAMP'])]
    public ?\DateTime $dateCreated = null;
}

src/Entity/Customer.php:

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

#[ORM\Table(name: 'user_customers')]
#[ORM\Entity]
class Customer extends AbstractUser
{
    #[ORM\Column]
    public string $customerNumber = '42';
}

src/Controller/DebugController.php:

<?php

namespace App\Controller;

use App\Entity\Customer;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class DebugController extends AbstractController
{
    #[Route(path: '/debug', name: 'debug', methods: ['GET'])]
    public function debug(EntityManagerInterface $em): Response
    {
        $customer = new Customer();
        $em->persist($customer);
        $em->flush();
        dd($customer);
    }
}

Outcome after executing the route /debug (with dd($this->sql, $this->params); in vendor/doctrine/dbal/src/Statement.php::execute($params)@187):

INSERT INTO user_common (date_created, entity) VALUES (?, ?)
array:1 [ 1 => "customer" ]

Expected outcome:

INSERT INTO user_common ( entity) VALUES (?)
array:1 [ 1 => "customer" ]

EDIT: The updatable flag (at least in my tests) worked as expected. It will cause an empty transaction if you only change the value of the updatable:false field, but that is acceptable imo.

@mrVrAlex
Copy link
Contributor

@derrabus You can close this due to #10598
Now (in v2.16.x) this should be working as expected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
6 participants