Пример FOREIGN KEY: возвращаемое значение errmsg в sqlite3_errmsg () для использования sqlite3_exec () не совпадает с использованием sqlite3_step () и sqlite3_bind () - PullRequest
0 голосов
/ 29 марта 2012

Две таблицы (учителя и ученики) создаются, когда идентификатор учащегося привязан к идентификатору учителя в учителях с помощью клавиши FOREIGN KEY. Если мы используем два разных метода для вставки значений в Студенты: 1. sqlite3_exec (); 2. sqlite3_bind (); Возвращаемое сообщение от sqlite3_errmsg () не то же самое, используя эти два метода: 1. sqlite3_exec (): вернуть «ограничения внешнего ключа не пройдены»; 2. sqlite3_bind (): возвращает «Ошибка логики SQL или отсутствует база данных»;

Сообщение от sqlite3_errmsg () для sqlite3_exec () более понятное, чем для sqlite3_bind (); Однако sqlite3_bind () удобнее и эффективнее для вставки значений по сравнению с sqlite3_exec ();

Мой вопрос: как получить более четкое возвращаемое сообщение об ошибке для sqlite3_bind ()?

Ниже приведены полные коды:

#include <string.h>
#include <stdio.h>
#include <iostream>
#include "sqlite3.h"

using namespace std;

sqlite3* db;

int first_row;

// callback function;
int select_callback(void *p_data, int num_fields, char **p_fields, char **p_col_names)
{
    int i;
    int* nof_records = (int*) p_data;
    (*nof_records)++;

    // first_row was defined in <select_stmt> function;
    // if first_row == 1, print the first row
    // and then set first_row = 0 to avoid the subsequent execution for the following rows.
    if (first_row == 1)
    {
        first_row = 0;
        for (i=0; i < num_fields; i++)
        {   printf("%20s", p_col_names[i]);
        }
        printf("\n");
        for (i=0; i< num_fields*20; i++)
        {   printf("=");
        }
        printf("\n");
    }

    for(i=0; i < num_fields; i++)
    {   if (p_fields[i])
        {   printf("%20s", p_fields[i]);
        }
        else
        {   printf("%20s", " ");
        }
    }

    printf("\n");
    return 0;
}

// With callback function;
void select_stmt(const char* stmt)
{   char *errmsg;
    int   ret;
    int   nrecs = 0;
    first_row = 1;
    ret = sqlite3_exec(db, stmt, select_callback, &nrecs, &errmsg);

    if(ret!=SQLITE_OK)
    {   printf("Error in select statement %s [%s].\n", stmt, errmsg);
    }
    else
    {   printf("\n   %d records returned.\n", nrecs);
    }
}

// Without callback function;
void sql_stmt(const char* stmt)
{   char *errmsg;
    int   ret;
    ret = sqlite3_exec(db, stmt, 0, 0, &errmsg);

    if(ret != SQLITE_OK)
    {   printf("Error in statement: %s [%s].\n", stmt, errmsg);
    }
}

//////////////////////////////////////// Main /////////////////////////////////
int main()
{   cout << "sqlite3_open("", &db): " << sqlite3_open("./shcool.db", &db) << endl;

    if(db == 0)
    {   printf("\nCould not open database.");
        return 1;
    }
    char *errmsg;

    int result;
    result = sqlite3_exec ( db,
                            "Drop TABLE IF EXISTS Teachers",  // stmt
                            0,
                            0,
                            &errmsg
                          );
    if ( result != SQLITE_OK )
    {   cout << "\nCould not prepare statement: Drop TABLE: " << result << endl;
        cout << "errmsg: " << errmsg << endl;
        return 1;
    }

    result = sqlite3_exec ( db,
                            "Drop TABLE IF EXISTS Students",  // stmt
                            0,
                            0,
                            &errmsg
                          );
    if ( result != SQLITE_OK )
    {   cout << "\nCould not prepare statement: Drop TABLE: " << result << endl;
        cout << "errmsg: " << errmsg << endl;
        return 1;
    }


    // CREATE TABLE Teachers;
    sql_stmt("CREATE TABLE Teachers(Id integer PRIMARY KEY,Name text,Age integer NOT NULL)");



    //////////////////////////// insert values into Teachers; /////////////////////////////////
    sqlite3_stmt *stmt;
    sqlite3_prepare(db, "PRAGMA foreign_keys = ON;", -1, &stmt, 0);

    if ( sqlite3_prepare
            ( db,
              "insert into Teachers values (:Id,:Name,:Age)",  // stmt
              -1, // If than zero, then stmt is read up to the first nul terminator
              &stmt,
              0  // Pointer to unused portion of stmt
            )
            != SQLITE_OK )
    {   printf("\nCould not prepare statement.");
        return 1;
    }

    int index1, index2, index3;
    index1 =  sqlite3_bind_parameter_index(stmt, ":Id");
    index2 =  sqlite3_bind_parameter_index(stmt, ":Name");
    index3 =  sqlite3_bind_parameter_index(stmt, ":Age");

    cout << index1 << endl;
    cout << index2 << endl;
    cout << index3 << endl;

    printf("\nThe statement has %d wildcards\n", sqlite3_bind_parameter_count(stmt));

    int id[] = {1, 2, 3};
    string name[] = {"Zhang", "Hou", "Liu"};
    int age[] = {28, 29, 31};

    for ( int i = 0; i != 3; i++ )
    {   if (sqlite3_bind_int
                (stmt,
                 index1,  // Index of wildcard
                 id[i]
                )
                != SQLITE_OK)
        {   printf("\nCould not bind sqlite3_bind_int.\n");
            return 1;
        }

        if (sqlite3_bind_text
                ( stmt,
                  index2,  // Index of wildcard
                  name[i].c_str(),
                  strlen(name[i].c_str()),
                  SQLITE_STATIC
                )
                != SQLITE_OK)
        {   printf("\nCould not bind sqlite3_bind_text.\n");
            return 1;
        }

        if (sqlite3_bind_int
                ( stmt,
                  index3,  // Index of wildcard
                  age[i]
                )
                != SQLITE_OK)
        {   printf("\nCould not bind sqlite3_bind_int.\n");
            return 1;
        }
        // execute sqlite3_step(), and check the state of the statement
        if (sqlite3_step(stmt) != SQLITE_DONE)
        {   printf("\nCould not step (execute) stmt.\n");
            return 1;
        }
        // reset the statement if you want to continue the sqlite3_bind().
        cout << "sqlite3_reset(stmt): " << sqlite3_reset(stmt) << endl;
    }

//////////////////////////// insert values into Students; /////////////////////////////////

    // CREATE TABLE Students;
    sql_stmt("CREATE TABLE Students (Id integer PRIMARY KEY,  TeacherId integer,  FOREIGN KEY(TeacherId) REFERENCES Teachers(id) )" );

    //////// Method 1: use sqlite3_exec to insert values; ////////////
    sql_stmt("INSERT INTO Students Values (0,  1)" );
    sql_stmt("INSERT INTO Students Values (1,  2)" );
    sql_stmt("INSERT INTO Students Values (2,  9)" );
    cout << "sqlite3_errmsg: " << sqlite3_errmsg(db) << endl;

    //////// Method 2: use sqlite3_bind to insert values; ////////////
    result = sqlite3_exec ( db,
                            "Drop TABLE IF EXISTS Students",  // stmt
                            0,
                            0,
                            &errmsg
                          );
    if ( result != SQLITE_OK )
    {   cout << "\nCould not prepare statement: Drop TABLE: " << result << endl;
        cout << "errmsg: " << errmsg << endl;
        return 1;
    }
    // CREATE TABLE Students;
    sql_stmt("CREATE TABLE Students (Id integer PRIMARY KEY,  TeacherId integer,  FOREIGN KEY(TeacherId) REFERENCES Teachers(id) )" );


    if ( sqlite3_prepare
            ( db,
              "insert into Students values ( :Id,:TeacherId )",  // stmt
              -1, // If than zero, then stmt is read up to the first nul terminator
              &stmt,
              0  // Pointer to unused portion of stmt
            )
            != SQLITE_OK )
    {   printf("\nCould not prepare statement.");
        return 1;
    }

    index1 =  sqlite3_bind_parameter_index(stmt, ":Id");
    index2 =  sqlite3_bind_parameter_index(stmt, ":TeacherId");

    cout << index1 << endl;
    cout << index2 << endl;

    printf("\nThe statement has %d wildcards\n", sqlite3_bind_parameter_count(stmt));

    int studentId[] = {0, 1, 2};
    /// if the FOREIGN KEY works, the teacherId should not be 9;
    int teacherId[] = {1, 2, 9};

    for ( int i = 0; i != 3; i++ )
    {   if (sqlite3_bind_int
                (stmt,
                 index1,  // Index of wildcard
                 studentId[i]
                )
                != SQLITE_OK)
        {   printf("\nCould not bind sqlite3_bind_int.\n");
            return 1;
        }

        if (sqlite3_bind_int
                ( stmt,
                  index2,  // Index of wildcard
                  teacherId[i]
                )
                != SQLITE_OK)
        {   printf("\nCould not bind sqlite3_bind_int.\n");
            cout << "sqlite3_errmsg: " << sqlite3_errmsg(db) << endl;
            return 1;
        }
        // execute sqlite3_step(), and check the state of the statement

//        cout << "sqlite3_errmsg: " << sqlite3_errmsg(db) << endl;
        if (  result = sqlite3_step(stmt)  != SQLITE_DONE )
        {   printf("\nCould not step (execute) stmt.\n");
            cout << "sqlite3_errmsg: " << sqlite3_errmsg(db) << endl;
            cout << "result: " << result << endl;
            return 1;
        }


        cout << "result: " << result << endl;
        // reset the statement if you want to continue the sqlite3_bind().
        cout << "sqlite3_reset(stmt): " << sqlite3_reset(stmt) << endl;
    }

    printf("\n");

    // Print all;
    select_stmt("select * from Teachers");
    select_stmt("select * from Students");

    sqlite3_close(db);
    return 0;
}

1 Ответ

1 голос
/ 03 апреля 2012

рекомендуется использовать sqlite3_prepare_v2 вместо sqlite3_prepare.Выдержка:

Интерфейсы sqlite3_prepare_v2 () и sqlite3_prepare16_v2 () рекомендуются для всех новых программ.Два более старых интерфейса сохраняются для обратной совместимости, но их использование не рекомендуется.В интерфейсах "v2" возвращаемый подготовленный оператор (объект sqlite3_stmt) содержит копию исходного текста SQL.Это приводит к тому, что интерфейс sqlite3_step () ведет себя по-разному тремя способами:

[Точка 1 опущена]

2. При возникновении ошибки sqlite3_step () возвращает один из подробныхкоды ошибок или расширенные коды ошибок.Устаревшее поведение заключалось в том, что sqlite3_step () возвращал только общий код результата SQLITE_ERROR, и приложение должно было бы сделать второй вызов sqlite3_reset (), чтобы найти причину проблемы.С интерфейсами подготовки v2 основная причина ошибки возвращается немедленно.

[пункт 3 опущен]

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...