Две таблицы (учителя и ученики) создаются, когда идентификатор учащегося привязан к идентификатору учителя в учителях с помощью клавиши 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;
}