It's convenient to be able to define new functions in C.  But sometimes, 
it would be *more* convenient to be able to define new functions in 
SQL.  This could be done by registering a CREATE_FUNCTION() function; 
then you could write something like:

SELECT CREATE_FUNCTION('LEFT', 2, 'SUBSTR(?1, 1, ?2)');

My first idea for implementing CREATE_FUNCTION is:

1. Create (if not exists) a table with columns for the function name, 
number of arguments, and SQL expression.
2. Add the new function to the table.
3. Call sqlite3_create_function to register the new function.

C doesn't have the ability to create functions at runtime, so the xFunc 
parameter would refer to a common global function, which would:

1. Look up the SQL expression corresponding to the SQL function name.
2. Evaluate the expression.

But how do I get the SQL function name from within the xFunc function?  
Can I get it from the sqlite3_context object, or do I have to use 
sqlite3_user_data()?

Or is there a better approach?

As a first step, I've written an EVAL() function, which supports 
parameter binding.

// EVAL(expr [, param]*)
void x_sqlite_eval(sqlite3_context* pContext,
                   int argc,
                   sqlite3_value* argv[])
{
   sqlite3*             pDB     = sqlite3_context_db_handle(pContext);
   sqlite3_stmt*        pStmt   = NULL;
   const unsigned char* expr    = NULL;
   char*                sql     = NULL;
   size_t               len;
   int                  err;
   int                  index;

   if (argc == 0)
   {
      sqlite3_result_null(pContext);
      return;
   }

   expr = sqlite3_value_text(argv[0]);
   len = sqlite3_value_bytes(argv[0]);

   // Build the SQL statement "SELECT (expr)"

   sql = malloc(len + 10); 
 
   if (sql == NULL)
   {
      sqlite3_result_error_nomem(pContext);
      return;
   }

   memcpy(sql, "SELECT (", 8);
   memcpy(sql + 8, expr, len);
   sql[8 + len] = ')';

   err = sqlite3_prepare_v2(pDB, sql, len + 9, &pStmt, NULL);

   if (err != SQLITE_OK)
   {
      sqlite3_result_error_code(pContext, err);
      goto EXIT;
   }

   // bind parameters
   for (index = 1; index < argc; index++)
   {
      err = sqlite3_bind_value(pStmt, index, argv[index]);

      if (err != SQLITE_OK)
      {
         sqlite3_result_error_code(pContext, err);
         goto EXIT;
      }
   }

   // execute the statement
   err = sqlite3_step(pStmt);

   if (err != SQLITE_ROW)
   {
      sqlite3_result_error_code(pContext, err);
      goto EXIT;
   }

   sqlite3_result_value(pContext, sqlite3_column_value(pStmt, 0));

EXIT:
   sqlite3_finalize(pStmt);
}

_______________________________________________
sqlite-users mailing list
sqlite-users@sqlite.org
http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users

Reply via email to