1 %{
2 /*-------------------------------------------------------------------------
3  *
4  * bootparse.y
5  *	  yacc grammar for the "bootstrap" mode (BKI file format)
6  *
7  * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
8  * Portions Copyright (c) 1994, Regents of the University of California
9  *
10  *
11  * IDENTIFICATION
12  *	  src/backend/bootstrap/bootparse.y
13  *
14  *-------------------------------------------------------------------------
15  */
16 
17 #include "postgres.h"
18 
19 #include <unistd.h>
20 
21 #include "access/attnum.h"
22 #include "access/htup.h"
23 #include "access/itup.h"
24 #include "access/tupdesc.h"
25 #include "bootstrap/bootstrap.h"
26 #include "catalog/catalog.h"
27 #include "catalog/heap.h"
28 #include "catalog/namespace.h"
29 #include "catalog/pg_am.h"
30 #include "catalog/pg_attribute.h"
31 #include "catalog/pg_authid.h"
32 #include "catalog/pg_class.h"
33 #include "catalog/pg_namespace.h"
34 #include "catalog/pg_tablespace.h"
35 #include "catalog/toasting.h"
36 #include "commands/defrem.h"
37 #include "miscadmin.h"
38 #include "nodes/makefuncs.h"
39 #include "nodes/nodes.h"
40 #include "nodes/parsenodes.h"
41 #include "nodes/pg_list.h"
42 #include "nodes/primnodes.h"
43 #include "rewrite/prs2lock.h"
44 #include "storage/block.h"
45 #include "storage/fd.h"
46 #include "storage/ipc.h"
47 #include "storage/itemptr.h"
48 #include "storage/off.h"
49 #include "storage/smgr.h"
50 #include "tcop/dest.h"
51 #include "utils/memutils.h"
52 #include "utils/rel.h"
53 
54 
55 /*
56  * Bison doesn't allocate anything that needs to live across parser calls,
57  * so we can easily have it use palloc instead of malloc.  This prevents
58  * memory leaks if we error out during parsing.  Note this only works with
59  * bison >= 2.0.  However, in bison 1.875 the default is to use alloca()
60  * if possible, so there's not really much problem anyhow, at least if
61  * you're building with gcc.
62  */
63 #define YYMALLOC palloc
64 #define YYFREE   pfree
65 
66 static MemoryContext per_line_ctx = NULL;
67 
68 static void
do_start(void)69 do_start(void)
70 {
71 	Assert(CurrentMemoryContext == CurTransactionContext);
72 	/* First time through, create the per-line working context */
73 	if (per_line_ctx == NULL)
74 		per_line_ctx = AllocSetContextCreate(CurTransactionContext,
75 											 "bootstrap per-line processing",
76 											 ALLOCSET_DEFAULT_SIZES);
77 	MemoryContextSwitchTo(per_line_ctx);
78 }
79 
80 
81 static void
do_end(void)82 do_end(void)
83 {
84 	/* Reclaim memory allocated while processing this line */
85 	MemoryContextSwitchTo(CurTransactionContext);
86 	MemoryContextReset(per_line_ctx);
87 	CHECK_FOR_INTERRUPTS();		/* allow SIGINT to kill bootstrap run */
88 	if (isatty(0))
89 	{
90 		printf("bootstrap> ");
91 		fflush(stdout);
92 	}
93 }
94 
95 
96 static int num_columns_read = 0;
97 
98 %}
99 
100 %expect 0
101 %name-prefix="boot_yy"
102 
103 %union
104 {
105 	List		*list;
106 	IndexElem	*ielem;
107 	char		*str;
108 	const char	*kw;
109 	int			ival;
110 	Oid			oidval;
111 }
112 
113 %type <list>  boot_index_params
114 %type <ielem> boot_index_param
115 %type <str>   boot_ident
116 %type <ival>  optbootstrap optsharedrelation optwithoutoids boot_column_nullness
117 %type <oidval> oidspec optoideq optrowtypeoid
118 
119 %token <str> ID
120 %token COMMA EQUALS LPAREN RPAREN
121 /* NULLVAL is a reserved keyword */
122 %token NULLVAL
123 /* All the rest are unreserved, and should be handled in boot_ident! */
124 %token <kw> OPEN XCLOSE XCREATE INSERT_TUPLE
125 %token <kw> XDECLARE INDEX ON USING XBUILD INDICES UNIQUE XTOAST
126 %token <kw> OBJ_ID XBOOTSTRAP XSHARED_RELATION XWITHOUT_OIDS XROWTYPE_OID
127 %token <kw> XFORCE XNOT XNULL
128 
129 %start TopLevel
130 
131 %%
132 
133 TopLevel:
134 		  Boot_Queries
135 		|
136 		;
137 
138 Boot_Queries:
139 		  Boot_Query
140 		| Boot_Queries Boot_Query
141 		;
142 
143 Boot_Query :
144 		  Boot_OpenStmt
145 		| Boot_CloseStmt
146 		| Boot_CreateStmt
147 		| Boot_InsertStmt
148 		| Boot_DeclareIndexStmt
149 		| Boot_DeclareUniqueIndexStmt
150 		| Boot_DeclareToastStmt
151 		| Boot_BuildIndsStmt
152 		;
153 
154 Boot_OpenStmt:
155 		  OPEN boot_ident
156 				{
157 					do_start();
158 					boot_openrel($2);
159 					do_end();
160 				}
161 		;
162 
163 Boot_CloseStmt:
164 		  XCLOSE boot_ident
165 				{
166 					do_start();
167 					closerel($2);
168 					do_end();
169 				}
170 		;
171 
172 Boot_CreateStmt:
173 		  XCREATE boot_ident oidspec optbootstrap optsharedrelation optwithoutoids optrowtypeoid LPAREN
174 				{
175 					do_start();
176 					numattr = 0;
177 					elog(DEBUG4, "creating%s%s relation %s %u",
178 						 $4 ? " bootstrap" : "",
179 						 $5 ? " shared" : "",
180 						 $2,
181 						 $3);
182 				}
183 		  boot_column_list
184 				{
185 					do_end();
186 				}
187 		  RPAREN
188 				{
189 					TupleDesc tupdesc;
190 					bool	shared_relation;
191 					bool	mapped_relation;
192 
193 					do_start();
194 
195 					tupdesc = CreateTupleDesc(numattr, !($6), attrtypes);
196 
197 					shared_relation = $5;
198 
199 					/*
200 					 * The catalogs that use the relation mapper are the
201 					 * bootstrap catalogs plus the shared catalogs.  If this
202 					 * ever gets more complicated, we should invent a BKI
203 					 * keyword to mark the mapped catalogs, but for now a
204 					 * quick hack seems the most appropriate thing.  Note in
205 					 * particular that all "nailed" heap rels (see formrdesc
206 					 * in relcache.c) must be mapped.
207 					 */
208 					mapped_relation = ($4 || shared_relation);
209 
210 					if ($4)
211 					{
212 						if (boot_reldesc)
213 						{
214 							elog(DEBUG4, "create bootstrap: warning, open relation exists, closing first");
215 							closerel(NULL);
216 						}
217 
218 						boot_reldesc = heap_create($2,
219 												   PG_CATALOG_NAMESPACE,
220 												   shared_relation ? GLOBALTABLESPACE_OID : 0,
221 												   $3,
222 												   InvalidOid,
223 												   tupdesc,
224 												   RELKIND_RELATION,
225 												   RELPERSISTENCE_PERMANENT,
226 												   shared_relation,
227 												   mapped_relation,
228 												   true);
229 						elog(DEBUG4, "bootstrap relation created");
230 					}
231 					else
232 					{
233 						Oid id;
234 
235 						id = heap_create_with_catalog($2,
236 													  PG_CATALOG_NAMESPACE,
237 													  shared_relation ? GLOBALTABLESPACE_OID : 0,
238 													  $3,
239 													  $7,
240 													  InvalidOid,
241 													  BOOTSTRAP_SUPERUSERID,
242 													  tupdesc,
243 													  NIL,
244 													  RELKIND_RELATION,
245 													  RELPERSISTENCE_PERMANENT,
246 													  shared_relation,
247 													  mapped_relation,
248 													  true,
249 													  0,
250 													  ONCOMMIT_NOOP,
251 													  (Datum) 0,
252 													  false,
253 													  true,
254 													  false,
255 													  InvalidOid,
256 													  NULL);
257 						elog(DEBUG4, "relation created with OID %u", id);
258 					}
259 					do_end();
260 				}
261 		;
262 
263 Boot_InsertStmt:
264 		  INSERT_TUPLE optoideq
265 				{
266 					do_start();
267 					if ($2)
268 						elog(DEBUG4, "inserting row with oid %u", $2);
269 					else
270 						elog(DEBUG4, "inserting row");
271 					num_columns_read = 0;
272 				}
273 		  LPAREN boot_column_val_list RPAREN
274 				{
275 					if (num_columns_read != numattr)
276 						elog(ERROR, "incorrect number of columns in row (expected %d, got %d)",
277 							 numattr, num_columns_read);
278 					if (boot_reldesc == NULL)
279 						elog(FATAL, "relation not open");
280 					InsertOneTuple($2);
281 					do_end();
282 				}
283 		;
284 
285 Boot_DeclareIndexStmt:
286 		  XDECLARE INDEX boot_ident oidspec ON boot_ident USING boot_ident LPAREN boot_index_params RPAREN
287 				{
288 					IndexStmt *stmt = makeNode(IndexStmt);
289 					Oid		relationId;
290 
291 					elog(DEBUG4, "creating index \"%s\"", $3);
292 
293 					do_start();
294 
295 					stmt->idxname = $3;
296 					stmt->relation = makeRangeVar(NULL, $6, -1);
297 					stmt->accessMethod = $8;
298 					stmt->tableSpace = NULL;
299 					stmt->indexParams = $10;
300 					stmt->indexIncludingParams = NIL;
301 					stmt->options = NIL;
302 					stmt->whereClause = NULL;
303 					stmt->excludeOpNames = NIL;
304 					stmt->idxcomment = NULL;
305 					stmt->indexOid = InvalidOid;
306 					stmt->oldNode = InvalidOid;
307 					stmt->unique = false;
308 					stmt->primary = false;
309 					stmt->isconstraint = false;
310 					stmt->deferrable = false;
311 					stmt->initdeferred = false;
312 					stmt->transformed = false;
313 					stmt->concurrent = false;
314 					stmt->if_not_exists = false;
315 
316 					/* locks and races need not concern us in bootstrap mode */
317 					relationId = RangeVarGetRelid(stmt->relation, NoLock,
318 												  false);
319 
320 					DefineIndex(relationId,
321 								stmt,
322 								$4,
323 								InvalidOid,
324 								InvalidOid,
325 								false,
326 								false,
327 								false,
328 								true, /* skip_build */
329 								false);
330 					do_end();
331 				}
332 		;
333 
334 Boot_DeclareUniqueIndexStmt:
335 		  XDECLARE UNIQUE INDEX boot_ident oidspec ON boot_ident USING boot_ident LPAREN boot_index_params RPAREN
336 				{
337 					IndexStmt *stmt = makeNode(IndexStmt);
338 					Oid		relationId;
339 
340 					elog(DEBUG4, "creating unique index \"%s\"", $4);
341 
342 					do_start();
343 
344 					stmt->idxname = $4;
345 					stmt->relation = makeRangeVar(NULL, $7, -1);
346 					stmt->accessMethod = $9;
347 					stmt->tableSpace = NULL;
348 					stmt->indexParams = $11;
349 					stmt->indexIncludingParams = NIL;
350 					stmt->options = NIL;
351 					stmt->whereClause = NULL;
352 					stmt->excludeOpNames = NIL;
353 					stmt->idxcomment = NULL;
354 					stmt->indexOid = InvalidOid;
355 					stmt->oldNode = InvalidOid;
356 					stmt->unique = true;
357 					stmt->primary = false;
358 					stmt->isconstraint = false;
359 					stmt->deferrable = false;
360 					stmt->initdeferred = false;
361 					stmt->transformed = false;
362 					stmt->concurrent = false;
363 					stmt->if_not_exists = false;
364 
365 					/* locks and races need not concern us in bootstrap mode */
366 					relationId = RangeVarGetRelid(stmt->relation, NoLock,
367 												  false);
368 
369 					DefineIndex(relationId,
370 								stmt,
371 								$5,
372 								InvalidOid,
373 								InvalidOid,
374 								false,
375 								false,
376 								false,
377 								true, /* skip_build */
378 								false);
379 					do_end();
380 				}
381 		;
382 
383 Boot_DeclareToastStmt:
384 		  XDECLARE XTOAST oidspec oidspec ON boot_ident
385 				{
386 					elog(DEBUG4, "creating toast table for table \"%s\"", $6);
387 
388 					do_start();
389 
390 					BootstrapToastTable($6, $3, $4);
391 					do_end();
392 				}
393 		;
394 
395 Boot_BuildIndsStmt:
396 		  XBUILD INDICES
397 				{
398 					do_start();
399 					build_indices();
400 					do_end();
401 				}
402 		;
403 
404 
405 boot_index_params:
406 		boot_index_params COMMA boot_index_param	{ $$ = lappend($1, $3); }
407 		| boot_index_param							{ $$ = list_make1($1); }
408 		;
409 
410 boot_index_param:
411 		boot_ident boot_ident
412 				{
413 					IndexElem *n = makeNode(IndexElem);
414 					n->name = $1;
415 					n->expr = NULL;
416 					n->indexcolname = NULL;
417 					n->collation = NIL;
418 					n->opclass = list_make1(makeString($2));
419 					n->ordering = SORTBY_DEFAULT;
420 					n->nulls_ordering = SORTBY_NULLS_DEFAULT;
421 					$$ = n;
422 				}
423 		;
424 
425 optbootstrap:
426 			XBOOTSTRAP	{ $$ = 1; }
427 		|				{ $$ = 0; }
428 		;
429 
430 optsharedrelation:
431 			XSHARED_RELATION	{ $$ = 1; }
432 		|						{ $$ = 0; }
433 		;
434 
435 optwithoutoids:
436 			XWITHOUT_OIDS	{ $$ = 1; }
437 		|					{ $$ = 0; }
438 		;
439 
440 optrowtypeoid:
441 			XROWTYPE_OID oidspec	{ $$ = $2; }
442 		|							{ $$ = InvalidOid; }
443 		;
444 
445 boot_column_list:
446 		  boot_column_def
447 		| boot_column_list COMMA boot_column_def
448 		;
449 
450 boot_column_def:
451 		  boot_ident EQUALS boot_ident boot_column_nullness
452 				{
453 				   if (++numattr > MAXATTR)
454 						elog(FATAL, "too many columns");
455 				   DefineAttr($1, $3, numattr-1, $4);
456 				}
457 		;
458 
459 boot_column_nullness:
460 			XFORCE XNOT XNULL	{ $$ = BOOTCOL_NULL_FORCE_NOT_NULL; }
461 		|	XFORCE XNULL		{  $$ = BOOTCOL_NULL_FORCE_NULL; }
462 		| { $$ = BOOTCOL_NULL_AUTO; }
463 		;
464 
465 oidspec:
466 			boot_ident							{ $$ = atooid($1); }
467 		;
468 
469 optoideq:
470 			OBJ_ID EQUALS oidspec				{ $$ = $3; }
471 		|										{ $$ = InvalidOid; }
472 		;
473 
474 boot_column_val_list:
475 		   boot_column_val
476 		|  boot_column_val_list boot_column_val
477 		|  boot_column_val_list COMMA boot_column_val
478 		;
479 
480 boot_column_val:
481 		  boot_ident
482 			{ InsertOneValue($1, num_columns_read++); }
483 		| NULLVAL
484 			{ InsertOneNull(num_columns_read++); }
485 		;
486 
487 boot_ident:
488 		  ID			{ $$ = $1; }
489 		| OPEN			{ $$ = pstrdup($1); }
490 		| XCLOSE		{ $$ = pstrdup($1); }
491 		| XCREATE		{ $$ = pstrdup($1); }
492 		| INSERT_TUPLE	{ $$ = pstrdup($1); }
493 		| XDECLARE		{ $$ = pstrdup($1); }
494 		| INDEX			{ $$ = pstrdup($1); }
495 		| ON			{ $$ = pstrdup($1); }
496 		| USING			{ $$ = pstrdup($1); }
497 		| XBUILD		{ $$ = pstrdup($1); }
498 		| INDICES		{ $$ = pstrdup($1); }
499 		| UNIQUE		{ $$ = pstrdup($1); }
500 		| XTOAST		{ $$ = pstrdup($1); }
501 		| OBJ_ID		{ $$ = pstrdup($1); }
502 		| XBOOTSTRAP	{ $$ = pstrdup($1); }
503 		| XSHARED_RELATION	{ $$ = pstrdup($1); }
504 		| XWITHOUT_OIDS	{ $$ = pstrdup($1); }
505 		| XROWTYPE_OID	{ $$ = pstrdup($1); }
506 		| XFORCE		{ $$ = pstrdup($1); }
507 		| XNOT			{ $$ = pstrdup($1); }
508 		| XNULL			{ $$ = pstrdup($1); }
509 		;
510 %%
511 
512 #include "bootscanner.c"
513