无法安装MySQL UDF

时间:2014-03-12 20:07:02

标签: mysql dll java-native-interface

我创建了一个使用JNI调用Java函数的MySQL UDF。 MS Visual Studio编译我的代码没有任何问题,并为我的64位MySQL服务器(v.5.5)生成一个DLL。 这是C / C ++代码的样子:

#ifdef STANDARD
/* STANDARD is defined, don't use any mysql functions */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifdef __WIN__
typedef unsigned __int64 ulonglong;/* Microsofts 64 bit types */
typedef __int64 longlong;
#else
typedef unsigned long long ulonglong;
typedef long long longlong;
#endif /*__WIN__*/
#else
#include <my_global.h>
#include <my_sys.h>
#if defined(MYSQL_SERVER)
#include <m_string.h>/* To get strmov() */
#else
/* when compiled as standalone */
#include <string.h>
#define strmov(a,b) stpcpy(a,b)
#define bzero(a,b) memset(a,0,b)
#define memcpy_fixed(a,b,c) memcpy(a,b,c)
#endif
#endif
#include <mysql.h>
#include <ctype.h>

#ifdef _WIN32
/* inet_aton needs winsock library */
#pragma comment(lib, "ws2_32")
#endif

#ifdef HAVE_DLOPEN

#if !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_SOLARIS_STYLE_GETHOST)
static pthread_mutex_t LOCK_hostname;
#endif

#if defined(_WIN32) || defined(_WIN64)
#define DLLEXP __declspec(dllexport)
#else
#define DLLEXP
#endif

#include <jni.h>

DLLEXP my_bool call_java_method_init(UDF_INIT *initid __attribute__((unused)),    UDF_ARGS *args, char *message);
DLLEXP void call_java_method_deinit(UDF_INIT *initid __attribute__((unused)));
DLLEXP char* call_java_method(UDF_INIT *initid __attribute__((unused)), UDF_ARGS *args,
                     char* is_null __attribute__((unused)),   char* error __attribute__((unused)));

 static JNIEnv *env;
 static JavaVM *jvm;

 JNIEnv* create_vm(JavaVM **jvm) {
   JNIEnv *env;
   JavaVMInitArgs vm_args;
   JavaVMOption options;
   options.optionString = "-Djava.class.path=C:\\JavaApps\\CallJavaFromC++\\bin"; //Path to the java source code
   vm_args.version = JNI_VERSION_1_6; //JDK version. This indicates version 1.6
   vm_args.nOptions = 1;
   vm_args.options = &options;
   vm_args.ignoreUnrecognized = 0;

   int ret = JNI_CreateJavaVM(jvm, (void**)&env, &vm_args);
   if(ret < 0)
      printf("\nUnable to Launch JVM\n");       
  return env;
 }


my_bool call_java_method_init(UDF_INIT *initid __attribute__((unused)), UDF_ARGS *args, char *message)
{
if(!(args->arg_count == 3)) {
    char *m = "Expected 3 arguments. Function usage: call_java_method('classPath', 'className', 'methodName(methodArguments)') ";
    strcpy_s(message,  strlen(m)+1, m);
    return 1;
}

env = create_vm(&jvm);
if (env == NULL){
    char *m = "Could not load JVM!";
    strcpy_s(message,  strlen(m)+1, m);
    return 1;
}


args->arg_type[0] = STRING_RESULT;  
args->arg_type[1] = STRING_RESULT;
args->arg_type[2] = STRING_RESULT;

return 0;
}

void call_java_method_deinit(UDF_INIT *initid __attribute__((unused)))
{
int n = jvm->DestroyJavaVM();
}

char* call_java_method(UDF_INIT *initid __attribute__((unused)), UDF_ARGS *args,
                     char* is_null __attribute__((unused)), char* error __attribute__((unused)))
{
char* retResult = "";

jclass clsLEC = NULL;
jmethodID callJavaClass = NULL;
jstring jstrcalljava = NULL;
const char *pResult = NULL;

clsLEC = env->FindClass("LoadExternalClass");
if(clsLEC != NULL)
{
    callJavaClass = env->GetStaticMethodID(clsLEC,"callJavaClass","([Ljava/lang/String;)Ljava/lang/String;");
}
else
    {
    printf("\nUnable to find the requested class\n");       
    }
if(clsLEC != NULL && callJavaClass != NULL)
{
    jobjectArray ret= (jobjectArray)env->NewObjectArray(3, env->FindClass("java/lang/String"), env->NewStringUTF(""));
    env->SetObjectArrayElement(ret,0,env->NewStringUTF(args->args[0]));
    env->SetObjectArrayElement(ret,1,env->NewStringUTF(args->args[1]));
    env->SetObjectArrayElement(ret,2,env->NewStringUTF(args->args[2]));

    jstrcalljava = (jstring)env->CallStaticObjectMethod(clsLEC,callJavaClass,ret); 
    pResult = env->GetStringUTFChars(jstrcalljava,0);   
    retResult = _strdup(pResult);
    env->ReleaseStringUTFChars(jstrcalljava,pResult);
}
//Release resources.    
return retResult;
}

#endif /* HAVE_DLOPEN */

我已将 MySQLJavaUDF.dll 文件放入MySQL插件文件夹中。为了验证文件是否为64位操作系统编译,我使用了stackoverflow成员(How can I test a Windows DLL file to determine if it is 32 bit or 64 bit?)编写的 PE解构器
然后我还通过执行Visual Studios dumpbin / EXPORTS 命令检查DLL是否包含所有必需的功能。输出如下所示,似乎是正确的:

File Type: DLL

  Section contains the following exports for MySQLJavaUDF.dll

    00000000 characteristics
    531F74A1 time date stamp Tue Mar 11 21:40:01 2014
        0.00 version
           1 ordinal base
           3 number of functions
           3 number of names

    ordinal hint RVA      name

          1    0 00001150 ?call_java_method@@YAPEADPEAUst_udf_init@@PEAUst_udf_a
rgs@@PEAD2@Z = ?call_java_method@@YAPEADPEAUst_udf_init@@PEAUst_udf_args@@PEAD2@
Z (char * __cdecl call_java_method(struct st_udf_init *,struct st_udf_args *,cha
r *,char *))
          2    1 00001140 ?call_java_method_deinit@@YAXPEAUst_udf_init@@@Z = ?ca
ll_java_method_deinit@@YAXPEAUst_udf_init@@@Z (void __cdecl call_java_method_dei
nit(struct st_udf_init *))
          3    2 000010B0 ?call_java_method_init@@YADPEAUst_udf_init@@PEAUst_udf
_args@@PEAD@Z = ?call_java_method_init@@YADPEAUst_udf_init@@PEAUst_udf_args@@PEA
D@Z (char __cdecl call_java_method_init(struct st_udf_init *,struct st_udf_args
*,char *))

  Summary

        4000 .data
        2000 .pdata
        9000 .rdata
        1000 .reloc
        5000 .rsrc
       22000 .text

然后我通过在MySQL浏览器中输入以下命令首次尝试安装UDF:

CREATE FUNCTION call_java_method
RETURNS STRING
SONAME 'MySQLJavaUDF.dll';

安装失败,错误1126:
无法打开共享库'MySQLJavaUDF.dll'(错误号:126找不到指定的模块。)
所以我已经下载了Process Monitor,看看在Monitoring application calls to DLL帖子中建议安装DLL文件时会发生什么 结果发现无法找到 jvm.dll 。好的,将我的Java 1.6.45 64位安装文件夹中的文件复制到MySQL Server / bin文件夹中,再次执行SQL命令,再次获得Procmon关于 WINMM.dll 文件。没问题,将有问题的文件从我的Windows / System32目录复制并粘贴到bin文件夹中。这次Procmon只报告了“SUCCESS”结果,但现在MySQL抱怨错误1127,告诉我它 无法在库中找到符号'call_java_method'
。 而这里是我被困和放弃的地方。也许你们中的一位专家对于出了什么问题有任何想法。我怀疑DLL由于某种原因无法加载JVM。我还认为将jvm.dll复制到bin文件夹不是正确的方法。

我的系统:
Windows 7 64位
MySQL Server 5.5 64位
Java 1.6.45 64位
MS Visual Studio 2012(使用x64解决方案平台)

1 个答案:

答案 0 :(得分:0)

mysql只能理解类似C语言的函数名称。您需要明确导出此类名称:

#if defined(_WIN32) || defined(_WIN64)
#define DLLEXP extern "C" __declspec(dllexport)

以防万一尝试使用标志编译你的dll,在你的平台上编译mysql。为此,使用mysql发行版中的mysql_config实用程序,如下所示:

mysql_config --cxxflags

因此,您将能够定义必要的编译标志。