2011-06-09 30 views
6

Estoy usando sqlite3 en C y me gustaría agregar soporte para el operador REGEXP. De forma predeterminada, una función definida por el usuario regexp() no está presente y llamar al REGEXP generalmente producirá un error (según las páginas SQLite).SQLite en C y compatible con REGEXP

  1. ¿Cómo agrego una función regexp para apoyar REGEXP? Presumiblemente, haré esto a través de la llamada sqlite3_create_function, pero no sé cómo se verá el regexp() definido por la aplicación.

  2. ¿Puedo utilizar una función desde regex.h con sqlite3_create_function y cómo? Cualquier función que paso a SQLite tiene que tomar tres argumentos de tipo sqlite3_context *, int, sqlite3_value **. Sin embargo, los documentos SQLite no parecen explicar el significado de estos parámetros.

  3. ¿Hay código de ejemplo para una función Cregexp()?

No he podido encontrar mucho en esto usando Google o las páginas de SQLite.

+0

se explica la función de la materia usuario aquí: http: //www.sqlite .org/c3ref/create_function.html –

+0

@mu: Gracias, fue el primer lugar al que fui. Mientras estás aquí, ¿qué valores enteros puede devolver una función de usuario? ¿O deben ser nulas? – SK9

+1

Todos los prototipos tienen retornos 'vacíos', así que presumiblemente hay alguna manera de informar los valores de retorno "reales" a través del parámetro 'sqlite3_context'. –

Respuesta

3

También puede probar esto:

#include <regex.h> 

...

void sqlite_regexp(sqlite3_context* context, int argc, sqlite3_value** values) { 
    int ret; 
    regex_t regex; 
    char* reg = (char*)sqlite3_value_text(values[0]); 
    char* text = (char*)sqlite3_value_text(values[1]); 

    if (argc != 2 || reg == 0 || text == 0) { 
     sqlite3_result_error(context, "SQL function regexp() called with invalid arguments.\n", -1); 
     return; 
    } 

    ret = regcomp(&regex, reg, REG_EXTENDED | REG_NOSUB); 
    if (ret != 0) { 
     sqlite3_result_error(context, "error compiling regular expression", -1); 
     return; 
    } 

    ret = regexec(&regex, text , 0, NULL, 0); 
    regfree(&regex); 

    sqlite3_result_int(context, (ret != REG_NOMATCH)); 
} 

...

sqlite3_create_function(*db, "regexp", 2, SQLITE_ANY,0, &sqlite_regexp,0,0) 
1

Se vería algo como esto:

static void user_regexp(sqlite3_context *context, int argc, sqlite3_value **argv) 
{ 
    struct re_pattern_buffer buffer; 
    const char *out; 
    char *pattern; 
    char *input_string; 
    char *result; 
    struct re_registers regs; 

    if ((sqlite3_value_type(argv[0]) != SQLITE_TEXT) 
     || ((sqlite3_value_type(argv[1]) != SQLITE_TEXT)) 
    { 
     sqlite3_result_err("Improper argument types"); 
     return; 
    } 

    re_set_syntax(RE_SYNTAX_POSIX_EGREP); 
    memset(&buffer, 0, sizeof (buffer)); 
    if (!(pattern = strdupa(sqlite3_value_text(argv[0]))) 
     || !(input_string = strdupa(sqlite3_value_text(argv[1])))) 
    { 
     sqlite3_result_err_nomem("Could not allocate memory for strings"); 
     return; 
    } 

    if ((out = re_compile_pattern(pattern, strlen(pattern), &buffer)) 
    { 
     sqlite3_result_err("Could not compile pattern!"); 
     return; 
    } 

    if (re_match(&buffer, input_string, strlen(input_string), 0, &regs) < 0) 
     sqlite3_result_int64(context, 0); 
    else 
    { 
     result = strndupa(input_string + regs.start[0], regs.end[0] - regs.start[0]);  
     sqlite3_result_text(context, result, NULL, SQLITE_TRANSIENT); 
    } 
} 
0

Bien, un poco demasiado tarde para esto, pero estoy tentado de publicar esto para todas las personas que están usando un Contenedor C++ para la API C SQLITE como el [ SQLiteCpp ] que estoy usando. Esta respuesta asume que usa SQLiteCpp.

  • Instalar expresión regular para las ventanas binarios de [ here ]. Esto le proporciona los archivos suficientes, es decir, el archivo de inclusión regex.h y regex2.dll. Recuerde agregar la ruta regex.h en su proyecto y tener una copia de la DLL en la carpeta que contiene los archivos ejecutables del cliente.
  • Antes de compilar el [ SQLiteCpp ], debemos hacer algunos cambios para agregar las capacidades de expresiones regulares a las consultas SELECT. Para ello, abra el archivo Database.cpp del proyecto [ SQLiteCpp ] y

    • Incluir el encabezado regex.h del expresión regular para las ventanas
    • Después de toda la incluye, añadir a continuación (por supuesto se puede personalizar con la costumbre ajuste a sus necesidades !) pieza de código justo debajo de ella.

      extern "C" { 
      void sqlite_regexp(sqlite3_context* context, int argc, sqlite3_value** values) { 
      int ret; 
      regex_t regex; 
      char regtext[100]; 
      char* reg = (char*)sqlite3_value_text(values[0]); 
      sprintf(regtext, ".*%s.*", reg); 
      //printf("Regtext : %s", regtext); 
      char* text = (char*)sqlite3_value_text(values[1]); 
      /* printf("Text : %s\n", text); 
      printf("Reg : %s\n", reg); */ 
      if (argc != 2 || reg == 0 || text == 0) { 
          sqlite3_result_error(context, "SQL function regexp() called with invalid arguments.\n", -1); 
          return; 
      } 
      
      ret = regcomp(&regex, regtext, REG_EXTENDED | REG_NOSUB | REG_ICASE); 
      if (ret != 0) { 
          sqlite3_result_error(context, "error compiling regular expression", -1); 
          return; 
      } 
      
      ret = regexec(&regex, text, 0, NULL, 0); 
      /* if (ret == 0) { 
      printf("Found a match. Press any key to continue"); 
      getc(stdin); 
      }*/ 
      regfree(&regex); 
      
      sqlite3_result_int(context, (ret != REG_NOMATCH)); 
      } 
      } 
      
    • Ahora es el momento de cambiar los constructores definidos en el archivo. Cambie los que se muestran a continuación.

      // Open the provided database UTF-8 filename with SQLite::OPEN_xxx provided flags. 
      Database::Database(const char* apFilename, 
      const int aFlags /*= SQLite::OPEN_READONLY*/, 
      const int aBusyTimeoutMs/* = 0 */, 
      const char* apVfs/*= NULL*/) : 
      mpSQLite(NULL), 
      mFilename(apFilename) 
      { 
      const int ret = sqlite3_open_v2(apFilename, &mpSQLite, aFlags, apVfs); 
      //std::cout << "Reached here"; 
      //sqlite3_create_function_v2(mpSQLite, "REGEXP", 2, SQLITE_ANY,&sqlite_regexp, NULL, NULL, NULL,NULL); 
      sqlite3_create_function(mpSQLite, "regexp", 2, SQLITE_ANY, 0, &sqlite_regexp, 0, 0); 
      if (SQLITE_OK != ret) 
      { 
      const SQLite::Exception exception(mpSQLite, ret); // must create before closing 
      sqlite3_close(mpSQLite); // close is required even in case of error on opening 
      throw exception; 
      } 
      else { 
      
      } 
      if (aBusyTimeoutMs > 0) 
      { 
      setBusyTimeout(aBusyTimeoutMs); 
      } 
      } 
      
      // Open the provided database UTF-8 filename with SQLite::OPEN_xxx provided flags. 
      Database::Database(const std::string& aFilename, 
      const int   aFlags  /* = SQLite::OPEN_READONLY*/, 
      const int   aBusyTimeoutMs/* = 0*/, 
      const std::string& aVfs/* = "" */) : 
      mpSQLite(NULL), 
      mFilename(aFilename) 
      { 
      
      const int ret = sqlite3_open_v2(aFilename.c_str(), &mpSQLite, aFlags, aVfs.empty() ? NULL : aVfs.c_str()); 
      sqlite3_create_function(mpSQLite, "regexp", 2, SQLITE_ANY, 0, &sqlite_regexp, 0, 0); 
      if (SQLITE_OK != ret) 
      { 
      const SQLite::Exception exception(mpSQLite, ret); // must create before closing 
      sqlite3_close(mpSQLite); // close is required even in case of error on opening 
      throw exception; 
      } 
      if (aBusyTimeoutMs > 0) 
      { 
      setBusyTimeout(aBusyTimeoutMs); 
      } 
      } 
      
  • Por ahora, has algunos graves regex capacidades con su sqlite. Solo crea el proyecto.

  • Escriba un programa de cliente para probar la funcionalidad. Puede ser algo como a continuación (prestado sin pena de SQLiteCpp Example).

    #include <iostream> 
    #include <cstdio> 
    #include <cstdlib> 
    #include <string> 
    #include <SQLiteCpp/SQLiteCpp.h> 
    #include <SQLiteCpp/VariadicBind.h> 
    // Notice no sqlite3.h huh? 
    // Well, this is a C++ wrapper for the SQLITE CAPI afterall. 
    
    #ifdef SQLITECPP_ENABLE_ASSERT_HANDLER 
    namespace SQLite 
    { 
    /// definition of the assertion handler enabled when SQLITECPP_ENABLE_ASSERT_HANDLER is defined in the project (CMakeList.txt) 
    void assertion_failed(const char* apFile, const long apLine, const char* apFunc, const char* apExpr, const char* apMsg) 
    { 
    // Print a message to the standard error output stream, and abort the program. 
    std::cerr << apFile << ":" << apLine << ":" << " error: assertion failed (" << apExpr << ") in " << apFunc << "() with message \"" << apMsg << "\"\n"; 
    std::abort(); 
    } 
    } 
    #endif 
    
    /// Get example path 
    static inline std::string getExamplePath() 
    { 
    std::string filePath(__FILE__); 
    return filePath.substr(0, filePath.length() - std::string("Client.cpp").length()); 
    } 
    
    /// Example Database 
    static const std::string filename_example_db3 = getExamplePath() + "/example.db3"; 
    /// Image 
    static const std::string filename_logo_png = getExamplePath() + "/logo.png"; 
    
    
    /// Object Oriented Basic example 
    class Example 
    { 
    public: 
    //Constructor 
    Example() : 
    mDb(filename_example_db3), 
    // User change the db and tables accordingly 
    mQuery(mDb, "SELECT id,name FROM lookup WHERE name REGEXP :keyword") 
    // Open a database file in readonly mode 
    {  
    } 
    virtual ~Example() 
    { 
    } 
    
    /// List the rows where the "weight" column is greater than the provided aParamValue 
    void namehaskeyword(const std::string searchfor) 
    { 
    std::cout << "Matching results for " << searchfor << "\n"; 
    
    // Bind the integer value provided to the first parameter of the SQL query 
    mQuery.bind(1,searchfor); // same as mQuery.bind(1, aParamValue); 
    
    // Loop to execute the query step by step, to get one a row of results at a time 
    while (mQuery.executeStep()) 
    { 
    std::cout<<mQuery.getColumn(0) << "\t" << mQuery.getColumn(1) << "\n"; 
    } 
    
    // Reset the query to be able to use it again later 
    mQuery.reset(); 
    } 
    
    private: 
    SQLite::Database mDb; ///< Database connection 
    SQLite::Statement mQuery; ///< Database prepared SQL query 
    }; 
    
    int main() 
    { 
    // Using SQLITE_VERSION would require #include <sqlite3.h> which we want to avoid: use SQLite::VERSION if possible. 
    // std::cout << "SQlite3 version " << SQLITE_VERSION << std::endl; 
    std::cout << "SQlite3 version " << SQLite::VERSION << " (" << SQLite::getLibVersion() << ")" << std::endl; 
    std::cout << "SQliteC++ version " << SQLITECPP_VERSION << std::endl; 
    
    try 
    { 
    // Doing a regex query. 
    Example example; 
    char wannaquit = 'n'; 
    std::string keyword; 
    // Deliberate unlimited loop. You implement something sensible here. 
    while (wannaquit != 'y') { 
    // Demonstrates the way to use the same query with different parameter values 
    std::cout << "Enter the keyword to search for : "; 
    std::getline(std::cin, keyword); 
    example.namehaskeyword(keyword); 
    } 
    } 
    catch (std::exception& e) 
    { 
    std::cout << "SQLite exception : " << e.what() << std::endl; 
    return EXIT_FAILURE; // unexpected error : exit the example program 
    } 
    return EXIT_SUCCESS; 
    } 
    

Nota: Esto supone que la base de datos se encuentra en la misma carpeta que el CPP