Example #1
0
/**
 * @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();
}
Example #2
0
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;
}
Example #3
0
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;
}
Example #4
0
/* ----------
 * 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);
}
Example #5
0
/*
 * 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 */
	}
}
Example #6
0
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;
}