Data type conversion
Hologres is compatible with PostgreSQL 11 and lets you convert between data types using CAST, the :: operator, TRY_CAST, and formatting functions such as TO_CHAR. This topic describes the syntax of each approach, how their behavior differs, and when to use which.
Conversion functions and operators
Hologres supports the following type conversion functions and operators. For how each method behaves and for examples, see the sections that follow.
|
Function or operator |
Syntax |
Description |
Details |
|
|
|
Standard SQL form. Converts an expression to the specified target type. |
|
|
|
|
PostgreSQL shorthand. Equivalent to |
|
|
|
Converts TEXT data to the target type. Returns NULL when a value does not match the target type. |
||
|
|
Converts a number or datetime to a string according to a template. |
||
|
|
Converts a string to NUMERIC according to a template. |
||
|
|
Converts a string to DATE according to a template. |
||
|
|
Converts a string to TIMESTAMPTZ according to a template. |
Conversion methods
Hologres provides three ways to convert data types. They differ mainly in what happens when a value cannot be converted.
|
Method |
Behavior on failure |
When to use |
|
Explicit conversion |
Raises an error and the entire query fails. |
Data is already clean and types are known. You want malformed values to surface immediately. |
|
Fault-tolerant conversion |
Returns NULL and the query continues. |
The source contains malformed values and you want to skip them rather than fail. |
|
Formatted conversion |
Raises an error and the entire query fails. |
You need to control the output format when converting between strings and numbers or datetimes. |
For the data types that Hologres supports, see Data types.
Explicit conversion
Explicit conversion uses the CAST function or the :: operator. CAST(expr AS type) is the standard SQL form and expr::type is the PostgreSQL shorthand. The two are equivalent.
Common conversions:
-- String to integer. Both forms return 123.
SELECT CAST('123' AS int);
SELECT '123'::int;
-- Integer to string. Returns 123.
SELECT CAST(123 AS text);
-- String to NUMERIC. Returns 12.34.
SELECT CAST('12.34' AS numeric);
-- String to boolean. Returns t, the default display form of a PostgreSQL boolean.
SELECT CAST('true' AS boolean);
-- String to date. Returns 2026-08-13.
SELECT CAST('2026-08-13' AS date);
-- String to JSONB. Returns {"a": 1}.
SELECT CAST('{"a":1}' AS jsonb);
-- Boolean to integer. Returns 1.
SELECT CAST(true AS int);
Rounding differs between floating-point and NUMERIC sources
When you convert a fractional value to an integer, the rounding rule depends on the source type:
-
DOUBLE PRECISION and REAL to integer: rounds half to even (banker's rounding). A value ending in exactly 0.5 rounds to the nearest even integer.
-
NUMERIC and DECIMAL to integer: rounds half away from zero. A value ending in exactly 0.5 rounds up in absolute terms.
As a result, the same literal 2.5 can convert to different integers:
-- Returns 2 (2.5 rounds to the nearest even integer, 2).
SELECT CAST(2.5::float8 AS int);
-- Returns 4 (3.5 rounds to the nearest even integer, 4).
SELECT CAST(3.5::float8 AS int);
-- Returns 3 (rounds half away from zero).
SELECT CAST(2.5::numeric AS int);
ROUND also rounds half to even for DOUBLE PRECISION values, so calling ROUND on its own does not change the outcome: ROUND(2.5::float8) returns 2. To round half away from zero, convert to NUMERIC first: ROUND(2.5::float8::numeric) returns 3.
Errors raised when conversion fails
With CAST, a single unconvertible row fails the entire query. The following errors are the ones you are most likely to hit.
|
Scenario |
Error message |
Suggested fix |
|
The string does not match the target type, such as converting |
|
Use TRY_CAST to turn malformed values into NULL, or filter them out during data cleansing. |
|
A string containing a decimal point is converted to an integer, such as |
|
Convert to NUMERIC first, then to INT: |
|
A string representation of a number exceeds the range of the target type, such as |
|
Use a wider target type, such as BIGINT. |
|
The value exceeds the range of the target type, such as the maximum BIGINT value to SMALLINT. |
|
Use a wider target type, such as BIGINT. |
Fault-tolerant conversion
Fault-tolerant conversion uses the TRY_CAST function, which converts TEXT data to a target type. TRY_CAST returns NULL instead of raising an error when a value does not match the target type, which makes it a good fit for sources that contain malformed values. For the syntax, supported target types, and version requirements, see Data type conversion function.
TRY_CAST tolerates malformed values only in column data. A string constant is evaluated while the query is planned, so it still raises an error. For example, SELECT TRY_CAST('abc' AS int); returns ERROR: invalid input syntax for integer: "abc". To test TRY_CAST behavior, write the values to a table and query the table.
In addition, a string constant without an explicit type is of type unknown and raises an error even when the value is valid. For example, SELECT TRY_CAST('123' AS int); returns ERROR: cannot try cast type unknown to integer. To try the function on a constant, type it explicitly, for example SELECT TRY_CAST('123'::text AS int);.
TRY_CAST has the following type restrictions.
-
The source type must be TEXT: applying TRY_CAST to any other source type raises
ERROR: cannot try cast type <source type> to <target type>instead of returning NULL. For example, applying it to a NUMERIC column returnsERROR: cannot try cast type numeric to integer. Use CAST or::for these cases. -
Some target types are not supported: TEXT and JSON target types raise
ERROR: cannot try cast type ...instead of returning NULL.
The following example builds a table that contains malformed values.
CREATE TABLE type_convert_test (
k int,
v text
);
INSERT INTO type_convert_test VALUES
(1, '123'),
(2, 'abc'),
(3, '12.7'),
(4, ''),
(5, NULL);
TRY_CAST returns NULL for every value it cannot convert.
SELECT k, v,
TRY_CAST(v AS int) AS v_int,
TRY_CAST(v AS numeric) AS v_numeric
FROM type_convert_test
ORDER BY k;
The result is as follows.
k | v | v_int | v_numeric
---+------+-------+-------------------
1 | 123 | 123 | 123.000000000000000
2 | abc | |
3 | 12.7 | | 12.700000000000000
4 | | |
5 | | |
(5 rows)
Keep the following behavior in mind when you use TRY_CAST.
-
No truncation or rounding: a string with a decimal point returns NULL when converted to an integer rather than being rounded. In the preceding example,
12.7converted to INT returns NULL. Convert to NUMERIC first if you need to keep the value. -
Out-of-range values also return NULL: a value that exceeds the range of the target type returns NULL instead of raising an error.
-
Empty strings return NULL: an empty string is not valid input for numeric or datetime types, so the result is NULL.
-
NUMERIC and DECIMAL results carry 15 decimal places: specify the precision explicitly if you need a fixed scale, such as
TRY_CAST(v AS numeric)::numeric(10,2). -
Converting to DATE drops the time part:
2026-08-13 10:20:30converted to DATE returns2026-08-13.
Formatted conversion
Formatted conversion uses the TO_CHAR, TO_NUMBER, TO_DATE, and TO_TIMESTAMP functions. These functions are compatible with PostgreSQL and convert between strings and numbers or datetimes according to a template string: TO_CHAR renders a number or datetime as a formatted string, while TO_NUMBER, TO_DATE, and TO_TIMESTAMP parse a string into NUMERIC, DATE, and TIMESTAMPTZ respectively.
TO_TIMESTAMP returns TIMESTAMPTZ (a timestamp with time zone). The result includes a time zone offset, such as +08, and the displayed value changes with the session time zone. If you need a TIMESTAMP without a time zone, cast the result explicitly, for example TO_TIMESTAMP(...)::timestamp.
For the full syntax of the datetime functions, the template patterns they accept, and their version requirements, see Date and time functions.
Numeric template examples
The following examples show what the common numeric templates produce.
-- Returns ' 125'. The leading position is reserved for the sign, so a positive number starts with a space.
SELECT TO_CHAR(125, '999');
-- Returns ' 0125'. Pads with zeros.
SELECT TO_CHAR(125, '0999');
-- Returns ' 1,234'. Groups thousands.
SELECT TO_CHAR(1234, '9,999');
-- Returns '125'. The FM prefix removes padding spaces.
SELECT TO_CHAR(125, 'FM999');
-- Returns '-125'. S shows the sign.
SELECT TO_CHAR(-125, 'S999');
-- Returns '125-'. MI places the minus sign at the end.
SELECT TO_CHAR(-125, '999MI');
-- L shows the currency symbol, which depends on the lc_monetary setting of the instance.
-- Returns '$ 125' when lc_monetary is en_US.UTF-8. Run SHOW lc_monetary; to check the current setting.
SELECT TO_CHAR(125, 'L999');
-- Returns ' 1.23e+03'. EEEE uses scientific notation.
SELECT TO_CHAR(1234.5, '9.99EEEE');
-- Returns -12454.8. Parses the thousands separator and the trailing minus sign.
SELECT TO_NUMBER('12,454.8-', '99G999D9S');
The template must provide enough digit positions for the integer part of the value. Otherwise the integer part is replaced with #. For example, the template in TO_CHAR(125.8, '99D9') has only two integer positions and cannot hold three digits, so it returns ' ##.#'. Use TO_CHAR(125.8, '999D9') to get ' 125.8'.
Which method to choose
-
Prefer
::or CAST when the data is clean and column types are under your control. Malformed values fail fast instead of being hidden behind NULL. -
Use TRY_CAST for TEXT columns ingested from logs, tracking events, and similar sources. Pair it with a
COUNTof NULL values to monitor how much of the data is malformed. -
Use TO_CHAR when you need to control the output format, such as zero padding, thousands separators, currency symbols, or scientific notation. Do not assemble the format with string concatenation.
-
Conversion costs extra computation. For columns that are queried frequently, store the target type at write time rather than converting on every query.