Funkce rownum()
Číslování řádků je takový docela typický problém, který v PostgreSQL není uspokojivě vyřešený. Můžeme použít dočasné sekvence. Ty však musíme vytvářet a resetovat. Tomův návrh tímto neduhem netrpí. Je založený na tom, že každá funkce má pro každý výraz přiřazenou cache omezenou na dobu provádění dotazu. Obyčejně tato cache slouží pro uložení prováděcích plánů, oid funkcí, atd. Nicméně, jelikož toto řešení má daleko do ideálu, není funkce rownum() standardně k dispozici.
regression=# create function rownum() returns int as '/home/tgl/pgsql/rownum' regression-# language c; CREATE FUNCTION regression=# select rownum(),* from int8_tbl; rownum | q1 | q2 --------+------------------+------------------- 1 | 123 | 456 2 | 123 | 4567890123456789 3 | 4567890123456789 | 123 4 | 4567890123456789 | 4567890123456789 5 | 4567890123456789 | -4567890123456789 (5 rows)
Řazení probíhá až po vyhodnocení položek v SELECTu. Pokud se tato funkce objeví v SELECTu spolu s klauzulí ORDER BY, dopadne to špatně. Zapouzdřením SUBSELECTu do derivované tabulky už se funkce rownum bude vyhodnocovat až po vyhodnocení ORDER BY, a dostaneme očekávaný výsledek.
regression=# select rownum(),* from int8_tbl order by q2; rownum | q1 | q2 --------+------------------+------------------- 5 | 4567890123456789 | -4567890123456789 3 | 4567890123456789 | 123 1 | 123 | 456 2 | 123 | 4567890123456789 4 | 4567890123456789 | 4567890123456789 (5 rows) regression=# select rownum(),* from (select * from int8_tbl order by q2) ss; rownum | q1 | q2 --------+------------------+------------------- 1 | 4567890123456789 | -4567890123456789 2 | 4567890123456789 | 123 3 | 123 | 456 4 | 123 | 4567890123456789 5 | 4567890123456789 | 4567890123456789 (5 rows)
Vlastní kód funkce:
#include "postgres.h" #include "fmgr.h" #ifdef PG_MODULE_MAGIC PG_MODULE_MAGIC; #endif Datum rownum(PG_FUNCTION_ARGS); PG_FUNCTION_INFO_V1(rownum); Datum rownum(PG_FUNCTION_ARGS) { int32 *ptr; ptr = (int32 *) fcinfo->flinfo->fn_extra; if (ptr == NULL) { /* First time through for the current query: allocate storage */ fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(int32)); ptr = (int32 *) fcinfo->flinfo->fn_extra; /* ... and initialize counter */ *ptr = 1; } else (*ptr)++; PG_RETURN_INT32(*ptr); }