/*------------------------------------------------------------------------- * * bootstrap.c * routines to support running postgres in 'bootstrap' mode * bootstrap mode is used to create the initial template database * * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.250 2009/02/18 15:58:41 heikki Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" #include #include #include #ifdef HAVE_GETOPT_H #include #endif #include "access/genam.h" #include "access/heapam.h" #include "access/xact.h" #include "bootstrap/bootstrap.h" #include "catalog/index.h" #include "catalog/pg_type.h" #include "cdb/cdbfilerep.h" #include "libpq/pqsignal.h" #include "miscadmin.h" #include "nodes/makefuncs.h" #include "postmaster/bgwriter.h" #include "postmaster/walwriter.h" #include "replication/walreceiver.h" #include "storage/bufpage.h" #include "storage/bufmgr.h" #include "storage/ipc.h" #include "storage/proc.h" #include "tcop/tcopprot.h" #include "utils/builtins.h" #include "utils/fmgroids.h" #include "utils/memutils.h" #include "utils/ps_status.h" #include "utils/tqual.h" extern int optind; extern char *optarg; uint32 bootstrap_data_checksum_version = 0; /* No checksum */ #define ALLOC(t, c) ((t *) calloc((unsigned)(c), sizeof(t))) static void CheckerModeMain(void); static void BootstrapModeMain(void); static void bootstrap_signals(void); static void ShutdownAuxiliaryProcess(int code, Datum arg); static hashnode *AddStr(char *str, int strlength, int mderef); static Form_pg_attribute AllocateAttribute(void); static int CompHash(char *str, int len); static hashnode *FindStr(char *str, int length, hashnode *mderef); static Oid gettype(char *type); static void cleanup(void); /* ---------------- * global variables * ---------------- */ Relation boot_reldesc; /* current relation descriptor */ AuxProcType MyAuxProcType = NotAnAuxProcess; /* * In the lexical analyzer, we need to get the reference number quickly from * the string, and the string from the reference number. Thus we have * as our data structure a hash table, where the hashing key taken from * the particular string. The hash table is chained. One of the fields * of the hash table node is an index into the array of character pointers. * The unique index number that every string is assigned is simply the * position of its string pointer in the array of string pointers. */ #define STRTABLESIZE 10000 #define HASHTABLESIZE 503 /* Hash function numbers */ #define NUM 23 #define NUMSQR 529 #define NUMCUBE 12167 char *strtable[STRTABLESIZE]; hashnode *hashtable[HASHTABLESIZE]; static int strtable_end = -1; /* Tells us last occupied string space */ /*- * Basic information associated with each type. This is used before * pg_type is created. * * XXX several of these input/output functions do catalog scans * (e.g., F_REGPROCIN scans pg_proc). this obviously creates some * order dependencies in the catalog creation process. */ struct typinfo { char name[NAMEDATALEN]; Oid oid; Oid elem; int16 len; bool byval; char align; char storage; Oid inproc; Oid outproc; }; static const struct typinfo TypInfo[] = { {"bool", BOOLOID, 0, 1, true, 'c', 'p', F_BOOLIN, F_BOOLOUT}, {"bytea", BYTEAOID, 0, -1, false, 'i', 'x', F_BYTEAIN, F_BYTEAOUT}, {"char", CHAROID, 0, 1, true, 'c', 'p', F_CHARIN, F_CHAROUT}, {"int2", INT2OID, 0, 2, true, 's', 'p', F_INT2IN, F_INT2OUT}, {"int4", INT4OID, 0, 4, true, 'i', 'p', F_INT4IN, F_INT4OUT}, {"float4", FLOAT4OID, 0, 4, FLOAT4PASSBYVAL, 'i', 'p', F_FLOAT4IN, F_FLOAT4OUT}, {"name", NAMEOID, CHAROID, NAMEDATALEN, false, 'c', 'p', F_NAMEIN, F_NAMEOUT}, {"regclass", REGCLASSOID, 0, 4, true, 'i', 'p', F_REGCLASSIN, F_REGCLASSOUT}, {"regproc", REGPROCOID, 0, 4, true, 'i', 'p', F_REGPROCIN, F_REGPROCOUT}, {"regtype", REGTYPEOID, 0, 4, true, 'i', 'p', F_REGTYPEIN, F_REGTYPEOUT}, {"text", TEXTOID, 0, -1, false, 'i', 'x', F_TEXTIN, F_TEXTOUT}, {"oid", OIDOID, 0, 4, true, 'i', 'p', F_OIDIN, F_OIDOUT}, {"tid", TIDOID, 0, 6, false, 's', 'p', F_TIDIN, F_TIDOUT}, {"xid", XIDOID, 0, 4, true, 'i', 'p', F_XIDIN, F_XIDOUT}, {"cid", CIDOID, 0, 4, true, 'i', 'p', F_CIDIN, F_CIDOUT}, {"int2vector", INT2VECTOROID, INT2OID, -1, false, 'i', 'p', F_INT2VECTORIN, F_INT2VECTOROUT}, {"oidvector", OIDVECTOROID, OIDOID, -1, false, 'i', 'p', F_OIDVECTORIN, F_OIDVECTOROUT}, {"_int4", INT4ARRAYOID, INT4OID, -1, false, 'i', 'x', F_ARRAY_IN, F_ARRAY_OUT}, {"_text", 1009, TEXTOID, -1, false, 'i', 'x', F_ARRAY_IN, F_ARRAY_OUT}, {"_oid", 1028, OIDOID, -1, false, 'i', 'x', F_ARRAY_IN, F_ARRAY_OUT}, {"_char", 1002, CHAROID, -1, false, 'i', 'x', F_ARRAY_IN, F_ARRAY_OUT}, {"_aclitem", 1034, ACLITEMOID, -1, false, 'i', 'x', F_ARRAY_IN, F_ARRAY_OUT} }; static const int n_types = sizeof(TypInfo) / sizeof(struct typinfo); struct typmap { /* a hack */ Oid am_oid; FormData_pg_type am_typ; }; static struct typmap **Typ = NULL; static struct typmap *Ap = NULL; static bool Nulls[MAXATTR]; Form_pg_attribute attrtypes[MAXATTR]; /* points to attribute info */ static Datum values[MAXATTR]; /* corresponding attribute values */ int numattr; /* number of attributes for cur. rel */ static MemoryContext nogc = NULL; /* special no-gc mem context */ /* * At bootstrap time, we first declare all the indices to be built, and * then build them. The IndexList structure stores enough information * to allow us to build the indices after they've been declared. */ typedef struct _IndexList { Oid il_heap; Oid il_ind; IndexInfo *il_info; struct _IndexList *il_next; } IndexList; static IndexList *ILHead = NULL; /* * AuxiliaryProcessMain * * The main entry point for auxiliary processes, such as the bgwriter, * walwriter, bootstrapper and the shared memory checker code. * * This code is here just because of historical reasons. */ void AuxiliaryProcessMain(int argc, char *argv[]) { char *progname = argv[0]; int flag; char *userDoption = NULL; char stack_base; /* * initialize globals */ MyProcPid = getpid(); MyStartTime = time(NULL); /* Set up reference point for stack depth checking */ stack_base_ptr = &stack_base; /* * Fire up essential subsystems: error and memory management * * If we are running under the postmaster, this is done already. */ if (!IsUnderPostmaster) MemoryContextInit(); /* Compute paths, if we didn't inherit them from postmaster */ if (my_exec_path[0] == '\0') { if (find_my_exec(progname, my_exec_path) < 0) elog(FATAL, "%s: could not locate my own executable path", progname); } /* * process command arguments */ /* Set defaults, to be overriden by explicit options below */ if (!IsUnderPostmaster) InitializeGUCOptions(); /* Ignore the initial --boot argument, if present */ if (argc > 1 && strcmp(argv[1], "--boot") == 0) { argv++; argc--; } MyAuxProcType = CheckerProcess; while ((flag = getopt(argc, argv, "B:c:d:D:Fkr:x:y:-:")) != -1) { switch (flag) { case 'B': SetConfigOption("shared_buffers", optarg, PGC_POSTMASTER, PGC_S_ARGV); break; case 'D': userDoption = optarg; break; case 'd': { /* Turn on debugging for the bootstrap process. */ char *debugstr = palloc(strlen("debug") + strlen(optarg) + 1); sprintf(debugstr, "debug%s", optarg); SetConfigOption("log_min_messages", debugstr, PGC_POSTMASTER, PGC_S_ARGV); SetConfigOption("client_min_messages", debugstr, PGC_POSTMASTER, PGC_S_ARGV); pfree(debugstr); } break; case 'F': SetConfigOption("fsync", "false", PGC_POSTMASTER, PGC_S_ARGV); break; case 'k': bootstrap_data_checksum_version = PG_DATA_CHECKSUM_VERSION; break; case 'r': strlcpy(OutputFileName, optarg, MAXPGPATH); break; case 'x': MyAuxProcType = atoi(optarg); break; case 'c': case '-': { char *name, *value; ParseLongOption(optarg, &name, &value); if (!value) { if (flag == '-') ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("--%s requires a value", optarg))); else ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("-c %s requires a value", optarg))); } SetConfigOption(name, value, PGC_POSTMASTER, PGC_S_ARGV); free(name); if (value) free(value); break; } default: write_stderr("Try \"%s --help\" for more information.\n", progname); proc_exit(1); break; } } if (argc != optind) { write_stderr("%s: invalid command-line arguments\n", progname); proc_exit(1); } /* * Identify myself via ps */ if (IsUnderPostmaster) { const char *statmsg; switch (MyAuxProcType) { case StartupProcess: statmsg = "startup process"; break; case StartupPass2Process: statmsg = "startup pass 2 process"; break; case StartupPass3Process: statmsg = "startup pass 3 process"; break; case StartupPass4Process: statmsg = "startup pass 4 process"; break; case BgWriterProcess: statmsg = "writer process"; break; case WalWriterProcess: statmsg = "wal writer process"; break; case CheckpointerProcess: statmsg = "checkpointer process"; break; case WalReceiverProcess: statmsg = "wal receiver process"; break; case FilerepProcess: statmsg = "filerep process"; break; case FilerepResetPeerProcess: statmsg = "filerep reset peer process"; break; default: statmsg = "??? process"; break; } init_ps_display(statmsg, "", "", ""); } /* Acquire configuration parameters, unless inherited from postmaster */ if (!IsUnderPostmaster) { if (!SelectConfigFiles(userDoption, progname)) proc_exit(1); /* If timezone is not set, determine what the OS uses */ pg_timezone_initialize(); /* If timezone_abbreviations is not set, select default */ pg_timezone_abbrev_initialize(); } /* Validate we have been given a reasonable-looking DataDir */ Assert(DataDir); ValidatePgVersion(DataDir); /* Change into DataDir (if under postmaster, should be done already) */ if (!IsUnderPostmaster) ChangeToDataDir(); /* If standalone, create lockfile for data directory */ if (!IsUnderPostmaster) CreateDataDirLockFile(false); SetProcessingMode(BootstrapProcessing); IgnoreSystemIndexes = true; BaseInit(); /* * When we are an auxiliary process, we aren't going to do the full * InitPostgres pushups, but there are a couple of things that need to get * lit up even in an auxiliary process. */ /* * FileRep Main process does not use LWLock and so PGPROC is not required * to be initialized. * It is temporary fix to handle that here as an exception. */ if (IsUnderPostmaster && (MyAuxProcType != FilerepProcess && MyAuxProcType != FilerepResetPeerProcess)) { /* * Create a PGPROC so we can use LWLocks. In the EXEC_BACKEND case, * this was already done by SubPostmasterMain(). */ #ifndef EXEC_BACKEND InitAuxiliaryProcess(); #endif /* finish setting up bufmgr.c */ InitBufferPoolBackend(); /* register a shutdown callback for LWLock cleanup */ on_shmem_exit(ShutdownAuxiliaryProcess, 0); } /* * XLOG operations */ SetProcessingMode(NormalProcessing); switch (MyAuxProcType) { case CheckerProcess: bootstrap_signals(); CheckerModeMain(); proc_exit(1); /* should never return */ case BootstrapProcess: bootstrap_signals(); BootStrapXLOG(); StartupXLOG(); BootstrapModeMain(); proc_exit(1); /* should never return */ case StartupProcess: /* don't set signals, startup process has its own agenda */ StartupProcessMain(1); proc_exit(1); /* should never return */ case StartupPass2Process: /* don't set signals, startup process has its own agenda */ StartupProcessMain(2); proc_exit(1); /* should never return */ case StartupPass3Process: /* don't set signals, startup process has its own agenda */ StartupProcessMain(3); proc_exit(1); /* should never return */ case StartupPass4Process: /* don't set signals, startup process has its own agenda */ StartupProcessMain(4); proc_exit(1); /* should never return */ case BgWriterProcess: /* don't set signals, bgwriter has its own agenda */ BackgroundWriterMain(); proc_exit(1); /* should never return */ case CheckpointerProcess: /* don't set signals, checkpointer is similar to bgwriter and has its own agenda */ InitXLOGAccess(); CheckpointerMain(); proc_exit(1); /* should never return */ case WalReceiverProcess: /* don't set signals, walreceiver has its own agenda */ WalReceiverMain(); proc_exit(1); /* should never return */ case WalWriterProcess: /* don't set signals, walwriter has its own agenda */ InitXLOGAccess(); WalWriterMain(); proc_exit(1); /* should never return */ case FilerepProcess: FileRep_Main(); proc_exit(1); /* should never return */ default: elog(PANIC, "unrecognized process type: %d", MyAuxProcType); proc_exit(1); } } /* * In shared memory checker mode, all we really want to do is create shared * memory and semaphores (just to prove we can do it with the current GUC * settings). */ static void CheckerModeMain(void) { /* * We must be getting invoked for bootstrap mode */ Assert(!IsUnderPostmaster); SetProcessingMode(BootstrapProcessing); /* * Do backend-like initialization for bootstrap mode */ InitProcess(); InitPostgres(NULL, InvalidOid, NULL, NULL); proc_exit(0); } /* * The main entry point for running the backend in bootstrap mode * * The bootstrap mode is used to initialize the template database. * The bootstrap backend doesn't speak SQL, but instead expects * commands in a special bootstrap language. */ static void BootstrapModeMain(void) { int i; Assert(!IsUnderPostmaster); SetProcessingMode(BootstrapProcessing); /* * Do backend-like initialization for bootstrap mode */ InitProcess(); InitPostgres(NULL, InvalidOid, NULL, NULL); /* Initialize stuff for bootstrap-file processing */ for (i = 0; i < MAXATTR; i++) { attrtypes[i] = NULL; Nulls[i] = false; } for (i = 0; i < STRTABLESIZE; ++i) strtable[i] = NULL; for (i = 0; i < HASHTABLESIZE; ++i) hashtable[i] = NULL; /* * Process bootstrap input. */ boot_yyparse(); /* Perform a checkpoint to ensure everything's down to disk */ SetProcessingMode(NormalProcessing); CreateCheckPoint(CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_IMMEDIATE); /* Clean up and exit */ cleanup(); proc_exit(0); } /* ---------------------------------------------------------------- * misc functions * ---------------------------------------------------------------- */ /* * Set up signal handling for a bootstrap process */ static void bootstrap_signals(void) { if (IsUnderPostmaster) { /* * If possible, make this process a group leader, so that the * postmaster can signal any child processes too. */ #ifdef HAVE_SETSID if (setsid() < 0) elog(FATAL, "setsid() failed: %m"); #endif /* * Properly accept or ignore signals the postmaster might send us */ pqsignal(SIGHUP, SIG_IGN); pqsignal(SIGINT, SIG_IGN); /* ignore query-cancel */ pqsignal(SIGTERM, die); pqsignal(SIGQUIT, quickdie); pqsignal(SIGALRM, SIG_IGN); pqsignal(SIGPIPE, SIG_IGN); pqsignal(SIGUSR1, SIG_IGN); pqsignal(SIGUSR2, SIG_IGN); /* * Reset some signals that are accepted by postmaster but not here */ pqsignal(SIGCHLD, SIG_DFL); pqsignal(SIGTTIN, SIG_DFL); pqsignal(SIGTTOU, SIG_DFL); pqsignal(SIGCONT, SIG_DFL); pqsignal(SIGWINCH, SIG_DFL); /* * Unblock signals (they were blocked when the postmaster forked us) */ PG_SETMASK(&UnBlockSig); } else { /* Set up appropriately for interactive use */ pqsignal(SIGHUP, die); pqsignal(SIGINT, die); pqsignal(SIGTERM, die); pqsignal(SIGQUIT, die); } } /* * Begin shutdown of an auxiliary process. This is approximately the equivalent * of ShutdownPostgres() in postinit.c. We can't run transactions in an * auxiliary process, so most of the work of AbortTransaction() is not needed, * but we do need to make sure we've released any LWLocks we are holding. * (This is only critical during an error exit.) */ static void ShutdownAuxiliaryProcess(int code, Datum arg) { LWLockReleaseAll(); } /* ---------------------------------------------------------------- * MANUAL BACKEND INTERACTIVE INTERFACE COMMANDS * ---------------------------------------------------------------- */ /* ---------------- * boot_openrel * ---------------- */ void boot_openrel(char *relname) { int i; struct typmap **app; Relation rel; HeapScanDesc scan; HeapTuple tup; if (strlen(relname) >= NAMEDATALEN) relname[NAMEDATALEN - 1] = '\0'; if (Typ == NULL) { /* We can now load the pg_type data */ rel = heap_open(TypeRelationId, NoLock); scan = heap_beginscan(rel, SnapshotNow, 0, NULL); i = 0; while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL) ++i; heap_endscan(scan); app = Typ = ALLOC(struct typmap *, i + 1); while (i-- > 0) *app++ = ALLOC(struct typmap, 1); *app = NULL; scan = heap_beginscan(rel, SnapshotNow, 0, NULL); app = Typ; while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL) { (*app)->am_oid = HeapTupleGetOid(tup); memcpy((char *) &(*app)->am_typ, (char *) GETSTRUCT(tup), sizeof((*app)->am_typ)); app++; } heap_endscan(scan); heap_close(rel, NoLock); } if (boot_reldesc != NULL) closerel(NULL); elog(DEBUG4, "open relation %s, attrsize %d", relname, (int) ATTRIBUTE_FIXED_PART_SIZE); boot_reldesc = heap_openrv(makeRangeVar(NULL, relname, -1), NoLock); numattr = boot_reldesc->rd_rel->relnatts; for (i = 0; i < numattr; i++) { if (attrtypes[i] == NULL) attrtypes[i] = AllocateAttribute(); memmove((char *) attrtypes[i], (char *) boot_reldesc->rd_att->attrs[i], ATTRIBUTE_FIXED_PART_SIZE); { Form_pg_attribute at = attrtypes[i]; elog(DEBUG4, "create attribute %d name %s len %d num %d type %u", i, NameStr(at->attname), at->attlen, at->attnum, at->atttypid); } } } /* ---------------- * closerel * ---------------- */ void closerel(char *name) { if (name) { if (boot_reldesc) { if (strcmp(RelationGetRelationName(boot_reldesc), name) != 0) elog(ERROR, "close of %s when %s was expected", name, RelationGetRelationName(boot_reldesc)); } else elog(ERROR, "close of %s before any relation was opened", name); } if (boot_reldesc == NULL) elog(ERROR, "no open relation to close"); else { elog(DEBUG4, "close relation %s", RelationGetRelationName(boot_reldesc)); heap_close(boot_reldesc, NoLock); boot_reldesc = NULL; } } /* ---------------- * DEFINEATTR() * * define a pair * if there are n fields in a relation to be created, this routine * will be called n times * ---------------- */ void DefineAttr(char *name, char *type, int attnum) { Oid typeoid; if (boot_reldesc != NULL) { elog(WARNING, "no open relations allowed with CREATE command"); closerel(NULL); } if (attrtypes[attnum] == NULL) attrtypes[attnum] = AllocateAttribute(); MemSet(attrtypes[attnum], 0, ATTRIBUTE_FIXED_PART_SIZE); namestrcpy(&attrtypes[attnum]->attname, name); elog(DEBUG4, "column %s %s", NameStr(attrtypes[attnum]->attname), type); attrtypes[attnum]->attnum = attnum + 1; /* fillatt */ typeoid = gettype(type); if (Typ != NULL) { attrtypes[attnum]->atttypid = Ap->am_oid; attrtypes[attnum]->attlen = Ap->am_typ.typlen; attrtypes[attnum]->attbyval = Ap->am_typ.typbyval; attrtypes[attnum]->attstorage = Ap->am_typ.typstorage; attrtypes[attnum]->attalign = Ap->am_typ.typalign; /* if an array type, assume 1-dimensional attribute */ if (Ap->am_typ.typelem != InvalidOid && Ap->am_typ.typlen < 0) attrtypes[attnum]->attndims = 1; else attrtypes[attnum]->attndims = 0; } else { attrtypes[attnum]->atttypid = TypInfo[typeoid].oid; attrtypes[attnum]->attlen = TypInfo[typeoid].len; attrtypes[attnum]->attbyval = TypInfo[typeoid].byval; attrtypes[attnum]->attstorage = TypInfo[typeoid].storage; attrtypes[attnum]->attalign = TypInfo[typeoid].align; /* if an array type, assume 1-dimensional attribute */ if (TypInfo[typeoid].elem != InvalidOid && attrtypes[attnum]->attlen < 0) attrtypes[attnum]->attndims = 1; else attrtypes[attnum]->attndims = 0; } attrtypes[attnum]->attstattarget = -1; attrtypes[attnum]->attcacheoff = -1; attrtypes[attnum]->atttypmod = -1; attrtypes[attnum]->attislocal = true; /* * Mark as "not null" if type is fixed-width and prior columns are too. * This corresponds to case where column can be accessed directly via C * struct declaration. * * oidvector and int2vector are also treated as not-nullable, even though * they are no longer fixed-width. */ #define MARKNOTNULL(att) \ ((att)->attlen > 0 || \ (att)->atttypid == OIDVECTOROID || \ (att)->atttypid == INT2VECTOROID) if (MARKNOTNULL(attrtypes[attnum])) { int i; for (i = 0; i < attnum; i++) { if (!MARKNOTNULL(attrtypes[i])) break; } if (i == attnum) attrtypes[attnum]->attnotnull = true; } } /* ---------------- * InsertOneTuple * * If objectid is not zero, it is a specific OID to assign to the tuple. * Otherwise, an OID will be assigned (if necessary) by heap_insert. * ---------------- */ void InsertOneTuple(Oid objectid) { HeapTuple tuple; TupleDesc tupDesc; int i; elog(DEBUG4, "inserting row oid %u, %d columns", objectid, numattr); tupDesc = CreateTupleDesc(numattr, RelationGetForm(boot_reldesc)->relhasoids, attrtypes); tuple = heap_form_tuple(tupDesc, values, Nulls); if (objectid != (Oid) 0) HeapTupleSetOid(tuple, objectid); pfree(tupDesc); /* just free's tupDesc, not the attrtypes */ simple_heap_insert(boot_reldesc, tuple); heap_freetuple(tuple); elog(DEBUG4, "row inserted"); /* * Reset null markers for next tuple */ for (i = 0; i < numattr; i++) Nulls[i] = false; } /* ---------------- * InsertOneValue * ---------------- */ void InsertOneValue(char *value, int i) { Oid typoid; int16 typlen; bool typbyval; char typalign; char typdelim; Oid typioparam; Oid typinput; Oid typoutput; char *prt; AssertArg(i >= 0 || i < MAXATTR); elog(DEBUG4, "inserting column %d value \"%s\"", i, value); typoid = boot_reldesc->rd_att->attrs[i]->atttypid; boot_get_type_io_data(typoid, &typlen, &typbyval, &typalign, &typdelim, &typioparam, &typinput, &typoutput); values[i] = OidInputFunctionCall(typinput, value, typioparam, -1); prt = OidOutputFunctionCall(typoutput, values[i]); elog(DEBUG4, "inserted -> %s", prt); pfree(prt); } /* ---------------- * InsertOneNull * ---------------- */ void InsertOneNull(int i) { elog(DEBUG4, "inserting column %d NULL", i); Assert(i >= 0 || i < MAXATTR); values[i] = PointerGetDatum(NULL); Nulls[i] = true; } /* ---------------- * cleanup * ---------------- */ static void cleanup(void) { if (boot_reldesc != NULL) closerel(NULL); } /* ---------------- * gettype * * NB: this is really ugly; it will return an integer index into TypInfo[], * and not an OID at all, until the first reference to a type not known in * TypInfo[]. At that point it will read and cache pg_type in the Typ array, * and subsequently return a real OID (and set the global pointer Ap to * point at the found row in Typ). So caller must check whether Typ is * still NULL to determine what the return value is! * ---------------- */ static Oid gettype(char *type) { int i; Relation rel; HeapScanDesc scan; HeapTuple tup; struct typmap **app; if (Typ != NULL) { for (app = Typ; *app != NULL; app++) { if (strncmp(NameStr((*app)->am_typ.typname), type, NAMEDATALEN) == 0) { Ap = *app; return (*app)->am_oid; } } } else { for (i = 0; i < n_types; i++) { if (strncmp(type, TypInfo[i].name, NAMEDATALEN) == 0) return i; } elog(DEBUG4, "external type: %s", type); rel = heap_open(TypeRelationId, NoLock); scan = heap_beginscan(rel, SnapshotNow, 0, NULL); i = 0; while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL) ++i; heap_endscan(scan); app = Typ = ALLOC(struct typmap *, i + 1); while (i-- > 0) *app++ = ALLOC(struct typmap, 1); *app = NULL; scan = heap_beginscan(rel, SnapshotNow, 0, NULL); app = Typ; while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL) { (*app)->am_oid = HeapTupleGetOid(tup); memmove((char *) &(*app++)->am_typ, (char *) GETSTRUCT(tup), sizeof((*app)->am_typ)); } heap_endscan(scan); heap_close(rel, NoLock); return gettype(type); } elog(ERROR, "unrecognized type \"%s\"", type); /* not reached, here to make compiler happy */ return 0; } /* ---------------- * boot_get_type_io_data * * Obtain type I/O information at bootstrap time. This intentionally has * almost the same API as lsyscache.c's get_type_io_data, except that * we only support obtaining the typinput and typoutput routines, not * the binary I/O routines. It is exported so that array_in and array_out * can be made to work during early bootstrap. * ---------------- */ void boot_get_type_io_data(Oid typid, int16 *typlen, bool *typbyval, char *typalign, char *typdelim, Oid *typioparam, Oid *typinput, Oid *typoutput) { if (Typ != NULL) { /* We have the boot-time contents of pg_type, so use it */ struct typmap **app; struct typmap *ap; app = Typ; while (*app && (*app)->am_oid != typid) ++app; ap = *app; if (ap == NULL) elog(ERROR, "type OID %u not found in Typ list", typid); *typlen = ap->am_typ.typlen; *typbyval = ap->am_typ.typbyval; *typalign = ap->am_typ.typalign; *typdelim = ap->am_typ.typdelim; /* XXX this logic must match getTypeIOParam() */ if (OidIsValid(ap->am_typ.typelem)) *typioparam = ap->am_typ.typelem; else *typioparam = typid; *typinput = ap->am_typ.typinput; *typoutput = ap->am_typ.typoutput; } else { /* We don't have pg_type yet, so use the hard-wired TypInfo array */ int typeindex; for (typeindex = 0; typeindex < n_types; typeindex++) { if (TypInfo[typeindex].oid == typid) break; } if (typeindex >= n_types) elog(ERROR, "type OID %u not found in TypInfo", typid); *typlen = TypInfo[typeindex].len; *typbyval = TypInfo[typeindex].byval; *typalign = TypInfo[typeindex].align; /* We assume typdelim is ',' for all boot-time types */ *typdelim = ','; /* XXX this logic must match getTypeIOParam() */ if (OidIsValid(TypInfo[typeindex].elem)) *typioparam = TypInfo[typeindex].elem; else *typioparam = typid; *typinput = TypInfo[typeindex].inproc; *typoutput = TypInfo[typeindex].outproc; } } /* ---------------- * AllocateAttribute * * Note: bootstrap never sets any per-column ACLs, so we only need * ATTRIBUTE_FIXED_PART_SIZE space per attribute. * ---------------- */ static Form_pg_attribute AllocateAttribute(void) { Form_pg_attribute attribute = (Form_pg_attribute) malloc(ATTRIBUTE_FIXED_PART_SIZE); if (!PointerIsValid(attribute)) elog(FATAL, "out of memory"); MemSet(attribute, 0, ATTRIBUTE_FIXED_PART_SIZE); return attribute; } /* ---------------- * MapArrayTypeName * XXX arrays of "basetype" are always "_basetype". * this is an evil hack inherited from rel. 3.1. * XXX array dimension is thrown away because we * don't support fixed-dimension arrays. again, * sickness from 3.1. * * the string passed in must have a '[' character in it * * the string returned is a pointer to static storage and should NOT * be freed by the CALLER. * ---------------- */ char * MapArrayTypeName(char *s) { int i, j; static char newStr[NAMEDATALEN]; /* array type names < NAMEDATALEN long */ if (s == NULL || s[0] == '\0') return s; j = 1; newStr[0] = '_'; for (i = 0; i < NAMEDATALEN - 1 && s[i] != '['; i++, j++) newStr[j] = s[i]; newStr[j] = '\0'; return newStr; } /* ---------------- * EnterString * returns the string table position of the identifier * passed to it. We add it to the table if we can't find it. * ---------------- */ int EnterString(char *str) { hashnode *node; int len; len = strlen(str); node = FindStr(str, len, NULL); if (node) return node->strnum; else { node = AddStr(str, len, 0); return node->strnum; } } /* ---------------- * LexIDStr * when given an idnum into the 'string-table' return the string * associated with the idnum * ---------------- */ char * LexIDStr(int ident_num) { return strtable[ident_num]; } /* ---------------- * CompHash * * Compute a hash function for a given string. We look at the first, * the last, and the middle character of a string to try to get spread * the strings out. The function is rather arbitrary, except that we * are mod'ing by a prime number. * ---------------- */ static int CompHash(char *str, int len) { int result; Assert(len >=0 && "string length is negative"); if (len == 0) { return 0; } result = (NUM * str[0] + NUMSQR * str[len - 1] + NUMCUBE * str[(len - 1) / 2]); return result % HASHTABLESIZE; } /* ---------------- * FindStr * * This routine looks for the specified string in the hash * table. It returns a pointer to the hash node found, * or NULL if the string is not in the table. * ---------------- */ static hashnode * FindStr(char *str, int length, hashnode *mderef) { hashnode *node; node = hashtable[CompHash(str, length)]; while (node != NULL) { /* * We must differentiate between string constants that might have the * same value as a identifier and the identifier itself. */ if (!strcmp(str, strtable[node->strnum])) { return node; /* no need to check */ } else node = node->next; } /* Couldn't find it in the list */ return NULL; } /* ---------------- * AddStr * * This function adds the specified string, along with its associated * data, to the hash table and the string table. We return the node * so that the calling routine can find out the unique id that AddStr * has assigned to this string. * ---------------- */ static hashnode * AddStr(char *str, int strlength, int mderef) { hashnode *temp, *trail, *newnode; int hashresult; int len; if (++strtable_end >= STRTABLESIZE) elog(FATAL, "bootstrap string table overflow"); /* * Some of the utilites (eg, define type, create relation) assume that the * string they're passed is a NAMEDATALEN. We get array bound read * violations from purify if we don't allocate at least NAMEDATALEN bytes * for strings of this sort. Because we're lazy, we allocate at least * NAMEDATALEN bytes all the time. */ if ((len = strlength + 1) < NAMEDATALEN) len = NAMEDATALEN; strtable[strtable_end] = malloc((unsigned) len); if (strtable[strtable_end] == NULL) ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Bootstrap failed: out of memory in AddStr"))); strcpy(strtable[strtable_end], str); /* Now put a node in the hash table */ newnode = (hashnode *) malloc(sizeof(hashnode) * 1); if (newnode == NULL) ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Bootstrap failed: out of memory in AddStr"))); newnode->strnum = strtable_end; newnode->next = NULL; /* Find out where it goes */ hashresult = CompHash(str, strlength); if (hashtable[hashresult] == NULL) hashtable[hashresult] = newnode; else { /* There is something in the list */ trail = hashtable[hashresult]; temp = trail->next; while (temp != NULL) { trail = temp; temp = temp->next; } trail->next = newnode; } return newnode; } /* * index_register() -- record an index that has been set up for building * later. * * At bootstrap time, we define a bunch of indexes on system catalogs. * We postpone actually building the indexes until just before we're * finished with initialization, however. This is because the indexes * themselves have catalog entries, and those have to be included in the * indexes on those catalogs. Doing it in two phases is the simplest * way of making sure the indexes have the right contents at the end. */ void index_register(Oid heap, Oid ind, IndexInfo *indexInfo) { IndexList *newind; MemoryContext oldcxt; /* * XXX mao 10/31/92 -- don't gc index reldescs, associated info at * bootstrap time. we'll declare the indexes now, but want to create them * later. */ if (nogc == NULL) nogc = AllocSetContextCreate(NULL, "BootstrapNoGC", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); oldcxt = MemoryContextSwitchTo(nogc); newind = (IndexList *) palloc(sizeof(IndexList)); newind->il_heap = heap; newind->il_ind = ind; newind->il_info = (IndexInfo *) palloc(sizeof(IndexInfo)); memcpy(newind->il_info, indexInfo, sizeof(IndexInfo)); /* expressions will likely be null, but may as well copy it */ newind->il_info->ii_Expressions = (List *) copyObject(indexInfo->ii_Expressions); newind->il_info->ii_ExpressionsState = NIL; /* predicate will likely be null, but may as well copy it */ newind->il_info->ii_Predicate = (List *) copyObject(indexInfo->ii_Predicate); newind->il_info->ii_PredicateState = NIL; newind->il_next = ILHead; ILHead = newind; MemoryContextSwitchTo(oldcxt); } /* * build_indices -- fill in all the indexes registered earlier */ void build_indices(void) { for (; ILHead != NULL; ILHead = ILHead->il_next) { Relation heap; Relation ind; /* need not bother with locks during bootstrap */ heap = heap_open(ILHead->il_heap, NoLock); ind = index_open(ILHead->il_ind, NoLock); index_build(heap, ind, ILHead->il_info, false, false); index_close(ind, NoLock); heap_close(heap, NoLock); } }