微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用自定义客户端调用插入操作时,插入之前的触发器会引发错误,但使用MySQL客户端时则不会

如何解决使用自定义客户端调用插入操作时,插入之前的触发器会引发错误,但使用MySQL客户端时则不会

您好,感谢您谁愿意帮助我。

我正在编写一个名为directory_azienDale(公司目录)的MysqL数据库模式,该模式必须跟踪公司的所有雇主,他们的工作,他们在办公室的位置以及在办公室之间的迁移。
我已经编写了一个名为scambiaDipendenti的存储过程(switchEmployers)。当被调用时,仅当提供的雇主当前被分配到同一职位时,它才会切换席位,在TRASFERITO_A表(转移到)中插入雇主转移到的座位的电话号码,日期和工作。
如果雇主在过去三年中已被分配到该席位,则TRASFERITO_A的插入前触发器将中止插入。为此,它使用保存在TRASFERITO_A中的日期
我已清空表TRASFERITO_A并使用准备好的语句调用scambiaDipendenti,这就是ouptut,它符合预期的行为:

MysqL> prepare stmt from "call scambiaDipendenti(?,?)"
    -> ;
Query OK,0 rows affected (0.00 sec)
MysqL> select * from TRASFERITO_A;
Empty set (0.00 sec)
MysqL> set @b = "TXHTGD97E65H851W"
    -> ;
Query OK,0 rows affected (0.00 sec)
 
MysqL> set @a = "LNYRYM72A08Z327C"
    -> ;
Query OK,0 rows affected (0.00 sec)
 
MysqL> execute stmt using @a,@b;
Query OK,0 rows affected (0.01 sec)
 
MysqL> select * from TRASFERITO_A;
+------------------+--------------------------------+------------+------------------+--------------+
| CFDipendente     | NumTelefonicoEsternopostazione | Data       | NomeMansione     | NomeSettore  |
+------------------+--------------------------------+------------+------------------+--------------+
| TXHTGD97E65H851W | 1230987654                     | 2020-09-06 | calcolo bilancio | contabilità  |
| LNYRYM72A08Z327C | 8582484945                     | 2020-09-06 | calcolo bilancio | contabilità  |
+------------------+--------------------------------+------------+------------------+--------------+
2 rows in set (0.00 sec)

我还正在使用MysqL C API用C编写瘦客户机,以与DB交互以调用某些操作。它们的一个操作准备了相同的状态,这是输出

$ ./debug dipendenteSettoreSpazi
[I] DirAz Thin Client - connector for directory_azienDale DB
[I] Please enter password:
[I] Succesfully logged in as dipendenteSettoreSpazi
[I] Here is a list of supported operations. Please note that You must have the correct privileges for an operation in order to execute it. For executing an operation,opCode followed by its arguments (i.e: opCode [arg0,...])
[I] op1: generaReportDaTrasferire()
[I] op2_1: trovaDipendentiScambiabili(cfDipendente)
[I] op2_2: scambiaDipendenti(cfDipendente1,cfDipendente2)
[I] op3_1: trovaUfficiConPostazioneVuota(nomeMansione,nomeSettore)
[I] op3_2: assegnaDipendenteAPostazioneVuota(cfDipendente,numTelefonicoEsternopostazioneVuota)
[I] op4: cambiaMansioneDipendente(cfDipendente,nomeNuovaMansione,nomeNuovoSettore)
[I] op5: elencaTrasferimentiDipendente(cfDipendente)
[I] op6: ricercaDipendente(nome,cognome)
[I] op7: ricercaPerNumeroTelefono(numTelefonoEsterno)
[I] op9: assumiDipendente(cf,nome,cognome,luogoNascita,datanascita,emailPersonale,indirizzoResidenza,nomeMansione,nomeSettore)
[I] Type here:
op2_2 LNYRYM72A08Z327C TXHTGD97E65H851W
[D] 45004
[E] MysqL_stmt_execute: ERROR: Un dipendente non può essere trasferito a una postazione dove è stato già trasferito meno di tre anni fa
[E] Failed to launch statement
[E] Failed to prepare and launch statement
[E] Failed to execute op2_2

scambiaDipendentisqlstatus 45004而失败,这意味着TRASFERITO_A的插入前触发器失败。

我的问题是:当MysqL客户端调用scambiaDipendenti时,而我的瘦客户端调用失败时,为什么相同的触发器没有引发错误

以下是来源。 op2_2是scambiaDipendenti的操作码。

CREATE DEFINER=`root`@`localhost` PROCEDURE `scambiaDipendenti`(
    in cfDipendente1 char(16),in cfDipendente2 char(16)
)
BEGIN
    declare tempNumeroTelefonico1 varchar(45);
    declare tempNumeroTelefonico2 varchar(45);
    declare tempNomeMansione varchar(45);
    declare tempNomeSettore varchar(45);
    declare exit handler for sqlexception
    begin
        rollback;
        resignal;
    end;
    start transaction;
    -- controlla se le due postazioni appartengono ad uffici fisici assegnati alla stessa mansione e settore,che verranno salvati dentro delle variabili
    call checkDipendentiStessaMansione(cfDipendente1,cfDipendente2,tempNomeMansione,tempNomeSettore);
    if (tempNomeMansione is null and tempNomeSettore is null) 
        then signal sqlstate "45005" set message_text = "ERROR: i dipendenti forniti non sono assegnati alla stessa mansione";
    end if;
    -- scambia i dipendenti e aggiorna la tabella TRASFERITO_A atomicamente
    set tempNumeroTelefonico1 = (select NumTelefonicoEsternopostazione from DIPENDENTE where CF = cfDipendente1);
    set tempNumeroTelefonico2 = (select NumTelefonicoEsternopostazione from DIPENDENTE where CF = cfDipendente2);
    update DIPENDENTE set NumTelefonicoEsternopostazione = null where CF = cfDipendente1;
    update DIPENDENTE set NumTelefonicoEsternopostazione = null where CF = cfDipendente2;
    insert into TRASFERITO_A values (cfDipendente1,tempNumeroTelefonico2,curdate(),tempNomeSettore);
    insert into TRASFERITO_A values (cfDipendente2,tempNumeroTelefonico1,tempNomeSettore);
    commit;
END
CREATE
DEFINER=`root`@`localhost`
TRIGGER `directory_azienDale`.`TRASFERITO_A_BEFORE_INSERT`
BEFORE INSERT ON `directory_azienDale`.`TRASFERITO_A`
FOR EACH ROW
BEGIN
    if (select CFDipendente from DA_TRASFERIRE_A where new.CFDipendente = DA_TRASFERIRE_A.CFDipendente) is not null
        then delete from DA_TRASFERIRE_A where new.CFDipendente = DA_TRASFERIRE_A.CFDipendente;
        end if;
    if (select NumTelefonicoEsternopostazione 
        from TRASFERITO_A 
        where TRASFERITO_A.NumTelefonicoEsternopostazione = new.NumTelefonicoEsternopostazione 
            and TRASFERITO_A.CFDipendente = new.CFDipendente
            and timestampdiff(year,TRASFERITO_A.`Data`,curdate()) <= 3 
    ) is not null
        then signal sqlstate '45004' set message_text = "ERROR: Un dipendente non può essere trasferito a una postazione dove è stato già trasferito meno di tre anni fa";
    end if;
END
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdbool.h>
#include <MysqL_time.h>

#include "controller.h"
#include "logger.h"

#define MAX_LENGTH 1024

struct op {
    const char* name;
    const char* params;
    const char* stmt;
};

static const char* db_name = "directory_azienDale";
static const char* host_name = "localhost";
static struct op operations[NUM_OPS];
static char *opStrings[NUM_OPS];

void initController(){
    // populates opStrings with literal form of opCode
    // ...
    opStrings[op2_2] = "op2_2";
    
    // ...

    // Populates structures in operations with data for each op. (Todo: data should be read from a file)
    // ...
    operations[op2_2].name = "scambiaDipendenti";
    operations[op2_2].params = "cfDipendente1,cfDipendente2";
    operations[op2_2].stmt = "call scambiaDipendenti(?,?)";

    // ...

}

int prepareOp(MysqL *conn,enum opCode op,MysqL_STMT **stmtAddr){
    // init stmt
    if ((*stmtAddr = MysqL_stmt_init(conn)) == NULL){
        logMsg(E,"MysqL_stmt_init: %s\n",MysqL_error(conn));
        return 1;
    }
    // prepares stmt
    MysqL_STMT *stmt = *stmtAddr;
    if (MysqL_stmt_prepare(stmt,operations[op].stmt,strlen(operations[op].stmt)) != 0){
        logMsg(E,"MysqL_stmt_prepare: %s\n",MysqL_stmt_error(stmt));
        return 1;
    }
    return 0;
}

int launchOp(MysqL *conn,MysqL_STMT *stmt,MysqL_BIND *inParams){
    // bind params
    if (inParams != NULL && MysqL_stmt_bind_param(stmt,inParams)){
        logMsg(E,"MysqL_stmt_bind_param: %s\n",MysqL_stmt_error(stmt));
        return 1;
    }

    // execute statement
    if (MysqL_stmt_execute(stmt)){
        logMsg(D,"%s\n",MysqL_sqlstate(conn));
        logMsg(E,"MysqL_stmt_execute: %s\n",MysqL_stmt_error(stmt));
        return 1;
    }
    // buffers result set
    if (MysqL_stmt_store_result(stmt) != 0){
        logMsg(E,"MysqL_stmt_store_result: %s\n",MysqL_stmt_error(stmt));
        return 1;
    }
    return 0;
}

int prepareAndLaunchOp(MysqL *conn,MysqL_BIND *inParams,MysqL_STMT **stmtAddr){
    // prepares stmt
    if (prepareOp(conn,op,stmtAddr)){
        logMsg(E,"Failed to prepare statement\n");
        return 1;
    }
    MysqL_STMT *stmt = *stmtAddr;
    // launches op
    if (launchOp(conn,stmt,"Failed to launch statement\n");
        return 1;
    }
    return 0;
}

int printRes(MysqL_STMT* stmt,MysqL_RES *MetaRes,MysqL_BIND *resultSetCols){
    MysqL_field_seek(MetaRes,0);
    int resNumCol = MysqL_num_fields(MetaRes);
    int width[resNumCol],res;
    logMsg(I," )   ");
    for (int c = 0; c < resNumCol; c ++){
        printf("%s%n | ",MysqL_fetch_field(MetaRes) -> name,width + c);
    }
    printf("\n");
    fflush(stdout);
    for (int r = 0; ; r ++){
        if ((res = MysqL_stmt_fetch(stmt)) == MysqL_NO_DATA){
            break;
        }
        switch(res) {
            case 1:
                logMsg(E,"MysqL_stmt_fetch: %d\n",MysqL_stmt_error(stmt));
                return 1;
            case MysqL_DATA_TruncATED:
                logMsg(W,"data truncation occurred\n",r);
            case 0:
                break;
        }
        logMsg(I,"%d) ",r);
        for (int c = 0; c < resNumCol; c ++){
            switch((resultSetCols + c) -> buffer_type){
            case MysqL_TYPE_STRING:
            case MysqL_TYPE_VAR_STRING:
            case MysqL_TYPE_NEWDECIMAL:
                printf("%*s | ",width[c],(*((bool *) ((resultSetCols + c) -> is_null)))? "NULL" : (char *) resultSetCols[c].buffer);
                break;
            case MysqL_TYPE_TINY:
            case MysqL_TYPE_SHORT:
            case MysqL_TYPE_INT24:
            case MysqL_TYPE_LONG:
            case MysqL_TYPE_LONGLONG:
                if (*((bool *) ((resultSetCols + c) -> is_null))){
                    printf("%*s | ","NULL");
                }
                else {
                    printf("%*d | ",*((int *)(resultSetCols[c].buffer)));
                }
                break;
            case MysqL_TYPE_FLOAT:
            case MysqL_TYPE_DOUBLE:
                if (*((bool *) ((resultSetCols + c) -> is_null))){
                    printf("%*s | ","NULL");
                }
                else {
                    printf("%*f | ",*((double *)(resultSetCols[c].buffer)));
                }
                break;
            case MysqL_TYPE_DATE:
                if (*((bool *) ((resultSetCols + c) -> is_null))){
                    printf("%s/ ","NULL");
                }
                else {
                    printf("%d/%d/%d | ",((MysqL_TIME *)(resultSetCols[c].buffer)) -> day,((MysqL_TIME *)(resultSetCols[c].buffer)) -> month,((MysqL_TIME *)(resultSetCols[c].buffer)) -> year
                    );
                }
                break;
            default:
                printf("(not supported) | ");
            }
        }
        printf("\n");
    }
    fflush(stdout);

    return 0;
}

void freeResultSet(MysqL_BIND *resultSetCols,int resNumCol){
   for (int i = 0; i < resNumCol; i ++){
        free(resultSetCols[i].buffer);
        free(resultSetCols[i].length);
        free(resultSetCols[i].is_null);
        free(resultSetCols[i].error);
    }
    free(resultSetCols);
}

MysqL_BIND *callocResultSetCols(MysqL_RES *MetaRes){
    int resNumCol = MysqL_num_fields(MetaRes);
    MysqL_BIND *resultSetCols = calloc(resNumCol,sizeof(MysqL_BIND));
    MysqL_FIELD *currentField;

    MysqL_field_seek(MetaRes,0);
    for (int i = 0; i < resNumCol; i ++){
    currentField = MysqL_fetch_field(MetaRes);
    resultSetCols[i].buffer_type = currentField -> type;
    resultSetCols[i].buffer = calloc(MAX_LENGTH,sizeof(char));
    resultSetCols[i].buffer_length  = MAX_LENGTH;
    resultSetCols[i].length = (unsigned long*) calloc(1,sizeof(unsigned long));
    resultSetCols[i].is_null = (bool *) calloc(1,sizeof(bool));
    resultSetCols[i].error = (bool *) calloc(1,sizeof(bool));
    }
    return resultSetCols;
}

int bindRes(MysqL_STMT *stmt,MysqL_BIND **resultSetColsAddr,MysqL_RES **MetaResAddr){
    MysqL_RES *MetaRes;
    MysqL_BIND *resultSetCols;
    int resNumCol;
    int numRes = 0;
    if ((*MetaResAddr = MysqL_stmt_result_Metadata(stmt)) == NULL){
            logMsg(E,"MysqL_stmt_result_Metadata: %s\n",MysqL_stmt_error(stmt));
            return -1;
    }
    MetaRes = *MetaResAddr;
    resNumCol = MysqL_num_fields(MetaRes);
    if (resNumCol > 0){
        numRes ++;
        // binds result set dinamically
        *resultSetColsAddr = callocResultSetCols(MetaRes);
        resultSetCols = *resultSetColsAddr;
        if (MysqL_stmt_bind_result(stmt,resultSetCols)){
            logMsg(E,"MysqL_stmt_bind_result: %s\n",MysqL_stmt_error(stmt));
            return -1;
        }
    }
    return numRes;
}

// ...

int callOp2_2(MysqL *conn,int numOfArgs,char *cfDipendente1,char *cfDipendente2){
    MysqL_STMT *stmt;
    MysqL_BIND *resSet;
    MysqL_BIND *inParams = calloc(numOfArgs,sizeof(MysqL_BIND));
    MysqL_RES *MetaRes;
    int hasNext;
    unsigned long len[numOfArgs];
    bool isNull[numOfArgs];

    // prepares params

    memset(isNull,false,sizeof(bool) * numOfArgs);

    if (cfDipendente1 == NULL){
        cfDipendente1 = "";
        isNull[0] = true;
        inParams -> is_null = isNull;
    }
    len[0] = sizeof(char) * strlen(cfDipendente1);
    inParams -> buffer_type = MysqL_TYPE_STRING;
    inParams -> buffer = cfDipendente1;
    inParams -> buffer_length = len[0];
    inParams -> length = len;

    if (cfDipendente2 == NULL){
        cfDipendente2 = "";
        isNull[1] = true;
        (inParams + 1) -> is_null = isNull;
    }
    len[1] = sizeof(char) * strlen(cfDipendente2);
    (inParams + 1) -> buffer_type = MysqL_TYPE_STRING;
    (inParams + 1) -> buffer = cfDipendente2;
    (inParams+ 1) -> buffer_length = len[1];
    (inParams + 1) -> length = len + 1;

    // prepare and launches stmt
    if (prepareAndLaunchOp(conn,op2_2,inParams,&stmt)){
        logMsg(E,"Failed to prepare and launch statement\n");
        return 1;
    }


    // binds res set
    if (bindRes(stmt,&resSet,&MetaRes) <= 0){
        logMsg(W,"Either Failed to bind a result set or no result set was available to bind\n");
    }

    // no res set has to be printed

    // discards remaining result sets
    do {MysqL_stmt_free_result(stmt);} while ((hasNext = MysqL_stmt_next_result(stmt) == 0));
    if (hasNext > 0){
        logMsg(E,"MysqL_stmt_next_result: %s\n",MysqL_stmt_error(stmt));
        return 1;
    }

    // frees memory allocated dinamically
    freeResultSet(resSet,MysqL_num_fields(MetaRes));
    MysqL_free_result(MetaRes);
    if (MysqL_stmt_close(stmt) != 0){
        logMsg(E,"MysqL_stmt_close: %s\n",MysqL_error(conn));
        return 1;
    }

    return 0;
}

// ...

int callOp(MysqL *conn,const enum opCode op,char *opArgs){
    // Todo: Binding of in and out params and collecting the result set are demanded to the particular op
    int res;
    switch(op){
        // ...
        case op2_2:
            //FIXME: sqlstate 45004 caught even if TRASFERITO_A is empty. Calling scambiaDipendenti with MysqL client result in success
            res = callOp2_2(conn,2,strtok(opArgs,ARG_DEL),strtok(NULL,ARG_DEL));
            break;
        // ...
        default:
            logMsg(E,"There is no such operation with provided opCode\n");
            return 1;
    }
    return res;
}

int connectToDB(char *username,char* passwd,MysqL** connAddr){
    // Initialize connection
    if ((*connAddr = MysqL_init(NULL)) == NULL){
        int err = errno;
        logMsg(E,"MysqL_init: %s\n",strerror(err));
        return 1;
    }
    MysqL *conn = *connAddr;
    // Tries to connect with db. NULL values are read from settings file
    if ((MysqL_real_connect(conn,host_name,username,passwd,db_name,// port number
                        NULL,// socket name
                        CLIENT_MULTI_STATEMENTS )) == NULL){
        logMsg(E,"MysqL_real_connect: %s\n",MysqL_error(conn));
        return 1;
    }
    return 0;
}

// ...

解决方法

我发现了错误,它在callOp2_2中:


// ...

bool isNull[numOfArgs];

// ...

if (cfDipendente2 == NULL){
        cfDipendente1 = "";
        isNull[1] = true;
        inParams -> is_null = isNull;  // it should be inParams -> is_null = isNull + 1
    }

这导致传递未定义的值以绑定到准备好的语句,从而导致不可预测的错误。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。