6

Is there a way how you can cast the following result to array?

select pg_tables from pg_tables

This will return one column only, however the data type is not array.

Edit: I'm using PostgreSql 9.1.4

Update: I need an equivalent of the following SQL statement, without the need to write column names, applicable for every table:

select 
    string_to_array(
    schemaname || '|' ||
    tablename || '|' || 
    tableowner || '|' ||
    coalesce(tablespace,'') || '|' ||
    hasindexes || '|' ||
    hasrules || '|' ||
    hastriggers
    ,'|')
from 
    pg_tables
Tomas Greif
  • 21,685
  • 23
  • 106
  • 155

4 Answers4

10

Might be this: http://www.sqlfiddle.com/#!1/d41d8/364

select translate(string_to_array(x.*::text,',')::text,'()','')::text[] 
from pg_tables as x

How it works (inside-out), 5 steps:

1st:

select x.*::text from pg_tables as x;

Sample Output:

|                                                            X |
----------------------------------------------------------------
|                    (pg_catalog,pg_statistic,postgres,,t,f,f) |
|                         (pg_catalog,pg_type,postgres,,t,f,f) |

2nd:

select string_to_array(x.*::text,',') from pg_tables as x;

Sample Output:

|                           STRING_TO_ARRAY |
---------------------------------------------
| (pg_catalog,pg_statistic,postgres,,t,f,f) |
|      (pg_catalog,pg_type,postgres,,t,f,f) |

3rd:

select string_to_array(x.*::text,',')::text from pg_tables as x;

Sample Output:

|                               STRING_TO_ARRAY |
-------------------------------------------------
| {(pg_catalog,pg_statistic,postgres,"",t,f,f)} |
|      {(pg_catalog,pg_type,postgres,"",t,f,f)} |

4th:

select translate( string_to_array(x.*::text,',')::text, '()', '') from pg_tables as x

Sample Output:

|                                   TRANSLATE |
-----------------------------------------------
| {pg_catalog,pg_statistic,postgres,"",t,f,f} |
|      {pg_catalog,pg_type,postgres,"",t,f,f} |

Finally:

select translate( string_to_array(x.*::text,',')::text, '()', '')::text[] 
from pg_tables as x

Sample Output:

|                               TRANSLATE |
-------------------------------------------
| pg_catalog,pg_statistic,postgres,,t,f,f |
|      pg_catalog,pg_type,postgres,,t,f,f |

Live test: http://www.sqlfiddle.com/#!1/d41d8/373

To prove that it works:

with a as 
(
  select translate( string_to_array(x.*::text,',')::text, '()', '')::text[] as colArray 
  from pg_tables as x
)
select row_number() over(), unnest(colArray)
from a;

Sample output:

| ROW_NUMBER |                  UNNEST |
----------------------------------------
|          1 |              pg_catalog |
|          1 |            pg_statistic |
|          1 |                postgres |
|          1 |                         |
|          1 |                       t |
|          1 |                       f |
|          1 |                       f |
|          2 |              pg_catalog |
|          2 |                 pg_type |
|          2 |                postgres |
|          2 |                         |
|          2 |                       t |
|          2 |                       f |
|          2 |                       f |
Michael Buen
  • 38,643
  • 9
  • 94
  • 118
  • Thank you for your answer, however this is not what I need. I've tried to improve my question to make it more clear. – Tomas Greif Jul 13 '12 at 12:06
  • @twn08 Updated the answer. Malformed array is not resolvable (e.g. empty element in between commas), I'm using latest version, 9.1, the article is 2005: http://postgresql.1045698.n5.nabble.com/Supporting-NULL-elements-in-arrays-td1936562.html – Michael Buen Jul 13 '12 at 12:34
  • @MichaelBuen Thank you, I think this is closest to the solution I was looking for. Please note that this will also not work in case when there is ',' as value in column: CREATE TABLE TEST_DATA ( my_id int, my_long_char varchar ); insert into test_data values (1, 'Text Text Text Text'); insert into test_data values (2, 'Text, Text, Text, Text'); select translate(string_to_array(x.*::text,',')::text,'()','')::text[] from test_data as x; – Tomas Greif Jul 13 '12 at 12:48
  • I also see the utility of extracting column values from a row. An example, it can make a generic logging for trigger http://okbob.blogspot.com/2010/12/iteration-over-record-in-plpgsql.html Documentation found here: http://postgres.cz/wiki/PL_toolbox_(en) I'm looking for the `record_expand` Postgresql Windows' library (`pltoolbox.dll`), the one on pgFoundry is Linux-centric, it's compiled to `pltoolbox.out` – Michael Buen Jul 13 '12 at 13:07
6

Another approach, use hstore type, this is more robust, can solve the comma in a field's value

Add the hstore contrib type, by executing this once:

CREATE EXTENSION hstore; 

Create this function:

create or replace function hstore_to_array(r hstore) returns text[] as
$$
begin
    return array(select (each(r)).value);
end;
$$ language 'plpgsql';

Then try this:

select hstore_to_array(hstore(r)) from pg_tables r limit 10;

Output:

                  hstore_to_array                  
---------------------------------------------------
 {f,pg_statistic,t,pg_catalog,postgres,NULL,f}
 {f,pg_type,t,pg_catalog,postgres,NULL,f}
 {f,pg_attribute,t,pg_catalog,postgres,NULL,f}
 {f,xx,t,public,postgres,NULL,f}
 {f,yy,t,public,postgres,NULL,f}
 {f,tblx,f,public,postgres,NULL,f}
 {f,pg_authid,t,pg_catalog,postgres,pg_global,f}
 {f,pg_proc,t,pg_catalog,postgres,NULL,f}
 {f,pg_class,t,pg_catalog,postgres,NULL,f}
 {f,pg_database,t,pg_catalog,postgres,pg_global,f}
(10 rows)

Another example:

create table Beatle(firstname text, middlename text, lastname text);


insert into Beatle(firstname, middlename, lastname) values
('John','Winston','Lennon'),
('Paul','','McCartney'),
('George',NULL,'Harrison'),
('Ringo','my passions are ring,drum and shades','Starr');

Query:

select hstore_to_array(hstore(b)) from Beatle b;

Output:

                   hstore_to_array                    
------------------------------------------------------
 {Lennon,John,Winston}
 {McCartney,Paul,""}
 {Harrison,George,NULL}
 {Starr,Ringo,"my passions are ring,drum and shades"}
(4 rows)

As we can see, even the value with comma is preserved properly.

An astute reader will notice something on the above output though. The hstore function doesn't preseve the original order of fields. In order to preserve it, put the table on subquery, i.e.

select hstore_to_array(hstore(b)) from (select * from Beatle) as b

Output:

                   hstore_to_array                    
------------------------------------------------------
 {John,Winston,Lennon}
 {Paul,"",McCartney}
 {George,NULL,Harrison}
 {Ringo,"my passions are ring,drum and shades",Starr}
(4 rows)

References used: http://okbob.blogspot.com/2009/10/dynamic-access-to-record-fields-in.html

Next feature to watch: http://www.postgresonline.com/journal/archives/254-PostgreSQL-9.2-Preserving-column-names-of-subqueries.html


UPDATE

It looks like the preservation of column ordering via subquery is just a fluke. I tried putting a sorting(e.g. on firstname).

select hstore_to_array(hstore(b)) 
from (select * from Beatle order by firstname) as b

The output doesn't preserve the original columns order anymore:

                   hstore_to_array                    
------------------------------------------------------
 {Harrison,George,NULL}
 {Lennon,John,Winston}
 {McCartney,Paul,""}
 {Starr,Ringo,"my passions are ring,drum and shades"}
(4 rows)

Will investigate further how to preserve original columns order.


UPDATE

If you need sort the table, in order to preserve the original columns order, put the ORDER BY outside of subquery:

select hstore_to_array(hstore(b)) 
from (select * from Beatle) as b order by firstname;

Output:

                   hstore_to_array                    
------------------------------------------------------
 {George,NULL,Harrison}
 {John,Winston,Lennon}
 {Paul,"",McCartney}
 {Ringo,"my passions are ring,drum and shades",Starr}
(4 rows)

It's correct now.

And selecting from in-memory table works too:

select hstore_to_array(hstore(b)) 
from 
(
    select * from 
    (values
        ('John',1940,'Winston','Lennon'),
        ('Paul',1942,'','McCartney'),
        ('George',1943,NULL,'Harrison'),
        ('Ringo',1940,'my passions are ring,drum and shades','Starr')
    ) as x(Firstname,BirthYear,Middlename,Lastname)
) as b     
order by BirthYear desc, Firstname desc

Output:

                      hstore_to_array                      
-----------------------------------------------------------
 {George,1943,NULL,Harrison}
 {Paul,1942,"",McCartney}
 {Ringo,1940,"my passions are ring,drum and shades",Starr}
 {John,1940,Winston,Lennon}
(4 rows)

UPDATE

It turns out functionality hstore_to_array is a built-in functionality already, just use avals: http://www.postgresql.org/docs/9.1/static/hstore.html

select 
    avals (hstore(b))
from 
(
    select * from 
    (values
        ('John',1940,'Winston','Lennon'),
        ('Paul',1942,'','McCartney'),
        ('George',1943,NULL,'Harrison'),
        ('Ringo',1940,'my passions are ring,drum and shades','Starr')
    ) as x(Firstname,BirthYear,Middlename,Lastname)
) as b 
order by BirthYear desc, Firstname desc;

Output:

                           avals                           
-----------------------------------------------------------
 {George,1943,NULL,Harrison}
 {Paul,1942,"",McCartney}
 {Ringo,1940,"my passions are ring,drum and shades",Starr}
 {John,1940,Winston,Lennon}
(4 rows)

Another sample:

select avals(hstore(b)) 
from (select * from Beatle) as b order by Firstname;

Output:

                        avals
------------------------------------------------------
 {George,NULL,Harrison}
 {John,Winston,Lennon}
 {Paul,"",McCartney}
 {Ringo,"my passions are ring,drum and shades",Starr}
(4 rows)

Just use avals

Live test: http://www.sqlfiddle.com/#!1/d41d8/388

Please do note that although sqlfiddle output don't have an array indicator(curly brackets) and double quotes on "my passions are ring,drum and shades", the avals result is an array and the string with comma in them has double quote in actual results, you can test it on your pgAdmin or psql

Michael Buen
  • 38,643
  • 9
  • 94
  • 118
  • I have noticed that if coluimn value is timestamp with timezone (aka ISO8601 sting) - the ordering **IS NOT** preserved....try adding "2016-06-13 16:00:00-04" as a second column to Beatles in memory table sample...It shows as first column...Fiddle example - http://www.sqlfiddle.com/#!15/9eecb7db59d16c80417c72d1e1f4fbf1/11397 – zam6ak Sep 09 '16 at 19:07
2

This function works with all corner cases including NULL values, empty strings or special characters in the values.

CREATE OR REPLACE FUNCTION f_rows_to_arr(_tbl text)
  RETURNS SETOF text[] AS
$BODY$
BEGIN

RETURN QUERY EXECUTE '
SELECT ARRAY[' || (
        SELECT string_agg(quote_ident(attname) || '::text', ',')
        FROM   pg_catalog.pg_attribute 
        WHERE  attrelid = _tbl::regclass  -- valid, visible table name 
        AND    attnum > 0                 -- exclude tableoid & friends
        AND    attisdropped = FALSE       -- exclude dropped columns
        ) || ' ]
FROM   ' || _tbl::regclass;

END;
$BODY$ LANGUAGE plpgsql;

Call:

SELECT * FROM f_rows_to_arr ('mytbl');

The cast to regclass avoids SQLi. Columns are not sorted in this version. More explanation for used techniques and links in this related answer.

Community
  • 1
  • 1
Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
0

string_to_array might help:

SELECT string_to_array(pg_tables::text,','::text) FROM pg_tables;
Frank Heikens
  • 117,544
  • 24
  • 142
  • 135
  • I've tried string_to_array. Unfortunately, the function will include all the text (including '(' and ')'. This also doesn't work when there is "," as value in column. SELECT (string_to_array(pg_tables::text,','::text))[1] FROM pg_tables – Tomas Greif Jul 13 '12 at 11:11
  • @twn08: how could there be a value with a comma in pg_tables? –  Jul 13 '12 at 11:33
  • I guess not, but I've chosen pg_tables as an example. However I'm looking for generic solution for any table. – Tomas Greif Jul 13 '12 at 11:41