Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions include/freetds/odbc.h
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,14 @@ struct _hdbc
TDS_INT default_query_timeout;

TDSBCPINFO *bcpinfo;

/* ODBC extra options. Format strings for tds_strftime()
* to be applied when a client application fetches a date/time field
* as a character type. (Not used for the reverse direction)
*/
DSTR datetime_fmt;
DSTR date_fmt;
DSTR time_fmt;
};

struct _hsattr
Expand Down Expand Up @@ -452,7 +460,7 @@ typedef struct _hchk TDS_CHK;
typedef struct {
/* this must be the first member */
TDSCOLUMNFUNCS common;
void (*set_type_info)(TDSCOLUMN *col, struct _drecord *drec, SQLINTEGER odbc_ver);
void (*set_type_info)(TDSCOLUMN *col, struct _drecord *drec, const TDS_DBC *dbc);
} TDS_FUNCS;

#define IS_HENV(x) (((TDS_CHK *)x)->htype == SQL_HANDLE_ENV)
Expand Down Expand Up @@ -507,6 +515,8 @@ bool get_login_info(HWND hwndParent, TDSLOGIN * login);
ODBC_PARAM(ClientCharset) \
ODBC_PARAM(ConnectionTimeout) \
ODBC_PARAM(Database) \
ODBC_PARAM(DateFmt) \
ODBC_PARAM(DateTimeFmt) \
ODBC_PARAM(DebugFlags) \
ODBC_PARAM(DSN) \
ODBC_PARAM(DumpFile) \
Expand All @@ -526,6 +536,7 @@ bool get_login_info(HWND hwndParent, TDSLOGIN * login);
ODBC_PARAM(ServerSPN) \
ODBC_PARAM(TDS_Version) \
ODBC_PARAM(TextSize) \
ODBC_PARAM(TimeFmt) \
ODBC_PARAM(Timeout) \
ODBC_PARAM(Trusted_Connection) \
ODBC_PARAM(UID) \
Expand Down Expand Up @@ -646,7 +657,7 @@ SQLRETURN odbc_set_stmt_query(struct _hstmt *stmt, const ODBC_CHAR *sql, ptrdiff
void odbc_set_return_status(struct _hstmt *stmt, unsigned int n_row);
void odbc_set_return_params(struct _hstmt *stmt, unsigned int n_row);

void odbc_set_sql_type_info(TDSCOLUMN * col, struct _drecord *drec, SQLINTEGER odbc_ver);
void odbc_set_sql_type_info(TDSCOLUMN* col, struct _drecord* drec, const TDS_DBC* dbc);

int odbc_sql_to_c_type_default(int sql_type);
TDS_SERVER_TYPE odbc_sql_to_server_type(TDSCONNECTION * conn, int sql_type, int sql_unsigned);
Expand Down
3 changes: 3 additions & 0 deletions include/freetds/tds/convert.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ TDS_INT tds_convert(const TDSCONTEXT * context, int srctype, const void *src, TD

size_t tds_strftime(char *buf, size_t maxsize, const char *format, const TDSDATEREC * timeptr, int prec);

/** Maximum expected size of strftime() output for a given string and precision (excluding null terminator) */
size_t tds_strftime_maxsize(const char* format, int prec);

/* Fast int to string (massively outperforms sprintf in hot loop).
* No null termination; returns number of characters read.
*/
Expand Down
6 changes: 5 additions & 1 deletion include/odbcss.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,11 @@ typedef struct tagSS_TIMESTAMPOFFSET_STRUCT {
SQLSMALLINT timezone_minute;
} SQL_SS_TIMESTAMPOFFSET_STRUCT;

#define SQL_COPT_TDSODBC_IMPL_BASE 1500
/* note: values +0 to +8 allocated to deprecated TDSODBC_BCP options below */
#define SQL_COPT_TDSODBC_DATETIME_FORMAT (SQL_COPT_TDSODBC_IMPL_BASE+9)
#define SQL_COPT_TDSODBC_DATE_FORMAT (SQL_COPT_TDSODBC_IMPL_BASE+10)
#define SQL_COPT_TDSODBC_TIME_FORMAT (SQL_COPT_TDSODBC_IMPL_BASE+11)

#ifdef TDSODBC_BCP

Expand All @@ -179,7 +184,6 @@ typedef struct tagSS_TIMESTAMPOFFSET_STRUCT {
#define SQL_BCP_OFF 0
#define SQL_BCP_ON 1

#define SQL_COPT_TDSODBC_IMPL_BASE 1500
#define SQL_COPT_TDSODBC_IMPL_BCP_INITA (SQL_COPT_TDSODBC_IMPL_BASE)
/* deprecated SQL_COPT_TDSODBC_IMPL_BCP_CONTROL */
#define SQL_COPT_TDSODBC_IMPL_BCP_COLPTR (SQL_COPT_TDSODBC_IMPL_BASE+2)
Expand Down
3 changes: 3 additions & 0 deletions src/odbc/connectparams.c
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,9 @@ odbc_parse_connect_string(TDS_ERRS *errs, const char *connect_string, const char
|| strcasecmp(option, "client_charset") == 0) {
num_param = ODBC_PARAM_ClientCharset;
tds_parse_conf_section(TDS_STR_CLCHARSET, tds_dstr_cstr(&value), login);
} else if (CHK_PARAM(DateTimeFmt)) {
} else if (CHK_PARAM(DateFmt)) {
} else if (CHK_PARAM(TimeFmt)) {
} else if (CHK_PARAM(DumpFile)) {
tds_parse_conf_section(TDS_STR_DUMPFILE, tds_dstr_cstr(&value), login);
} else if (CHK_PARAM(DumpFileAppend)) {
Expand Down
8 changes: 5 additions & 3 deletions src/odbc/convert_tds2sql.c
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,8 @@ odbc_tds2sql(TDS_STMT * stmt, TDSCOLUMN *curcol, int srctype, TDS_CHAR * src, TD
const char *fmt = NULL;
const TDS_DATETIMEALL *dta = (const TDS_DATETIMEALL *) src;

/* This logic should match odbc_datetime_display_size() in odbc_data.c */
/* TODO: so, instead of dta->time_prec this should be using curcol->column_prec? */
switch (srctype) {
case SYBMSDATETIMEOFFSET:
case SYBMSDATETIME2:
Expand All @@ -381,7 +383,7 @@ odbc_tds2sql(TDS_STMT * stmt, TDSCOLUMN *curcol, int srctype, TDS_CHAR * src, TD
case SYBDATETIME4:
prec = 0;
datetime:
fmt = "%Y-%m-%d %H:%M:%S.%z";
fmt = tds_dstr_cstr(&stmt->dbc->datetime_fmt);
break;
case SYBMSTIME:
prec = dta->time_prec;
Expand All @@ -392,12 +394,12 @@ odbc_tds2sql(TDS_STMT * stmt, TDSCOLUMN *curcol, int srctype, TDS_CHAR * src, TD
case SYBTIME:
prec = 3;
time:
fmt = "%H:%M:%S.%z";
fmt = tds_dstr_cstr(&stmt->dbc->time_fmt);
break;
case SYBMSDATE:
case SYBDATE:
prec = 0;
fmt = "%Y-%m-%d";
fmt = tds_dstr_cstr(&stmt->dbc->date_fmt);
break;
}
if (!fmt) goto normal_conversion;
Expand Down
49 changes: 46 additions & 3 deletions src/odbc/odbc.c
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,19 @@ odbc_prepare(TDS_STMT *stmt)
ODBC_RETURN_(stmt);
}

/** Helper for SQLDriverConnect to apply DBC parameters */
static void dbc_apply_param(TDS_DBC* dbc, DSTR* dst, const TDS_PARSED_PARAM* param)
{
if (param->len > 0)
{
/* TDS_PARSED_PARAM still contains the optional {} delimiters */
int offset = (param->p[0] == '{' && param->p[param->len - 1] == '}');

if (!tds_dstr_copyn(dst, param->p + offset, param->len - 2 * offset))
odbc_errs_add(&dbc->errs, "HY001", NULL);
}
}

ODBC_FUNC(SQLDriverConnect, (P(SQLHDBC,hdbc), P(SQLHWND,hwnd), PCHARIN(ConnStrIn,SQLSMALLINT),
PCHAROUT(ConnStrOut,SQLSMALLINT), P(SQLUSMALLINT,fDriverCompletion) WIDE))
{
Expand Down Expand Up @@ -633,6 +646,10 @@ ODBC_FUNC(SQLDriverConnect, (P(SQLHDBC,hdbc), P(SQLHWND,hwnd), PCHARIN(ConnStrIn
odbc_set_dstr(dbc, szConnStrOut, cbConnStrOutMax, pcbConnStrOut, &conn_str);
tds_dstr_free(&conn_str);

dbc_apply_param(dbc, &dbc->datetime_fmt, &params[ODBC_PARAM_DateTimeFmt]);
dbc_apply_param(dbc, &dbc->date_fmt, &params[ODBC_PARAM_DateFmt]);
dbc_apply_param(dbc, &dbc->time_fmt, &params[ODBC_PARAM_TimeFmt]);

/* add login info */
if (hwnd && fDriverCompletion != SQL_DRIVER_NOPROMPT
&& (fDriverCompletion == SQL_DRIVER_PROMPT || (!params[ODBC_PARAM_UID].p && !params[ODBC_PARAM_Trusted_Connection].p)
Expand Down Expand Up @@ -1766,6 +1783,15 @@ odbc_SQLAllocConnect(SQLHENV henv, SQLHDBC FAR * phdbc)
dbc->attr.mars_enabled = SQL_MARS_ENABLED_NO;
dbc->attr.bulk_enabled = SQL_BCP_OFF;

/* Initial values of DBC date formats taken from environment context.
* odbc_SQLAllocEnv() always initializes that. */
tds_dstr_init(&dbc->datetime_fmt);
tds_dstr_copy(&dbc->datetime_fmt, env->tds_ctx->locale->datetime_fmt);
tds_dstr_init(&dbc->date_fmt);
tds_dstr_copy(&dbc->date_fmt, env->tds_ctx->locale->date_fmt);
tds_dstr_init(&dbc->time_fmt);
tds_dstr_copy(&dbc->time_fmt, env->tds_ctx->locale->time_fmt);

tds_mutex_init(&dbc->mtx);
*phdbc = (SQLHDBC) dbc;

Expand Down Expand Up @@ -1810,7 +1836,7 @@ odbc_SQLAllocEnv(SQLHENV FAR * phenv, SQLINTEGER odbc_version)
ctx->msg_handler = odbc_errmsg_handler;
ctx->err_handler = odbc_errmsg_handler;

/* ODBC has its own format */
/* ODBC driver date-to-char conversion: mimic MS client */
free(ctx->locale->datetime_fmt);
ctx->locale->datetime_fmt = strdup("%Y-%m-%d %H:%M:%S.%z");
free(ctx->locale->date_fmt);
Expand Down Expand Up @@ -3299,7 +3325,7 @@ odbc_populate_ird(TDS_STMT * stmt)
* is formatting function correct ??
* we should not convert to string with invalid precision!
*/
odbc_set_sql_type_info(col, drec, stmt->dbc->env->attr.odbc_version);
odbc_set_sql_type_info(col, drec, stmt->dbc);

drec->sql_desc_fixed_prec_scale = (col->column_prec && col->column_scale) ? SQL_TRUE : SQL_FALSE;
if (!tds_dstr_dup(&drec->sql_desc_label, &col->column_name))
Expand Down Expand Up @@ -4349,6 +4375,11 @@ odbc_SQLFreeConnect(SQLHDBC hdbc)
tds_free_socket(dbc->tds_socket);

odbc_bcp_free_storage(dbc);

tds_dstr_free(&dbc->datetime_fmt);
tds_dstr_free(&dbc->date_fmt);
tds_dstr_free(&dbc->time_fmt);

/* free attributes */
#ifdef TDS_NO_DM
tds_dstr_free(&dbc->attr.tracefile);
Expand Down Expand Up @@ -6607,6 +6638,18 @@ ODBC_FUNC(SQLSetConnectAttr, (P(SQLHDBC,hdbc), P(SQLINTEGER,Attribute), P(SQLPOI
(const ODBC_CHAR *) params->errfile, params->direction _wide0);
}
break;
case SQL_COPT_TDSODBC_DATETIME_FORMAT:
if (!odbc_dstr_copy(dbc, &dbc->datetime_fmt, StringLength, (ODBC_CHAR*)ValuePtr))
odbc_errs_add(&dbc->errs, "HY001", NULL);
break;
case SQL_COPT_TDSODBC_DATE_FORMAT:
if (!odbc_dstr_copy(dbc, &dbc->date_fmt, StringLength, (ODBC_CHAR*)ValuePtr))
odbc_errs_add(&dbc->errs, "HY001", NULL);
break;
case SQL_COPT_TDSODBC_TIME_FORMAT:
if (!odbc_dstr_copy(dbc, &dbc->time_fmt, StringLength, (ODBC_CHAR*)ValuePtr))
odbc_errs_add(&dbc->errs, "HY001", NULL);
break;
#ifdef ENABLE_ODBC_WIDE
case SQL_COPT_TDSODBC_IMPL_BCP_INITW:
if (!ValuePtr)
Expand Down Expand Up @@ -8100,7 +8143,7 @@ read_params(TDS_STMT *stmt)
col->column_prec = precision;
col->column_scale = scale;
drec->sql_desc_nullable = SQL_NULLABLE;
odbc_set_sql_type_info(col, drec, stmt->dbc->env->attr.odbc_version);
odbc_set_sql_type_info(col, drec, stmt->dbc);
break;
}
continue;
Expand Down
Loading
Loading