এসকিউএলাইট অপ্টিমাইজ করা জটিল। একটি সি অ্যাপ্লিকেশনের বাল্ক-সন্নিবেশ সম্পাদনা প্রতি সেকেন্ডে 85 টি সন্নিবেশ থেকে প্রতি সেকেন্ডে 96,000 এরও বেশি পরিবর্তিত হতে পারে!
পটভূমি: আমরা ডেস্কটপ অ্যাপ্লিকেশনটির অংশ হিসাবে এসকিউএলাইট ব্যবহার করছি। আমাদের কাছে এক্সএমএল ফাইলগুলিতে প্রচুর পরিমাণে কনফিগারেশন ডেটা সঞ্চিত রয়েছে যা অ্যাপ্লিকেশনটি আরম্ভ করার সময় আরও প্রক্রিয়াকরণের জন্য একটি এসকিউএল ডাটাবেসে পার্স করা এবং লোড করা হয়। এসকিউএলাইট এই পরিস্থিতির জন্য আদর্শ কারণ এটি দ্রুত, এটির জন্য বিশেষায়িত কনফিগারেশন প্রয়োজন নেই এবং ডাটাবেসটি একক ফাইল হিসাবে ডিস্কে সঞ্চিত থাকে।
যুক্তি: প্রাথমিকভাবে আমি যে পারফরম্যান্সটি দেখছিলাম তাতে হতাশ হয়েছি। এটি দেখা গেছে যে ডাটাবেসটি কীভাবে কনফিগার করা হয়েছে এবং আপনি কীভাবে API ব্যবহার করছেন তার উপর নির্ভর করে এসকিউএলাইটের পারফরম্যান্স উল্লেখযোগ্যভাবে পরিবর্তিত হতে পারে (বাল্ক-সন্নিবেশকারী এবং নির্বাচিত উভয়ের জন্য)। বিকল্পগুলি এবং কৌশলগুলির মধ্যে কী কী তা নির্ধারণ করা কোনও তুচ্ছ বিষয় ছিল না, তাই আমি অন্যদের একই তদন্তের ঝামেলা বাঁচাতে স্ট্যাক ওভারফ্লো পাঠকদের সাথে ফলাফলগুলি ভাগ করে নেওয়ার জন্য এই সম্প্রদায়টির উইকি এন্ট্রি তৈরি করা বুদ্ধিমানের বলে মনে করি।
পরীক্ষা: সাধারণ অর্থে পারফরম্যান্স টিপসের কথা বলার চেয়ে (যেমন "একটি লেনদেন ব্যবহার করুন!" ), আমি কিছু সি কোড লিখতে এবং বিভিন্ন বিকল্পের প্রভাবটি পরিমাপ করা ভাল বলে মনে করি । আমরা কিছু সাধারণ ডেটা দিয়ে শুরু করতে যাচ্ছি:
- টরন্টো শহরের পুরো ট্রানজিট শিডিয়ুলের একটি 28 এমবি ট্যাব-সীমাবদ্ধ পাঠ্য ফাইল (আনুমানিক 865,000 রেকর্ডস)
- আমার পরীক্ষা মেশিনটি একটি 3.60 গিগাহার্টজ পি 4 চলমান উইন্ডোজ এক্সপি।
- কোডটি ভিজ্যুয়াল সি ++ 2005 এর সাথে "সম্পূর্ণ অপ্টিমাইজেশন" (/ অক্স) এবং ফেভারিট ফাস্ট কোড (/ ওটি) সহ "রিলিজ" হিসাবে সংকলিত হয়েছে ।
- আমি সরাসরি আমার পরীক্ষার অ্যাপ্লিকেশনটিতে সংকলিত এসকিউএলাইট "সংহতি" ব্যবহার করছি। আমার কাছে এসকিউএলাইট সংস্করণটি কিছুটা পুরানো (৩.6..7), তবে আমি সন্দেহ করি যে এই ফলাফলগুলি সর্বশেষ প্রকাশের সাথে তুলনাযোগ্য হবে (যদি আপনি অন্যথায় মনে করেন তবে একটি মন্তব্য দিন)।
আসুন কিছু কোড লিখি!
কোড: একটি সাধারণ সি প্রোগ্রাম যা পাঠ্য ফাইলটি লাইন বাই লাইন পড়ে, স্ট্রিংকে মানগুলিতে বিভক্ত করে এবং তারপরে একটি এসকিউএল ডাটাবেসে ডেটা সন্নিবেশ করে। কোডটির এই "বেসলাইন" সংস্করণে ডাটাবেস তৈরি করা হয়েছে, তবে আমরা আসলে ডেটা won'tোকাতে চাই না:
/*************************************************************
Baseline code to experiment with SQLite performance.
Input data is a 28 MB TAB-delimited text file of the
complete Toronto Transit System schedule/route info
from http://www.toronto.ca/open/datasets/ttc-routes/
**************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include "sqlite3.h"
#define INPUTDATA "C:\\TTC_schedule_scheduleitem_10-27-2009.txt"
#define DATABASE "c:\\TTC_schedule_scheduleitem_10-27-2009.sqlite"
#define TABLE "CREATE TABLE IF NOT EXISTS TTC (id INTEGER PRIMARY KEY, Route_ID TEXT, Branch_Code TEXT, Version INTEGER, Stop INTEGER, Vehicle_Index INTEGER, Day Integer, Time TEXT)"
#define BUFFER_SIZE 256
int main(int argc, char **argv) {
sqlite3 * db;
sqlite3_stmt * stmt;
char * sErrMsg = 0;
char * tail = 0;
int nRetCode;
int n = 0;
clock_t cStartClock;
FILE * pFile;
char sInputBuf [BUFFER_SIZE] = "\0";
char * sRT = 0; /* Route */
char * sBR = 0; /* Branch */
char * sVR = 0; /* Version */
char * sST = 0; /* Stop Number */
char * sVI = 0; /* Vehicle */
char * sDT = 0; /* Date */
char * sTM = 0; /* Time */
char sSQL [BUFFER_SIZE] = "\0";
/*********************************************/
/* Open the Database and create the Schema */
sqlite3_open(DATABASE, &db);
sqlite3_exec(db, TABLE, NULL, NULL, &sErrMsg);
/*********************************************/
/* Open input file and import into Database*/
cStartClock = clock();
pFile = fopen (INPUTDATA,"r");
while (!feof(pFile)) {
fgets (sInputBuf, BUFFER_SIZE, pFile);
sRT = strtok (sInputBuf, "\t"); /* Get Route */
sBR = strtok (NULL, "\t"); /* Get Branch */
sVR = strtok (NULL, "\t"); /* Get Version */
sST = strtok (NULL, "\t"); /* Get Stop Number */
sVI = strtok (NULL, "\t"); /* Get Vehicle */
sDT = strtok (NULL, "\t"); /* Get Date */
sTM = strtok (NULL, "\t"); /* Get Time */
/* ACTUAL INSERT WILL GO HERE */
n++;
}
fclose (pFile);
printf("Imported %d records in %4.2f seconds\n", n, (clock() - cStartClock) / (double)CLOCKS_PER_SEC);
sqlite3_close(db);
return 0;
}
নিয়ন্ত্রণ"
কোডটি যেমন হয় তেমন চালানো আসলে কোনও ডাটাবেস অপারেশন করে না, তবে এটি আমাদের ধারণা দেয় যে কাঁচা সি ফাইল I / O এবং স্ট্রিং প্রসেসিং অপারেশনগুলি কত দ্রুত fast
0.94 সেকেন্ডে 864913 রেকর্ড আমদানি করা হয়েছে
গ্রেট! আমরা প্রতি সেকেন্ডে 920,000 সন্নিবেশ করতে পারি, তবে শর্ত থাকে যে আমরা আসলে কোনও প্রবেশদ্বার না করি :-)
"সবচেয়ে খারাপ কেস-সিনারিও"
আমরা ফাইল থেকে পঠিত মানগুলি ব্যবহার করে এসকিউএল স্ট্রিং তৈরি করতে যাচ্ছি এবং এসকিউএল অপারেশনটি sqlite3_exec ব্যবহার করে অনুরোধ করব:
sprintf(sSQL, "INSERT INTO TTC VALUES (NULL, '%s', '%s', '%s', '%s', '%s', '%s', '%s')", sRT, sBR, sVR, sST, sVI, sDT, sTM);
sqlite3_exec(db, sSQL, NULL, NULL, &sErrMsg);
এটি ধীর হতে চলেছে কারণ এসকিউএল প্রতিটি সন্নিবেশের জন্য ভিডিবিই কোডে সংকলিত হবে এবং প্রতিটি সন্নিবেশ তার নিজস্ব লেনদেনে ঘটবে। কত ধীর?
9933.61 সেকেন্ডে 864913 রেকর্ড আমদানি করা হয়েছে
বাবা! 2 ঘন্টা 45 মিনিট! এটি প্রতি সেকেন্ডে কেবল 85 টি প্রবেশদ্বার।
একটি লেনদেন ব্যবহার করে
ডিফল্টরূপে, এসকিউএলাইট একটি অনন্য লেনদেনের মধ্যে প্রতিটি INSERT / UPDATE বিবৃতি মূল্যায়ন করবে। যদি প্রচুর সংখ্যক সন্নিবেশ করানো হয় তবে এটি কোনও লেনদেনের মধ্যে আপনার ক্রিয়াকলাপটি মোড়ানো পরামর্শ দেওয়া হয়:
sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, &sErrMsg);
pFile = fopen (INPUTDATA,"r");
while (!feof(pFile)) {
...
}
fclose (pFile);
sqlite3_exec(db, "END TRANSACTION", NULL, NULL, &sErrMsg);
38.03 সেকেন্ডে 864913 রেকর্ড আমদানি করা হয়েছে
এটা তুলনামূলক ভাল. কেবলমাত্র আমাদের সমস্ত সন্নিবেশকে একটি একক লেনদেনের মধ্যে মোড়ানো আমাদের পারফরম্যান্সকে প্রতি সেকেন্ডে 23,000 সন্নিবেশে উন্নত করে improved
একটি প্রস্তুত বিবৃতি ব্যবহার
লেনদেনটি ব্যবহার করা একটি বিশাল উন্নতি ছিল, তবে প্রতিটি সন্নিবেশের জন্য এসকিউএল স্টেটমেন্টটি পুনরায় সংশ্লেষ করা যদি আমরা একই এসকিউএলকে ওভার-ওভার ব্যবহার করি তা বোঝা যায় না। আসুন sqlite3_prepare_v2
একবার আমাদের এসকিউএল স্টেটমেন্টটি সংকলন করতে এবং এরপরে আমাদের পরামিতিগুলিকে সেই বিবৃতিটিতে আবদ্ধ করুন sqlite3_bind_text
:
/* Open input file and import into the database */
cStartClock = clock();
sprintf(sSQL, "INSERT INTO TTC VALUES (NULL, @RT, @BR, @VR, @ST, @VI, @DT, @TM)");
sqlite3_prepare_v2(db, sSQL, BUFFER_SIZE, &stmt, &tail);
sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, &sErrMsg);
pFile = fopen (INPUTDATA,"r");
while (!feof(pFile)) {
fgets (sInputBuf, BUFFER_SIZE, pFile);
sRT = strtok (sInputBuf, "\t"); /* Get Route */
sBR = strtok (NULL, "\t"); /* Get Branch */
sVR = strtok (NULL, "\t"); /* Get Version */
sST = strtok (NULL, "\t"); /* Get Stop Number */
sVI = strtok (NULL, "\t"); /* Get Vehicle */
sDT = strtok (NULL, "\t"); /* Get Date */
sTM = strtok (NULL, "\t"); /* Get Time */
sqlite3_bind_text(stmt, 1, sRT, -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 2, sBR, -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 3, sVR, -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 4, sST, -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 5, sVI, -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 6, sDT, -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 7, sTM, -1, SQLITE_TRANSIENT);
sqlite3_step(stmt);
sqlite3_clear_bindings(stmt);
sqlite3_reset(stmt);
n++;
}
fclose (pFile);
sqlite3_exec(db, "END TRANSACTION", NULL, NULL, &sErrMsg);
printf("Imported %d records in %4.2f seconds\n", n, (clock() - cStartClock) / (double)CLOCKS_PER_SEC);
sqlite3_finalize(stmt);
sqlite3_close(db);
return 0;
16.27 সেকেন্ডে 864913 রেকর্ড আমদানি করা হয়েছে
নিস! এখানে আরও কিছুটা কোড রয়েছে (কল করতে sqlite3_clear_bindings
এবং ভুলেও ভুলবেন না sqlite3_reset
) তবে আমরা আমাদের পারফরম্যান্সকে দ্বিগুণ করে প্রতি সেকেন্ডে 53,000 সন্নিবেশ করিয়েছি।
PRAGMA সিঙ্ক্রোনাস = অফ
ডিফল্টরূপে এসকিউএলাইট ওএস-স্তরের রাইট কমান্ড জারির পরে বিরতি দেবে। এটি গ্যারান্টি দেয় যে ডেটা ডিস্কে লিখিত হয়। সেট করে synchronous = OFF
, আমরা এসকিউএলাইটকে লেখার জন্য ওএসের কাছে ডেটা হ্যান্ড-অফ করার নির্দেশ দিচ্ছি এবং তারপরে চালিয়ে যাচ্ছি। এই সম্ভাবনা রয়েছে যে কম্পিউটার প্লাটারে ডেটা লেখার আগে কম্পিউটারটি যদি একটি বিপর্যয়কর ক্রাশ (বা পাওয়ার ব্যর্থতা) ভোগ করে তবে ডাটাবেস ফাইলটি দূষিত হয়ে যেতে পারে:
/* Open the database and create the schema */
sqlite3_open(DATABASE, &db);
sqlite3_exec(db, TABLE, NULL, NULL, &sErrMsg);
sqlite3_exec(db, "PRAGMA synchronous = OFF", NULL, NULL, &sErrMsg);
12.41 সেকেন্ডে 864913 রেকর্ড আমদানি করা হয়েছে
উন্নতিগুলি এখন ছোট, তবে আমরা প্রতি সেকেন্ডে 69,600 সন্নিবেশ করিয়েছি।
PRAGMA জার্নাল_মোড = স্মৃতি
মূল্যায়ন করে মেমরিতে রোলব্যাক জার্নালটি সংরক্ষণ করার বিষয়ে বিবেচনা করুন PRAGMA journal_mode = MEMORY
। আপনার লেনদেন দ্রুত হবে, তবে আপনি যদি শক্তি হারিয়ে ফেলেন বা কোনও লেনদেনের সময় আপনার প্রোগ্রাম ক্রাশ হয়ে যায় তবে আপনার ডাটাবেসটি একটি আংশিক-সম্পূর্ণ লেনদেনের সাথে একটি দুর্নীতিগ্রস্থ অবস্থায় থাকতে পারে:
/* Open the database and create the schema */
sqlite3_open(DATABASE, &db);
sqlite3_exec(db, TABLE, NULL, NULL, &sErrMsg);
sqlite3_exec(db, "PRAGMA journal_mode = MEMORY", NULL, NULL, &sErrMsg);
13.50 সেকেন্ডে 864913 রেকর্ড আমদানি করা হয়েছে
আগের অপ্টিমাইজেশনের তুলনায় কিছুটা ধীর গতিতে প্রতি সেকেন্ডে 64৪,০০০ সন্নিবেশ করা হয়।
PRAGMA সিঙ্ক্রোনাস = অফ এবং PRAGMA জার্নাল_মোড = স্মৃতি
আগের দুটি অপটিমাইজেশন একত্রিত করা যাক। এটি কিছুটা ঝুঁকিপূর্ণ (ক্রাশের ক্ষেত্রে) তবে আমরা কেবল ডেটা আমদানি করি (ব্যাংক চালাচ্ছি না):
/* Open the database and create the schema */
sqlite3_open(DATABASE, &db);
sqlite3_exec(db, TABLE, NULL, NULL, &sErrMsg);
sqlite3_exec(db, "PRAGMA synchronous = OFF", NULL, NULL, &sErrMsg);
sqlite3_exec(db, "PRAGMA journal_mode = MEMORY", NULL, NULL, &sErrMsg);
12.00 সেকেন্ডে 864913 রেকর্ড আমদানি করা হয়েছে
ফ্যান্টাস্টিক! আমরা প্রতি সেকেন্ডে ,000২,০০০ সন্নিবেশ করতে সক্ষম ।
একটি ইন-মেমরি ডেটাবেস ব্যবহার করা
কেবল কিক্সের জন্য, আসুন পূর্ববর্তী সমস্ত অপ্টিমাইজেশানগুলির উপর ভিত্তি করে তৈরি করুন এবং ডাটাবেস ফাইলের নামটি নতুনভাবে সংজ্ঞায়িত করুন যাতে আমরা সম্পূর্ণ র্যামে কাজ করছি:
#define DATABASE ":memory:"
10.94 সেকেন্ডে 864913 রেকর্ড আমদানি করা হয়েছে
আমাদের ডেটাবেসটি র্যামে সঞ্চয় করা অত্যন্ত ব্যবহারিক নয়, তবে এটি চিত্তাকর্ষক যে আমরা প্রতি সেকেন্ডে ,000৯,০০০ সন্নিবেশ সম্পাদন করতে পারি ।
সি কোড রিফ্যাক্টরিং
যদিও বিশেষত এসকিউএলাইট উন্নতি নয়, লুপের অতিরিক্ত char*
অ্যাসাইনমেন্ট ক্রিয়াকলাপটি আমি পছন্দ করি না while
। strtok()
সরাসরি সেই আউটপুট সরাসরি প্রবেশের জন্য সেই কোডটিকে দ্রুত রিফ্যাক্টর করি এবং সংকলকটি sqlite3_bind_text()
আমাদের জন্য জিনিসগুলি দ্রুত করার চেষ্টা করুক:
pFile = fopen (INPUTDATA,"r");
while (!feof(pFile)) {
fgets (sInputBuf, BUFFER_SIZE, pFile);
sqlite3_bind_text(stmt, 1, strtok (sInputBuf, "\t"), -1, SQLITE_TRANSIENT); /* Get Route */
sqlite3_bind_text(stmt, 2, strtok (NULL, "\t"), -1, SQLITE_TRANSIENT); /* Get Branch */
sqlite3_bind_text(stmt, 3, strtok (NULL, "\t"), -1, SQLITE_TRANSIENT); /* Get Version */
sqlite3_bind_text(stmt, 4, strtok (NULL, "\t"), -1, SQLITE_TRANSIENT); /* Get Stop Number */
sqlite3_bind_text(stmt, 5, strtok (NULL, "\t"), -1, SQLITE_TRANSIENT); /* Get Vehicle */
sqlite3_bind_text(stmt, 6, strtok (NULL, "\t"), -1, SQLITE_TRANSIENT); /* Get Date */
sqlite3_bind_text(stmt, 7, strtok (NULL, "\t"), -1, SQLITE_TRANSIENT); /* Get Time */
sqlite3_step(stmt); /* Execute the SQL Statement */
sqlite3_clear_bindings(stmt); /* Clear bindings */
sqlite3_reset(stmt); /* Reset VDBE */
n++;
}
fclose (pFile);
দ্রষ্টব্য: আমরা একটি বাস্তব ডাটাবেস ফাইল ব্যবহার করতে ফিরে এসেছি। মেমরির ডাটাবেসগুলি দ্রুত, তবে ব্যবহারিকভাবে অগত্যা নয়
8.94 সেকেন্ডে 864913 রেকর্ড আমদানি করা হয়েছে
আমাদের প্যারামিটার বাইন্ডিংয়ে ব্যবহৃত স্ট্রিং প্রসেসিং কোডটিতে কিছুটা রিফ্যাক্টরিং আমাদের প্রতি সেকেন্ডে 96,700 সন্নিবেশ সম্পাদনের অনুমতি দিয়েছে । আমি মনে করি এটি নিরাপদ যে এটি যথেষ্ট দ্রুত । যেহেতু আমরা অন্যান্য ভেরিয়েবলগুলি (যেমন পৃষ্ঠার আকার, সূচক তৈরি ইত্যাদি) টুইট করতে শুরু করি এটি আমাদের বেঞ্চমার্ক হবে।
সংক্ষিপ্তসার (এখনও অবধি)
আমি আশা করি আপনি এখনও আমার সাথে আছেন! আমরা এই রাস্তাটি শুরু করার কারণটি হ'ল এসকিউএলাইটের সাথে বাল্ক-sertোকানো পারফরম্যান্সের পরিমাণ এতটাই ভিন্ন and একই সংকলক (এবং সংকলক বিকল্পগুলি) ব্যবহার করে, এসকিউএলাইটের একই সংস্করণ এবং একই ডাটা আমরা আমাদের সেকেন্ডে এবং এসকিউএলাইটের ব্যবহারের প্রতি সেকেন্ডে 85 টি সন্নিবেশ করায় প্রতি সেকেন্ডে 96,000 এরও বেশি সংখ্যার নিকৃষ্ট পরিস্থিতি থেকে যায়!
INDEX তৈরি করুন তারপরে INSERT বনাম INSERT তারপর INDEX তৈরি করুন
আমরা SELECT
কর্মক্ষমতা পরিমাপ করা শুরু করার আগে , আমরা জানি যে আমরা সূচকগুলি তৈরি করব। এটি নীচের উত্তরগুলির মধ্যে একটিতে প্রস্তাবিত হয়েছে যে বাল্ক সন্নিবেশগুলি করার সময়, তথ্য সন্নিবেশ করার পরে সূচকটি তৈরি করা দ্রুত হয় (প্রথমে সূচি তৈরির পরে ডেটা সন্নিবেশ করার বিপরীতে)। আসুন চেষ্টা করুন:
সূচক তৈরি করুন তারপরে ডেটা Inোকান
sqlite3_exec(db, "CREATE INDEX 'TTC_Stop_Index' ON 'TTC' ('Stop')", NULL, NULL, &sErrMsg);
sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, &sErrMsg);
...
18.13 সেকেন্ডে 864913 রেকর্ড আমদানি করা হয়েছে
ডেটা thenোকান তারপরে সূচি তৈরি করুন
...
sqlite3_exec(db, "END TRANSACTION", NULL, NULL, &sErrMsg);
sqlite3_exec(db, "CREATE INDEX 'TTC_Stop_Index' ON 'TTC' ('Stop')", NULL, NULL, &sErrMsg);
13.66 সেকেন্ডে 864913 রেকর্ড আমদানি করা হয়েছে
যেমনটি প্রত্যাশা করা হয়েছিল, একটি কলাম ইনডেক্স করা হলে বাল্ক-সন্নিবেশগুলি ধীরে ধীরে, তবে তথ্য সন্নিবেশ করার পরে সূচীটি তৈরি করা থাকলে এটি কোনও পার্থক্য করে। আমাদের নং-সূচক বেসলাইনটি প্রতি সেকেন্ডে 96,000 সন্নিবেশ করানো হয়। প্রথমে সূচক তৈরি করা পরে ডেটা সন্নিবেশ করানো আমাদের প্রতি সেকেন্ডে 47,700 সন্নিবেশ দেয়, যেখানে প্রথমে তথ্য সন্নিবেশ করিয়ে সূচক তৈরি করে প্রতি সেকেন্ডে আমাদের 63,300 সন্নিবেশ দেয়।
আমি আনন্দের সাথে চেষ্টা করার জন্য অন্যান্য পরিস্থিতিতে পরামর্শ চাই ... এবং শীঘ্রই নির্বাচন জিজ্ঞাসার জন্য অনুরূপ ডেটা সংকলন করা হবে।
sqlite3_clear_bindings(stmt);
? আপনি প্রতিবার বাইন্ডিংগুলি সেট করেছেন যার মাধ্যমে পর্যাপ্ত হওয়া উচিত: প্রথমবারের জন্য sqlite3_step () কল করার আগে বা sqlite3_reset () এর ঠিক পরে, অ্যাপ্লিকেশনটি স্ক্রাইলাইট3_বাইন্ড () ইন্টারফেসগুলির মধ্যে একটিকে প্যারামিটারগুলিতে মান সংযুক্ত করতে পারে। Sqlite3_bind () এ প্রতিটি কল একই প্যারামিটারে পূর্বের বাইন্ডিংগুলিকে ওভাররাইড করে (দেখুন: sqlite.org/cintro.html )। আপনাকে অবশ্যই এটি কল করতে হবে বলে এই ফাংশনটির জন্য ডক্সে কিছু নেই ।
feof()
আপনার ইনপুট লুপের সমাপ্তি নিয়ন্ত্রণ করতে ব্যবহার করবেন না । দ্বারা প্রত্যাবর্তিত ফলাফল ব্যবহার করুন fgets()
। stackoverflow.com/a/15485689/827263