pgsqlite provides comprehensive support for PostgreSQL's JSON and JSONB data types, allowing you to store, query, and manipulate JSON data in SQLite databases using familiar PostgreSQL syntax.
Both JSON and JSONB types are supported and stored as TEXT in SQLite. The key features include:
- Automatic validation: JSON columns include validation constraints to ensure data integrity
- PostgreSQL operators: All major JSON operators (
->,->>,@>, etc.) are translated to SQLite-compatible functions - Rich function library: Core PostgreSQL JSON functions are implemented as SQLite custom functions
- Seamless integration: Works with both simple and extended query protocols
In pgsqlite, both JSON and JSONB types are stored identically as TEXT in SQLite:
CREATE TABLE example (
data JSON, -- Stored as TEXT with JSON validation
config JSONB -- Also stored as TEXT with JSON validation
);While PostgreSQL stores JSONB in a binary format for faster processing, pgsqlite's implementation provides the same functionality with TEXT storage, relying on SQLite's efficient JSON functions.
pgsqlite translates PostgreSQL JSON operators to SQLite-compatible functions automatically:
| Operator | Description | Example | Result |
|---|---|---|---|
-> |
Extract JSON field as JSON | '{"a": {"b": 2}}'::json -> 'a' |
{"b": 2} |
->> |
Extract JSON field as text | '{"a": 1}'::json ->> 'a' |
1 |
-- Extract nested JSON
SELECT data->'address'->'city' FROM users;
-- Extract as text
SELECT data->>'email' FROM users;
-- Array element access
SELECT data->'items'->0 FROM orders;
SELECT data->'skills'->>1 FROM profiles;| Operator | Description | Example | Result |
|---|---|---|---|
#> |
Extract at path as JSON | '{"a": {"b": {"c": 1}}}'::json #> '{a,b}' |
{"c": 1} |
#>> |
Extract at path as text | '{"a": {"b": 2}}'::json #>> '{a,b}' |
2 |
-- Path extraction
SELECT data#>'{address,coordinates,lat}' FROM locations;
SELECT data#>>'{user,profile,name}' FROM accounts;| Operator | Description | Example |
|---|---|---|
@> |
Does JSON contain | '{"a": 1, "b": 2}'::jsonb @> '{"a": 1}' |
<@ |
Is JSON contained by | '{"a": 1}'::jsonb <@ '{"a": 1, "b": 2}' |
-- Find records containing specific JSON
SELECT * FROM products WHERE specs @> '{"color": "red"}';
-- Check if value is contained
SELECT * FROM orders WHERE '{"status": "pending"}' <@ metadata;The following operators check for key existence in JSON objects:
| Operator | Description | Example |
|---|---|---|
? |
Does key exist | '{"a": 1, "b": 2}'::jsonb ? 'a' |
| `? | ` | Do any of the keys exist |
?& |
Do all of the keys exist | '{"a": 1, "b": 2}'::jsonb ?& ARRAY['a', 'b'] |
-- Check if key exists
SELECT * FROM users WHERE profile ? 'email';
-- Check if any of several keys exist
SELECT * FROM products WHERE specs ?| ARRAY['color', 'size', 'weight'];
-- Check if all required keys exist
SELECT * FROM orders WHERE metadata ?& ARRAY['customer_id', 'order_date'];Validates if a string is valid JSON.
SELECT json_valid('{"valid": true}'); -- Returns 1
SELECT json_valid('{invalid}'); -- Returns 0Returns the type of the JSON value.
SELECT json_typeof('123'); -- 'number'
SELECT json_typeof('"text"'); -- 'string'
SELECT json_typeof('{"a": 1}'); -- 'object'
SELECT json_typeof('[1,2,3]'); -- 'array'
SELECT json_typeof('true'); -- 'boolean'
SELECT json_typeof('null'); -- 'null'Returns the length of a JSON array.
SELECT json_array_length('[1, 2, 3, 4, 5]'); -- Returns 5
SELECT json_array_length('{"a": 1}'); -- Returns NULL (not an array)Expands a JSON array to a set of JSON values (currently returns comma-separated string).
SELECT json_array_elements('[1, 2, 3]');
-- Returns: "1,2,3"Expands a JSON array to a set of text values.
SELECT json_array_elements_text('["a", "b", "c"]');
-- Returns: "a,b,c"Returns the keys of a JSON object.
SELECT jsonb_object_keys('{"name": "John", "age": 30, "city": "NYC"}');
-- Returns: "name,age,city"Converts any value to JSON.
SELECT to_json('hello world'); -- Returns: "hello world"
SELECT to_json(123); -- Returns: 123
SELECT to_json(NULL); -- Returns: nullBuilds a JSON object from key-value pairs.
SELECT json_build_object('name', 'John', 'age', 30);
-- Returns: {"name": "John", "age": 30}Removes all null values from JSON.
SELECT json_strip_nulls('{"a": 1, "b": null, "c": {"d": null, "e": 2}}');
-- Returns: {"a": 1, "c": {"e": 2}}Sets a value at the specified path.
SELECT jsonb_set('{"a": 1, "b": 2}', '{b}', '99');
-- Returns: {"a": 1, "b": 99}
SELECT jsonb_set('{"a": {"b": 1}}', '{a,c}', '"new"');
-- Returns: {"a": {"b": 1, "c": "new"}}Extracts a value at the specified path.
SELECT json_extract_path('{"a": {"b": {"c": 42}}}', 'a', 'b', 'c');
-- Returns: 42Extracts a value at the specified path as text.
SELECT json_extract_path_text('{"name": "John", "age": 30}', 'name');
-- Returns: JohnChecks if the first JSON contains the second.
SELECT jsonb_contains('{"a": 1, "b": 2}', '{"a": 1}'); -- Returns 1
SELECT jsonb_contains('{"a": 1}', '{"b": 2}'); -- Returns 0Checks if the first JSON is contained by the second.
SELECT jsonb_contained('{"a": 1}', '{"a": 1, "b": 2}'); -- Returns 1Populates a record from JSON object data. In pgsqlite, this returns a formatted string representation acknowledging the operation.
SELECT json_populate_record('null', '{"name": "Alice", "age": 30}');
-- Returns: json_populate_record: base=null, json={"name": "Alice", "age": 30}Converts a JSON object to a record-like string representation with key:value pairs.
SELECT json_to_record('{"id": 1, "name": "Bob", "active": true}');
-- Returns: (id:1,name:Bob,active:true)
SELECT json_to_record('{"user": "Charlie", "score": 95, "verified": false}');
-- Returns: (user:Charlie,score:95,verified:false)
-- Handles edge cases
SELECT json_to_record('{}'); -- Returns: ()
SELECT json_to_record('[{"invalid": "array"}]'); -- Returns error messageNote: These functions provide simplified implementations of PostgreSQL's record conversion capabilities. Full RECORD type support would require significant infrastructure changes in SQLite.
-- User profiles with flexible attributes
CREATE TABLE user_profiles (
id SERIAL PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
profile JSON,
settings JSONB
);
-- Product catalog with specifications
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
specs JSONB,
metadata JSON
);-- Insert user profile
INSERT INTO user_profiles (username, profile, settings) VALUES
('john_doe',
'{"name": "John Doe", "age": 30, "interests": ["coding", "music"]}',
'{"theme": "dark", "notifications": {"email": true, "sms": false}}');
-- Insert product with specifications
INSERT INTO products (name, specs, metadata) VALUES
('Laptop Pro',
'{"cpu": "Intel i7", "ram": "16GB", "storage": {"type": "SSD", "size": "512GB"}}',
'{"created": "2024-01-15", "tags": ["electronics", "computers"]}');-- Find users interested in coding
SELECT username, profile->>'name' as full_name
FROM user_profiles
WHERE profile @> '{"interests": ["coding"]}';
-- Get products with specific CPU
SELECT name, specs->>'cpu' as processor
FROM products
WHERE specs->>'cpu' LIKE 'Intel%';
-- Extract nested values
SELECT
name,
specs#>>'{storage,size}' as storage_size,
specs#>>'{storage,type}' as storage_type
FROM products;
-- Complex filtering with multiple operators
SELECT
username,
profile->>'age' as age,
settings#>>'{notifications,email}' as email_notifications
FROM user_profiles
WHERE
profile->>'age' > '25'
AND settings @> '{"theme": "dark"}';-- Update a specific field in JSON
UPDATE user_profiles
SET settings = jsonb_set(settings, '{theme}', '"light"')
WHERE username = 'john_doe';
-- Add a new field to JSON
UPDATE products
SET specs = jsonb_set(specs, '{warranty}', '"2 years"')
WHERE name = 'Laptop Pro';-
Indexing: While PostgreSQL supports GIN indexes on JSONB columns, SQLite doesn't have equivalent functionality. Consider extracting frequently queried JSON fields into separate columns for better performance.
-
Validation: JSON validation happens during INSERT/UPDATE operations. For bulk operations, this may add overhead.
-
Operator Translation: JSON operators are translated to SQLite functions at query time. Complex queries with many operators may have additional translation overhead.
-
Storage: JSON is stored as TEXT in SQLite, which means no binary optimization like PostgreSQL's JSONB. However, SQLite's JSON functions are highly optimized.
- Storage Format: Both JSON and JSONB are stored as TEXT (no binary format)
- Indexing: No GIN or GiST index support for JSON columns
- Operators: Existence operators (
?,?|,?&) are not yet implemented - Functions: Some advanced functions are not yet implemented:
json_each()/jsonb_each()json_populate_record()json_agg()/jsonb_agg()row_to_json()- JSON path expressions (jsonpath)
- Validate JSON before insertion when possible to avoid runtime errors
- Use JSONB type for consistency with PostgreSQL applications
- Extract frequently queried fields into separate columns for better performance
- Keep JSON documents reasonably sized as they're stored and processed as TEXT
- Use batch operations when inserting multiple JSON records for better performance
When migrating from PostgreSQL to pgsqlite:
- Table definitions work without changes - both JSON and JSONB are supported
- Queries using operators are automatically translated
- Most common functions work identically
- Consider performance implications for large JSON documents or complex queries
- Test existence operators (
?,?|,?&) as they're not yet supported
Planned improvements for JSON support include:
- Implementation of existence operators (
?,?|,?&) - Table-valued functions like
json_each(),json_array_elements()returning proper result sets - Aggregate functions (
json_agg(),json_object_agg()) - JSON path expression support
- Performance optimizations for large JSON documents