Datum caql_copy_to_in_memory_pg_class(PG_FUNCTION_ARGS) { text *inText = PG_GETARG_TEXT_P(0);; char *inStr = text_to_cstring(inText); char kind = PG_GETARG_CHAR(1); StringInfoData buf; initStringInfo(&buf); /* create tuples for pg_class table */ HeapTuple reltup = NULL; HeapTuple copytup = NULL; Form_pg_class relform; cqContext *pcqCtx; cqContext *pcqCtxInsert; pcqCtx = caql_beginscan( NULL, cql("SELECT * FROM pg_class " " WHERE relname = :1", CStringGetDatum((char *) inStr))); reltup = caql_getnext(pcqCtx); if (NULL == reltup) { appendStringInfo(&buf, "no tuples with relname=%s found!", inStr); } else { copytup = heaptuple_copy_to(reltup, NULL, NULL); relform = (Form_pg_class) GETSTRUCT(copytup); relform->relkind = kind; appendStringInfo(&buf, "table pg_class, insert 1 line (relname %s, relkind %c)", NameStr(relform->relname), kind); /* insert */ pcqCtxInsert = caql_beginscan( NULL, cql("INSERT INTO pg_class", NULL)); caql_insert_inmem(pcqCtxInsert, copytup); caql_endscan(pcqCtxInsert); heap_freetuple(copytup); } caql_endscan(pcqCtx); PG_RETURN_TEXT_P(cstring_to_text(buf.data)); }
/* * assign_func_result_transient_type * assign typmod if the result of function is transient type. * */ void assign_func_result_transient_type(Oid funcid) { HeapTuple tp; Form_pg_proc procform; TupleDesc tupdesc; cqContext *pcqCtx; pcqCtx = caql_beginscan( NULL, cql("SELECT * FROM pg_proc " " WHERE oid = :1 ", ObjectIdGetDatum(funcid))); tp = caql_getnext(pcqCtx); caql_endscan(pcqCtx); if (!HeapTupleIsValid(tp)) elog(ERROR, "cache lookup failed for function %u", funcid); procform = (Form_pg_proc) GETSTRUCT(tp); tupdesc = build_function_result_tupdesc_t(tp); if (tupdesc == NULL) return; if (resolve_polymorphic_tupdesc(tupdesc, &procform->proargtypes, NULL)) { if (tupdesc->tdtypeid == RECORDOID && tupdesc->tdtypmod < 0) assign_record_type_typmod(tupdesc); } }
/* * ConversionDrop * * Drop a conversion after doing permission checks. */ void ConversionDrop(Oid conversionOid, DropBehavior behavior) { HeapTuple tuple; ObjectAddress object; cqContext *pcqCtx; pcqCtx = caql_beginscan( NULL, cql("SELECT * FROM pg_conversion " " WHERE oid = :1 ", ObjectIdGetDatum(conversionOid))); tuple = caql_getnext(pcqCtx); if (!HeapTupleIsValid(tuple)) elog(ERROR, "cache lookup failed for conversion %u", conversionOid); if (!superuser() && ((Form_pg_conversion) GETSTRUCT(tuple))->conowner != GetUserId()) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION, NameStr(((Form_pg_conversion) GETSTRUCT(tuple))->conname)); caql_endscan(pcqCtx); /* * Do the deletion */ object.classId = ConversionRelationId; object.objectId = conversionOid; object.objectSubId = 0; performDeletion(&object, behavior); }
Datum caql_insert_to_in_memory_pg_attribute(PG_FUNCTION_ARGS) { Oid attrelid = PG_GETARG_OID(0); char *attname = text_to_cstring(PG_GETARG_TEXT_P(1)); AttrNumber attno = PG_GETARG_INT16(2); cqContext *pcqCtx = caql_beginscan( NULL, cql("INSERT INTO pg_attribute", NULL)); FormData_pg_attribute attributeD; HeapTuple attributeTuple = heap_addheader(Natts_pg_attribute, false, ATTRIBUTE_TUPLE_SIZE, (void *) &attributeD); Form_pg_attribute attribute = (Form_pg_attribute) GETSTRUCT(attributeTuple); attribute->attrelid = attrelid; namestrcpy(&(attribute->attname), attname); attribute->attnum = attno; caql_insert_inmem(pcqCtx, attributeTuple); caql_endscan(pcqCtx); StringInfoData buf; initStringInfo(&buf); appendStringInfo(&buf, "inserted tuple to pg_attribute (attrelid %d, attname %s, attno %d)", attribute->attrelid, NameStr(attribute->attname), attribute->attnum); PG_RETURN_TEXT_P(cstring_to_text(buf.data)); }
/* * Add a single attribute encoding entry. */ static void add_attribute_encoding_entry(Oid relid, AttrNumber attnum, Datum attoptions) { Datum values[Natts_pg_attribute_encoding]; bool nulls[Natts_pg_attribute_encoding]; HeapTuple tuple; cqContext *pcqCtx; Insist(!gp_upgrade_mode); Insist(attnum != InvalidAttrNumber); pcqCtx = caql_beginscan( NULL, cql("INSERT INTO pg_attribute_encoding", NULL)); MemSet(nulls, 0, sizeof(nulls)); values[Anum_pg_attribute_encoding_attrelid - 1] = ObjectIdGetDatum(relid); values[Anum_pg_attribute_encoding_attnum - 1] = Int16GetDatum(attnum); values[Anum_pg_attribute_encoding_attoptions - 1] = attoptions; tuple = caql_form_tuple(pcqCtx, values, nulls); /* insert a new tuple */ caql_insert(pcqCtx, tuple); /* implicit update of index as well */ heap_freetuple(tuple); caql_endscan(pcqCtx); }
/* * Record a type's default encoding clause in the catalog. */ void add_type_encoding(Oid typid, Datum typoptions) { Datum values[Natts_pg_type_encoding]; bool nulls[Natts_pg_type_encoding]; HeapTuple tuple; cqContext *pcqCtx; pcqCtx = caql_beginscan( NULL, cql("INSERT INTO pg_type_encoding ", NULL)); MemSet(nulls, false, sizeof(nulls)); values[Anum_pg_type_encoding_typid - 1] = ObjectIdGetDatum(typid); values[Anum_pg_type_encoding_typoptions - 1] = typoptions; tuple = caql_form_tuple(pcqCtx, values, nulls); /* Insert tuple into the relation */ caql_insert(pcqCtx, tuple); /* implicit update of index as well */ caql_endscan(pcqCtx); }
/* * FindConversion * * Find conversion by namespace and conversion name. * Returns conversion OID. */ Oid FindConversion(const char *conname, Oid connamespace) { HeapTuple tuple; Oid procoid; Oid conoid; AclResult aclresult; cqContext *pcqCtx; /* search pg_conversion by connamespace and conversion name */ pcqCtx = caql_beginscan( NULL, cql("SELECT * FROM pg_conversion " " WHERE conname = :1 " " AND connamespace = :2 ", CStringGetDatum((char *) conname), ObjectIdGetDatum(connamespace))); tuple = caql_getnext(pcqCtx); if (!HeapTupleIsValid(tuple)) return InvalidOid; procoid = ((Form_pg_conversion) GETSTRUCT(tuple))->conproc; conoid = HeapTupleGetOid(tuple); caql_endscan(pcqCtx); /* Check we have execute rights for the function */ aclresult = pg_proc_aclcheck(procoid, GetUserId(), ACL_EXECUTE); if (aclresult != ACLCHECK_OK) return InvalidOid; return conoid; }
void update_segment_status_by_id(uint32_t id, char status) { /* we use AccessExclusiveLock to prevent races */ Relation rel = heap_open(GpSegmentConfigRelationId, AccessExclusiveLock); HeapTuple tuple; cqContext cqc; cqContext *pcqCtx; Assert(status == 'u' || status == 'd'); pcqCtx = caql_beginscan(caql_addrel(cqclr(&cqc), rel), cql("SELECT * FROM gp_segment_configuration " " WHERE registration_order = :1 " " FOR UPDATE ", Int32GetDatum(id))); tuple = caql_getnext(pcqCtx); if (tuple != NULL) { if (((Form_gp_segment_configuration)GETSTRUCT(tuple))->status != status) { ((Form_gp_segment_configuration)GETSTRUCT(tuple))->status = status; caql_update_current(pcqCtx, tuple); } } else { elog(LOG, "Can not find segment id: %d when update its status", id); } caql_endscan(pcqCtx); heap_close(rel, NoLock); }
/* * Add a master standby. * * gp_add_master_standby(hostname, address) * * Args: * hostname - as above * address - as above * * Returns: * dbid of the new standby */ Datum gp_add_master_standby(PG_FUNCTION_ARGS) { CdbComponentDatabaseInfo *master = NULL; Relation gprel; Datum values[Natts_gp_segment_configuration]; bool nulls[Natts_gp_segment_configuration]; HeapTuple tuple; cqContext cqc; cqContext *pcqCtx = NULL; if (PG_ARGISNULL(0)) elog(ERROR, "host name cannot be NULL"); if (PG_ARGISNULL(1)) elog(ERROR, "address cannot be NULL"); mirroring_sanity_check(MASTER_ONLY | UTILITY_MODE, "gp_add_master_standby"); if (standby_exists()) elog(ERROR, "only a single master standby may be defined"); /* master */ master = registration_order_get_dbinfo(MASTER_ORDER_ID); /* Lock exclusively to avoid concurrent changes */ gprel = heap_open(GpSegmentConfigRelationId, AccessExclusiveLock); pcqCtx = caql_beginscan( caql_addrel(cqclr(&cqc), gprel), cql("INSERT INTO gp_segment_configuration ", NULL)); MemSet(nulls, false, sizeof(nulls)); values[Anum_gp_segment_configuration_registration_order - 1] = Int32GetDatum(STANDBY_ORDER_ID); values[Anum_gp_segment_configuration_role - 1] = CharGetDatum(SEGMENT_ROLE_STANDBY_CONFIG); values[Anum_gp_segment_configuration_status - 1] = CharGetDatum('u'); values[Anum_gp_segment_configuration_port - 1] = Int32GetDatum(master->port); values[Anum_gp_segment_configuration_hostname - 1] = PG_GETARG_DATUM(0); values[Anum_gp_segment_configuration_address - 1] = PG_GETARG_DATUM(1); nulls[Anum_gp_segment_configuration_description - 1] = true; tuple = caql_form_tuple(pcqCtx, values, nulls); /* insert a new tuple */ caql_insert(pcqCtx, tuple); /* implicit update of index as well */ caql_endscan(pcqCtx); if(master) pfree(master); heap_close(gprel, NoLock); PG_RETURN_INT16(1); }
/* * Guts of rule deletion. */ void RemoveRewriteRuleById(Oid ruleOid) { cqContext *pcqCtx; Relation event_relation; HeapTuple tuple; Oid eventRelationOid; bool hasMoreRules; /* * Find the tuple for the target rule. */ pcqCtx = caql_beginscan( NULL, cql("SELECT * FROM pg_rewrite " " WHERE oid = :1 " " FOR UPDATE ", ObjectIdGetDatum(ruleOid))); tuple = caql_getnext(pcqCtx); if (!HeapTupleIsValid(tuple)) elog(ERROR, "could not find tuple for rule %u", ruleOid); /* * We had better grab AccessExclusiveLock so that we know no other rule * additions/deletions are going on for this relation. Else we cannot set * relhasrules correctly. Besides, we don't want to be changing the * ruleset while queries are executing on the rel. */ eventRelationOid = ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class; event_relation = heap_open(eventRelationOid, AccessExclusiveLock); hasMoreRules = event_relation->rd_rules != NULL && event_relation->rd_rules->numLocks > 1; /* * Now delete the pg_rewrite tuple for the rule */ caql_delete_current(pcqCtx); caql_endscan(pcqCtx); /* * Set pg_class 'relhasrules' field correctly for event relation. * * Important side effect: an SI notice is broadcast to force all backends * (including me!) to update relcache entries with the new rule set. * Therefore, must do this even if relhasrules is still true! */ SetRelationRuleStatus(eventRelationOid, hasMoreRules, false); /* Close rel, but keep lock till commit... */ heap_close(event_relation, NoLock); }
/* * IsErrorTable * * Returns true if relid is used as an error table, which has dependent object * that is an external table. Though it's not great we didn't have a clear * definition of Error Table, it satisfies the current requirements. */ bool IsErrorTable(Relation rel) { cqContext *pcqCtx, *pcqCtxExt, ctxExt; HeapTuple tup; Relation extrel; bool result = false; /* fast path to quick check */ if (!RelationIsHeap(rel)) return false; /* * Otherwise, go deeper and play with pg_depend... */ pcqCtx = caql_beginscan(NULL, cql("SELECT * FROM pg_depend " " WHERE refclassid = :1 " " AND refobjid = :2 " " AND refobjsubid = :3 ", ObjectIdGetDatum(RelationRelationId), ObjectIdGetDatum(RelationGetRelid(rel)), Int32GetDatum(0))); extrel = relation_open(ExtTableRelationId, AccessShareLock); pcqCtxExt = caql_addrel(cqclr(&ctxExt), extrel); while (HeapTupleIsValid(tup = caql_getnext(pcqCtx))) { Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(tup); Oid fmterrtbl; fmterrtbl = caql_getoid(pcqCtxExt, cql("SELECT fmterrtbl FROM pg_exttable " " WHERE reloid = :1", ObjectIdGetDatum(dep->objid))); if (RelationGetRelid(rel) == fmterrtbl) { result = true; break; } } relation_close(extrel, AccessShareLock); caql_endscan(pcqCtx); return result; }
/* * Get datum representations of the attoptions field in pg_attribute_encoding * for the given relation. */ Datum * get_rel_attoptions(Oid relid, AttrNumber max_attno) { Form_pg_attribute attform; HeapTuple tuple; cqContext cqc; cqContext *pcqCtx; Datum *dats; Relation pgae = heap_open(AttributeEncodingRelationId, AccessShareLock); /* used for attbyval and len below */ attform = pgae->rd_att->attrs[Anum_pg_attribute_encoding_attoptions - 1]; dats = palloc0(max_attno * sizeof(Datum)); pcqCtx = caql_beginscan( caql_addrel(cqclr(&cqc), pgae), cql("SELECT * FROM pg_attribute_encoding " " WHERE attrelid = :1 ", ObjectIdGetDatum(relid))); while (HeapTupleIsValid(tuple = caql_getnext(pcqCtx))) { Form_pg_attribute_encoding a = (Form_pg_attribute_encoding)GETSTRUCT(tuple); int16 attnum = a->attnum; Datum attoptions; bool isnull; Insist(attnum > 0 && attnum <= max_attno); attoptions = heap_getattr(tuple, Anum_pg_attribute_encoding_attoptions, RelationGetDescr(pgae), &isnull); Insist(!isnull); dats[attnum - 1] = datumCopy(attoptions, attform->attbyval, attform->attlen); } caql_endscan(pcqCtx); heap_close(pgae, AccessShareLock); return dats; }
/* * ProcessRoleGUC -- * We now process pg_authid.rolconfig separately from InitializeSessionUserId, * since it's too early to access toast table before initializing all * relcaches in phase3. */ static void ProcessRoleGUC(void) { cqContext *pcqCtx; Oid roleId; HeapTuple roleTup; Datum datum; bool isnull; /* This should have been set by now */ roleId = GetUserId(); Assert(OidIsValid(roleId)); pcqCtx = caql_beginscan( NULL, cql("SELECT * FROM pg_authid " " WHERE oid = :1", ObjectIdGetDatum(roleId))); roleTup = caql_getnext(pcqCtx); if (!HeapTupleIsValid(roleTup)) ereport(FATAL, (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), errmsg("role %u does not exist", roleId), errSendAlert(false))); /* * Set up user-specific configuration variables. This is a good place to * do it so we don't have to read pg_authid twice during session startup. */ datum = caql_getattr(pcqCtx, Anum_pg_authid_rolconfig, &isnull); if (!isnull) { ArrayType *a = DatumGetArrayTypeP(datum); /* * We process all the options at SUSET level. We assume that the * right to insert an option into pg_authid was checked when it was * inserted. */ ProcessGUCArray(a, PGC_SUSET, PGC_S_USER, GUC_ACTION_SET); } caql_endscan(pcqCtx); }
/* * Test whether given name is currently used as a constraint name * for the given object (relation or domain). * * This is used to decide whether to accept a user-specified constraint name. * It is deliberately not the same test as ChooseConstraintName uses to decide * whether an auto-generated name is OK: here, we will allow it unless there * is an identical constraint name in use *on the same object*. * * NB: Caller should hold exclusive lock on the given object, else * this test can be fooled by concurrent additions. */ bool ConstraintNameIsUsed(ConstraintCategory conCat, Oid objId, Oid objNamespace, const char *conname) { bool found; Relation conDesc; HeapTuple tup; cqContext *pcqCtx; cqContext cqc; conDesc = heap_open(ConstraintRelationId, AccessShareLock); found = false; pcqCtx = caql_beginscan( caql_addrel(cqclr(&cqc), conDesc), cql("SELECT * FROM pg_constraint " " WHERE conname = :1 " " AND connamespace = :2 ", CStringGetDatum((char *) conname), ObjectIdGetDatum(objNamespace))); while (HeapTupleIsValid(tup = caql_getnext(pcqCtx))) { Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup); if (conCat == CONSTRAINT_RELATION && con->conrelid == objId) { found = true; break; } else if (conCat == CONSTRAINT_DOMAIN && con->contypid == objId) { found = true; break; } } caql_endscan(pcqCtx); heap_close(conDesc, AccessShareLock); return found; }
/* * OperatorGet * * finds an operator given an exact specification (name, namespace, * left and right type IDs). * * *defined is set TRUE if defined (not a shell) */ static Oid OperatorGet(const char *operatorName, Oid operatorNamespace, Oid leftObjectId, Oid rightObjectId, bool *defined) { HeapTuple tup; Oid operatorObjectId; cqContext *pcqCtx; pcqCtx = caql_beginscan( NULL, cql("SELECT * FROM pg_operator " " WHERE oprname = :1 " " AND oprleft = :2 " " AND oprright = :3 " " AND oprnamespace = :4 ", CStringGetDatum((char *) operatorName), ObjectIdGetDatum(leftObjectId), ObjectIdGetDatum(rightObjectId), ObjectIdGetDatum(operatorNamespace))); tup = caql_getnext(pcqCtx); if (HeapTupleIsValid(tup)) { RegProcedure oprcode = ((Form_pg_operator) GETSTRUCT(tup))->oprcode; operatorObjectId = HeapTupleGetOid(tup); *defined = RegProcedureIsValid(oprcode); } else { operatorObjectId = InvalidOid; *defined = false; } caql_endscan(pcqCtx); return operatorObjectId; }
Datum caql_insert_to_in_memory_pg_class(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); char *tblname = text_to_cstring(PG_GETARG_TEXT_P(1)); Oid nspid = PG_GETARG_OID(2); Datum values[Natts_pg_class]; bool nulls[Natts_pg_class]; for (int i = 0; i < Natts_pg_class; i++) { nulls[i] = true; values[i] = (Datum) 0; } NameData name; namestrcpy(&name, tblname); values[Anum_pg_class_relname - 1] = NameGetDatum(&name); values[Anum_pg_class_relnamespace - 1] = ObjectIdGetDatum(nspid); nulls[Anum_pg_class_relname - 1] = false; nulls[Anum_pg_class_relnamespace - 1] = false; cqContext *pcqCtx = caql_beginscan( NULL, cql("INSERT INTO pg_class", NULL)); HeapTuple tup = caql_form_tuple(pcqCtx, values, nulls); HeapTupleSetOid(tup, relid); caql_insert_inmem(pcqCtx, tup); caql_endscan(pcqCtx); StringInfoData buf; initStringInfo(&buf); appendStringInfo(&buf, "inserted tuple to pg_class (oid %d, relname %s, relnamespace %d)", relid, tblname, nspid); PG_RETURN_TEXT_P(cstring_to_text(buf.data)); }
/* * Create a table space * * Only superusers can create a tablespace. This seems a reasonable restriction * since we're determining the system layout and, anyway, we probably have * root if we're doing this kind of activity */ void CreateTableSpace(CreateTableSpaceStmt *stmt) { Relation rel; Relation filespaceRel; Datum values[Natts_pg_tablespace]; bool nulls[Natts_pg_tablespace]; HeapTuple tuple; Oid tablespaceoid; Oid filespaceoid; Oid ownerId; TablespaceDirNode tablespaceDirNode; ItemPointerData persistentTid; int64 persistentSerialNum; cqContext cqc; cqContext *pcqCtx; /* Must be super user */ if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to create tablespace \"%s\"", stmt->tablespacename), errhint("Must be superuser to create a tablespace."))); /* However, the eventual owner of the tablespace need not be */ if (stmt->owner) ownerId = get_roleid_checked(stmt->owner); else ownerId = GetUserId(); /* * Disallow creation of tablespaces named "pg_xxx"; we reserve this * namespace for system purposes. */ if (!allowSystemTableModsDDL && IsReservedName(stmt->tablespacename)) { ereport(ERROR, (errcode(ERRCODE_RESERVED_NAME), errmsg("unacceptable tablespace name \"%s\"", stmt->tablespacename), errdetail("The prefix \"%s\" is reserved for system tablespaces.", GetReservedPrefix(stmt->tablespacename)))); } /* * Check the specified filespace */ filespaceRel = heap_open(FileSpaceRelationId, RowShareLock); filespaceoid = get_filespace_oid(filespaceRel, stmt->filespacename); heap_close(filespaceRel, NoLock); /* hold lock until commit/abort */ if (!OidIsValid(filespaceoid)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("filespace \"%s\" does not exist", stmt->filespacename))); /* * Filespace pg_system is reserved for system use: * - Used for pg_global and pg_default tablespaces only * * Directory layout is slightly different for the system filespace. * Instead of having subdirectories for individual tablespaces instead * the two system tablespaces have specific locations within it: * pg_global : $PG_SYSTEM/global/relfilenode * pg_default : $PG_SYSTEM/base/dboid/relfilenode * * In other words PG_SYSTEM points to the segments "datadir", or in * postgres vocabulary $PGDATA. * */ if (filespaceoid == SYSTEMFILESPACE_OID && !IsBootstrapProcessingMode()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to create tablespace \"%s\"", stmt->tablespacename), errhint("filespace %s is reserved for system use", stmt->filespacename))); /* * Check that there is no other tablespace by this name. (The unique * index would catch this anyway, but might as well give a friendlier * message.) */ if (OidIsValid(get_tablespace_oid(stmt->tablespacename))) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("tablespace \"%s\" already exists", stmt->tablespacename))); /* * Insert tuple into pg_tablespace. The purpose of doing this first is to * lock the proposed tablename against other would-be creators. The * insertion will roll back if we find problems below. */ rel = heap_open(TableSpaceRelationId, RowExclusiveLock); pcqCtx = caql_beginscan( caql_addrel(cqclr(&cqc), rel), cql("INSERT INTO pg_tablespace", NULL)); MemSet(nulls, true, sizeof(nulls)); values[Anum_pg_tablespace_spcname - 1] = DirectFunctionCall1(namein, CStringGetDatum(stmt->tablespacename)); values[Anum_pg_tablespace_spcowner - 1] = ObjectIdGetDatum(ownerId); values[Anum_pg_tablespace_spcfsoid - 1] = ObjectIdGetDatum(filespaceoid); nulls[Anum_pg_tablespace_spcname - 1] = false; nulls[Anum_pg_tablespace_spcowner - 1] = false; nulls[Anum_pg_tablespace_spcfsoid - 1] = false; tuple = caql_form_tuple(pcqCtx, values, nulls); /* Keep oids synchonized between master and segments */ if (OidIsValid(stmt->tsoid)) HeapTupleSetOid(tuple, stmt->tsoid); tablespaceoid = caql_insert(pcqCtx, tuple); /* and Update indexes (implicit) */ heap_freetuple(tuple); /* We keep the lock on pg_tablespace until commit */ caql_endscan(pcqCtx); heap_close(rel, NoLock); /* Create the persistent directory for the tablespace */ tablespaceDirNode.tablespace = tablespaceoid; tablespaceDirNode.filespace = filespaceoid; MirroredFileSysObj_TransactionCreateTablespaceDir( &tablespaceDirNode, &persistentTid, &persistentSerialNum); /* * Record dependency on owner * * We do not record the dependency on pg_filespace because we do not track * dependencies between shared objects. Additionally the pg_tablespace * table itself contains the foreign key back to pg_filespace and can be * used to fulfill the same purpose that an entry in pg_shdepend would. */ recordDependencyOnOwner(TableSpaceRelationId, tablespaceoid, ownerId); /* * Create the PG_VERSION file in the target directory. This has several * purposes: to make sure we can write in the directory, to prevent * someone from creating another tablespace pointing at the same directory * (the emptiness check above will fail), and to label tablespace * directories by PG version. */ // set_short_version(sublocation); if (Gp_role == GP_ROLE_DISPATCH) { stmt->tsoid = tablespaceoid; CdbDispatchUtilityStatement((Node *) stmt, DF_CANCEL_ON_ERROR| DF_WITH_SNAPSHOT| DF_NEED_TWO_PHASE, NULL); /* MPP-6929: metadata tracking */ MetaTrackAddObject(TableSpaceRelationId, tablespaceoid, GetUserId(), "CREATE", "TABLESPACE" ); } /* * Force synchronous commit, to minimize the window between creating the * symlink on-disk and marking the transaction committed. It's not great * that there is any window at all, but definitely we don't want to make * it larger than necessary. */ ForceSyncCommit(); }
/* * regclassout - converts class OID to "class_name" */ Datum regclassout(PG_FUNCTION_ARGS) { Oid classid = PG_GETARG_OID(0); char *result; HeapTuple classtup; cqContext *pcqCtx; if (classid == InvalidOid) { result = pstrdup("-"); PG_RETURN_CSTRING(result); } pcqCtx = caql_beginscan( NULL, cql("SELECT * FROM pg_class " " WHERE oid = :1 ", ObjectIdGetDatum(classid))); classtup = caql_getnext(pcqCtx); /* XXX XXX select relname, relnamespace from pg_class */ if (HeapTupleIsValid(classtup)) { Form_pg_class classform = (Form_pg_class) GETSTRUCT(classtup); char *classname = NameStr(classform->relname); /* * In bootstrap mode, skip the fancy namespace stuff and just return * the class name. (This path is only needed for debugging output * anyway.) */ if (IsBootstrapProcessingMode()) result = pstrdup(classname); else { char *nspname; /* * Would this class be found by regclassin? If not, qualify it. */ if (RelationIsVisible(classid)) nspname = NULL; else nspname = get_namespace_name(classform->relnamespace); result = quote_qualified_identifier(nspname, classname); } } else { /* If OID doesn't match any pg_class entry, return it numerically */ result = (char *) palloc(NAMEDATALEN); snprintf(result, NAMEDATALEN, "%u", classid); } caql_endscan(pcqCtx); PG_RETURN_CSTRING(result); }
/* * format_operator - converts operator OID to "opr_name(args)" * * This exports the useful functionality of regoperatorout for use * in other backend modules. The result is a palloc'd string. */ char * format_operator(Oid operator_oid) { char *result; HeapTuple opertup; cqContext *pcqCtx; pcqCtx = caql_beginscan( NULL, cql("SELECT * FROM pg_operator " " WHERE oid = :1 ", ObjectIdGetDatum(operator_oid))); opertup = caql_getnext(pcqCtx); /* XXX XXX select oprname, oprnamespace from pg_operator */ if (HeapTupleIsValid(opertup)) { Form_pg_operator operform = (Form_pg_operator) GETSTRUCT(opertup); char *oprname = NameStr(operform->oprname); char *nspname; StringInfoData buf; /* XXX no support here for bootstrap mode */ initStringInfo(&buf); /* * Would this oper be found (given the right args) by regoperatorin? * If not, we need to qualify it. */ if (!OperatorIsVisible(operator_oid)) { nspname = get_namespace_name(operform->oprnamespace); appendStringInfo(&buf, "%s.", quote_identifier(nspname)); } appendStringInfo(&buf, "%s(", oprname); if (operform->oprleft) appendStringInfo(&buf, "%s,", format_type_be(operform->oprleft)); else appendStringInfo(&buf, "NONE,"); if (operform->oprright) appendStringInfo(&buf, "%s)", format_type_be(operform->oprright)); else appendStringInfo(&buf, "NONE)"); result = buf.data; } else { /* * If OID doesn't match any pg_operator entry, return it numerically */ result = (char *) palloc(NAMEDATALEN); snprintf(result, NAMEDATALEN, "%u", operator_oid); } caql_endscan(pcqCtx); return result; }
/* * regoperout - converts operator OID to "opr_name" */ Datum regoperout(PG_FUNCTION_ARGS) { Oid oprid = PG_GETARG_OID(0); char *result; HeapTuple opertup; cqContext *pcqCtx; if (oprid == InvalidOid) { result = pstrdup("0"); PG_RETURN_CSTRING(result); } pcqCtx = caql_beginscan( NULL, cql("SELECT * FROM pg_operator " " WHERE oid = :1 ", ObjectIdGetDatum(oprid))); opertup = caql_getnext(pcqCtx); /* XXX XXX select oprname, oprnamespace from pg_operator */ if (HeapTupleIsValid(opertup)) { Form_pg_operator operform = (Form_pg_operator) GETSTRUCT(opertup); char *oprname = NameStr(operform->oprname); /* * In bootstrap mode, skip the fancy namespace stuff and just return * the oper name. (This path is only needed for debugging output * anyway.) */ if (IsBootstrapProcessingMode()) result = pstrdup(oprname); else { FuncCandidateList clist; /* * Would this oper be found (uniquely!) by regoperin? If not, * qualify it. */ clist = OpernameGetCandidates(list_make1(makeString(oprname)), '\0'); if (clist != NULL && clist->next == NULL && clist->oid == oprid) result = pstrdup(oprname); else { const char *nspname; nspname = get_namespace_name(operform->oprnamespace); nspname = quote_identifier(nspname); result = (char *) palloc(strlen(nspname) + strlen(oprname) + 2); sprintf(result, "%s.%s", nspname, oprname); } } } else { /* * If OID doesn't match any pg_operator entry, return it numerically */ result = (char *) palloc(NAMEDATALEN); snprintf(result, NAMEDATALEN, "%u", oprid); } caql_endscan(pcqCtx); PG_RETURN_CSTRING(result); }
/* * format_procedure - converts proc OID to "pro_name(args)" * * This exports the useful functionality of regprocedureout for use * in other backend modules. The result is a palloc'd string. */ char * format_procedure(Oid procedure_oid) { char *result; HeapTuple proctup; cqContext *pcqCtx; pcqCtx = caql_beginscan( NULL, cql("SELECT * FROM pg_proc " " WHERE oid = :1 ", ObjectIdGetDatum(procedure_oid))); proctup = caql_getnext(pcqCtx); /* XXX XXX select proname, pronamespace from pg_proc */ if (HeapTupleIsValid(proctup)) { Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(proctup); char *proname = NameStr(procform->proname); int nargs = procform->pronargs; int i; char *nspname; StringInfoData buf; /* XXX no support here for bootstrap mode */ initStringInfo(&buf); /* * Would this proc be found (given the right args) by regprocedurein? * If not, we need to qualify it. */ if (FunctionIsVisible(procedure_oid)) nspname = NULL; else nspname = get_namespace_name(procform->pronamespace); appendStringInfo(&buf, "%s(", quote_qualified_identifier(nspname, proname)); for (i = 0; i < nargs; i++) { Oid thisargtype = procform->proargtypes.values[i]; if (i > 0) appendStringInfoChar(&buf, ','); appendStringInfoString(&buf, format_type_be(thisargtype)); } appendStringInfoChar(&buf, ')'); result = buf.data; } else { /* If OID doesn't match any pg_proc entry, return it numerically */ result = (char *) palloc(NAMEDATALEN); snprintf(result, NAMEDATALEN, "%u", procedure_oid); } caql_endscan(pcqCtx); return result; }
/* * regprocout - converts proc OID to "pro_name" */ Datum regprocout(PG_FUNCTION_ARGS) { RegProcedure proid = PG_GETARG_OID(0); char *result; HeapTuple proctup; cqContext *pcqCtx; if (proid == InvalidOid) { result = pstrdup("-"); PG_RETURN_CSTRING(result); } pcqCtx = caql_beginscan( NULL, cql("SELECT * FROM pg_proc " " WHERE oid = :1 ", ObjectIdGetDatum(proid))); proctup = caql_getnext(pcqCtx); /* XXX XXX select proname, pronamespace from pg_proc */ if (HeapTupleIsValid(proctup)) { Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(proctup); char *proname = NameStr(procform->proname); /* * In bootstrap mode, skip the fancy namespace stuff and just return * the proc name. (This path is only needed for debugging output * anyway.) */ if (IsBootstrapProcessingMode()) result = pstrdup(proname); else { char *nspname; FuncCandidateList clist; /* * Would this proc be found (uniquely!) by regprocin? If not, * qualify it. */ clist = FuncnameGetCandidates(list_make1(makeString(proname)), -1); if (clist != NULL && clist->next == NULL && clist->oid == proid) nspname = NULL; else nspname = get_namespace_name(procform->pronamespace); result = quote_qualified_identifier(nspname, proname); } } else { /* If OID doesn't match any pg_proc entry, return it numerically */ result = (char *) palloc(NAMEDATALEN); snprintf(result, NAMEDATALEN, "%u", proid); } caql_endscan(pcqCtx); PG_RETURN_CSTRING(result); }
/* * RemoveRewriteRule * * Delete a rule given its name. */ void RemoveRewriteRule(Oid owningRel, const char *ruleName, DropBehavior behavior, bool missing_ok) { HeapTuple tuple; Oid eventRelationOid; ObjectAddress object; cqContext *pcqCtx; /* * Find the tuple for the target rule. */ pcqCtx = caql_beginscan( NULL, cql("SELECT * FROM pg_rewrite " " WHERE ev_class = :1 " " AND rulename = :2 " " FOR UPDATE ", ObjectIdGetDatum(owningRel), CStringGetDatum((char *) ruleName))); tuple = caql_getnext(pcqCtx); /* * complain if no rule with such name exists */ if (!HeapTupleIsValid(tuple)) { if (!missing_ok) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("rule \"%s\" for relation \"%s\" does not exist", ruleName, get_rel_name(owningRel)))); else ereport(NOTICE, (errmsg("rule \"%s\" for relation \"%s\" does not exist, skipping", ruleName, get_rel_name(owningRel)))); caql_endscan(pcqCtx); return; } /* * Verify user has appropriate permissions. */ eventRelationOid = ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class; Assert(eventRelationOid == owningRel); if (!pg_class_ownercheck(eventRelationOid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, get_rel_name(eventRelationOid)); /* * Do the deletion */ object.classId = RewriteRelationId; object.objectId = HeapTupleGetOid(tuple); object.objectSubId = 0; caql_endscan(pcqCtx); performDeletion(&object, behavior); }
/* MPP-6923: */ static List * AlterResqueueCapabilityEntry( List *stmtOptIdList, Oid queueid, ListCell *initcell, bool bCreate) { ListCell *lc; List *elems = NIL; List *dropelems = NIL; List *dupcheck = NIL; HeapTuple tuple; cqContext *pcqCtx; cqContext cqc; Relation rel = NULL; bool bWithout = false; TupleDesc tupdesc = NULL; #ifdef USE_ASSERT_CHECKING { DefElem *defel = (DefElem *) lfirst(initcell); Assert(0 == strcmp(defel->defname, "withliststart")); } #endif initcell = lnext(initcell); /* walk the original list and build a list of valid entries */ for_each_cell(lc, initcell) { DefElem *defel = (DefElem *) lfirst(lc); Oid resTypeOid = InvalidOid; int resTypeInt = 0; List *pentry = NIL; Value *pKeyVal = NULL; Value *pStrVal = NULL; if (!bWithout && (strcmp(defel->defname, "withoutliststart") == 0)) { bWithout = true; rel = heap_open(ResourceTypeRelationId, RowExclusiveLock); tupdesc = RelationGetDescr(rel); goto L_loop_cont; } /* ignore the basic threshold entries -- should already be processed */ if (strcmp(defel->defname, "active_statements") == 0) goto L_loop_cont; if (strcmp(defel->defname, "max_cost") == 0) goto L_loop_cont; if (strcmp(defel->defname, "cost_overcommit") == 0) goto L_loop_cont; if (strcmp(defel->defname, "min_cost") == 0) goto L_loop_cont; if (!GetResourceTypeByName(defel->defname, &resTypeInt, &resTypeOid)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("option \"%s\" is not a valid resource type", defel->defname))); pKeyVal = makeString(defel->defname); /* WITHOUT clause value determined in pg_resourcetype */ if (!bWithout) pStrVal = makeString(defGetString(defel)); else { pStrVal = NULL; /* if NULL, delete entry from * pg_resqueuecapability */ pcqCtx = caql_beginscan( caql_addrel(cqclr(&cqc), rel), cql("SELECT * FROM pg_resourcetype" " WHERE restypid = :1 FOR UPDATE", Int16GetDatum(resTypeInt))); while (HeapTupleIsValid(tuple = caql_getnext(pcqCtx))) { text *shutoff_text = NULL; char *shutoff_str = NULL; Datum shutoff_datum; bool isnull = false; Form_pg_resourcetype rtyp = (Form_pg_resourcetype)GETSTRUCT(tuple); if (!rtyp->reshasdisable) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("option \"%s\" cannot be disabled", defel->defname))); } /* required type must have a default value if it can * be disabled */ if (!rtyp->reshasdefault) { if (!rtyp->resrequired) /* optional resource without a default is * turned off by removing entry from * pg_resqueuecapability */ break; else { /* XXX XXX */ Assert(0); ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("required option \"%s\" cannot be disabled", defel->defname))); } } /* get the shutoff string */ shutoff_datum = heap_getattr(tuple, Anum_pg_resourcetype_resdisabledsetting, tupdesc, &isnull); Assert(!isnull); shutoff_text = DatumGetTextP(shutoff_datum); shutoff_str = DatumGetCString( DirectFunctionCall1( textout, PointerGetDatum(shutoff_text))); pStrVal = makeString(shutoff_str); break; } /* end while heaptuple is valid */ caql_endscan(pcqCtx); } /* check for duplicate key specifications */ if (list_member(dupcheck, pKeyVal)) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant option for \"%s\"", defel->defname))); dupcheck = lappend(dupcheck, pKeyVal); pentry = list_make2( makeInteger(resTypeInt), pStrVal); /* list of lists - (resource type, resource setting) */ if (bWithout) { /* if the "without" entry has an "off" value, then treat * it as a regular "with" item and update it in * pg_resqueuecapability, else remove its entry */ if (!pStrVal) dropelems = lappend(dropelems, pentry); else elems = lappend(elems, pentry); } else elems = lappend(elems, pentry); L_loop_cont: resTypeInt = 0; /* make compiler happy */ }
/* * TupleDescInitEntry * This function initializes a single attribute structure in * a previously allocated tuple descriptor. * * If attributeName is NULL, the attname field is set to an empty string * (this is for cases where we don't know or need a name for the field). * Also, some callers use this function to change the datatype-related fields * in an existing tupdesc; they pass attributeName = NameStr(att->attname) * to indicate that the attname field shouldn't be modified. */ void TupleDescInitEntry(TupleDesc desc, AttrNumber attributeNumber, const char *attributeName, Oid oidtypeid, int32 typmod, int attdim) { HeapTuple tuple; Form_pg_type typeForm; Form_pg_attribute att; cqContext *pcqCtx; /* * sanity checks */ AssertArg(PointerIsValid(desc)); AssertArg(attributeNumber >= 1); AssertArg(attributeNumber <= desc->natts); /* * initialize the attribute fields */ att = desc->attrs[attributeNumber - 1]; att->attrelid = 0; /* dummy value */ /* * Note: attributeName can be NULL, because the planner doesn't always * fill in valid resname values in targetlists, particularly for resjunk * attributes. Also, do nothing if caller wants to re-use the old attname. */ if (attributeName == NULL) MemSet(NameStr(att->attname), 0, NAMEDATALEN); else if (attributeName != NameStr(att->attname)) namestrcpy(&(att->attname), attributeName); att->attstattarget = -1; att->attcacheoff = -1; att->atttypmod = typmod; att->attnum = attributeNumber; att->attndims = attdim; att->attnotnull = false; att->atthasdef = false; att->attisdropped = false; att->attislocal = true; att->attinhcount = 0; pcqCtx = caql_beginscan( NULL, cql("SELECT * FROM pg_type " " WHERE oid = :1 ", ObjectIdGetDatum(oidtypeid))); tuple = caql_getnext(pcqCtx); if (!HeapTupleIsValid(tuple)) elog(ERROR, "cache lookup failed for type %u", oidtypeid); typeForm = (Form_pg_type) GETSTRUCT(tuple); att->atttypid = oidtypeid; att->attlen = typeForm->typlen; att->attbyval = typeForm->typbyval; att->attalign = typeForm->typalign; att->attstorage = typeForm->typstorage; caql_endscan(pcqCtx); }
/* * ExtProtocolCreateWithOid */ Oid ExtProtocolCreateWithOid(const char *protocolName, List *readfuncName, List *writefuncName, List *validatorfuncName, Oid protOid, bool trusted) { Relation rel; HeapTuple tup; bool nulls[Natts_pg_extprotocol]; Datum values[Natts_pg_extprotocol]; Oid readfn = InvalidOid; Oid writefn = InvalidOid; Oid validatorfn = InvalidOid; NameData prtname; int i; ObjectAddress myself, referenced; Oid ownerId = GetUserId(); cqContext cqc; cqContext cqc2; cqContext *pcqCtx; /* sanity checks (caller should have caught these) */ if (!protocolName) elog(ERROR, "no protocol name supplied"); if (!readfuncName && !writefuncName) elog(ERROR, "protocol must have at least one of readfunc or writefunc"); /* * Until we add system protocols to pg_extprotocol, make sure no * protocols with the same name are created. */ if (strcasecmp(protocolName, "file") == 0 || strcasecmp(protocolName, "http") == 0 || strcasecmp(protocolName, "gpfdist") == 0 || strcasecmp(protocolName, "gpfdists") == 0) { ereport(ERROR, (errcode(ERRCODE_RESERVED_NAME), errmsg("protocol \"%s\" already exists", protocolName), errhint("pick a different protocol name"))); } rel = heap_open(ExtprotocolRelationId, RowExclusiveLock); pcqCtx = caql_beginscan( caql_addrel(cqclr(&cqc), rel), cql("INSERT INTO pg_extprotocol", NULL)); /* make sure there is no existing protocol of same name */ if (caql_getcount( caql_addrel(cqclr(&cqc2), rel), cql("SELECT COUNT(*) FROM pg_extprotocol " " WHERE ptcname = :1 ", CStringGetDatum((char *) protocolName)))) { ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("protocol \"%s\" already exists", protocolName))); } /* * function checks: if supplied, check existence and correct signature in the catalog */ if (readfuncName) readfn = ValidateProtocolFunction(readfuncName, EXTPTC_FUNC_READER); if (writefuncName) writefn = ValidateProtocolFunction(writefuncName, EXTPTC_FUNC_WRITER); if (validatorfuncName) validatorfn = ValidateProtocolFunction(validatorfuncName, EXTPTC_FUNC_VALIDATOR); /* * Everything looks okay. Try to create the pg_extprotocol entry for the * protocol. (This could fail if there's already a conflicting entry.) */ /* initialize nulls and values */ for (i = 0; i < Natts_pg_extprotocol; i++) { nulls[i] = false; values[i] = (Datum) 0; } namestrcpy(&prtname, protocolName); values[Anum_pg_extprotocol_ptcname - 1] = NameGetDatum(&prtname); values[Anum_pg_extprotocol_ptcreadfn - 1] = ObjectIdGetDatum(readfn); values[Anum_pg_extprotocol_ptcwritefn - 1] = ObjectIdGetDatum(writefn); values[Anum_pg_extprotocol_ptcvalidatorfn - 1] = ObjectIdGetDatum(validatorfn); values[Anum_pg_extprotocol_ptcowner - 1] = ObjectIdGetDatum(ownerId); values[Anum_pg_extprotocol_ptctrusted - 1] = BoolGetDatum(trusted); nulls[Anum_pg_extprotocol_ptcacl - 1] = true; tup = caql_form_tuple(pcqCtx, values, nulls); if (protOid != (Oid) 0) HeapTupleSetOid(tup, protOid); /* insert a new tuple */ protOid = caql_insert(pcqCtx, tup); /* implicit update of index as well */ caql_endscan(pcqCtx); heap_close(rel, RowExclusiveLock); /* * Create dependencies for the protocol */ myself.classId = ExtprotocolRelationId; myself.objectId = protOid; myself.objectSubId = 0; /* Depends on read function, if any */ if (OidIsValid(readfn)) { referenced.classId = ProcedureRelationId; referenced.objectId = readfn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Depends on write function, if any */ if (OidIsValid(writefn)) { referenced.classId = ProcedureRelationId; referenced.objectId = writefn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* dependency on owner */ recordDependencyOnOwner(ExtprotocolRelationId, protOid, GetUserId()); return protOid; }
/* * InsertRule - * takes the arguments and inserts them as a row into the system * relation "pg_rewrite" */ static Oid InsertRule(char *rulname, int evtype, Oid eventrel_oid, AttrNumber evslot_index, bool evinstead, Node *event_qual, List *action, bool replace, Oid ruleOid) { char *evqual = nodeToString(event_qual); char *actiontree = nodeToString((Node *) action); int i; Datum values[Natts_pg_rewrite]; bool nulls[Natts_pg_rewrite]; bool replaces[Natts_pg_rewrite]; NameData rname; HeapTuple tup, oldtup; Oid rewriteObjectId; ObjectAddress myself, referenced; bool is_update = false; cqContext *pcqCtx; /* * Set up *nulls and *values arrays */ MemSet(nulls, false, sizeof(nulls)); i = 0; namestrcpy(&rname, rulname); values[i++] = NameGetDatum(&rname); /* rulename */ values[i++] = ObjectIdGetDatum(eventrel_oid); /* ev_class */ values[i++] = Int16GetDatum(evslot_index); /* ev_attr */ values[i++] = CharGetDatum(evtype + '0'); /* ev_type */ values[i++] = BoolGetDatum(evinstead); /* is_instead */ values[i++] = CStringGetTextDatum(evqual); /* ev_qual */ values[i++] = CStringGetTextDatum(actiontree); /* ev_action */ /* * Ready to store new pg_rewrite tuple */ /* * Check to see if we are replacing an existing tuple */ pcqCtx = caql_beginscan( NULL, cql("SELECT * FROM pg_rewrite " " WHERE ev_class = :1 " " AND rulename = :2 " " FOR UPDATE ", ObjectIdGetDatum(eventrel_oid), CStringGetDatum(rulname))); oldtup = caql_getnext(pcqCtx); if (HeapTupleIsValid(oldtup)) { if (!replace) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("rule \"%s\" for relation \"%s\" already exists", rulname, get_rel_name(eventrel_oid)))); /* * When replacing, we don't need to replace every attribute */ MemSet(replaces, false, sizeof(replaces)); replaces[Anum_pg_rewrite_ev_attr - 1] = true; replaces[Anum_pg_rewrite_ev_type - 1] = true; replaces[Anum_pg_rewrite_is_instead - 1] = true; replaces[Anum_pg_rewrite_ev_qual - 1] = true; replaces[Anum_pg_rewrite_ev_action - 1] = true; tup = caql_modify_current(pcqCtx, values, nulls, replaces); caql_update_current(pcqCtx, tup); /* and Update indexes (implicit) */ rewriteObjectId = HeapTupleGetOid(tup); is_update = true; } else { tup = caql_form_tuple(pcqCtx, values, nulls); if (OidIsValid(ruleOid)) HeapTupleSetOid(tup, ruleOid); rewriteObjectId = caql_insert(pcqCtx, tup); /* and Update indexes (implicit) */ } heap_freetuple(tup); /* If replacing, get rid of old dependencies and make new ones */ if (is_update) deleteDependencyRecordsFor(RewriteRelationId, rewriteObjectId); /* * Install dependency on rule's relation to ensure it will go away on * relation deletion. If the rule is ON SELECT, make the dependency * implicit --- this prevents deleting a view's SELECT rule. Other kinds * of rules can be AUTO. */ myself.classId = RewriteRelationId; myself.objectId = rewriteObjectId; myself.objectSubId = 0; referenced.classId = RelationRelationId; referenced.objectId = eventrel_oid; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, (evtype == CMD_SELECT) ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO); /* * Also install dependencies on objects referenced in action and qual. */ recordDependencyOnExpr(&myself, (Node *) action, NIL, DEPENDENCY_NORMAL); if (event_qual != NULL) { /* Find query containing OLD/NEW rtable entries */ Query *qry = (Query *) linitial(action); qry = getInsertSelectQuery(qry, NULL); recordDependencyOnExpr(&myself, event_qual, qry->rtable, DEPENDENCY_NORMAL); } caql_endscan(pcqCtx); return rewriteObjectId; }
/* * AggregateCreateWithOid */ Oid AggregateCreateWithOid(const char *aggName, Oid aggNamespace, Oid *aggArgTypes, int numArgs, List *aggtransfnName, List *aggprelimfnName, List *aggfinalfnName, List *aggsortopName, Oid aggTransType, const char *agginitval, bool aggordered, Oid procOid) { HeapTuple tup; bool nulls[Natts_pg_aggregate]; Datum values[Natts_pg_aggregate]; Form_pg_proc proc; Oid transfn; Oid invtransfn = InvalidOid; /* MPP windowing optimization */ Oid prelimfn = InvalidOid; /* if omitted, disables MPP 2-stage for this aggregate */ Oid invprelimfn = InvalidOid; /* MPP windowing optimization */ Oid finalfn = InvalidOid; /* can be omitted */ Oid sortop = InvalidOid; /* can be omitted */ bool hasPolyArg; bool hasInternalArg; Oid rettype; Oid finaltype; Oid prelimrettype; Oid *fnArgs; int nargs_transfn; int i; ObjectAddress myself, referenced; cqContext *pcqCtx; cqContext *pcqCtx2; /* sanity checks (caller should have caught these) */ if (!aggName) elog(ERROR, "no aggregate name supplied"); if (!aggtransfnName) elog(ERROR, "aggregate must have a transition function"); /* check for polymorphic arguments and INTERNAL arguments */ hasPolyArg = false; hasInternalArg = false; for (i = 0; i < numArgs; i++) { if (aggArgTypes[i] == ANYARRAYOID || aggArgTypes[i] == ANYELEMENTOID) hasPolyArg = true; else if (aggArgTypes[i] == INTERNALOID) hasInternalArg = true; } /* * If transtype is polymorphic, must have polymorphic argument also; else * we will have no way to deduce the actual transtype. */ if (!hasPolyArg && (aggTransType == ANYARRAYOID || aggTransType == ANYELEMENTOID)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot determine transition data type"), errdetail("An aggregate using \"anyarray\" or \"anyelement\" as transition type must have at least one argument of either type."))); /* find the transfn */ nargs_transfn = numArgs + 1; fnArgs = (Oid *) palloc(nargs_transfn * sizeof(Oid)); fnArgs[0] = aggTransType; memcpy(fnArgs + 1, aggArgTypes, numArgs * sizeof(Oid)); transfn = lookup_agg_function(aggtransfnName, nargs_transfn, fnArgs, &rettype); elog(DEBUG5,"AggregateCreateWithOid: successfully located transition " "function %s with return type %d", func_signature_string(aggtransfnName, nargs_transfn, fnArgs), rettype); /* * Return type of transfn (possibly after refinement by * enforce_generic_type_consistency, if transtype isn't polymorphic) must * exactly match declared transtype. * * In the non-polymorphic-transtype case, it might be okay to allow a * rettype that's binary-coercible to transtype, but I'm not quite * convinced that it's either safe or useful. When transtype is * polymorphic we *must* demand exact equality. */ if (rettype != aggTransType) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("return type of transition function %s is not %s", NameListToString(aggtransfnName), format_type_be(aggTransType)))); pcqCtx2 = caql_beginscan( NULL, cql("SELECT * FROM pg_proc " " WHERE oid = :1 ", ObjectIdGetDatum(transfn))); tup = caql_getnext(pcqCtx2); if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup failed for function %u", transfn); proc = (Form_pg_proc) GETSTRUCT(tup); /* * If the transfn is strict and the initval is NULL, make sure first input * type and transtype are the same (or at least binary-compatible), so * that it's OK to use the first input value as the initial transValue. */ if (proc->proisstrict && agginitval == NULL) { if (numArgs < 1 || !IsBinaryCoercible(aggArgTypes[0], aggTransType)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("must not omit initial value when transition function is strict and transition type is not compatible with input type"))); } caql_endscan(pcqCtx2); /* handle prelimfn, if supplied */ if (aggprelimfnName) { /* * The preliminary state function (pfunc) input arguments are the results of the * state transition function (sfunc) and therefore must be of the same types. */ fnArgs[0] = rettype; fnArgs[1] = rettype; /* * Check that such a function name and prototype exists in the catalog. */ prelimfn = lookup_agg_function(aggprelimfnName, 2, fnArgs, &prelimrettype); elog(DEBUG5,"AggregateCreateWithOid: successfully located preliminary " "function %s with return type %d", func_signature_string(aggprelimfnName, 2, fnArgs), prelimrettype); Assert(OidIsValid(prelimrettype)); /* * The preliminary return type must be of the same type as the internal * state. (See similar error checking for transition types above) */ if (prelimrettype != rettype) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("return type of preliminary function %s is not %s", NameListToString(aggprelimfnName), format_type_be(rettype)))); } /* handle finalfn, if supplied */ if (aggfinalfnName) { fnArgs[0] = aggTransType; finalfn = lookup_agg_function(aggfinalfnName, 1, fnArgs, &finaltype); } else { /* * If no finalfn, aggregate result type is type of the state value */ finaltype = aggTransType; } Assert(OidIsValid(finaltype)); /* * If finaltype (i.e. aggregate return type) is polymorphic, inputs must * be polymorphic also, else parser will fail to deduce result type. * (Note: given the previous test on transtype and inputs, this cannot * happen, unless someone has snuck a finalfn definition into the catalogs * that itself violates the rule against polymorphic result with no * polymorphic input.) */ if (!hasPolyArg && (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("cannot determine result data type"), errdetail("An aggregate returning \"anyarray\" or \"anyelement\" " "must have at least one argument of either type."))); /* * Also, the return type can't be INTERNAL unless there's at least one * INTERNAL argument. This is the same type-safety restriction we * enforce for regular functions, but at the level of aggregates. We * must test this explicitly because we allow INTERNAL as the transtype. */ if (finaltype == INTERNALOID && !hasInternalArg) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("unsafe use of pseudo-type \"internal\""), errdetail("A function returning \"internal\" must have at least one \"internal\" argument."))); /* handle sortop, if supplied */ if (aggsortopName) { if (numArgs != 1) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("sort operator can only be specified for single-argument aggregates"))); sortop = LookupOperName(NULL, aggsortopName, aggArgTypes[0], aggArgTypes[0], false, -1); } /* * Everything looks okay. Try to create the pg_proc entry for the * aggregate. (This could fail if there's already a conflicting entry.) */ procOid = ProcedureCreate(aggName, aggNamespace, false, /* no replacement */ false, /* doesn't return a set */ finaltype, /* returnType */ INTERNALlanguageId, /* languageObjectId */ InvalidOid, /* no validator */ InvalidOid, /* no describe function */ "aggregate_dummy", /* placeholder proc */ NULL, /* probin */ true, /* isAgg */ false, /* isWin */ false, /* security invoker (currently not * definable for agg) */ false, /* isStrict (not needed for agg) */ PROVOLATILE_IMMUTABLE, /* volatility (not * needed for agg) */ buildoidvector(aggArgTypes, numArgs), /* paramTypes */ PointerGetDatum(NULL), /* allParamTypes */ PointerGetDatum(NULL), /* parameterModes */ PointerGetDatum(NULL), /* parameterNames */ NIL, /* parameterDefaults */ 1, /* procost */ 0, /* prorows */ PRODATAACCESS_NONE, /* prodataaccess */ procOid); /* * Okay to create the pg_aggregate entry. */ /* initialize nulls and values */ for (i = 0; i < Natts_pg_aggregate; i++) { nulls[i] = false; values[i] = (Datum) 0; } values[Anum_pg_aggregate_aggfnoid - 1] = ObjectIdGetDatum(procOid); values[Anum_pg_aggregate_aggtransfn - 1] = ObjectIdGetDatum(transfn); values[Anum_pg_aggregate_agginvtransfn - 1] = ObjectIdGetDatum(invtransfn); values[Anum_pg_aggregate_aggprelimfn - 1] = ObjectIdGetDatum(prelimfn); values[Anum_pg_aggregate_agginvprelimfn - 1] = ObjectIdGetDatum(invprelimfn); values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(finalfn); values[Anum_pg_aggregate_aggsortop - 1] = ObjectIdGetDatum(sortop); values[Anum_pg_aggregate_aggtranstype - 1] = ObjectIdGetDatum(aggTransType); if (agginitval) values[Anum_pg_aggregate_agginitval - 1] = CStringGetTextDatum(agginitval); else nulls[Anum_pg_aggregate_agginitval - 1] = true; values[Anum_pg_aggregate_aggordered - 1] = BoolGetDatum(aggordered); pcqCtx = caql_beginscan( NULL, cql("INSERT INTO pg_aggregate", NULL)); tup = caql_form_tuple(pcqCtx, values, nulls); /* insert a new tuple */ caql_insert(pcqCtx, tup); /* implicit update of index as well */ caql_endscan(pcqCtx); /* * Create dependencies for the aggregate (above and beyond those already * made by ProcedureCreate). Note: we don't need an explicit dependency * on aggTransType since we depend on it indirectly through transfn. */ myself.classId = ProcedureRelationId; myself.objectId = procOid; myself.objectSubId = 0; /* Depends on transition function */ referenced.classId = ProcedureRelationId; referenced.objectId = transfn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* Depends on inverse transition function, if any */ if (OidIsValid(invtransfn)) { referenced.classId = ProcedureRelationId; referenced.objectId = invtransfn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Depends on preliminary aggregation function, if any */ if (OidIsValid(prelimfn)) { referenced.classId = ProcedureRelationId; referenced.objectId = prelimfn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Depends on inverse preliminary aggregation function, if any */ if (OidIsValid(invprelimfn)) { referenced.classId = ProcedureRelationId; referenced.objectId = invprelimfn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Depends on final function, if any */ if (OidIsValid(finalfn)) { referenced.classId = ProcedureRelationId; referenced.objectId = finalfn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Depends on sort operator, if any */ if (OidIsValid(sortop)) { referenced.classId = OperatorRelationId; referenced.objectId = sortop; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } return procOid; }
/* * Sets the policy of a table into the gp_distribution_policy table * from a GpPolicy structure. * */ void GpPolicyStore(Oid tbloid, const GpPolicy *policy) { Relation gp_policy_rel; HeapTuple gp_policy_tuple = NULL; ArrayType *attrnums; bool nulls[2]; Datum values[2]; cqContext cqc; cqContext *pcqCtx; Insist(policy->ptype == POLICYTYPE_PARTITIONED); /* * Open and lock the gp_distribution_policy catalog. */ gp_policy_rel = heap_open(GpPolicyRelationId, RowExclusiveLock); pcqCtx = caql_beginscan( caql_addrel(cqclr(&cqc), gp_policy_rel), cql("INSERT INTO gp_distribution_policy ", NULL)); /* * Convert C arrays into Postgres arrays. */ if (policy->nattrs > 0) { int i; Datum *akey; akey = (Datum *) palloc(policy->nattrs * sizeof(Datum)); for (i = 0; i < policy->nattrs; i++) akey[i] = Int16GetDatum(policy->attrs[i]); attrnums = construct_array(akey, policy->nattrs, INT2OID, 2, true, 's'); } else { attrnums = NULL; } nulls[0] = false; nulls[1] = false; values[0] = ObjectIdGetDatum(tbloid); if (attrnums) values[1] = PointerGetDatum(attrnums); else nulls[1] = true; gp_policy_tuple = caql_form_tuple(pcqCtx, values, nulls); /* Insert tuple into the relation */ caql_insert(pcqCtx, gp_policy_tuple); /* implicit update of index as well*/ /* * Close the gp_distribution_policy relcache entry without unlocking. * We have updated the catalog: consequently the lock must be held until * end of transaction. */ caql_endscan(pcqCtx); heap_close(gp_policy_rel, NoLock); } /* GpPolicyStore */
/* * shdepChangeDep * * Update shared dependency records to account for an updated referenced * object. This is an internal workhorse for operations such as changing * an object's owner. * * There must be no more than one existing entry for the given dependent * object and dependency type! So in practice this can only be used for * updating SHARED_DEPENDENCY_OWNER entries, which should have that property. * * If there is no previous entry, we assume it was referencing a PINned * object, so we create a new entry. If the new referenced object is * PINned, we don't create an entry (and drop the old one, if any). * * sdepRel must be the pg_shdepend relation, already opened and suitably * locked. */ static void shdepChangeDep(Relation sdepRel, Oid classid, Oid objid, Oid refclassid, Oid refobjid, SharedDependencyType deptype) { Oid dbid = classIdGetDbId(classid); bool bGotOne = false; HeapTuple oldtup = NULL; HeapTuple scantup; cqContext *pcqCtx; cqContext cqc; /* * Make sure the new referenced object doesn't go away while we record the * dependency. */ shdepLockAndCheckObject(refclassid, refobjid); /* * Look for a previous entry */ pcqCtx = caql_beginscan( caql_addrel(cqclr(&cqc), sdepRel), cql("SELECT * FROM pg_shdepend " " WHERE dbid = :1 " " AND classid = :2 " " AND objid = :3 " " FOR UPDATE ", ObjectIdGetDatum(dbid), ObjectIdGetDatum(classid), ObjectIdGetDatum(objid))); while (HeapTupleIsValid(scantup = caql_getnext(pcqCtx))) { /* Ignore if not of the target dependency type */ if (((Form_pg_shdepend) GETSTRUCT(scantup))->deptype != deptype) continue; /* Caller screwed up if multiple matches */ if (bGotOne) elog(ERROR, "multiple pg_shdepend entries for object %u/%u deptype %c", classid, objid, deptype); bGotOne = true; } caql_endscan(pcqCtx); /* XXX XXX XXX XXX XXX XXX XXX XXX XXX * Should match this logic: * * if isSharedObjectpinned * if Gotone then drop it * else * if Gotone * then update it * else insert it * * XXX XXX XXX XXX XXX XXX XXX XXX XXX */ if (!bGotOne) /* no match */ { /* if no match and pinned, new entry not needed */ if (isSharedObjectPinned(refclassid, refobjid, sdepRel)) { /* just return -- don't need to free anything because * sdelRel was passed in, and pcqCtx is freed */ return; } pcqCtx = caql_beginscan( caql_addrel(cqclr(&cqc), sdepRel), cql("INSERT INTO pg_shdepend ", NULL)); /* Need to insert new entry */ Datum values[Natts_pg_shdepend]; bool nulls[Natts_pg_shdepend]; memset(nulls, 0, sizeof(nulls)); values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(dbid); values[Anum_pg_shdepend_classid - 1] = ObjectIdGetDatum(classid); values[Anum_pg_shdepend_objid - 1] = ObjectIdGetDatum(objid); values[Anum_pg_shdepend_refclassid - 1] = ObjectIdGetDatum(refclassid); values[Anum_pg_shdepend_refobjid - 1] = ObjectIdGetDatum(refobjid); values[Anum_pg_shdepend_deptype - 1] = CharGetDatum(deptype); /* * we are reusing oldtup just to avoid declaring a new variable, but * it's certainly a new tuple */ oldtup = caql_form_tuple(pcqCtx, values, nulls); caql_insert(pcqCtx, oldtup); /* and Update indexes (implicit) */ heap_freetuple(oldtup); caql_endscan(pcqCtx); } else { /* XXX XXX Do the scan again, but do the update/delete this time */ pcqCtx = caql_beginscan( caql_addrel(cqclr(&cqc), sdepRel), cql("SELECT * FROM pg_shdepend " " WHERE dbid = :1 " " AND classid = :2 " " AND objid = :3 " " FOR UPDATE ", ObjectIdGetDatum(dbid), ObjectIdGetDatum(classid), ObjectIdGetDatum(objid))); while (HeapTupleIsValid(scantup = caql_getnext(pcqCtx))) { /* Ignore if not of the target dependency type */ if (((Form_pg_shdepend) GETSTRUCT(scantup))->deptype != deptype) continue; /* * NOTE: already tested for multiple matches - just use * first one */ if (isSharedObjectPinned(refclassid, refobjid, sdepRel)) { /* No new entry needed, so just delete existing entry if any */ caql_delete_current(pcqCtx); } else { oldtup = heap_copytuple(scantup); /* Need to update existing entry */ Form_pg_shdepend shForm = (Form_pg_shdepend) GETSTRUCT(oldtup); /* Since oldtup is a copy, we can just modify it in-memory */ shForm->refclassid = refclassid; shForm->refobjid = refobjid; caql_update_current(pcqCtx, oldtup); /* and Update indexes (implicit) */ heap_freetuple(oldtup); } break; } caql_endscan(pcqCtx); } }