@@ -3118,24 +3118,26 @@ static int print_timestamp(TIMESTAMP_STRUCT *tss, BOOL iso8601,
31183118 tss -> hour , tss -> minute , tss -> second ,
31193119 /* fraction is always provided, but only printed if 'decdigits' */
31203120 decdigits , nsec );
3121+ if (n <= 0 ) {
3122+ return n ;
3123+ }
3124+
31213125 if ((int )lim < n ) {
31223126 n = (int )lim ;
31233127 }
3124- if (0 < n ) {
3125- if (iso8601 ) {
3126- dest [DATE_TEMPLATE_LEN ] = L'T' ;
3127- /* The SQL column sizes are considered for ISO format too, to
3128- * allow the case where the client app specifies a timestamp with
3129- * non-zero seconds, but wants to cut those away in the parameter.
3130- * The 'Z' would then be on top of the colsize. */
3131- dest [n ] = L'Z' ;
3132- n ++ ;
3133- dest [n ] = L'\0' ;
3134- }
3135- DBG ("printed UTC %s timestamp (colsz: %lu, decdig: %hd): "
3136- "[%d] `" LWPDL "`." , iso8601 ? "ISO8601" : "SQL" ,
3137- (SQLUINTEGER )colsize , decdigits , n , n , dest );
3128+ if (iso8601 ) {
3129+ dest [DATE_TEMPLATE_LEN ] = L'T' ;
3130+ /* The SQL column sizes are considered for ISO format too, to
3131+ * allow the case where the client app specifies a timestamp with
3132+ * non-zero seconds, but wants to cut those away in the parameter.
3133+ * The 'Z' would then be on top of the colsize. */
3134+ dest [n ] = L'Z' ;
3135+ n ++ ;
31383136 }
3137+ dest [n ] = L'\0' ;
3138+ DBG ("printed UTC %s timestamp (colsz: %lu, decdig: %hd): "
3139+ "[%d] `" LWPDL "`." , iso8601 ? "ISO8601" : "SQL" ,
3140+ (SQLUINTEGER )colsize , decdigits , n , n , dest );
31393141
31403142 return n ;
31413143}
@@ -4252,10 +4254,80 @@ static SQLRETURN struct_to_iso8601_timestamp(esodbc_stmt_st *stmt,
42524254 return SQL_SUCCESS ;
42534255}
42544256
4257+ /* apply corrections depending on the (column) size and decimal digits
4258+ * values given at binding time: nullify or trim the resulted string:
4259+ * https://docs.microsoft.com/en-us/sql/odbc/reference/appendixes/column-size
4260+ * */
4261+ static SQLRETURN size_decdigits_for_iso8601 (esodbc_rec_st * irec ,
4262+ SQLULEN * _colsize , SQLSMALLINT * _decdigits )
4263+ {
4264+ SQLULEN colsize ;
4265+ SQLSMALLINT decdigits ;
4266+ esodbc_stmt_st * stmt = HDRH (irec -> desc )-> stmt ;
4267+
4268+ colsize = get_param_size (irec );
4269+ DBGH (stmt , "requested column size: %llu." , colsize );
4270+
4271+ decdigits = get_param_decdigits (irec );
4272+ DBGH (stmt , "requested decimal digits: %llu." , decdigits );
4273+ if (ESODBC_MAX_SEC_PRECISION < decdigits ) {
4274+ WARNH (stmt , "requested decimal digits adjusted from %hd to %d (max)." ,
4275+ decdigits , ESODBC_MAX_SEC_PRECISION );
4276+ decdigits = ESODBC_MAX_SEC_PRECISION ;
4277+ }
4278+
4279+ switch (irec -> es_type -> data_type ) {
4280+ case SQL_TYPE_TIME :
4281+ if (colsize ) {
4282+ if (colsize < TIME_TEMPLATE_LEN (0 ) ||
4283+ colsize == TIME_TEMPLATE_LEN (1 ) - 1 /* `:ss.`*/ ) {
4284+ ERRH (stmt , "invalid column size value: %llu; allowed: "
4285+ "8 or 9 + fractions count." , colsize );
4286+ RET_HDIAGS (stmt , SQL_STATE_HY104 );
4287+ }
4288+ colsize += DATE_TEMPLATE_LEN + /* ` `/`T` */ 1 ;
4289+ }
4290+ break ;
4291+ case SQL_TYPE_DATE :
4292+ /* if origin is a timestamp (struct or string), the time part
4293+ * needs to be zeroed. */
4294+ if (colsize ) {
4295+ if (colsize != DATE_TEMPLATE_LEN ) {
4296+ ERRH (stmt , "invalid column size value: %llu; allowed: "
4297+ "%zu." , colsize , DATE_TEMPLATE_LEN );
4298+ RET_HDIAGS (stmt , SQL_STATE_HY104 );
4299+ }
4300+ colsize += /* ` `/`T` */ 1 + TIME_TEMPLATE_LEN (0 );
4301+ }
4302+ if (decdigits ) {
4303+ ERRH (stmt , "invalid decimal digits %hd for TIME type." ,
4304+ decdigits );
4305+ RET_HDIAGS (stmt , SQL_STATE_HY104 );
4306+ }
4307+ break ;
4308+ case SQL_TYPE_TIMESTAMP :
4309+ if (colsize && (colsize < TIMESTAMP_NOSEC_TEMPLATE_LEN ||
4310+ colsize == 17 || colsize == 18 )) {
4311+ ERRH (stmt , "invalid column size value: %llu; allowed: "
4312+ "16, 19 or 20 + fractions count." , colsize );
4313+ RET_HDIAGS (stmt , SQL_STATE_HY104 );
4314+ }
4315+ break ;
4316+ default :
4317+ assert (0 );
4318+ }
4319+
4320+ DBGH (stmt , "applying: column size: %llu, decimal digits: %hd." ,
4321+ colsize , decdigits );
4322+ * _colsize = colsize ;
4323+ * _decdigits = decdigits ;
4324+ return SQL_SUCCESS ;
4325+ }
4326+
42554327SQLRETURN c2sql_date_time (esodbc_rec_st * arec , esodbc_rec_st * irec ,
42564328 SQLULEN pos , char * dest , size_t * len )
42574329{
4258- # define ZERO_TIME_Z "00:00:00Z"
4330+ static const wstr_st time_0_z = WSTR_INIT ( "00:00:00Z" );
42594331 esodbc_stmt_st * stmt ;
42604332 void * data_ptr ;
42614333 SQLLEN * octet_len_ptr ;
@@ -4283,24 +4355,9 @@ SQLRETURN c2sql_date_time(esodbc_rec_st *arec, esodbc_rec_st *irec,
42834355 /* pointer to app's buffer */
42844356 data_ptr = deferred_address (SQL_DESC_DATA_PTR , pos , arec );
42854357
4286- /* apply corrections depending on the (column) size and decimal digits
4287- * values given at binding time: nullify or trim the resulted string:
4288- * https://docs.microsoft.com/en-us/sql/odbc/reference/appendixes/column-size
4289- * */
4290- colsize = get_param_size (irec );
4291- DBGH (stmt , "requested column size: %llu." , colsize );
4292- if (colsize && (colsize < sizeof ("yyyy-mm-dd hh:mm" ) - 1 ||
4293- colsize == 17 || colsize == 18 )) {
4294- ERRH (stmt , "invalid column size value: %llu; allowed: 16, 19, 20+f." ,
4295- colsize );
4296- RET_HDIAGS (stmt , SQL_STATE_HY104 );
4297- }
4298- decdigits = get_param_decdigits (irec );
4299- DBGH (stmt , "requested decimal digits: %llu." , decdigits );
4300- if (ESODBC_MAX_SEC_PRECISION < decdigits ) {
4301- WARNH (stmt , "requested decimal digits adjusted from %hd to %d (max)." ,
4302- decdigits , ESODBC_MAX_SEC_PRECISION );
4303- decdigits = ESODBC_MAX_SEC_PRECISION ;
4358+ ret = size_decdigits_for_iso8601 (irec , & colsize , & decdigits );
4359+ if (! SQL_SUCCEEDED (ret )) {
4360+ return ret ;
43044361 }
43054362
43064363 /*INDENT-OFF*/
@@ -4315,7 +4372,7 @@ SQLRETURN c2sql_date_time(esodbc_rec_st *arec, esodbc_rec_st *irec,
43154372 }
43164373 /* disallow DATE <-> TIME conversions */
43174374 if ((irec -> es_type -> data_type == SQL_C_TYPE_TIME &&
4318- format == SQL_C_TYPE_DATE ) || (format == SQL_C_TYPE_TIME &&
4375+ format == SQL_TYPE_DATE ) || (format == SQL_TYPE_TIME &&
43194376 irec -> es_type -> data_type == SQL_C_TYPE_DATE )) {
43204377 ERRH (stmt , "TIME-DATE conversions are not possible." );
43214378 RET_HDIAGS (stmt , SQL_STATE_22018 );
@@ -4351,27 +4408,27 @@ SQLRETURN c2sql_date_time(esodbc_rec_st *arec, esodbc_rec_st *irec,
43514408 * expense. */
43524409 /* Adapt the resulting ISO8601 value to the target data type */
43534410 switch (irec -> es_type -> data_type ) {
4354- case SQL_C_TYPE_TIME :
4411+ case SQL_TYPE_TIME :
43554412 /* shift value + \0 upwards over the DATE component */
43564413 /* Note: by the book, non-0 fractional seconds in timestamp should
43574414 * lead to 22008 a failure. However, ES/SQL's TIME supports
43584415 * fractions, so will just ignore this provision. */
43594416 cnt -= DATE_TEMPLATE_LEN + /*'T'*/ 1 ;
43604417 wmemmove (wbuff , wbuff + DATE_TEMPLATE_LEN + /*'T'*/ 1 , cnt + 1 );
43614418 break ;
4362- case SQL_C_TYPE_DATE :
4419+ case SQL_TYPE_DATE :
43634420 /* if origin is a timestamp (struct or string), the time part
43644421 * needs to be zeroed. */
43654422 if (ctype == SQL_C_TYPE_TIMESTAMP ||
4366- format == SQL_C_TYPE_TIMESTAMP ) {
4423+ format == SQL_TYPE_TIMESTAMP ) {
43674424 assert (ISO8601_TIMESTAMP_MIN_LEN <= cnt );
43684425 wmemcpy (wbuff + DATE_TEMPLATE_LEN + /*'T'*/ 1 ,
4369- MK_WPTR ( ZERO_TIME_Z ), sizeof ( ZERO_TIME_Z ) /*+ \0*/ );
4426+ ( wchar_t * ) time_0_z . str , time_0_z . cnt + /* \0*/1 );
43704427 cnt = ISO8601_TIMESTAMP_MIN_LEN ;
43714428 }
43724429 break ;
43734430 default :
4374- assert (irec -> es_type -> data_type == SQL_C_TYPE_TIMESTAMP );
4431+ assert (irec -> es_type -> data_type == SQL_TYPE_TIMESTAMP );
43754432 }
43764433 DBGH (stmt , "converted value: [%zu] `" LWPDL "`." , cnt , cnt , wbuff );
43774434
@@ -4381,7 +4438,6 @@ SQLRETURN c2sql_date_time(esodbc_rec_st *arec, esodbc_rec_st *irec,
43814438
43824439 dest [(* len ) ++ ] = '"' ;
43834440 return SQL_SUCCESS ;
4384- # undef ZERO_TIME_Z
43854441}
43864442
43874443/* parses an interval literal string from app's char/wchar_t buffer */
0 commit comments