oguimbal / pg-mem

An in memory postgres DB instance for your unit tests
MIT License
2k stars 97 forks source link

Incorrect coercion between text and citext #280

Open Fryuni opened 2 years ago

Fryuni commented 2 years ago

Describe the bug

Parameters of data type text are being coerced to citext when writing data, but not when comparing.

To Reproduce

CREATE TABLE foo
(
    id    int NOT NULL PRIMARY KEY,
    value citext NOT NULL
);

-- Input is properly coerced to citext
insert into "foo" ("id", "value")
values (1, 'foo');

-- This should coerse to citext before comparing, but it's not
select * from foo where "value" = 'FOO';

-- Explicit casting works
select * from foo where "value" = 'FOO'::citext;

-- Otherwise it compares as normal text
select * from foo where "value" = 'foo';
select * from foo where "value" = 'foo'::citext;

On a real postgres all of the above returns the inserted row:

postgres.public> CREATE TABLE foo
                 (
                     id    int NOT NULL PRIMARY KEY,
                     value citext NOT NULL
                 )
[2022-11-02 18:31:24] completed in 6 ms
postgres.public> insert into "foo" ("id", "value")
                 values (1, 'foo')
[2022-11-02 18:31:27] 1 row affected in 5 ms
postgres.public> select * from foo where "value" = 'FOO'
[2022-11-02 18:31:36] 1 row retrieved starting from 1 in 13 ms (execution: 3 ms, fetching: 10 ms)
postgres.public> select * from foo where "value" = 'FOO'::citext
[2022-11-02 18:31:36] 1 row retrieved starting from 1 in 27 ms (execution: 2 ms, fetching: 25 ms)
postgres.public> select * from foo where "value" = 'foo'
[2022-11-02 18:31:36] 1 row retrieved starting from 1 in 59 ms (execution: 4 ms, fetching: 55 ms)
postgres.public> select * from foo where "value" = 'foo'::citext
[2022-11-02 18:31:37] 1 row retrieved starting from 1 in 54 ms (execution: 3 ms, fetching: 51 ms)

pg-mem version

2.6.3

Fryuni commented 2 years ago

Even crazier:

CREATE TABLE foo
(
    id    int NOT NULL PRIMARY KEY,
    value citext NOT NULL
);

-- Insert without index
insert into "foo" ("id", "value")
values (1, 'foo');
-- Selecting without index: doesn't work
select * from foo where "value" = 'Foo';
select * from foo where "value" = 'foo';
select * from foo where "value" = 'Foo'::citext; -- Works with explicit casting

CREATE INDEX foo_value ON foo ("value");

-- Select with index, but data added before index: works!
select * from foo where "value" = 'Foo';
select * from foo where "value" = 'foo';

-- Insert with index
insert into "foo" ("id", "value")
values (2, 'foo-bar');

-- Select with index, data added with index: doesn't work!
select * from foo where "value" = 'Foo-Bar';
select * from foo where "value" = 'foo-bar';
select * from foo where "value" = 'Foo-Bar'::citext; -- Not even with explicit casting

-- But only the last...
insert into "foo" ("id", "value")
values (3, 'foobar');
select * from foo where "value" = 'FooBar';
select * from foo where "value" = 'foobar';

-- As soon as a new entry is added, the previous entries work!
-- But only if they share a prefix up to a `-` apparently 🤔... weird 😕 
select * from foo where "value" = 'Foo-Bar';
select * from foo where "value" = 'foo-bar';