As @ypercube has pointed out, when you executes:
SELECT tbl FROM tbl;
you get this result:
| tbl |
| :------------ |
| (1,"Title 1") |
| (2,"Title 2") |
| (3,"Title 3") |
same output as using:
SELECT ROW(1, 'Title 1');
| row |
| :------------ |
| (1,"Title 1") |
It is a (named) composite type. (Have a look at docs about TYPE)
By default, the value created by a ROW expression is of an anonymous record type. If necessary, it can be cast to a named composite type — either the row type of a table, or a composite type created with CREATE TYPE AS
Having a look at row_to_json() function:
+------------------------+----------------------------------------------------+---------------------------+---------------------+
| row_to_json(record [, | Returns the row as a JSON object. | row_to_json(row(1,'foo')) | {"f1":1,"f2":"foo"} |
| pretty_bool]) | Line feeds will be added between level-1 elements | | |
| | if pretty_bool is true. | | |
+------------------------+----------------------------------------------------+---------------------------+---------------------+
Uses a row as example: row_to_json(row(1,'foo'))