---
title: "Oracle to PostgreSQL data types"
description: "Every Oracle data type that PostgreSQL stores differently, what the converter writes for it, when, and the report line."
canonical: https://intosql.com/docs/types/oracle-to-postgresql
last_updated: 2026-10-15
---

# Oracle to PostgreSQL data types

Each row is a type from Oracle that PostgreSQL stores differently: what the PostgreSQL script gets, why, and the line the report writes about it. The rows come from the converter's own rules, so this page matches what it does.

**Convert in the browser:** [Oracle to PostgreSQL](/oracle-to-postgresql) opens the converter set up for this pair. The [Oracle to PostgreSQL migration guide](/docs/migrations/oracle-to-postgresql) covers dumping, loading and the rules for keys, indexes and names.

A report line is a fix when the converter changed something and lists it, or a problem when a value or object is worth a look.

## Numbers

| In Oracle | In PostgreSQL | Why | Report line |
| --- | --- | --- | --- |
| NUMBER, NUMBER(p,s), FLOAT(b) | numeric(p,s), or bigint | NUMBER keeps every digit; a NUMBER without precision whose values are all whole numbers of up to 18 digits is a 64-bit integer, and a negative scale holds as many digits before the point. | None |

## Booleans and bits

| In Oracle | In PostgreSQL | Why | Report line |
| --- | --- | --- | --- |
| BOOLEAN (23ai) | boolean | TRUE and FALSE stay true and false. | None |

## Text

| In Oracle | In PostgreSQL | Why | Report line |
| --- | --- | --- | --- |
| VARCHAR2(n BYTE\|CHAR), NVARCHAR2(n), CHAR(n), NCHAR(n), CLOB, NCLOB, LONG | varchar(n) and text | Lengths count characters, whether Oracle counted bytes or characters, so every value fits. | None |
| NUL characters in text | removed | PostgreSQL text can't hold a NUL character. It is removed and the report counts the values. | `nul_removed` (problem) |
| text that isn't valid in its encoding | the bad bytes as the replacement character | PostgreSQL text must be valid UTF-8. | `invalid_utf8_replaced` (problem) |

## Dates and times

| In Oracle | In PostgreSQL | Why | Report line |
| --- | --- | --- | --- |
| DATE, TIMESTAMP(p) | timestamp(p) | Oracle's DATE holds a time to the second, so it is a date and time. An export in an American or German session writes dates without their time or century (DD-MON-RR); export in an ISO session (SYYYY-MM-DD HH24:MI:SS) to keep them. | None |
| TIMESTAMP(7) to TIMESTAMP(9) | six digits of a second, the seventh cut | PostgreSQL keeps microseconds; the seventh digit (100 nanoseconds) is cut, never rounded, so no value moves to the next second or day. | `fraction_cut` (fix) |
| TIMESTAMP WITH TIME ZONE, TIMESTAMP WITH LOCAL TIME ZONE | timestamptz, the same instants | timestamptz keeps the instant; PostgreSQL shows it in the session's time zone instead of the offset each value had. | None |
| dates before year 1 and time zones given as region names | NULL | A value that isn't a date becomes NULL and the report counts it. | `date_out_of_range` (problem) |

## Binary

| In Oracle | In PostgreSQL | Why | Report line |
| --- | --- | --- | --- |
| BLOB, RAW(n), LONG RAW | bytea | Bytes stay bytes. | None |

## JSON, spatial and other types

| In Oracle | In PostgreSQL | Why | Report line |
| --- | --- | --- | --- |
| JSON (21c), and CLOB holding JSON | jsonb | The same JSON values; a SQL NULL the export wrote as 'null' stays NULL. | None |
| ROWID, UROWID, XMLTYPE, SDO_GEOMETRY | text, and xml for XMLTYPE | Oracle's own types have no equivalent; their values keep their text, ROWID values included, though they mean nothing off Oracle. | `type_replaced` (fix) |
| types IntoSQL doesn't know, and object types | text | Text holds any value; change the type after loading. | `unknown_type` (problem) |
| NaN and Infinity in BINARY_FLOAT and BINARY_DOUBLE | NaN and Infinity kept | PostgreSQL's floating-point types hold NaN and infinity. | None |

## Counters and identities

| In Oracle | In PostgreSQL | Why | Report line |
| --- | --- | --- | --- |
| identity columns, and a sequence a BEFORE INSERT trigger assigns | GENERATED BY DEFAULT AS IDENTITY, continuing after the highest id | The copied ids load as they are, and the counter continues after the highest one. A column a trigger fills from a sequence (the way before 12c) is an identity too. | None |

## Other pairs

- Oracle into [MySQL](/docs/types/oracle-to-mysql), [MariaDB](/docs/types/oracle-to-mariadb), [SQLite](/docs/types/oracle-to-sqlite), [SQL Server](/docs/types/oracle-to-sql-server), [Oracle](/docs/types/oracle-to-oracle)
- Into PostgreSQL from [MySQL](/docs/types/mysql-to-postgresql), [MariaDB](/docs/types/mariadb-to-postgresql), [PostgreSQL](/docs/types/postgresql-to-postgresql), [SQL Server](/docs/types/sql-server-to-postgresql), [SQLite](/docs/types/sqlite-to-postgresql)
- [Every pair](/docs/types#from-one-database-to-another)
