/src/postgres/src/backend/bootstrap/bootstrap.c
Line | Count | Source |
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-2026, 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_authid.h" |
29 | | #include "catalog/pg_collation.h" |
30 | | #include "catalog/pg_proc.h" |
31 | | #include "catalog/pg_type.h" |
32 | | #include "common/link-canary.h" |
33 | | #include "miscadmin.h" |
34 | | #include "nodes/makefuncs.h" |
35 | | #include "port/pg_getopt_ctx.h" |
36 | | #include "postmaster/postmaster.h" |
37 | | #include "storage/bufpage.h" |
38 | | #include "storage/checksum.h" |
39 | | #include "storage/fd.h" |
40 | | #include "storage/ipc.h" |
41 | | #include "storage/proc.h" |
42 | | #include "storage/shmem_internal.h" |
43 | | #include "utils/builtins.h" |
44 | | #include "utils/fmgroids.h" |
45 | | #include "utils/guc.h" |
46 | | #include "utils/memutils.h" |
47 | | #include "utils/rel.h" |
48 | | #include "utils/relmapper.h" |
49 | | |
50 | | |
51 | | static void CheckerModeMain(void); |
52 | | static void bootstrap_signals(void); |
53 | | static Form_pg_attribute AllocateAttribute(void); |
54 | | static void InsertOneProargdefaultsValue(char *value); |
55 | | static void populate_typ_list(void); |
56 | | static Oid gettype(char *type); |
57 | | static void cleanup(void); |
58 | | |
59 | | /* ---------------- |
60 | | * global variables |
61 | | * ---------------- |
62 | | */ |
63 | | |
64 | | Relation boot_reldesc; /* current relation descriptor */ |
65 | | |
66 | | Form_pg_attribute attrtypes[MAXATTR]; /* points to attribute info */ |
67 | | int numattr; /* number of attributes for cur. rel */ |
68 | | |
69 | | |
70 | | /* |
71 | | * Basic information associated with each type. This is used before |
72 | | * pg_type is filled, so it has to cover the datatypes used as column types |
73 | | * in the core "bootstrapped" catalogs. |
74 | | * |
75 | | * XXX several of these input/output functions do catalog scans |
76 | | * (e.g., F_REGPROCIN scans pg_proc). this obviously creates some |
77 | | * order dependencies in the catalog creation process. |
78 | | */ |
79 | | struct typinfo |
80 | | { |
81 | | char name[NAMEDATALEN]; |
82 | | Oid oid; |
83 | | Oid elem; |
84 | | int16 len; |
85 | | bool byval; |
86 | | char align; |
87 | | char storage; |
88 | | Oid collation; |
89 | | Oid inproc; |
90 | | Oid outproc; |
91 | | }; |
92 | | |
93 | | static const struct typinfo TypInfo[] = { |
94 | | {"bool", BOOLOID, 0, 1, true, TYPALIGN_CHAR, TYPSTORAGE_PLAIN, InvalidOid, |
95 | | F_BOOLIN, F_BOOLOUT}, |
96 | | {"bytea", BYTEAOID, 0, -1, false, TYPALIGN_INT, TYPSTORAGE_EXTENDED, InvalidOid, |
97 | | F_BYTEAIN, F_BYTEAOUT}, |
98 | | {"char", CHAROID, 0, 1, true, TYPALIGN_CHAR, TYPSTORAGE_PLAIN, InvalidOid, |
99 | | F_CHARIN, F_CHAROUT}, |
100 | | {"cstring", CSTRINGOID, 0, -2, false, TYPALIGN_CHAR, TYPSTORAGE_PLAIN, InvalidOid, |
101 | | F_CSTRING_IN, F_CSTRING_OUT}, |
102 | | {"int2", INT2OID, 0, 2, true, TYPALIGN_SHORT, TYPSTORAGE_PLAIN, InvalidOid, |
103 | | F_INT2IN, F_INT2OUT}, |
104 | | {"int4", INT4OID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid, |
105 | | F_INT4IN, F_INT4OUT}, |
106 | | {"int8", INT8OID, 0, 8, true, TYPALIGN_DOUBLE, TYPSTORAGE_PLAIN, InvalidOid, |
107 | | F_INT8IN, F_INT8OUT}, |
108 | | {"float4", FLOAT4OID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid, |
109 | | F_FLOAT4IN, F_FLOAT4OUT}, |
110 | | {"float8", FLOAT8OID, 0, 8, true, TYPALIGN_DOUBLE, TYPSTORAGE_PLAIN, InvalidOid, |
111 | | F_FLOAT8IN, F_FLOAT8OUT}, |
112 | | {"name", NAMEOID, CHAROID, NAMEDATALEN, false, TYPALIGN_CHAR, TYPSTORAGE_PLAIN, C_COLLATION_OID, |
113 | | F_NAMEIN, F_NAMEOUT}, |
114 | | {"regproc", REGPROCOID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid, |
115 | | F_REGPROCIN, F_REGPROCOUT}, |
116 | | {"text", TEXTOID, 0, -1, false, TYPALIGN_INT, TYPSTORAGE_EXTENDED, DEFAULT_COLLATION_OID, |
117 | | F_TEXTIN, F_TEXTOUT}, |
118 | | {"jsonb", JSONBOID, 0, -1, false, TYPALIGN_INT, TYPSTORAGE_EXTENDED, InvalidOid, |
119 | | F_JSONB_IN, F_JSONB_OUT}, |
120 | | {"oid", OIDOID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid, |
121 | | F_OIDIN, F_OIDOUT}, |
122 | | {"aclitem", ACLITEMOID, 0, 16, false, TYPALIGN_DOUBLE, TYPSTORAGE_PLAIN, InvalidOid, |
123 | | F_ACLITEMIN, F_ACLITEMOUT}, |
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", TEXTARRAYOID, TEXTOID, -1, false, TYPALIGN_INT, TYPSTORAGE_EXTENDED, DEFAULT_COLLATION_OID, |
133 | | F_ARRAY_IN, F_ARRAY_OUT}, |
134 | | {"_oid", OIDARRAYOID, OIDOID, -1, false, TYPALIGN_INT, TYPSTORAGE_EXTENDED, InvalidOid, |
135 | | F_ARRAY_IN, F_ARRAY_OUT}, |
136 | | {"_char", CHARARRAYOID, CHAROID, -1, false, TYPALIGN_INT, TYPSTORAGE_EXTENDED, InvalidOid, |
137 | | F_ARRAY_IN, F_ARRAY_OUT}, |
138 | | {"_aclitem", ACLITEMARRAYOID, ACLITEMOID, -1, false, TYPALIGN_DOUBLE, 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 | | /* |
154 | | * Basic information about built-in roles. |
155 | | * |
156 | | * Presently, this need only list roles that are mentioned in aclitem arrays |
157 | | * in the catalog .dat files. We might as well list everything that is in |
158 | | * pg_authid.dat, since there aren't that many. Like pg_authid.dat, we |
159 | | * represent the bootstrap superuser's name as "POSTGRES", even though it |
160 | | * (probably) won't be that in the finished installation; this means aclitem |
161 | | * entries in .dat files must spell it like that. |
162 | | */ |
163 | | struct rolinfo |
164 | | { |
165 | | const char *rolname; |
166 | | Oid oid; |
167 | | }; |
168 | | |
169 | | static const struct rolinfo RolInfo[] = { |
170 | | {"POSTGRES", BOOTSTRAP_SUPERUSERID}, |
171 | | {"pg_database_owner", ROLE_PG_DATABASE_OWNER}, |
172 | | {"pg_read_all_data", ROLE_PG_READ_ALL_DATA}, |
173 | | {"pg_write_all_data", ROLE_PG_WRITE_ALL_DATA}, |
174 | | {"pg_monitor", ROLE_PG_MONITOR}, |
175 | | {"pg_read_all_settings", ROLE_PG_READ_ALL_SETTINGS}, |
176 | | {"pg_read_all_stats", ROLE_PG_READ_ALL_STATS}, |
177 | | {"pg_stat_scan_tables", ROLE_PG_STAT_SCAN_TABLES}, |
178 | | {"pg_read_server_files", ROLE_PG_READ_SERVER_FILES}, |
179 | | {"pg_write_server_files", ROLE_PG_WRITE_SERVER_FILES}, |
180 | | {"pg_execute_server_program", ROLE_PG_EXECUTE_SERVER_PROGRAM}, |
181 | | {"pg_signal_backend", ROLE_PG_SIGNAL_BACKEND}, |
182 | | {"pg_checkpoint", ROLE_PG_CHECKPOINT}, |
183 | | {"pg_maintain", ROLE_PG_MAINTAIN}, |
184 | | {"pg_use_reserved_connections", ROLE_PG_USE_RESERVED_CONNECTIONS}, |
185 | | {"pg_create_subscription", ROLE_PG_CREATE_SUBSCRIPTION}, |
186 | | {"pg_signal_autovacuum_worker", ROLE_PG_SIGNAL_AUTOVACUUM_WORKER} |
187 | | }; |
188 | | |
189 | | |
190 | | static Datum values[MAXATTR]; /* current row's attribute values */ |
191 | | static bool Nulls[MAXATTR]; |
192 | | |
193 | | static MemoryContext nogc = NULL; /* special no-gc mem context */ |
194 | | |
195 | | /* |
196 | | * At bootstrap time, we first declare all the indices to be built, and |
197 | | * then build them. The IndexList structure stores enough information |
198 | | * to allow us to build the indices after they've been declared. |
199 | | */ |
200 | | |
201 | | typedef struct _IndexList |
202 | | { |
203 | | Oid il_heap; |
204 | | Oid il_ind; |
205 | | IndexInfo *il_info; |
206 | | struct _IndexList *il_next; |
207 | | } IndexList; |
208 | | |
209 | | static IndexList *ILHead = NULL; |
210 | | |
211 | | |
212 | | /* |
213 | | * In shared memory checker mode, all we really want to do is create shared |
214 | | * memory and semaphores (just to prove we can do it with the current GUC |
215 | | * settings). Since, in fact, that was already done by |
216 | | * CreateSharedMemoryAndSemaphores(), we have nothing more to do here. |
217 | | */ |
218 | | static void |
219 | | CheckerModeMain(void) |
220 | 0 | { |
221 | 0 | proc_exit(0); |
222 | 0 | } |
223 | | |
224 | | /* |
225 | | * The main entry point for running the backend in bootstrap mode |
226 | | * |
227 | | * The bootstrap mode is used to initialize the template database. |
228 | | * The bootstrap backend doesn't speak SQL, but instead expects |
229 | | * commands in a special bootstrap language. |
230 | | * |
231 | | * When check_only is true, startup is done only far enough to verify that |
232 | | * the current configuration, particularly the passed in options pertaining |
233 | | * to shared memory sizing, options work (or at least do not cause an error |
234 | | * up to shared memory creation). |
235 | | */ |
236 | | void |
237 | | BootstrapModeMain(int argc, char *argv[], bool check_only) |
238 | 0 | { |
239 | 0 | int i; |
240 | 0 | char *progname = argv[0]; |
241 | 0 | pg_getopt_ctx optctx; |
242 | 0 | int flag; |
243 | 0 | char *userDoption = NULL; |
244 | 0 | uint32 bootstrap_data_checksum_version = PG_DATA_CHECKSUM_OFF; |
245 | 0 | yyscan_t scanner; |
246 | |
|
247 | 0 | Assert(!IsUnderPostmaster); |
248 | |
|
249 | 0 | InitStandaloneProcess(argv[0]); |
250 | | |
251 | | /* Set defaults, to be overridden by explicit options below */ |
252 | 0 | InitializeGUCOptions(); |
253 | | |
254 | | /* an initial --boot or --check should be present */ |
255 | 0 | Assert(argc > 1 |
256 | 0 | && (strcmp(argv[1], "--boot") == 0 |
257 | 0 | || strcmp(argv[1], "--check") == 0)); |
258 | 0 | argv++; |
259 | 0 | argc--; |
260 | |
|
261 | 0 | pg_getopt_start(&optctx, argc, argv, "B:c:d:D:Fkr:X:-:"); |
262 | 0 | while ((flag = pg_getopt_next(&optctx)) != -1) |
263 | 0 | { |
264 | 0 | switch (flag) |
265 | 0 | { |
266 | 0 | case 'B': |
267 | 0 | SetConfigOption("shared_buffers", optctx.optarg, PGC_POSTMASTER, PGC_S_ARGV); |
268 | 0 | break; |
269 | 0 | case '-': |
270 | | |
271 | | /* |
272 | | * Error if the user misplaced a special must-be-first option |
273 | | * for dispatching to a subprogram. parse_dispatch_option() |
274 | | * returns DISPATCH_POSTMASTER if it doesn't find a match, so |
275 | | * error for anything else. |
276 | | */ |
277 | 0 | if (parse_dispatch_option(optctx.optarg) != DISPATCH_POSTMASTER) |
278 | 0 | ereport(ERROR, |
279 | 0 | (errcode(ERRCODE_SYNTAX_ERROR), |
280 | 0 | errmsg("--%s must be first argument", optctx.optarg))); |
281 | | |
282 | 0 | pg_fallthrough; |
283 | 0 | case 'c': |
284 | 0 | { |
285 | 0 | char *name, |
286 | 0 | *value; |
287 | |
|
288 | 0 | ParseLongOption(optctx.optarg, &name, &value); |
289 | 0 | if (!value) |
290 | 0 | { |
291 | 0 | if (flag == '-') |
292 | 0 | ereport(ERROR, |
293 | 0 | (errcode(ERRCODE_SYNTAX_ERROR), |
294 | 0 | errmsg("--%s requires a value", |
295 | 0 | optctx.optarg))); |
296 | 0 | else |
297 | 0 | ereport(ERROR, |
298 | 0 | (errcode(ERRCODE_SYNTAX_ERROR), |
299 | 0 | errmsg("-c %s requires a value", |
300 | 0 | optctx.optarg))); |
301 | 0 | } |
302 | | |
303 | 0 | SetConfigOption(name, value, PGC_POSTMASTER, PGC_S_ARGV); |
304 | 0 | pfree(name); |
305 | 0 | pfree(value); |
306 | 0 | break; |
307 | 0 | } |
308 | 0 | case 'D': |
309 | 0 | userDoption = pstrdup(optctx.optarg); |
310 | 0 | break; |
311 | 0 | case 'd': |
312 | 0 | { |
313 | | /* Turn on debugging for the bootstrap process. */ |
314 | 0 | char *debugstr; |
315 | |
|
316 | 0 | debugstr = psprintf("debug%s", optctx.optarg); |
317 | 0 | SetConfigOption("log_min_messages", debugstr, |
318 | 0 | PGC_POSTMASTER, PGC_S_ARGV); |
319 | 0 | SetConfigOption("client_min_messages", debugstr, |
320 | 0 | PGC_POSTMASTER, PGC_S_ARGV); |
321 | 0 | pfree(debugstr); |
322 | 0 | } |
323 | 0 | break; |
324 | 0 | case 'F': |
325 | 0 | SetConfigOption("fsync", "false", PGC_POSTMASTER, PGC_S_ARGV); |
326 | 0 | break; |
327 | 0 | case 'k': |
328 | 0 | bootstrap_data_checksum_version = PG_DATA_CHECKSUM_VERSION; |
329 | 0 | break; |
330 | 0 | case 'r': |
331 | 0 | strlcpy(OutputFileName, optctx.optarg, MAXPGPATH); |
332 | 0 | break; |
333 | 0 | case 'X': |
334 | 0 | SetConfigOption("wal_segment_size", optctx.optarg, PGC_INTERNAL, PGC_S_DYNAMIC_DEFAULT); |
335 | 0 | break; |
336 | 0 | default: |
337 | 0 | write_stderr("Try \"%s --help\" for more information.\n", |
338 | 0 | progname); |
339 | 0 | proc_exit(1); |
340 | 0 | break; |
341 | 0 | } |
342 | 0 | } |
343 | | |
344 | 0 | if (argc != optctx.optind) |
345 | 0 | { |
346 | 0 | write_stderr("%s: invalid command-line arguments\n", progname); |
347 | 0 | proc_exit(1); |
348 | 0 | } |
349 | | |
350 | | /* Acquire configuration parameters */ |
351 | 0 | if (!SelectConfigFiles(userDoption, progname)) |
352 | 0 | proc_exit(1); |
353 | | |
354 | | /* |
355 | | * Validate we have been given a reasonable-looking DataDir and change |
356 | | * into it |
357 | | */ |
358 | 0 | checkDataDir(); |
359 | 0 | ChangeToDataDir(); |
360 | |
|
361 | 0 | CreateDataDirLockFile(false); |
362 | |
|
363 | 0 | SetProcessingMode(BootstrapProcessing); |
364 | 0 | IgnoreSystemIndexes = true; |
365 | |
|
366 | 0 | RegisterBuiltinShmemCallbacks(); |
367 | |
|
368 | 0 | InitializeMaxBackends(); |
369 | | |
370 | | /* |
371 | | * Even though bootstrapping runs in single-process mode, initialize |
372 | | * postmaster child slots array so that --check can detect running out of |
373 | | * shared memory or other resources if max_connections is set too high. |
374 | | */ |
375 | 0 | InitPostmasterChildSlots(); |
376 | |
|
377 | 0 | InitializeFastPathLocks(); |
378 | |
|
379 | 0 | ShmemCallRequestCallbacks(); |
380 | 0 | CreateSharedMemoryAndSemaphores(); |
381 | | |
382 | | /* |
383 | | * Estimate number of openable files. This is essential too in --check |
384 | | * mode, because on some platforms semaphores count as open files. |
385 | | */ |
386 | 0 | set_max_safe_fds(); |
387 | | |
388 | | /* |
389 | | * XXX: It might make sense to move this into its own function at some |
390 | | * point. Right now it seems like it'd cause more code duplication than |
391 | | * it's worth. |
392 | | */ |
393 | 0 | if (check_only) |
394 | 0 | { |
395 | 0 | SetProcessingMode(NormalProcessing); |
396 | 0 | CheckerModeMain(); |
397 | 0 | abort(); |
398 | 0 | } |
399 | | |
400 | | /* |
401 | | * Do backend-like initialization for bootstrap mode |
402 | | */ |
403 | 0 | InitProcess(); |
404 | |
|
405 | 0 | BaseInit(); |
406 | |
|
407 | 0 | bootstrap_signals(); |
408 | 0 | BootStrapXLOG(bootstrap_data_checksum_version); |
409 | | |
410 | | /* |
411 | | * To ensure that src/common/link-canary.c is linked into the backend, we |
412 | | * must call it from somewhere. Here is as good as anywhere. |
413 | | */ |
414 | 0 | if (pg_link_canary_is_frontend()) |
415 | 0 | elog(ERROR, "backend is incorrectly linked to frontend functions"); |
416 | | |
417 | 0 | InitPostgres(NULL, InvalidOid, NULL, InvalidOid, 0, NULL); |
418 | | |
419 | | /* Initialize stuff for bootstrap-file processing */ |
420 | 0 | for (i = 0; i < MAXATTR; i++) |
421 | 0 | { |
422 | 0 | attrtypes[i] = NULL; |
423 | 0 | Nulls[i] = false; |
424 | 0 | } |
425 | |
|
426 | 0 | if (boot_yylex_init(&scanner) != 0) |
427 | 0 | elog(ERROR, "yylex_init() failed: %m"); |
428 | | |
429 | | /* |
430 | | * Process bootstrap input. |
431 | | */ |
432 | 0 | StartTransactionCommand(); |
433 | 0 | boot_yyparse(scanner); |
434 | 0 | CommitTransactionCommand(); |
435 | | |
436 | | /* |
437 | | * We should now know about all mapped relations, so it's okay to write |
438 | | * out the initial relation mapping files. |
439 | | */ |
440 | 0 | RelationMapFinishBootstrap(); |
441 | | |
442 | | /* Clean up and exit */ |
443 | 0 | cleanup(); |
444 | 0 | proc_exit(0); |
445 | 0 | } |
446 | | |
447 | | |
448 | | /* ---------------------------------------------------------------- |
449 | | * misc functions |
450 | | * ---------------------------------------------------------------- |
451 | | */ |
452 | | |
453 | | /* |
454 | | * Set up signal handling for a bootstrap process |
455 | | */ |
456 | | static void |
457 | | bootstrap_signals(void) |
458 | 0 | { |
459 | 0 | Assert(!IsUnderPostmaster); |
460 | | |
461 | | /* |
462 | | * We don't actually need any non-default signal handling in bootstrap |
463 | | * mode; "curl up and die" is a sufficient response for all these cases. |
464 | | * Let's set that handling explicitly, as documentation if nothing else. |
465 | | */ |
466 | 0 | pqsignal(SIGHUP, PG_SIG_DFL); |
467 | 0 | pqsignal(SIGINT, PG_SIG_DFL); |
468 | 0 | pqsignal(SIGTERM, PG_SIG_DFL); |
469 | 0 | pqsignal(SIGQUIT, PG_SIG_DFL); |
470 | 0 | } |
471 | | |
472 | | /* ---------------------------------------------------------------- |
473 | | * MANUAL BACKEND INTERACTIVE INTERFACE COMMANDS |
474 | | * ---------------------------------------------------------------- |
475 | | */ |
476 | | |
477 | | /* ---------------- |
478 | | * boot_openrel |
479 | | * |
480 | | * Execute BKI OPEN command. |
481 | | * ---------------- |
482 | | */ |
483 | | void |
484 | | boot_openrel(char *relname) |
485 | | { |
486 | | int i; |
487 | | |
488 | | if (strlen(relname) >= NAMEDATALEN) |
489 | | relname[NAMEDATALEN - 1] = '\0'; |
490 | | |
491 | | /* |
492 | | * pg_type must be filled before any OPEN command is executed, hence we |
493 | | * can now populate Typ if we haven't yet. |
494 | | */ |
495 | | if (Typ == NIL) |
496 | | populate_typ_list(); |
497 | | |
498 | | if (boot_reldesc != NULL) |
499 | | closerel(NULL); |
500 | | |
501 | | elog(DEBUG4, "open relation %s, attrsize %d", |
502 | | relname, (int) ATTRIBUTE_FIXED_PART_SIZE); |
503 | | |
504 | | boot_reldesc = table_openrv(makeRangeVar(NULL, relname, -1), NoLock); |
505 | | numattr = RelationGetNumberOfAttributes(boot_reldesc); |
506 | | for (i = 0; i < numattr; i++) |
507 | | { |
508 | | if (attrtypes[i] == NULL) |
509 | | attrtypes[i] = AllocateAttribute(); |
510 | | memmove(attrtypes[i], |
511 | | TupleDescAttr(boot_reldesc->rd_att, i), |
512 | | ATTRIBUTE_FIXED_PART_SIZE); |
513 | | |
514 | | { |
515 | | Form_pg_attribute at = attrtypes[i]; |
516 | | |
517 | | elog(DEBUG4, "create attribute %d name %s len %d num %d type %u", |
518 | | i, NameStr(at->attname), at->attlen, at->attnum, |
519 | | at->atttypid); |
520 | | } |
521 | | } |
522 | | } |
523 | | |
524 | | /* ---------------- |
525 | | * closerel |
526 | | * ---------------- |
527 | | */ |
528 | | void |
529 | | closerel(char *relname) |
530 | | { |
531 | | if (relname) |
532 | | { |
533 | | if (boot_reldesc) |
534 | | { |
535 | | if (strcmp(RelationGetRelationName(boot_reldesc), relname) != 0) |
536 | | elog(ERROR, "close of %s when %s was expected", |
537 | | relname, RelationGetRelationName(boot_reldesc)); |
538 | | } |
539 | | else |
540 | | elog(ERROR, "close of %s before any relation was opened", |
541 | | relname); |
542 | | } |
543 | | |
544 | | if (boot_reldesc == NULL) |
545 | | elog(ERROR, "no open relation to close"); |
546 | | else |
547 | | { |
548 | | elog(DEBUG4, "close relation %s", |
549 | | RelationGetRelationName(boot_reldesc)); |
550 | | table_close(boot_reldesc, NoLock); |
551 | | boot_reldesc = NULL; |
552 | | } |
553 | | } |
554 | | |
555 | | |
556 | | |
557 | | /* ---------------- |
558 | | * DEFINEATTR() |
559 | | * |
560 | | * define a <field,type> pair |
561 | | * if there are n fields in a relation to be created, this routine |
562 | | * will be called n times |
563 | | * ---------------- |
564 | | */ |
565 | | void |
566 | | DefineAttr(char *name, char *type, int attnum, int nullness) |
567 | 0 | { |
568 | 0 | Oid typeoid; |
569 | |
|
570 | 0 | if (boot_reldesc != NULL) |
571 | 0 | { |
572 | 0 | elog(WARNING, "no open relations allowed with CREATE command"); |
573 | 0 | closerel(NULL); |
574 | 0 | } |
575 | | |
576 | 0 | if (attrtypes[attnum] == NULL) |
577 | 0 | attrtypes[attnum] = AllocateAttribute(); |
578 | 0 | MemSet(attrtypes[attnum], 0, ATTRIBUTE_FIXED_PART_SIZE); |
579 | |
|
580 | 0 | namestrcpy(&attrtypes[attnum]->attname, name); |
581 | 0 | elog(DEBUG4, "column %s %s", NameStr(attrtypes[attnum]->attname), type); |
582 | 0 | attrtypes[attnum]->attnum = attnum + 1; |
583 | |
|
584 | 0 | typeoid = gettype(type); |
585 | |
|
586 | 0 | if (Typ != NIL) |
587 | 0 | { |
588 | 0 | attrtypes[attnum]->atttypid = Ap->am_oid; |
589 | 0 | attrtypes[attnum]->attlen = Ap->am_typ.typlen; |
590 | 0 | attrtypes[attnum]->attbyval = Ap->am_typ.typbyval; |
591 | 0 | attrtypes[attnum]->attalign = Ap->am_typ.typalign; |
592 | 0 | attrtypes[attnum]->attstorage = Ap->am_typ.typstorage; |
593 | 0 | attrtypes[attnum]->attcompression = InvalidCompressionMethod; |
594 | 0 | attrtypes[attnum]->attcollation = Ap->am_typ.typcollation; |
595 | | /* if an array type, assume 1-dimensional attribute */ |
596 | 0 | if (Ap->am_typ.typelem != InvalidOid && Ap->am_typ.typlen < 0) |
597 | 0 | attrtypes[attnum]->attndims = 1; |
598 | 0 | else |
599 | 0 | attrtypes[attnum]->attndims = 0; |
600 | 0 | } |
601 | 0 | else |
602 | 0 | { |
603 | 0 | attrtypes[attnum]->atttypid = TypInfo[typeoid].oid; |
604 | 0 | attrtypes[attnum]->attlen = TypInfo[typeoid].len; |
605 | 0 | attrtypes[attnum]->attbyval = TypInfo[typeoid].byval; |
606 | 0 | attrtypes[attnum]->attalign = TypInfo[typeoid].align; |
607 | 0 | attrtypes[attnum]->attstorage = TypInfo[typeoid].storage; |
608 | 0 | attrtypes[attnum]->attcompression = InvalidCompressionMethod; |
609 | 0 | attrtypes[attnum]->attcollation = TypInfo[typeoid].collation; |
610 | | /* if an array type, assume 1-dimensional attribute */ |
611 | 0 | if (TypInfo[typeoid].elem != InvalidOid && |
612 | 0 | attrtypes[attnum]->attlen < 0) |
613 | 0 | attrtypes[attnum]->attndims = 1; |
614 | 0 | else |
615 | 0 | attrtypes[attnum]->attndims = 0; |
616 | 0 | } |
617 | | |
618 | | /* |
619 | | * If a system catalog column is collation-aware, force it to use C |
620 | | * collation, so that its behavior is independent of the database's |
621 | | * collation. This is essential to allow template0 to be cloned with a |
622 | | * different database collation. |
623 | | */ |
624 | 0 | if (OidIsValid(attrtypes[attnum]->attcollation)) |
625 | 0 | attrtypes[attnum]->attcollation = C_COLLATION_OID; |
626 | |
|
627 | 0 | attrtypes[attnum]->atttypmod = -1; |
628 | 0 | attrtypes[attnum]->attislocal = true; |
629 | |
|
630 | 0 | if (nullness == BOOTCOL_NULL_FORCE_NOT_NULL) |
631 | 0 | { |
632 | 0 | attrtypes[attnum]->attnotnull = true; |
633 | 0 | } |
634 | 0 | else if (nullness == BOOTCOL_NULL_FORCE_NULL) |
635 | 0 | { |
636 | 0 | attrtypes[attnum]->attnotnull = false; |
637 | 0 | } |
638 | 0 | else |
639 | 0 | { |
640 | 0 | Assert(nullness == BOOTCOL_NULL_AUTO); |
641 | | |
642 | | /* |
643 | | * Mark as "not null" if type is fixed-width and prior columns are |
644 | | * likewise fixed-width and not-null. This corresponds to case where |
645 | | * column can be accessed directly via C struct declaration. |
646 | | */ |
647 | 0 | if (attrtypes[attnum]->attlen > 0) |
648 | 0 | { |
649 | 0 | int i; |
650 | | |
651 | | /* check earlier attributes */ |
652 | 0 | for (i = 0; i < attnum; i++) |
653 | 0 | { |
654 | 0 | if (attrtypes[i]->attlen <= 0 || |
655 | 0 | !attrtypes[i]->attnotnull) |
656 | 0 | break; |
657 | 0 | } |
658 | 0 | if (i == attnum) |
659 | 0 | attrtypes[attnum]->attnotnull = true; |
660 | 0 | } |
661 | 0 | } |
662 | 0 | } |
663 | | |
664 | | |
665 | | /* ---------------- |
666 | | * InsertOneTuple |
667 | | * |
668 | | * If objectid is not zero, it is a specific OID to assign to the tuple. |
669 | | * Otherwise, an OID will be assigned (if necessary) by heap_insert. |
670 | | * ---------------- |
671 | | */ |
672 | | void |
673 | | InsertOneTuple(void) |
674 | 0 | { |
675 | 0 | HeapTuple tuple; |
676 | 0 | TupleDesc tupDesc; |
677 | 0 | int i; |
678 | |
|
679 | 0 | elog(DEBUG4, "inserting row with %d columns", numattr); |
680 | | |
681 | 0 | tupDesc = CreateTupleDesc(numattr, attrtypes); |
682 | 0 | tuple = heap_form_tuple(tupDesc, values, Nulls); |
683 | 0 | pfree(tupDesc); /* just free's tupDesc, not the attrtypes */ |
684 | |
|
685 | 0 | simple_heap_insert(boot_reldesc, tuple); |
686 | 0 | heap_freetuple(tuple); |
687 | 0 | elog(DEBUG4, "row inserted"); |
688 | | |
689 | | /* |
690 | | * Reset null markers for next tuple |
691 | | */ |
692 | 0 | for (i = 0; i < numattr; i++) |
693 | 0 | Nulls[i] = false; |
694 | 0 | } |
695 | | |
696 | | /* ---------------- |
697 | | * InsertOneValue |
698 | | * ---------------- |
699 | | */ |
700 | | void |
701 | | InsertOneValue(char *value, int i) |
702 | | { |
703 | | Form_pg_attribute attr; |
704 | | Oid typoid; |
705 | | int16 typlen; |
706 | | bool typbyval; |
707 | | char typalign; |
708 | | char typdelim; |
709 | | Oid typioparam; |
710 | | Oid typinput; |
711 | | Oid typoutput; |
712 | | Oid typcollation; |
713 | | |
714 | | Assert(i >= 0 && i < MAXATTR); |
715 | | |
716 | | elog(DEBUG4, "inserting column %d value \"%s\"", i, value); |
717 | | |
718 | | attr = TupleDescAttr(RelationGetDescr(boot_reldesc), i); |
719 | | typoid = attr->atttypid; |
720 | | |
721 | | boot_get_type_io_data(typoid, |
722 | | &typlen, &typbyval, &typalign, |
723 | | &typdelim, &typioparam, |
724 | | &typinput, &typoutput, |
725 | | &typcollation); |
726 | | |
727 | | /* |
728 | | * pg_node_tree values can't be inserted normally (pg_node_tree_in would |
729 | | * just error out), so provide special cases for such columns that we |
730 | | * would like to fill during bootstrap. |
731 | | */ |
732 | | if (typoid == PG_NODE_TREEOID) |
733 | | { |
734 | | /* pg_proc.proargdefaults */ |
735 | | if (RelationGetRelid(boot_reldesc) == ProcedureRelationId && |
736 | | i == Anum_pg_proc_proargdefaults - 1) |
737 | | InsertOneProargdefaultsValue(value); |
738 | | else /* maybe other cases later */ |
739 | | elog(ERROR, "can't handle pg_node_tree input for %s.%s", |
740 | | RelationGetRelationName(boot_reldesc), |
741 | | NameStr(attr->attname)); |
742 | | } |
743 | | else |
744 | | { |
745 | | /* Normal case */ |
746 | | values[i] = OidInputFunctionCall(typinput, value, typioparam, -1); |
747 | | } |
748 | | |
749 | | /* |
750 | | * We use ereport not elog here so that parameters aren't evaluated unless |
751 | | * the message is going to be printed, which generally it isn't |
752 | | */ |
753 | | ereport(DEBUG4, |
754 | | (errmsg_internal("inserted -> %s", |
755 | | OidOutputFunctionCall(typoutput, values[i])))); |
756 | | } |
757 | | |
758 | | /* ---------------- |
759 | | * InsertOneProargdefaultsValue |
760 | | * |
761 | | * In general, proargdefaults can be a list of any expressions, but |
762 | | * for bootstrap we only support a list of Const nodes. The input |
763 | | * has the form of a text array, and we feed non-null elements to the |
764 | | * typinput functions for the appropriate parameters. |
765 | | * ---------------- |
766 | | */ |
767 | | static void |
768 | | InsertOneProargdefaultsValue(char *value) |
769 | 0 | { |
770 | 0 | int pronargs; |
771 | 0 | oidvector *proargtypes; |
772 | 0 | Datum arrayval; |
773 | 0 | Datum *array_datums; |
774 | 0 | bool *array_nulls; |
775 | 0 | int array_count; |
776 | 0 | List *proargdefaults; |
777 | 0 | char *nodestring; |
778 | | |
779 | | /* The pg_proc columns we need to use must have been filled already */ |
780 | 0 | StaticAssertDecl(Anum_pg_proc_pronargs < Anum_pg_proc_proargdefaults, |
781 | 0 | "pronargs must come before proargdefaults"); |
782 | 0 | StaticAssertDecl(Anum_pg_proc_pronargdefaults < Anum_pg_proc_proargdefaults, |
783 | 0 | "pronargdefaults must come before proargdefaults"); |
784 | 0 | StaticAssertDecl(Anum_pg_proc_proargtypes < Anum_pg_proc_proargdefaults, |
785 | 0 | "proargtypes must come before proargdefaults"); |
786 | 0 | if (Nulls[Anum_pg_proc_pronargs - 1]) |
787 | 0 | elog(ERROR, "pronargs must not be null"); |
788 | 0 | if (Nulls[Anum_pg_proc_proargtypes - 1]) |
789 | 0 | elog(ERROR, "proargtypes must not be null"); |
790 | 0 | pronargs = DatumGetInt16(values[Anum_pg_proc_pronargs - 1]); |
791 | 0 | proargtypes = DatumGetPointer(values[Anum_pg_proc_proargtypes - 1]); |
792 | 0 | Assert(pronargs == proargtypes->dim1); |
793 | | |
794 | | /* Parse the input string as an array value, then deconstruct to Datums */ |
795 | 0 | arrayval = OidFunctionCall3(F_ARRAY_IN, |
796 | 0 | CStringGetDatum(value), |
797 | 0 | ObjectIdGetDatum(CSTRINGOID), |
798 | 0 | Int32GetDatum(-1)); |
799 | 0 | deconstruct_array_builtin(DatumGetArrayTypeP(arrayval), CSTRINGOID, |
800 | 0 | &array_datums, &array_nulls, &array_count); |
801 | | |
802 | | /* The values should correspond to the last N argtypes */ |
803 | 0 | if (array_count > pronargs) |
804 | 0 | elog(ERROR, "too many proargdefaults entries"); |
805 | | |
806 | | /* Build the List of Const nodes */ |
807 | 0 | proargdefaults = NIL; |
808 | 0 | for (int i = 0; i < array_count; i++) |
809 | 0 | { |
810 | 0 | Oid argtype = proargtypes->values[pronargs - array_count + i]; |
811 | 0 | int16 typlen; |
812 | 0 | bool typbyval; |
813 | 0 | char typalign; |
814 | 0 | char typdelim; |
815 | 0 | Oid typioparam; |
816 | 0 | Oid typinput; |
817 | 0 | Oid typoutput; |
818 | 0 | Oid typcollation; |
819 | 0 | Datum defval; |
820 | 0 | bool defnull; |
821 | 0 | Const *defConst; |
822 | |
|
823 | 0 | boot_get_type_io_data(argtype, |
824 | 0 | &typlen, &typbyval, &typalign, |
825 | 0 | &typdelim, &typioparam, |
826 | 0 | &typinput, &typoutput, |
827 | 0 | &typcollation); |
828 | |
|
829 | 0 | defnull = array_nulls[i]; |
830 | 0 | if (defnull) |
831 | 0 | defval = (Datum) 0; |
832 | 0 | else |
833 | 0 | defval = OidInputFunctionCall(typinput, |
834 | 0 | DatumGetCString(array_datums[i]), |
835 | 0 | typioparam, -1); |
836 | |
|
837 | 0 | defConst = makeConst(argtype, |
838 | 0 | -1, /* never any typmod */ |
839 | 0 | typcollation, |
840 | 0 | typlen, |
841 | 0 | defval, |
842 | 0 | defnull, |
843 | 0 | typbyval); |
844 | 0 | proargdefaults = lappend(proargdefaults, defConst); |
845 | 0 | } |
846 | | |
847 | | /* |
848 | | * Flatten the List to a node-tree string, then convert to a text datum, |
849 | | * which is the storage representation of pg_node_tree. |
850 | | */ |
851 | 0 | nodestring = nodeToString(proargdefaults); |
852 | 0 | values[Anum_pg_proc_proargdefaults - 1] = CStringGetTextDatum(nodestring); |
853 | 0 | Nulls[Anum_pg_proc_proargdefaults - 1] = false; |
854 | | |
855 | | /* |
856 | | * Hack: fill in pronargdefaults with the right value. This is surely |
857 | | * ugly, but it beats making the programmer do it. |
858 | | */ |
859 | 0 | values[Anum_pg_proc_pronargdefaults - 1] = Int16GetDatum(array_count); |
860 | 0 | Nulls[Anum_pg_proc_pronargdefaults - 1] = false; |
861 | 0 | } |
862 | | |
863 | | /* ---------------- |
864 | | * InsertOneNull |
865 | | * ---------------- |
866 | | */ |
867 | | void |
868 | | InsertOneNull(int i) |
869 | 0 | { |
870 | 0 | elog(DEBUG4, "inserting column %d NULL", i); |
871 | 0 | Assert(i >= 0 && i < MAXATTR); |
872 | 0 | if (TupleDescAttr(boot_reldesc->rd_att, i)->attnotnull) |
873 | 0 | elog(ERROR, |
874 | 0 | "NULL value specified for not-null column \"%s\" of relation \"%s\"", |
875 | 0 | NameStr(TupleDescAttr(boot_reldesc->rd_att, i)->attname), |
876 | 0 | RelationGetRelationName(boot_reldesc)); |
877 | 0 | values[i] = PointerGetDatum(NULL); |
878 | 0 | Nulls[i] = true; |
879 | 0 | } |
880 | | |
881 | | /* ---------------- |
882 | | * cleanup |
883 | | * ---------------- |
884 | | */ |
885 | | static void |
886 | | cleanup(void) |
887 | 0 | { |
888 | 0 | if (boot_reldesc != NULL) |
889 | 0 | closerel(NULL); |
890 | 0 | } |
891 | | |
892 | | /* ---------------- |
893 | | * populate_typ_list |
894 | | * |
895 | | * Load the Typ list by reading pg_type. |
896 | | * ---------------- |
897 | | */ |
898 | | static void |
899 | | populate_typ_list(void) |
900 | 0 | { |
901 | 0 | Relation rel; |
902 | 0 | TableScanDesc scan; |
903 | 0 | HeapTuple tup; |
904 | 0 | MemoryContext old; |
905 | |
|
906 | 0 | Assert(Typ == NIL); |
907 | |
|
908 | 0 | rel = table_open(TypeRelationId, NoLock); |
909 | 0 | scan = table_beginscan_catalog(rel, 0, NULL); |
910 | 0 | old = MemoryContextSwitchTo(TopMemoryContext); |
911 | 0 | while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL) |
912 | 0 | { |
913 | 0 | Form_pg_type typForm = (Form_pg_type) GETSTRUCT(tup); |
914 | 0 | struct typmap *newtyp; |
915 | |
|
916 | 0 | newtyp = palloc_object(struct typmap); |
917 | 0 | Typ = lappend(Typ, newtyp); |
918 | |
|
919 | 0 | newtyp->am_oid = typForm->oid; |
920 | 0 | memcpy(&newtyp->am_typ, typForm, sizeof(newtyp->am_typ)); |
921 | 0 | } |
922 | 0 | MemoryContextSwitchTo(old); |
923 | 0 | table_endscan(scan); |
924 | 0 | table_close(rel, NoLock); |
925 | 0 | } |
926 | | |
927 | | /* ---------------- |
928 | | * gettype |
929 | | * |
930 | | * NB: this is really ugly; it will return an integer index into TypInfo[], |
931 | | * and not an OID at all, until the first reference to a type not known in |
932 | | * TypInfo[]. At that point it will read and cache pg_type in Typ, |
933 | | * and subsequently return a real OID (and set the global pointer Ap to |
934 | | * point at the found row in Typ). So caller must check whether Typ is |
935 | | * still NIL to determine what the return value is! |
936 | | * ---------------- |
937 | | */ |
938 | | static Oid |
939 | | gettype(char *type) |
940 | 0 | { |
941 | 0 | if (Typ != NIL) |
942 | 0 | { |
943 | 0 | ListCell *lc; |
944 | |
|
945 | 0 | foreach(lc, Typ) |
946 | 0 | { |
947 | 0 | struct typmap *app = lfirst(lc); |
948 | |
|
949 | 0 | if (strncmp(NameStr(app->am_typ.typname), type, NAMEDATALEN) == 0) |
950 | 0 | { |
951 | 0 | Ap = app; |
952 | 0 | return app->am_oid; |
953 | 0 | } |
954 | 0 | } |
955 | | |
956 | | /* |
957 | | * The type wasn't known; reload the pg_type contents and check again |
958 | | * to handle composite types, added since last populating the list. |
959 | | */ |
960 | | |
961 | 0 | list_free_deep(Typ); |
962 | 0 | Typ = NIL; |
963 | 0 | populate_typ_list(); |
964 | | |
965 | | /* |
966 | | * Calling gettype would result in infinite recursion for types |
967 | | * missing in pg_type, so just repeat the lookup. |
968 | | */ |
969 | 0 | foreach(lc, Typ) |
970 | 0 | { |
971 | 0 | struct typmap *app = lfirst(lc); |
972 | |
|
973 | 0 | if (strncmp(NameStr(app->am_typ.typname), type, NAMEDATALEN) == 0) |
974 | 0 | { |
975 | 0 | Ap = app; |
976 | 0 | return app->am_oid; |
977 | 0 | } |
978 | 0 | } |
979 | 0 | } |
980 | 0 | else |
981 | 0 | { |
982 | 0 | int i; |
983 | |
|
984 | 0 | for (i = 0; i < n_types; i++) |
985 | 0 | { |
986 | 0 | if (strncmp(type, TypInfo[i].name, NAMEDATALEN) == 0) |
987 | 0 | return i; |
988 | 0 | } |
989 | | /* Not in TypInfo, so we'd better be able to read pg_type now */ |
990 | 0 | elog(DEBUG4, "external type: %s", type); |
991 | 0 | populate_typ_list(); |
992 | 0 | return gettype(type); |
993 | 0 | } |
994 | 0 | elog(ERROR, "unrecognized type \"%s\"", type); |
995 | | /* not reached, here to make compiler happy */ |
996 | 0 | return 0; |
997 | 0 | } |
998 | | |
999 | | /* ---------------- |
1000 | | * boot_get_type_io_data |
1001 | | * |
1002 | | * Obtain type I/O information at bootstrap time. This intentionally has |
1003 | | * an API very close to that of lsyscache.c's get_type_io_data, except that |
1004 | | * we only support obtaining the typinput and typoutput routines, not |
1005 | | * the binary I/O routines, and we also return the type's collation. |
1006 | | * This is exported so that array_in and array_out can be made to work |
1007 | | * during early bootstrap. |
1008 | | * ---------------- |
1009 | | */ |
1010 | | void |
1011 | | boot_get_type_io_data(Oid typid, |
1012 | | int16 *typlen, |
1013 | | bool *typbyval, |
1014 | | char *typalign, |
1015 | | char *typdelim, |
1016 | | Oid *typioparam, |
1017 | | Oid *typinput, |
1018 | | Oid *typoutput, |
1019 | | Oid *typcollation) |
1020 | 0 | { |
1021 | 0 | if (Typ != NIL) |
1022 | 0 | { |
1023 | | /* We have the boot-time contents of pg_type, so use it */ |
1024 | 0 | struct typmap *ap = NULL; |
1025 | 0 | ListCell *lc; |
1026 | |
|
1027 | 0 | foreach(lc, Typ) |
1028 | 0 | { |
1029 | 0 | ap = lfirst(lc); |
1030 | 0 | if (ap->am_oid == typid) |
1031 | 0 | break; |
1032 | 0 | } |
1033 | |
|
1034 | 0 | if (!ap || ap->am_oid != typid) |
1035 | 0 | elog(ERROR, "type OID %u not found in Typ list", typid); |
1036 | | |
1037 | 0 | *typlen = ap->am_typ.typlen; |
1038 | 0 | *typbyval = ap->am_typ.typbyval; |
1039 | 0 | *typalign = ap->am_typ.typalign; |
1040 | 0 | *typdelim = ap->am_typ.typdelim; |
1041 | | |
1042 | | /* XXX this logic must match getTypeIOParam() */ |
1043 | 0 | if (OidIsValid(ap->am_typ.typelem)) |
1044 | 0 | *typioparam = ap->am_typ.typelem; |
1045 | 0 | else |
1046 | 0 | *typioparam = typid; |
1047 | |
|
1048 | 0 | *typinput = ap->am_typ.typinput; |
1049 | 0 | *typoutput = ap->am_typ.typoutput; |
1050 | |
|
1051 | 0 | *typcollation = ap->am_typ.typcollation; |
1052 | 0 | } |
1053 | 0 | else |
1054 | 0 | { |
1055 | | /* We don't have pg_type yet, so use the hard-wired TypInfo array */ |
1056 | 0 | int typeindex; |
1057 | |
|
1058 | 0 | for (typeindex = 0; typeindex < n_types; typeindex++) |
1059 | 0 | { |
1060 | 0 | if (TypInfo[typeindex].oid == typid) |
1061 | 0 | break; |
1062 | 0 | } |
1063 | 0 | if (typeindex >= n_types) |
1064 | 0 | elog(ERROR, "type OID %u not found in TypInfo", typid); |
1065 | | |
1066 | 0 | *typlen = TypInfo[typeindex].len; |
1067 | 0 | *typbyval = TypInfo[typeindex].byval; |
1068 | 0 | *typalign = TypInfo[typeindex].align; |
1069 | | /* We assume typdelim is ',' for all boot-time types */ |
1070 | 0 | *typdelim = ','; |
1071 | | |
1072 | | /* XXX this logic must match getTypeIOParam() */ |
1073 | 0 | if (OidIsValid(TypInfo[typeindex].elem)) |
1074 | 0 | *typioparam = TypInfo[typeindex].elem; |
1075 | 0 | else |
1076 | 0 | *typioparam = typid; |
1077 | |
|
1078 | 0 | *typinput = TypInfo[typeindex].inproc; |
1079 | 0 | *typoutput = TypInfo[typeindex].outproc; |
1080 | |
|
1081 | 0 | *typcollation = TypInfo[typeindex].collation; |
1082 | 0 | } |
1083 | 0 | } |
1084 | | |
1085 | | /* ---------------- |
1086 | | * boot_get_role_oid |
1087 | | * |
1088 | | * Look up a role name at bootstrap time. This is equivalent to |
1089 | | * get_role_oid(rolname, true): return the role OID or InvalidOid if |
1090 | | * not found. We only need to cope with built-in role names. |
1091 | | * ---------------- |
1092 | | */ |
1093 | | Oid |
1094 | | boot_get_role_oid(const char *rolname) |
1095 | 0 | { |
1096 | 0 | for (size_t i = 0; i < lengthof(RolInfo); i++) |
1097 | 0 | { |
1098 | 0 | if (strcmp(RolInfo[i].rolname, rolname) == 0) |
1099 | 0 | return RolInfo[i].oid; |
1100 | 0 | } |
1101 | 0 | return InvalidOid; |
1102 | 0 | } |
1103 | | |
1104 | | /* ---------------- |
1105 | | * AllocateAttribute |
1106 | | * |
1107 | | * Note: bootstrap never sets any per-column ACLs, so we only need |
1108 | | * ATTRIBUTE_FIXED_PART_SIZE space per attribute. |
1109 | | * ---------------- |
1110 | | */ |
1111 | | static Form_pg_attribute |
1112 | | AllocateAttribute(void) |
1113 | 0 | { |
1114 | 0 | return (Form_pg_attribute) |
1115 | 0 | MemoryContextAllocZero(TopMemoryContext, ATTRIBUTE_FIXED_PART_SIZE); |
1116 | 0 | } |
1117 | | |
1118 | | /* |
1119 | | * index_register() -- record an index that has been set up for building |
1120 | | * later. |
1121 | | * |
1122 | | * At bootstrap time, we define a bunch of indexes on system catalogs. |
1123 | | * We postpone actually building the indexes until just before we're |
1124 | | * finished with initialization, however. This is because the indexes |
1125 | | * themselves have catalog entries, and those have to be included in the |
1126 | | * indexes on those catalogs. Doing it in two phases is the simplest |
1127 | | * way of making sure the indexes have the right contents at the end. |
1128 | | */ |
1129 | | void |
1130 | | index_register(Oid heap, |
1131 | | Oid ind, |
1132 | | const IndexInfo *indexInfo) |
1133 | 0 | { |
1134 | 0 | IndexList *newind; |
1135 | 0 | MemoryContext oldcxt; |
1136 | | |
1137 | | /* |
1138 | | * XXX mao 10/31/92 -- don't gc index reldescs, associated info at |
1139 | | * bootstrap time. we'll declare the indexes now, but want to create them |
1140 | | * later. |
1141 | | */ |
1142 | |
|
1143 | 0 | if (nogc == NULL) |
1144 | 0 | nogc = AllocSetContextCreate(NULL, |
1145 | 0 | "BootstrapNoGC", |
1146 | 0 | ALLOCSET_DEFAULT_SIZES); |
1147 | |
|
1148 | 0 | oldcxt = MemoryContextSwitchTo(nogc); |
1149 | |
|
1150 | 0 | newind = palloc_object(IndexList); |
1151 | 0 | newind->il_heap = heap; |
1152 | 0 | newind->il_ind = ind; |
1153 | 0 | newind->il_info = palloc_object(IndexInfo); |
1154 | |
|
1155 | 0 | memcpy(newind->il_info, indexInfo, sizeof(IndexInfo)); |
1156 | | /* expressions will likely be null, but may as well copy it */ |
1157 | 0 | newind->il_info->ii_Expressions = |
1158 | 0 | copyObject(indexInfo->ii_Expressions); |
1159 | 0 | newind->il_info->ii_ExpressionsState = NIL; |
1160 | | /* predicate will likely be null, but may as well copy it */ |
1161 | 0 | newind->il_info->ii_Predicate = |
1162 | 0 | copyObject(indexInfo->ii_Predicate); |
1163 | 0 | newind->il_info->ii_PredicateState = NULL; |
1164 | | /* no exclusion constraints at bootstrap time, so no need to copy */ |
1165 | 0 | Assert(indexInfo->ii_ExclusionOps == NULL); |
1166 | 0 | Assert(indexInfo->ii_ExclusionProcs == NULL); |
1167 | 0 | Assert(indexInfo->ii_ExclusionStrats == NULL); |
1168 | |
|
1169 | 0 | newind->il_next = ILHead; |
1170 | 0 | ILHead = newind; |
1171 | |
|
1172 | 0 | MemoryContextSwitchTo(oldcxt); |
1173 | 0 | } |
1174 | | |
1175 | | |
1176 | | /* |
1177 | | * build_indices -- fill in all the indexes registered earlier |
1178 | | */ |
1179 | | void |
1180 | | build_indices(void) |
1181 | 0 | { |
1182 | 0 | for (; ILHead != NULL; ILHead = ILHead->il_next) |
1183 | 0 | { |
1184 | 0 | Relation heap; |
1185 | 0 | Relation ind; |
1186 | | |
1187 | | /* need not bother with locks during bootstrap */ |
1188 | 0 | heap = table_open(ILHead->il_heap, NoLock); |
1189 | 0 | ind = index_open(ILHead->il_ind, NoLock); |
1190 | |
|
1191 | 0 | index_build(heap, ind, ILHead->il_info, false, false, false); |
1192 | |
|
1193 | 0 | index_close(ind, NoLock); |
1194 | 0 | table_close(heap, NoLock); |
1195 | 0 | } |
1196 | 0 | } |