/* * setNewRelfilenode - assign a new relfilenode value to the relation * * Caller must already hold exclusive lock on the relation. */ void setNewRelfilenode(Relation relation) { Oid newrelfilenode; Relation pg_class; HeapTuple tuple; Form_pg_class rd_rel; RelationData workrel; /* Can't change relfilenode for nailed tables (indexes ok though) */ Assert(!relation->rd_isnailed || relation->rd_rel->relkind == RELKIND_INDEX); /* Can't change for shared tables or indexes */ Assert(!relation->rd_rel->relisshared); /* Allocate a new relfilenode */ newrelfilenode = newoid(); /* * Find the pg_class tuple for the given relation. This is not used * during bootstrap, so okay to use heap_update always. */ pg_class = heap_openr(RelationRelationName, RowExclusiveLock); tuple = SearchSysCacheCopy(RELOID, ObjectIdGetDatum(RelationGetRelid(relation)), 0, 0, 0); if (!HeapTupleIsValid(tuple)) elog(ERROR, "could not find tuple for relation %u", RelationGetRelid(relation)); rd_rel = (Form_pg_class) GETSTRUCT(tuple); /* create another storage file. Is it a little ugly ? */ /* NOTE: any conflict in relfilenode value will be caught here */ memcpy((char *) &workrel, relation, sizeof(RelationData)); workrel.rd_fd = -1; workrel.rd_node.relNode = newrelfilenode; heap_storage_create(&workrel); smgrclose(DEFAULT_SMGR, &workrel); /* schedule unlinking old relfilenode */ smgrunlink(DEFAULT_SMGR, relation); /* update the pg_class row */ rd_rel->relfilenode = newrelfilenode; simple_heap_update(pg_class, &tuple->t_self, tuple); CatalogUpdateIndexes(pg_class, tuple); heap_freetuple(tuple); heap_close(pg_class, RowExclusiveLock); /* Make sure the relfilenode change is visible */ CommandCounterIncrement(); }
/* * inv_create -- create a new large object. * * Arguments: * flags * * Returns: * large object descriptor, appropriately filled in. */ LargeObjectDesc * inv_create(int flags) { Oid file_oid; LargeObjectDesc *retval; /* * Allocate an OID to be the LO's identifier. */ file_oid = newoid(); /* Check for duplicate (shouldn't happen) */ if (LargeObjectExists(file_oid)) elog(ERROR, "large object %u already exists", file_oid); /* * Create the LO by writing an empty first page for it in * pg_largeobject */ LargeObjectCreate(file_oid); /* * Advance command counter so that new tuple will be seen by later * large-object operations in this transaction. */ CommandCounterIncrement(); /* * Prepare LargeObjectDesc data structure for accessing LO */ retval = (LargeObjectDesc *) palloc(sizeof(LargeObjectDesc)); retval->id = file_oid; retval->subid = GetCurrentSubTransactionId(); retval->offset = 0; if (flags & INV_WRITE) retval->flags = IFS_WRLOCK | IFS_RDLOCK; else if (flags & INV_READ) retval->flags = IFS_RDLOCK; else elog(ERROR, "invalid flags: %d", flags); return retval; }
/* ---------- * toast_save_datum - * * Save one single datum into the secondary relation and return * a varattrib reference for it. * ---------- */ static Datum toast_save_datum(Relation rel, Datum value) { Relation toastrel; Relation toastidx; HeapTuple toasttup; InsertIndexResult idxres; TupleDesc toasttupDesc; Datum t_values[3]; char t_nulls[3]; varattrib *result; struct { struct varlena hdr; char data[TOAST_MAX_CHUNK_SIZE]; } chunk_data; int32 chunk_size; int32 chunk_seq = 0; char *data_p; int32 data_todo; /* * Create the varattrib reference */ result = (varattrib *) palloc(sizeof(varattrib)); result->va_header = sizeof(varattrib) | VARATT_FLAG_EXTERNAL; if (VARATT_IS_COMPRESSED(value)) { result->va_header |= VARATT_FLAG_COMPRESSED; result->va_content.va_external.va_rawsize = ((varattrib *) value)->va_content.va_compressed.va_rawsize; } else result->va_content.va_external.va_rawsize = VARATT_SIZE(value); result->va_content.va_external.va_extsize = VARATT_SIZE(value) - VARHDRSZ; result->va_content.va_external.va_valueid = newoid(); result->va_content.va_external.va_toastrelid = rel->rd_rel->reltoastrelid; /* * Initialize constant parts of the tuple data */ t_values[0] = ObjectIdGetDatum(result->va_content.va_external.va_valueid); t_values[2] = PointerGetDatum(&chunk_data); t_nulls[0] = ' '; t_nulls[1] = ' '; t_nulls[2] = ' '; /* * Get the data to process */ data_p = VARATT_DATA(value); data_todo = VARATT_SIZE(value) - VARHDRSZ; /* * Open the toast relation */ toastrel = heap_open(rel->rd_rel->reltoastrelid, RowExclusiveLock); toasttupDesc = toastrel->rd_att; toastidx = index_open(toastrel->rd_rel->reltoastidxid); /* * Split up the item into chunks */ while (data_todo > 0) { /* * Calculate the size of this chunk */ chunk_size = Min(TOAST_MAX_CHUNK_SIZE, data_todo); /* * Build a tuple and store it */ t_values[1] = Int32GetDatum(chunk_seq++); VARATT_SIZEP(&chunk_data) = chunk_size + VARHDRSZ; memcpy(VARATT_DATA(&chunk_data), data_p, chunk_size); toasttup = heap_formtuple(toasttupDesc, t_values, t_nulls); if (!HeapTupleIsValid(toasttup)) elog(ERROR, "failed to build TOAST tuple"); simple_heap_insert(toastrel, toasttup); /* * Create the index entry. We cheat a little here by not using * FormIndexDatum: this relies on the knowledge that the index * columns are the same as the initial columns of the table. * * Note also that there had better not be any user-created index on * the TOAST table, since we don't bother to update anything else. */ idxres = index_insert(toastidx, t_values, t_nulls, &(toasttup->t_self), toastrel, toastidx->rd_index->indisunique); if (idxres == NULL) elog(ERROR, "failed to insert index entry for TOAST tuple"); /* * Free memory */ pfree(idxres); heap_freetuple(toasttup); /* * Move on to next chunk */ data_todo -= chunk_size; data_p += chunk_size; } /* * Done - close toast relation and return the reference */ index_close(toastidx); heap_close(toastrel, RowExclusiveLock); return PointerGetDatum(result); }