Procesar tabla de editoriales

Ahora que tenemos divididas las tareas, podemos empezar a codificarlas una a una. Empezaremos con las editoriales, diseñando las funciones para añadir, editar, borrar y consultar datos.

Como siempre, añadiremos dos ficheros al proyecto, uno con los prototipos de funciones y otro con la implementación.

Fichero de cabecera:

/*
 * Aplicación de ejemplo de uso de SQLite en C++
 * EjemploSQLite
 * Salvador Pozo, Con Clase (www.conclase.net)
 * Marzo de 2012
 * Fichero: editorial.h
 * fichero de cabecera para manupular datos de editoriales
 */

#ifndef __EDITORIAL_H__
#define __EDITORIAL_H__

#include <sqlite/sqlite3.h>

void NuevaEditorial(sqlite3 *);
int ListaEditoriales(sqlite3 *);
void EditarEditorial(sqlite3 *);
void BorrarEditorial(sqlite3 *);
void BuscarEditorial(sqlite3 *);
#endif

El fichero de implementación es algo más largo:

/*
 * Aplicación de ejemplo de uso de SQLite en C++
 * EjemploSQLite
 * Salvador Pozo, Con Clase (www.conclase.net)
 * Marzo de 2012
 * Fichero: editorial.cpp
 * fichero de implementación para manupular datos de editoriales
 */

#include <iostream>
#include <iomanip>
#include "editorial.h"

using namespace std;

void NuevaEditorial(sqlite3 *db) {
    char nombre[64];
    char direccion[128];
    char telefono[32];
    sqlite3_stmt *ppStmt;
    int rc;
    char consulta[1024];
    bool existe, ignorar=false;
    char nombre2[64];
    int clave;
    char resp[2];

    for(int i= 0; i < 24; i++) cout << endl;
    cout << "A continuacion se pedira el nombre, direccion y telefono de la editorial." << endl;

    cin.ignore();
    cout << "Nombre: ";
    cin.getline(nombre, 64);
    cout << "Direccion: ";
    cin.getline(direccion,128);
    cout << "Telefono: ";
    cin.getline(telefono,32);

    // Verificar si el nombre existe ya:
    sprintf(consulta, "SELECT claveeditorial,editorial FROM editorial WHERE editorial LIKE '%s';", nombre);
    rc = sqlite3_prepare_v2(db, consulta, -1, &ppStmt, NULL);
    existe=false;
    if( rc!=SQLITE_OK ){
        cout << "Error: " << sqlite3_errmsg(db) << endl;
    } else {
        if(SQLITE_ROW == sqlite3_step(ppStmt)) {
            existe = true;
            clave = sqlite3_column_int(ppStmt, 0);
            strncpy(nombre2, (const char*)sqlite3_column_text(ppStmt, 1), 64);
            nombre2[63]=0;
        }
        sqlite3_finalize(ppStmt);
    }

    if(!existe) {
        sprintf(consulta, "INSERT INTO editorial(editorial,direccion,telefono) VALUES('%s','%s','%s');",
                nombre, direccion, telefono);
    } else {
        cout << "Ya existe una editorial con el nombre " << nombre2 << " (s)obrescribir, insert(a)r o (i)gnorar: " << endl;
        cin >> resp;
        switch(resp[0]) {
            case 's':
                sprintf(consulta, "UPDATE editorial SET editorial='%s',direccion='%s',telefono='%s' WHERE claveeditorial=%d;",
                        nombre, direccion, telefono, clave);
                break;
            case 'a':
                sprintf(consulta, "INSERT INTO editorial(editorial,direccion,telefono) VALUES('%s','%s','%s');",
                        nombre, direccion, telefono);
                break;
            case 'i':
            default:
                ignorar=true;
        }
    }
    if(!ignorar) {
        if(SQLITE_OK != sqlite3_exec(db, consulta, 0, 0, 0)) {
            cout << "Error: " << sqlite3_errmsg(db) << endl;
        }
        else cout << "Editorial insertada" << endl;
    }
    cin.ignore();
}

int ListaEditoriales(sqlite3 *db) {
    sqlite3_stmt *ppStmt;
    int rc;
    char consulta[1024];
    int desplazamiento=0;
    char resp[10];
    bool salir=false;
    bool ultima;
    int fila=0;
    int i;

    // Mostrar una lista, teniendo en cuenta que puede haber más de las que caben en una pantalla.
    do {
        cout << "Elegir editorial" << endl << endl;
        sprintf(consulta, "SELECT claveeditorial,editorial FROM editorial ORDER BY nombre LIMIT 20 OFFSET %d;", desplazamiento);
        rc = sqlite3_prepare_v2(db, consulta, -1, &ppStmt, NULL);
        if( rc!=SQLITE_OK ){
            cout << "Error: " << sqlite3_errmsg(db) << endl;
        } else {
            i = 0;
            while(SQLITE_ROW == sqlite3_step(ppStmt)) {
                cout << sqlite3_column_int(ppStmt, 0) << ") " <<
                    sqlite3_column_text(ppStmt, 1) << endl;
                i++;
            }
            sqlite3_finalize(ppStmt);
        }
        ultima = (i < 20);
        while(i < 20) { cout << endl; i++; }
        cout << "\n" << "(n) editar, (s)ig pagina, (a)nt pagina, (x)salir" << endl;
        cin >> resp;
        switch(resp[0]) {
            case 's':
                if(!ultima) desplazamiento+=20;
                break;
            case 'a':
                if(desplazamiento > 0) desplazamiento-=20;
                break;
            case 'x':
                salir=true;
                break;
            default:
                if(isdigit(resp[0])) {
                    fila = atoi(resp);
                    salir=true;
                }
                break;
        }
    } while(!salir);

    return fila;
}

void EditarEditorial(sqlite3 *db) {
    sqlite3_stmt *ppStmt;
    int rc;
    char consulta[1024];
    char nombre[64];
    char direccion[128];
    char telefono[32];
    int i;
    int fila;
    bool salir=true;

    fila = ListaEditoriales(db);

    // Editar:
    for(i = 0; i < 22; i++) cout << endl;
    sprintf(consulta, "SELECT editorial,direccion,telefono FROM editorial WHERE claveeditorial='%d';", fila);
    rc = sqlite3_prepare_v2(db, consulta, -1, &ppStmt, NULL);
    if( rc!=SQLITE_OK ){
        cout << "Error: " << sqlite3_errmsg(db) << endl;
    } else {
        i = 0;
        if(SQLITE_ROW == sqlite3_step(ppStmt)) {
            cout << "Nombre:    " << sqlite3_column_text(ppStmt, 0) << endl;
            cout << "Direccion: " << sqlite3_column_text(ppStmt, 1) << endl;
            cout << "Telefono:  " << sqlite3_column_text(ppStmt, 2) << endl;
            cout << "Dejar en blanco los campos que no se quieren modifiar" << endl;
            salir=false;
        }
        sqlite3_finalize(ppStmt);
    }
    if(!salir){
        cin.ignore();
        cout << "Nombre: ";
        cin.getline(nombre, 64);
        cout << "Direccion: ";
        cin.getline(direccion,128);
        cout << "Telefono: ";
        cin.getline(telefono,32);
        if(strlen(nombre)>0) {
            sprintf(consulta, "UPDATE editorial SET editorial='%s' WHERE claveeditorial=%d;", nombre, fila);
            if(SQLITE_OK != sqlite3_exec(db, consulta, 0, 0, 0)) {
                cout << "Error: " << sqlite3_errmsg(db) << endl;
            }
        }
        if(strlen(direccion)>0) {
            sprintf(consulta, "UPDATE editorial SET direccion='%s' WHERE claveeditorial=%d;", direccion, fila);
            if(SQLITE_OK != sqlite3_exec(db, consulta, 0, 0, 0)) {
                cout << "Error: " << sqlite3_errmsg(db) << endl;
            }
        }
        if(strlen(telefono)>0) {
            sprintf(consulta, "UPDATE editorial SET telefono='%s' WHERE claveeditorial=%d;", telefono, fila);
            if(SQLITE_OK != sqlite3_exec(db, consulta, 0, 0, 0)) {
                cout << "Error: " << sqlite3_errmsg(db) << endl;
            }
        }
        cout << "Editorial modificada" << endl;
        cin.ignore();
    }
}

void BorrarEditorial(sqlite3 *db) {
    sqlite3_stmt *ppStmt;
    int rc;
    char consulta[1024];
    int fila;
    char resp[2];
    int i;
    bool salir=true;

    fila = ListaEditoriales(db);
    cout << "Borrar: " << fila << endl;

    for(i = 0; i < 22; i++) cout << endl;
    sprintf(consulta, "SELECT editorial,direccion,telefono FROM editorial WHERE claveeditorial='%d';", fila);
    rc = sqlite3_prepare_v2(db, consulta, -1, &ppStmt, NULL);
    if( rc!=SQLITE_OK ){
        cout << "Error: " << sqlite3_errmsg(db) << endl;
    } else {
        i = 0;
        if(SQLITE_ROW == sqlite3_step(ppStmt)) {
            cout << "Nombre:    " << sqlite3_column_text(ppStmt, 0) << endl;
            cout << "Direccion: " << sqlite3_column_text(ppStmt, 1) << endl;
            cout << "Telefono:  " << sqlite3_column_text(ppStmt, 2) << endl;
            salir=false;
        }
        sqlite3_finalize(ppStmt);
    }
    if(!salir){
        cin.ignore();
        cout << "Borrar este registro? (s/n)" << endl;
        cin >> resp;
        if(resp[0] == 's' || resp[0] == 'S') {
            sprintf(consulta, "DELETE FROM editorial WHERE claveeditorial=%d;", fila);
            if(SQLITE_OK != sqlite3_exec(db, consulta, 0, 0, 0)) {
                cout << "Error: " << sqlite3_errmsg(db) << endl;
            }
        }
    }
    cout << "Editorial borrada" << endl;
    cin.ignore();
}

void BuscarEditorial(sqlite3 *db) {
    sqlite3_stmt *ppStmt;
    int rc;
    char consulta[1024];
    char nombre[64];
    char direccion[128];
    char telefono[32];
    int i;

    for(i = 0; i < 22; i++) cout << endl;
    // Búsqueda de editoriales por nombre, direccion o telefono:
    cout << "Introducir cadenas de busqueda, _ para comodin de caracter, % para comodin de cadena" << endl;
    cout << "Dejar en blanco para ignorar el campo en la busqueda" << endl;
    cin.ignore();
    cout << "Nombre: ";
    cin.getline(nombre, 64);
    cout << "Direccion: ";
    cin.getline(direccion,128);
    cout << "Telefono: ";
    cin.getline(telefono,32);

    if(strlen(nombre) == 0) strcpy(nombre, "%");
    if(strlen(direccion) == 0) strcpy(direccion, "%");
    if(strlen(telefono) == 0) strcpy(telefono, "%");
    sprintf(consulta, "SELECT editorial,direccion,telefono FROM editorial "
            "WHERE editorial LIKE '%s' AND direccion LIKE '%s' AND telefono LIKE '%s';",
            nombre, direccion, telefono);
    rc = sqlite3_prepare_v2(db, consulta, -1, &ppStmt, NULL);
    if( rc!=SQLITE_OK ){
        cout << "Error: " << sqlite3_errmsg(db) << endl;
    } else {
        i = 0;
        while(SQLITE_ROW == sqlite3_step(ppStmt)) {
            cout.setf(ios::left);
            cout.width(64);
            cout << sqlite3_column_text(ppStmt, 0) << endl;
            cout << sqlite3_column_text(ppStmt, 1) << " ";
            cout.width(12);
            cout << sqlite3_column_text(ppStmt, 2) << endl;
            i +=2;
            if(!(i % 22)) {
                cout << "Pulsa return";
                cin.ignore();
                cin.get();
            }
        }
        sqlite3_finalize(ppStmt);
    }
    cin.ignore();
    cin.get();
}

Por último, modificamos el fichero "main.cpp", añadiendo un include para el nuevo fichero de cabecera, y modificando el bucle de procesamiento de menú:

/*
 * Aplicación de ejemplo de uso de SQLite en C++
 * EjemploSQLite
 * Salvador Pozo, Con Clase (www.conclase.net)
 * Marzo de 2012
 * Fichero: main.cpp
 * fichero principal
 * Incluir en el enlazador la librería libsqlite.a (sqlite)
 * Facilitar el acceso a la librería de enlace dinámico sqlite3.dll
 */

#include <iostream>
#include <sqlite/sqlite3.h>
#include "menus.h"
#include "editorial.h"

const int nTablas = 9;

bool VerificarTablas(sqlite3 *);

using namespace std;

int main()
{
    int rc;
    sqlite3 *db;
    int nivel=1;
    bool salir;

    // Abrir base de datos
    rc = sqlite3_open("biblioteca.db", &db);
    if(SQLITE_OK != rc) {
        cout << "Error: No se puede abrir la base de datos" << endl;
		return 1;
    }

    if(!VerificarTablas(db)) return -1;
    if(!IniciarMenu(db)) return -1;

    do {
        MostrarMenu(db, nivel);
        switch(LeerMenu(db, nivel)) {
            case ABRIRMENU:
               // Nada que hacer.
               break;
            case NUEVAEDITORIAL:
               NuevaEditorial(db);
               break;
            case EDITAREDITORIAL:
               EditarEditorial(db);
               break;
            case BORRAREDITORIAL:
               BorrarEditorial(db);
               break;
            case CONSULTAEDITORIAL:
               BuscarEditorial(db);
               break;
            case SALIR:
               salir = true;
        }
    } while(!salir);

    // Cerrar base de datos
    sqlite3_close(db);
    return 0;
}
...

Procesar tablas de autores y temas

Las funciones para el tratamiento de las tablas de autores y temas son similares, y se pueden adaptar de la de editoriales con muy pocos cambios.

Fichero 'autor.h':

/*
 * Aplicación de ejemplo de uso de SQLite en C++
 * EjemploSQLite
 * Salvador Pozo, Con Clase (www.conclase.net)
 * Marzo de 2012
 * Fichero: autor.h
 * fichero de cabecera para manupular datos de autores
 */

#ifndef __AUTOR_H__
#define __AUTOR_H__

#include <sqlite/sqlite3.h>

void NuevoAutor(sqlite3 *);
int ListaAutores(sqlite3 *);
void EditarAutor(sqlite3 *);
void BorrarAutor(sqlite3 *);
void BuscarAutor(sqlite3 *);
#endif

Fichero 'tema.h':

/*
 * Aplicación de ejemplo de uso de SQLite en C++
 * EjemploSQLite
 * Salvador Pozo, Con Clase (www.conclase.net)
 * Marzo de 2012
 * Fichero: tema.h
 * fichero de cabecera para manupular datos de temas
 */

#ifndef __TEMA_H__
#define __TEMA_H__

#include <sqlite/sqlite3.h>

void NuevoTema(sqlite3 *);
int ListaTemnas(sqlite3 *);
void EditarTema(sqlite3 *);
void BorrarTema(sqlite3 *);
void BuscarTema(sqlite3 *);
#endif

Fichero 'autor.cpp':

/*
 * Aplicación de ejemplo de uso de SQLite en C++
 * EjemploSQLite
 * Salvador Pozo, Con Clase (www.conclase.net)
 * Marzo de 2012
 * Fichero: autor.cpp
 * fichero de implementación para manupular datos de autores
 */

#include <iostream>
#include <iomanip>
#include "autor.h"

using namespace std;

void NuevoAutor(sqlite3 *db) {
    char nombre[64];
    sqlite3_stmt *ppStmt;
    int rc;
    char consulta[1024];
    bool existe, ignorar=false;
    char nombre2[64];
    int clave;
    char resp[2];

    for(int i= 0; i < 24; i++) cout << endl;
    cout << "A continuacion se pedira el nombre, del autor." << endl;

    cin.ignore();
    cout << "Nombre: ";
    cin.getline(nombre, 64);

    // Verificar si el nombre existe ya:
    sprintf(consulta, "SELECT claveautor,autor FROM autor WHERE autor LIKE '%s';", nombre);
    rc = sqlite3_prepare_v2(db, consulta, -1, &ppStmt, NULL);
    existe=false;
    if( rc!=SQLITE_OK ){
        cout << "Error: " << sqlite3_errmsg(db) << endl;
    } else {
        if(SQLITE_ROW == sqlite3_step(ppStmt)) {
            existe = true;
            clave = sqlite3_column_int(ppStmt, 0);
            strncpy(nombre2, (const char*)sqlite3_column_text(ppStmt, 1), 64);
            nombre2[63]=0;
        }
        sqlite3_finalize(ppStmt);
    }

    if(!existe) {
        sprintf(consulta, "INSERT INTO autor(autor) VALUES('%s');", nombre);
    } else {
        cout << "Ya existe un autor con el nombre " << nombre2 << " (s)obrescribir, insert(a)r o (i)gnorar: " << endl;
        cin >> resp;
        switch(resp[0]) {
            case 's':
                sprintf(consulta, "UPDATE autor SET autor='%s' WHERE claveautor=%d;", nombre,  clave);
                break;
            case 'a':
                sprintf(consulta, "INSERT INTO autor(autor) VALUES('%s');", nombre);
                break;
            case 'i':
            default:
                ignorar=true;
        }
    }
    if(!ignorar) {
        if(SQLITE_OK != sqlite3_exec(db, consulta, 0, 0, 0)) {
            cout << "Error: " << sqlite3_errmsg(db) << endl;
        }
        else cout << "Autor insertado" << endl;
    }
    cin.ignore();
}

int ListaAutores(sqlite3 *db) {
    sqlite3_stmt *ppStmt;
    int rc;
    char consulta[1024];
    int desplazamiento=0;
    char resp[10];
    bool salir=false;
    bool ultima;
    int fila=0;
    int i;

    // Mostrar una lista, teniendo en cuenta que puede haber más de las que caben en una pantalla.
    do {
        cout << "Elegir autor" << endl << endl;
        sprintf(consulta, "SELECT claveautor,autor FROM autor ORDER BY autor LIMIT 20 OFFSET %d;", desplazamiento);
        rc = sqlite3_prepare_v2(db, consulta, -1, &ppStmt, NULL);
        if( rc!=SQLITE_OK ){
            cout << "Error: " << sqlite3_errmsg(db) << endl;
        } else {
            i = 0;
            while(SQLITE_ROW == sqlite3_step(ppStmt)) {
                cout << sqlite3_column_int(ppStmt, 0) << ") " <<
                    sqlite3_column_text(ppStmt, 1) << endl;
                i++;
            }
            sqlite3_finalize(ppStmt);
        }
        ultima = (i < 20);
        while(i < 20) { cout << endl; i++; }
        cout << "\n" << "(n) editar, (s)ig pagina, (a)nt pagina, (x)salir" << endl;
        cin >> resp;
        switch(resp[0]) {
            case 's':
                if(!ultima) desplazamiento+=20;
                break;
            case 'a':
                if(desplazamiento > 0) desplazamiento-=20;
                break;
            case 'x':
                salir=true;
                break;
            default:
                if(isdigit(resp[0])) {
                    fila = atoi(resp);
                    salir=true;
                }
                break;
        }
    } while(!salir);

    return fila;
}

void EditarAutor(sqlite3 *db) {
    sqlite3_stmt *ppStmt;
    int rc;
    char consulta[1024];
    char nombre[64];
    int i;
    int fila;
    bool salir=true;

    fila = ListaAutores(db);

    // Editar:
    for(i = 0; i < 22; i++) cout << endl;
    sprintf(consulta, "SELECT autor FROM autor WHERE claveautor='%d';", fila);
    rc = sqlite3_prepare_v2(db, consulta, -1, &ppStmt, NULL);
    if( rc!=SQLITE_OK ){
        cout << "Error: " << sqlite3_errmsg(db) << endl;
    } else {
        i = 0;
        if(SQLITE_ROW == sqlite3_step(ppStmt)) {
            cout << "Nombre:    " << sqlite3_column_text(ppStmt, 0) << endl;
            cout << "Dejar en blanco los campos que no se quieren modifiar" << endl;
            salir=false;
        }
        sqlite3_finalize(ppStmt);
    }
    if(!salir){
        cin.ignore();
        cout << "Nombre: ";
        cin.getline(nombre, 64);
        if(strlen(nombre)>0) {
            sprintf(consulta, "UPDATE autor SET autor='%s' WHERE claveautor=%d;", nombre, fila);
            if(SQLITE_OK != sqlite3_exec(db, consulta, 0, 0, 0)) {
                cout << "Error: " << sqlite3_errmsg(db) << endl;
            }
        }
        cout << "Autor modificado" << endl;
        cin.ignore();
    }
}

void BorrarAutor(sqlite3 *db) {
    sqlite3_stmt *ppStmt;
    int rc;
    char consulta[1024];
    int fila;
    char resp[2];
    int i;
    bool salir=true;

    fila = ListaAutores(db);
    cout << "Borrar: " << fila << endl;

    for(i = 0; i < 22; i++) cout << endl;
    sprintf(consulta, "SELECT autor FROM autor WHERE claveautor='%d';", fila);
    rc = sqlite3_prepare_v2(db, consulta, -1, &ppStmt, NULL);
    if( rc!=SQLITE_OK ){
        cout << "Error: " << sqlite3_errmsg(db) << endl;
    } else {
        i = 0;
        if(SQLITE_ROW == sqlite3_step(ppStmt)) {
            cout << "Nombre:    " << sqlite3_column_text(ppStmt, 0) << endl;
            salir=false;
        }
        sqlite3_finalize(ppStmt);
    }
    if(!salir){
        cin.ignore();
        cout << "Borrar este registro? (s/n)" << endl;
        cin >> resp;
        if(resp[0] == 's' || resp[0] == 'S') {
            sprintf(consulta, "DELETE FROM autor WHERE claveautor=%d;", fila);
            if(SQLITE_OK != sqlite3_exec(db, consulta, 0, 0, 0)) {
                cout << "Error: " << sqlite3_errmsg(db) << endl;
            }
        }
    }
    cout << "Autor borrado" << endl;
    cin.ignore();
}

void BuscarAutor(sqlite3 *db) {
    sqlite3_stmt *ppStmt;
    int rc;
    char consulta[1024];
    char nombre[64];
    int i;

    for(i = 0; i < 22; i++) cout << endl;
    // Búsqueda de editoriales por nombre, direccion o telefono:
    cout << "Introducir cadenas de busqueda, _ para comodin de caracter, % para comodin de cadena" << endl;
    cout << "Dejar en blanco para ignorar el campo en la busqueda" << endl;
    cin.ignore();
    cout << "Nombre: ";
    cin.getline(nombre, 64);

    if(strlen(nombre) == 0) strcpy(nombre, "%");
    sprintf(consulta, "SELECT autor FROM autor WHERE autor LIKE '%s';", nombre);
    rc = sqlite3_prepare_v2(db, consulta, -1, &ppStmt, NULL);
    if( rc!=SQLITE_OK ){
        cout << "Error: " << sqlite3_errmsg(db) << endl;
    } else {
        i = 0;
        while(SQLITE_ROW == sqlite3_step(ppStmt)) {
            cout.setf(ios::left);
            cout.width(64);
            cout << sqlite3_column_text(ppStmt, 0) << endl;
            i++;
            if(!(i % 22)) {
                cout << "Pulsa return";
                cin.ignore();
                cin.get();
            }
        }
        sqlite3_finalize(ppStmt);
    }
    cin.ignore();
    cin.get();
}

Fichero 'tema.cpp':

/*
 * Aplicación de ejemplo de uso de SQLite en C++
 * EjemploSQLite
 * Salvador Pozo, Con Clase (www.conclase.net)
 * Marzo de 2012
 * Fichero: tema.cpp
 * fichero de implementación para manupular datos de temas
 */

#include <iostream>
#include <iomanip>
#include "tema.h"

using namespace std;

void NuevoTema(sqlite3 *db) {
    char nombre[64];
    sqlite3_stmt *ppStmt;
    int rc;
    char consulta[1024];
    bool existe, ignorar=false;
    char nombre2[64];
    int clave;
    char resp[2];

    for(int i= 0; i < 24; i++) cout << endl;
    cout << "A continuacion se pedira el nombre del tema." << endl;

    cin.ignore();
    cout << "Nombre: ";
    cin.getline(nombre, 64);

    // Verificar si el nombre existe ya:
    sprintf(consulta, "SELECT clavetema,tema FROM tema WHERE tema LIKE '%s';", nombre);
    rc = sqlite3_prepare_v2(db, consulta, -1, &ppStmt, NULL);
    existe=false;
    if( rc!=SQLITE_OK ){
        cout << "Error: " << sqlite3_errmsg(db) << endl;
    } else {
        if(SQLITE_ROW == sqlite3_step(ppStmt)) {
            existe = true;
            clave = sqlite3_column_int(ppStmt, 0);
            strncpy(nombre2, (const char*)sqlite3_column_text(ppStmt, 1), 64);
            nombre2[63]=0;
        }
        sqlite3_finalize(ppStmt);
    }

    if(!existe) {
        sprintf(consulta, "INSERT INTO tema(tema) VALUES('%s');", nombre);
    } else {
        cout << "Ya existe un tema con el nombre " << nombre2 << " (s)obrescribir, insert(a)r o (i)gnorar: " << endl;
        cin >> resp;
        switch(resp[0]) {
            case 's':
                sprintf(consulta, "UPDATE tema SET tema='%s' WHERE clavetema=%d;", nombre,  clave);
                break;
            case 'a':
                sprintf(consulta, "INSERT INTO tema(tema) VALUES('%s');", nombre);
                break;
            case 'i':
            default:
                ignorar=true;
        }
    }
    if(!ignorar) {
        if(SQLITE_OK != sqlite3_exec(db, consulta, 0, 0, 0)) {
            cout << "Error: " << sqlite3_errmsg(db) << endl;
        }
        else cout << "Tema insertado" << endl;
    }
    cin.ignore();
}

int ListaTemas(sqlite3 *db) {
    sqlite3_stmt *ppStmt;
    int rc;
    char consulta[1024];
    int desplazamiento=0;
    char resp[10];
    bool salir=false;
    bool ultima;
    int fila=0;
    int i;

    // Mostrar una lista, teniendo en cuenta que puede haber más de las que caben en una pantalla.
    do {
        cout << "Elegir tema" << endl << endl;
        sprintf(consulta, "SELECT clavetema,tema FROM tema ORDER BY tema LIMIT 20 OFFSET %d;", desplazamiento);
        rc = sqlite3_prepare_v2(db, consulta, -1, &ppStmt, NULL);
        if( rc!=SQLITE_OK ){
            cout << "Error: " << sqlite3_errmsg(db) << endl;
        } else {
            i = 0;
            while(SQLITE_ROW == sqlite3_step(ppStmt)) {
                cout << sqlite3_column_int(ppStmt, 0) << ") " <<
                    sqlite3_column_text(ppStmt, 1) << endl;
                i++;
            }
            sqlite3_finalize(ppStmt);
        }
        ultima = (i < 20);
        while(i < 20) { cout << endl; i++; }
        cout << "\n" << "(n) editar, (s)ig pagina, (a)nt pagina, (x)salir" << endl;
        cin >> resp;
        switch(resp[0]) {
            case 's':
                if(!ultima) desplazamiento+=20;
                break;
            case 'a':
                if(desplazamiento > 0) desplazamiento-=20;
                break;
            case 'x':
                salir=true;
                break;
            default:
                if(isdigit(resp[0])) {
                    fila = atoi(resp);
                    salir=true;
                }
                break;
        }
    } while(!salir);

    return fila;
}

void EditarTema(sqlite3 *db) {
    sqlite3_stmt *ppStmt;
    int rc;
    char consulta[1024];
    char nombre[64];
    int i;
    int fila;
    bool salir=true;

    fila = ListaTemas(db);

    // Editar:
    for(i = 0; i < 22; i++) cout << endl;
    sprintf(consulta, "SELECT tema FROM tema WHERE clavetema='%d';", fila);
    rc = sqlite3_prepare_v2(db, consulta, -1, &ppStmt, NULL);
    if( rc!=SQLITE_OK ){
        cout << "Error: " << sqlite3_errmsg(db) << endl;
    } else {
        i = 0;
        if(SQLITE_ROW == sqlite3_step(ppStmt)) {
            cout << "Nombre:    " << sqlite3_column_text(ppStmt, 0) << endl;
            cout << "Dejar en blanco los campos que no se quieren modifiar" << endl;
            salir=false;
        }
        sqlite3_finalize(ppStmt);
    }
    if(!salir){
        cin.ignore();
        cout << "Nombre: ";
        cin.getline(nombre, 64);
        if(strlen(nombre)>0) {
            sprintf(consulta, "UPDATE tema SET tema='%s' WHERE clavetema=%d;", nombre, fila);
            if(SQLITE_OK != sqlite3_exec(db, consulta, 0, 0, 0)) {
                cout << "Error: " << sqlite3_errmsg(db) << endl;
            }
        }
        cout << "Tema modificado" << endl;
        cin.ignore();
    }
}

void BorrarTema(sqlite3 *db) {
    sqlite3_stmt *ppStmt;
    int rc;
    char consulta[1024];
    int fila;
    char resp[2];
    int i;
    bool salir=true;

    fila = ListaTemas(db);
    cout << "Borrar: " << fila << endl;

    for(i = 0; i < 22; i++) cout << endl;
    sprintf(consulta, "SELECT tema FROM tema WHERE clavetema='%d';", fila);
    rc = sqlite3_prepare_v2(db, consulta, -1, &ppStmt, NULL);
    if( rc!=SQLITE_OK ){
        cout << "Error: " << sqlite3_errmsg(db) << endl;
    } else {
        i = 0;
        if(SQLITE_ROW == sqlite3_step(ppStmt)) {
            cout << "Nombre:    " << sqlite3_column_text(ppStmt, 0) << endl;
            salir=false;
        }
        sqlite3_finalize(ppStmt);
    }
    if(!salir){
        cin.ignore();
        cout << "Borrar este registro? (s/n)" << endl;
        cin >> resp;
        if(resp[0] == 's' || resp[0] == 'S') {
            sprintf(consulta, "DELETE FROM tema WHERE clavetema=%d;", fila);
            if(SQLITE_OK != sqlite3_exec(db, consulta, 0, 0, 0)) {
                cout << "Error: " << sqlite3_errmsg(db) << endl;
            }
        }
    }
    cout << "Tema borrado" << endl;
    cin.ignore();
}

void BuscarTema(sqlite3 *db) {
    sqlite3_stmt *ppStmt;
    int rc;
    char consulta[1024];
    char nombre[64];
    int i;

    for(i = 0; i < 22; i++) cout << endl;
    // Búsqueda de editoriales por nombre, direccion o telefono:
    cout << "Introducir cadenas de busqueda, _ para comodin de caracter, % para comodin de cadena" << endl;
    cout << "Dejar en blanco para ignorar el campo en la busqueda" << endl;
    cin.ignore();
    cout << "Nombre: ";
    cin.getline(nombre, 64);

    if(strlen(nombre) == 0) strcpy(nombre, "%");
    sprintf(consulta, "SELECT tema FROM tema WHERE tema LIKE '%s';", nombre);
    rc = sqlite3_prepare_v2(db, consulta, -1, &ppStmt, NULL);
    if( rc!=SQLITE_OK ){
        cout << "Error: " << sqlite3_errmsg(db) << endl;
    } else {
        i = 0;
        while(SQLITE_ROW == sqlite3_step(ppStmt)) {
            cout.setf(ios::left);
            cout.width(64);
            cout << sqlite3_column_text(ppStmt, 0) << endl;
            i++;
            if(!(i % 22)) {
                cout << "Pulsa return";
                cin.ignore();
                cin.get();
            }
        }
        sqlite3_finalize(ppStmt);
    }
    cin.ignore();
    cin.get();
}

En 'menus.h' añadimos la definición de las macros para las nuevas opciones de menú:

#define NUEVOAUTOR          6
#define EDITARAUTOR         7
#define BORRARAUTOR         8
#define CONSULTAAUTOR       9
#define NUEVOTEMA           10
#define EDITARTEMA          11
#define BORRARTEMA          12
#define CONSULTATEMA        13

En 'menus.cpp' añadimos los valores del array de menú para las nuevas opciones:

    {7,"-","---MENU AUTORES---",0,TITULO},
    {7, "1", "Nuevo", 0, NUEVOAUTOR},
    {7, "2", "Editar", 0, EDITARAUTOR},
    {7, "3", "Borrar", 0, BORRARAUTOR},
    {7, "4", "Consultar", 0, CONSULTAAUTOR},
    {7, "0", "Salir <", 2, ABRIRMENU},
    {8,"-","---MENU TEMAS---",0,TITULO},
    {8, "1", "Nuevo", 0, NUEVOTEMA},
    {8, "2", "Editar", 0, EDITARTEMA},
    {8, "3", "Borrar", 0, BORRARTEMA},
    {8, "4", "Consultar", 0, CONSULTATEMA},
    {8, "0", "Salir <", 2, ABRIRMENU}

Finalmente, modificamos el fichero 'main.cpp' para añadir los ficheros de cabecera y los 'case' para procesar las nuevas opciones:

/*
 * Aplicación de ejemplo de uso de SQLite en C++
 * EjemploSQLite
 * Salvador Pozo, Con Clase (www.conclase.net)
 * Marzo de 2012
 * Fichero: main.cpp
 * fichero principal
 * Incluir en el enlazador la librería libsqlite.a (sqlite)
 * Facilitar el acceso a la librería de enlace dinámico sqlite3.dll
 */

#include <iostream>
#include <sqlite/sqlite3.h>
#include "menus.h"
#include "editorial.h"
#include "autor.h"
#include "tema.h"

const int nTablas = 9;

bool VerificarTablas(sqlite3 *);

using namespace std;

int main()
{
    int rc;
    sqlite3 *db;
    int nivel=1;
    bool salir;

    // Abrir base de datos
    rc = sqlite3_open("biblioteca.db", &db);
    if(SQLITE_OK != rc) {
        cout << "Error: No se puede abrir la base de datos" << endl;
		return 1;
    }

    if(!VerificarTablas(db)) return -1;
    if(!IniciarMenu(db)) return -1;

    do {
        MostrarMenu(db, nivel);
        switch(LeerMenu(db, nivel)) {
            case ABRIRMENU:
               // Nada que hacer.
               break;
            case NUEVAEDITORIAL:
               NuevaEditorial(db);
               break;
            case EDITAREDITORIAL:
               EditarEditorial(db);
               break;
            case BORRAREDITORIAL:
               BorrarEditorial(db);
               break;
            case CONSULTAEDITORIAL:
               BuscarEditorial(db);
               break;
            case NUEVOAUTOR:
               NuevoAutor(db);
               break;
            case EDITARAUTOR:
               EditarAutor(db);
               break;
            case BORRARAUTOR:
               BorrarAutor(db);
               break;
            case CONSULTAAUTOR:
               BuscarAutor(db);
               break;
            case NUEVOTEMA:
               NuevoTema(db);
               break;
            case EDITARTEMA:
               EditarTema(db);
               break;
            case BORRARTEMA:
               BorrarTema(db);
               break;
            case CONSULTATEMA:
               BuscarTema(db);
               break;
            case SALIR:
               salir = true;
        }
    } while(!salir);

    // Cerrar base de datos
    sqlite3_close(db);
    return 0;
}
...