केवल पढ़ने के लिए sqlite3 डीबी तक पहुंचने पर मुझे कुछ समस्याएं होती हैं। सिस्टम पर (लिनक्स एम्बेडेड) एक सी ++ एप्लिकेशन है जो एक डीबी के लिए एक आर/डब्ल्यू कनेक्शन खोलता है (और खुला रहता है)। यह एप्लिकेशन लगभग लगातार पढ़ता और लिखता है।

फिर एक PHP वेब एप्लिकेशन है जो डेटा लाने के लिए हर 1 सेकंड में केवल-पढ़ने के लिए कनेक्शन खोलता है। ऐसा लगता है कि इसमें कोई समस्या नहीं है।

अंत में एक सी एप्लिकेशन है जिसे उसी डीबी से एक और रीड-ओनली कनेक्शन खोलना चाहिए और हर कुछ सेकंड में डेटा प्राप्त करना चाहिए। यह एप्लिकेशन अक्सर व्यस्त त्रुटि में चलता है।

जहां तक ​​​​मैं इस विषय पर सहायता और अन्य प्रश्नों से समझता हूं, यह आमतौर पर तब निकाल दिया जाता है जब आप लिखने का प्रयास करते हैं और एक और लेखन लेनदेन होता है। सहायता यह भी कहती है "या कुछ मामलों में पढ़ें" लेकिन मैं यह नहीं समझ सकता कि ये कौन से मामले हैं।

पढ़ने की कोशिश कर रहा है जब सी ++ एप्लिकेशन अभी भी लिख रहा है, लेकिन जहां तक ​​​​मुझे पता है कि इससे यह त्रुटि नहीं होनी चाहिए। पठन क्वेरी "पुराने" मान लौटाएगी लेकिन समस्याओं के बिना पूरी होनी चाहिए।

क्या यह सही है?

यहां सी कोड जो इस त्रुटि को लौटाता है:

sqlite3 *db;
sqlite3_stmt *stmt;
char *qry=0;
int rc,value=0;

sqlite3_open_v2( "<mydb>", &db,SQLITE_OPEN_READONLY,NULL);
if (db == NULL)
{
    syslog(LOG_INFO,"Failed to open db\n");
    return -1;
}

qry=malloc(80);

if (qry) {
    sprintf (qry,"select value from dataplc WHERE address=%d",addr);

    rc = sqlite3_prepare_v2(db, qry,-1,&stmt,NULL);
    if (rc != SQLITE_OK) {
        syslog(LOG_INFO, "Failed to read: %d - rc: %d", addr,rc); // -5 -> db busy
        value = -1;
    } else {
        rc = sqlite3_step(stmt);
        if (rc == SQLITE_ROW) {
           value = sqlite3_column_int(stmt, 0);
           // do something
        }
    }
    free (qry);
}
sqlite3_finalize(stmt);   
sqlite3_close(db);
return value;

क्या कोड या संपूर्ण दृष्टिकोण में कुछ स्पष्ट रूप से गलत है?

0
Mark 8 अप्रैल 2020, 18:26

1 उत्तर

सबसे बढ़िया उत्तर

संभावित रूप से डीबी के वर्तमान स्कीमा डेटा को पढ़ने के लिए तैयार को एक विशेष पहुंच की आवश्यकता होती है।

एक संभावित समाधान sqlite3_busy_handler का उपयोग करना होगा, उदा।

#define ABORT 0
#define CONTINUE 1

int busy_handler(void *data, int attempt) {
    printf("attempt: %d\n", attempt);
    if(attempt < 10) {
        sqlite3_sleep(250);
        return CONTINUE;
    }
    return ABORT;
}

और इसे sqlite3_prepare_v2 की कॉल से पहले इस तरह सेट करें:

sqlite3_busy_handler(db, busy_handler, NULL);

आपकी आवश्यकताओं के आधार पर पुनर्प्रयासों की अधिकतम संख्या और सोने का समय (मिलीसेकंड में) निर्धारित किया जाना चाहिए।

जैसा कि टिप्पणियों में पहले ही उल्लेख किया गया था, यह sqlite3_open_v2 कॉल के रिटर्न कोड की जांच करने के लिए समझ में आता है।

संभवतः इस तरह तैयार कथन का उपयोग करना भी समझ में आता है:

rc = sqlite3_prepare_v2(db, "SELECT value FROM dataplc WHERE address = ?1", -1, &stmt, NULL);

और पैरामीटर को इसके साथ बांधें:

sqlite3_bind_int(stmt, 1, addr);

परीक्षण कैसे करें

परीक्षण के लिए कोई एक तरकीब का उपयोग कर सकता है जो इस बढ़िया उत्तर में पाई जा सकती है: https://stackoverflow.com/a/57786662/2331445< /ए>:

उदाहरण के लिए सोने का समय अस्थायी रूप से 1 सेकंड (बिजी_हैंडलर sqlite3_sleep(1000);) में सेट किया जा सकता है।

फिर तैयारी से ठीक पहले एक getchar(); जोड़ें।

आप उपर्युक्त बिंदुओं के संबंध में थोड़ा सा संशोधित कार्यक्रम इस तरह दिखेंगे:

#include <stdio.h>
#include <sys/syslog.h>
#include "sqlite3.h"

#define ABORT 0
#define CONTINUE 1

int busy_handler(void *data, int attempt) {
    printf("attempt: %d\n", attempt);
    if (attempt < 10) {
        sqlite3_sleep(1000);
        return CONTINUE;
    }
    return ABORT;
}

int read_from_db(int addr) {
    sqlite3 *db;
    sqlite3_stmt *stmt;
    int rc, value = 0;

    rc = sqlite3_open_v2("mydb", &db, SQLITE_OPEN_READONLY, NULL);
    if (rc != SQLITE_OK) {
        sqlite3_close(db);
        syslog(LOG_INFO, "Failed to open db\n");
        return -1;
    }

    sqlite3_busy_handler(db, busy_handler, NULL);

    printf("press enter to continue:\n");
    getchar(); //only for testing

    rc = sqlite3_prepare_v2(db, "SELECT value FROM dataplc WHERE address = ?1", -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        syslog(LOG_INFO, "Failed to read: %d - rc: %d", addr, rc); // -5 -> db busy
        value = -1;
    } else {
        sqlite3_bind_int(stmt, 1, addr);

        rc = sqlite3_step(stmt);
        if (rc == SQLITE_ROW) {
            value = sqlite3_column_int(stmt, 0);
            // do something
        }
    }

    sqlite3_finalize(stmt);
    sqlite3_close(db);
    return value;
}


int main(void) {
    int val = read_from_db(42);
    printf("result: %d\n", val);
    return 0;
}

Sqlite3 कमांड लाइन इंटरफेस पर कोई प्रवेश कर सकता है:

begin exclusive;

फिर कंसोल पर जहां प्रोग्राम चलता है आप ENTER कुंजी दबा सकते हैं। यह अब बिजी_हैंडलर से प्रिंटफ दिखाएगा। एक बार जब हम डेटाबेस को a . के साथ जारी करते हैं

commit;

परिणाम लौटा दिया जाता है।

screencast

1
Stephan Schlecht 8 अप्रैल 2020, 22:18