/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 | } |