]> granicus.if.org Git - postgresql/blob - src/backend/catalog/genbki.pl
ebdc919414fbd6c7e158940cfbbe27430e915842
[postgresql] / src / backend / catalog / genbki.pl
1 #!/usr/bin/perl -w
2 #----------------------------------------------------------------------
3 #
4 # genbki.pl
5 #    Perl script that generates postgres.bki, postgres.description,
6 #    postgres.shdescription, and symbol definition headers from specially
7 #    formatted header files and data files.  The BKI files are used to
8 #    initialize the postgres template database.
9 #
10 # Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
11 # Portions Copyright (c) 1994, Regents of the University of California
12 #
13 # src/backend/catalog/genbki.pl
14 #
15 #----------------------------------------------------------------------
16
17 use Catalog;
18
19 use strict;
20 use warnings;
21
22 my @input_files;
23 my $output_path = '';
24 my $major_version;
25
26 # Process command line switches.
27 while (@ARGV)
28 {
29         my $arg = shift @ARGV;
30         if ($arg !~ /^-/)
31         {
32                 push @input_files, $arg;
33         }
34         elsif ($arg =~ /^-o/)
35         {
36                 $output_path = length($arg) > 2 ? substr($arg, 2) : shift @ARGV;
37         }
38         elsif ($arg =~ /^--set-version=(.*)$/)
39         {
40                 $major_version = $1;
41                 die "Invalid version string.\n"
42                   if !($major_version =~ /^\d+$/);
43         }
44         else
45         {
46                 usage();
47         }
48 }
49
50 # Sanity check arguments.
51 die "No input files.\n" if !@input_files;
52 die "--set-version must be specified.\n" if !defined $major_version;
53
54 # Make sure output_path ends in a slash.
55 if ($output_path ne '' && substr($output_path, -1) ne '/')
56 {
57         $output_path .= '/';
58 }
59
60 # Read all the files into internal data structures.
61 my @catnames;
62 my %catalogs;
63 my %catalog_data;
64 my @toast_decls;
65 my @index_decls;
66 my %oidcounts;
67
68 foreach my $header (@input_files)
69 {
70         $header =~ /(.+)\.h$/
71           or die "Input files need to be header files.\n";
72         my $datfile = "$1.dat";
73
74         my $catalog = Catalog::ParseHeader($header);
75         my $catname = $catalog->{catname};
76         my $schema  = $catalog->{columns};
77
78         if (defined $catname)
79         {
80                 push @catnames, $catname;
81                 $catalogs{$catname} = $catalog;
82         }
83
84         # While checking for duplicated OIDs, we ignore the pg_class OID and
85         # rowtype OID of bootstrap catalogs, as those are expected to appear
86         # in the initial data for pg_class and pg_type.  For regular catalogs,
87         # include these OIDs.  (See also Catalog::FindAllOidsFromHeaders
88         # if you change this logic.)
89         if (!$catalog->{bootstrap})
90         {
91                 $oidcounts{ $catalog->{relation_oid} }++
92                   if ($catalog->{relation_oid});
93                 $oidcounts{ $catalog->{rowtype_oid} }++
94                   if ($catalog->{rowtype_oid});
95         }
96
97         # Not all catalogs have a data file.
98         if (-e $datfile)
99         {
100                 my $data = Catalog::ParseData($datfile, $schema, 0);
101                 $catalog_data{$catname} = $data;
102
103                 # Check for duplicated OIDs while we're at it.
104                 foreach my $row (@$data)
105                 {
106                         $oidcounts{ $row->{oid} }++ if defined $row->{oid};
107                 }
108         }
109
110         # If the header file contained toast or index info, build BKI
111         # commands for those, which we'll output later.
112         foreach my $toast (@{ $catalog->{toasting} })
113         {
114                 push @toast_decls,
115                   sprintf "declare toast %s %s on %s\n",
116                   $toast->{toast_oid}, $toast->{toast_index_oid},
117                   $toast->{parent_table};
118                 $oidcounts{ $toast->{toast_oid} }++;
119                 $oidcounts{ $toast->{toast_index_oid} }++;
120         }
121         foreach my $index (@{ $catalog->{indexing} })
122         {
123                 push @index_decls,
124                   sprintf "declare %sindex %s %s %s\n",
125                   $index->{is_unique} ? 'unique ' : '',
126                   $index->{index_name}, $index->{index_oid},
127                   $index->{index_decl};
128                 $oidcounts{ $index->{index_oid} }++;
129         }
130 }
131
132 # Complain and exit if we found any duplicate OIDs.
133 # While duplicate OIDs would only cause a failure if they appear in
134 # the same catalog, our project policy is that manually assigned OIDs
135 # should be globally unique, to avoid confusion.
136 my $found = 0;
137 foreach my $oid (keys %oidcounts)
138 {
139         next unless $oidcounts{$oid} > 1;
140         print STDERR "Duplicate OIDs detected:\n" if !$found;
141         print STDERR "$oid\n";
142         $found++;
143 }
144 die "found $found duplicate OID(s) in catalog data\n" if $found;
145
146 # Fetch some special data that we will substitute into the output file.
147 # CAUTION: be wary about what symbols you substitute into the .bki file here!
148 # It's okay to substitute things that are expected to be really constant
149 # within a given Postgres release, such as fixed OIDs.  Do not substitute
150 # anything that could depend on platform or configuration.  (The right place
151 # to handle those sorts of things is in initdb.c's bootstrap_template1().)
152 my $BOOTSTRAP_SUPERUSERID =
153   Catalog::FindDefinedSymbolFromData($catalog_data{pg_authid},
154         'BOOTSTRAP_SUPERUSERID');
155 my $PG_CATALOG_NAMESPACE =
156   Catalog::FindDefinedSymbolFromData($catalog_data{pg_namespace},
157         'PG_CATALOG_NAMESPACE');
158
159
160 # Build lookup tables for OID macro substitutions and for pg_attribute
161 # copies of pg_type values.
162
163 # index access method OID lookup
164 my %amoids;
165 foreach my $row (@{ $catalog_data{pg_am} })
166 {
167         $amoids{ $row->{amname} } = $row->{oid};
168 }
169
170 # opclass OID lookup
171 my %opcoids;
172 foreach my $row (@{ $catalog_data{pg_opclass} })
173 {
174         # There is no unique name, so we need to combine access method
175         # and opclass name.
176         my $key = sprintf "%s/%s", $row->{opcmethod}, $row->{opcname};
177         $opcoids{$key} = $row->{oid};
178 }
179
180 # operator OID lookup
181 my %operoids;
182 foreach my $row (@{ $catalog_data{pg_operator} })
183 {
184         # There is no unique name, so we need to invent one that contains
185         # the relevant type names.
186         my $key = sprintf "%s(%s,%s)",
187           $row->{oprname}, $row->{oprleft}, $row->{oprright};
188         $operoids{$key} = $row->{oid};
189 }
190
191 # opfamily OID lookup
192 my %opfoids;
193 foreach my $row (@{ $catalog_data{pg_opfamily} })
194 {
195         # There is no unique name, so we need to combine access method
196         # and opfamily name.
197         my $key = sprintf "%s/%s", $row->{opfmethod}, $row->{opfname};
198         $opfoids{$key} = $row->{oid};
199 }
200
201 # procedure OID lookup
202 my %procoids;
203 foreach my $row (@{ $catalog_data{pg_proc} })
204 {
205         # Generate an entry under just the proname (corresponds to regproc lookup)
206         my $prokey = $row->{proname};
207         if (defined $procoids{$prokey})
208         {
209                 $procoids{$prokey} = 'MULTIPLE';
210         }
211         else
212         {
213                 $procoids{$prokey} = $row->{oid};
214         }
215
216         # Also generate an entry using proname(proargtypes).  This is not quite
217         # identical to regprocedure lookup because we don't worry much about
218         # special SQL names for types etc; we just use the names in the source
219         # proargtypes field.  These *should* be unique, but do a multiplicity
220         # check anyway.
221         $prokey .= '(' . join(',', split(/\s+/, $row->{proargtypes})) . ')';
222         if (defined $procoids{$prokey})
223         {
224                 $procoids{$prokey} = 'MULTIPLE';
225         }
226         else
227         {
228                 $procoids{$prokey} = $row->{oid};
229         }
230 }
231
232 # type lookups
233 my %typeoids;
234 my %types;
235 foreach my $row (@{ $catalog_data{pg_type} })
236 {
237         $typeoids{ $row->{typname} } = $row->{oid};
238         $types{ $row->{typname} }    = $row;
239 }
240
241 # Map catalog name to OID lookup.
242 my %lookup_kind = (
243         pg_am       => \%amoids,
244         pg_opclass  => \%opcoids,
245         pg_operator => \%operoids,
246         pg_opfamily => \%opfoids,
247         pg_proc     => \%procoids,
248         pg_type     => \%typeoids);
249
250
251 # Open temp files
252 my $tmpext  = ".tmp$$";
253 my $bkifile = $output_path . 'postgres.bki';
254 open my $bki, '>', $bkifile . $tmpext
255   or die "can't open $bkifile$tmpext: $!";
256 my $schemafile = $output_path . 'schemapg.h';
257 open my $schemapg, '>', $schemafile . $tmpext
258   or die "can't open $schemafile$tmpext: $!";
259 my $descrfile = $output_path . 'postgres.description';
260 open my $descr, '>', $descrfile . $tmpext
261   or die "can't open $descrfile$tmpext: $!";
262 my $shdescrfile = $output_path . 'postgres.shdescription';
263 open my $shdescr, '>', $shdescrfile . $tmpext
264   or die "can't open $shdescrfile$tmpext: $!";
265
266 # Generate postgres.bki, postgres.description, postgres.shdescription,
267 # and pg_*_d.h headers.
268
269 # version marker for .bki file
270 print $bki "# PostgreSQL $major_version\n";
271
272 # vars to hold data needed for schemapg.h
273 my %schemapg_entries;
274 my @tables_needing_macros;
275
276 # produce output, one catalog at a time
277 foreach my $catname (@catnames)
278 {
279         my $catalog = $catalogs{$catname};
280
281         # Create one definition header with macro definitions for each catalog.
282         my $def_file = $output_path . $catname . '_d.h';
283         open my $def, '>', $def_file . $tmpext
284           or die "can't open $def_file$tmpext: $!";
285
286         # Opening boilerplate for pg_*_d.h
287         printf $def <<EOM, $catname, $catname, uc $catname, uc $catname;
288 /*-------------------------------------------------------------------------
289  *
290  * %s_d.h
291  *    Macro definitions for %s
292  *
293  * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
294  * Portions Copyright (c) 1994, Regents of the University of California
295  *
296  * NOTES
297  *  ******************************
298  *  *** DO NOT EDIT THIS FILE! ***
299  *  ******************************
300  *
301  *  It has been GENERATED by src/backend/catalog/genbki.pl
302  *
303  *-------------------------------------------------------------------------
304  */
305 #ifndef %s_D_H
306 #define %s_D_H
307
308 EOM
309
310         # Emit OID macros for catalog's OID and rowtype OID, if wanted
311         printf $def "#define %s %s\n",
312           $catalog->{relation_oid_macro}, $catalog->{relation_oid}
313           if $catalog->{relation_oid_macro};
314         printf $def "#define %s %s\n",
315           $catalog->{rowtype_oid_macro}, $catalog->{rowtype_oid}
316           if $catalog->{rowtype_oid_macro};
317         print $def "\n";
318
319         # .bki CREATE command for this catalog
320         print $bki "create $catname $catalog->{relation_oid}"
321           . $catalog->{shared_relation}
322           . $catalog->{bootstrap}
323           . $catalog->{without_oids}
324           . $catalog->{rowtype_oid_clause};
325
326         my $first = 1;
327
328         print $bki "\n (\n";
329         my $schema = $catalog->{columns};
330         my %attnames;
331         my $attnum = 0;
332         foreach my $column (@$schema)
333         {
334                 $attnum++;
335                 my $attname = $column->{name};
336                 my $atttype = $column->{type};
337
338                 # Build hash of column names for use later
339                 $attnames{$attname} = 1;
340
341                 # Emit column definitions
342                 if (!$first)
343                 {
344                         print $bki " ,\n";
345                 }
346                 $first = 0;
347
348                 print $bki " $attname = $atttype";
349
350                 if (defined $column->{forcenotnull})
351                 {
352                         print $bki " FORCE NOT NULL";
353                 }
354                 elsif (defined $column->{forcenull})
355                 {
356                         print $bki " FORCE NULL";
357                 }
358
359                 # Emit Anum_* constants
360                 printf $def "#define Anum_%s_%s %s\n", $catname, $attname, $attnum;
361         }
362         print $bki "\n )\n";
363
364         # Emit Natts_* constant
365         print $def "\n#define Natts_$catname $attnum\n\n";
366
367         # Emit client code copied from source header
368         foreach my $line (@{ $catalog->{client_code} })
369         {
370                 print $def $line;
371         }
372
373         # Open it, unless it's a bootstrap catalog (create bootstrap does this
374         # automatically)
375         if (!$catalog->{bootstrap})
376         {
377                 print $bki "open $catname\n";
378         }
379
380         # For pg_attribute.h, we generate data entries ourselves.
381         if ($catname eq 'pg_attribute')
382         {
383                 gen_pg_attribute($schema);
384         }
385
386         # Ordinary catalog with a data file
387         foreach my $row (@{ $catalog_data{$catname} })
388         {
389                 my %bki_values = %$row;
390
391                 # Complain about unrecognized keys; they are presumably misspelled
392                 foreach my $key (keys %bki_values)
393                 {
394                         next
395                           if $key eq "oid"
396                           || $key eq "oid_symbol"
397                           || $key eq "descr"
398                           || $key eq "line_number";
399                         die sprintf "unrecognized field name \"%s\" in %s.dat line %s\n",
400                           $key, $catname, $bki_values{line_number}
401                           if (!exists($attnames{$key}));
402                 }
403
404                 # Perform required substitutions on fields
405                 foreach my $column (@$schema)
406                 {
407                         my $attname = $column->{name};
408                         my $atttype = $column->{type};
409
410                         # Substitute constant values we acquired above.
411                         # (It's intentional that this can apply to parts of a field).
412                         $bki_values{$attname} =~ s/\bPGUID\b/$BOOTSTRAP_SUPERUSERID/g;
413                         $bki_values{$attname} =~ s/\bPGNSP\b/$PG_CATALOG_NAMESPACE/g;
414
415                         # Replace OID synonyms with OIDs per the appropriate lookup rule.
416                         #
417                         # If the column type is oidvector or _oid, we have to replace
418                         # each element of the array as per the lookup rule.
419                         if ($column->{lookup})
420                         {
421                                 my $lookup = $lookup_kind{ $column->{lookup} };
422                                 my @lookupnames;
423                                 my @lookupoids;
424
425                                 die "unrecognized BKI_LOOKUP type " . $column->{lookup}
426                                   if !defined($lookup);
427
428                                 if ($atttype eq 'oidvector')
429                                 {
430                                         @lookupnames = split /\s+/, $bki_values{$attname};
431                                         @lookupoids = lookup_oids($lookup, $catname, \%bki_values,
432                                                 @lookupnames);
433                                         $bki_values{$attname} = join(' ', @lookupoids);
434                                 }
435                                 elsif ($atttype eq '_oid')
436                                 {
437                                         if ($bki_values{$attname} ne '_null_')
438                                         {
439                                                 $bki_values{$attname} =~ s/[{}]//g;
440                                                 @lookupnames = split /,/, $bki_values{$attname};
441                                                 @lookupoids =
442                                                   lookup_oids($lookup, $catname, \%bki_values,
443                                                         @lookupnames);
444                                                 $bki_values{$attname} = sprintf "{%s}",
445                                                   join(',', @lookupoids);
446                                         }
447                                 }
448                                 else
449                                 {
450                                         $lookupnames[0] = $bki_values{$attname};
451                                         @lookupoids = lookup_oids($lookup, $catname, \%bki_values,
452                                                 @lookupnames);
453                                         $bki_values{$attname} = $lookupoids[0];
454                                 }
455                         }
456                 }
457
458                 # Special hack to generate OID symbols for pg_type entries
459                 # that lack one.
460                 if ($catname eq 'pg_type' and !exists $bki_values{oid_symbol})
461                 {
462                         my $symbol = form_pg_type_symbol($bki_values{typname});
463                         $bki_values{oid_symbol} = $symbol
464                           if defined $symbol;
465                 }
466
467                 # Write to postgres.bki
468                 print_bki_insert(\%bki_values, $schema);
469
470                 # Write comments to postgres.description and
471                 # postgres.shdescription
472                 if (defined $bki_values{descr})
473                 {
474                         if ($catalog->{shared_relation})
475                         {
476                                 printf $shdescr "%s\t%s\t%s\n",
477                                   $bki_values{oid}, $catname, $bki_values{descr};
478                         }
479                         else
480                         {
481                                 printf $descr "%s\t%s\t0\t%s\n",
482                                   $bki_values{oid}, $catname, $bki_values{descr};
483                         }
484                 }
485
486                 # Emit OID symbol
487                 if (defined $bki_values{oid_symbol})
488                 {
489                         printf $def "#define %s %s\n",
490                           $bki_values{oid_symbol}, $bki_values{oid};
491                 }
492         }
493
494         print $bki "close $catname\n";
495         printf $def "\n#endif\t\t\t\t\t\t\t/* %s_D_H */\n", uc $catname;
496
497         # Close and rename definition header
498         close $def;
499         Catalog::RenameTempFile($def_file, $tmpext);
500 }
501
502 # Any information needed for the BKI that is not contained in a pg_*.h header
503 # (i.e., not contained in a header with a CATALOG() statement) comes here
504
505 # Write out declare toast/index statements
506 foreach my $declaration (@toast_decls)
507 {
508         print $bki $declaration;
509 }
510
511 foreach my $declaration (@index_decls)
512 {
513         print $bki $declaration;
514 }
515
516 # last command in the BKI file: build the indexes declared above
517 print $bki "build indices\n";
518
519
520 # Now generate schemapg.h
521
522 # Opening boilerplate for schemapg.h
523 print $schemapg <<EOM;
524 /*-------------------------------------------------------------------------
525  *
526  * schemapg.h
527  *    Schema_pg_xxx macros for use by relcache.c
528  *
529  * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
530  * Portions Copyright (c) 1994, Regents of the University of California
531  *
532  * NOTES
533  *  ******************************
534  *  *** DO NOT EDIT THIS FILE! ***
535  *  ******************************
536  *
537  *  It has been GENERATED by src/backend/catalog/genbki.pl
538  *
539  *-------------------------------------------------------------------------
540  */
541 #ifndef SCHEMAPG_H
542 #define SCHEMAPG_H
543 EOM
544
545 # Emit schemapg declarations
546 foreach my $table_name (@tables_needing_macros)
547 {
548         print $schemapg "\n#define Schema_$table_name \\\n";
549         print $schemapg join ", \\\n", @{ $schemapg_entries{$table_name} };
550         print $schemapg "\n";
551 }
552
553 # Closing boilerplate for schemapg.h
554 print $schemapg "\n#endif\t\t\t\t\t\t\t/* SCHEMAPG_H */\n";
555
556 # We're done emitting data
557 close $bki;
558 close $schemapg;
559 close $descr;
560 close $shdescr;
561
562 # Finally, rename the completed files into place.
563 Catalog::RenameTempFile($bkifile,     $tmpext);
564 Catalog::RenameTempFile($schemafile,  $tmpext);
565 Catalog::RenameTempFile($descrfile,   $tmpext);
566 Catalog::RenameTempFile($shdescrfile, $tmpext);
567
568 exit 0;
569
570 #################### Subroutines ########################
571
572
573 # For each catalog marked as needing a schema macro, generate the
574 # per-user-attribute data to be incorporated into schemapg.h.  Also, for
575 # bootstrap catalogs, emit pg_attribute entries into the .bki file
576 # for both user and system attributes.
577 sub gen_pg_attribute
578 {
579         my $schema = shift;
580
581         my @attnames;
582         foreach my $column (@$schema)
583         {
584                 push @attnames, $column->{name};
585         }
586
587         foreach my $table_name (@catnames)
588         {
589                 my $table = $catalogs{$table_name};
590
591                 # Currently, all bootstrap catalogs also need schemapg.h
592                 # entries, so skip if it isn't to be in schemapg.h.
593                 next if !$table->{schema_macro};
594
595                 $schemapg_entries{$table_name} = [];
596                 push @tables_needing_macros, $table_name;
597
598                 # Generate entries for user attributes.
599                 my $attnum       = 0;
600                 my $priornotnull = 1;
601                 foreach my $attr (@{ $table->{columns} })
602                 {
603                         $attnum++;
604                         my %row;
605                         $row{attnum}   = $attnum;
606                         $row{attrelid} = $table->{relation_oid};
607
608                         morph_row_for_pgattr(\%row, $schema, $attr, $priornotnull);
609                         $priornotnull &= ($row{attnotnull} eq 't');
610
611                         # If it's bootstrapped, put an entry in postgres.bki.
612                         print_bki_insert(\%row, $schema) if $table->{bootstrap};
613
614                         # Store schemapg entries for later.
615                         morph_row_for_schemapg(\%row, $schema);
616                         push @{ $schemapg_entries{$table_name} },
617                           sprintf "{ %s }",
618                           join(', ', grep { defined $_ } @row{@attnames});
619                 }
620
621                 # Generate entries for system attributes.
622                 # We only need postgres.bki entries, not schemapg.h entries.
623                 if ($table->{bootstrap})
624                 {
625                         $attnum = 0;
626                         my @SYS_ATTRS = (
627                                 { name => 'ctid',     type => 'tid' },
628                                 { name => 'oid',      type => 'oid' },
629                                 { name => 'xmin',     type => 'xid' },
630                                 { name => 'cmin',     type => 'cid' },
631                                 { name => 'xmax',     type => 'xid' },
632                                 { name => 'cmax',     type => 'cid' },
633                                 { name => 'tableoid', type => 'oid' });
634                         foreach my $attr (@SYS_ATTRS)
635                         {
636                                 $attnum--;
637                                 my %row;
638                                 $row{attnum}        = $attnum;
639                                 $row{attrelid}      = $table->{relation_oid};
640                                 $row{attstattarget} = '0';
641
642                                 # Omit the oid column if the catalog doesn't have them
643                                 next
644                                   if $table->{without_oids}
645                                   && $attr->{name} eq 'oid';
646
647                                 morph_row_for_pgattr(\%row, $schema, $attr, 1);
648                                 print_bki_insert(\%row, $schema);
649                         }
650                 }
651         }
652 }
653
654 # Given $pgattr_schema (the pg_attribute schema for a catalog sufficient for
655 # AddDefaultValues), $attr (the description of a catalog row), and
656 # $priornotnull (whether all prior attributes in this catalog are not null),
657 # modify the $row hashref for print_bki_insert.  This includes setting data
658 # from the corresponding pg_type element and filling in any default values.
659 # Any value not handled here must be supplied by caller.
660 sub morph_row_for_pgattr
661 {
662         my ($row, $pgattr_schema, $attr, $priornotnull) = @_;
663         my $attname = $attr->{name};
664         my $atttype = $attr->{type};
665
666         $row->{attname} = $attname;
667
668         # Copy the type data from pg_type, and add some type-dependent items
669         my $type = $types{$atttype};
670
671         $row->{atttypid}   = $type->{oid};
672         $row->{attlen}     = $type->{typlen};
673         $row->{attbyval}   = $type->{typbyval};
674         $row->{attstorage} = $type->{typstorage};
675         $row->{attalign}   = $type->{typalign};
676
677         # set attndims if it's an array type
678         $row->{attndims} = $type->{typcategory} eq 'A' ? '1' : '0';
679         $row->{attcollation} = $type->{typcollation};
680
681         if (defined $attr->{forcenotnull})
682         {
683                 $row->{attnotnull} = 't';
684         }
685         elsif (defined $attr->{forcenull})
686         {
687                 $row->{attnotnull} = 'f';
688         }
689         elsif ($priornotnull)
690         {
691
692                 # attnotnull will automatically be set if the type is
693                 # fixed-width and prior columns are all NOT NULL ---
694                 # compare DefineAttr in bootstrap.c. oidvector and
695                 # int2vector are also treated as not-nullable.
696                 $row->{attnotnull} =
697                     $type->{typname} eq 'oidvector'  ? 't'
698                   : $type->{typname} eq 'int2vector' ? 't'
699                   : $type->{typlen} eq 'NAMEDATALEN' ? 't'
700                   : $type->{typlen} > 0              ? 't'
701                   :                                    'f';
702         }
703         else
704         {
705                 $row->{attnotnull} = 'f';
706         }
707
708         Catalog::AddDefaultValues($row, $pgattr_schema, 'pg_attribute');
709 }
710
711 # Write an entry to postgres.bki.
712 sub print_bki_insert
713 {
714         my $row    = shift;
715         my $schema = shift;
716
717         my @bki_values;
718         my $oid = $row->{oid} ? "OID = $row->{oid} " : '';
719
720         foreach my $column (@$schema)
721         {
722                 my $attname   = $column->{name};
723                 my $atttype   = $column->{type};
724                 my $bki_value = $row->{$attname};
725
726                 # Fold backslash-zero to empty string if it's the entire string,
727                 # since that represents a NUL char in C code.
728                 $bki_value = '' if $bki_value eq '\0';
729
730                 # Handle single quotes by doubling them, and double quotes by
731                 # converting them to octal escapes, because that's what the
732                 # bootstrap scanner requires.  We do not process backslashes
733                 # specially; this allows escape-string-style backslash escapes
734                 # to be used in catalog data.
735                 $bki_value =~ s/'/''/g;
736                 $bki_value =~ s/"/\\042/g;
737
738                 # Quote value if needed.  We need not quote values that satisfy
739                 # the "id" pattern in bootscanner.l, currently "[-A-Za-z0-9_]+".
740                 $bki_value = sprintf(qq'"%s"', $bki_value)
741                   if length($bki_value) == 0
742                   or $bki_value =~ /[^-A-Za-z0-9_]/;
743
744                 push @bki_values, $bki_value;
745         }
746         printf $bki "insert %s( %s )\n", $oid, join(' ', @bki_values);
747 }
748
749 # Given a row reference, modify it so that it becomes a valid entry for
750 # a catalog schema declaration in schemapg.h.
751 #
752 # The field values of a Schema_pg_xxx declaration are similar, but not
753 # quite identical, to the corresponding values in postgres.bki.
754 sub morph_row_for_schemapg
755 {
756         my $row           = shift;
757         my $pgattr_schema = shift;
758
759         foreach my $column (@$pgattr_schema)
760         {
761                 my $attname = $column->{name};
762                 my $atttype = $column->{type};
763
764                 # Some data types have special formatting rules.
765                 if ($atttype eq 'name')
766                 {
767                         # add {" ... "} quoting
768                         $row->{$attname} = sprintf(qq'{"%s"}', $row->{$attname});
769                 }
770                 elsif ($atttype eq 'char')
771                 {
772                         # Add single quotes
773                         $row->{$attname} = sprintf("'%s'", $row->{$attname});
774                 }
775
776                 # Expand booleans from 'f'/'t' to 'false'/'true'.
777                 # Some values might be other macros (eg FLOAT4PASSBYVAL),
778                 # don't change.
779                 elsif ($atttype eq 'bool')
780                 {
781                         $row->{$attname} = 'true'  if $row->{$attname} eq 't';
782                         $row->{$attname} = 'false' if $row->{$attname} eq 'f';
783                 }
784
785                 # We don't emit initializers for the variable length fields at all.
786                 # Only the fixed-size portions of the descriptors are ever used.
787                 delete $row->{$attname} if $column->{is_varlen};
788         }
789 }
790
791 # Perform OID lookups on an array of OID names.
792 # If we don't have a unique value to substitute, warn and
793 # leave the entry unchanged.
794 # (A warning seems sufficient because the bootstrap backend will reject
795 # non-numeric values anyway.  So we might as well detect multiple problems
796 # within this genbki.pl run.)
797 sub lookup_oids
798 {
799         my ($lookup, $catname, $bki_values, @lookupnames) = @_;
800
801         my @lookupoids;
802         foreach my $lookupname (@lookupnames)
803         {
804                 my $lookupoid = $lookup->{$lookupname};
805                 if (defined($lookupoid) and $lookupoid ne 'MULTIPLE')
806                 {
807                         push @lookupoids, $lookupoid;
808                 }
809                 else
810                 {
811                         push @lookupoids, $lookupname;
812                         warn sprintf
813                           "unresolved OID reference \"%s\" in %s.dat line %s\n",
814                           $lookupname, $catname, $bki_values->{line_number}
815                           if $lookupname ne '-' and $lookupname ne '0';
816                 }
817         }
818         return @lookupoids;
819 }
820
821 # Determine canonical pg_type OID #define symbol from the type name.
822 sub form_pg_type_symbol
823 {
824         my $typename = shift;
825
826         # Skip for rowtypes of bootstrap catalogs, since they have their
827         # own naming convention defined elsewhere.
828         return
829              if $typename eq 'pg_type'
830           or $typename eq 'pg_proc'
831           or $typename eq 'pg_attribute'
832           or $typename eq 'pg_class';
833
834         # Transform like so:
835         #  foo_bar  ->  FOO_BAROID
836         # _foo_bar  ->  FOO_BARARRAYOID
837         $typename =~ /(_)?(.+)/;
838         my $arraystr = $1 ? 'ARRAY' : '';
839         my $name = uc $2;
840         return $name . $arraystr . 'OID';
841 }
842
843 sub usage
844 {
845         die <<EOM;
846 Usage: genbki.pl [options] header...
847
848 Options:
849     -o               output path
850     --set-version    PostgreSQL version number for initdb cross-check
851
852 genbki.pl generates BKI files and symbol definition
853 headers from specially formatted header files and .dat
854 files.  The BKI files are used to initialize the
855 postgres template database.
856
857 Report bugs to <pgsql-bugs\@postgresql.org>.
858 EOM
859 }