📄 indexcmds.c
字号:
* error checks) */ if (isconstraint && !quiet) ereport(NOTICE, (errmsg("%s %s will create implicit index \"%s\" for table \"%s\"", is_alter_table ? "ALTER TABLE / ADD" : "CREATE TABLE /", primary ? "PRIMARY KEY" : "UNIQUE", indexRelationName, RelationGetRelationName(rel)))); index_create(relationId, indexRelationName, indexRelationId, indexInfo, accessMethodId, tablespaceId, classObjectId, primary, isconstraint, allowSystemTableMods, skip_build); /* * We update the relation's pg_class tuple even if it already has * relhasindex = true. This is needed to cause a shared-cache-inval * message to be sent for the pg_class tuple, which will cause other * backends to flush their relcache entries and in particular their cached * lists of the indexes for this relation. */ setRelhasindex(relationId, true, primary, InvalidOid);}/* * CheckPredicate * Checks that the given partial-index predicate is valid. * * This used to also constrain the form of the predicate to forms that * indxpath.c could do something with. However, that seems overly * restrictive. One useful application of partial indexes is to apply * a UNIQUE constraint across a subset of a table, and in that scenario * any evaluatable predicate will work. So accept any predicate here * (except ones requiring a plan), and let indxpath.c fend for itself. */static voidCheckPredicate(Expr *predicate){ /* * We don't currently support generation of an actual query plan for a * predicate, only simple scalar expressions; hence these restrictions. */ if (contain_subplans((Node *) predicate)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot use subquery in index predicate"))); if (contain_agg_clause((Node *) predicate)) ereport(ERROR, (errcode(ERRCODE_GROUPING_ERROR), errmsg("cannot use aggregate in index predicate"))); /* * A predicate using mutable functions is probably wrong, for the same * reasons that we don't allow an index expression to use one. */ if (contain_mutable_functions((Node *) predicate)) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("functions in index predicate must be marked IMMUTABLE")));}static voidComputeIndexAttrs(IndexInfo *indexInfo, Oid *classOidP, List *attList, /* list of IndexElem's */ Oid relId, char *accessMethodName, Oid accessMethodId, bool isconstraint){ ListCell *rest; int attn = 0; /* * process attributeList */ foreach(rest, attList) { IndexElem *attribute = (IndexElem *) lfirst(rest); Oid atttype; if (attribute->name != NULL) { /* Simple index attribute */ HeapTuple atttuple; Form_pg_attribute attform; Assert(attribute->expr == NULL); atttuple = SearchSysCacheAttName(relId, attribute->name); if (!HeapTupleIsValid(atttuple)) { /* difference in error message spellings is historical */ if (isconstraint) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("column \"%s\" named in key does not exist", attribute->name))); else ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("column \"%s\" does not exist", attribute->name))); } attform = (Form_pg_attribute) GETSTRUCT(atttuple); indexInfo->ii_KeyAttrNumbers[attn] = attform->attnum; atttype = attform->atttypid; ReleaseSysCache(atttuple); } else if (attribute->expr && IsA(attribute->expr, Var)) { /* Tricky tricky, he wrote (column) ... treat as simple attr */ Var *var = (Var *) attribute->expr; indexInfo->ii_KeyAttrNumbers[attn] = var->varattno; atttype = get_atttype(relId, var->varattno); } else { /* Index expression */ Assert(attribute->expr != NULL); indexInfo->ii_KeyAttrNumbers[attn] = 0; /* marks expression */ indexInfo->ii_Expressions = lappend(indexInfo->ii_Expressions, attribute->expr); atttype = exprType(attribute->expr); /* * We don't currently support generation of an actual query plan * for an index expression, only simple scalar expressions; hence * these restrictions. */ if (contain_subplans(attribute->expr)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot use subquery in index expression"))); if (contain_agg_clause(attribute->expr)) ereport(ERROR, (errcode(ERRCODE_GROUPING_ERROR), errmsg("cannot use aggregate function in index expression"))); /* * A expression using mutable functions is probably wrong, since * if you aren't going to get the same result for the same data * every time, it's not clear what the index entries mean at all. */ if (contain_mutable_functions(attribute->expr)) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("functions in index expression must be marked IMMUTABLE"))); } classOidP[attn] = GetIndexOpClass(attribute->opclass, atttype, accessMethodName, accessMethodId); attn++; }}/* * Resolve possibly-defaulted operator class specification */static OidGetIndexOpClass(List *opclass, Oid attrType, char *accessMethodName, Oid accessMethodId){ char *schemaname; char *opcname; HeapTuple tuple; Oid opClassId, opInputType; /* * Release 7.0 removed network_ops, timespan_ops, and datetime_ops, so we * ignore those opclass names so the default *_ops is used. This can be * removed in some later release. bjm 2000/02/07 * * Release 7.1 removes lztext_ops, so suppress that too for a while. tgl * 2000/07/30 * * Release 7.2 renames timestamp_ops to timestamptz_ops, so suppress that * too for awhile. I'm starting to think we need a better approach. tgl * 2000/10/01 * * Release 8.0 removes bigbox_ops (which was dead code for a long while * anyway). tgl 2003/11/11 */ if (list_length(opclass) == 1) { char *claname = strVal(linitial(opclass)); if (strcmp(claname, "network_ops") == 0 || strcmp(claname, "timespan_ops") == 0 || strcmp(claname, "datetime_ops") == 0 || strcmp(claname, "lztext_ops") == 0 || strcmp(claname, "timestamp_ops") == 0 || strcmp(claname, "bigbox_ops") == 0) opclass = NIL; } if (opclass == NIL) { /* no operator class specified, so find the default */ opClassId = GetDefaultOpClass(attrType, accessMethodId); if (!OidIsValid(opClassId)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("data type %s has no default operator class for access method \"%s\"", format_type_be(attrType), accessMethodName), errhint("You must specify an operator class for the index or define a default operator class for the data type."))); return opClassId; } /* * Specific opclass name given, so look up the opclass. */ /* deconstruct the name list */ DeconstructQualifiedName(opclass, &schemaname, &opcname); if (schemaname) { /* Look in specific schema only */ Oid namespaceId; namespaceId = LookupExplicitNamespace(schemaname); tuple = SearchSysCache(CLAAMNAMENSP, ObjectIdGetDatum(accessMethodId), PointerGetDatum(opcname), ObjectIdGetDatum(namespaceId), 0); } else { /* Unqualified opclass name, so search the search path */ opClassId = OpclassnameGetOpcid(accessMethodId, opcname); if (!OidIsValid(opClassId)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("operator class \"%s\" does not exist for access method \"%s\"", opcname, accessMethodName))); tuple = SearchSysCache(CLAOID, ObjectIdGetDatum(opClassId), 0, 0, 0); } if (!HeapTupleIsValid(tuple)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("operator class \"%s\" does not exist for access method \"%s\"", NameListToString(opclass), accessMethodName))); /* * Verify that the index operator class accepts this datatype. Note we * will accept binary compatibility. */ opClassId = HeapTupleGetOid(tuple); opInputType = ((Form_pg_opclass) GETSTRUCT(tuple))->opcintype; if (!IsBinaryCoercible(attrType, opInputType)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("operator class \"%s\" does not accept data type %s", NameListToString(opclass), format_type_be(attrType)))); ReleaseSysCache(tuple); return opClassId;}/* * GetDefaultOpClass * * Given the OIDs of a datatype and an access method, find the default * operator class, if any. Returns InvalidOid if there is none. */OidGetDefaultOpClass(Oid type_id, Oid am_id){ int nexact = 0; int ncompatible = 0; Oid exactOid = InvalidOid; Oid compatibleOid = InvalidOid; Relation rel; ScanKeyData skey[1]; SysScanDesc scan; HeapTuple tup; /* If it's a domain, look at the base type instead */ type_id = getBaseType(type_id); /* * We scan through all the opclasses available for the access method, * looking for one that is marked default and matches the target type * (either exactly or binary-compatibly, but prefer an exact match). * * We could find more than one binary-compatible match, in which case we * require the user to specify which one he wants. If we find more than * one exact match, then someone put bogus entries in pg_opclass. */ rel = heap_open(OperatorClassRelationId, AccessShareLock); ScanKeyInit(&skey[0], Anum_pg_opclass_opcamid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(am_id)); scan = systable_beginscan(rel, OpclassAmNameNspIndexId, true, SnapshotNow, 1, skey); while (HeapTupleIsValid(tup = systable_getnext(scan))) { Form_pg_opclass opclass = (Form_pg_opclass) GETSTRUCT(tup); if (opclass->opcdefault) { if (opclass->opcintype == type_id) { nexact++; exactOid = HeapTupleGetOid(tup); } else if (IsBinaryCoercible(type_id, opclass->opcintype)) { ncompatible++; compatibleOid = HeapTupleGetOid(tup); } } } systable_endscan(scan); heap_close(rel, AccessShareLock); if (nexact == 1) return exactOid; if (nexact != 0) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("there are multiple default operator classes for data type %s", format_type_be(type_id)))); if (ncompatible == 1) return compatibleOid; return InvalidOid;}/* * makeObjectName() * * Create a name for an implicitly created index, sequence, constraint, etc. * * The parameters are typically: the original table name, the original field * name, and a "type" string (such as "seq" or "pkey"). The field name * and/or type can be NULL if not relevant. * * The result is a palloc'd string. * * The basic result we want is "name1_name2_label", omitting "_name2" or * "_label" when those parameters are NULL. However, we must generate * a name with less than NAMEDATALEN characters! So, we truncate one or * both names if necessary to make a short-enough string. The label part * is never truncated (so it had better be reasonably short). * * The caller is responsible for checking uniqueness of the generated * name and retrying as needed; retrying will be done by altering the * "label" string (which is why we never truncate that part). */char *makeObjectName(const char *name1, const char *name2, const char *label){ char *name;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -