Coverage Report

Created: 2025-06-15 06:31

/src/postgres/src/backend/access/common/session.c
Line
Count
Source (jump to first uncovered line)
1
/*-------------------------------------------------------------------------
2
 *
3
 * session.c
4
 *    Encapsulation of user session.
5
 *
6
 * This is intended to contain data that needs to be shared between backends
7
 * performing work for a client session.  In particular such a session is
8
 * shared between the leader and worker processes for parallel queries.  At
9
 * some later point it might also become useful infrastructure for separating
10
 * backends from client connections, e.g. for the purpose of pooling.
11
 *
12
 * Currently this infrastructure is used to share:
13
 * - typemod registry for ephemeral row-types, i.e. BlessTupleDesc etc.
14
 *
15
 * Portions Copyright (c) 2017-2025, PostgreSQL Global Development Group
16
 *
17
 * src/backend/access/common/session.c
18
 *
19
 *-------------------------------------------------------------------------
20
 */
21
#include "postgres.h"
22
23
#include "access/session.h"
24
#include "storage/lwlock.h"
25
#include "storage/shm_toc.h"
26
#include "utils/memutils.h"
27
#include "utils/typcache.h"
28
29
/* Magic number for per-session DSM TOC. */
30
0
#define SESSION_MAGIC           0xabb0fbc9
31
32
/*
33
 * We want to create a DSA area to store shared state that has the same
34
 * lifetime as a session.  So far, it's only used to hold the shared record
35
 * type registry.  We don't want it to have to create any DSM segments just
36
 * yet in common cases, so we'll give it enough space to hold a very small
37
 * SharedRecordTypmodRegistry.
38
 */
39
0
#define SESSION_DSA_SIZE          0x30000
40
41
/*
42
 * Magic numbers for state sharing in the per-session DSM area.
43
 */
44
0
#define SESSION_KEY_DSA           UINT64CONST(0xFFFFFFFFFFFF0001)
45
0
#define SESSION_KEY_RECORD_TYPMOD_REGISTRY  UINT64CONST(0xFFFFFFFFFFFF0002)
46
47
/* This backend's current session. */
48
Session    *CurrentSession = NULL;
49
50
/*
51
 * Set up CurrentSession to point to an empty Session object.
52
 */
53
void
54
InitializeSession(void)
55
0
{
56
0
  CurrentSession = MemoryContextAllocZero(TopMemoryContext, sizeof(Session));
57
0
}
58
59
/*
60
 * Initialize the per-session DSM segment if it isn't already initialized, and
61
 * return its handle so that worker processes can attach to it.
62
 *
63
 * Unlike the per-context DSM segment, this segment and its contents are
64
 * reused for future parallel queries.
65
 *
66
 * Return DSM_HANDLE_INVALID if a segment can't be allocated due to lack of
67
 * resources.
68
 */
69
dsm_handle
70
GetSessionDsmHandle(void)
71
0
{
72
0
  shm_toc_estimator estimator;
73
0
  shm_toc    *toc;
74
0
  dsm_segment *seg;
75
0
  size_t    typmod_registry_size;
76
0
  size_t    size;
77
0
  void     *dsa_space;
78
0
  void     *typmod_registry_space;
79
0
  dsa_area   *dsa;
80
0
  MemoryContext old_context;
81
82
  /*
83
   * If we have already created a session-scope DSM segment in this backend,
84
   * return its handle.  The same segment will be used for the rest of this
85
   * backend's lifetime.
86
   */
87
0
  if (CurrentSession->segment != NULL)
88
0
    return dsm_segment_handle(CurrentSession->segment);
89
90
  /* Otherwise, prepare to set one up. */
91
0
  old_context = MemoryContextSwitchTo(TopMemoryContext);
92
0
  shm_toc_initialize_estimator(&estimator);
93
94
  /* Estimate space for the per-session DSA area. */
95
0
  shm_toc_estimate_keys(&estimator, 1);
96
0
  shm_toc_estimate_chunk(&estimator, SESSION_DSA_SIZE);
97
98
  /* Estimate space for the per-session record typmod registry. */
99
0
  typmod_registry_size = SharedRecordTypmodRegistryEstimate();
100
0
  shm_toc_estimate_keys(&estimator, 1);
101
0
  shm_toc_estimate_chunk(&estimator, typmod_registry_size);
102
103
  /* Set up segment and TOC. */
104
0
  size = shm_toc_estimate(&estimator);
105
0
  seg = dsm_create(size, DSM_CREATE_NULL_IF_MAXSEGMENTS);
106
0
  if (seg == NULL)
107
0
  {
108
0
    MemoryContextSwitchTo(old_context);
109
110
0
    return DSM_HANDLE_INVALID;
111
0
  }
112
0
  toc = shm_toc_create(SESSION_MAGIC,
113
0
             dsm_segment_address(seg),
114
0
             size);
115
116
  /* Create per-session DSA area. */
117
0
  dsa_space = shm_toc_allocate(toc, SESSION_DSA_SIZE);
118
0
  dsa = dsa_create_in_place(dsa_space,
119
0
                SESSION_DSA_SIZE,
120
0
                LWTRANCHE_PER_SESSION_DSA,
121
0
                seg);
122
0
  shm_toc_insert(toc, SESSION_KEY_DSA, dsa_space);
123
124
125
  /* Create session-scoped shared record typmod registry. */
126
0
  typmod_registry_space = shm_toc_allocate(toc, typmod_registry_size);
127
0
  SharedRecordTypmodRegistryInit((SharedRecordTypmodRegistry *)
128
0
                   typmod_registry_space, seg, dsa);
129
0
  shm_toc_insert(toc, SESSION_KEY_RECORD_TYPMOD_REGISTRY,
130
0
           typmod_registry_space);
131
132
  /*
133
   * If we got this far, we can pin the shared memory so it stays mapped for
134
   * the rest of this backend's life.  If we don't make it this far, cleanup
135
   * callbacks for anything we installed above (ie currently
136
   * SharedRecordTypmodRegistry) will run when the DSM segment is detached
137
   * by CurrentResourceOwner so we aren't left with a broken CurrentSession.
138
   */
139
0
  dsm_pin_mapping(seg);
140
0
  dsa_pin_mapping(dsa);
141
142
  /* Make segment and area available via CurrentSession. */
143
0
  CurrentSession->segment = seg;
144
0
  CurrentSession->area = dsa;
145
146
0
  MemoryContextSwitchTo(old_context);
147
148
0
  return dsm_segment_handle(seg);
149
0
}
150
151
/*
152
 * Attach to a per-session DSM segment provided by a parallel leader.
153
 */
154
void
155
AttachSession(dsm_handle handle)
156
0
{
157
0
  dsm_segment *seg;
158
0
  shm_toc    *toc;
159
0
  void     *dsa_space;
160
0
  void     *typmod_registry_space;
161
0
  dsa_area   *dsa;
162
0
  MemoryContext old_context;
163
164
0
  old_context = MemoryContextSwitchTo(TopMemoryContext);
165
166
  /* Attach to the DSM segment. */
167
0
  seg = dsm_attach(handle);
168
0
  if (seg == NULL)
169
0
    elog(ERROR, "could not attach to per-session DSM segment");
170
0
  toc = shm_toc_attach(SESSION_MAGIC, dsm_segment_address(seg));
171
172
  /* Attach to the DSA area. */
173
0
  dsa_space = shm_toc_lookup(toc, SESSION_KEY_DSA, false);
174
0
  dsa = dsa_attach_in_place(dsa_space, seg);
175
176
  /* Make them available via the current session. */
177
0
  CurrentSession->segment = seg;
178
0
  CurrentSession->area = dsa;
179
180
  /* Attach to the shared record typmod registry. */
181
0
  typmod_registry_space =
182
0
    shm_toc_lookup(toc, SESSION_KEY_RECORD_TYPMOD_REGISTRY, false);
183
0
  SharedRecordTypmodRegistryAttach((SharedRecordTypmodRegistry *)
184
0
                   typmod_registry_space);
185
186
  /* Remain attached until end of backend or DetachSession(). */
187
0
  dsm_pin_mapping(seg);
188
0
  dsa_pin_mapping(dsa);
189
190
0
  MemoryContextSwitchTo(old_context);
191
0
}
192
193
/*
194
 * Detach from the current session DSM segment.  It's not strictly necessary
195
 * to do this explicitly since we'll detach automatically at backend exit, but
196
 * if we ever reuse parallel workers it will become important for workers to
197
 * detach from one session before attaching to another.  Note that this runs
198
 * detach hooks.
199
 */
200
void
201
DetachSession(void)
202
0
{
203
  /* Runs detach hooks. */
204
0
  dsm_detach(CurrentSession->segment);
205
0
  CurrentSession->segment = NULL;
206
0
  dsa_detach(CurrentSession->area);
207
0
  CurrentSession->area = NULL;
208
0
}