/* * pg_get_sequencedef_string returns the definition of a given sequence. This * definition includes explicit values for all CREATE SEQUENCE options. */ char * pg_get_sequencedef_string(Oid sequenceRelationId) { char *qualifiedSequenceName = NULL; char *sequenceDef = NULL; Form_pg_sequence pgSequenceForm = NULL; Relation sequenceRel = NULL; AclResult permissionCheck = ACLCHECK_NO_PRIV; SysScanDesc scanDescriptor = NULL; HeapTuple heapTuple = NULL; /* open and lock sequence */ sequenceRel = heap_open(sequenceRelationId, AccessShareLock); /* check permissions to read sequence attributes */ permissionCheck = pg_class_aclcheck(sequenceRelationId, GetUserId(), ACL_SELECT | ACL_USAGE); if (permissionCheck != ACLCHECK_OK) { ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied for sequence %s", RelationGetRelationName(sequenceRel)))); } /* retrieve attributes from first tuple */ scanDescriptor = systable_beginscan(sequenceRel, InvalidOid, false, NULL, 0, NULL); heapTuple = systable_getnext(scanDescriptor); if (!HeapTupleIsValid(heapTuple)) { ereport(ERROR, (errmsg("could not find specified sequence"))); } pgSequenceForm = (Form_pg_sequence) GETSTRUCT(heapTuple); /* build our DDL command */ qualifiedSequenceName = generate_relation_name(sequenceRelationId, NIL); sequenceDef = psprintf(CREATE_SEQUENCE_COMMAND, qualifiedSequenceName, pgSequenceForm->increment_by, pgSequenceForm->min_value, pgSequenceForm->max_value, pgSequenceForm->cache_value, pgSequenceForm->is_cycled ? "" : "NO "); systable_endscan(scanDescriptor); heap_close(sequenceRel, AccessShareLock); return sequenceDef; }
/* * pg_get_tableschemadef_string returns the definition of a given table. This * definition includes table's schema, default column values, not null and check * constraints. The definition does not include constraints that trigger index * creations; specifically, unique and primary key constraints are excluded. */ static char * pg_shard_get_tableschemadef_string(Oid tableRelationId) { Relation relation = NULL; char *relationName = NULL; char relationKind = 0; TupleDesc tupleDescriptor = NULL; TupleConstr *tupleConstraints = NULL; int attributeIndex = 0; bool firstAttributePrinted = false; AttrNumber defaultValueIndex = 0; AttrNumber constraintIndex = 0; AttrNumber constraintCount = 0; StringInfoData buffer = { NULL, 0, 0, 0 }; /* * Instead of retrieving values from system catalogs as other functions in * ruleutils.c do, we follow an unusual approach here: we open the relation, * and fetch the relation's tuple descriptor. We do this because the tuple * descriptor already contains information harnessed from pg_attrdef, * pg_attribute, pg_constraint, and pg_class; and therefore using the * descriptor saves us from a lot of additional work. */ relation = relation_open(tableRelationId, AccessShareLock); relationName = generate_relation_name(tableRelationId); relationKind = relation->rd_rel->relkind; if (relationKind != RELKIND_RELATION && relationKind != RELKIND_FOREIGN_TABLE) { ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("%s is not a regular or foreign table", relationName))); } initStringInfo(&buffer); if (relationKind == RELKIND_RELATION) { appendStringInfo(&buffer, "CREATE TABLE %s (", relationName); } else { appendStringInfo(&buffer, "CREATE FOREIGN TABLE %s (", relationName); } /* * Iterate over the table's columns. If a particular column is not dropped * and is not inherited from another table, print the column's name and its * formatted type. */ tupleDescriptor = RelationGetDescr(relation); tupleConstraints = tupleDescriptor->constr; for (attributeIndex = 0; attributeIndex < tupleDescriptor->natts; attributeIndex++) { Form_pg_attribute attributeForm = tupleDescriptor->attrs[attributeIndex]; if (!attributeForm->attisdropped && attributeForm->attinhcount == 0) { const char *attributeName = NULL; const char *attributeTypeName = NULL; if (firstAttributePrinted) { appendStringInfoString(&buffer, ", "); } firstAttributePrinted = true; attributeName = NameStr(attributeForm->attname); appendStringInfo(&buffer, "%s ", quote_identifier(attributeName)); attributeTypeName = format_type_with_typemod(attributeForm->atttypid, attributeForm->atttypmod); appendStringInfoString(&buffer, attributeTypeName); /* if this column has a default value, append the default value */ if (attributeForm->atthasdef) { AttrDefault *defaultValueList = NULL; AttrDefault *defaultValue = NULL; Node *defaultNode = NULL; List *defaultContext = NULL; char *defaultString = NULL; Assert(tupleConstraints != NULL); defaultValueList = tupleConstraints->defval; Assert(defaultValueList != NULL); defaultValue = &(defaultValueList[defaultValueIndex]); defaultValueIndex++; Assert(defaultValue->adnum == (attributeIndex + 1)); Assert(defaultValueIndex <= tupleConstraints->num_defval); /* convert expression to node tree, and prepare deparse context */ defaultNode = (Node *) stringToNode(defaultValue->adbin); defaultContext = deparse_context_for(relationName, tableRelationId); /* deparse default value string */ defaultString = deparse_expression(defaultNode, defaultContext, false, false); appendStringInfo(&buffer, " DEFAULT %s", defaultString); } /* if this column has a not null constraint, append the constraint */ if (attributeForm->attnotnull) { appendStringInfoString(&buffer, " NOT NULL"); } } } /* * Now check if the table has any constraints. If it does, set the number of * check constraints here. Then iterate over all check constraints and print * them. */ if (tupleConstraints != NULL) { constraintCount = tupleConstraints->num_check; } for (constraintIndex = 0; constraintIndex < constraintCount; constraintIndex++) { ConstrCheck *checkConstraintList = tupleConstraints->check; ConstrCheck *checkConstraint = &(checkConstraintList[constraintIndex]); Node *checkNode = NULL; List *checkContext = NULL; char *checkString = NULL; /* if an attribute or constraint has been printed, format properly */ if (firstAttributePrinted || constraintIndex > 0) { appendStringInfoString(&buffer, ", "); } appendStringInfo(&buffer, "CONSTRAINT %s CHECK ", quote_identifier(checkConstraint->ccname)); /* convert expression to node tree, and prepare deparse context */ checkNode = (Node *) stringToNode(checkConstraint->ccbin); checkContext = deparse_context_for(relationName, tableRelationId); /* deparse check constraint string */ checkString = deparse_expression(checkNode, checkContext, false, false); appendStringInfoString(&buffer, checkString); } /* close create table's outer parentheses */ appendStringInfoString(&buffer, ")"); /* * If the relation is a foreign table, append the server name and options to * the create table statement. */ if (relationKind == RELKIND_FOREIGN_TABLE) { ForeignTable *foreignTable = GetForeignTable(tableRelationId); ForeignServer *foreignServer = GetForeignServer(foreignTable->serverid); char *serverName = foreignServer->servername; appendStringInfo(&buffer, " SERVER %s", quote_identifier(serverName)); AppendOptionListToString(&buffer, foreignTable->options); } relation_close(relation, AccessShareLock); return (buffer.data); }
/* * pg_get_tablecolumnoptionsdef_string returns column storage type and column * statistics definitions for given table, _if_ these definitions differ from * their default values. The function returns null if all columns use default * values for their storage types and statistics. */ char * pg_get_tablecolumnoptionsdef_string(Oid tableRelationId) { Relation relation = NULL; char *relationName = NULL; char relationKind = 0; TupleDesc tupleDescriptor = NULL; AttrNumber attributeIndex = 0; List *columnOptionList = NIL; ListCell *columnOptionCell = NULL; bool firstOptionPrinted = false; StringInfoData buffer = { NULL, 0, 0, 0 }; /* * Instead of retrieving values from system catalogs, we open the relation, * and use the relation's tuple descriptor to access attribute information. * This is primarily to maintain symmetry with pg_get_tableschemadef. */ relation = relation_open(tableRelationId, AccessShareLock); relationName = generate_relation_name(tableRelationId, NIL); relationKind = relation->rd_rel->relkind; if (relationKind != RELKIND_RELATION && relationKind != RELKIND_FOREIGN_TABLE) { ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("%s is not a regular or foreign table", relationName))); } /* * Iterate over the table's columns. If a particular column is not dropped * and is not inherited from another table, check if column storage or * statistics statements need to be printed. */ tupleDescriptor = RelationGetDescr(relation); for (attributeIndex = 0; attributeIndex < tupleDescriptor->natts; attributeIndex++) { Form_pg_attribute attributeForm = tupleDescriptor->attrs[attributeIndex]; char *attributeName = NameStr(attributeForm->attname); char defaultStorageType = get_typstorage(attributeForm->atttypid); if (!attributeForm->attisdropped && attributeForm->attinhcount == 0) { /* * If the user changed the column's default storage type, create * alter statement and add statement to a list for later processing. */ if (attributeForm->attstorage != defaultStorageType) { char *storageName = 0; StringInfoData statement = { NULL, 0, 0, 0 }; initStringInfo(&statement); switch (attributeForm->attstorage) { case 'p': { storageName = "PLAIN"; break; } case 'e': { storageName = "EXTERNAL"; break; } case 'm': { storageName = "MAIN"; break; } case 'x': { storageName = "EXTENDED"; break; } default: { ereport(ERROR, (errmsg("unrecognized storage type: %c", attributeForm->attstorage))); break; } } appendStringInfo(&statement, "ALTER COLUMN %s ", quote_identifier(attributeName)); appendStringInfo(&statement, "SET STORAGE %s", storageName); columnOptionList = lappend(columnOptionList, statement.data); } /* * If the user changed the column's statistics target, create * alter statement and add statement to a list for later processing. */ if (attributeForm->attstattarget >= 0) { StringInfoData statement = { NULL, 0, 0, 0 }; initStringInfo(&statement); appendStringInfo(&statement, "ALTER COLUMN %s ", quote_identifier(attributeName)); appendStringInfo(&statement, "SET STATISTICS %d", attributeForm->attstattarget); columnOptionList = lappend(columnOptionList, statement.data); } } } /* * Iterate over column storage and statistics statements that we created, * and append them to a single alter table statement. */ foreach(columnOptionCell, columnOptionList) { char *columnOptionStatement = NULL; if (!firstOptionPrinted) { initStringInfo(&buffer); appendStringInfo(&buffer, "ALTER TABLE ONLY %s ", generate_relation_name(tableRelationId, NIL)); } else { appendStringInfoString(&buffer, ", "); } firstOptionPrinted = true; columnOptionStatement = (char *) lfirst(columnOptionCell); appendStringInfoString(&buffer, columnOptionStatement); pfree(columnOptionStatement); }