forked from postgrespro/pg_pathman
/
worker.c
211 lines (182 loc) · 5.32 KB
/
worker.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
#include "pathman.h"
#include "miscadmin.h"
#include "postmaster/bgworker.h"
#include "catalog/pg_type.h"
#include "executor/spi.h"
#include "storage/dsm.h"
#include "access/xact.h"
#include "utils/snapmgr.h"
#include "utils/typcache.h"
/*-------------------------------------------------------------------------
*
* worker.c
*
* The purpose of this module is to create partitions in a separate
* transaction. To do so we create a separate background worker,
* pass arguments to it (see PartitionArgs) and gather the result
* (which is the new partition oid).
*
*-------------------------------------------------------------------------
*/
static dsm_segment *segment;
static void bg_worker_main(Datum main_arg);
typedef struct PartitionArgs
{
Oid dbid;
Oid relid;
#ifdef HAVE_INT64_TIMESTAMP
int64 value;
#else
double value;
#endif
Oid value_type;
bool by_val;
Oid result;
bool crashed;
} PartitionArgs;
/*
* Starts background worker that will create new partitions,
* waits till it finishes the job and returns the result (new partition oid)
*/
Oid
create_partitions_bg_worker(Oid relid, Datum value, Oid value_type, bool *crashed)
{
BackgroundWorker worker;
BackgroundWorkerHandle *worker_handle;
BgwHandleStatus status;
dsm_segment *segment;
dsm_handle segment_handle;
pid_t pid;
PartitionArgs *args;
Oid child_oid;
TypeCacheEntry *tce;
/* Create a dsm segment for the worker to pass arguments */
segment = dsm_create(sizeof(PartitionArgs), 0);
segment_handle = dsm_segment_handle(segment);
tce = lookup_type_cache(value_type, 0);
/* Fill arguments structure */
args = (PartitionArgs *) dsm_segment_address(segment);
args->dbid = MyDatabaseId;
args->relid = relid;
if (tce->typbyval)
args->value = value;
else
memcpy(&args->value, DatumGetPointer(value), sizeof(args->value));
args->by_val = tce->typbyval;
args->value_type = value_type;
args->result = 0;
/* Initialize worker struct */
worker.bgw_flags = BGWORKER_SHMEM_ACCESS |
BGWORKER_BACKEND_DATABASE_CONNECTION;
worker.bgw_start_time = BgWorkerStart_RecoveryFinished;
worker.bgw_restart_time = BGW_NEVER_RESTART;
worker.bgw_main = bg_worker_main;
worker.bgw_main_arg = Int32GetDatum(segment_handle);
worker.bgw_notify_pid = MyProcPid;
/* Start dynamic worker */
if (!RegisterDynamicBackgroundWorker(&worker, &worker_handle))
{
elog(WARNING, "Unable to create background worker for pg_pathman");
}
status = WaitForBackgroundWorkerStartup(worker_handle, &pid);
if (status == BGWH_POSTMASTER_DIED)
{
ereport(WARNING,
(errmsg("Postmaster died during the pg_pathman background worker process"),
errhint("More details may be available in the server log.")));
}
/* Wait till the worker finishes its job */
status = WaitForBackgroundWorkerShutdown(worker_handle);
*crashed = args->crashed;
child_oid = args->result;
/* Free dsm segment */
dsm_detach(segment);
return child_oid;
}
/*
* Main worker routine. Accepts dsm_handle as an argument
*/
static void
bg_worker_main(Datum main_arg)
{
PartitionArgs *args;
dsm_handle handle = DatumGetInt32(main_arg);
/* Create resource owner */
CurrentResourceOwner = ResourceOwnerCreate(NULL, "CreatePartitionsWorker");
/* Attach to dynamic shared memory */
if (!handle)
{
ereport(WARNING,
(errmsg("pg_pathman worker: invalid dsm_handle")));
}
segment = dsm_attach(handle);
args = dsm_segment_address(segment);
/* Establish connection and start transaction */
BackgroundWorkerInitializeConnectionByOid(args->dbid, InvalidOid);
StartTransactionCommand();
SPI_connect();
PushActiveSnapshot(GetTransactionSnapshot());
/* Create partitions */
args->result = create_partitions(args->relid, PATHMAN_GET_DATUM(args->value, args->by_val), args->value_type, &args->crashed);
/* Cleanup */
SPI_finish();
PopActiveSnapshot();
CommitTransactionCommand();
dsm_detach(segment);
}
/*
* Create partitions and return an OID of the partition that contain value
*/
Oid
create_partitions(Oid relid, Datum value, Oid value_type, bool *crashed)
{
int ret;
RangeEntry *ranges;
Datum vals[2];
Oid oids[] = {OIDOID, value_type};
bool nulls[] = {false, false};
char *sql;
bool found;
int pos;
PartRelationInfo *prel;
RangeRelation *rangerel;
FmgrInfo cmp_func;
char *schema;
*crashed = false;
schema = get_extension_schema();
prel = get_pathman_relation_info(relid, NULL);
rangerel = get_pathman_range_relation(relid, NULL);
ranges = dsm_array_get_pointer(&rangerel->ranges);
/* Comparison function */
cmp_func = *get_cmp_func(value_type, prel->atttype);
vals[0] = ObjectIdGetDatum(relid);
vals[1] = value;
/* Perform PL procedure */
sql = psprintf("SELECT %s.append_partitions_on_demand_internal($1, $2)",
schema);
PG_TRY();
{
ret = SPI_execute_with_args(sql, 2, oids, vals, nulls, false, 0);
if (ret > 0)
{
/* Update relation info */
free_dsm_array(&rangerel->ranges);
free_dsm_array(&prel->children);
load_check_constraints(relid, GetCatalogSnapshot(relid));
}
}
PG_CATCH();
{
elog(WARNING, "Attempt to create new partitions failed");
if (crashed != NULL)
*crashed = true;
return 0;
}
PG_END_TRY();
/* Repeat binary search */
ranges = dsm_array_get_pointer(&rangerel->ranges);
pos = range_binary_search(rangerel, &cmp_func, value, &found);
if (found)
return ranges[pos].child_oid;
return 0;
}