int /* return count of SQL text rows */ print_results(DBPROCESS *dbproc) { static const char empty_string[] = ""; struct METADATA *metadata = NULL; struct DATA { char *buffer; int status; } *data = NULL; RETCODE erc; int row_code; int c, ret; int iresultset; int nrows=0, ncols=0, ncomputeids; /* * Set up each result set with dbresults() */ for (iresultset=1; (erc = dbresults(dbproc)) != NO_MORE_RESULTS; iresultset++) { if (erc == FAIL) { fprintf(stderr, "%s:%d: dbresults(), result set %d failed\n", options.appname, __LINE__, iresultset); return -1; } if (SUCCEED != dbrows(dbproc)) { return 0; } /* Free prior allocations, if any. */ for (c=0; c < ncols; c++) { free(metadata[c].format_string); free(data[c].buffer); } free(metadata); metadata = NULL; free(data); data = NULL; ncols = 0; /* * Allocate memory for metadata and bound columns */ ncols = dbnumcols(dbproc); metadata = (struct METADATA*) calloc(ncols, sizeof(struct METADATA)); assert(metadata); data = (struct DATA*) calloc(ncols, sizeof(struct DATA)); assert(data); /* The hard-coded queries don't generate compute rows. */ ncomputeids = dbnumcompute(dbproc); assert(0 == ncomputeids); /* * 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 db-lib the address of the buffer we want filled as each row is fetched. */ for (c=0; c < ncols; c++) { int width; /* Get and print the metadata. Optional: get only what you need. */ char *name = dbcolname(dbproc, c+1); metadata[c].name = (name)? name : (char*) empty_string; name = dbcolsource(dbproc, c+1); metadata[c].source = (name)? name : (char*) empty_string; metadata[c].type = dbcoltype(dbproc, c+1); metadata[c].size = dbcollen(dbproc, c+1); assert(metadata[c].size != -1); /* -1 means indicates an out-of-range request*/ #if 0 fprintf(stderr, "%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)); #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?" */ width = get_printable_size(metadata[c].type, metadata[c].size); if (width < strlen(metadata[c].name)) width = strlen(metadata[c].name); ret = set_format_string(&metadata[c], (c+1 < ncols)? " " : "\n"); if (ret <= 0) { fprintf(stderr, "%s:%d: asprintf(), column %d failed\n", options.appname, __LINE__, c+1); return -1; } /* * Bind the column to our variable. * We bind everything to strings, because we want db-lib 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, metadata[c].size); assert(data[c].buffer); erc = dbbind(dbproc, c+1, STRINGBIND, -1, (BYTE *) data[c].buffer); if (erc == FAIL) { fprintf(stderr, "%s:%d: dbbind(), column %d failed\n", options.appname, __LINE__, c+1); return -1; } erc = dbnullbind(dbproc, c+1, &data[c].status); if (erc == FAIL) { fprintf(stderr, "%s:%d: dbnullbind(), column %d failed\n", options.appname, __LINE__, c+1); return -1; } } /* * Print the data to stdout. */ for (;(row_code = dbnextrow(dbproc)) != NO_MORE_ROWS; nrows++) { switch (row_code) { case REG_ROW: for (c=0; c < ncols; c++) { switch (data[c].status) { /* handle nulls */ case -1: /* is null */ fprintf(stderr, "defncopy: error: unexpected NULL row in SQL text\n"); break; case 0: /* OK */ default: /* >1 is datlen when buffer is too small */ fprintf(stdout, "%s", data[c].buffer); break; } } break; case BUF_FULL: default: fprintf(stderr, "defncopy: error: expected REG_ROW (%d), got %d instead\n", REG_ROW, row_code); assert(row_code == REG_ROW); break; } /* row_code */ } /* wend dbnextrow */ fprintf(stdout, "\n"); } /* wend dbresults */ return nrows; }
DBMSqlDatatable* SqlServer::exec(const core::string &sql) { MSSqlDatatable* dt = NULL; # ifdef _FREETDS RETCODE sta = dbcmd(d_ptr->proc, sql.c_str()); if (sta == FAIL) return NULL; sta = dbsqlexec(d_ptr->proc); if (sta == FAIL) return NULL; sta = dbresults(d_ptr->proc); if (sta == NO_MORE_RESULTS) return NULL; while (0 == dbrows(d_ptr->proc) && \ FAIL == dbresults(d_ptr->proc)); dt = new MSSqlDatatable(d_ptr->proc); dt->update(); MSSqlDatatable* prev_ = dt; while (SUCCEED == (sta = dbresults(d_ptr->proc))) { MSSqlDatatable* next_dt = new MSSqlDatatable(d_ptr->proc); next_dt->update(); prev_->next = next_dt; next_dt->prev = prev_; prev_ = next_dt; } # endif # ifdef _MSSQL _RecordsetPtr rcdset; try { rcdset.CreateInstance(__uuidof(Recordset)); rcdset->Open(sql.c_str(), _variant_t((IDispatch*)d_ptr->conn, true), adOpenStatic, adLockOptimistic, adCmdText); } catch (_com_error& err) { _bstr_t msg = err.ErrorMessage(); trace_msg((char const*)msg); return NULL; } catch (...) { trace_msg("failed to execute sql."); return NULL; } if (rcdset->adoEOF) return NULL; dt = new MSSqlDatatable(rcdset); dt->update(); # endif return dt; }