diff --git a/pmodbc.c b/pmodbc.c new file mode 100644 index 0000000000000000000000000000000000000000..fa1abbf787cc33484ce9f3d5a2271ce3b17daa44 --- /dev/null +++ b/pmodbc.c @@ -0,0 +1,135 @@ + +#include +#include +#include +#include +#include +#include + +// compilation: gcc -g pmodbc.c -lodbc -o pmodbc + +static void list_drivers(); +static void query_db(char* dsn, char* query, int csv); +void extract_error(char *fn, SQLHANDLE handle, SQLSMALLINT type); + +int main(int argc, char** argv) { + if (argc < 4) { + printf("Incorrect number of arguments.\n\n"); + printf("Arguments:\n"); + printf("pmodbc [dsn] [query] [csv={0,1}]\n\n"); + printf("DSN List:\n"); + list_drivers(); + } else { + query_db(argv[1], argv[2], *argv[3] == '1'); + } +} + + +static void list_drivers() { + SQLHENV env; + char dsn[256]; + char desc[256]; + SQLSMALLINT dsn_ret; + SQLSMALLINT desc_ret; + SQLUSMALLINT direction; + SQLRETURN ret; + + SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env); + SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0); + + direction = SQL_FETCH_FIRST; + while(SQL_SUCCEEDED(ret = SQLDataSources(env, direction, dsn, sizeof(dsn), &dsn_ret, desc, sizeof(desc), &desc_ret))) { + direction = SQL_FETCH_NEXT; + printf("%s - %s\n", dsn, desc); + if (ret == SQL_SUCCESS_WITH_INFO) printf("\tdata truncation\n"); + } +} + + +static void query_db(char* dsn_str, char* query, int csv) { + SQLHENV env; + SQLHDBC dbc; + SQLHSTMT stmt; + SQLRETURN ret; /* ODBC API return status */ + SQLSMALLINT columns; /* number of columns in result-set */ + int row = 0; + char dsn[1000]; + snprintf(dsn, 1000, "DSN=%s;", dsn_str); + + /* Allocate an environment handle */ + SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env); + /* We want ODBC 3 support */ + SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0); + /* Allocate a connection handle */ + ret = SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc); + if (!SQL_SUCCEEDED(ret)) { + extract_error("SQLAllocHandle for dbc", env, SQL_HANDLE_ENV); + exit(1); + } + /* Connect to the DSN mydsn */ + /* You will need to change mydsn to one you have created and tested */ + SQLDriverConnect(dbc, NULL, dsn, SQL_NTS, NULL, 0, NULL, SQL_DRIVER_COMPLETE); + /* Allocate a statement handle */ + SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt); + /* Retrieve a list of tables */ + //SQLTables(stmt, NULL, 0, NULL, 0, NULL, 0, "TABLE", SQL_NTS); + ret = SQLExecDirect(stmt, query, SQL_NTS); + if (!SQL_SUCCEEDED(ret)) { + extract_error("SQLExecuteDirect for dbc", stmt, SQL_HANDLE_STMT); + exit(1); + } + /* How many columns are there */ + SQLNumResultCols(stmt, &columns); + if (csv == 0) { + while (SQL_SUCCEEDED(ret = SQLFetch(stmt))) { + } + } else { + char csvbuf[1000]; + /* Loop through the rows in the result-set */ + while (SQL_SUCCEEDED(ret = SQLFetch(stmt))) { + SQLUSMALLINT i; + SQLLEN curlen = 0; + SQLLEN indicator; + /* Loop through the columns */ + for (i = 1; i <= columns; i++) { + /* retrieve column data as a string */ + ret = SQLGetData(stmt, i, SQL_C_CHAR, csvbuf + curlen, sizeof(csvbuf) - curlen, &indicator); + if (!SQL_SUCCEEDED(ret)) { + extract_error("SQLExecuteDirect for dbc", stmt, SQL_HANDLE_STMT); + exit(1); + } + curlen += indicator; + if (i != columns) + csvbuf[curlen++] = ','; + } + puts(csvbuf); + } + } + extract_error("SQLStmt for dbc", stmt, SQL_HANDLE_STMT); + exit(1); +} + +void extract_error(char *fn, SQLHANDLE handle, SQLSMALLINT type) +{ + SQLINTEGER i = 0; + SQLINTEGER native; + SQLCHAR state[ 7 ]; + SQLCHAR text[256]; + SQLSMALLINT len; + SQLRETURN ret; + + fprintf(stderr, + "\n" + "The driver reported the following diagnostics whilst running " + "%s\n\n", + fn); + + do + { + ret = SQLGetDiagRec(type, handle, ++i, state, &native, text, + sizeof(text), &len ); + if (SQL_SUCCEEDED(ret)) + printf("%s:%d:%d:%s\n", state, i, native, text); + } + while( ret == SQL_SUCCESS ); +}