Use the reset API of SQLite correctly

Keywords: Database SQLite SQL

The reset interface of the SQLite database means that it resets the state object of the SQLite to its initial state and can be executed again by the bytecode virtual machine of the SQLite. The reset interface does not change the value of the state object binding. The reset interface can be called before or after the step interface. The reset interface is to re-execute the virtual machine, so in most scenarios, it is the step interface. Later calls, and reset interfaces are usually used in conjunction with bind interfaces to insert new rows. Here are two scenarios to illustrate the use of reset interfaces.

Scene 1:

static int insert_with_reset1(sqlite3 *db) {
	int rc = -1;
	sqlite3_stmt *pStmt = NULL;
	rc = sqlite3_prepare_v2(db, "insert into tl  VALUES (?1, ?2);", -1, &pStmt, 0);
	if(rc != SQLITE_OK ) {
	    goto out;
	}

	rc = sqlite3_bind_text(pStmt, 1, "Colunm 1 text value.", -1, SQLITE_STATIC);
	if(rc != SQLITE_OK) {
		goto out;
	}

	rc = sqlite3_bind_int(pStmt, 2, 23);
	if(rc != SQLITE_OK) {
		goto out;
	}

	rc = sqlite3_step(pStmt);
	assert(rc == SQLITE_DONE);

	// Reset the vdbe and insert the same row.
	rc = sqlite3_reset(pStmt);
	if(rc != SQLITE_OK) {
		goto out;
	}

	rc = sqlite3_step(pStmt);
	assert(rc == SQLITE_DONE);
	
out:
	sqlite3_finalize(pStmt);
	return rc;
}


static void reset_test1() {
	sqlite3 *db = 0;
	char *zErrMsg = 0;

	// Open database.
	int rc = sqlite3_open("mysqlite.db", &db);
	if(rc != SQLITE_OK) {
		goto out;
	}

	// Create table.
	char *create_table_cmd = "CREATE TABLE tl (info TEXT, line INT);";
	rc = sqlite3_exec(db, create_table_cmd, exec_callback, 0, &zErrMsg);
	if(rc != SQLITE_OK) {
		goto out;
	}

	// Insert.
	rc = insert_with_reset1(db);
	if(rc != SQLITE_OK) {
		goto out;
	}

	//Query
	char *query_cmd = "select * from tl;";
	rc = sqlite3_exec(db, query_cmd, exec_callback, 0, &zErrMsg);
	if(rc != SQLITE_OK) {
		goto out;
	}

	out:
	if (rc != SQLITE_OK) {
		fprintf(stderr, "SQL error: %s\n", zErrMsg);
	}
	sqlite3_free(zErrMsg);
	sqlite3_close(db);
}

In function insert_with_reset1, after the first step interface is executed, the reset is performed, and then the step interface is executed. So the state object is bound to the same value and the same row data is inserted. Of course, the contents of the rows are exactly the same here. Its ROW ID is managed by SQLite and will be updated automatically. Calling the query interface of SQLite can verify this result. Its callback function returns two lines with exactly the same content.

Scene 2:

static int insert_with_reset2(sqlite3 *db) {
	int rc = -1;
	sqlite3_stmt *pStmt = NULL;
	rc = sqlite3_prepare_v2(db, "insert into tl  VALUES (?1, ?2);", -1, &pStmt, 0);
	if(rc != SQLITE_OK ) {
	    goto out;
	}

	rc = sqlite3_bind_text(pStmt, 1, "Colunm 1 text value.", -1, SQLITE_STATIC);
	if(rc != SQLITE_OK) {
		goto out;
	}

	rc = sqlite3_bind_int(pStmt, 2, 23);
	if(rc != SQLITE_OK) {
		goto out;
	}

	rc = sqlite3_step(pStmt);
	assert(rc == SQLITE_DONE);

	rc = sqlite3_reset(pStmt);
	if(rc != SQLITE_OK) {
		goto out;
	}

	rc = sqlite3_clear_bindings(pStmt);
	if( rc!=SQLITE_OK ){
		goto out;
	}

	rc = sqlite3_bind_text(pStmt, 1, "Colunm 1 text value after reset.", -1, SQLITE_STATIC);
	if(rc != SQLITE_OK) {
		goto out;
	}

	rc = sqlite3_bind_int(pStmt, 2, 24);
	if(rc != SQLITE_OK) {
		goto out;
	}

	rc = sqlite3_step(pStmt);
	assert(rc == SQLITE_DONE);

out:
	sqlite3_finalize(pStmt);
	return rc;
}

static void reset_test2() {
	sqlite3 *db = 0;
	char *zErrMsg = 0;

	// Open database.
	int rc = sqlite3_open("mysqlite.db", &db);
	if(rc != SQLITE_OK) {
		goto out;
	}
	
	// Create table.
	char *create_table_cmd = "CREATE TABLE tl (info TEXT, line INT);";
	rc = sqlite3_exec(db, create_table_cmd, exec_callback, 0, &zErrMsg);
	if(rc != SQLITE_OK) {
		goto out;
	}

	// Insert.
	insert_with_reset2(db);

	//Query
	char *query_cmd = "select * from tl;";
	rc = sqlite3_exec(db, query_cmd, exec_callback, 0, &zErrMsg);
	if(rc != SQLITE_OK) {
		goto out;
	}

	out:
	if (rc != SQLITE_OK) {
		fprintf(stderr, "SQL error: %s\n", zErrMsg);
	}
	sqlite3_free(zErrMsg);
	sqlite3_close(db);
}

After the function insert_with_reset2 executes the first step interface, it resets, clears the value bound by the state object, rebinds the new value, and then executes the step interface again. That is, it inserts row data of different contents. Calling the query interface of SQLite can verify this result, and its callback function returns two lines with different contents.

The prepare interface of SQLite is a heavyweight operation. By performing reset and re bind operations, it can improve the execution efficiency and meet the needs of different scenarios.

Posted by lesmckeown on Sun, 27 Jan 2019 20:48:14 -0800