What are the names of these Postgres concepts?
If you want relational theory names, then:


Heading (but nobody calls it that, usually "field names" or "attribute names" or "column names" is more typical)
Tuple
Attribute name
Attribute (but that'll confuse people)
Attribute value


... and a data type is a domain. The whole table toether is a relation.

Nobody really uses that language though. More typical would be:


Table definition (if it includes data types, constraints, etc), or column names if it doesn't.
Tuple or row
Field name or column name
Column (if it includes name) or column values (if it doesn't); but the latter is uncommon in relational DBs since you rarely refer to a column except by selecting fields out of tuples.
Field or attribute value


The whole thing together is usually called a table, but relation is common enough too.



Whatever names you choose to use, please do not assume that tuples have any natural ordering in a relation. They don't.

And don't bake column ordering into your API. Column ordering is stable within a query result, of course, but apps should be discouraged from hard coding column ordinal positions. This is OK (pseudocode):

 zip(['col1', 'col2', 'col3'], do_query('SELECT col1, col2, col3 FROM my_table WHERE ...'))


but this is a bad, bad idea:

 zip(['col1', 'col2', 'col3'], do_query('SELECT * FROM my_table WHERE ...)]


as any addition of a column will break the code. So will less obvious changes like altering the data type of a column.

Apps should be encouraged to use query interfaces that produce a field-name to value map as a result, where field names are discovered from the result set metadata.

For efficiency you still want to offer ordinal access. But field name access is usually preferable.

Python for example does this with a result object that can behave as both an array (ordinal access) and dictionary (key access).



You've omitted data typing though. Relational DBs are strongly typed.

Here's a more typical table definition:

demo-&gt; \d address
                                Table "public.address"
   Column    |  Type   |                          Modifiers                           
-------------+---------+--------------------------------------------------------------
 address_id  | integer | not null default nextval('address_address_id_seq'::regclass)
 customer_id | integer | not null
 address     | text    | 
Indexes:
    "address_pkey" PRIMARY KEY, btree (address_id)
Foreign-key constraints:
    "address_customer_id_fkey" FOREIGN KEY (customer_id) REFERENCES customer(customer_id)


Critically, note that every column has a name, a type, and zero or more constraints like not null or primary key. There are also various other details like foreign key constraints.