📄 dblib.c
字号:
static char *dbstring_get(DBSTRING * dbstr){ DBSTRING *next; int len; char *ret; char *cp; /* tdsdump_log(TDS_DBG_FUNC, "dbstring_get(%p)\n", dbstr); */ if (dbstr == NULL) { return NULL; } len = dbstring_length(dbstr); if ((ret = malloc(len + 1)) == NULL) { dbperror(NULL, SYBEMEM, errno); return NULL; } cp = ret; for (next = dbstr; next != NULL; next = next->strnext) { memcpy(cp, next->strtext, next->strtotlen); cp += next->strtotlen; } *cp = '\0'; return ret;}static const char *const opttext[DBNUMOPTIONS] = { "parseonly", "estimate", "showplan", "noexec", "arithignore", "nocount", "arithabort", "textlimit", "browse", "offsets", "statistics", "errlvl", "confirm", "spid", "buffer", "noautofree", "rowcount", "textsize", "language", "dateformat", "prpad", "prcolsep", "prlinelen", "prlinesep", "lfconvert", "datefirst", "chained", "fipsflagger", "transaction isolation level", "auth", "identity_insert", "no_identity_column", "cnv_date2char_short", "client cursors", "set time", "quoted_identifier"};static DBOPTION *init_dboptions(void){ DBOPTION *dbopts; int i; if ((dbopts = calloc(DBNUMOPTIONS, sizeof(DBOPTION))) == NULL) { dbperror(NULL, SYBEMEM, errno); return NULL; } for (i = 0; i < DBNUMOPTIONS; i++) { tds_strlcpy(dbopts[i].text, opttext[i], sizeof(dbopts[i].text)); dbopts[i].param = NULL; dbopts[i].factive = FALSE; } dbstring_assign(&(dbopts[DBPRPAD].param), " "); dbstring_assign(&(dbopts[DBPRCOLSEP].param), " "); dbstring_assign(&(dbopts[DBPRLINELEN].param), "80"); dbstring_assign(&(dbopts[DBPRLINESEP].param), "\n"); dbstring_assign(&(dbopts[DBCLIENTCURSORS].param), " "); dbstring_assign(&(dbopts[DBSETTIME].param), " "); return dbopts;}/** \internal * \ingroup dblib_internal * \brief Form a connection with the server. * * Called by the \c dbopen() macro, normally. If FreeTDS was configured with \c --enable-msdblib, this * function is called by (exported) \c dbopen() function. \c tdsdbopen is so-named to avoid * namespace conflicts with other database libraries that use the same function name. * \param login \c LOGINREC* carrying the account information. * \param server name of the dataserver to connect to. * \return valid pointer on successful login. * \retval NULL insufficient memory, unable to connect for any reason. * \sa dbopen() * \todo use \c asprintf() to avoid buffer overflow. * \todo separate error messages for \em no-such-server and \em no-such-user. */DBPROCESS *tdsdbopen(LOGINREC * login, const char *server, int msdblib){ DBPROCESS *dbproc; TDSCONNECTION *connection; tdsdump_log(TDS_DBG_FUNC, "dbopen(%p, %s, [%s])\n", login, server, msdblib? "microsoft" : "sybase"); if ((dbproc = calloc(1, sizeof(DBPROCESS))) == NULL) { dbperror(NULL, SYBEMEM, errno); return NULL; } dbproc->msdblib = msdblib; dbproc->dbopts = init_dboptions(); if (dbproc->dbopts == NULL) { free(dbproc); return NULL; } dbproc->dboptcmd = NULL; dbproc->avail_flag = TRUE; dbproc->command_state = DBCMDNONE; tds_set_server(login->tds_login, server); dbproc->tds_socket = tds_alloc_socket(dblib_get_tds_ctx(), 512); tds_set_parent(dbproc->tds_socket, dbproc); dbproc->tds_socket->option_flag2 &= ~0x02; /* we're not an ODBC driver */ dbproc->tds_socket->env_chg_func = db_env_chg; dbproc->envchange_rcv = 0; dbproc->dbcurdb[0] = '\0'; dbproc->servcharset[0] = '\0'; connection = tds_read_config_info(NULL, login->tds_login, g_dblib_ctx.tds_ctx->locale); if (!connection) { dbclose(dbproc); return NULL; } dbproc->chkintr = NULL; dbproc->hndlintr = NULL; TDS_MUTEX_LOCK(&dblib_mutex); /* override connection timeout if dbsetlogintime() was called */ if (g_dblib_ctx.login_timeout > 0) { connection->connect_timeout = g_dblib_ctx.login_timeout; } /* override query timeout if dbsettime() was called */ if (g_dblib_ctx.query_timeout > 0) { connection->query_timeout = g_dblib_ctx.query_timeout; } TDS_MUTEX_UNLOCK(&dblib_mutex); if (tds_connect(dbproc->tds_socket, connection) == TDS_FAIL) { tds_free_connection(connection); dbclose(dbproc); return NULL; } tds_free_connection(connection); dbproc->dbbuf = NULL; dbproc->dbbufsz = 0; TDS_MUTEX_LOCK(&dblib_mutex); dblib_add_connection(&g_dblib_ctx, dbproc->tds_socket); TDS_MUTEX_UNLOCK(&dblib_mutex); /* set the DBBUFFER capacity to nil */ buffer_set_capacity(dbproc, 0); TDS_MUTEX_LOCK(&dblib_mutex); if (g_dblib_ctx.recftos_filename != NULL) { char *temp_filename = NULL; const int len = asprintf(&temp_filename, "%s.%d", g_dblib_ctx.recftos_filename, g_dblib_ctx.recftos_filenum); if (len >= 0) { dbproc->ftos = fopen(temp_filename, "w"); if (dbproc->ftos != NULL) { fprintf(dbproc->ftos, "/* dbopen() at %s */\n", _dbprdate(temp_filename)); fflush(dbproc->ftos); g_dblib_ctx.recftos_filenum++; } free(temp_filename); } } memcpy(dbproc->nullreps, default_null_representations, sizeof(default_null_representations)); TDS_MUTEX_UNLOCK(&dblib_mutex); return dbproc;}/** * \ingroup dblib_core * \brief \c printf-like way to form SQL to send to the server. * * Forms a command string and writes to the command buffer with dbcmd(). * \param dbproc contains all information needed by db-lib to manage communications with the server. * \param fmt <tt> man vasprintf</tt> for details. * \retval SUCCEED success. * \retval FAIL insufficient memory, or dbcmd() failed. * \sa dbcmd(), dbfreebuf(), dbgetchar(), dbopen(), dbstrcpy(), dbstrlen(). */RETCODEdbfcmd(DBPROCESS * dbproc, const char *fmt, ...){ va_list ap; char *s; int len; RETCODE ret; tdsdump_log(TDS_DBG_FUNC, "dbfcmd(%p, %s, ...)\n", dbproc, fmt); CHECK_DBPROC(); DBPERROR_RETURN(IS_TDSDEAD(dbproc->tds_socket), SYBEDDNE); CHECK_NULP(fmt, "dbfcmd", 2, FAIL); va_start(ap, fmt); len = vasprintf(&s, fmt, ap); va_end(ap); if (len < 0) { dbperror(NULL, SYBEMEM, errno); return FAIL; } ret = dbcmd(dbproc, s); free(s); return ret;}/** * \ingroup dblib_core * \brief \c Append SQL to the command buffer. * * \param dbproc contains all information needed by db-lib to manage communications with the server. * \param cmdstring SQL to append to the command buffer. * \retval SUCCEED success. * \retval FAIL insufficient memory. * \remarks set command state to \c DBCMDPEND unless the command state is DBCMDSENT, in which case * it frees the command buffer. This latter may or may not be the Right Thing to do. * \sa dbfcmd(), dbfreebuf(), dbgetchar(), dbopen(), dbstrcpy(), dbstrlen(). */RETCODEdbcmd(DBPROCESS * dbproc, const char *cmdstring){ int newsz; void *p; tdsdump_log(TDS_DBG_FUNC, "dbcmd(%p, %s)\n", dbproc, cmdstring); CHECK_DBPROC(); DBPERROR_RETURN(IS_TDSDEAD(dbproc->tds_socket), SYBEDDNE); CHECK_NULP(cmdstring, "dbcmd", 2, FAIL); dbproc->avail_flag = FALSE; tdsdump_log(TDS_DBG_FUNC, "dbcmd() bufsz = %d\n", dbproc->dbbufsz); if (dbproc->command_state == DBCMDSENT) { if (!dbproc->noautofree) { dbfreebuf(dbproc); } } if (dbproc->dbbufsz == 0) { dbproc->dbbuf = malloc(strlen(cmdstring) + 1); if (dbproc->dbbuf == NULL) { dbperror(NULL, SYBEMEM, errno); return FAIL; } strcpy((char *) dbproc->dbbuf, cmdstring); dbproc->dbbufsz = strlen(cmdstring) + 1; } else { newsz = strlen(cmdstring) + dbproc->dbbufsz; if ((p = realloc(dbproc->dbbuf, newsz)) == NULL) { dbperror(NULL, SYBEMEM, errno); return FAIL; } dbproc->dbbuf = (unsigned char *) p; strcat((char *) dbproc->dbbuf, cmdstring); dbproc->dbbufsz = newsz; } dbproc->command_state = DBCMDPEND; return SUCCEED;}/** * \ingroup dblib_core * \brief send the SQL command to the server and wait for an answer. * * Please be patient. This function waits for the server to respond. \c dbsqlexec is equivalent * to dbsqlsend() followed by dbsqlok(). * \param dbproc contains all information needed by db-lib to manage communications with the server. * \retval SUCCEED query was processed without errors. * \retval FAIL was returned by dbsqlsend() or dbsqlok(). * \sa dbcmd(), dbfcmd(), dbnextrow(), dbresults(), dbretstatus(), dbsettime(), dbsqlok(), dbsqlsend() */RETCODEdbsqlexec(DBPROCESS * dbproc){ RETCODE rc = FAIL; tdsdump_log(TDS_DBG_FUNC, "dbsqlexec(%p)\n", dbproc); CHECK_DBPROC(); DBPERROR_RETURN(IS_TDSDEAD(dbproc->tds_socket), SYBEDDNE); if (SUCCEED == (rc = dbsqlsend(dbproc))) { rc = dbsqlok(dbproc); } return rc;}/** * \ingroup dblib_core * \brief Change current database. * * Analagous to the unix command \c cd, dbuse() makes \a name the default database. Waits for an answer * from the server. * \param dbproc contains all information needed by db-lib to manage communications with the server. * \param name database to use. * \retval SUCCEED query was processed without errors. * \retval FAIL query was not processed * \todo \a name should be quoted. * \sa dbchange(), dbname(). */RETCODEdbuse(DBPROCESS * dbproc, const char *name){ RETCODE rc; char *query; tdsdump_log(TDS_DBG_FUNC, "dbuse(%p, %s)\n", dbproc, name); CHECK_DBPROC(); DBPERROR_RETURN(IS_TDSDEAD(dbproc->tds_socket), SYBEDDNE); CHECK_NULP(name, "dbuse", 2, FAIL); if (!dbproc->tds_socket) return FAIL; /* quote name */ query = malloc(tds_quote_id(dbproc->tds_socket, NULL, name, -1) + 6); if (!query) { dbperror(NULL, SYBEMEM, errno); return FAIL; } strcpy(query, "use "); /* TODO PHP suggest to quote by yourself with []... what should I do ?? quote or not ?? */ if (name[0] == '[' && name[strlen(name)-1] == ']') strcat(query, name); else tds_quote_id(dbproc->tds_socket, query + 4, name, -1); rc = SUCCEED; if ((dbcmd(dbproc, query) == FAIL) || (dbsqlexec(dbproc) == FAIL) || (dbresults(dbproc) == FAIL) || (dbcanquery(dbproc) == FAIL)) rc = FAIL; free(query); return rc;}/** * \ingroup dblib_core * \brief Close a connection to the server and free associated resources. * * \param dbproc contains all information needed by db-lib to manage communications with the server. * \sa dbexit(), dbopen(). */voiddbclose(DBPROCESS * dbproc){ TDSSOCKET *tds; int i; char timestr[256]; tdsdump_log(TDS_DBG_FUNC, "dbclose(%p)\n", dbproc); CHECK_PARAMETER(dbproc, SYBENULL, ); tds = dbproc->tds_socket; if (tds) { /* * this MUST be done before socket destruction * it is possible that a TDSSOCKET is allocated on same position */ TDS_MUTEX_LOCK(&dblib_mutex); dblib_del_connection(&g_dblib_ctx, dbproc->tds_socket); TDS_MUTEX_UNLOCK(&dblib_mutex); tds_free_socket(tds); dblib_release_tds_ctx(1); } buffer_free(&(dbproc->row_buf)); if (dbproc->ftos != NULL) { fprintf(dbproc->ftos, "/* dbclose() at %s */\n", _dbprdate(timestr)); fclose(dbproc->ftos); } if (dbproc->bcpinfo) free(dbproc->bcpinfo->tablename); if (dbproc->hostfileinfo) { free(dbproc->hostfileinfo->hostfile); free(dbproc->hostfileinfo->errorfile); if (dbproc->hostfileinfo->host_columns) { for (i = 0; i < dbproc->hostfileinfo->host_colcount; i++) { free(dbproc->hostfileinfo->host_columns[i]->terminator); free(dbproc->hostfileinfo->host_columns[i]); } free(dbproc->hostfileinfo->host_columns); } } for (i = 0; i < DBNUMOPTIONS; i++) { dbstring_free(&(dbproc->dbopts[i].param)); } free(dbproc->dbopts); dbstring_free(&(dbproc->dboptcmd)); for (i=0; i < MAXBINDTYPES; i++) { if (dbproc->nullreps[i].bindval != default_null_representations[i].bindval) free((BYTE*)dbproc->nullreps[i].bindval); } dbfreebuf(dbproc); free(dbproc); return;}/** * \ingroup dblib_core * \brief Close server connections and free all related structures. * * \sa dbclose(), dbinit(), dbopen(). * \todo breaks if ctlib/dblib used in same process. */voiddbexit(){ TDSSOCKET *tds; DBPROCESS *dbproc; int i, list_size; int count = 1; tdsdump_log(TDS_DBG_FUNC, "dbexit(void)\n"); TDS_MUTEX_LOCK(&dblib_mutex); if (--g_dblib_ctx.ref_count != 0) { TDS_MUTEX_UNLOCK(&dblib_mutex); return; } list_size = g_dblib_ctx.connection_list_size; for (i = 0; i < list_size; i++) { tds = g_dblib_ctx.connection_list[i]; g_dblib_ctx.connection_list[i] = NULL; if (tds) { ++count; dbproc = (DBPROCESS *) tds->parent; tds_free_socket(tds); if (dbproc) { /* avoid locking in dbclose */ dbproc->tds_socket = NULL; dbclose(dbproc); } } } if (g_dblib_ctx.connection_list) { TDS_ZERO_FREE(g_dblib_ctx.connection_list); g_dblib_ctx.connection_list_size = 0; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -