/** * @brief Internal function for retrieving if the type is composite and, if * yes, the PostgreSQL TupleDesc * * @param inTargetTypeID PostgreSQL OID for the type * @param[in,out] ioTargetIsComposite On input, whether the type is composite. * \c indeterminate if unknown. On return, the updated boolean value. * @param[out] outTupleHandle TupleHandle that will be updated if type is * composite * * @internal * Having this as separate function isolates the PG_TRY block. Otherwise, * the compiler might warn that the longjmp could clobber local variables. */ inline void AbstractionLayer::AnyType::backendGetIsCompositeTypeAndTupleHandle( Oid inTargetTypeID, boost::tribool &ioTargetIsComposite, TupleHandle &outTupleHandle) const { bool exceptionOccurred = false; PG_TRY(); { if (boost::indeterminate(ioTargetIsComposite)) ioTargetIsComposite = isRowTypeInCache(inTargetTypeID, type_is_rowtype(inTargetTypeID)); if (ioTargetIsComposite) { // Don't ereport errors. We set typmod < 0, and this should // not cause an error because compound types in another // compound can never be transient. (I think) outTupleHandle.desc = lookup_rowtype_tupdesc_noerror( inTargetTypeID, -1, true); outTupleHandle.shouldReleaseDesc = true; } } PG_CATCH(); { exceptionOccurred = true; } PG_END_TRY(); if (exceptionOccurred) throw PGException(); }
static jobject coerceTupleDatum(UDT udt, Datum arg) { jobject result = JNI_newObject(Type_getJavaClass((Type)udt), udt->init); Oid typeId = ((Type)udt)->typeId; TupleDesc tupleDesc = lookup_rowtype_tupdesc_noerror(typeId, -1, true); jobject inputStream = SQLInputFromTuple_create(DatumGetHeapTupleHeader(arg), tupleDesc); ReleaseTupleDesc(tupleDesc); JNI_callVoidMethod(result, udt->readSQL, inputStream, udt->sqlTypeName); JNI_deleteLocalRef(inputStream); return result; }
static Datum coerceTupleObject(UDT self, jobject value) { Datum result = 0; if(value != 0) { HeapTuple tuple; Oid typeId = ((Type)self)->typeId; TupleDesc tupleDesc = lookup_rowtype_tupdesc_noerror(typeId, -1, true); jobject sqlOutput = SQLOutputToTuple_create(tupleDesc); ReleaseTupleDesc(tupleDesc); JNI_callVoidMethod(value, self->writeSQL, sqlOutput); tuple = SQLOutputToTuple_getTuple(sqlOutput); if(tuple != 0) result = HeapTupleGetDatum(tuple); } return result; }
/* ---------- * toast_flatten_tuple_attribute - * * If a Datum is of composite type, "flatten" it to contain no toasted fields. * This must be invoked on any potentially-composite field that is to be * inserted into a tuple. Doing this preserves the invariant that toasting * goes only one level deep in a tuple. * * Note that flattening does not mean expansion of short-header varlenas, * so in one sense toasting is allowed within composite datums. * ---------- */ Datum toast_flatten_tuple_attribute(Datum value, Oid typeId, int32 typeMod) { TupleDesc tupleDesc; HeapTupleHeader olddata; HeapTupleHeader new_data; int32 new_len; int32 new_data_len; HeapTupleData tmptup; Form_pg_attribute *att; int numAttrs; int i; bool need_change = false; bool has_nulls = false; Datum toast_values[MaxTupleAttributeNumber]; bool toast_isnull[MaxTupleAttributeNumber]; bool toast_free[MaxTupleAttributeNumber]; /* * See if it's a composite type, and get the tupdesc if so. */ tupleDesc = lookup_rowtype_tupdesc_noerror(typeId, typeMod, true); if (tupleDesc == NULL) return value; /* not a composite type */ att = tupleDesc->attrs; numAttrs = tupleDesc->natts; /* * Break down the tuple into fields. */ olddata = DatumGetHeapTupleHeader(value); Assert(typeId == HeapTupleHeaderGetTypeId(olddata)); Assert(typeMod == HeapTupleHeaderGetTypMod(olddata)); /* Build a temporary HeapTuple control structure */ tmptup.t_len = HeapTupleHeaderGetDatumLength(olddata); ItemPointerSetInvalid(&(tmptup.t_self)); tmptup.t_tableOid = InvalidOid; tmptup.t_data = olddata; Assert(numAttrs <= MaxTupleAttributeNumber); heap_deform_tuple(&tmptup, tupleDesc, toast_values, toast_isnull); memset(toast_free, 0, numAttrs * sizeof(bool)); for (i = 0; i < numAttrs; i++) { /* * Look at non-null varlena attributes */ if (toast_isnull[i]) has_nulls = true; else if (att[i]->attlen == -1) { struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(toast_values[i]); if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { new_value = heap_tuple_untoast_attr(new_value); toast_values[i] = PointerGetDatum(new_value); toast_free[i] = true; need_change = true; } } } /* * If nothing to untoast, just return the original tuple. */ if (!need_change) { ReleaseTupleDesc(tupleDesc); return value; } /* * Calculate the new size of the tuple. Header size should not change, * but data size might. */ new_len = offsetof(HeapTupleHeaderData, t_bits); if (has_nulls) new_len += BITMAPLEN(numAttrs); if (olddata->t_infomask & HEAP_HASOID) new_len += sizeof(Oid); new_len = MAXALIGN(new_len); Assert(new_len == olddata->t_hoff); new_data_len = heap_compute_data_size(tupleDesc, toast_values, toast_isnull); new_len += new_data_len; new_data = (HeapTupleHeader) palloc0(new_len); /* * Put the tuple header and the changed values into place */ memcpy(new_data, olddata, olddata->t_hoff); HeapTupleHeaderSetDatumLength(new_data, new_len); heap_fill_tuple(tupleDesc, toast_values, toast_isnull, (char *) new_data + olddata->t_hoff, new_data_len, &(new_data->t_infomask), has_nulls ? new_data->t_bits : NULL); /* * Free allocated temp values */ for (i = 0; i < numAttrs; i++) if (toast_free[i]) pfree(DatumGetPointer(toast_values[i])); ReleaseTupleDesc(tupleDesc); return PointerGetDatum(new_data); }
/* * Assign a tuple descriptor to variable specified by dno */ void plpgsql_check_assign_tupdesc_dno(PLpgSQL_checkstate *cstate, int varno, TupleDesc tupdesc, bool isnull) { PLpgSQL_datum *target = cstate->estate->datums[varno]; switch (target->dtype) { case PLPGSQL_DTYPE_VAR: { PLpgSQL_var *var = (PLpgSQL_var *) target; plpgsql_check_assign_to_target_type(cstate, var->datatype->typoid, var->datatype->atttypmod, TupleDescAttr(tupdesc, 0)->atttypid, isnull); } break; case PLPGSQL_DTYPE_ROW: plpgsql_check_assign_tupdesc_row_or_rec(cstate, (PLpgSQL_row *) target, NULL, tupdesc, isnull); break; case PLPGSQL_DTYPE_REC: plpgsql_check_assign_tupdesc_row_or_rec(cstate, NULL, (PLpgSQL_rec *) target, tupdesc, isnull); break; case PLPGSQL_DTYPE_RECFIELD: { Oid typoid; int typmod; plpgsql_check_target(cstate, varno, &typoid, &typmod); plpgsql_check_assign_to_target_type(cstate, typoid, typmod, TupleDescAttr(tupdesc, 0)->atttypid, isnull); } break; case PLPGSQL_DTYPE_ARRAYELEM: { Oid expected_typoid; int expected_typmod; plpgsql_check_target(cstate, varno, &expected_typoid, &expected_typmod); /* When target is composite type, then source is expanded already */ if (type_is_rowtype(expected_typoid)) { PLpgSQL_rec rec; plpgsql_check_recval_init(&rec); PG_TRY(); { plpgsql_check_recval_assign_tupdesc(cstate, &rec, lookup_rowtype_tupdesc_noerror(expected_typoid, expected_typmod, true), isnull); plpgsql_check_assign_tupdesc_row_or_rec(cstate, NULL, &rec, tupdesc, isnull); plpgsql_check_recval_release(&rec); } PG_CATCH(); { plpgsql_check_recval_release(&rec); PG_RE_THROW(); } PG_END_TRY(); } else plpgsql_check_assign_to_target_type(cstate, expected_typoid, expected_typmod, TupleDescAttr(tupdesc, 0)->atttypid, isnull); } break; default: ; /* nope */ } }
Type Type_fromOid(Oid typeId, jobject typeMap) { CacheEntry ce; HeapTuple typeTup; Form_pg_type typeStruct; Type type = Type_fromOidCache(typeId); if(type != 0) return type; typeTup = PgObject_getValidTuple(TYPEOID, typeId, "type"); typeStruct = (Form_pg_type)GETSTRUCT(typeTup); if(typeStruct->typelem != 0 && typeStruct->typlen == -1) { type = Type_getArrayType(Type_fromOid(typeStruct->typelem, typeMap), typeId); goto finally; } /* For some reason, the anyarray is *not* an array with anyelement as the * element type. We'd like to see it that way though. */ if(typeId == ANYARRAYOID) { type = Type_getArrayType(Type_fromOid(ANYELEMENTOID, typeMap), typeId); goto finally; } if(typeStruct->typbasetype != 0) { /* Domain type, recurse using the base type (which in turn may * also be a domain) */ type = Type_fromOid(typeStruct->typbasetype, typeMap); goto finally; } if(typeMap != 0) { jobject joid = Oid_create(typeId); jclass typeClass = (jclass)JNI_callObjectMethod(typeMap, s_Map_get, joid); JNI_deleteLocalRef(joid); if(typeClass != 0) { TupleDesc tupleDesc = lookup_rowtype_tupdesc_noerror(typeId, -1, true); type = (Type)UDT_registerUDT(typeClass, typeId, typeStruct, tupleDesc, false); JNI_deleteLocalRef(typeClass); goto finally; } } /* Composite and record types will not have a TypeObtainer registered */ if(typeStruct->typtype == 'c' || (typeStruct->typtype == 'p' && typeId == RECORDOID)) { type = Composite_obtain(typeId); goto finally; } ce = (CacheEntry)HashMap_getByOid(s_obtainerByOid, typeId); if(ce == 0) /* * Default to String and standard textin/textout coersion. */ type = String_obtain(typeId); else { type = ce->type; if(type == 0) type = ce->obtainer(typeId); } finally: ReleaseSysCache(typeTup); Type_cacheByOid(typeId, type); return type; }