Datum UDT_output(UDT udt, PG_FUNCTION_ARGS) { char* txt; if(!UDT_isScalar(udt)) ereport(ERROR, ( errcode(ERRCODE_CANNOT_COERCE), errmsg("UDT with Oid %d is not scalar", Type_getOid((Type)udt)))); if(Type_getLength((Type)udt) == -2) { txt = PG_GETARG_CSTRING(0); if(txt != 0) txt = pstrdup(txt); } else { jobject value = _UDT_coerceDatum((Type)udt, PG_GETARG_DATUM(0)).l; jstring jstr = (jstring)JNI_callObjectMethod(value, udt->toString); MemoryContext currCtx = Invocation_switchToUpperContext(); txt = String_createNTS(jstr); MemoryContextSwitchTo(currCtx); JNI_deleteLocalRef(value); JNI_deleteLocalRef(jstr); } PG_RETURN_CSTRING(txt); }
jstring String_createJavaString(text* t) { jstring result = 0; if(t != 0) { jobject bytebuf; jobject charbuf; char* src = VARDATA(t); char* utf8 = src; Size srcLen = VARSIZE(t) - VARHDRSZ; if(srcLen == 0) return s_the_empty_string; if ( s_two_step_conversion ) { utf8 = (char*)pg_do_encoding_conversion((unsigned char*)src, (int)srcLen, s_server_encoding, PG_UTF8); srcLen = strlen(utf8); } bytebuf = JNI_newDirectByteBuffer(utf8, srcLen); charbuf = JNI_callObjectMethodLocked(s_CharsetDecoder_instance, s_CharsetDecoder_decode, bytebuf); result = JNI_callObjectMethodLocked(charbuf, s_Object_toString); JNI_deleteLocalRef(bytebuf); JNI_deleteLocalRef(charbuf); /* pg_do_encoding_conversion will return the source argument * when no conversion is required. We don't want to accidentally * free that pointer. */ if(utf8 != src) pfree(utf8); } return result; }
void InstallHelper_groundwork() { Invocation ctx; Invocation_pushInvocation(&ctx, false); ctx.function = Function_INIT_WRITER; PG_TRY(); { char const *lpt = LOADPATH_TBL_NAME; char const *lptq = quote_identifier(lpt); jstring pljlp = String_createJavaStringFromNTS(pljavaLoadPath); jstring jlpt = String_createJavaStringFromNTS(lpt); jstring jlptq = String_createJavaStringFromNTS(lptq); if ( lptq != lpt ) pfree((void *)lptq); JNI_callStaticVoidMethod( s_InstallHelper_class, s_InstallHelper_groundwork, pljlp, jlpt, jlptq, pljavaLoadingAsExtension ? JNI_TRUE : JNI_FALSE, extensionExNihilo ? JNI_TRUE : JNI_FALSE); JNI_deleteLocalRef(pljlp); JNI_deleteLocalRef(jlpt); JNI_deleteLocalRef(jlptq); Invocation_popInvocation(false); } PG_CATCH(); { Invocation_popInvocation(true); PG_RE_THROW(); } PG_END_TRY(); }
jstring String_createJavaStringFromNTS(const char* cp) { jstring result = 0; if(cp != 0) { jobject bytebuf; jobject charbuf; Size sz = strlen(cp); char const * utf8 = cp; if ( s_two_step_conversion ) { utf8 = (char*)pg_do_encoding_conversion((unsigned char*)cp, (int)sz, s_server_encoding, PG_UTF8); sz = strlen(utf8); } bytebuf = JNI_newDirectByteBuffer((void *)utf8, sz); charbuf = JNI_callObjectMethodLocked(s_CharsetDecoder_instance, s_CharsetDecoder_decode, bytebuf); result = JNI_callObjectMethodLocked(charbuf, s_Object_toString); JNI_deleteLocalRef(bytebuf); JNI_deleteLocalRef(charbuf); /* pg_do_encoding_conversion will return the source argument * when no conversion is required. We don't want to accidentally * free that pointer. */ if(utf8 != cp) pfree((void *)utf8); } return result; }
static void appendCharBuffer(StringInfoData* buf, jobject charbuf) { Size nchars; char *bp; Size cap; jobject bytebuf; jobject coderresult; for ( ;; ) { /* * Invariant: charbuf has some chars to encode, buf _might_ have room. * Broken StringInfo invariant: within this loop, might lack end NUL. */ nchars = JNI_callIntMethodLocked(charbuf, s_Buffer_remaining); /* * enlargeStringInfo does nothing if it's already large enough, and * enlarges generously if it isn't, not by nickels and dimes. */ cap = (Size)(s_CharsetEncoder_averageBytesPerChar * (double)nchars); enlargeStringInfo(buf, (int)cap); /* * Give the JVM a window into the unused portion of buf. */ bp = buf->data + buf->len; cap = buf->maxlen - buf->len; bytebuf = JNI_newDirectByteBuffer(bp, cap); /* * Encode as much as will fit, then update StringInfo len to reflect it. */ coderresult = JNI_callObjectMethodLocked(s_CharsetEncoder_instance, s_CharsetEncoder_encode, charbuf, bytebuf, (jboolean)JNI_TRUE); buf->len += JNI_callIntMethodLocked(bytebuf, s_Buffer_position); JNI_deleteLocalRef(bytebuf); if ( ! JNI_isSameObject(coderresult, s_CoderResult_OVERFLOW) ) break; JNI_deleteLocalRef(coderresult); } /* * Remember the StringInfo-is-NUL-terminated invariant might not hold here. */ if ( JNI_isSameObject(coderresult, s_CoderResult_UNDERFLOW) ) if ( 0 == JNI_callIntMethodLocked(charbuf, s_Buffer_remaining) ) { JNI_deleteLocalRef(coderresult); enlargeStringInfo(buf, 1); /* MOST PROBABLY a no-op */ buf->data[buf->len] = '\0'; /* I want my invariant back! */ return; } JNI_callVoidMethodLocked(coderresult, s_CoderResult_throwException); }
char *InstallHelper_hello() { char pathbuf[MAXPGPATH]; Invocation ctx; jstring nativeVer; jstring user; jstring dbname; jstring clustername; jstring ddir; jstring ldir; jstring sdir; jstring edir; jstring greeting; char *greetingC; char const *clusternameC = pljavaClusterName(); Invocation_pushBootContext(&ctx); nativeVer = String_createJavaStringFromNTS(SO_VERSION_STRING); user = String_createJavaStringFromNTS(MyProcPort->user_name); dbname = String_createJavaStringFromNTS(MyProcPort->database_name); if ( '\0' == *clusternameC ) clustername = NULL; else clustername = String_createJavaStringFromNTS(clusternameC); ddir = String_createJavaStringFromNTS(DataDir); get_pkglib_path(my_exec_path, pathbuf); ldir = String_createJavaStringFromNTS(pathbuf); get_share_path(my_exec_path, pathbuf); sdir = String_createJavaStringFromNTS(pathbuf); get_etc_path(my_exec_path, pathbuf); edir = String_createJavaStringFromNTS(pathbuf); greeting = JNI_callStaticObjectMethod( s_InstallHelper_class, s_InstallHelper_hello, nativeVer, user, dbname, clustername, ddir, ldir, sdir, edir); JNI_deleteLocalRef(nativeVer); JNI_deleteLocalRef(user); JNI_deleteLocalRef(dbname); if ( NULL != clustername ) JNI_deleteLocalRef(clustername); JNI_deleteLocalRef(ddir); JNI_deleteLocalRef(ldir); JNI_deleteLocalRef(sdir); JNI_deleteLocalRef(edir); greetingC = String_createNTS(greeting); JNI_deleteLocalRef(greeting); Invocation_popBootContext(); return greetingC; }
static jvalue _Array_coerceDatum(Type self, Datum arg) { jvalue result; jsize idx; Type elemType = Type_getElementType(self); int16 elemLength = Type_getLength(elemType); char elemAlign = Type_getAlign(elemType); bool elemByValue = Type_isByValue(elemType); ArrayType* v = DatumGetArrayTypeP(arg); jsize nElems = (jsize)ArrayGetNItems(ARR_NDIM(v), ARR_DIMS(v)); jobjectArray objArray = JNI_newObjectArray(nElems, Type_getJavaClass(elemType), 0); const char* values = ARR_DATA_PTR(v); bits8* nullBitMap = ARR_NULLBITMAP(v); for(idx = 0; idx < nElems; ++idx) { if(arrayIsNull(nullBitMap, idx)) JNI_setObjectArrayElement(objArray, idx, 0); else { Datum value = fetch_att(values, elemByValue, elemLength); jvalue obj = Type_coerceDatum(elemType, value); JNI_setObjectArrayElement(objArray, idx, obj.l); JNI_deleteLocalRef(obj.l); values = att_addlength_datum(values, elemLength, PointerGetDatum(values)); values = (char*)att_align_nominal(values, elemAlign); } } result.l = (jobject)objArray; return result; }
Datum Function_invokeTrigger(Function self, PG_FUNCTION_ARGS) { jvalue arg; Datum ret; arg.l = TriggerData_create((TriggerData*)fcinfo->context); if(arg.l == 0) return 0; currentInvocation->function = self; Type_invoke(self->func.nonudt.returnType, self->clazz, self->func.nonudt.method, &arg, fcinfo); fcinfo->isnull = false; if(JNI_exceptionCheck()) ret = 0; else { /* A new Tuple may or may not be created here. If it is, ensure that * it is created in the upper SPI context. */ MemoryContext currCtx = Invocation_switchToUpperContext(); ret = PointerGetDatum(TriggerData_getTriggerReturnTuple(arg.l, &fcinfo->isnull)); /* Triggers are not allowed to set the fcinfo->isnull, even when * they return null. */ fcinfo->isnull = false; MemoryContextSwitchTo(currCtx); } JNI_deleteLocalRef(arg.l); return ret; }
void Invocation_initialize(void) { jclass cls; JNINativeMethod invocationMethods[] = { { "_getCurrent", "()Lorg/postgresql/pljava/jdbc/Invocation;", Java_org_postgresql_pljava_jdbc_Invocation__1getCurrent }, { "_getNestingLevel", "()I", Java_org_postgresql_pljava_jdbc_Invocation__1getNestingLevel }, { "_clearErrorCondition", "()V", Java_org_postgresql_pljava_jdbc_Invocation__1clearErrorCondition }, { "_register", "()V", Java_org_postgresql_pljava_jdbc_Invocation__1register }, { 0, 0, 0 } }; cls = PgObject_getJavaClass("org/postgresql/pljava/jdbc/Invocation"); PgObject_registerNatives2(cls, invocationMethods); s_Invocation_onExit = PgObject_getJavaMethod(cls, "onExit", "()V"); JNI_deleteLocalRef(cls); }
static Datum _Type_nextSRF(Type self, jobject rowProducer, jobject rowCollector) { jobject tmp = JNI_callObjectMethod(rowProducer, s_Iterator_next); Datum result = Type_coerceObject(self, tmp); JNI_deleteLocalRef(tmp); return result; }
Datum UDT_input(UDT udt, PG_FUNCTION_ARGS) { jstring jstr; jobject obj; char* txt; if(!UDT_isScalar(udt)) ereport(ERROR, ( errcode(ERRCODE_CANNOT_COERCE), errmsg("UDT with Oid %d is not scalar", Type_getOid((Type)udt)))); txt = PG_GETARG_CSTRING(0); if(Type_getLength((Type)udt) == -2) { if(txt != 0) txt = pstrdup(txt); PG_RETURN_CSTRING(txt); } jstr = String_createJavaStringFromNTS(txt); obj = JNI_callStaticObjectMethod(Type_getJavaClass((Type)udt), udt->parse, jstr, udt->sqlTypeName); JNI_deleteLocalRef(jstr); return _UDT_coerceObject((Type)udt, obj); }
jclass Type_getJavaClass(Type self) { TypeClass typeClass = self->typeClass; if(typeClass->javaClass == 0) { jclass cls; const char* cp = typeClass->JNISignature; if(cp == 0 || *cp == 0) ereport(ERROR, ( errmsg("Type '%s' has no corresponding java class", PgObjectClass_getName((PgObjectClass)typeClass)))); if(*cp == 'L') { /* L<object name>; should be just <object name>. Strange * since the L and ; are retained if its an array. */ int len = strlen(cp) - 2; char* bp = palloc(len + 1); memcpy(bp, cp + 1, len); bp[len] = 0; cls = PgObject_getJavaClass(bp); pfree(bp); } else cls = PgObject_getJavaClass(cp); typeClass->javaClass = JNI_newGlobalRef(cls); JNI_deleteLocalRef(cls); } return typeClass->javaClass; }
static jobject coerceTupleDatum(UDT udt, Datum arg) { jobject result = JNI_newObject(Type_getJavaClass((Type)udt), udt->init); jobject inputStream = SQLInputFromTuple_create(DatumGetHeapTupleHeader(arg), udt->tupleDesc); JNI_callVoidMethod(result, udt->readSQL, inputStream, udt->sqlTypeName); JNI_deleteLocalRef(inputStream); return result; }
static jobject coerceScalarDatum(UDT self, Datum arg) { jobject result; int32 dataLen = Type_getLength((Type)self); jclass javaClass = Type_getJavaClass((Type)self); bool isJavaBasedScalar = 0 != self->toString; if(dataLen == -2) { /* Data is a zero terminated string */ jstring jstr = String_createJavaStringFromNTS(DatumGetCString(arg)); result = JNI_callStaticObjectMethod(javaClass, self->parse, jstr, self->sqlTypeName); JNI_deleteLocalRef(jstr); } else { char* data; jobject inputStream; if(dataLen == -1) { /* Data is a varlena struct */ bytea* bytes = DatumGetByteaP(arg); dataLen = VARSIZE(bytes) - VARHDRSZ; data = VARDATA(bytes); } else { bool passByValue = Type_isByValue((Type)self); /* Data is a binary chunk of size dataLen */ if (passByValue) { /* pass by value data is stored in the least * significant bits of a Datum. */ #ifdef WORDS_BIGENDIAN data = ((char *)(&arg)) + SIZEOF_DATUM - dataLen; #else data = ((char *)(&arg)); #endif } else { data = DatumGetPointer(arg); } } result = JNI_newObject(javaClass, self->init); inputStream = SQLInputFromChunk_create(data, dataLen, isJavaBasedScalar); JNI_callVoidMethod(result, self->readSQL, inputStream, self->sqlTypeName); SQLInputFromChunk_close(inputStream); } return result; }
jobjectArray Tuple_createArray(HeapTuple* vals, jint size, bool mustCopy) { jobjectArray tuples = JNI_newObjectArray(size, s_Tuple_class, 0); while(--size >= 0) { jobject heapTuple = Tuple_internalCreate(vals[size], mustCopy); JNI_setObjectArrayElement(tuples, size, heapTuple); JNI_deleteLocalRef(heapTuple); } return tuples; }
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; }
/** * Initialize the session */ static void initJavaSession(void) { jclass sessionClass = PgObject_getJavaClass("org/postgresql/pljava/internal/Session"); jmethodID init = PgObject_getStaticJavaMethod(sessionClass, "init", "()J"); mainThreadId = JNI_callStaticLongMethod(sessionClass, init); JNI_deleteLocalRef(sessionClass); if(JNI_exceptionCheck()) { JNI_exceptionDescribe(); JNI_exceptionClear(); ereport(ERROR, ( errcode(ERRCODE_INTERNAL_ERROR), errmsg("Unable to initialize java session"))); } }
static Datum _byte_array_coerceObject(Type self, jobject byteArray) { bytea* bytes = 0; if(byteArray == 0) return 0; if(JNI_isInstanceOf(byteArray, s_byteArray_class)) { jsize length = JNI_getArrayLength((jarray)byteArray); int32 byteaSize = length + VARHDRSZ; bytes = (bytea*)palloc(byteaSize); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 3) VARATT_SIZEP(bytes) = byteaSize; #else SET_VARSIZE(bytes, byteaSize); #endif JNI_getByteArrayRegion((jbyteArray)byteArray, 0, length, (jbyte*)VARDATA(bytes)); } else if(JNI_isInstanceOf(byteArray, s_BlobValue_class)) { jobject byteBuffer; int32 byteaSize; jlong length = JNI_callLongMethod(byteArray, s_BlobValue_length); byteaSize = (int32)(length + VARHDRSZ); bytes = (bytea*)palloc(byteaSize); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 3) VARATT_SIZEP(bytes) = byteaSize; #else SET_VARSIZE(bytes, byteaSize); #endif byteBuffer = JNI_newDirectByteBuffer((void*)VARDATA(bytes), length); if(byteBuffer != 0) JNI_callVoidMethod(byteArray, s_BlobValue_getContents, byteBuffer); JNI_deleteLocalRef(byteBuffer); } else { Exception_throwIllegalArgument("Not coercable to bytea"); } PG_RETURN_BYTEA_P(bytes); }
char* String_createNTS(jstring javaString) { char* result = 0; if( 0 == javaString ) return result; if ( uninitialized ) { const char* u8buf; s_server_encoding = GetDatabaseEncoding(); u8buf = JNI_getStringUTFChars( javaString, NULL); if ( 0 == u8buf ) return result; result = (char*)pg_do_encoding_conversion( (unsigned char *)u8buf, (int)strlen( u8buf), PG_UTF8, s_server_encoding); if ( result == u8buf ) result = pstrdup( result); JNI_releaseStringUTFChars( javaString, u8buf); return result; } else { jobject charbuf = JNI_callStaticObjectMethodLocked(s_CharBuffer_class, s_CharBuffer_wrap, javaString); StringInfoData sid; initStringInfo(&sid); appendCharBuffer(&sid, charbuf); JNI_deleteLocalRef(charbuf); result = (char*)pg_do_encoding_conversion( (unsigned char *)sid.data, sid.len, PG_UTF8, s_server_encoding); /* pg_do_encoding_conversion will return the source argument * when no conversion is required. Don't free it in that case. */ if(result != sid.data) pfree(sid.data); } return result; }
void String_appendJavaString(StringInfoData* buf, jstring javaString) { if ( 0 == javaString ) return; if ( ! s_two_step_conversion ) { jobject charbuf = JNI_callStaticObjectMethodLocked(s_CharBuffer_class, s_CharBuffer_wrap, javaString); appendCharBuffer(buf, charbuf); JNI_deleteLocalRef(charbuf); } else { char* dbEnc = String_createNTS(javaString); if ( 0 == dbEnc ) /* this can happen if a JNI call fails */ return; appendStringInfoString(buf, dbEnc); pfree(dbEnc); } }
Datum _Type_invoke(Type self, jclass cls, jmethodID method, jvalue* args, PG_FUNCTION_ARGS) { MemoryContext currCtx; Datum ret; jobject value = JNI_callStaticObjectMethodA(cls, method, args); if(value == 0) { fcinfo->isnull = true; return 0; } /* The return value cannot be created in the current context since it * goes out of scope when SPI_finish is called. */ currCtx = Invocation_switchToUpperContext(); ret = self->typeClass->coerceObject(self, value); MemoryContextSwitchTo(currCtx); JNI_deleteLocalRef(value); return ret; }
static Datum _Array_coerceObject(Type self, jobject objArray) { ArrayType* v; jsize idx; int lowerBound = 1; Type elemType = Type_getElementType(self); int nElems = (int)JNI_getArrayLength((jarray)objArray); Datum* values = (Datum*)palloc(nElems * sizeof(Datum) + nElems * sizeof(bool)); bool* nulls = (bool*)(values + nElems); for(idx = 0; idx < nElems; ++idx) { jobject obj = JNI_getObjectArrayElement(objArray, idx); if(obj == 0) { nulls[idx] = true; values[idx] = 0; } else { nulls[idx] = false; values[idx] = Type_coerceObject(elemType, obj); JNI_deleteLocalRef(obj); } } v = construct_md_array( values, nulls, 1, &nElems, &lowerBound, Type_getOid(elemType), Type_getLength(elemType), Type_isByValue(elemType), Type_getAlign(elemType)); pfree(values); PG_RETURN_ARRAYTYPE_P(v); }
text* String_createText(jstring javaString) { text* result = 0; if(javaString != 0) { char* denc; Size dencLen; Size varSize; jobject charbuf = JNI_callStaticObjectMethodLocked(s_CharBuffer_class, s_CharBuffer_wrap, javaString); StringInfoData sid; initStringInfo(&sid); appendCharBuffer(&sid, charbuf); JNI_deleteLocalRef(charbuf); denc = sid.data; dencLen = sid.len; if ( s_two_step_conversion ) { denc = (char*)pg_do_encoding_conversion( (unsigned char*)denc, (int)dencLen, PG_UTF8, s_server_encoding); dencLen = strlen(denc); } varSize = dencLen + VARHDRSZ; /* Allocate and initialize the text structure. */ result = (text*)palloc(varSize); #if PG_VERSION_NUM < 80300 VARATT_SIZEP(result) = varSize; /* Total size of structure, not just data */ #else SET_VARSIZE(result, varSize); /* Total size of structure, not just data */ #endif memcpy(VARDATA(result), denc, dencLen); if(denc != sid.data) pfree(denc); pfree(sid.data); } return result; }
Datum _String_coerceObject(Type self, jobject jstr) { char* tmp; Datum ret; if(jstr == 0) return 0; jstr = JNI_callObjectMethod(jstr, s_Object_toString); if(JNI_exceptionCheck()) return 0; tmp = String_createNTS(jstr); JNI_deleteLocalRef(jstr); ret = FunctionCall3( &((String)self)->textInput, CStringGetDatum(tmp), ObjectIdGetDatum(((String)self) -> Type_extension.typeId /* elementType */ ), Int32GetDatum(-1)); pfree(tmp); return ret; }
/* Make this datatype available to the postgres system. */ UDT UDT_registerUDT(jclass clazz, Oid typeId, Form_pg_type pgType, TupleDesc td, bool isJavaBasedScalar) { jstring jcn; MemoryContext currCtx; HeapTuple nspTup; Form_pg_namespace nspStruct; TypeClass udtClass; UDT udt; Size signatureLen; jstring sqlTypeName; char* className; char* classSignature; char* sp; const char* cp; const char* tp; char c; Type existing = Type_fromOidCache(typeId); if(existing != 0) { if(existing->typeClass->coerceDatum != _UDT_coerceDatum) { ereport(ERROR, ( errcode(ERRCODE_CANNOT_COERCE), errmsg("Attempt to register UDT with Oid %d failed. Oid appoints a non UDT type", typeId))); } return (UDT)existing; } nspTup = PgObject_getValidTuple(NAMESPACEOID, pgType->typnamespace, "namespace"); nspStruct = (Form_pg_namespace)GETSTRUCT(nspTup); /* Concatenate namespace + '.' + typename */ cp = NameStr(nspStruct->nspname); tp = NameStr(pgType->typname); sp = palloc(strlen(cp) + strlen(tp) + 2); sprintf(sp, "%s.%s", cp, tp); sqlTypeName = String_createJavaStringFromNTS(sp); pfree(sp); ReleaseSysCache(nspTup); /* Create a Java Signature String from the class name */ jcn = JNI_callObjectMethod(clazz, Class_getName); currCtx = MemoryContextSwitchTo(TopMemoryContext); className = String_createNTS(jcn); JNI_deleteLocalRef(jcn); signatureLen = strlen(className) + 2; classSignature = palloc(signatureLen + 1); MemoryContextSwitchTo(currCtx); sp = classSignature; cp = className; *sp++ = 'L'; while((c = *cp++) != 0) { if(c == '.') c = '/'; *sp++ = c; } *sp++ = ';'; *sp = 0; udtClass = TypeClass_alloc2("type.UDT", sizeof(struct TypeClass_), sizeof(struct UDT_)); udtClass->JNISignature = classSignature; udtClass->javaTypeName = className; udtClass->javaClass = JNI_newGlobalRef(clazz); udtClass->canReplaceType = _Type_canReplaceType; udtClass->coerceDatum = _UDT_coerceDatum; udtClass->coerceObject = _UDT_coerceObject; udt = (UDT)TypeClass_allocInstance2(udtClass, typeId, pgType); udt->sqlTypeName = JNI_newGlobalRef(sqlTypeName); JNI_deleteLocalRef(sqlTypeName); udt->init = PgObject_getJavaMethod(clazz, "<init>", "()V"); if(isJavaBasedScalar) { /* A scalar mapping that is implemented in Java will have the static method: * * T parse(String stringRep, String sqlTypeName); * * and a matching: * * String toString(); * * instance method. A pure mapping (i.e. no Java I/O methods) will not have * this. */ udt->toString = PgObject_getJavaMethod(clazz, "toString", "()Ljava/lang/String;"); /* The parse method is a static method on the class with the signature * (Ljava/lang/String;Ljava/lang/String;)<classSignature> */ sp = palloc(signatureLen + 40); strcpy(sp, "(Ljava/lang/String;Ljava/lang/String;)"); strcpy(sp + 38, classSignature); udt->parse = PgObject_getStaticJavaMethod(clazz, "parse", sp); pfree(sp); } else { udt->toString = 0; udt->parse = 0; } udt->tupleDesc = td; udt->readSQL = PgObject_getJavaMethod(clazz, "readSQL", "(Ljava/sql/SQLInput;Ljava/lang/String;)V"); udt->writeSQL = PgObject_getJavaMethod(clazz, "writeSQL", "(Ljava/sql/SQLOutput;)V"); Type_registerType(className, (Type)udt); return udt; }
static Datum coerceScalarObject(UDT self, jobject value) { Datum result; int32 dataLen = Type_getLength((Type)self); if(dataLen == -2) { jstring jstr = (jstring)JNI_callObjectMethod(value, self->toString); char* tmp = String_createNTS(jstr); result = CStringGetDatum(tmp); JNI_deleteLocalRef(jstr); } else { jobject outputStream; StringInfoData buffer; bool passByValue = Type_isByValue((Type)self); MemoryContext currCtx = Invocation_switchToUpperContext(); initStringInfo(&buffer); if(dataLen < 0) /* * Reserve space for an int32 at the beginning. We are building * a varlena */ appendBinaryStringInfo(&buffer, (char*)&dataLen, sizeof(int32)); outputStream = SQLOutputToChunk_create(&buffer); JNI_callVoidMethod(value, self->writeSQL, outputStream); SQLOutputToChunk_close(outputStream); MemoryContextSwitchTo(currCtx); if(dataLen < 0) { /* Assign the correct length. */ #if PG_VERSION_NUM < 80300 VARATT_SIZEP(buffer.data) = buffer.len; #else SET_VARSIZE(buffer.data, buffer.len); #endif } else if(dataLen != buffer.len) { ereport(ERROR, ( errcode(ERRCODE_CANNOT_COERCE), errmsg("UDT for Oid %d produced image with incorrect size. Expected %d, was %d", Type_getOid((Type)self), dataLen, buffer.len))); } if (passByValue) { memset(&result, 0, SIZEOF_DATUM); /* pass by value data is stored in the least * significant bits of a Datum. */ #ifdef WORDS_BIGENDIAN memcpy(&result + SIZEOF_DATUM - dataLen, buffer.data, dataLen); #else memcpy(&result, buffer.data, dataLen); #endif } else { result = PointerGetDatum(buffer.data); } } return result; }
static void Function_init(Function self, ParseResult info, Form_pg_proc procStruct, PG_FUNCTION_ARGS) { StringInfoData sign; jobject loader; jstring className; /* Get the ClassLoader for the schema that this function belongs to */ jstring schemaName = getSchemaName(procStruct->pronamespace); /* Install the type map for the current schema. This must be done ASAP since * many other functions (including obtaining the loader) depends on it. */ jobject tmp = JNI_callStaticObjectMethod(s_Loader_class, s_Loader_getTypeMap, schemaName); self->func.nonudt.typeMap = JNI_newGlobalRef(tmp); JNI_deleteLocalRef(tmp); self->readOnly = (procStruct->provolatile != PROVOLATILE_VOLATILE); self->isUDT = info->isUDT; currentInvocation->function = self; /* Get the ClassLoader for the schema that this function belongs to */ loader = JNI_callStaticObjectMethod(s_Loader_class, s_Loader_getSchemaLoader, schemaName); JNI_deleteLocalRef(schemaName); elog(DEBUG1, "Loading class %s", info->className); className = String_createJavaStringFromNTS(info->className); tmp = JNI_callObjectMethod(loader, s_ClassLoader_loadClass, className); JNI_deleteLocalRef(loader); JNI_deleteLocalRef(className); self->clazz = (jclass)JNI_newGlobalRef(tmp); JNI_deleteLocalRef(tmp); if(self->isUDT) { setupUDT(self, info, procStruct); return; } if(CALLED_AS_TRIGGER(fcinfo)) { self->func.nonudt.typeMap = 0; setupTriggerParams(self, info); } else { setupFunctionParams(self, info, procStruct, fcinfo); } initStringInfo(&sign); buildSignature(self, &sign, self->func.nonudt.returnType, false); elog(DEBUG1, "Obtaining method %s.%s %s", info->className, info->methodName, sign.data); self->func.nonudt.method = JNI_getStaticMethodIDOrNull(self->clazz, info->methodName, sign.data); if(self->func.nonudt.method == 0) { char* origSign = sign.data; Type altType = 0; Type realRetType = self->func.nonudt.returnType; elog(DEBUG1, "Method %s.%s %s not found", info->className, info->methodName, origSign); if(Type_isPrimitive(self->func.nonudt.returnType)) { /* * One valid reason for not finding the method is when * the return type used in the signature is a primitive and * the true return type of the method is the object class that * corresponds to that primitive. */ altType = Type_getObjectType(self->func.nonudt.returnType); realRetType = altType; } else if(strcmp(Type_getJavaTypeName(self->func.nonudt.returnType), "java.sql.ResultSet") == 0) { /* * Another reason might be that we expected a ResultSetProvider * but the implementation returns a ResultSetHandle that needs to be * wrapped. The wrapping is internal so we retain the original * return type anyway. */ altType = realRetType; } if(altType != 0) { JNI_exceptionClear(); initStringInfo(&sign); buildSignature(self, &sign, altType, true); elog(DEBUG1, "Obtaining method %s.%s %s", info->className, info->methodName, sign.data); self->func.nonudt.method = JNI_getStaticMethodIDOrNull(self->clazz, info->methodName, sign.data); if(self->func.nonudt.method != 0) self->func.nonudt.returnType = realRetType; } if(self->func.nonudt.method == 0) PgObject_throwMemberError(self->clazz, info->methodName, origSign, true, true); if(sign.data != origSign) pfree(origSign); } pfree(sign.data); }
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; }
Datum Type_invokeSRF(Type self, jclass cls, jmethodID method, jvalue* args, PG_FUNCTION_ARGS) { bool hasRow; CallContextData* ctxData; FuncCallContext* context; MemoryContext currCtx; /* stuff done only on the first call of the function */ if(SRF_IS_FIRSTCALL()) { jobject tmp; /* create a function context for cross-call persistence */ context = SRF_FIRSTCALL_INIT(); currCtx = MemoryContextSwitchTo(context->multi_call_memory_ctx); /* Call the declared Java function. It returns an instance that can produce * the rows. */ tmp = Type_getSRFProducer(self, cls, method, args); if(tmp == 0) { Invocation_assertDisconnect(); MemoryContextSwitchTo(currCtx); fcinfo->isnull = true; SRF_RETURN_DONE(context); } ctxData = (CallContextData*)palloc(sizeof(CallContextData)); context->user_fctx = ctxData; ctxData->elemType = self; ctxData->rowProducer = JNI_newGlobalRef(tmp); JNI_deleteLocalRef(tmp); /* Some row producers will need a writable result set in order * to produce the row. If one is needed, it's created here. */ tmp = Type_getSRFCollector(self, fcinfo); if(tmp == 0) ctxData->rowCollector = 0; else { ctxData->rowCollector = JNI_newGlobalRef(tmp); JNI_deleteLocalRef(tmp); } ctxData->trusted = currentInvocation->trusted; ctxData->hasConnected = currentInvocation->hasConnected; ctxData->invocation = currentInvocation->invocation; if(ctxData->hasConnected) ctxData->spiContext = CurrentMemoryContext; else ctxData->spiContext = 0; ctxData->rowContext = AllocSetContextCreate(context->multi_call_memory_ctx, "PL/Java row context", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); /* Register callback to be called when the function ends */ RegisterExprContextCallback(((ReturnSetInfo*)fcinfo->resultinfo)->econtext, _endOfSetCB, PointerGetDatum(ctxData)); MemoryContextSwitchTo(currCtx); } context = SRF_PERCALL_SETUP(); ctxData = (CallContextData*)context->user_fctx; MemoryContextReset(ctxData->rowContext); currCtx = MemoryContextSwitchTo(ctxData->rowContext); currentInvocation->hasConnected = ctxData->hasConnected; currentInvocation->invocation = ctxData->invocation; hasRow = Type_hasNextSRF(self, ctxData->rowProducer, ctxData->rowCollector, (jint)context->call_cntr); ctxData->hasConnected = currentInvocation->hasConnected; ctxData->invocation = currentInvocation->invocation; currentInvocation->hasConnected = false; currentInvocation->invocation = 0; if(hasRow) { Datum result = Type_nextSRF(self, ctxData->rowProducer, ctxData->rowCollector); MemoryContextSwitchTo(currCtx); SRF_RETURN_NEXT(context, result); } MemoryContextSwitchTo(currCtx); /* Unregister this callback and call it manually. We do this because * otherwise it will be called when the backend is in progress of * cleaning up Portals. If we close cursors (i.e. drop portals) in * the close, then that mechanism fails since attempts are made to * delete portals more then once. */ UnregisterExprContextCallback( ((ReturnSetInfo*)fcinfo->resultinfo)->econtext, _endOfSetCB, PointerGetDatum(ctxData)); _closeIteration(ctxData); /* This is the end of the set. */ SRF_RETURN_DONE(context); }