/* bigblob.c - test fetching a large blob (> 65K) through ODBC This file is in the public domain. */ #include #include #include #include #include #define CREATE_AND_INSERT 0 #if CREATE_AND_INSERT /* Set this to a large file you want to insert and fetch - it should be readable by the mysqld process - and ensure that max_allowed_packet is larger than the file. */ const char *bigfilename = "/tmp/screenshot.bmp"; #endif /* You might want to change these. You can also call the program as: progname [DSN [USER [PASSWORD]]] to override these values. For example, call it as: progname myodbc3 -- use the myodbc3 dsn progname test abc -- use the test dsn, with user abc progname test abc 0U8I2 -- ditto, with password */ static char *dsn = "test"; static char *user = NULL; /* Use DSN default */ static char *pass = NULL; /* Use DSN default */ /* Max length for ODBC diagnostic messages */ #ifdef SQL_MAX_MESSAGE_LENGTH # define MAX_MESSAGE_LENGTH SQL_MAX_MESSAGE_LENGTH #else # define MAX_MESSAGE_LENGTH 1024 #endif /* Thes are initialized in db_connect() */ static SQLHENV henv; static SQLHDBC hdbc; static SQLHSTMT hstmt; /* Convenience macros for error checking: env_die Print diagnostic info from an HENV and exit dbc_die Print diagnostic info from an HDBC and exit stmt_die Print diagnostic info from an HSTMT and exit env_warn, dbc_warn, stmt_warn print the info, but do not exit */ #define env_die(rc) \ _error((rc), SQL_HANDLE_ENV, (henv), 1, __FILE__, __LINE__) #define env_warn(rc) \ _error((rc), SQL_HANDLE_ENV, (henv), 0, __FILE__, __LINE__) #define dbc_die(rc) \ _error((rc), SQL_HANDLE_DBC, (hdbc), 1, __FILE__, __LINE__) #define dbc_warn(rc) \ _error((rc), SQL_HANDLE_DBC, (hdbc), 0, __FILE__, __LINE__) #define stmt_die(rc) \ _error((rc), SQL_HANDLE_STMT, (hstmt), 1, __FILE__, __LINE__) #define stmt_warn(rc) \ _error((rc), SQL_HANDLE_STMT, (hstmt), 0, __FILE__, __LINE__) #define RC_ERR(rc) ((rc) < 0) void _error(SQLRETURN rc, SQLSMALLINT htype, SQLHANDLE handle, int is_fatal, const char *file, const unsigned int line); void db_connect(void); void db_disconnect(void); /* Some basic tests */ void runtests(void) { SQLRETURN rc; #if CREATE_AND_INSERT /* Create the table (droping it first, if it exists) */ rc = SQLExecDirect(hstmt, "DROP TABLE IF EXISTS bigblob", SQL_NTS); if (RC_ERR(rc)) stmt_die(rc); rc = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_COMMIT); if (RC_ERR(rc)) dbc_die(rc); fprintf(stderr, "Creating the table\n"); rc = SQLExecDirect(hstmt, "CREATE TABLE bigblob (" "id int PRIMARY KEY AUTO_INCREMENT," "b longblob NOT NULL)", SQL_NTS); if (RC_ERR(rc)) stmt_die(rc); rc = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_COMMIT); if (RC_ERR(rc)) dbc_die(rc); { char query[128]; fprintf(stderr, "Inserting file '%s' into the table\n", bigfilename); sprintf(query, "INSERT INTO bigblob (b) VALUES (LOAD_FILE('%s'))", bigfilename); rc = SQLExecDirect(hstmt, query, SQL_NTS); if (RC_ERR(rc)) stmt_die(rc); } rc = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_COMMIT); if (RC_ERR(rc)) dbc_die(rc); #endif /* CREATE_AND_INSERT */ /* Select a blob */ fprintf(stderr, "Selecting the blob...\n"); rc = SQLExecDirect(hstmt, "SELECT b FROM bigblob LIMIT 1", SQL_NTS); if (RC_ERR(rc)) stmt_die(rc); { char buf[1024]; long int blob_size = 0; rc = SQLFetch(hstmt); if (RC_ERR(rc)) stmt_die(rc); /* Write it out in chunks */ for (;;) { SQLINTEGER result; rc = SQLGetData(hstmt, 1, SQL_C_BINARY, buf, sizeof(buf), &result); if (RC_ERR(rc)) stmt_die(rc); if (rc == SQL_NO_DATA) { fprintf(stderr, "\tdone.\n"); break; } if (result == SQL_NULL_DATA) { fprintf(stderr, "\tthe blob is NULL\n"); break; } assert(result > 0); result = result < (long int)sizeof(buf) ? result : (long int)sizeof(buf); blob_size += result; (void)fwrite(buf, 1, result, stdout); fprintf(stderr, "\t%ld bytes\n", blob_size); } } SQLFreeStmt(hstmt, SQL_UNBIND); /* Free the statement row bind resources */ rc = SQLFreeStmt(hstmt, SQL_UNBIND); if (RC_ERR(rc)) stmt_die(rc); /* Free the statement cursor */ rc = SQLFreeStmt(hstmt, SQL_CLOSE); if (RC_ERR(rc)) stmt_die(rc); } int main(int argc, char **argv) { if (--argc > 0) dsn = *++argv; if (--argc > 0) user = *++argv; if (--argc > 0) pass = *++argv; db_connect(); runtests(); db_disconnect(); exit(EXIT_SUCCESS); } /* db_connect() initializes the global henv, hdbc and hstmt variables. It uses the global dsn, user, and pass variables. */ void db_connect(void) { SQLRETURN rc; rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv); if (RC_ERR(rc)) env_die(rc); rc = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0); if (RC_ERR(rc)) env_die(rc); fprintf(stderr, "connecting (DSN='%s', USER='%s')\n", dsn, user ? user : ""); rc = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc); if (RC_ERR(rc)) env_die(rc); rc = SQLConnect(hdbc, dsn, SQL_NTS, user, SQL_NTS, pass, SQL_NTS); if (RC_ERR(rc)) dbc_die(rc); rc = SQLSetConnectAttr(hdbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, 0); if (RC_ERR(rc)) dbc_die(rc); rc = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt); if (RC_ERR(rc)) dbc_die(rc); } /* db_disconnect() releases all the resources acquired by db_connect() */ void db_disconnect(void) { SQLRETURN rc; rc = SQLFreeStmt(hstmt, SQL_DROP); if (RC_ERR(rc)) dbc_die(rc); rc = SQLDisconnect(hdbc); if (RC_ERR(rc)) dbc_die(rc); rc = SQLFreeConnect(hdbc); if (RC_ERR(rc)) dbc_die(rc); rc = SQLFreeEnv(henv); if (RC_ERR(rc)) env_die(rc); } /* _error() should be called via the convenience macros. If there is any diagnostic info, print it. If is_fatal is true, exit if rc == SQL_ERROR. */ void _error(SQLRETURN rc, SQLSMALLINT htype, SQLHANDLE handle, int is_fatal, const char *file, const unsigned int line) { SQLCHAR sql_state[6], message[MAX_MESSAGE_LENGTH + 1]; SQLINTEGER native_error; SQLSMALLINT dummy; SQLRETURN new_rc; switch (rc) { case SQL_SUCCESS: fprintf(stderr, "\nSuccess\n"); break; case SQL_NO_DATA: fprintf(stderr, "\nEOF (finished reading all data)\n"); break; case SQL_ERROR: case SQL_SUCCESS_WITH_INFO: default: fprintf(stderr, "\nError at line %s:%u: ", file, line); new_rc = SQLGetDiagRec(htype, handle, 1, sql_state, &native_error, message, SQL_MAX_MESSAGE_LENGTH, &dummy); if(new_rc == SQL_SUCCESS || new_rc == SQL_SUCCESS_WITH_INFO) fprintf(stderr, "[%s:%ld] %s\n", sql_state, native_error, message); else fprintf(stderr, "Can't get error message (SQLGetDiagRec " "returned rc[%d]; original rc[%d])\n", new_rc, rc); break; } if (is_fatal && rc == SQL_ERROR) exit(EXIT_FAILURE); }