row
The row data type represents a composite value (also known as a struct or record) containing one or more fields of different types. row values support field access, lexicographic comparison, NULL checks, conversion to text, and use in GROUP BY, ORDER BY, and JOIN clauses.
Syntax
ROW(expression [, ...])
(expression, expression [, ...])
The explicit row keyword is required for single-element composites. For two or more elements, the row keyword is optional and the parenthesized list is treated as an implicit tuple.
Examples
Access fields
Access by position
For anonymous ROW expressions, fields are accessed by the positional names f1, f2, and so on, in declaration order:
SELECT (ROW(1, 'hello', 3.14)).f1, (ROW(1, 'hello', 3.14)).f2;
f1 | f2
----+-------
1 | hello
(1 row)
The parentheses around the ROW expression are required when accessing a field.
Access by name
For composite columns with declared field names, for example, columns mapped from a topic with struct_mapping_policy = 'COMPOUND' (see CREATE TABLE), access fields by their declared names:
SELECT (record).customer_id, (record).order_total FROM orders;
Expand all fields with a wildcard
To project every field of a ROW value as a separate result column, use the .* form:
SELECT (ROW(1, 'hello', 3.14)).*;
f1 | f2 | f3
----+-------+------
1 | hello | 3.14
(1 row)
The wildcard form also works inside a ROW(…) constructor to copy fields from one composite into another.
Compare ROW values
ROW values support the standard comparison operators =, <>, <, ⇐, >, and >=. Comparison is lexicographic: fields are compared in order, left to right, and the first differing field determines the result.
SELECT ROW(1, 'a') < ROW(1, 'b');
?column?
----------
t
(1 row)
Both ROW values must have the same number of fields, and corresponding fields must have comparable types.
Check for NULL
ROW values support IS NULL and IS NOT NULL, but with semantics that differ from scalar columns:
-
expression IS NULLreturnstruewhen the expression itself is NULL, or when all of the row’s fields are NULL. -
expression IS NOT NULLreturnstruewhen the expression itself is non-NULL and all of the row’s fields are non-NULL.
Because of this, IS NULL and IS NOT NULL are not always inverses for ROW values. Both can return false for the same input, such as a ROW with a mix of NULL and non-NULL fields.
SELECT ROW(1, 'a') IS NULL AS is_null,
ROW(1, 'a') IS NOT NULL AS is_not_null;
is_null | is_not_null
---------+-------------
f | t
(1 row)
For a ROW with at least one NULL field and at least one non-NULL field, both checks return false:
SELECT ROW(NULL, 'a') IS NULL AS is_null,
ROW(NULL, 'a') IS NOT NULL AS is_not_null;
is_null | is_not_null
---------+-------------
f | f
(1 row)
These checks do not recurse into nested ROW values. A nested ROW with all-NULL fields counts as a value (not NULL) at the outer level, so the outer IS NULL returns false. To check a specific nested field directly, access the field and test that.
|
Convert to text
Cast a ROW value to text to produce the standard PostgreSQL composite literal form:
SELECT ROW(1, 'hello', 3.14)::text;
row
-----------------
(1,"hello",3.14)
(1 row)
Use ROW in queries
Suggested reading
-
CREATE TABLE: Maps a Redpanda topic to a SQL table. Use
struct_mapping_policy = 'COMPOUND'to surface nested topic fields as user-defined types accessible with ROW field-access syntax.