1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* nodeSeqscan.c
|
1997-09-07 07:04:48 +02:00
|
|
|
* Support routines for sequential scans of relations.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2006-03-05 16:59:11 +01:00
|
|
|
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
2000-01-26 06:58:53 +01:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2006-07-14 16:52:27 +02:00
|
|
|
* $PostgreSQL: pgsql/src/backend/executor/nodeSeqscan.c,v 1.60 2006/07/14 14:52:19 momjian Exp $
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
* INTERFACE ROUTINES
|
1997-09-07 07:04:48 +02:00
|
|
|
* ExecSeqScan sequentially scans a relation.
|
|
|
|
* ExecSeqNext retrieve next tuple in sequential order.
|
|
|
|
* ExecInitSeqScan creates and initializes a seqscan node.
|
|
|
|
* ExecEndSeqScan releases any storage allocated.
|
|
|
|
* ExecSeqReScan rescans the relation
|
2002-11-30 06:21:03 +01:00
|
|
|
* ExecSeqMarkPos marks scan position
|
|
|
|
* ExecSeqRestrPos restores scan position
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1996-10-31 11:12:26 +01:00
|
|
|
#include "postgres.h"
|
|
|
|
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "access/heapam.h"
|
1996-11-08 07:02:30 +01:00
|
|
|
#include "executor/execdebug.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "executor/nodeSeqscan.h"
|
|
|
|
|
2003-08-08 23:42:59 +02:00
|
|
|
static void InitScanRelation(SeqScanState *node, EState *estate);
|
|
|
|
static TupleTableSlot *SeqNext(SeqScanState *node);
|
1997-08-19 23:40:56 +02:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/* ----------------------------------------------------------------
|
1997-09-07 07:04:48 +02:00
|
|
|
* Scan Support
|
1996-07-09 08:22:35 +02:00
|
|
|
* ----------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
/* ----------------------------------------------------------------
|
1997-09-07 07:04:48 +02:00
|
|
|
* SeqNext
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* This is a workhorse for ExecSeqScan
|
1996-07-09 08:22:35 +02:00
|
|
|
* ----------------------------------------------------------------
|
|
|
|
*/
|
1997-08-19 23:40:56 +02:00
|
|
|
static TupleTableSlot *
|
2003-08-08 23:42:59 +02:00
|
|
|
SeqNext(SeqScanState *node)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
HeapTuple tuple;
|
|
|
|
HeapScanDesc scandesc;
|
2002-12-05 16:50:39 +01:00
|
|
|
Index scanrelid;
|
1997-09-08 04:41:22 +02:00
|
|
|
EState *estate;
|
|
|
|
ScanDirection direction;
|
1997-09-07 07:04:48 +02:00
|
|
|
TupleTableSlot *slot;
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* get information from the estate and scan state
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2002-12-05 16:50:39 +01:00
|
|
|
estate = node->ps.state;
|
|
|
|
scandesc = node->ss_currentScanDesc;
|
|
|
|
scanrelid = ((SeqScan *) node->ps.plan)->scanrelid;
|
1997-09-07 07:04:48 +02:00
|
|
|
direction = estate->es_direction;
|
2002-12-05 16:50:39 +01:00
|
|
|
slot = node->ss_ScanTupleSlot;
|
1999-01-29 10:23:17 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if we are evaluating PlanQual for tuple of this relation.
|
1999-05-25 18:15:34 +02:00
|
|
|
* Additional checking is not good, but no other way for now. We could
|
|
|
|
* introduce new nodes for this case and handle SeqScan --> NewNode
|
|
|
|
* switching in Init/ReScan plan...
|
1999-01-29 10:23:17 +01:00
|
|
|
*/
|
1999-05-25 18:15:34 +02:00
|
|
|
if (estate->es_evTuple != NULL &&
|
2002-12-05 16:50:39 +01:00
|
|
|
estate->es_evTuple[scanrelid - 1] != NULL)
|
1999-01-29 10:23:17 +01:00
|
|
|
{
|
2002-12-05 16:50:39 +01:00
|
|
|
if (estate->es_evTupleNull[scanrelid - 1])
|
2005-11-25 05:24:48 +01:00
|
|
|
return ExecClearTuple(slot);
|
1999-09-24 02:25:33 +02:00
|
|
|
|
2002-12-05 16:50:39 +01:00
|
|
|
ExecStoreTuple(estate->es_evTuple[scanrelid - 1],
|
2001-01-29 01:39:20 +01:00
|
|
|
slot, InvalidBuffer, false);
|
1999-05-25 18:15:34 +02:00
|
|
|
|
1999-01-29 10:23:17 +01:00
|
|
|
/*
|
1999-05-25 18:15:34 +02:00
|
|
|
* Note that unlike IndexScan, SeqScan never use keys in
|
2005-10-15 04:49:52 +02:00
|
|
|
* heap_beginscan (and this is very bad) - so, here we do not check
|
|
|
|
* are keys ok or not.
|
1999-01-29 10:23:17 +01:00
|
|
|
*/
|
1999-09-24 02:25:33 +02:00
|
|
|
|
1999-01-29 10:23:17 +01:00
|
|
|
/* Flag for the next call that no more tuples */
|
2002-12-05 16:50:39 +01:00
|
|
|
estate->es_evTupleNull[scanrelid - 1] = true;
|
2005-11-25 05:24:48 +01:00
|
|
|
return slot;
|
1999-01-29 10:23:17 +01:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* get the next tuple from the access methods
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2002-05-21 01:51:44 +02:00
|
|
|
tuple = heap_getnext(scandesc, direction);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* save the tuple and the buffer returned to us by the access methods in
|
|
|
|
* our scan tuple slot and return the slot. Note: we pass 'false' because
|
|
|
|
* tuples returned by heap_getnext() are pointers onto disk pages and were
|
|
|
|
* not created with palloc() and so should not be pfree()'d. Note also
|
|
|
|
* that ExecStoreTuple will increment the refcount of the buffer; the
|
|
|
|
* refcount will not be dropped until the tuple table slot is cleared.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2005-03-16 22:38:10 +01:00
|
|
|
if (tuple)
|
2005-10-15 04:49:52 +02:00
|
|
|
ExecStoreTuple(tuple, /* tuple to store */
|
|
|
|
slot, /* slot to store in */
|
|
|
|
scandesc->rs_cbuf, /* buffer associated with this
|
|
|
|
* tuple */
|
|
|
|
false); /* don't pfree this pointer */
|
2005-11-25 05:24:48 +01:00
|
|
|
else
|
|
|
|
ExecClearTuple(slot);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
return slot;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
1997-09-07 07:04:48 +02:00
|
|
|
* ExecSeqScan(node)
|
|
|
|
*
|
|
|
|
* Scans the relation sequentially and returns the next qualifying
|
|
|
|
* tuple.
|
|
|
|
* It calls the ExecScan() routine and passes it the access method
|
|
|
|
* which retrieve tuples sequentially.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
TupleTableSlot *
|
2003-08-08 23:42:59 +02:00
|
|
|
ExecSeqScan(SeqScanState *node)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* use SeqNext as access method
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2002-12-05 16:50:39 +01:00
|
|
|
return ExecScan((ScanState *) node, (ExecScanAccessMtd) SeqNext);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
1997-09-07 07:04:48 +02:00
|
|
|
* InitScanRelation
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* This does the initialization for scan relations and
|
|
|
|
* subplans of scans.
|
1996-07-09 08:22:35 +02:00
|
|
|
* ----------------------------------------------------------------
|
|
|
|
*/
|
2002-12-05 16:50:39 +01:00
|
|
|
static void
|
2003-08-08 23:42:59 +02:00
|
|
|
InitScanRelation(SeqScanState *node, EState *estate)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
Relation currentRelation;
|
|
|
|
HeapScanDesc currentScanDesc;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* get the relation object id from the relid'th entry in the range table,
|
2005-12-02 21:03:42 +01:00
|
|
|
* open that relation and acquire appropriate lock on it.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
2005-12-02 21:03:42 +01:00
|
|
|
currentRelation = ExecOpenScanRelation(estate,
|
|
|
|
((SeqScan *) node->ps.plan)->scanrelid);
|
2002-02-19 21:11:20 +01:00
|
|
|
|
|
|
|
currentScanDesc = heap_beginscan(currentRelation,
|
|
|
|
estate->es_snapshot,
|
|
|
|
0,
|
|
|
|
NULL);
|
2000-07-12 04:37:39 +02:00
|
|
|
|
2002-12-05 16:50:39 +01:00
|
|
|
node->ss_currentRelation = currentRelation;
|
|
|
|
node->ss_currentScanDesc = currentScanDesc;
|
2000-07-12 04:37:39 +02:00
|
|
|
|
2006-06-16 20:42:24 +02:00
|
|
|
ExecAssignScanType(node, RelationGetDescr(currentRelation));
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
1997-09-07 07:04:48 +02:00
|
|
|
* ExecInitSeqScan
|
1996-07-09 08:22:35 +02:00
|
|
|
* ----------------------------------------------------------------
|
|
|
|
*/
|
2002-12-05 16:50:39 +01:00
|
|
|
SeqScanState *
|
2006-02-28 05:10:28 +01:00
|
|
|
ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2002-12-05 16:50:39 +01:00
|
|
|
SeqScanState *scanstate;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Once upon a time it was possible to have an outerPlan of a SeqScan, but
|
|
|
|
* not any more.
|
2000-07-12 04:37:39 +02:00
|
|
|
*/
|
2002-12-05 16:50:39 +01:00
|
|
|
Assert(outerPlan(node) == NULL);
|
|
|
|
Assert(innerPlan(node) == NULL);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
2002-12-05 16:50:39 +01:00
|
|
|
* create state structure
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2002-12-05 16:50:39 +01:00
|
|
|
scanstate = makeNode(SeqScanState);
|
|
|
|
scanstate->ps.plan = (Plan *) node;
|
|
|
|
scanstate->ps.state = estate;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* Miscellaneous initialization
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
2001-03-22 07:16:21 +01:00
|
|
|
* create expression context for node
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2002-12-05 16:50:39 +01:00
|
|
|
ExecAssignExprContext(estate, &scanstate->ps);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* initialize child expressions
|
|
|
|
*/
|
|
|
|
scanstate->ps.targetlist = (List *)
|
2002-12-13 20:46:01 +01:00
|
|
|
ExecInitExpr((Expr *) node->plan.targetlist,
|
2002-12-05 16:50:39 +01:00
|
|
|
(PlanState *) scanstate);
|
|
|
|
scanstate->ps.qual = (List *)
|
2002-12-13 20:46:01 +01:00
|
|
|
ExecInitExpr((Expr *) node->plan.qual,
|
2002-12-05 16:50:39 +01:00
|
|
|
(PlanState *) scanstate);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2001-05-27 22:42:20 +02:00
|
|
|
#define SEQSCAN_NSLOTS 2
|
2001-03-22 07:16:21 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* tuple table initialization
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2002-12-05 16:50:39 +01:00
|
|
|
ExecInitResultTupleSlot(estate, &scanstate->ps);
|
1997-09-07 07:04:48 +02:00
|
|
|
ExecInitScanTupleSlot(estate, scanstate);
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* initialize scan relation
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2002-12-05 16:50:39 +01:00
|
|
|
InitScanRelation(scanstate, estate);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-12-05 16:50:39 +01:00
|
|
|
scanstate->ps.ps_TupFromTlist = false;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
2003-01-12 23:01:38 +01:00
|
|
|
* Initialize result tuple type and projection info.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2002-12-05 16:50:39 +01:00
|
|
|
ExecAssignResultTypeFromTL(&scanstate->ps);
|
2003-02-03 16:07:08 +01:00
|
|
|
ExecAssignScanProjectionInfo(scanstate);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-12-05 16:50:39 +01:00
|
|
|
return scanstate;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
1997-09-08 23:56:23 +02:00
|
|
|
ExecCountSlotsSeqScan(SeqScan *node)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
return ExecCountSlotsNode(outerPlan(node)) +
|
2001-10-25 07:50:21 +02:00
|
|
|
ExecCountSlotsNode(innerPlan(node)) +
|
|
|
|
SEQSCAN_NSLOTS;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
1997-09-07 07:04:48 +02:00
|
|
|
* ExecEndSeqScan
|
|
|
|
*
|
|
|
|
* frees any storage allocated through C routines.
|
1996-07-09 08:22:35 +02:00
|
|
|
* ----------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
void
|
2003-08-08 23:42:59 +02:00
|
|
|
ExecEndSeqScan(SeqScanState *node)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2002-02-19 21:11:20 +01:00
|
|
|
Relation relation;
|
|
|
|
HeapScanDesc scanDesc;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* get information from node
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2002-12-05 16:50:39 +01:00
|
|
|
relation = node->ss_currentRelation;
|
|
|
|
scanDesc = node->ss_currentScanDesc;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
2002-12-15 17:17:59 +01:00
|
|
|
* Free the exprcontext
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2002-12-05 16:50:39 +01:00
|
|
|
ExecFreeExprContext(&node->ps);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-12-05 16:50:39 +01:00
|
|
|
/*
|
|
|
|
* clean out the tuple table
|
|
|
|
*/
|
|
|
|
ExecClearTuple(node->ps.ps_ResultTupleSlot);
|
|
|
|
ExecClearTuple(node->ss_ScanTupleSlot);
|
|
|
|
|
2002-12-15 17:17:59 +01:00
|
|
|
/*
|
|
|
|
* close heap scan
|
|
|
|
*/
|
|
|
|
heap_endscan(scanDesc);
|
|
|
|
|
2002-02-19 21:11:20 +01:00
|
|
|
/*
|
|
|
|
* close the heap relation.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2005-12-02 21:03:42 +01:00
|
|
|
ExecCloseScanRelation(relation);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
1997-09-07 07:04:48 +02:00
|
|
|
* Join Support
|
1996-07-09 08:22:35 +02:00
|
|
|
* ----------------------------------------------------------------
|
|
|
|
*/
|
2000-07-12 04:37:39 +02:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/* ----------------------------------------------------------------
|
1997-09-07 07:04:48 +02:00
|
|
|
* ExecSeqReScan
|
|
|
|
*
|
|
|
|
* Rescans the relation.
|
1996-07-09 08:22:35 +02:00
|
|
|
* ----------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
void
|
2003-08-08 23:42:59 +02:00
|
|
|
ExecSeqReScan(SeqScanState *node, ExprContext *exprCtxt)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
EState *estate;
|
2002-12-05 16:50:39 +01:00
|
|
|
Index scanrelid;
|
1998-08-19 04:04:17 +02:00
|
|
|
HeapScanDesc scan;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-12-05 16:50:39 +01:00
|
|
|
estate = node->ps.state;
|
|
|
|
scanrelid = ((SeqScan *) node->ps.plan)->scanrelid;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-07-12 04:37:39 +02:00
|
|
|
/* If this is re-scanning of PlanQual ... */
|
|
|
|
if (estate->es_evTuple != NULL &&
|
2002-12-05 16:50:39 +01:00
|
|
|
estate->es_evTuple[scanrelid - 1] != NULL)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2002-12-05 16:50:39 +01:00
|
|
|
estate->es_evTupleNull[scanrelid - 1] = false;
|
2000-07-12 04:37:39 +02:00
|
|
|
return;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2002-02-19 21:11:20 +01:00
|
|
|
|
2002-12-05 16:50:39 +01:00
|
|
|
scan = node->ss_currentScanDesc;
|
2002-02-19 21:11:20 +01:00
|
|
|
|
|
|
|
heap_rescan(scan, /* scan desc */
|
|
|
|
NULL); /* new scan keys */
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
1997-09-07 07:04:48 +02:00
|
|
|
* ExecSeqMarkPos(node)
|
|
|
|
*
|
|
|
|
* Marks scan position.
|
1996-07-09 08:22:35 +02:00
|
|
|
* ----------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
void
|
2003-08-08 23:42:59 +02:00
|
|
|
ExecSeqMarkPos(SeqScanState *node)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2005-05-15 23:19:55 +02:00
|
|
|
HeapScanDesc scan = node->ss_currentScanDesc;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-08-19 04:04:17 +02:00
|
|
|
heap_markpos(scan);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
1997-09-07 07:04:48 +02:00
|
|
|
* ExecSeqRestrPos
|
|
|
|
*
|
|
|
|
* Restores scan position.
|
1996-07-09 08:22:35 +02:00
|
|
|
* ----------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
void
|
2003-08-08 23:42:59 +02:00
|
|
|
ExecSeqRestrPos(SeqScanState *node)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2005-05-15 23:19:55 +02:00
|
|
|
HeapScanDesc scan = node->ss_currentScanDesc;
|
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Clear any reference to the previously returned tuple. This is needed
|
|
|
|
* because the slot is simply pointing at scan->rs_cbuf, which
|
|
|
|
* heap_restrpos will change; we'd have an internally inconsistent slot if
|
|
|
|
* we didn't do this.
|
2005-05-15 23:19:55 +02:00
|
|
|
*/
|
|
|
|
ExecClearTuple(node->ss_ScanTupleSlot);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-08-19 04:04:17 +02:00
|
|
|
heap_restrpos(scan);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|