/src/postgres/src/backend/bootstrap/bootstrap.c
Line | Count | Source (jump to first uncovered line) |
1 | | /*------------------------------------------------------------------------- |
2 | | * |
3 | | * bootstrap.c |
4 | | * routines to support running postgres in 'bootstrap' mode |
5 | | * bootstrap mode is used to create the initial template database |
6 | | * |
7 | | * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group |
8 | | * Portions Copyright (c) 1994, Regents of the University of California |
9 | | * |
10 | | * IDENTIFICATION |
11 | | * src/backend/bootstrap/bootstrap.c |
12 | | * |
13 | | *------------------------------------------------------------------------- |
14 | | */ |
15 | | #include "postgres.h" |
16 | | |
17 | | #include <unistd.h> |
18 | | #include <signal.h> |
19 | | |
20 | | #include "access/genam.h" |
21 | | #include "access/heapam.h" |
22 | | #include "access/htup_details.h" |
23 | | #include "access/tableam.h" |
24 | | #include "access/toast_compression.h" |
25 | | #include "access/xact.h" |
26 | | #include "bootstrap/bootstrap.h" |
27 | | #include "catalog/index.h" |
28 | | #include "catalog/pg_collation.h" |
29 | | #include "catalog/pg_type.h" |
30 | | #include "common/link-canary.h" |
31 | | #include "miscadmin.h" |
32 | | #include "nodes/makefuncs.h" |
33 | | #include "pg_getopt.h" |
34 | | #include "postmaster/postmaster.h" |
35 | | #include "storage/bufpage.h" |
36 | | #include "storage/ipc.h" |
37 | | #include "storage/proc.h" |
38 | | #include "utils/builtins.h" |
39 | | #include "utils/fmgroids.h" |
40 | | #include "utils/guc.h" |
41 | | #include "utils/memutils.h" |
42 | | #include "utils/rel.h" |
43 | | #include "utils/relmapper.h" |
44 | | |
45 | | |
46 | | static void CheckerModeMain(void); |
47 | | static void bootstrap_signals(void); |
48 | | static Form_pg_attribute AllocateAttribute(void); |
49 | | static void populate_typ_list(void); |
50 | | static Oid gettype(char *type); |
51 | | static void cleanup(void); |
52 | | |
53 | | /* ---------------- |
54 | | * global variables |
55 | | * ---------------- |
56 | | */ |
57 | | |
58 | | Relation boot_reldesc; /* current relation descriptor */ |
59 | | |
60 | | Form_pg_attribute attrtypes[MAXATTR]; /* points to attribute info */ |
61 | | int numattr; /* number of attributes for cur. rel */ |
62 | | |
63 | | |
64 | | /* |
65 | | * Basic information associated with each type. This is used before |
66 | | * pg_type is filled, so it has to cover the datatypes used as column types |
67 | | * in the core "bootstrapped" catalogs. |
68 | | * |
69 | | * XXX several of these input/output functions do catalog scans |
70 | | * (e.g., F_REGPROCIN scans pg_proc). this obviously creates some |
71 | | * order dependencies in the catalog creation process. |
72 | | */ |
73 | | struct typinfo |
74 | | { |
75 | | char name[NAMEDATALEN]; |
76 | | Oid oid; |
77 | | Oid elem; |
78 | | int16 len; |
79 | | bool byval; |
80 | | char align; |
81 | | char storage; |
82 | | Oid collation; |
83 | | Oid inproc; |
84 | | Oid outproc; |
85 | | }; |
86 | | |
87 | | static const struct typinfo TypInfo[] = { |
88 | | {"bool", BOOLOID, 0, 1, true, TYPALIGN_CHAR, TYPSTORAGE_PLAIN, InvalidOid, |
89 | | F_BOOLIN, F_BOOLOUT}, |
90 | | {"bytea", BYTEAOID, 0, -1, false, TYPALIGN_INT, TYPSTORAGE_EXTENDED, InvalidOid, |
91 | | F_BYTEAIN, F_BYTEAOUT}, |
92 | | {"char", CHAROID, 0, 1, true, TYPALIGN_CHAR, TYPSTORAGE_PLAIN, InvalidOid, |
93 | | F_CHARIN, F_CHAROUT}, |
94 | | {"int2", INT2OID, 0, 2, true, TYPALIGN_SHORT, TYPSTORAGE_PLAIN, InvalidOid, |
95 | | F_INT2IN, F_INT2OUT}, |
96 | | {"int4", INT4OID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid, |
97 | | F_INT4IN, F_INT4OUT}, |
98 | | {"float4", FLOAT4OID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid, |
99 | | F_FLOAT4IN, F_FLOAT4OUT}, |
100 | | {"name", NAMEOID, CHAROID, NAMEDATALEN, false, TYPALIGN_CHAR, TYPSTORAGE_PLAIN, C_COLLATION_OID, |
101 | | F_NAMEIN, F_NAMEOUT}, |
102 | | {"regclass", REGCLASSOID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid, |
103 | | F_REGCLASSIN, F_REGCLASSOUT}, |
104 | | {"regproc", REGPROCOID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid, |
105 | | F_REGPROCIN, F_REGPROCOUT}, |
106 | | {"regtype", REGTYPEOID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid, |
107 | | F_REGTYPEIN, F_REGTYPEOUT}, |
108 | | {"regrole", REGROLEOID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid, |
109 | | F_REGROLEIN, F_REGROLEOUT}, |
110 | | {"regnamespace", REGNAMESPACEOID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid, |
111 | | F_REGNAMESPACEIN, F_REGNAMESPACEOUT}, |
112 | | {"regdatabase", REGDATABASEOID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid, |
113 | | F_REGDATABASEIN, F_REGDATABASEOUT}, |
114 | | {"text", TEXTOID, 0, -1, false, TYPALIGN_INT, TYPSTORAGE_EXTENDED, DEFAULT_COLLATION_OID, |
115 | | F_TEXTIN, F_TEXTOUT}, |
116 | | {"oid", OIDOID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid, |
117 | | F_OIDIN, F_OIDOUT}, |
118 | | {"tid", TIDOID, 0, 6, false, TYPALIGN_SHORT, TYPSTORAGE_PLAIN, InvalidOid, |
119 | | F_TIDIN, F_TIDOUT}, |
120 | | {"xid", XIDOID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid, |
121 | | F_XIDIN, F_XIDOUT}, |
122 | | {"cid", CIDOID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid, |
123 | | F_CIDIN, F_CIDOUT}, |
124 | | {"pg_node_tree", PG_NODE_TREEOID, 0, -1, false, TYPALIGN_INT, TYPSTORAGE_EXTENDED, DEFAULT_COLLATION_OID, |
125 | | F_PG_NODE_TREE_IN, F_PG_NODE_TREE_OUT}, |
126 | | {"int2vector", INT2VECTOROID, INT2OID, -1, false, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid, |
127 | | F_INT2VECTORIN, F_INT2VECTOROUT}, |
128 | | {"oidvector", OIDVECTOROID, OIDOID, -1, false, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid, |
129 | | F_OIDVECTORIN, F_OIDVECTOROUT}, |
130 | | {"_int4", INT4ARRAYOID, INT4OID, -1, false, TYPALIGN_INT, TYPSTORAGE_EXTENDED, InvalidOid, |
131 | | F_ARRAY_IN, F_ARRAY_OUT}, |
132 | | {"_text", 1009, TEXTOID, -1, false, TYPALIGN_INT, TYPSTORAGE_EXTENDED, DEFAULT_COLLATION_OID, |
133 | | F_ARRAY_IN, F_ARRAY_OUT}, |
134 | | {"_oid", 1028, OIDOID, -1, false, TYPALIGN_INT, TYPSTORAGE_EXTENDED, InvalidOid, |
135 | | F_ARRAY_IN, F_ARRAY_OUT}, |
136 | | {"_char", 1002, CHAROID, -1, false, TYPALIGN_INT, TYPSTORAGE_EXTENDED, InvalidOid, |
137 | | F_ARRAY_IN, F_ARRAY_OUT}, |
138 | | {"_aclitem", 1034, ACLITEMOID, -1, false, TYPALIGN_INT, TYPSTORAGE_EXTENDED, InvalidOid, |
139 | | F_ARRAY_IN, F_ARRAY_OUT} |
140 | | }; |
141 | | |
142 | | static const int n_types = sizeof(TypInfo) / sizeof(struct typinfo); |
143 | | |
144 | | struct typmap |
145 | | { /* a hack */ |
146 | | Oid am_oid; |
147 | | FormData_pg_type am_typ; |
148 | | }; |
149 | | |
150 | | static List *Typ = NIL; /* List of struct typmap* */ |
151 | | static struct typmap *Ap = NULL; |
152 | | |
153 | | static Datum values[MAXATTR]; /* current row's attribute values */ |
154 | | static bool Nulls[MAXATTR]; |
155 | | |
156 | | static MemoryContext nogc = NULL; /* special no-gc mem context */ |
157 | | |
158 | | /* |
159 | | * At bootstrap time, we first declare all the indices to be built, and |
160 | | * then build them. The IndexList structure stores enough information |
161 | | * to allow us to build the indices after they've been declared. |
162 | | */ |
163 | | |
164 | | typedef struct _IndexList |
165 | | { |
166 | | Oid il_heap; |
167 | | Oid il_ind; |
168 | | IndexInfo *il_info; |
169 | | struct _IndexList *il_next; |
170 | | } IndexList; |
171 | | |
172 | | static IndexList *ILHead = NULL; |
173 | | |
174 | | |
175 | | /* |
176 | | * In shared memory checker mode, all we really want to do is create shared |
177 | | * memory and semaphores (just to prove we can do it with the current GUC |
178 | | * settings). Since, in fact, that was already done by |
179 | | * CreateSharedMemoryAndSemaphores(), we have nothing more to do here. |
180 | | */ |
181 | | static void |
182 | | CheckerModeMain(void) |
183 | 0 | { |
184 | 0 | proc_exit(0); |
185 | 0 | } |
186 | | |
187 | | /* |
188 | | * The main entry point for running the backend in bootstrap mode |
189 | | * |
190 | | * The bootstrap mode is used to initialize the template database. |
191 | | * The bootstrap backend doesn't speak SQL, but instead expects |
192 | | * commands in a special bootstrap language. |
193 | | * |
194 | | * When check_only is true, startup is done only far enough to verify that |
195 | | * the current configuration, particularly the passed in options pertaining |
196 | | * to shared memory sizing, options work (or at least do not cause an error |
197 | | * up to shared memory creation). |
198 | | */ |
199 | | void |
200 | | BootstrapModeMain(int argc, char *argv[], bool check_only) |
201 | 0 | { |
202 | 0 | int i; |
203 | 0 | char *progname = argv[0]; |
204 | 0 | int flag; |
205 | 0 | char *userDoption = NULL; |
206 | 0 | uint32 bootstrap_data_checksum_version = 0; /* No checksum */ |
207 | 0 | yyscan_t scanner; |
208 | |
|
209 | 0 | Assert(!IsUnderPostmaster); |
210 | |
|
211 | 0 | InitStandaloneProcess(argv[0]); |
212 | | |
213 | | /* Set defaults, to be overridden by explicit options below */ |
214 | 0 | InitializeGUCOptions(); |
215 | | |
216 | | /* an initial --boot or --check should be present */ |
217 | 0 | Assert(argc > 1 |
218 | 0 | && (strcmp(argv[1], "--boot") == 0 |
219 | 0 | || strcmp(argv[1], "--check") == 0)); |
220 | 0 | argv++; |
221 | 0 | argc--; |
222 | |
|
223 | 0 | while ((flag = getopt(argc, argv, "B:c:d:D:Fkr:X:-:")) != -1) |
224 | 0 | { |
225 | 0 | switch (flag) |
226 | 0 | { |
227 | 0 | case 'B': |
228 | 0 | SetConfigOption("shared_buffers", optarg, PGC_POSTMASTER, PGC_S_ARGV); |
229 | 0 | break; |
230 | 0 | case '-': |
231 | | |
232 | | /* |
233 | | * Error if the user misplaced a special must-be-first option |
234 | | * for dispatching to a subprogram. parse_dispatch_option() |
235 | | * returns DISPATCH_POSTMASTER if it doesn't find a match, so |
236 | | * error for anything else. |
237 | | */ |
238 | 0 | if (parse_dispatch_option(optarg) != DISPATCH_POSTMASTER) |
239 | 0 | ereport(ERROR, |
240 | 0 | (errcode(ERRCODE_SYNTAX_ERROR), |
241 | 0 | errmsg("--%s must be first argument", optarg))); |
242 | | |
243 | | /* FALLTHROUGH */ |
244 | 0 | case 'c': |
245 | 0 | { |
246 | 0 | char *name, |
247 | 0 | *value; |
248 | |
|
249 | 0 | ParseLongOption(optarg, &name, &value); |
250 | 0 | if (!value) |
251 | 0 | { |
252 | 0 | if (flag == '-') |
253 | 0 | ereport(ERROR, |
254 | 0 | (errcode(ERRCODE_SYNTAX_ERROR), |
255 | 0 | errmsg("--%s requires a value", |
256 | 0 | optarg))); |
257 | 0 | else |
258 | 0 | ereport(ERROR, |
259 | 0 | (errcode(ERRCODE_SYNTAX_ERROR), |
260 | 0 | errmsg("-c %s requires a value", |
261 | 0 | optarg))); |
262 | 0 | } |
263 | | |
264 | 0 | SetConfigOption(name, value, PGC_POSTMASTER, PGC_S_ARGV); |
265 | 0 | pfree(name); |
266 | 0 | pfree(value); |
267 | 0 | break; |
268 | 0 | } |
269 | 0 | case 'D': |
270 | 0 | userDoption = pstrdup(optarg); |
271 | 0 | break; |
272 | 0 | case 'd': |
273 | 0 | { |
274 | | /* Turn on debugging for the bootstrap process. */ |
275 | 0 | char *debugstr; |
276 | |
|
277 | 0 | debugstr = psprintf("debug%s", optarg); |
278 | 0 | SetConfigOption("log_min_messages", debugstr, |
279 | 0 | PGC_POSTMASTER, PGC_S_ARGV); |
280 | 0 | SetConfigOption("client_min_messages", debugstr, |
281 | 0 | PGC_POSTMASTER, PGC_S_ARGV); |
282 | 0 | pfree(debugstr); |
283 | 0 | } |
284 | 0 | break; |
285 | 0 | case 'F': |
286 | 0 | SetConfigOption("fsync", "false", PGC_POSTMASTER, PGC_S_ARGV); |
287 | 0 | break; |
288 | 0 | case 'k': |
289 | 0 | bootstrap_data_checksum_version = PG_DATA_CHECKSUM_VERSION; |
290 | 0 | break; |
291 | 0 | case 'r': |
292 | 0 | strlcpy(OutputFileName, optarg, MAXPGPATH); |
293 | 0 | break; |
294 | 0 | case 'X': |
295 | 0 | SetConfigOption("wal_segment_size", optarg, PGC_INTERNAL, PGC_S_DYNAMIC_DEFAULT); |
296 | 0 | break; |
297 | 0 | default: |
298 | 0 | write_stderr("Try \"%s --help\" for more information.\n", |
299 | 0 | progname); |
300 | 0 | proc_exit(1); |
301 | 0 | break; |
302 | 0 | } |
303 | 0 | } |
304 | | |
305 | 0 | if (argc != optind) |
306 | 0 | { |
307 | 0 | write_stderr("%s: invalid command-line arguments\n", progname); |
308 | 0 | proc_exit(1); |
309 | 0 | } |
310 | | |
311 | | /* Acquire configuration parameters */ |
312 | 0 | if (!SelectConfigFiles(userDoption, progname)) |
313 | 0 | proc_exit(1); |
314 | | |
315 | | /* |
316 | | * Validate we have been given a reasonable-looking DataDir and change |
317 | | * into it |
318 | | */ |
319 | 0 | checkDataDir(); |
320 | 0 | ChangeToDataDir(); |
321 | |
|
322 | 0 | CreateDataDirLockFile(false); |
323 | |
|
324 | 0 | SetProcessingMode(BootstrapProcessing); |
325 | 0 | IgnoreSystemIndexes = true; |
326 | |
|
327 | 0 | InitializeMaxBackends(); |
328 | | |
329 | | /* |
330 | | * Even though bootstrapping runs in single-process mode, initialize |
331 | | * postmaster child slots array so that --check can detect running out of |
332 | | * shared memory or other resources if max_connections is set too high. |
333 | | */ |
334 | 0 | InitPostmasterChildSlots(); |
335 | |
|
336 | 0 | InitializeFastPathLocks(); |
337 | |
|
338 | 0 | CreateSharedMemoryAndSemaphores(); |
339 | | |
340 | | /* |
341 | | * Estimate number of openable files. This is essential too in --check |
342 | | * mode, because on some platforms semaphores count as open files. |
343 | | */ |
344 | 0 | set_max_safe_fds(); |
345 | | |
346 | | /* |
347 | | * XXX: It might make sense to move this into its own function at some |
348 | | * point. Right now it seems like it'd cause more code duplication than |
349 | | * it's worth. |
350 | | */ |
351 | 0 | if (check_only) |
352 | 0 | { |
353 | 0 | SetProcessingMode(NormalProcessing); |
354 | 0 | CheckerModeMain(); |
355 | 0 | abort(); |
356 | 0 | } |
357 | | |
358 | | /* |
359 | | * Do backend-like initialization for bootstrap mode |
360 | | */ |
361 | 0 | InitProcess(); |
362 | |
|
363 | 0 | BaseInit(); |
364 | |
|
365 | 0 | bootstrap_signals(); |
366 | 0 | BootStrapXLOG(bootstrap_data_checksum_version); |
367 | | |
368 | | /* |
369 | | * To ensure that src/common/link-canary.c is linked into the backend, we |
370 | | * must call it from somewhere. Here is as good as anywhere. |
371 | | */ |
372 | 0 | if (pg_link_canary_is_frontend()) |
373 | 0 | elog(ERROR, "backend is incorrectly linked to frontend functions"); |
374 | | |
375 | 0 | InitPostgres(NULL, InvalidOid, NULL, InvalidOid, 0, NULL); |
376 | | |
377 | | /* Initialize stuff for bootstrap-file processing */ |
378 | 0 | for (i = 0; i < MAXATTR; i++) |
379 | 0 | { |
380 | 0 | attrtypes[i] = NULL; |
381 | 0 | Nulls[i] = false; |
382 | 0 | } |
383 | |
|
384 | 0 | if (boot_yylex_init(&scanner) != 0) |
385 | 0 | elog(ERROR, "yylex_init() failed: %m"); |
386 | | |
387 | | /* |
388 | | * Process bootstrap input. |
389 | | */ |
390 | 0 | StartTransactionCommand(); |
391 | 0 | boot_yyparse(scanner); |
392 | 0 | CommitTransactionCommand(); |
393 | | |
394 | | /* |
395 | | * We should now know about all mapped relations, so it's okay to write |
396 | | * out the initial relation mapping files. |
397 | | */ |
398 | 0 | RelationMapFinishBootstrap(); |
399 | | |
400 | | /* Clean up and exit */ |
401 | 0 | cleanup(); |
402 | 0 | proc_exit(0); |
403 | 0 | } |
404 | | |
405 | | |
406 | | /* ---------------------------------------------------------------- |
407 | | * misc functions |
408 | | * ---------------------------------------------------------------- |
409 | | */ |
410 | | |
411 | | /* |
412 | | * Set up signal handling for a bootstrap process |
413 | | */ |
414 | | static void |
415 | | bootstrap_signals(void) |
416 | 0 | { |
417 | 0 | Assert(!IsUnderPostmaster); |
418 | | |
419 | | /* |
420 | | * We don't actually need any non-default signal handling in bootstrap |
421 | | * mode; "curl up and die" is a sufficient response for all these cases. |
422 | | * Let's set that handling explicitly, as documentation if nothing else. |
423 | | */ |
424 | 0 | pqsignal(SIGHUP, SIG_DFL); |
425 | 0 | pqsignal(SIGINT, SIG_DFL); |
426 | 0 | pqsignal(SIGTERM, SIG_DFL); |
427 | 0 | pqsignal(SIGQUIT, SIG_DFL); |
428 | 0 | } |
429 | | |
430 | | /* ---------------------------------------------------------------- |
431 | | * MANUAL BACKEND INTERACTIVE INTERFACE COMMANDS |
432 | | * ---------------------------------------------------------------- |
433 | | */ |
434 | | |
435 | | /* ---------------- |
436 | | * boot_openrel |
437 | | * |
438 | | * Execute BKI OPEN command. |
439 | | * ---------------- |
440 | | */ |
441 | | void |
442 | | boot_openrel(char *relname) |
443 | 0 | { |
444 | 0 | int i; |
445 | |
|
446 | 0 | if (strlen(relname) >= NAMEDATALEN) |
447 | 0 | relname[NAMEDATALEN - 1] = '\0'; |
448 | | |
449 | | /* |
450 | | * pg_type must be filled before any OPEN command is executed, hence we |
451 | | * can now populate Typ if we haven't yet. |
452 | | */ |
453 | 0 | if (Typ == NIL) |
454 | 0 | populate_typ_list(); |
455 | |
|
456 | 0 | if (boot_reldesc != NULL) |
457 | 0 | closerel(NULL); |
458 | |
|
459 | 0 | elog(DEBUG4, "open relation %s, attrsize %d", |
460 | 0 | relname, (int) ATTRIBUTE_FIXED_PART_SIZE); |
461 | | |
462 | 0 | boot_reldesc = table_openrv(makeRangeVar(NULL, relname, -1), NoLock); |
463 | 0 | numattr = RelationGetNumberOfAttributes(boot_reldesc); |
464 | 0 | for (i = 0; i < numattr; i++) |
465 | 0 | { |
466 | 0 | if (attrtypes[i] == NULL) |
467 | 0 | attrtypes[i] = AllocateAttribute(); |
468 | 0 | memmove(attrtypes[i], |
469 | 0 | TupleDescAttr(boot_reldesc->rd_att, i), |
470 | 0 | ATTRIBUTE_FIXED_PART_SIZE); |
471 | |
|
472 | 0 | { |
473 | 0 | Form_pg_attribute at = attrtypes[i]; |
474 | |
|
475 | 0 | elog(DEBUG4, "create attribute %d name %s len %d num %d type %u", |
476 | 0 | i, NameStr(at->attname), at->attlen, at->attnum, |
477 | 0 | at->atttypid); |
478 | 0 | } |
479 | 0 | } |
480 | 0 | } |
481 | | |
482 | | /* ---------------- |
483 | | * closerel |
484 | | * ---------------- |
485 | | */ |
486 | | void |
487 | | closerel(char *relname) |
488 | 0 | { |
489 | 0 | if (relname) |
490 | 0 | { |
491 | 0 | if (boot_reldesc) |
492 | 0 | { |
493 | 0 | if (strcmp(RelationGetRelationName(boot_reldesc), relname) != 0) |
494 | 0 | elog(ERROR, "close of %s when %s was expected", |
495 | 0 | relname, RelationGetRelationName(boot_reldesc)); |
496 | 0 | } |
497 | 0 | else |
498 | 0 | elog(ERROR, "close of %s before any relation was opened", |
499 | 0 | relname); |
500 | 0 | } |
501 | | |
502 | 0 | if (boot_reldesc == NULL) |
503 | 0 | elog(ERROR, "no open relation to close"); |
504 | 0 | else |
505 | 0 | { |
506 | 0 | elog(DEBUG4, "close relation %s", |
507 | 0 | RelationGetRelationName(boot_reldesc)); |
508 | 0 | table_close(boot_reldesc, NoLock); |
509 | 0 | boot_reldesc = NULL; |
510 | 0 | } |
511 | 0 | } |
512 | | |
513 | | |
514 | | |
515 | | /* ---------------- |
516 | | * DEFINEATTR() |
517 | | * |
518 | | * define a <field,type> pair |
519 | | * if there are n fields in a relation to be created, this routine |
520 | | * will be called n times |
521 | | * ---------------- |
522 | | */ |
523 | | void |
524 | | DefineAttr(char *name, char *type, int attnum, int nullness) |
525 | 0 | { |
526 | 0 | Oid typeoid; |
527 | |
|
528 | 0 | if (boot_reldesc != NULL) |
529 | 0 | { |
530 | 0 | elog(WARNING, "no open relations allowed with CREATE command"); |
531 | 0 | closerel(NULL); |
532 | 0 | } |
533 | | |
534 | 0 | if (attrtypes[attnum] == NULL) |
535 | 0 | attrtypes[attnum] = AllocateAttribute(); |
536 | 0 | MemSet(attrtypes[attnum], 0, ATTRIBUTE_FIXED_PART_SIZE); |
537 | |
|
538 | 0 | namestrcpy(&attrtypes[attnum]->attname, name); |
539 | 0 | elog(DEBUG4, "column %s %s", NameStr(attrtypes[attnum]->attname), type); |
540 | 0 | attrtypes[attnum]->attnum = attnum + 1; |
541 | |
|
542 | 0 | typeoid = gettype(type); |
543 | |
|
544 | 0 | if (Typ != NIL) |
545 | 0 | { |
546 | 0 | attrtypes[attnum]->atttypid = Ap->am_oid; |
547 | 0 | attrtypes[attnum]->attlen = Ap->am_typ.typlen; |
548 | 0 | attrtypes[attnum]->attbyval = Ap->am_typ.typbyval; |
549 | 0 | attrtypes[attnum]->attalign = Ap->am_typ.typalign; |
550 | 0 | attrtypes[attnum]->attstorage = Ap->am_typ.typstorage; |
551 | 0 | attrtypes[attnum]->attcompression = InvalidCompressionMethod; |
552 | 0 | attrtypes[attnum]->attcollation = Ap->am_typ.typcollation; |
553 | | /* if an array type, assume 1-dimensional attribute */ |
554 | 0 | if (Ap->am_typ.typelem != InvalidOid && Ap->am_typ.typlen < 0) |
555 | 0 | attrtypes[attnum]->attndims = 1; |
556 | 0 | else |
557 | 0 | attrtypes[attnum]->attndims = 0; |
558 | 0 | } |
559 | 0 | else |
560 | 0 | { |
561 | 0 | attrtypes[attnum]->atttypid = TypInfo[typeoid].oid; |
562 | 0 | attrtypes[attnum]->attlen = TypInfo[typeoid].len; |
563 | 0 | attrtypes[attnum]->attbyval = TypInfo[typeoid].byval; |
564 | 0 | attrtypes[attnum]->attalign = TypInfo[typeoid].align; |
565 | 0 | attrtypes[attnum]->attstorage = TypInfo[typeoid].storage; |
566 | 0 | attrtypes[attnum]->attcompression = InvalidCompressionMethod; |
567 | 0 | attrtypes[attnum]->attcollation = TypInfo[typeoid].collation; |
568 | | /* if an array type, assume 1-dimensional attribute */ |
569 | 0 | if (TypInfo[typeoid].elem != InvalidOid && |
570 | 0 | attrtypes[attnum]->attlen < 0) |
571 | 0 | attrtypes[attnum]->attndims = 1; |
572 | 0 | else |
573 | 0 | attrtypes[attnum]->attndims = 0; |
574 | 0 | } |
575 | | |
576 | | /* |
577 | | * If a system catalog column is collation-aware, force it to use C |
578 | | * collation, so that its behavior is independent of the database's |
579 | | * collation. This is essential to allow template0 to be cloned with a |
580 | | * different database collation. |
581 | | */ |
582 | 0 | if (OidIsValid(attrtypes[attnum]->attcollation)) |
583 | 0 | attrtypes[attnum]->attcollation = C_COLLATION_OID; |
584 | |
|
585 | 0 | attrtypes[attnum]->atttypmod = -1; |
586 | 0 | attrtypes[attnum]->attislocal = true; |
587 | |
|
588 | 0 | if (nullness == BOOTCOL_NULL_FORCE_NOT_NULL) |
589 | 0 | { |
590 | 0 | attrtypes[attnum]->attnotnull = true; |
591 | 0 | } |
592 | 0 | else if (nullness == BOOTCOL_NULL_FORCE_NULL) |
593 | 0 | { |
594 | 0 | attrtypes[attnum]->attnotnull = false; |
595 | 0 | } |
596 | 0 | else |
597 | 0 | { |
598 | 0 | Assert(nullness == BOOTCOL_NULL_AUTO); |
599 | | |
600 | | /* |
601 | | * Mark as "not null" if type is fixed-width and prior columns are |
602 | | * likewise fixed-width and not-null. This corresponds to case where |
603 | | * column can be accessed directly via C struct declaration. |
604 | | */ |
605 | 0 | if (attrtypes[attnum]->attlen > 0) |
606 | 0 | { |
607 | 0 | int i; |
608 | | |
609 | | /* check earlier attributes */ |
610 | 0 | for (i = 0; i < attnum; i++) |
611 | 0 | { |
612 | 0 | if (attrtypes[i]->attlen <= 0 || |
613 | 0 | !attrtypes[i]->attnotnull) |
614 | 0 | break; |
615 | 0 | } |
616 | 0 | if (i == attnum) |
617 | 0 | attrtypes[attnum]->attnotnull = true; |
618 | 0 | } |
619 | 0 | } |
620 | 0 | } |
621 | | |
622 | | |
623 | | /* ---------------- |
624 | | * InsertOneTuple |
625 | | * |
626 | | * If objectid is not zero, it is a specific OID to assign to the tuple. |
627 | | * Otherwise, an OID will be assigned (if necessary) by heap_insert. |
628 | | * ---------------- |
629 | | */ |
630 | | void |
631 | | InsertOneTuple(void) |
632 | 0 | { |
633 | 0 | HeapTuple tuple; |
634 | 0 | TupleDesc tupDesc; |
635 | 0 | int i; |
636 | |
|
637 | 0 | elog(DEBUG4, "inserting row with %d columns", numattr); |
638 | | |
639 | 0 | tupDesc = CreateTupleDesc(numattr, attrtypes); |
640 | 0 | tuple = heap_form_tuple(tupDesc, values, Nulls); |
641 | 0 | pfree(tupDesc); /* just free's tupDesc, not the attrtypes */ |
642 | |
|
643 | 0 | simple_heap_insert(boot_reldesc, tuple); |
644 | 0 | heap_freetuple(tuple); |
645 | 0 | elog(DEBUG4, "row inserted"); |
646 | | |
647 | | /* |
648 | | * Reset null markers for next tuple |
649 | | */ |
650 | 0 | for (i = 0; i < numattr; i++) |
651 | 0 | Nulls[i] = false; |
652 | 0 | } |
653 | | |
654 | | /* ---------------- |
655 | | * InsertOneValue |
656 | | * ---------------- |
657 | | */ |
658 | | void |
659 | | InsertOneValue(char *value, int i) |
660 | 0 | { |
661 | 0 | Oid typoid; |
662 | 0 | int16 typlen; |
663 | 0 | bool typbyval; |
664 | 0 | char typalign; |
665 | 0 | char typdelim; |
666 | 0 | Oid typioparam; |
667 | 0 | Oid typinput; |
668 | 0 | Oid typoutput; |
669 | |
|
670 | 0 | Assert(i >= 0 && i < MAXATTR); |
671 | |
|
672 | 0 | elog(DEBUG4, "inserting column %d value \"%s\"", i, value); |
673 | | |
674 | 0 | typoid = TupleDescAttr(boot_reldesc->rd_att, i)->atttypid; |
675 | |
|
676 | 0 | boot_get_type_io_data(typoid, |
677 | 0 | &typlen, &typbyval, &typalign, |
678 | 0 | &typdelim, &typioparam, |
679 | 0 | &typinput, &typoutput); |
680 | |
|
681 | 0 | values[i] = OidInputFunctionCall(typinput, value, typioparam, -1); |
682 | | |
683 | | /* |
684 | | * We use ereport not elog here so that parameters aren't evaluated unless |
685 | | * the message is going to be printed, which generally it isn't |
686 | | */ |
687 | 0 | ereport(DEBUG4, |
688 | 0 | (errmsg_internal("inserted -> %s", |
689 | 0 | OidOutputFunctionCall(typoutput, values[i])))); |
690 | 0 | } |
691 | | |
692 | | /* ---------------- |
693 | | * InsertOneNull |
694 | | * ---------------- |
695 | | */ |
696 | | void |
697 | | InsertOneNull(int i) |
698 | 0 | { |
699 | 0 | elog(DEBUG4, "inserting column %d NULL", i); |
700 | 0 | Assert(i >= 0 && i < MAXATTR); |
701 | 0 | if (TupleDescAttr(boot_reldesc->rd_att, i)->attnotnull) |
702 | 0 | elog(ERROR, |
703 | 0 | "NULL value specified for not-null column \"%s\" of relation \"%s\"", |
704 | 0 | NameStr(TupleDescAttr(boot_reldesc->rd_att, i)->attname), |
705 | 0 | RelationGetRelationName(boot_reldesc)); |
706 | 0 | values[i] = PointerGetDatum(NULL); |
707 | 0 | Nulls[i] = true; |
708 | 0 | } |
709 | | |
710 | | /* ---------------- |
711 | | * cleanup |
712 | | * ---------------- |
713 | | */ |
714 | | static void |
715 | | cleanup(void) |
716 | 0 | { |
717 | 0 | if (boot_reldesc != NULL) |
718 | 0 | closerel(NULL); |
719 | 0 | } |
720 | | |
721 | | /* ---------------- |
722 | | * populate_typ_list |
723 | | * |
724 | | * Load the Typ list by reading pg_type. |
725 | | * ---------------- |
726 | | */ |
727 | | static void |
728 | | populate_typ_list(void) |
729 | 0 | { |
730 | 0 | Relation rel; |
731 | 0 | TableScanDesc scan; |
732 | 0 | HeapTuple tup; |
733 | 0 | MemoryContext old; |
734 | |
|
735 | 0 | Assert(Typ == NIL); |
736 | |
|
737 | 0 | rel = table_open(TypeRelationId, NoLock); |
738 | 0 | scan = table_beginscan_catalog(rel, 0, NULL); |
739 | 0 | old = MemoryContextSwitchTo(TopMemoryContext); |
740 | 0 | while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL) |
741 | 0 | { |
742 | 0 | Form_pg_type typForm = (Form_pg_type) GETSTRUCT(tup); |
743 | 0 | struct typmap *newtyp; |
744 | |
|
745 | 0 | newtyp = (struct typmap *) palloc(sizeof(struct typmap)); |
746 | 0 | Typ = lappend(Typ, newtyp); |
747 | |
|
748 | 0 | newtyp->am_oid = typForm->oid; |
749 | 0 | memcpy(&newtyp->am_typ, typForm, sizeof(newtyp->am_typ)); |
750 | 0 | } |
751 | 0 | MemoryContextSwitchTo(old); |
752 | 0 | table_endscan(scan); |
753 | 0 | table_close(rel, NoLock); |
754 | 0 | } |
755 | | |
756 | | /* ---------------- |
757 | | * gettype |
758 | | * |
759 | | * NB: this is really ugly; it will return an integer index into TypInfo[], |
760 | | * and not an OID at all, until the first reference to a type not known in |
761 | | * TypInfo[]. At that point it will read and cache pg_type in Typ, |
762 | | * and subsequently return a real OID (and set the global pointer Ap to |
763 | | * point at the found row in Typ). So caller must check whether Typ is |
764 | | * still NIL to determine what the return value is! |
765 | | * ---------------- |
766 | | */ |
767 | | static Oid |
768 | | gettype(char *type) |
769 | 0 | { |
770 | 0 | if (Typ != NIL) |
771 | 0 | { |
772 | 0 | ListCell *lc; |
773 | |
|
774 | 0 | foreach(lc, Typ) |
775 | 0 | { |
776 | 0 | struct typmap *app = lfirst(lc); |
777 | |
|
778 | 0 | if (strncmp(NameStr(app->am_typ.typname), type, NAMEDATALEN) == 0) |
779 | 0 | { |
780 | 0 | Ap = app; |
781 | 0 | return app->am_oid; |
782 | 0 | } |
783 | 0 | } |
784 | | |
785 | | /* |
786 | | * The type wasn't known; reload the pg_type contents and check again |
787 | | * to handle composite types, added since last populating the list. |
788 | | */ |
789 | | |
790 | 0 | list_free_deep(Typ); |
791 | 0 | Typ = NIL; |
792 | 0 | populate_typ_list(); |
793 | | |
794 | | /* |
795 | | * Calling gettype would result in infinite recursion for types |
796 | | * missing in pg_type, so just repeat the lookup. |
797 | | */ |
798 | 0 | foreach(lc, Typ) |
799 | 0 | { |
800 | 0 | struct typmap *app = lfirst(lc); |
801 | |
|
802 | 0 | if (strncmp(NameStr(app->am_typ.typname), type, NAMEDATALEN) == 0) |
803 | 0 | { |
804 | 0 | Ap = app; |
805 | 0 | return app->am_oid; |
806 | 0 | } |
807 | 0 | } |
808 | 0 | } |
809 | 0 | else |
810 | 0 | { |
811 | 0 | int i; |
812 | |
|
813 | 0 | for (i = 0; i < n_types; i++) |
814 | 0 | { |
815 | 0 | if (strncmp(type, TypInfo[i].name, NAMEDATALEN) == 0) |
816 | 0 | return i; |
817 | 0 | } |
818 | | /* Not in TypInfo, so we'd better be able to read pg_type now */ |
819 | 0 | elog(DEBUG4, "external type: %s", type); |
820 | 0 | populate_typ_list(); |
821 | 0 | return gettype(type); |
822 | 0 | } |
823 | 0 | elog(ERROR, "unrecognized type \"%s\"", type); |
824 | | /* not reached, here to make compiler happy */ |
825 | 0 | return 0; |
826 | 0 | } |
827 | | |
828 | | /* ---------------- |
829 | | * boot_get_type_io_data |
830 | | * |
831 | | * Obtain type I/O information at bootstrap time. This intentionally has |
832 | | * almost the same API as lsyscache.c's get_type_io_data, except that |
833 | | * we only support obtaining the typinput and typoutput routines, not |
834 | | * the binary I/O routines. It is exported so that array_in and array_out |
835 | | * can be made to work during early bootstrap. |
836 | | * ---------------- |
837 | | */ |
838 | | void |
839 | | boot_get_type_io_data(Oid typid, |
840 | | int16 *typlen, |
841 | | bool *typbyval, |
842 | | char *typalign, |
843 | | char *typdelim, |
844 | | Oid *typioparam, |
845 | | Oid *typinput, |
846 | | Oid *typoutput) |
847 | 0 | { |
848 | 0 | if (Typ != NIL) |
849 | 0 | { |
850 | | /* We have the boot-time contents of pg_type, so use it */ |
851 | 0 | struct typmap *ap = NULL; |
852 | 0 | ListCell *lc; |
853 | |
|
854 | 0 | foreach(lc, Typ) |
855 | 0 | { |
856 | 0 | ap = lfirst(lc); |
857 | 0 | if (ap->am_oid == typid) |
858 | 0 | break; |
859 | 0 | } |
860 | |
|
861 | 0 | if (!ap || ap->am_oid != typid) |
862 | 0 | elog(ERROR, "type OID %u not found in Typ list", typid); |
863 | | |
864 | 0 | *typlen = ap->am_typ.typlen; |
865 | 0 | *typbyval = ap->am_typ.typbyval; |
866 | 0 | *typalign = ap->am_typ.typalign; |
867 | 0 | *typdelim = ap->am_typ.typdelim; |
868 | | |
869 | | /* XXX this logic must match getTypeIOParam() */ |
870 | 0 | if (OidIsValid(ap->am_typ.typelem)) |
871 | 0 | *typioparam = ap->am_typ.typelem; |
872 | 0 | else |
873 | 0 | *typioparam = typid; |
874 | |
|
875 | 0 | *typinput = ap->am_typ.typinput; |
876 | 0 | *typoutput = ap->am_typ.typoutput; |
877 | 0 | } |
878 | 0 | else |
879 | 0 | { |
880 | | /* We don't have pg_type yet, so use the hard-wired TypInfo array */ |
881 | 0 | int typeindex; |
882 | |
|
883 | 0 | for (typeindex = 0; typeindex < n_types; typeindex++) |
884 | 0 | { |
885 | 0 | if (TypInfo[typeindex].oid == typid) |
886 | 0 | break; |
887 | 0 | } |
888 | 0 | if (typeindex >= n_types) |
889 | 0 | elog(ERROR, "type OID %u not found in TypInfo", typid); |
890 | | |
891 | 0 | *typlen = TypInfo[typeindex].len; |
892 | 0 | *typbyval = TypInfo[typeindex].byval; |
893 | 0 | *typalign = TypInfo[typeindex].align; |
894 | | /* We assume typdelim is ',' for all boot-time types */ |
895 | 0 | *typdelim = ','; |
896 | | |
897 | | /* XXX this logic must match getTypeIOParam() */ |
898 | 0 | if (OidIsValid(TypInfo[typeindex].elem)) |
899 | 0 | *typioparam = TypInfo[typeindex].elem; |
900 | 0 | else |
901 | 0 | *typioparam = typid; |
902 | |
|
903 | 0 | *typinput = TypInfo[typeindex].inproc; |
904 | 0 | *typoutput = TypInfo[typeindex].outproc; |
905 | 0 | } |
906 | 0 | } |
907 | | |
908 | | /* ---------------- |
909 | | * AllocateAttribute |
910 | | * |
911 | | * Note: bootstrap never sets any per-column ACLs, so we only need |
912 | | * ATTRIBUTE_FIXED_PART_SIZE space per attribute. |
913 | | * ---------------- |
914 | | */ |
915 | | static Form_pg_attribute |
916 | | AllocateAttribute(void) |
917 | 0 | { |
918 | 0 | return (Form_pg_attribute) |
919 | 0 | MemoryContextAllocZero(TopMemoryContext, ATTRIBUTE_FIXED_PART_SIZE); |
920 | 0 | } |
921 | | |
922 | | /* |
923 | | * index_register() -- record an index that has been set up for building |
924 | | * later. |
925 | | * |
926 | | * At bootstrap time, we define a bunch of indexes on system catalogs. |
927 | | * We postpone actually building the indexes until just before we're |
928 | | * finished with initialization, however. This is because the indexes |
929 | | * themselves have catalog entries, and those have to be included in the |
930 | | * indexes on those catalogs. Doing it in two phases is the simplest |
931 | | * way of making sure the indexes have the right contents at the end. |
932 | | */ |
933 | | void |
934 | | index_register(Oid heap, |
935 | | Oid ind, |
936 | | const IndexInfo *indexInfo) |
937 | 0 | { |
938 | 0 | IndexList *newind; |
939 | 0 | MemoryContext oldcxt; |
940 | | |
941 | | /* |
942 | | * XXX mao 10/31/92 -- don't gc index reldescs, associated info at |
943 | | * bootstrap time. we'll declare the indexes now, but want to create them |
944 | | * later. |
945 | | */ |
946 | |
|
947 | 0 | if (nogc == NULL) |
948 | 0 | nogc = AllocSetContextCreate(NULL, |
949 | 0 | "BootstrapNoGC", |
950 | 0 | ALLOCSET_DEFAULT_SIZES); |
951 | |
|
952 | 0 | oldcxt = MemoryContextSwitchTo(nogc); |
953 | |
|
954 | 0 | newind = (IndexList *) palloc(sizeof(IndexList)); |
955 | 0 | newind->il_heap = heap; |
956 | 0 | newind->il_ind = ind; |
957 | 0 | newind->il_info = (IndexInfo *) palloc(sizeof(IndexInfo)); |
958 | |
|
959 | 0 | memcpy(newind->il_info, indexInfo, sizeof(IndexInfo)); |
960 | | /* expressions will likely be null, but may as well copy it */ |
961 | 0 | newind->il_info->ii_Expressions = |
962 | 0 | copyObject(indexInfo->ii_Expressions); |
963 | 0 | newind->il_info->ii_ExpressionsState = NIL; |
964 | | /* predicate will likely be null, but may as well copy it */ |
965 | 0 | newind->il_info->ii_Predicate = |
966 | 0 | copyObject(indexInfo->ii_Predicate); |
967 | 0 | newind->il_info->ii_PredicateState = NULL; |
968 | | /* no exclusion constraints at bootstrap time, so no need to copy */ |
969 | 0 | Assert(indexInfo->ii_ExclusionOps == NULL); |
970 | 0 | Assert(indexInfo->ii_ExclusionProcs == NULL); |
971 | 0 | Assert(indexInfo->ii_ExclusionStrats == NULL); |
972 | |
|
973 | 0 | newind->il_next = ILHead; |
974 | 0 | ILHead = newind; |
975 | |
|
976 | 0 | MemoryContextSwitchTo(oldcxt); |
977 | 0 | } |
978 | | |
979 | | |
980 | | /* |
981 | | * build_indices -- fill in all the indexes registered earlier |
982 | | */ |
983 | | void |
984 | | build_indices(void) |
985 | 0 | { |
986 | 0 | for (; ILHead != NULL; ILHead = ILHead->il_next) |
987 | 0 | { |
988 | 0 | Relation heap; |
989 | 0 | Relation ind; |
990 | | |
991 | | /* need not bother with locks during bootstrap */ |
992 | 0 | heap = table_open(ILHead->il_heap, NoLock); |
993 | 0 | ind = index_open(ILHead->il_ind, NoLock); |
994 | |
|
995 | 0 | index_build(heap, ind, ILHead->il_info, false, false); |
996 | |
|
997 | 0 | index_close(ind, NoLock); |
998 | 0 | table_close(heap, NoLock); |
999 | 0 | } |
1000 | 0 | } |