static int set_format_string(struct METADATA * meta, const char separator[]) { int width; const char *size_and_width; assert(meta); if ((width = get_printable_size(meta->type, meta->size)) == INT_MAX) { /* TEXT/IMAGE: no attempt to format the column; just splat the data */ meta->format_string = strdup(separator); return strlen(meta->format_string); } /* For anything except the default two-space separator, don't justify the strings. */ if(0 != strcmp(options.colsep, default_colsep)) { return asprintf(&meta->format_string, "%%s%s", separator); } /* Set the printing width large enough for the data or its title, whichever is greater. */ if (width < strlen(meta->name)) width = strlen(meta->name); /* right justify numbers, left justify strings */ size_and_width = is_character_data(meta->type)? "%%-%d.%ds%s" : "%%%d.%ds%s"; return asprintf(&meta->format_string, size_and_width, width, width, separator); }
static int set_format_string(struct METADATA * meta, const char separator[]) { int width, ret; const char *size_and_width; assert(meta); /* right justify numbers, left justify strings */ size_and_width = is_character_data(meta->type)? "%%-%d.%ds%s" : "%%%d.%ds%s"; width = get_printable_size(meta->type, meta->size); if (width < strlen(meta->name)) width = strlen(meta->name); ret = asprintf(&meta->format_string, size_and_width, width, width, separator); return ret; }
/** * Build the column header format string, based on the column width. * This is just one solution to the question, "How wide should my columns be when I print them out?" */ static int set_format_string(struct METADATA * meta, const char separator[]) { int width, ret; const char *size_and_width; assert(meta); if(0 == strcmp(options.colsep, default_colsep)) { /* right justify numbers, left justify strings */ size_and_width = is_character_data(meta->type)? "%%-%d.%ds%s" : "%%%d.%ds%s"; width = meta->width; /* get_printable_size(meta->type, meta->size); */ if (width < strlen(meta->name)) width = strlen(meta->name); ret = asprintf(&meta->format_string, size_and_width, width, width, separator); } else { /* For anything except the default two-space separator, don't justify the strings. */ ret = asprintf(&meta->format_string, "%%s%s", separator); } return ret; }
static void print_results(SQLHSTMT hStmt) { static const char dashes[] = "----------------------------------------------------------------" /* each line is 64 */ "----------------------------------------------------------------" "----------------------------------------------------------------" "----------------------------------------------------------------"; struct METADATA *metadata = NULL; struct DATA *data = NULL; SQLSMALLINT ncols = 0; RETCODE erc; int c, ret; /* * Process each resultset */ do { /* free metadata, in case it was previously allocated */ free_metadata(metadata, data, ncols); metadata = NULL; data = NULL; ncols = 0; /* * Allocate memory for metadata and bound columns */ if ((erc = SQLNumResultCols(hStmt, &ncols)) != SQL_SUCCESS){ odbc_perror(hStmt, erc, "SQLNumResultCols", "failed"); exit(EXIT_FAILURE); } metadata = (struct METADATA*) calloc(ncols, sizeof(struct METADATA)); assert(metadata); data = (struct DATA*) calloc(ncols, sizeof(struct DATA)); assert(data); /* * For each column, get its name, type, and size. * Allocate a buffer to hold the data, and bind the buffer to the column. * "bind" here means to give the address of the buffer we want filled as each row is fetched. */ fprintf(options.verbose, "Metadata\n"); fprintf(options.verbose, "%-6s %-30s %-10s %-18s %-6s %-6s \n", "col", "name", "type value", "type name", "size", "varies"); fprintf(options.verbose, "%.6s %.30s %.10s %.18s %.6s %.6s \n", dashes, dashes, dashes, dashes, dashes, dashes); for (c=0; c < ncols; c++) { /* Get and print the metadata. Optional: get only what you need. */ SQLCHAR name[512]; SQLSMALLINT namelen, ndigits, fnullable; if ((erc = SQLDescribeCol(hStmt, c+1, name, sizeof(name), &namelen, &metadata[c].type, &metadata[c].size, &ndigits, &fnullable)) != SQL_SUCCESS) { odbc_perror(hStmt, erc, "SQLDescribeCol", "failed"); exit(EXIT_FAILURE); } assert(namelen < sizeof(name)); name[namelen] = '\0'; metadata[c].name = strdup((char *) name); metadata[c].width = (ndigits > metadata[c].size)? ndigits : metadata[c].size; if (is_character_data(metadata[c].type)) { SQLHDESC hDesc; SQLINTEGER buflen; metadata[c].nchars = metadata[c].size; if ((erc = SQLAllocHandle(SQL_HANDLE_DESC, hStmt, &hDesc)) != SQL_SUCCESS) { odbc_perror(hStmt, erc, "SQLAllocHandle", "failed"); exit(EXIT_FAILURE); } if ((erc = SQLGetDescField(hDesc, c+1, SQL_DESC_OCTET_LENGTH, &metadata[c].size, sizeof(metadata[c].size), &buflen)) != SQL_SUCCESS) { odbc_perror(hStmt, erc, "SQLGetDescField", "failed"); exit(EXIT_FAILURE); } if ((erc = SQLFreeHandle(SQL_HANDLE_DESC, hStmt)) != SQL_SUCCESS) { odbc_perror(hStmt, erc, "SQLFreeHandle", "failed"); exit(EXIT_FAILURE); } } fprintf(options.verbose, "%6d %30s %10d %18s %6lu %6d \n", c+1, metadata[c].name, (int)metadata[c].type, prtype(metadata[c].type), (long unsigned int) metadata[c].size, -1); #if 0 fprintf(options.verbose, "%6d %30s %30s %15s %6d %6d \n", c+1, metadata[c].name, metadata[c].source, dbprtype(metadata[c].type), metadata[c].size, dbvarylen(dbproc, c+1)); metadata[c].width = get_printable_size(metadata[c].type, metadata[c].size); if (metadata[c].width < strlen(metadata[c].name)) metadata[c].width = strlen(metadata[c].name); #endif /* * Build the column header format string, based on the column width. * This is just one solution to the question, "How wide should my columns be when I print them out?" */ ret = set_format_string(&metadata[c], (c+1 < ncols)? options.colsep : "\n"); if (ret <= 0) { fprintf(stderr, "%s:%d: asprintf(), column %d failed\n", options.appname, __LINE__, c+1); return; } /* * Bind the column to our variable. * We bind everything to strings, because we want to convert everything to strings for us. * If you're performing calculations on the data in your application, you'd bind the numeric data * to C integers and floats, etc. instead. * * It is not necessary to bind to every column returned by the query. * Data in unbound columns are simply never copied to the user's buffers and are thus * inaccesible to the application. */ data[c].buffer = calloc(1, bufsize(&metadata[c])); assert(data[c].buffer); if ((erc = SQLBindCol(hStmt, c+1, SQL_C_CHAR, (SQLPOINTER)data[c].buffer, bufsize(&metadata[c]), &data[c].len)) != SQL_SUCCESS){ odbc_perror(hStmt, erc, "SQLBindCol", "failed"); exit(EXIT_FAILURE); } } if (!options.fquiet) { /* Print the column headers to stderr to keep them separate from the data. */ for (c=0; c < ncols; c++) { fprintf(options.headers, metadata[c].format_string, metadata[c].name); } /* Underline the column headers. */ for (c=0; c < ncols; c++) { fprintf(options.headers, metadata[c].format_string, dashes); } } /* * Print the data to stdout. */ while (ncols > 0 && (erc = SQLFetch(hStmt)) != SQL_NO_DATA) { switch(erc) { case SQL_SUCCESS_WITH_INFO: print_error_message(SQL_HANDLE_STMT, hStmt); case SQL_SUCCESS: break; default: odbc_perror(hStmt, erc, "SQLFetch", "failed"); exit(EXIT_FAILURE); } for (c=0; c < ncols; c++) { char *s; switch (data[c].len) { /* handle nulls */ case SQL_NULL_DATA: /* is null */ fprintf(stdout, metadata[c].format_string, "NULL"); break; default: assert(data[c].len > 0); s = calloc(1, 1 + data[c].len); assert(s); memcpy(s, data[c].buffer, data[c].len); fprintf(stdout, metadata[c].format_string, s); free(s); break; } } } if (ncols > 0 && erc == SQL_NO_DATA) print_error_message(SQL_HANDLE_STMT, hStmt); erc = SQLMoreResults(hStmt); fprintf(options.verbose, "SQLMoreResults returned %s\n", prret(erc)); switch (erc) { case SQL_NO_DATA: print_error_message(SQL_HANDLE_STMT, hStmt); break; case SQL_SUCCESS_WITH_INFO: print_error_message(SQL_HANDLE_STMT, hStmt); case SQL_SUCCESS: continue; default: odbc_perror(hStmt, erc, "SQLMoreResults", "failed"); exit(EXIT_FAILURE); } } while (erc != SQL_NO_DATA); if (erc != SQL_NO_DATA) { assert(erc != SQL_STILL_EXECUTING); odbc_perror(hStmt, erc, "SQLMoreResults", "failed"); exit(EXIT_FAILURE); } }