Skip to content

'Malformed string' or 'Cannot transliterate character between character sets' with non ASCII char #8244

Description

@Fab8573

Hello,`

Issue: "Malformed String" with Non-ASCII Characters in Numeric or Timestamp Fields on Firebird Database

This issue occurs in any version of Firebird when using non-ASCII characters in a numeric or timestamp field.

It can be easily reproduced using the sample EMPLOYEE.FDB database :

SELECT * FROM EMPLOYEE r where hire_date like '%€%'

  • If the database charset is NONE: The query results in a 'Malformed String' error.
  • If the database charset is UTF8: The query results in an 'Arithmetic exception, numeric overflow, or string truncation: Cannot transliterate character between character sets' error.

The same problem occurs with any non-ASCII character for example '£', 'é' or '²' etc...

However, using a standard ASCII string works fine:

SELECT * FROM EMPLOYEE r where hire_date like '%ytrytrytyn$trbtrytr$$$ybrtjury%'
=> Work fine

Activity

aafemt commented on Sep 5, 2024

@aafemt
Contributor

Database charset in this case is irrelevant. Strings are converted using connection charset. What was connection charset in your examples?

Fab8573 commented on Sep 5, 2024

@Fab8573
Author

With default EMPLOYEE.FDB
if Connection Charset = NONE error is 'Malformed String'
if Connection Charset = UTF8 or Charset = WIN1252 error is 'Arithmetic exception, numeric overflow, or string truncation: Cannot transliterate character between character sets'

Same problem if EMPLOYEE.FDB is recreated with UTF8 or WIN1252 character sets. Error will be 'Arithmetic exception, numeric overflow, or string truncation: Cannot transliterate character between character sets'

Problem is : In any case Firebird try do an ASCII conversion for non alphanumeric fields comparison. Why ?

madorin commented on Oct 3, 2026

@madorin
Contributor

I looked into this on v5.0-release (5.0.5 debug build) and master.

Cause. For LIKE, CONTAINING, STARTING WITH and SIMILAR TO, ComparativeBoolNode::stringBoolean() takes the text type of the operation from the first operand. When that operand isn't a string (DATE, TIMESTAMP, INTEGER, NUMERIC, BOOLEAN, ...), v3.0 to v5.0 get it from INTL_TEXT_TYPE(), which returns ttype_ascii for non-text types. The pattern is then converted to ASCII, so any non-ASCII character fails before the matching starts:

  • connection charset NONE: the NONE -> ASCII well-formedness check gives Malformed string;
  • UTF8, WIN1252, ...: Cannot transliterate character between character sets.

As @aafemt said, the database charset doesn't matter: the literal has the connection charset. The text form of a date or a number is ASCII only, so the right answer here is simply FALSE.

Smaller repro, no table needed:

select count(*) from rdb$database where current_date like '%é%';
select count(*) from rdb$database where rdb$relation_id like '%é%';

Both fail on 3.0.14 and 5.0.x with any connection charset. 2.5 fails only with a charset other than NONE.

master already returns FALSE here. In c2413fb (CSetId/TTypeId refactoring) INTL_TEXT_TYPE(*desc1) became desc1->getTextType(), which returns CS_NONE for non-text types, so the pattern converts without an error. (DATE/TIME/TIMESTAMP/DOUBLE operands of these operators currently have another problem in master: #9186.)

PR #9184 backports that one-line change to v5.0-release. I checked it with LIKE, NOT LIKE, CONTAINING, STARTING WITH and SIMILAR TO on DATE, TIME, TIMESTAMP, INTEGER, NUMERIC, DOUBLE PRECISION, DECFLOAT, INT128, BOOLEAN and DB_KEY, with connection charsets NONE, UTF8 and WIN1252. Non-ASCII patterns now return FALSE (NOT LIKE returns TRUE), and ASCII patterns (d like '2024%', ts like '%10:00%', n like '3.1%', ...) give the same results as before. The commit also cherry-picks cleanly onto v4.0-release (not built there).

One thing the change doesn't cover: a multibyte ESCAPE character with a non-text operand (d like '2024é%' escape 'é' with UTF8) is still rejected, now as Invalid ESCAPE sequence, because the escape is converted to NONE. Before the change it failed with the transliteration error.

Workaround for older versions: cast the operand to a string first, e.g. cast(hire_date as varchar(30)) like '%€%'.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions