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