dolthub / dolthub/doltgresql

Casting a character(n) value to text keeps its trailing spaces, so comparisons and CHECK constraints fail

Closed
#3,325 0 comments 0 reactions 1 assignee Claimed by @Hydrocharged View on GitHub
customer issue
Dominant language
Go
Stars
2.1k
Forks
73
Avg merge
1d 11h
Merged PRs (30d)
152

Description

On DoltgreSQL 1.3.1, a `character(n)` value that ends in spaces keeps them when it is cast to `text`, `varchar` or `name`. PostgreSQL removes them. Its documentation for [character types](https://www.postgresql.org/docs/18/datatype-character.html) says: "Trailing spaces are removed when converting a character value to one of the other string types." So comparisons on the cast value fail, and a CHECK constraint refuses rows that PostgreSQL accepts.

```sql
SELECT '[' || 'L '::character(2)::text || ']' AS as_text,
length('L '::character(2)::text) AS length,
'L '::character(2)::text = 'L' AS equals_l;

CREATE TABLE t (
c character(2),
CONSTRAINT t_check CHECK (c::text IN ('L', 'M', 'H'))
);
INSERT INTO t VALUES ('L ');
SELECT '[' || c::text || ']' AS as_text FROM t;
```

| | PostgreSQL 18.6 | DoltgreSQL 1.3.1 |
|---|---|---|
| `'[' \|\| 'L '::character(2)::text \|\| ']'` | `[L]` | `[L ]` |
| `length('L '::character(2)::text)` | `1` | `2` |
| `'L '::character(2)::text = 'L'` | `t` | `f` |
| `INSERT INTO t VALUES ('L ')` under `CHECK (c::text IN ('L', 'M', 'H'))` | `INSERT 0 1` | `ERROR: Check constraint "t_check" violated` |

A runnable reproduction is at https://github.com/Reliable-Collaboration/repro-doltgresql-bug-bpchar-padding. It runs the SQL on both engines in throwaway containers and prints the two outputs side by side:

```sh
git clone https://github.com/Reliable-Collaboration/repro-doltgresql-bug-bpchar-padding.git
cd repro-doltgresql-bug-bpchar-padding
./repro.sh # exits 1 while DoltgreSQL's output differs from PostgreSQL's
```

## Other observations

All on DoltgreSQL 1.3.1, compared with PostgreSQL 18.6:

- Casts to `varchar`, `varchar(5)` and `name` keep the space too, and so does the implicit cast: `'[' || 'L '::char(2) || ']'` answers `[L ]`, and `upper('L '::char(2)) = 'L'` answers `f`.
- `IN`, `= ANY` and `LIKE` on the cast value answer `f`, and `length` and `char_length` answer `2`.
- A `char(2)` column holding `'L '` does not match a `text` or `varchar(2)` column holding `'L'`.
- `INSERT ... SELECT` of that column into a `text` or `varchar` column stores the trailing space.
- `COPY ... FROM STDIN` into a table with the same CHECK constraint is refused too.
- Without any cast, `'L '::bpchar = 'L'::bpchar` answers `f` where PostgreSQL answers `t`, and `CHECK (c IN ('L', 'M', 'H'))` refuses `'L '`. That may be a separate comparison bug.
- DoltgreSQL does not pad a shorter literal: `octet_length('L'::char(2))` answers `1`, where PostgreSQL answers `2`.
- The same on both engines: `octet_length('L '::char(2))` is `2`, and `'L '::char(2) LIKE 'L'` is `f`.

## Possible cause

Read in the source, not verified with a build: the cast from `bpchar` to `text` returns the value unchanged (https://github.com/dolthub/doltgresql/blob/84805dd1ca67d7dc43cbb93ad3ccde3e1187287b/server/cast/char.go#L98-L104). `server/cast/char.go` is unchanged on `main`.

## Possibly related

None found. #2145 (closed) is about parsing `'1'::bpchar(1)`, which is a different problem.

## Environment

- `dolthub/doltgresql:1.3.1`, digest `sha256:6c85cb1f35beabf47f094336a420255130b841b1645f36d79ef046276af36851`, the latest release when this was written
- `postgres:18.6-bookworm`, digest `sha256:1c59e2c3c818eaa0f0628f695b36e7c9e362d6b219b36a54a32df645cbd7e1af`
- Docker 29.7.2 on Linux x86_64

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.