smi2 / phpClickHouse

php ClickHouse wrapper
MIT License
751 stars 139 forks source link

Unexpected readonly mode with specific string in query #194

Open JanMikes opened 1 year ago

JanMikes commented 1 year ago

Hi! I have experienced exceptions during inserts. It was weird, because out of approx 1B inserts, there were only few that failed on this exception:

In Statement.php line 172:
DatabaseException:  default: Cannot execute query in readonly mode. (READONLY)

After some debugging i was able to create minimal query to replicate the problem:

INSERT INTO `log` (`response_body`)  VALUES  ('{a:b}')

TLDR: the query will match regexp in \ClickHouseDB\Query\Query::isUseInUrlBindingsParams()

public function isUseInUrlBindingsParams():bool
    {
        //  'query=select {p1:UInt8} + {p2:UInt8}' -F "param_p1=3" -F "param_p2=4"
        return preg_match('#{[\w+]+:[\w+()]+}#',$this->sql);
    }

Then it removes the readonly: 0 default value - readonly=1 will be added to the database connection URL.

Screenshot 2023-09-06 at 23 05 40 Screenshot 2023-09-06 at 23 06 18

How to replicate:

/** @var \ClickHouseDB\Client $clickhouse */
$clickhouse->insert(
    'api_log',
    [['{a:b}']],
    ['response_body'],
);
CREATE TABLE IF NOT EXISTS api_log (
    response_body String NOT NULL
) ENGINE = MergeTree()

Expected behaviour - readonly: 0 in the URL (just changed value from {a:b} to {a: b}

Screenshot 2023-09-06 at 23 28 11

Working workaround - adds space after : in the whole query if it matches the regexp:

preg_replace('#{([\w+]+):([\w+()]+)}#', '{$1: $2}', $value);

Is there any way, other than modify the string value inserting? Thank you

simPod commented 1 year ago

@JanMikes you can try https://github.com/simPod/PhpClickHouseClient since there's no magic between usercode and issuing request so it might suite you better.

andrelec1 commented 11 months ago

Same here

image

MakarMS commented 10 months ago

A fix has been made