Hello I need your help to convert void* (hex number) to decimal number. I use sqlite3 in my code and the callback function give me a &data that contains number of row. the problem is: I need to save &data (hex number) as int. maybe there is another way to do this but I try to convert the &data hex to decimal int and the issue is to convert the void*. I try to convert the void* to int but the Running time problem to get the pointer. I will be happy if you help me do the conversion.
Thanks.
some code -
void* data = nullptr;
rc = sqlite3_exec(db, str.data(), callback, &data, &zErrMsg);
int id = 0;
if (data != nullptr)
{
int dataa = *((int *)data); // run time error
istringstream(dataa) >> std::hex >> id; \\ istringstream can't convert void* // NEED CHANGE
}
return id;
callback function -
if (argc == 0) {
user_exist = false; return 0;
}
else {
user_exist = true;
if (notUsed != NULL) {
cout << argv[0] << endl;
((int)notUsed) = atoi(argv[0]);
}
}
return 0;
sqlite3_exec() is useful only when you can actually handle the data in the callback.
In practice, you should normally use the sqlite3_prepare_v2()/sqlite3_step()/sqlite3_finalize() functions to read data:
sqlite3_stmt *stmt;
int rc = sqlite3_prepare_v2(db, "SELECT ...", -1, &stmt, NULL);
if (rc != SQLITE_OK) {
print("error: ", sqlite3_errmsg(db));
return -1;
}
rc = sqlite3_step(stmt);
if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
print("error: ", sqlite3_errmsg(db));
} else {
user_exist = rc == SQLITE_ROW;
}
sqlite3_finalize(stmt);
(For most other queries, you would call the sqlite3_column_*() functions to read the actual values, as long as you get SQLITE_ROW.)
Related
I try to save Data in my esp32. In my table there should be a primary key so that I can´t save something twice with the same id. But I get an error
The Error Code:
SQL error: disk I/O error
That´s the whole Code :
#include <stdio.h>
#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include "esp_err.h"
#include "esp_log.h"
#include "esp_spiffs.h"
#include "esp_timer.h"
#include "sqlite3.h"
static const char *TAG = "sqlite3_spiffs";
const char* data = "Callback function called";
static int callback(void *data, int argc, char **argv, char **azColName) {
int i;
printf("%s: ", (const char*)data);
for (i = 0; i<argc; i++){
printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
}
printf("\n");
return 0;
}
int db_open(const char *filename, sqlite3 **db) {
int rc = sqlite3_open(filename, db);
if (rc) {
printf("Can't open database: %s\n", sqlite3_errmsg(*db));
return rc;
} else {
printf("Opened database successfully\n");
}
return rc;
}
char *zErrMsg = 0;
int db_exec(sqlite3 *db, const char *sql) {
printf("%s\n", sql);
int64_t start = esp_timer_get_time();
int rc = sqlite3_exec(db, sql, callback, (void*)data, &zErrMsg);
if (rc != SQLITE_OK) {
printf("SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
} else {
printf("Operation done successfully\n");
}
printf("Time taken: %lld\n", esp_timer_get_time()-start);
return rc;
}
void setup()
{
sqlite3 *db1;
int rc;
ESP_LOGI(TAG, "Initializing SPIFFS");
esp_vfs_spiffs_conf_t conf = {
.base_path = "/spiffs",
//.partition_label = "storage",
.partition_label = NULL,
.max_files = 5,
.format_if_mount_failed = true
};
// Use settings defined above to initialize and mount SPIFFS filesystem.
// Note: esp_vfs_spiffs_register is an all-in-one convenience function.
esp_err_t ret = esp_vfs_spiffs_register(&conf);
if (ret != ESP_OK) {
if (ret == ESP_FAIL) {
ESP_LOGE(TAG, "Failed to mount or format filesystem");
} else if (ret == ESP_ERR_NOT_FOUND) {
ESP_LOGE(TAG, "Failed to find SPIFFS partition");
} else {
ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret));
}
return;
}
size_t total = 0, used = 0;
ret = esp_spiffs_info(NULL, &total, &used);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s)", esp_err_to_name(ret));
} else {
ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used);
}
// remove existing file
unlink("/spiffs/test1.db");
unlink("/spiffs/test2.db");
sqlite3_initialize();
if (db_open("/spiffs/test1.db", &db1))
return;
rc = db_exec(db1, "CREATE TABLE entries(id STRING PRIMARY KEY, value ,section)");
if (rc != SQLITE_OK) {
sqlite3_close(db1);
return;
}
rc = db_exec(db1, "INSERT INTO entries VALUES ('ROOMNAME', 'HH180L23G', 'general');");
if (rc != SQLITE_OK) {
sqlite3_close(db1);
return;
}
rc = db_exec(db1, "SELECT * FROM entries");
if (rc != SQLITE_OK) {
sqlite3_close(db1);
return;
}
sqlite3_close(db1);
// All done, unmount partition and disable SPIFFS
esp_vfs_spiffs_unregister(NULL);
ESP_LOGI(TAG, "SPIFFS unmounted");
//while(1);
}
void loop() {
// put your main code here, to run repeatedly:
}
I have ideas like that it has something to do with spiffs or with the mounting ..
I tried both partition.labels .. it´s the same error ..
I can´t figure out why it doesn´t let me take a primary key .. the same thing as I can´t implement unique Text or something I get the same error..
Did someone have experienced it before ?
It would be such a huge support
Log Monitor:
Opened database successfully
CREATE TABLE entries(id STRING PRIMARY KEY, value ,section)
SQL error: disk I/O error
Time taken: 179044
Looks like a known problem: https://github.com/siara-cc/esp32_arduino_sqlite3_lib/issues/18
Somebody has posted a workaround/fix, which doesn't look very promising to me - they explicitly specify the label of the SPIFFS partition when mounting it. Try it, see if it works.
https://github.com/siara-cc/esp32-idf-sqlite3/issues/13
Im trying to create a database which should contains an id (autoincrement) and an integer value by the sqlite3 library.
Now i have troubles to insert an integer value into the const char sql-query.
I tried to insert by %l, but it failed cause of const char type.
rc = db_exec(db1, "INSERT INTO test1 VALUES (?, %l)", random(0,200));
here is the full code:
void setup() {
Serial.begin(115200);
sqlite3 *db1;
int rc;
SPI.begin();
SD_MMC.begin();
sqlite3_initialize();
if (db_open("/sdcard/test1.db", &db1))
return;
//SD_MMC.remove("/test1.db");
if(SD_MMC.exists("/test1.db")){
Serial.println("Database exists");
}else{
rc = db_exec(db1, "CREATE TABLE IF NOT EXISTS test1 (id INTEGER PRIMARY KEY, content);");
if (rc != SQLITE_OK) {
sqlite3_close(db1);
return;
}
}
rc = db_exec(db1, "INSERT INTO test1 VALUES (?, ?)");
if (rc != SQLITE_OK) {
sqlite3_close(db1);
return;
}
rc = db_exec(db1, "SELECT * FROM test1");
if (rc != SQLITE_OK) {
sqlite3_close(db1);
return;
}
sqlite3_close(db1);
}
the excecute function of database:
int db_exec(sqlite3 *db, const char *sql) {
Serial.println(sql);
long start = millis();
int rc = sqlite3_exec(db, sql, callback, (void*)data, &zErrMsg);
if (rc != SQLITE_OK) {
Serial.printf("SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
} else {
Serial.printf("Operation done successfully\n");
}
Serial.print(F("Time taken:"));
Serial.println(millis()-start);
return rc;
}
You need to convert your string to a const char array. The sample code is below, you can add values with this method.
int Value1 = Function1();
int Value2 = Function2();
String s_Command = "INSERT INTO test1 VALUES ('" + Value1 + "','" + Value2 + "');";
const char *c_Command = s_Command.c_str();
rc = db_exec(db1, c_Command);
I have pasted my code below. It gives seg fault at sqlite3_get_table (). I want to get the number of row in table and return that. If number of row is greater than 0, the program needs to read all rows.
int countRowsInTable()
{
sqlite3 *db;
char *zErrMsg = 0;
int rc;
char const *sql;
char ***pazResult;
int *pnRow; /* Number of result rows written here */
int *pnColumn; /* Number of result columns written here */
rc = sqlite3_open("DeviceDetails.db", &db);
if( rc )
{
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
return(0);
}
else
{
fprintf(stderr, "Opened database successfully\n");
}
/* Create SQL statement */
sql = "select * from Device_Details";
/* Execute SQL statement */
// rc = sqlite3_exec(db, sql, 0/*callback*/, 0/*(void*)data*/, &zErrMsg);
rc = sqlite3_get_table(db, sql, pazResult, pnRow, pnColumn, &zErrMsg);
void sqlite3_free_table(char **result);
if( rc != SQLITE_OK )
{
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}
else
{
fprintf(stdout, "Operation done successfully\n");
cout<<"rows="<<pnRow<<endl;
sleep(10);
}
sqlite3_close(db);
cout<<"num rows =" << *pnRow <<endl;
}
after reading your suggestion I have changed my code. Now I use select count (*), but where (which) variable will hold the number of rows. Do, I need to implement the callback function in sqlite3_exec. Also I need to get data if row count is greater than zero.If I get the data in call back function, how to pass it to the function calling countRows in table? may be I can keep a global variable. As soon as call back is executed , it will signal the calling function through condition variable. So I have to maintain countRowsInTable function in a new thread. I there any simpler approach?
int countRowsInTable()
{
sqlite3 *db;
char *zErrMsg = new char[64];
int rc;
char const *sql;
char ***pazResult;
int *pnRow = new int; /* Number of result rows written here */
int *pnColumn = new int; /* Number of result columns written here */
//*pnRow = 0;
**pazResult = new char[20480];
rc = sqlite3_open("DeviceDetails.db", &db);
if( rc )
{
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
return(0);
}
else
{
fprintf(stderr, "Opened database successfully\n");
}
/* Create SQL statement */
sql = "select count(*) from Device_Details";
/* Execute SQL statement */
rc = sqlite3_exec(db, sql, 0/*callback*/, 0/*(void*)data*/, &zErrMsg);
//rc = sqlite3_get_table(db, sql, pazResult, pnRow, pnColumn, &zErrMsg);
//void sqlite3_free_table(char **result);
if( rc != SQLITE_OK )
{
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}
else
{
fprintf(stdout, "Operation done successfully\n");
}
}
Your are not calling the function with the right parameters. You are passing uninitialized pointers, whereas you must create the variables and pass pointers on them so SQLite can write the values.
const char *sql = "select * from Device_Details";
char **result;
int nrow;
int ncolumn;
char *errmsg;
int rc;
rc = sqlite3_get_table(db, sql, &result, &nrow, &ncolumn, &errmsg);
However, you should be aware the SQLite manual discourage the use of the function: "This is a legacy interface that is preserved for backwards compatibility. Use of this interface is not recommended."
Also, you should remove void sqlite3_free_table(char **result) that you probably pasted by mistake.
How do I load the PEM format certificate as an x509 in openssl c++?
int SSL_use_certificate(SSL *ssl, X509 *x);
int SSL_use_certificate_ASN1(SSL *ssl, unsigned char *d, int len);
int SSL_use_certificate_file(SSL *ssl, const char *file, int type);
These are the 3 functions available to add a certificate to a Handle. I have a certificate string inside the program(This is just a PEM formatted data). I want to add it to the handle. How do I proceed?
Will SSL_CTX_set_default_passwd_cb work with private keys that I am loading into and ssl handle and not a context?
How do I load the PEM format certificate as an x509 in openssl c++?
You should probably use SSL_CTX_use_certificate_chain_file and SSL_CTX_use_PrivateKey_file. You can use them and build a client or server context. The sample code is shown below. There are some nuances to using them, so take a look at OpenSSL's documentation at SSL_CTX_use_certificate(3).
I'm not sure what "as an x509" means. The certificate will be x509, but the private key will be PKCS #8. There are PEM_read_bio_X509 and PEM_read_X509, they return an X509*, and they may do what you want.
Will SSL_CTX_set_default_passwd_cb work with private keys that I am loading
It depends, but it should. The password callback is optional. Use it if you password protected the key. In the code below, I call it PasswordCallback, and its used for both reading and writing the key (only reading is shown below).
using SSL_CTX_ptr = std::unique_ptr<SSL_CTX, decltype(&::SSL_CTX_free)>;
SSL_CTX* CreateServerContext()
{
do
{
int rc;
unsigned long err;
const SSL_METHOD* method = SSLv23_server_method();
ASSERT(method != NULL);
if (method == NULL)
{
LogError("GetServerContext: SSLv23_server_method failed");
break; /* failed */
}
SSL_CTX_ptr t(SSL_CTX_new(method), ::SSL_CTX_free);
ASSERT(t.get() != NULL);
if (t.get() == NULL)
{
LogError("GetServerContext: SSL_CTX_new failed");
break; /* failed */
}
long flags = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
flags |= SSL_OP_NO_COMPRESSION;
flags |= SSL_OP_SAFARI_ECDHE_ECDSA_BUG;
flags |= SSL_OP_CIPHER_SERVER_PREFERENCE;
/* Cannot fail */
SSL_CTX_set_options(t.get(), flags);
string ciphers = GetServerCipherSuites();
ASSERT(!ciphers.empty());
rc = SSL_CTX_set_cipher_list(t.get(), ciphers.c_str());
err = ERR_get_error();
ASSERT(rc == 1);
if (rc != 1)
{
LogError("GetServerContext: SSL_CTX_set_cipher_list failed");
break; /* failed */
}
string certFile = config.GetServerCertFile();
ASSERT(!certFile.empty());
rc = SSL_CTX_use_certificate_chain_file(t.get(), certFile.c_str());
err = ERR_get_error();
ASSERT(rc == 1);
if (rc != 1)
{
LogError("GetServerContext: SSL_CTX_use_certificate_chain_file failed");
break; /* failed */
}
/* These two do not return a value... cannot fail? */
SSL_CTX_set_default_passwd_cb(t.get(), PasswordCallback);
SSL_CTX_set_default_passwd_cb_userdata(t.get(), (void*) SERVER_KEY_LABEL);
string keyFile = config.GetServerKeyFile();
ASSERT(!keyFile.empty());
rc = SSL_CTX_use_PrivateKey_file(t.get(), keyFile.c_str(), SSL_FILETYPE_PEM);
err = ERR_get_error();
ASSERT(rc == 1);
if (rc != 1)
{
LogError("GetServerContext: SSL_CTX_use_PrivateKey_file failed");
break; /* failed */
}
rc = SSL_CTX_check_private_key(t.get());
err = ERR_get_error();
ASSERT(rc == 1);
if (rc != 1)
{
LogError("GetServerContext: SSL_CTX_check_private_key failed");
/* non-fatal, but everything will probably break */
}
/* These three do not return a value... cannot fail? */
SSL_CTX_set_tmp_dh_callback(t.get(), DhCallback);
SSL_CTX_set_tmp_ecdh_callback(t.get(), EcdhCallback);
SSL_CTX_set_tlsext_servername_callback(t.get(), ServerNameCallback);
return t.release();
} while (0);
return NULL;
}
The idea with the password callback is: OpenSSL provides you a buffer and a size. You fill the buffer, and return the size of how much you filled.
My password callback is somewhat involved. It performs a single hash of the raw password before passing it on to the library. That ensures a "plain text" password is not used (but does not slow down the customary attacks). Yours can prompt the user for a string, or it can return a hard coded string.
My password callback uses a label. The label allows me to derive different keys depending on usage (even though the same 'base' secret is used). By specifying a different usage or label, I get a different derivation of key bits. The label is provided through arg below, and you can set it with SSL_CTX_set_default_passwd_cb_userdata.
using EVP_MD_CTX_ptr = std::unique_ptr<EVP_MD_CTX, decltype(&::EVP_MD_CTX_destroy)>;
int PasswordCallback(char *buffer, int size, int rwflag, void *arg)
{
UNUSED(rwflag);
int rc;
unsigned long err;
ostringstream oss;
const char* label = (char*) arg;
size_t lsize = (label ? strlen(label) : 0);
SecureVector sv = config.GetMasterKey();
AC_ASSERT(!sv.empty());
if (sv.empty())
{
...
throw runtime_error(oss.str().c_str());
}
EVP_MD_CTX_ptr ctx(EVP_MD_CTX_create(), ::EVP_MD_CTX_destroy);
AC_ASSERT(ctx.get() != NULL);
const EVP_MD* hash = EVP_sha512();
AC_ASSERT(hash != NULL);
rc = EVP_DigestInit_ex(ctx.get(), hash, NULL);
err = ERR_get_error();
AC_ASSERT(rc == 1);
if (rc != 1)
{
...
throw runtime_error(oss.str().c_str());
}
rc = EVP_DigestUpdate(ctx.get(), sv.data(), sv.size());
err = ERR_get_error();
AC_ASSERT(rc == 1);
if (rc != 1)
{
...
throw runtime_error(oss.str().c_str());
}
if (label && lsize)
{
rc = EVP_DigestUpdate(ctx.get(), label, lsize);
err = ERR_get_error();
AC_ASSERT(rc == 1);
if (rc != 1)
{
...
throw runtime_error(oss.str().c_str());
}
}
int n = std::min(size, EVP_MD_size(hash));
if (n <= 0)
return 0;
rc = EVP_DigestFinal_ex(ctx.get(), (unsigned char*) buffer, (unsigned int*) &n);
err = ERR_get_error();
AC_ASSERT(rc == 1);
if (rc != 1)
{
...
throw runtime_error(oss.str().c_str());
}
return n;
}
I have created a MySQL table where one of the columns stores a BLOB type. (The Internet told me BLOB is the correct data type for images.)
I am pretty much a beginner with both C++ and MySQL. What I would like to do is to write a small program with a main() that puts a jpeg into that table. For the sake of this exercise, I do not want to store a reference to a directory that contains an image.
Am I wrong to think that it is as simple as filling out the part in BLOCK 2 below?
#include <iostream>
#include <string>
#include <mysql.h>
using namespace std;
int main(int argc, char **argv)
{
//BLOCK 1: INIT
MYSQL *connection, mysql;
MYSQL_RES *result;
MYSQL_ROW row;
int query_state;
mysql_init(&mysql);
connection = mysql_real_connect(&mysql, "localhost", "root", "secret", "beginner_db",0,0,0);
//BLOCK 2: SEND QUERY
/* do something to insert image to table */
//BLOCK 3: DISPLAY QUERY RESULTS
result = mysql_store_result(connection);
/* do something with result */
//BLOCK 4: FREE
mysql_free_result(result);
mysql_close(connection);
return 0;
}
For this scenario, a good solution would be to use the mysql_stmt_send_long_data() function.
There is an example on the MySQL Manual page that I linked to, but here is a more relevant example of sending file contents:
#ifdef _WIN32
#include <windows.h>
#endif
#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <boost/scope_exit.hpp>
#include <mysql.h>
#define ARR_LEN(arr_id) ((sizeof (arr_id))/(sizeof (arr_id)[0]))
int main()
{
using namespace std;
MYSQL *pconn = mysql_init(NULL);
BOOST_SCOPE_EXIT( (pconn) ) {
mysql_close(pconn);
} BOOST_SCOPE_EXIT_END
const char *db_name = "test";
if (!mysql_real_connect(pconn, "localhost", "test", "********", db_name, 0, NULL, CLIENT_COMPRESS)) {
cerr << "Error: mysql_real_connect() failed to connect to `" << db_name << "`." << endl;
return EXIT_FAILURE;
}
MYSQL_STMT *pinsert_into_images_stmt = mysql_stmt_init(pconn);
BOOST_SCOPE_EXIT( (pinsert_into_images_stmt) ) {
mysql_stmt_close(pinsert_into_images_stmt);
} BOOST_SCOPE_EXIT_END
const char sql1[] = "INSERT INTO images(data) VALUES (?)";
if (mysql_stmt_prepare(pinsert_into_images_stmt, sql1, strlen(sql1)) != 0) {
cerr << "Error: mysql_stmt_prepare() failed to prepare `" << sql1 << "`." << endl;
return EXIT_FAILURE;
}
MYSQL_BIND bind_structs[] = {
{ 0 } // One for each ?-placeholder
};
unsigned long length0;
bind_structs[0].length = &length0;
bind_structs[0].buffer_type = MYSQL_TYPE_BLOB;
bind_structs[0].is_null_value = 0;
if (mysql_stmt_bind_param(pinsert_into_images_stmt, bind_structs) != 0) {
cerr << "Error: mysql_stmt_bind_param() failed." << endl;
return EXIT_FAILURE;
}
const char *file_name = "image.jpg";
FILE *fp = fopen(file_name, "rb");
BOOST_SCOPE_EXIT( (fp) ) {
fclose(fp);
} BOOST_SCOPE_EXIT_END
// Use mysql_stmt_send_long_data() to send the file data in chunks.
char buf[10*1024];
while (!ferror(fp) && !feof(fp)) {
size_t res = fread(buf, 1, ARR_LEN(buf), fp);
if (mysql_stmt_send_long_data(pinsert_into_images_stmt, 0, buf, res) != 0) {
cerr << "Error: mysql_stmt_send_long_data() failed." << endl;
return EXIT_FAILURE;
}
}
if (!feof(fp)) {
cerr << "Error: Failed to read `" << file_name << "` in its entirety." << endl;
return EXIT_FAILURE;
}
if (mysql_stmt_execute(pinsert_into_images_stmt) != 0) {
cerr << "Error: mysql_stmt_execute() failed." << endl;
return EXIT_FAILURE;
}
cout << "Inserted record #" << mysql_insert_id(pconn) << endl;
return EXIT_SUCCESS;
}
I am using the following definition of table `images`:
CREATE TABLE images (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
data MEDIUMBLOB NOT NULL,
PRIMARY KEY (id)
);
Upon running this program, it successfully sent the 38,339-byte JPEG image.jpg to the server and outputted "Inserted record #1".
You can verify that the correct number of bytes were sent:
mysql> SELECT octet_length(data) FROM images WHERE id=1;
+--------------------+
| octet_length(data) |
+--------------------+
| 38339 |
+--------------------+
1 row in set (0.00 sec)
I found this solution that worked... for images under 10kb.
//http://zetcode.com/tutorials/mysqlcapitutorial/
//g++ -o output source.cpp $(mysql_config --cflags) $(mysql_config --libs)
#include <stdio.h>
#include <iostream>
#include <mysql.h>
int main(int argc, char **argv)
{
MYSQL *conn;
int len, size;
char data[1000*1024];
char chunk[2*1000*1024+1];
char query[1024*5000];
FILE *fp;
conn = mysql_init(NULL);
mysql_real_connect(conn, "localhost", "root", "secret", "beginner_db", 0, NULL, 0);
fp = fopen("filename.png", "rb");
size = fread(data, 1, 1024*1000, fp);
mysql_real_escape_string(conn, chunk, data, size);
char *stat = "INSERT INTO pic_tbl(name, pic) VALUES('cexample', '%s')";
len = snprintf(query, sizeof(stat)+sizeof(chunk) , stat, chunk);
mysql_real_query(conn, query, len);
fclose(fp);
mysql_close(conn);
}
Something like this:
CString SaveFile( CMemoryFile& File )
{
*pFileKey = -1;
SQLRETURN retcode;
SQLHSTMT hstmt;
CLoggEntryList LoggEntryList( this ); // logg entries cannot be made while busy inside the hstmt, use this class to add them later
SQLINTEGER cbDocumentBlock; // For binding the main image
long lDocumentBufferSize = 0;
unsigned char* pDocumentBuffer; // Will be set to point to the buffer that should be written into the document blob field
unsigned char pDummyChar[] = {'0'}; // Dummy buffer to write in the image/thumbnail blob fields when using external storage
lDocumentBufferSize = File.m_lBufferSize;
pDocumentBuffer = File.m_pFileBuffer;
// Allocate statement handle
retcode = SQLAllocHandle(SQL_HANDLE_STMT, m_Database.m_hdbc, &hstmt);
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
{ // Create a result set
CString szSQL;
szSQL = ( "INSERT INTO ObjectTable (ObjectData) VALUES ( ? )");
retcode = SQLPrepare(hstmt, (SQLCHAR*)szSQL.GetBuffer(), SQL_NTS);
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
{
// Bind the parameters. For parameter 1, pass the parameter number in ParameterValuePtr instead of a buffer address.
SQLINTEGER cbNULL = 0;
SQLINTEGER cbTEXT = SQL_NTS;
int nColumn = 1;
// Bind ObjectData
cbDocumentBlock = SQL_LEN_DATA_AT_EXEC(0); //SQL_LEN_DATA_AT_EXEC(lImageBufferSize);
retcode = SQLBindParameter(hstmt, nColumn++, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY,
0, 0, (SQLPOINTER) DOCUMENT, 0, &cbDocumentBlock);
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
{
// Set values so data for parameter 1 will be passed at execution. Note that the length parameter in
// the macro SQL_LEN_DATA_AT_EXEC is 0. This assumes that the driver returns "N" for the
// SQL_NEED_LONG_DATA_LEN information type in SQLGetInfo.
retcode = SQLExecute(hstmt);
const long nMaxChunkSize = 400000;
// For data-at-execution parameters, call SQLParamData to get the parameter number set by SQLBindParameter.
// Call InitUserData. Call GetUserData and SQLPutData repeatedly to get and put all data for the parameter.
// Call SQLParamData to finish processing this parameter.
while (retcode == SQL_NEED_DATA)
{
SQLPOINTER pToken;
retcode = SQLParamData(hstmt, &pToken);
switch( (int)pToken )
{
case DOCUMENT:
{
if (retcode == SQL_NEED_DATA)
{
for( int nPos = 0; nPos < lDocumentBufferSize; nPos += nMaxChunkSize )
{
int nBufferSize = min( lDocumentBufferSize - nPos, nMaxChunkSize );
SQLRETURN retcode2 = SQLPutData(hstmt, pDocumentBuffer+nPos, nBufferSize );
if (retcode2 != SQL_SUCCESS && retcode2 != SQL_SUCCESS_WITH_INFO)
{
SQLCHAR Sqlstate[6];
SQLINTEGER NativeError;
SQLCHAR MessageText[201];
SQLSMALLINT TextLengthPtr;
retcode2 = SQLGetDiagRec( SQL_HANDLE_STMT, hstmt, 1, Sqlstate, &NativeError, MessageText, 200, &TextLengthPtr );
if (retcode2 == SQL_SUCCESS || retcode2 == SQL_SUCCESS_WITH_INFO)
{
MessageText[TextLengthPtr] = 0;
Sqlstate[5] = 0;
CString szSQLState( Sqlstate );
CString szMessageText( MessageText );
CString szMessage;
szMessage.Format("Error in SaveFile(). SQL State %s. Native %ld. Source: %s", szSQLState, NativeError, szMessageText );
SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
return szMessage;
}
}
}
}
break;
}
default:
{
CString szMessage;
szMessage.Format("Error in SaveBuffer(). Unknown parameter buffer.");
SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
return szMessage;
}
break;
}
}
}
SQLRETURN retcode3;
retcode3 = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
ASSERT(retcode3 == SQL_SUCCESS);
}
}
}
This code is not tested or even compiled, but it should point you in the right direction.