编程思想是这样的:
1,用 socket 建立守候进程,等待客户端发来SQL语句,并且返回数据库数值
2,用PROC建立了一个类,并且用PROC预编译通过,现在出现一下问题因为 socket 使用的是 fork() 多进程编程,每次接收客户端发送来的SQL语句后,就建立一个新进程,但是每个进程执行完毕后游标不自动清空,所以接收没有几个就被占满了,出现一下语句:
ORA-01000: maximum open cursors exceeded但是,我的类里面明明给游标关闭了呀?一下就是类的全部内容,请各位大侠帮忙看看。
因为太长,我分段,都是连续的:/*-------------------------------------------------------------------------
类名称:QDBC
类功能:连接ORACLE数据库专用类
设计日:2004-7-16
设计者:邱洋(Q龙-QDragon)
  邮箱:[email protected]
  修改日期:2004-7-19
  修改内容:绝对不能在头文件中加入 sqlcpr.h 这个函数声明文件  修改日期:2004-7-20
  修改内容:有时候要也要将自己的头文件放在系统头文件的前面  修改日期:2004-7-30
  修改内容:将 ReturnRes() 函数中 Mode==1 部分返回了 0,并且将 RES.Fields 和 RES.Count 赋值为 0
--------------------------------------------------------------------------*/
#include "../b_qdbc.hpp"
#include  <setjmp.h>
#include  <sqlcpr.h>
#include  <sqlda.h>
#include  <sqlca.h>
/////////////////////////////////////////////////////////////////////
#define   MAX_ITEMS      100  //定义选择列表项和绑定变量的最大个数
#define   MAX_VNAME_LEN   30  //定义选择列表项、绑定变量名称的最大长度
#define   MAX_INAME_LEN   30  //定义指示变量名称的最大长度
/////////////////////////////////////////////////////////////////////class QDBC :public B_QDBC
{
public:
//构造函x数
QDBC(char* cHostName, char* cUserName, char* cPassword, char* cDBoSerName, char cDBMode);
~QDBC();
//操作函数
bool  Connect();                             //建立连接
bool  Disconnect();                          //断开连接
bool  GetRES();                              //取得资源(如果是MYSQL也一次取完--根据返回记录类)
int   ReturnRes(char  *SqlQuary,  int  Mode);//获取资源(Mode=0 是资料语句 Mode=1 是操作语句) private:
void    sql_error();
void    alloc_descriptors(int, int, int);
void    set_bind_variables();
int     process_select_list();
void    free_res();
char    *RightTrim(char *str);   //处理右边的空格
char    *LeftTrim(char  *ptr);   //处理左边的空格
SQLDA   *select_dp;
};//---------------------------------------------------------------------------------------------------
QDBC::QDBC(char* cHostName, char* cUserName, char* cPassword, char* cDBoSerName, char cDBMode):B_QDBC(cHostName, cUserName, cPassword, cDBoSerName, cDBMode)
{
EXEC SQL WHENEVER SQLERROR DO sql_error();
}
//---------------------------------------------------------------------------------------------------
QDBC::~QDBC()
{
free_res();
//关闭连接
EXEC SQL COMMIT RELEASE;
}
//---------------------------------------------------------------------------------------------------
bool    QDBC::Connect()
{
//连接数据库
//定义oracle变量
EXEC SQL BEGIN DECLARE SECTION;
char     oHostName[20];     //主机名称或地址
char     oUserName[20];     //登陆用户名
char     oPassword[20];     //登陆密码
char     oDBoSerName[20];   //数据库名称或者服务器名称
EXEC SQL END DECLARE SECTION;
strcpy(oHostName,HostName);
strcpy(oUserName,UserName);
strcpy(oPassword,Password);
strcpy(oDBoSerName,DBoSerName);// EXEC SQL CONNECT :oUserName IDENTIFIED BY :oPassword AT :oHostName USING :oDBoSerName;
EXEC SQL CONNECT :oUserName IDENTIFIED BY :oPassword USING :oDBoSerName;
if(sqlca.sqlcode==0)
{
ErrOut(Q_COMMD_OK);
Connected = true;
return(true);
}
else
ErrOut(Q_CONNT_ERROR);
return(false);
}
//---------------------------------------------------------------------------------------------------
int     QDBC::ReturnRes(char  *SqlQuary,  int  Mode)
{
// EXEC ORACLE OPTION (HOLD_CURSOR=YES);
EXEC ORACLE OPTION (MAXOPENCURSORS=1000);
// EXEC ORACLE OPTION (RELEASE_CURSOR=YES);
int rc=0;
//首先判断是否连接
if (!Connected)
{
ErrOut(Q_CONNT_ERROR);
return(-1);
}
EXEC SQL BEGIN DECLARE SECTION;
VARCHAR  SQLquare[1024];    //查询语句
EXEC SQL END DECLARE SECTION;
//初试化
memset((char *)SQLquare.arr,'\0',1024);
SQLquare.len=0; //分配选择描述区和绑定描述区
alloc_descriptors(MAX_ITEMS, MAX_VNAME_LEN, MAX_INAME_LEN);
//SQL语句赋值
strcpy((char *)SQLquare.arr,SqlQuary);
SQLquare.len = strlen(SqlQuary);
EXEC SQL PREPARE S FROM :SQLquare;
//定义游标  select * from v$open_cursors
//select * from v$open_cursor where USER_NAME like 'HTMAIL';
EXEC SQL DECLARE C SCROLL CURSOR FOR S;
EXEC SQL OPEN C USING DESCRIPTOR select_dp;
if (sqlca.sqlcode!=0)
{
ErrOut(Q_OTHER_ERROR);
rc = -1;
}
else
{
//select的时候
if (Mode==0)
{
rc=process_select_list();
}
//操作语句的时候
if (Mode==1)
{
RES.Fields=0;
RES.Count=0;
rc=0;
}
}
EXEC SQL COMMIT;
EXEC SQL CLOSE C;
free_res();
return(rc);
}
//---------------------------------------------------------------------------------------------------
bool    QDBC::GetRES()
{
return(false);
}
//---------------------------------------------------------------------------------------------------
//打印错误
void    QDBC::sql_error()
{
sprintf(errdetail,"%.*s",sqlca.sqlerrm.sqlerrml,sqlca.sqlerrm.sqlerrmc);
}
//---------------------------------------------------------------------------------------------------
void    QDBC::alloc_descriptors(int size, int max_vname_len, int max_iname_len)
{
int i=0;
FreeRES();
select_dp=SQLSQLDAAlloc(0,size,max_vname_len,max_iname_len);
select_dp->N=MAX_ITEMS;
for (i=0;i<MAX_ITEMS;i++)
{
select_dp->I[i]=(short *)malloc(sizeof(short));
select_dp->V[i]=(char *)malloc(1);
}
}

解决方案 »

  1.   

    //---------------------------------------------------------------------------------------------------
    int     QDBC::process_select_list()
    {
    int   i, null_ok, precision, scale, j=0; //选择列表项,选择描述区
    EXEC SQL DESCRIBE SELECT LIST FOR S INTO select_dp;
    //设置选择列表项的实际个数
    select_dp->N = select_dp->F;
    if (select_dp->F<=0)
    return(select_dp->F);
    for (i=0;i<select_dp->F;i++)
    {
    //清除 T[i] 高位 null
    SQLColumnNullCheck(0,(unsigned short *)&(select_dp->T[i]),(unsigned short *)&(select_dp->T[i]), &null_ok);
    //设置数据长度
    switch (select_dp->T[i])
    {
    case  1:  //CHAR
    break; case  2:  //NUMBER
    SQLNumberPrecV6(0,(unsigned int *)&(select_dp->L[i]),&precision,&scale);
    if (precision == 0)
    precision = 40;
    select_dp->L[i] = precision + 2;
                    break; case  8:  //LONG
    select_dp->L[i] = 240;
    break; case 11:  //ROWID
    select_dp->L[i] = 18;
    break; case 12:  //DATE
    select_dp->L[i] = 9;
    break; case 23:  //RAW
    break; case 24:  //LONGRAW
    select_dp->L[i] = 240;
    break;
    }
    /*-显示标题部分,暂时注解--------------------------------
    //初试化 title
    memset(title,'\0',MAX_VNAME_LEN);
    //选择列表项目名称
    strncpy(title,select_dp->S[i],select_dp->C[i]);
    printf("\t|%-.*s|",strlen(title),title);
    -------------------------------------------------------*/ //为选择列表数据缓冲区重新分配内存
    select_dp->V[i] = (char *)realloc(select_dp->V[i],select_dp->L[i]+1);
    //所有都设置为CHAR类型
    select_dp->T[i]=1;
    }
    //字段数和记录数赋值:
    RES.Fields=select_dp->F;     //字段数
    i=0,j=0;
    //开始获取内容
    //清空数据
    FreeRES();
    EXEC SQL WHENEVER NOT FOUND DO BREAK;
    for (;;)
    {
    //如果大于最大记录数就退出
    if (j>=Q_MAX_RES)
    break;
    EXEC SQL FETCH C USING DESCRIPTOR select_dp;
    if (sqlca.sqlcode!=0)
    {
    ErrOut(Q_OTHER_ERROR);
    return(false);
    }
    //数据赋值
    for (i=0;i<select_dp->F;i++)
    {
    sprintf(RES.ROWS[j][i],"%.*s",select_dp->L[i],select_dp->V[i]);
    //处理字符串
    strcpy(RES.ROWS[j][i],RightTrim(RES.ROWS[j][i]));
    }
    j++;
    }
    if (sqlca.sqlerrd[2]>Q_MAX_RES)
    RES.Count = Q_MAX_RES;
    else
    RES.Count=sqlca.sqlerrd[2];  //记录数
    return(RES.Count);
    }
    //----------------------------------------------------------------------------------------------
    void    QDBC::free_res()
    {
    //释放绑定变量和选择列表项所占内存
    for (int i=0;i<MAX_ITEMS;i++)
    {
    if (select_dp->V[i]!=(char *)0)
    free(select_dp->V[i]);
    free(select_dp->I[i]);
    }
    //释放绑定描述区和选择描述区
    SQLSQLDAFree(0,select_dp);
    }
    //----------------------------------------------------------------------------------------------
    char   *QDBC::RightTrim(char  *ptr)
    {
    int k;
    k=strlen(ptr);
    while (k>0)
    {
    if(ptr[k]!=' ' && ptr[k]!='\t' && ptr[k]!=0)
    break;
    k--;
    }
    ptr[k+1]='\0';
    return(ptr);
    }
    //----------------------------------------------------------------------------------------------
    char   *QDBC::LeftTrim(char  *ptr)
    {
    return NULL;  //暂不处理
    }
      
    可以得到数据 ,并且数据完全正确,可就是老是无法释放游标,如果是单进程,那么就产生一个游标,并且每次执行的时候老保持一个游标(用 select * from v$open_cursor语句察看),可是如果使用 fork() 多进程 ,则每个进程都有一个游标残留。
      

  2.   

    提示maximum open cursors exceeded,有时不是因为打开游标的数量达到了数据库的限定值,先看一下初始文件的设置。再检查其他使用完的游标是否关闭等。