Skip to content

Commit

Permalink
ODBC-205 It was a regression, thus no test.
Browse files Browse the repository at this point in the history
The patch moves string to date/time types conversion from C/C on C/ODBC
side to better meet ODBC requirements.
  • Loading branch information
lawrinn committed Dec 10, 2018
1 parent 20e0a50 commit cb5b7ce
Show file tree
Hide file tree
Showing 8 changed files with 185 additions and 58 deletions.
2 changes: 1 addition & 1 deletion libmariadb
23 changes: 22 additions & 1 deletion ma_helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,28 @@ int MADB_GetMaDBTypeAndLength(SQLINTEGER SqlDataType, my_bool *Unsigned, unsigne
}
/* }}} */

void MADB_CopyOdbcTsToMadbTime(SQL_TIMESTAMP_STRUCT *Src, MYSQL_TIME *Dst)
{
Dst->year= Src->year;
Dst->month= Src->month;
Dst->day= Src->day;
Dst->hour= Src->hour;
Dst->minute= Src->minute;
Dst->second= Src->second;
Dst->second_part= Src->fraction / 1000;
}

void MADB_CopyMadbTimeToOdbcTs(MYSQL_TIME *Src, SQL_TIMESTAMP_STRUCT *Dst)
{
Dst->year= Src->year;
Dst->month= Src->month;
Dst->day= Src->day;
Dst->hour= Src->hour;
Dst->minute= Src->minute;
Dst->second= Src->second;
Dst->fraction= Src->second_part*1000;
}

SQLRETURN MADB_CopyMadbTimestamp(MADB_Stmt *Stmt, MYSQL_TIME *tm, SQLPOINTER DataPtr, SQLLEN *Length, SQLLEN *Ind,
SQLSMALLINT CType, SQLSMALLINT SqlType)
{
Expand Down Expand Up @@ -839,7 +861,6 @@ SQLRETURN MADB_CopyMadbTimestamp(MADB_Stmt *Stmt, MYSQL_TIME *tm, SQLPOINTER Dat
ts->hour= tm->hour;
ts->minute= tm->minute;
ts->second= tm->second;


if (ts->year + ts->month + ts->day + ts->hour + ts->minute + ts->fraction + ts->second == 0)
{
Expand Down
2 changes: 2 additions & 0 deletions ma_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ unsigned int GetMultiStatements(MADB_Stmt *Stmt, BOOL ExecDirect);
int MADB_KeyTypeCount(MADB_Dbc *Connection, char *TableName, int KeyFlag);
MYSQL_RES *MADB_ReadDefaultValues(MADB_Dbc *Dbc, const char *Catalog, const char *TableName);
int MADB_GetDefaultType(int SQLDataType);
void MADB_CopyOdbcTsToMadbTime(SQL_TIMESTAMP_STRUCT *Src, MYSQL_TIME *Dst);
void MADB_CopyMadbTimeToOdbcTs(MYSQL_TIME *Src, SQL_TIMESTAMP_STRUCT *Dst);
SQLRETURN MADB_CopyMadbTimestamp(MADB_Stmt *Stmt, MYSQL_TIME *tm, SQLPOINTER DataPtr, SQLLEN *Length, SQLLEN *Ind,
SQLSMALLINT CType, SQLSMALLINT SqlType);
int MADB_GetWCharType(int Type);
Expand Down
162 changes: 134 additions & 28 deletions ma_statement.c
Original file line number Diff line number Diff line change
Expand Up @@ -1535,20 +1535,48 @@ SQLRETURN MADB_PrepareBind(MADB_Stmt *Stmt, int RowNumber)
case SQL_C_TIME:
case SQL_C_DATE:
MADB_FREE(ArdRec->InternalBuffer);
ArdRec->InternalBuffer= (char *)MADB_CALLOC(sizeof(MYSQL_TIME));
Stmt->result[i].buffer= ArdRec->InternalBuffer;
Stmt->result[i].buffer_length= sizeof(MYSQL_TIME);
Stmt->result[i].buffer_type= MYSQL_TYPE_TIMESTAMP;
if (IrdRec->ConciseType == SQL_CHAR || IrdRec->ConciseType == SQL_VARCHAR)
{
ArdRec->InternalBuffer= (char *)MADB_CALLOC(Stmt->stmt->fields[i].max_length + 1);
if (ArdRec->InternalBuffer == NULL)
{
return MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0);
}
Stmt->result[i].buffer= ArdRec->InternalBuffer;
Stmt->result[i].buffer_type= MYSQL_TYPE_STRING;
Stmt->result[i].buffer_length= Stmt->stmt->fields[i].max_length + 1;
}
else
{
ArdRec->InternalBuffer= (char *)MADB_CALLOC(sizeof(MYSQL_TIME));
Stmt->result[i].buffer= ArdRec->InternalBuffer;
Stmt->result[i].buffer_length= sizeof(MYSQL_TIME);
Stmt->result[i].buffer_type= MYSQL_TYPE_TIMESTAMP;
}
break;
case SQL_C_INTERVAL_HOUR_TO_MINUTE:
case SQL_C_INTERVAL_HOUR_TO_SECOND:
{
MYSQL_FIELD *Field= mysql_fetch_field_direct(Stmt->metadata, i);
MADB_FREE(ArdRec->InternalBuffer);
ArdRec->InternalBuffer= (char *)MADB_CALLOC(sizeof(MYSQL_TIME));
Stmt->result[i].buffer= ArdRec->InternalBuffer;
Stmt->result[i].buffer_length= sizeof(MYSQL_TIME);
Stmt->result[i].buffer_type= Field && Field->type == MYSQL_TYPE_TIME ? MYSQL_TYPE_TIME : MYSQL_TYPE_TIMESTAMP;
if (IrdRec->ConciseType == SQL_CHAR || IrdRec->ConciseType == SQL_VARCHAR)
{
ArdRec->InternalBuffer= (char *)MADB_CALLOC(Stmt->stmt->fields[i].max_length + 1);
if (ArdRec->InternalBuffer == NULL)
{
return MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0);
}
Stmt->result[i].buffer= ArdRec->InternalBuffer;
Stmt->result[i].buffer_type= MYSQL_TYPE_STRING;
Stmt->result[i].buffer_length= Stmt->stmt->fields[i].max_length + 1;
}
else
{
ArdRec->InternalBuffer= (char *)MADB_CALLOC(sizeof(MYSQL_TIME));
Stmt->result[i].buffer= ArdRec->InternalBuffer;
Stmt->result[i].buffer_length= sizeof(MYSQL_TIME);
Stmt->result[i].buffer_type= Field && Field->type == MYSQL_TYPE_TIME ? MYSQL_TYPE_TIME : MYSQL_TYPE_TIMESTAMP;
}
}
break;
case SQL_C_TINYINT:
Expand Down Expand Up @@ -1682,15 +1710,55 @@ SQLRETURN MADB_FixFetchedValues(MADB_Stmt *Stmt, int RowNumber, MYSQL_ROW_OFFSET
case SQL_C_TIMESTAMP:
case SQL_C_TIME:
case SQL_C_DATE:
FieldRc= MADB_CopyMadbTimestamp(Stmt, (MYSQL_TIME *)ArdRec->InternalBuffer, DataPtr, LengthPtr, IndicatorPtr, ArdRec->Type, IrdRec->ConciseType);
CALC_ALL_FLDS_RC(rc, FieldRc);
{
MYSQL_TIME tm, *Intermidiate;

if (IrdRec->ConciseType == SQL_CHAR || IrdRec->ConciseType == SQL_VARCHAR)
{
BOOL isTime;

FieldRc= MADB_Str2Ts(ArdRec->InternalBuffer, *Stmt->stmt->bind[i].length, &tm, FALSE, &Stmt->Error, &isTime);
if (SQL_SUCCEEDED(FieldRc))
{
Intermidiate= &tm;
}
else
{
CALC_ALL_FLDS_RC(rc, FieldRc);
break;
}
}
else
{
Intermidiate= (MYSQL_TIME *)ArdRec->InternalBuffer;
}

FieldRc= MADB_CopyMadbTimestamp(Stmt, Intermidiate, DataPtr, LengthPtr, IndicatorPtr, ArdRec->Type, IrdRec->ConciseType);
CALC_ALL_FLDS_RC(rc, FieldRc);
}
break;
case SQL_C_INTERVAL_HOUR_TO_MINUTE:
case SQL_C_INTERVAL_HOUR_TO_SECOND:
{
MYSQL_TIME *tm= (MYSQL_TIME*)ArdRec->InternalBuffer;
MYSQL_TIME *tm= (MYSQL_TIME*)ArdRec->InternalBuffer, ForConversion;
SQL_INTERVAL_STRUCT *ts= (SQL_INTERVAL_STRUCT *)DataPtr;

if (IrdRec->ConciseType == SQL_CHAR || IrdRec->ConciseType == SQL_VARCHAR)
{
BOOL isTime;

FieldRc= MADB_Str2Ts(ArdRec->InternalBuffer, *Stmt->stmt->bind[i].length, &ForConversion, FALSE, &Stmt->Error, &isTime);
if (SQL_SUCCEEDED(FieldRc))
{
tm= &ForConversion;
}
else
{
CALC_ALL_FLDS_RC(rc, FieldRc);
break;
}
}

/* If we have ts == NULL we (may) have tm also NULL, since we didn't really bind this column */
if (ts != NULL)
{
Expand Down Expand Up @@ -1881,10 +1949,10 @@ else if (_cur_row_rc != _accumulated_rc) _accumulated_rc= SQL_SUCCESS_WITH_INFO
/* {{{ MADB_StmtFetch */
SQLRETURN MADB_StmtFetch(MADB_Stmt *Stmt)
{
unsigned int row_num, j, rc;
SQLULEN Rows2Fetch= Stmt->Ard->Header.ArraySize, Processed, *ProcessedPtr= &Processed;
MYSQL_ROW_OFFSET SaveCursor= NULL;
SQLRETURN Result= SQL_SUCCESS, RowResult;
unsigned int row_num, j, rc;
SQLULEN Rows2Fetch= Stmt->Ard->Header.ArraySize, Processed, *ProcessedPtr= &Processed;
MYSQL_ROW_OFFSET SaveCursor= NULL;
SQLRETURN Result= SQL_SUCCESS, RowResult;

MADB_CLEAR_ERROR(&Stmt->Error);

Expand Down Expand Up @@ -2528,19 +2596,40 @@ SQLRETURN MADB_StmtGetData(SQLHSTMT StatementHandle,
{
MYSQL_TIME tm;

Bind.buffer_length= sizeof(MYSQL_TIME);
Bind.buffer= (void *)&tm;
/* c/c is too smart to convert hours to days and days to hours, we don't need that */
if ((OdbcType == SQL_C_TIME || OdbcType == SQL_C_TYPE_TIME)
&& (IrdRec->ConciseType == SQL_TIME || IrdRec->ConciseType == SQL_TYPE_TIME))
if (IrdRec->ConciseType == SQL_CHAR || IrdRec->ConciseType == SQL_VARCHAR)
{
Bind.buffer_type= MYSQL_TYPE_TIME;
char *ClientValue= NULL;
BOOL isTime;

if (!(ClientValue = (char *)MADB_CALLOC(Stmt->stmt->fields[Offset].max_length + 1)))
{
return MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0);
}
Bind.buffer= ClientValue;
Bind.buffer_type= MYSQL_TYPE_STRING;
Bind.buffer_length= Stmt->stmt->fields[Offset].max_length + 1;
mysql_stmt_fetch_column(Stmt->stmt, &Bind, Offset, 0);
RETURN_ERROR_OR_CONTINUE(MADB_Str2Ts(ClientValue, Bind.length_value, &tm, FALSE, &Stmt->Error, &isTime));
}
else
{
Bind.buffer_type= MYSQL_TYPE_TIMESTAMP;


Bind.buffer_length= sizeof(MYSQL_TIME);
Bind.buffer= (void *)&tm;
/* c/c is too smart to convert hours to days and days to hours, we don't need that */
if ((OdbcType == SQL_C_TIME || OdbcType == SQL_C_TYPE_TIME)
&& (IrdRec->ConciseType == SQL_TIME || IrdRec->ConciseType == SQL_TYPE_TIME))
{
Bind.buffer_type= MYSQL_TYPE_TIME;
}
else
{
Bind.buffer_type= MYSQL_TYPE_TIMESTAMP;

}
mysql_stmt_fetch_column(Stmt->stmt, &Bind, Offset, 0);
}
mysql_stmt_fetch_column(Stmt->stmt, &Bind, Offset, 0);
RETURN_ERROR_OR_CONTINUE(MADB_CopyMadbTimestamp(Stmt, &tm, TargetValuePtr, StrLen_or_IndPtr, StrLen_or_IndPtr, OdbcType, IrdRec->ConciseType));
break;
}
Expand All @@ -2550,12 +2639,29 @@ SQLRETURN MADB_StmtGetData(SQLHSTMT StatementHandle,
MYSQL_TIME tm;
SQL_INTERVAL_STRUCT *ts= (SQL_INTERVAL_STRUCT *)TargetValuePtr;

Bind.buffer_length= sizeof(MYSQL_TIME);
Bind.buffer= (void *)&tm;
/* c/c is too smart to convert hours to days and days to hours, we don't need that */
Bind.buffer_type= Field && Field->type == MYSQL_TYPE_TIME ? MYSQL_TYPE_TIME : MYSQL_TYPE_TIMESTAMP;
if (IrdRec->ConciseType == SQL_CHAR || IrdRec->ConciseType == SQL_VARCHAR)
{
char *ClientValue= NULL;
BOOL isTime;

mysql_stmt_fetch_column(Stmt->stmt, &Bind, Offset, 0);
if (!(ClientValue = (char *)MADB_CALLOC(Stmt->stmt->fields[Offset].max_length + 1)))
{
return MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0);
}
Bind.buffer= ClientValue;
Bind.buffer_type= MYSQL_TYPE_STRING;
Bind.buffer_length= Stmt->stmt->fields[Offset].max_length + 1;
mysql_stmt_fetch_column(Stmt->stmt, &Bind, Offset, 0);
RETURN_ERROR_OR_CONTINUE(MADB_Str2Ts(ClientValue, Bind.length_value, &tm, TRUE, &Stmt->Error, &isTime));
}
else
{
Bind.buffer_length= sizeof(MYSQL_TIME);
Bind.buffer= (void *)&tm;
/* c/c is too smart to convert hours to days and days to hours, we don't need that */
Bind.buffer_type= Field && Field->type == MYSQL_TYPE_TIME ? MYSQL_TYPE_TIME : MYSQL_TYPE_TIMESTAMP;
mysql_stmt_fetch_column(Stmt->stmt, &Bind, Offset, 0);
}

if (tm.hour > 99999)
{
Expand Down
46 changes: 21 additions & 25 deletions ma_typeconv.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
#include <ma_odbc.h>

/* Borrowed from C/C and adapted */
SQLRETURN MADB_Str2Ts(const char *Str, size_t Length, SQL_TIMESTAMP_STRUCT *Ts, BOOL Interval, MADB_Error *Error, int *isTime)
SQLRETURN MADB_Str2Ts(const char *Str, size_t Length, MYSQL_TIME *Tm, BOOL Interval, MADB_Error *Error, BOOL *isTime)
{
char *Start= MADB_ALLOC(Length + 1), *Frac, *End= Start + Length;
my_bool isDate= 0;
Expand All @@ -32,15 +32,15 @@ SQLRETURN MADB_Str2Ts(const char *Str, size_t Length, SQL_TIMESTAMP_STRUCT *Ts,
return MADB_SetError(Error, MADB_ERR_HY001, NULL, 0);
}

memset(Ts, 0, sizeof(SQL_TIMESTAMP_STRUCT));
memset(Tm, 0, sizeof(MYSQL_TIME));
memcpy(Start, Str, Length);
Start[Length]= '\0';

while (Length && isspace(*Start)) Start++, Length--;

if (Length == 0)
{
return MADB_SetError(Error, MADB_ERR_22008, NULL, 0);
return SQL_SUCCESS;//MADB_SetError(Error, MADB_ERR_22008, NULL, 0);
}

/* Determine time type:
Expand All @@ -50,7 +50,7 @@ SQLRETURN MADB_Str2Ts(const char *Str, size_t Length, SQL_TIMESTAMP_STRUCT *Ts,
*/
if (strchr(Start, '-'))
{
if (sscanf(Start, "%hd-%hu-%hu", &Ts->year, &Ts->month, &Ts->day) < 3)
if (sscanf(Start, "%d-%u-%u", &Tm->year, &Tm->month, &Tm->day) < 3)
{
return MADB_SetError(Error, MADB_ERR_22008, NULL, 0);
}
Expand All @@ -74,22 +74,22 @@ SQLRETURN MADB_Str2Ts(const char *Str, size_t Length, SQL_TIMESTAMP_STRUCT *Ts,
{
size_t FracMulIdx= End - (Frac + 1) - 1/*to get index array index */;
/* ODBC - nano-seconds */
if (sscanf(Start, "%hd:%hu:%hu.%9u", &Ts->hour, &Ts->minute,
&Ts->second, &Ts->fraction) < 4)
if (sscanf(Start, "%d:%u:%u.%6u", &Tm->hour, &Tm->minute,
&Tm->second, &Tm->second_part) < 4)
{
return MADB_SetError(Error, MADB_ERR_22008, NULL, 0);
}
/* 9 digits up to nano-seconds, and -1 since comparing with arr idx */
if (FracMulIdx < 9 - 1)
if (FracMulIdx < 6 - 1)
{
static unsigned long Mul[]= {100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10 };
Ts->fraction*= Mul[FracMulIdx];
static unsigned long Mul[]= {100000, 10000, 1000, 100, 10};
Tm->second_part*= Mul[FracMulIdx];
}
}
else
{
if (sscanf(Start, "%hd:%hu:%hu", &Ts->hour, &Ts->minute,
&Ts->second) < 3)
if (sscanf(Start, "%d:%u:%u", &Tm->hour, &Tm->minute,
&Tm->second) < 3)
{
return MADB_SetError(Error, MADB_ERR_22008, NULL, 0);
}
Expand All @@ -100,15 +100,15 @@ SQLRETURN MADB_Str2Ts(const char *Str, size_t Length, SQL_TIMESTAMP_STRUCT *Ts,
{
if (isDate)
{
if (Ts->year > 0)
if (Tm->year > 0)
{
if (Ts->year < 69)
if (Tm->year < 70)
{
Ts->year+= 2000;
Tm->year+= 2000;
}
else if (Ts->year < 100)
else if (Tm->year < 100)
{
Ts->year+= 1900;
Tm->year+= 1900;
}
}
}
Expand Down Expand Up @@ -380,11 +380,13 @@ SQLRETURN MADB_Char2Sql(MADB_Stmt *Stmt, MADB_DescRecord *CRec, void* DataPtr, S
break;
case SQL_DATETIME:
{
MYSQL_TIME Tm;
SQL_TIMESTAMP_STRUCT Ts;
int isTime;
BOOL isTime;

/* Enforcing constraints on date/time values */
RETURN_ERROR_OR_CONTINUE(MADB_Str2Ts(DataPtr, Length, &Ts, FALSE, &Stmt->Error, &isTime));
RETURN_ERROR_OR_CONTINUE(MADB_Str2Ts(DataPtr, Length, &Tm, FALSE, &Stmt->Error, &isTime));
MADB_CopyMadbTimeToOdbcTs(&Tm, &Ts);
RETURN_ERROR_OR_CONTINUE(MADB_TsConversionIsPossible(&Ts, SqlRec->ConciseType, &Stmt->Error, MADB_ERR_22018, isTime));
/* To stay on the safe side - still sending as string in the default branch */
}
Expand Down Expand Up @@ -522,13 +524,7 @@ SQLRETURN MADB_Timestamp2Sql(MADB_Stmt *Stmt, MADB_DescRecord *CRec, void* DataP
tm->second= ts->second;
break;
default:
tm->year= ts->year;
tm->month= ts->month;
tm->day= ts->day;
tm->hour= ts->hour;
tm->minute= ts->minute;
tm->second= ts->second;
tm->second_part= ts->fraction / 1000;
MADB_CopyOdbcTsToMadbTime(ts, tm);
}

*LengthPtr= sizeof(MYSQL_TIME);
Expand Down
2 changes: 2 additions & 0 deletions ma_typeconv.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,6 @@ SQLRETURN MADB_ConvertC2Sql(MADB_Stmt *Stmt, MADB_DescRecord *CRec, void* DataPt
MADB_DescRecord *SqlRec, MYSQL_BIND *MaBind, void **Buffer, unsigned long *LengthPtr);

SQLRETURN MADB_TsConversionIsPossible(SQL_TIMESTAMP_STRUCT *ts, SQLSMALLINT SqlType, MADB_Error *Error, enum enum_madb_error SqlState, int isTime);
SQLRETURN MADB_Str2Ts(const char *Str, size_t Length, MYSQL_TIME *Tm, BOOL Interval, MADB_Error *Error, BOOL *isTime);

#endif
Loading

0 comments on commit cb5b7ce

Please sign in to comment.