2012-03-29 4 views
0

生徒のIDを参照している間外国人教師のTeacherIdはFOREIGN KEYを使っています。 2つの異なるメソッドを使用して値をStudentsに挿入する場合: 1. sqlite3_exec(); 2. sqlite3_bind(); sqlite3_errmsg()からの返信メッセージは、次の2つの方法を使用して同じではありません。 1. sqlite3_exec(): "外部キー制約が失敗しました"を返します。 2. sqlite3_bind():「SQLロジックエラーまたはデータベースがありません」を返します。FOREIGN KEYの例:sqlite3_exec()を使用するためのsqlite3_errmsg()の戻りerrmsgは、sqlite3_step()およびsqlite3_bind()の使用と同じではありません。

sqlite3_exec()のsqlite3_errmsg()からのメッセージは、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

それはsqlite3_prepare_v2代わりのsqlite3_prepareを使用するrecommendedです。抜粋:

すべての新しいプログラムでは、sqlite3_prepare_v2()およびsqlite3_prepare16_v2()インターフェイスをお勧めします。 2つの古いインタフェースは下位互換性のために残されていますが、その使用は推奨されません。 "v2"インタフェースでは、返されるプリペアドステートメント(sqlite3_stmtオブジェクト)には元のSQLテキストのコピーが含まれています。この3つの方法で異なる動作をするsqlite3_step()インターフェースを引き起こす:

[ポイント1省略]

2.エラーが発生した場合、sqlite3_step()の詳細なエラー・コードのいずれかまたは拡張が返されますエラーコード。従来の動作では、sqlite3_step()は汎用のSQLITE_ERROR結果コードのみを返し、アプリケーションはsqlite3_reset()を2回呼び出して問題の根本的な原因を突き止める必要がありました。 "v2"準備インターフェイスでは、エラーの根本的な理由がすぐに返されます。

[ポイント3省略]

+0

Iは、エラーメッセージがsqlite3_execを使用した場合と同じになり、sqlite3_prepare後 "_v2" を追加しました。もう一つは、省略されているので、point1とpoint3を追加できますか?ありがとう。 – zongshiwujie

+0

@ zongshiwujie私は現在の質問には関係がなかったので省略しました。 – MPelletier

関連する問題