#include "common.h" SQLHENV env; SQLHDBC conn; void print_diag(char *msg, SQLSMALLINT htype, SQLHANDLE handle) { char sqlstate[32]; char message[1000]; SQLINTEGER nativeerror; SQLSMALLINT textlen; SQLRETURN ret; SQLSMALLINT recno = 0; if (msg) printf("%s\n", msg); do { recno++; ret = SQLGetDiagRec(htype, handle, recno, sqlstate, &nativeerror, message, sizeof(message), &textlen); if (ret == SQL_INVALID_HANDLE) printf("Invalid handle\n"); else if (SQL_SUCCEEDED(ret)) printf("%s=%s\n", sqlstate, message); } while (ret == SQL_SUCCESS); if (ret == SQL_NO_DATA && recno == 1) printf("No error information\n"); } const char * const default_dsn = "psqlodbc_test_dsn"; const char * const test_dsn_env = "PSQLODBC_TEST_DSN"; const char * const test_dsn_ansi = "psqlodbc_test_dsn_ansi"; const char *get_test_dsn(void) { char *env = getenv(test_dsn_env); if (NULL != env && env[0]) return env; return default_dsn; } int IsAnsi(void) { return (NULL != strstr(get_test_dsn(), "_ansi")); } void test_connect_ext(char *extraparams) { SQLRETURN ret; SQLCHAR str[1024]; SQLSMALLINT strl; SQLCHAR dsn[1024]; const char * const test_dsn = get_test_dsn(); char *envvar; /* * Use an environment variable to switch settings of connection * strings throughout the regression test. Note that extraparams * parameters have precedence over the environment variable. * ODBC spec says * If any keywords are repeated in the connection string, * the driver uses the value associated with the first * occurrence of the keyword. * But the current psqlodbc driver uses the value associated with * the last occurrence of the keyword. Here we place extraparams * both before and after the value of the environment variable * so as to protect the priority order whichever way we take. */ if ((envvar = getenv("COMMON_CONNECTION_STRING_FOR_REGRESSION_TEST")) != NULL && envvar[0] != '\0') { if (NULL == extraparams) snprintf(dsn, sizeof(dsn), "DSN=%s;%s", test_dsn, envvar); else snprintf(dsn, sizeof(dsn), "DSN=%s;%s;%s;%s", test_dsn, extraparams, envvar, extraparams); } else snprintf(dsn, sizeof(dsn), "DSN=%s;%s", test_dsn, extraparams ? extraparams : ""); SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env); SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0); SQLAllocHandle(SQL_HANDLE_DBC, env, &conn); ret = SQLDriverConnect(conn, NULL, dsn, SQL_NTS, str, sizeof(str), &strl, SQL_DRIVER_COMPLETE); if (SQL_SUCCEEDED(ret)) { printf("connected\n"); } else { print_diag("SQLDriverConnect failed.", SQL_HANDLE_DBC, conn); fflush(stdout); exit(1); } } void test_connect(void) { test_connect_ext(NULL); } void test_disconnect(void) { SQLRETURN rc; printf("disconnecting\n"); rc = SQLDisconnect(conn); if (!SQL_SUCCEEDED(rc)) { print_diag("SQLDisconnect failed", SQL_HANDLE_DBC, conn); fflush(stdout); exit(1); } rc = SQLFreeHandle(SQL_HANDLE_DBC, conn); if (!SQL_SUCCEEDED(rc)) { print_diag("SQLFreeHandle failed", SQL_HANDLE_DBC, conn); fflush(stdout); exit(1); } conn = NULL; rc = SQLFreeHandle(SQL_HANDLE_ENV, env); if (!SQL_SUCCEEDED(rc)) { print_diag("SQLFreeHandle failed", SQL_HANDLE_ENV, env); fflush(stdout); exit(1); } env = NULL; } const char * datatype_str(SQLSMALLINT datatype) { static char buf[100]; switch (datatype) { case SQL_CHAR: return "CHAR"; case SQL_VARCHAR: return "VARCHAR"; case SQL_LONGVARCHAR: return "LONGVARCHAR"; case SQL_WCHAR: return "WCHAR"; case SQL_WVARCHAR: return "WVARCHAR"; case SQL_WLONGVARCHAR: return "WLONGVARCHAR"; case SQL_DECIMAL: return "DECIMAL"; case SQL_NUMERIC: return "NUMERIC"; case SQL_SMALLINT: return "SMALLINT"; case SQL_INTEGER: return "INTEGER"; case SQL_REAL: return "REAL"; case SQL_FLOAT: return "FLOAT"; case SQL_DOUBLE: return "DOUBLE"; case SQL_BIT: return "BIT"; case SQL_TINYINT: return "TINYINT"; case SQL_BIGINT: return "BIGINT"; case SQL_BINARY: return "BINARY"; case SQL_VARBINARY: return "VARBINARY"; case SQL_LONGVARBINARY: return "LONGVARBINARY"; case SQL_TYPE_DATE: return "TYPE_DATE"; case SQL_TYPE_TIME: return "TYPE_TIME"; case SQL_TYPE_TIMESTAMP: return "TYPE_TIMESTAMP"; case SQL_GUID: return "GUID"; default: snprintf(buf, sizeof(buf), "unknown sql type %d", datatype); return buf; } } const char *nullable_str(SQLSMALLINT nullable) { static char buf[100]; switch(nullable) { case SQL_NO_NULLS: return "not nullable"; case SQL_NULLABLE: return "nullable"; case SQL_NULLABLE_UNKNOWN: return "nullable_unknown"; default: snprintf(buf, sizeof(buf), "unknown nullable value %d", nullable); return buf; } } void print_result_meta_series(HSTMT hstmt, SQLSMALLINT *colids, SQLSMALLINT numcols) { int i; printf("Result set metadata:\n"); for (i = 0; i < numcols; i++) { SQLRETURN rc; SQLCHAR colname[50]; SQLSMALLINT colnamelen; SQLSMALLINT datatype; SQLULEN colsize; SQLSMALLINT decdigits; SQLSMALLINT nullable; rc = SQLDescribeCol(hstmt, colids[i], colname, sizeof(colname), &colnamelen, &datatype, &colsize, &decdigits, &nullable); if (!SQL_SUCCEEDED(rc)) { print_diag("SQLDescribeCol failed", SQL_HANDLE_STMT, hstmt); return; } printf("%s: %s(%u) digits: %d, %s\n", colname, datatype_str(datatype), (unsigned int) colsize, decdigits, nullable_str(nullable)); } } void print_result_meta(HSTMT hstmt) { SQLRETURN rc; SQLSMALLINT numcols, i; SQLSMALLINT *colids; rc = SQLNumResultCols(hstmt, &numcols); if (!SQL_SUCCEEDED(rc)) { print_diag("SQLNumResultCols failed", SQL_HANDLE_STMT, hstmt); return; } colids = (SQLSMALLINT *) malloc(numcols * sizeof(SQLSMALLINT)); for (i = 0; i < numcols; i++) colids[i] = i + 1; print_result_meta_series(hstmt, colids, numcols); free(colids); } /* * Initialize a buffer with "XxXxXx..." to indicate an uninitialized value. */ static void invalidate_buf(char *buf, size_t len) { size_t i; for (i = 0; i < len; i++) { if (i % 2 == 0) buf[i] = 'X'; else buf[i] = 'x'; } buf[len - 1] = '\0'; } /* * Print result only for the selected columns. */ void print_result_series(HSTMT hstmt, SQLSMALLINT *colids, SQLSMALLINT numcols, SQLINTEGER rowcount, BOOL printcolnames) { SQLRETURN rc; SQLINTEGER rowc = 0; char buf[40]; int i; printf("Result set:\n"); if (printcolnames) { for (i = 1; i <= numcols; i++) { invalidate_buf(buf, sizeof(buf)); rc = SQLColAttribute(hstmt, i, SQL_DESC_LABEL, buf, sizeof(buf), NULL, NULL); if (!SQL_SUCCEEDED(rc)) { print_diag("SQLColAttribute failed", SQL_HANDLE_STMT, hstmt); return; } printf("%s%s", (i > 1) ? "\t" : "", buf); } printf("\n"); } while (rowcount <0 || rowc < rowcount) { rc = SQLFetch(hstmt); if (rc == SQL_NO_DATA) break; if (rc == SQL_SUCCESS) { SQLLEN ind; rowc++; for (i = 0; i < numcols; i++) { /* * Initialize the buffer with garbage, so that we see readily * if SQLGetData fails to set the value properly or forgets * to null-terminate it. */ invalidate_buf(buf, sizeof(buf)); rc = SQLGetData(hstmt, colids[i], SQL_C_CHAR, buf, sizeof(buf), &ind); if (!SQL_SUCCEEDED(rc)) { print_diag("SQLGetData failed", SQL_HANDLE_STMT, hstmt); return; } if (ind == SQL_NULL_DATA) strcpy(buf, "NULL"); printf("%s%s", (i > 0) ? "\t" : "", buf); } printf("\n"); } else { print_diag("SQLFetch failed", SQL_HANDLE_STMT, hstmt); fflush(stdout); exit(1); } } } /* * Print result on all the columns */ void print_result_all(HSTMT hstmt, BOOL printcolnames) { SQLRETURN rc; SQLSMALLINT numcols, i; SQLSMALLINT *colids; rc = SQLNumResultCols(hstmt, &numcols); if (!SQL_SUCCEEDED(rc)) { print_diag("SQLNumResultCols failed", SQL_HANDLE_STMT, hstmt); return; } colids = (SQLSMALLINT *) malloc(numcols * sizeof(SQLSMALLINT)); for (i = 0; i < numcols; i++) colids[i] = i + 1; print_result_series(hstmt, colids, numcols, -1, printcolnames); free(colids); } /* * Print result on all the columns (without column names) */ void print_result(HSTMT hstmt) { print_result_all(hstmt, FALSE); } /* * Print result on all the columns (with column names) */ void print_result_with_column_names(HSTMT hstmt) { print_result_all(hstmt, TRUE); }