---
title: "SQLite data types between versions"
description: "Every SQLite data type an older or newer SQLite stores differently, what the converter writes for it, and the report line."
canonical: https://intosql.com/docs/types/sqlite-to-sqlite
last_updated: 2026-10-15
---

# SQLite data types between versions

Each row is a type from SQLite that an older or newer SQLite stores differently: what the 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:** [SQLite to SQLite](/sqlite-to-sqlite) opens the converter set up for this pair. The [SQLite to SQLite migration guide](/docs/migrations/sqlite-to-sqlite) 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 the dump | In the script | Why | Report line |
| --- | --- | --- | --- |
| DECIMAL(p,s), NUMERIC | the exact type when every value fits it, else a double | SQLite stores these as integers or doubles; a declared precision holds only when the values fit it. | None |

## Booleans and bits

| In the dump | In the script | Why | Report line |
| --- | --- | --- | --- |
| BOOLEAN, BOOL | INTEGER 0 or 1 with a CHECK | When every value is 0 or 1; a column that also holds other values keeps them as numbers or text. | None |

## Text

| In the dump | In the script | Why | Report line |
| --- | --- | --- | --- |
| TEXT, VARCHAR(n), CHAR(n), CLOB | the declared type, kept as it is | SQLite never enforces a length; a declared length holds only when every value fits it. | None |
| NUL characters in text | kept | The sqlite3 shell and Python cut text at a NUL character when they dump it, so only what precedes it is in the dump. | None |
| text that isn't valid UTF-8 | the bad bytes as the replacement character | Dumps write such text as raw bytes; the script is UTF-8. | `invalid_utf8_replaced` (problem) |

## Dates and times

| In the dump | In the script | Why | Report line |
| --- | --- | --- | --- |
| DATE, DATETIME, TIMESTAMP holding ISO 8601 text | DATE, DATETIME as ISO 8601 text | When every value is a date or a date and time; epoch numbers and other text keep their values as they are. | None |

## Binary

| In the dump | In the script | Why | Report line |
| --- | --- | --- | --- |
| BLOB, and columns holding bytes | BLOB | Bytes stay bytes; text and numbers in the same column are kept as their bytes. | None |

## JSON, spatial and other types

| In the dump | In the script | Why | Report line |
| --- | --- | --- | --- |
| declared types (any text: VARCHAR(255), DATETIME, BOOLEAN, JSON or none) | the type every value in the column fits | The same declared types, so the values load as they were stored. | None |
| JSON holding JSON text | TEXT | When every value is a JSON object or array. | None |
| Infinity in REAL columns | 9e999, SQLite's own infinity | SQLite writes infinity as an overflowing literal, which loads back as infinity. | None |

## Counters and identities

| In the dump | In the script | Why | Report line |
| --- | --- | --- | --- |
| INTEGER PRIMARY KEY, AUTOINCREMENT | INTEGER PRIMARY KEY, AUTOINCREMENT kept | Only a column declared exactly INTEGER PRIMARY KEY is the rowid; every row keeps its id, and the counter starts after the highest id ever handed out. | None |

## Other pairs

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