PostgreSQLLa base de données la plus sophistiquée au monde.
Documentation PostgreSQL 14.15 » Annexes » Modules supplémentaires fournis » pageinspect

F.23. pageinspect

Le module pageinspect fournit des fonctions qui vous permettent d'inspecter le contenu des pages de la base de données à un bas niveau, ce qui utile pour le débogage. Toutes ces fonctions ne sont utilisables que par les super-utilisateurs.

F.23.1. Fonctions générales

get_raw_page(relname text, fork text, blkno bigint) returns bytea

get_raw_page lit le bloc spécifié de la relation nommée et renvoie une copie en tant que valeur de type bytea. Ceci permet la récupération de la copie cohérente à un instant t d'un bloc spécifique. fork devrait être 'main' pour les données, et 'fsm' pour la carte des espaces libres, 'vm' pour la carte de visibilité ou 'init'.

get_raw_page(relname text, blkno bigint) returns bytea

Une version raccourcie de get_raw_page, pour ne lire que la partie des données. Équivalent à get_raw_page(relname, 'main', blkno).

page_header(page bytea) returns record

page_header affiche les champs communs à toutes les pages des tables et index PostgreSQL.

L'image d'une page obtenu avec get_raw_page doit être passé en argument. Par exemple :

test=# SELECT * FROM page_header(get_raw_page('pg_class', 0));
    lsn    | checksum | flags  | lower | upper | special | pagesize | version | prune_xid
-----------+----------+--------+-------+-------+---------+----------+---------+-----------
 0/24A1B50 |        0 |      1 |   232 |   368 |    8192 |     8192 |       4 |         0
     

Les colonnes renvoyées correspondent aux champs de la structure PageHeaderData. Voir src/include/storage/bufpage.h pour les détails.

Le champ checksum est la somme de contrôle stockée dans la page, qui peut être incorrecte si la page est d'une manière ou d'une autre corrompue. Si les sommes de contrôle des données ne sont pas activées pour cette instance, alors la valeur stockée n'a aucune signification.

page_checksum(page bytea, blkno bigint4) returns smallint

page_checksum calcule la somme de contrôle pour la page, comme si elle était citée au numéro de bloc passé en paramètre.

Une image de page obtenue avec get_raw_page devrait être passée en argument. Par exemple :

test=# SELECT page_checksum(get_raw_page('pg_class', 0), 0);
 page_checksum
---------------
         13443
      

Veuillez noter que la somme de contrôle dépend du numéro de bloc, ainsi des numéros de blocs qui se correspondent devraient être passé (sauf lors de débugage ésotériques).

Les sommes de contrôle calculées avec cette fonction peuvent être comparées avec le champ checksum retournée par la fonction page_header. Si les sommes de contrôle des données sont activées sur cette instance, alors les deux valeurs devraient être égales.

fsm_page_contents(page bytea) returns text

fsm_page_contents montre la structure interne du nœud d'une page FSM. Par exemple :

test=# SELECT fsm_page_contents(get_raw_page('pg_class', 'fsm', 0));
      

La sortie est une chaîne de texte multiligne, avec une ligne par nœud dans l'arbre binaire au sein de la page. Seuls ces nœuds qui ne valent pas zéro sont affichés. Le pointeur appelé « next », qui pointe vers le prochain slot à être retourné depuis la page, est également affiché.

Voir src/backend/storage/freespace/README pour plus d'information sur la structure d'une page FSM.

F.23.2. Fonctions Heap

heap_page_items(page bytea) returns setof record

heap_page_items affiche tous les pointeurs de ligne dans une page de table. Pour les pointeurs de ligne en utilisation, les en-têtes de ligne ainsi que les données des lignes sont aussi affichées. Toutes les lignes sont affichées, qu'elles soient ou non visibles dans l'image MVCC au moment où la page brute a été copiée.

Une image d'une page de table obtenue avec get_raw_page doit être fournie en argument. Par exemple :

test=# SELECT * FROM heap_page_items(get_raw_page('pg_class', 0));
     

Voir src/include/storage/itemid.h et src/include/access/htup_details.h pour des explications sur les champs renvoyés.

La fonction heap_tuple_infomask_flags peut être utilisée pour déchiffrer les drapeaux de t_infomask et t_infomask2 pour les lignes de la table.

tuple_data_split(rel_oid oid, t_data bytea, t_infomask integer, t_infomask2 integer, t_bits text [, do_detoast bool]) returns bytea[]

tuple_data_split divise les données d'une ligne en attributs de la même façon que le fait le moteur.

test=# SELECT tuple_data_split('pg_class'::regclass, t_data, t_infomask, t_infomask2, t_bits) FROM heap_page_items(get_raw_page('pg_class', 0));
      

Cette fonction doit être appelée avec les mêmes arguments que ce qui est renvoyé par heap_page_items.

Si do_detoast est true, la donnée TOAST sera traitée. La valeur par défaut est false.

heap_page_item_attrs(page bytea, rel_oid regclass [, do_detoast bool]) returns setof record

heap_page_item_attrs est équivalent à heap_page_items sauf qu'elle renvoie les données brutes de la ligne sous la forme d'un tableau d'attributs pouvant en option être traités par do_detoast (comportement désactivé par défaut).

Une image de la page HEAP obtenue avec get_raw_page doit être fournie comme argument. Par exemple :

test=# SELECT * FROM heap_page_item_attrs(get_raw_page('pg_class', 0), 'pg_class'::regclass);
      

heap_tuple_infomask_flags(t_infomask integer, t_infomask2 integer) returns record

heap_tuple_infomask_flags décode les champs t_infomask et t_infomask2 renvoyés par heap_page_items en un ensemble de tableaux composés de noms de drapeau, lisibles par un humain, avec une colonne pour tous les drapeaux et une colonne pour les drapeaux composés. Par exemple :

test=# SELECT t_ctid, raw_flags, combined_flags
         FROM heap_page_items(get_raw_page('pg_class', 0)),
           LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2)
         WHERE t_infomask IS NOT NULL OR t_infomask2 IS NOT NULL;
      

Cette fonction doit être appelée avec les mêmes arguments que les attributes de retour de heap_page_items.

Les drapeaux combinés sont affichés pour les macros de niveau source prenant en compte la valeur de plus d'un bit, comme par exemple HEAP_XMIN_FROZEN.

Voir src/include/access/htup_details.h pour des explications sur les noms des drapeaux renvoyés.

F.23.3. Fonctions B-tree

bt_metap(relname text) returns record

bt_metap renvoie des informations sur une méta-page d'un index B-tree. Par exemple :

test=# SELECT * FROM bt_metap('pg_cast_oid_index');
-[ RECORD 1 ]-------------+-------
magic                     | 340322
version                   | 4
root                      | 1
level                     | 0
fastroot                  | 1
fastlevel                 | 0
last_cleanup_num_delpages | 0
last_cleanup_num_tuples   | 230
allequalimage             | f
     
bt_page_stats(relname text, blkno bigint) returns record

bt_page_stats renvoie un résumé des informations sur les pages enfants des index B-tree. Par exemple :

test=# SELECT * FROM bt_page_stats('pg_cast_oid_index', 1);
-[ RECORD 1 ]-+-----
blkno         | 1
type          | l
live_items    | 224
dead_items    | 0
avg_item_size | 16
page_size     | 8192
free_size     | 3668
btpo_prev     | 0
btpo_next     | 0
btpo_level    | 0
btpo_flags    | 3
     
bt_page_items(relname text, blkno bigint) returns setof record

bt_page_items renvoie des informations détaillées sur tous les éléments d'une page d'index btree. Par exemple :

test=# SELECT itemoffset, ctid, itemlen, nulls, vars, data, dead, htid, tids[0:2] AS some_tids
        FROM bt_page_items('tenk2_hundred', 5);
 itemoffset |   ctid    | itemlen | nulls | vars |          data           | dead |  htid  |      some_tids
------------+-----------+---------+-------+------+-------------------------+------+--------+---------------------
          1 | (16,1)    |      16 | f     | f    | 30 00 00 00 00 00 00 00 |      |        |
          2 | (16,8292) |     616 | f     | f    | 24 00 00 00 00 00 00 00 | f    | (1,6)  | {"(1,6)","(10,22)"}
          3 | (16,8292) |     616 | f     | f    | 25 00 00 00 00 00 00 00 | f    | (1,18) | {"(1,18)","(4,22)"}
          4 | (16,8292) |     616 | f     | f    | 26 00 00 00 00 00 00 00 | f    | (4,18) | {"(4,18)","(6,17)"}
          5 | (16,8292) |     616 | f     | f    | 27 00 00 00 00 00 00 00 | f    | (1,2)  | {"(1,2)","(1,19)"}
          6 | (16,8292) |     616 | f     | f    | 28 00 00 00 00 00 00 00 | f    | (2,24) | {"(2,24)","(4,11)"}
          7 | (16,8292) |     616 | f     | f    | 29 00 00 00 00 00 00 00 | f    | (2,17) | {"(2,17)","(11,2)"}
          8 | (16,8292) |     616 | f     | f    | 2a 00 00 00 00 00 00 00 | f    | (0,25) | {"(0,25)","(3,20)"}
          9 | (16,8292) |     616 | f     | f    | 2b 00 00 00 00 00 00 00 | f    | (0,10) | {"(0,10)","(0,14)"}
         10 | (16,8292) |     616 | f     | f    | 2c 00 00 00 00 00 00 00 | f    | (1,3)  | {"(1,3)","(3,9)"}
         11 | (16,8292) |     616 | f     | f    | 2d 00 00 00 00 00 00 00 | f    | (6,28) | {"(6,28)","(11,1)"}
         12 | (16,8292) |     616 | f     | f    | 2e 00 00 00 00 00 00 00 | f    | (0,27) | {"(0,27)","(1,13)"}
         13 | (16,8292) |     616 | f     | f    | 2f 00 00 00 00 00 00 00 | f    | (4,17) | {"(4,17)","(4,21)"}
(13 rows)
     

Ceci est une page feuille B-tree. Tous les enregistrements qui pointent vers la table sont des éléments de la liste d'éléments (qui, chacune, stocke un total de 100 TID de 6 octets). Il existe aussi un enregistrement « clé haute » sur l'itemoffset numéro 1. ctid est utilisé pour enregistrer des informations codées sur chaque enregistrement dans cet exemple, bien que les enregistrements des pages feuilles enregistrent souvent directement le TID de la table dans le champ ctid. tids est la liste de TID enregistrée sous la forme d'une liste d'éléments.

Dans un bloc interne (non affiché), la partie du numéro de bloc dans le champ ctid esy un lien inférieur (« downlink »), qui est un numéro de bloc vers un autre bloc de l'index lui-même. La partie décalage (offset, le deuxième nombre) du champ ctid enregistre des informations codées sur l'enregistrement, telle que le nombre de colonnes présentes (le troncage du suffixe pourrait avoir supprimé des colonnes de suffixe inutiles). Les colonnes tronquées sont traitées comme ayant la valeur « moins infini ».

htid affiche un TID de table pour l'enregistrement, quelque soit la représentation sous-jacente de l'enregistrement. Cette valeur peut correspondre à un ctid ou peut être décodée parmi les représentations alternatives utilisées par les enregistrements des listes d'éléments et par les enregistrements des pages internes. Les enregistrements dans les pages internes ont habituellement la colonne TID de la table tronquée, ce qui est représenté comme une valeur htid NULL.

Notez que le premier élément une page (autre que la dernière, toute page avec une valeur différente de zéro dans le champ btpo_next) est la « clé haute » du bloc, ce qui signifie que ces données (data) serve comme limite haute de tous les éléments apparaissant sur la page, alors que son champ ctid ne pointe pas vers un autre bloc. De plus, sur les pages internes, le premier véritable élément de donnée (le premier élément qui n'est pas une clé haute) a chaque colonne tronquée, laissant aucune valeur réelle dans son champ data. Néanmoins, un tel élément n'a pas de lien inférieur valide dans son champ ctid.

Pour plus de détails sur la structure des index B-tree, voir Section 64.4.1. Pour plus de détails sur la déduplication et les listes d'éléments, voir Section 64.4.3.

bt_page_items(page bytea) returns setof record

Il est également possible de passer une page à bt_page_items comme une valeur de type bytea. Une image de page obtenue avec get_raw_page devrait être passé en argument. Ainsi le précédent exemple pourrait également être réécrit ainsi :

test=# SELECT itemoffset, ctid, itemlen, nulls, vars, data, dead, htid, tids[0:2] AS some_tids
        FROM bt_page_items(get_raw_page('tenk2_hundred', 5));
 itemoffset |   ctid    | itemlen | nulls | vars |          data           | dead |  htid  |      some_tids
------------+-----------+---------+-------+------+-------------------------+------+--------+---------------------
          1 | (16,1)    |      16 | f     | f    | 30 00 00 00 00 00 00 00 |      |        |
          2 | (16,8292) |     616 | f     | f    | 24 00 00 00 00 00 00 00 | f    | (1,6)  | {"(1,6)","(10,22)"}
          3 | (16,8292) |     616 | f     | f    | 25 00 00 00 00 00 00 00 | f    | (1,18) | {"(1,18)","(4,22)"}
          4 | (16,8292) |     616 | f     | f    | 26 00 00 00 00 00 00 00 | f    | (4,18) | {"(4,18)","(6,17)"}
          5 | (16,8292) |     616 | f     | f    | 27 00 00 00 00 00 00 00 | f    | (1,2)  | {"(1,2)","(1,19)"}
          6 | (16,8292) |     616 | f     | f    | 28 00 00 00 00 00 00 00 | f    | (2,24) | {"(2,24)","(4,11)"}
          7 | (16,8292) |     616 | f     | f    | 29 00 00 00 00 00 00 00 | f    | (2,17) | {"(2,17)","(11,2)"}
          8 | (16,8292) |     616 | f     | f    | 2a 00 00 00 00 00 00 00 | f    | (0,25) | {"(0,25)","(3,20)"}
          9 | (16,8292) |     616 | f     | f    | 2b 00 00 00 00 00 00 00 | f    | (0,10) | {"(0,10)","(0,14)"}
         10 | (16,8292) |     616 | f     | f    | 2c 00 00 00 00 00 00 00 | f    | (1,3)  | {"(1,3)","(3,9)"}
         11 | (16,8292) |     616 | f     | f    | 2d 00 00 00 00 00 00 00 | f    | (6,28) | {"(6,28)","(11,1)"}
         12 | (16,8292) |     616 | f     | f    | 2e 00 00 00 00 00 00 00 | f    | (0,27) | {"(0,27)","(1,13)"}
         13 | (16,8292) |     616 | f     | f    | 2f 00 00 00 00 00 00 00 | f    | (4,17) | {"(4,17)","(4,21)"}
(13 rows)
      

Tous les autres détails sont les même qu'expliqué au point précédent.

F.23.4. Fonctions BRIN

brin_page_type(page bytea) returns text

brin_page_type renvoie le type de bloc du bloc indiqué pour l'index BRIN donné ou renvoie une erreur si le bloc n'est pas un bloc valide d'un index BRIN. Par exemple :

test=# SELECT brin_page_type(get_raw_page('brinidx', 0));
 brin_page_type
----------------
 meta
      

brin_metapage_info(page bytea) returns record

brin_metapage_info renvoie une information assortie sur la métapage d'un index BRIN. Par exemple :

test=# SELECT * FROM brin_metapage_info(get_raw_page('brinidx', 0));
   magic    | version | pagesperrange | lastrevmappage
------------+---------+---------------+----------------
 0xA8109CFA |       1 |             4 |              2
      

brin_revmap_data(page bytea) returns setof tid

brin_revmap_data renvoie la liste des identifiants de lignes dans un bloc de type « range map » d'un index BRIN. Par exemple :

test=# SELECT * FROM brin_revmap_data(get_raw_page('brinidx', 2)) LIMIT 5;
  pages
---------
 (6,137)
 (6,138)
 (6,139)
 (6,140)
 (6,141)
      

brin_page_items(page bytea, index oid) returns setof record

brin_page_items renvoie les données enregistrées dans le bloc de données de l'index BRIN. Par exemple :

test=# SELECT * FROM brin_page_items(get_raw_page('brinidx', 5),
                                     'brinidx')
       ORDER BY blknum, attnum LIMIT 6;
 itemoffset | blknum | attnum | allnulls | hasnulls | placeholder |    value
------------+--------+--------+----------+----------+-------------+--------------
        137 |      0 |      1 | t        | f        | f           |
        137 |      0 |      2 | f        | f        | f           | {1 .. 88}
        138 |      4 |      1 | t        | f        | f           |
        138 |      4 |      2 | f        | f        | f           | {89 .. 176}
        139 |      8 |      1 | t        | f        | f           |
        139 |      8 |      2 | f        | f        | f           | {177 .. 264}
      

Les colonnes renvoyées correspondent aux champs des structures BrinMemTuple et BrinValues. Voir src/include/access/brin_tuple.h pour plus de détails.

F.23.5. Fonctions GIN

gin_metapage_info(page bytea) returns record

gin_metapage_info renvoie des informations sur la métapage d'un index GIN index metapage. Par exemple :

test=# SELECT * FROM gin_metapage_info(get_raw_page('gin_index', 0));
-[ RECORD 1 ]----+-----------
pending_head     | 4294967295
pending_tail     | 4294967295
tail_free_size   | 0
n_pending_pages  | 0
n_pending_tuples | 0
n_total_pages    | 7
n_entry_pages    | 6
n_data_pages     | 0
n_entries        | 693
version          | 2
      

gin_page_opaque_info(page bytea) returns record

gin_page_opaque_info renvoie des informations sur la partie opaque d'un index GIN, comme le type de bloc. Par exemple :

test=# SELECT * FROM gin_page_opaque_info(get_raw_page('gin_index', 2));
 rightlink | maxoff |         flags
-----------+--------+------------------------
         5 |      0 | {data,leaf,compressed}
(1 row)
      

gin_leafpage_items(page bytea) returns setof record

gin_leafpage_items renvoie des informations sur les données enregistrées dans un bloc feuille d'un index GIN. Par exemple :

test=# SELECT first_tid, nbytes, tids[0:5] AS some_tids
        FROM gin_leafpage_items(get_raw_page('gin_test_idx', 2));
 first_tid | nbytes |                        some_tids
-----------+--------+----------------------------------------------------------
 (8,41)    |    244 | {"(8,41)","(8,43)","(8,44)","(8,45)","(8,46)"}
 (10,45)   |    248 | {"(10,45)","(10,46)","(10,47)","(10,48)","(10,49)"}
 (12,52)   |    248 | {"(12,52)","(12,53)","(12,54)","(12,55)","(12,56)"}
 (14,59)   |    320 | {"(14,59)","(14,60)","(14,61)","(14,62)","(14,63)"}
 (167,16)  |    376 | {"(167,16)","(167,17)","(167,18)","(167,19)","(167,20)"}
 (170,30)  |    376 | {"(170,30)","(170,31)","(170,32)","(170,33)","(170,34)"}
 (173,44)  |    197 | {"(173,44)","(173,45)","(173,46)","(173,47)","(173,48)"}
(7 rows)
      

F.23.6. Fonctions GiST

gist_page_opaque_info(page bytea) returns record

gist_page_opaque_info renvoie des informations à partir de l'aire opaque d'un bloc d'index GiST, tel que le NSN, le lien droit et le type de bloc. Par exemple :

test=# SELECT * FROM gist_page_opaque_info(get_raw_page('test_gist_idx', 2));
 lsn | nsn | rightlink | flags  
-----+-----+-----------+--------
 0/1 | 0/0 |         1 | {leaf}
(1 row)

gist_page_items(page bytea, index_oid regclass) returns setof record

gist_page_items renvoie des informations sur les données enregistrées dans un bloc d'un index GiST. Par exemple :

test=# SELECT * FROM gist_page_items(get_raw_page('test_gist_idx', 0), 'test_gist_idx');
 itemoffset |   ctid    | itemlen | dead |             keys
------------+-----------+---------+------+-------------------------------
          1 | (1,65535) |      40 | f    | (p)=("(166,166),(1,1)")
          2 | (2,65535) |      40 | f    | (p)=("(332,332),(167,167)")
          3 | (3,65535) |      40 | f    | (p)=("(498,498),(333,333)")
          4 | (4,65535) |      40 | f    | (p)=("(664,664),(499,499)")
          5 | (5,65535) |      40 | f    | (p)=("(830,830),(665,665)")
          6 | (6,65535) |      40 | f    | (p)=("(996,996),(831,831)")
          7 | (7,65535) |      40 | f    | (p)=("(1000,1000),(997,997)")
(7 rows)

gist_page_items_bytea(page bytea) returns setof record

Identique à gist_page_items, mais renvoie la donnée clé sous la forme d'un objet brut de type bytea. Comme il ne tente pas de décoder la clé, il n'a pas besoin de connaître l'index impliqué. Par exemple :

test=# SELECT * FROM gist_page_items_bytea(get_raw_page('test_gist_idx', 0));
 itemoffset |   ctid    | itemlen | dead |                                      key_data                                      
------------+-----------+---------+------+-----------------------------------------​-------------------------------------------
          1 | (1,65535) |      40 | f    | \x00000100ffff28000000000000c0644000000000​00c06440000000000000f03f000000000000f03f
          2 | (2,65535) |      40 | f    | \x00000200ffff28000000000000c0744000000000​00c074400000000000e064400000000000e06440
          3 | (3,65535) |      40 | f    | \x00000300ffff28000000000000207f4000000000​00207f400000000000d074400000000000d07440
          4 | (4,65535) |      40 | f    | \x00000400ffff28000000000000c0844000000000​00c084400000000000307f400000000000307f40
          5 | (5,65535) |      40 | f    | \x00000500ffff28000000000000f0894000000000​00f089400000000000c884400000000000c88440
          6 | (6,65535) |      40 | f    | \x00000600ffff28000000000000208f4000000000​00208f400000000000f889400000000000f88940
          7 | (7,65535) |      40 | f    | \x00000700ffff28000000000000408f4000000000​00408f400000000000288f400000000000288f40
(7 rows)

F.23.7. Fonctions Hash

hash_page_type(page bytea) returns text

hash_page_type renvoie le type de page de la page d'index HASH donné. Par exemple :

test=# SELECT hash_page_type(get_raw_page('con_hash_index', 0));
 hash_page_type
----------------
 metapage
      

hash_page_stats(page bytea) returns setof record

hash_page_stats retourne des informations sur un bucket ou une page overflow d'un index HASH. Par exemple :

test=# SELECT * FROM hash_page_stats(get_raw_page('con_hash_index', 1));
-[ RECORD 1 ]---+-----------
live_items      | 407
dead_items      | 0
page_size       | 8192
free_size       | 8
hasho_prevblkno | 4096
hasho_nextblkno | 8474
hasho_bucket    | 0
hasho_flag      | 66
hasho_page_id   | 65408
      

hash_page_items(page bytea) returns setof record

hash_page_items retourne des informations sur les données stockées dans un bucket ou une page overflow d'une page d'index HASH. Par exemple :

test=# SELECT * FROM hash_page_items(get_raw_page('con_hash_index', 1)) LIMIT 5;
 itemoffset |   ctid    |    data
------------+-----------+------------
          1 | (899,77)  | 1053474816
          2 | (897,29)  | 1053474816
          3 | (894,207) | 1053474816
          4 | (892,159) | 1053474816
          5 | (890,111) | 1053474816
      

hash_bitmap_info(index oid, blkno bigint) returns record

hash_bitmap_info montre le statut d'un bit dans la page de bitmap pour une page overflow en particulier d'un index HASH. Par exemple :

test=# SELECT * FROM hash_bitmap_info('con_hash_index', 2052);
 bitmapblkno | bitmapbit | bitstatus
-------------+-----------+-----------
          65 |         3 | t
      

hash_metapage_info(page bytea) returns record

hash_metapage_info retourne l'information stockée dans la méta-page d'un index HASH index. Par exemple :

test=# SELECT magic, version, ntuples, ffactor, bsize, bmsize, bmshift,
test-#     maxbucket, highmask, lowmask, ovflpoint, firstfree, nmaps, procid,
test-#     regexp_replace(spares::text, '(,0)*}', '}') as spares,
test-#     regexp_replace(mapp::text, '(,0)*}', '}') as mapp
test-# FROM hash_metapage_info(get_raw_page('con_hash_index', 0));
-[ RECORD 1 ]-------------------------------------------------------------------------------
magic     | 105121344
version   | 4
ntuples   | 500500
ffactor   | 40
bsize     | 8152
bmsize    | 4096
bmshift   | 15
maxbucket | 12512
highmask  | 16383
lowmask   | 8191
ovflpoint | 28
firstfree | 1204
nmaps     | 1
procid    | 450
spares    | {0,0,0,0,0,0,1,1,1,1,1,1,1,1,3,4,4,4,45,55,58,59,508,567,628,704,1193,1202,1204}
mapp      | {65}