@@ -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}
@@ -4254,10 +4256,80 @@ static SQLRETURN struct_to_iso8601_timestamp(esodbc_stmt_st *stmt,
42544256 return SQL_SUCCESS ;
42554257}
42564258
4259+ /* apply corrections depending on the (column) size and decimal digits
4260+ * values given at binding time: nullify or trim the resulted string:
4261+ * https://docs.microsoft.com/en-us/sql/odbc/reference/appendixes/column-size
4262+ * */
4263+ static SQLRETURN size_decdigits_for_iso8601 (esodbc_rec_st * irec ,
4264+ SQLULEN * _colsize , SQLSMALLINT * _decdigits )
4265+ {
4266+ SQLULEN colsize ;
4267+ SQLSMALLINT decdigits ;
4268+ esodbc_stmt_st * stmt = HDRH (irec -> desc )-> stmt ;
4269+
4270+ colsize = get_param_size (irec );
4271+ DBGH (stmt , "requested column size: %llu." , colsize );
4272+
4273+ decdigits = get_param_decdigits (irec );
4274+ DBGH (stmt , "requested decimal digits: %llu." , decdigits );
4275+ if (ESODBC_MAX_SEC_PRECISION < decdigits ) {
4276+ WARNH (stmt , "requested decimal digits adjusted from %hd to %d (max)." ,
4277+ decdigits , ESODBC_MAX_SEC_PRECISION );
4278+ decdigits = ESODBC_MAX_SEC_PRECISION ;
4279+ }
4280+
4281+ switch (irec -> es_type -> data_type ) {
4282+ case SQL_TYPE_TIME :
4283+ if (colsize ) {
4284+ if (colsize < TIME_TEMPLATE_LEN (0 ) ||
4285+ colsize == TIME_TEMPLATE_LEN (1 ) - 1 /* `:ss.`*/ ) {
4286+ ERRH (stmt , "invalid column size value: %llu; allowed: "
4287+ "8 or 9 + fractions count." , colsize );
4288+ RET_HDIAGS (stmt , SQL_STATE_HY104 );
4289+ }
4290+ colsize += DATE_TEMPLATE_LEN + /* ` `/`T` */ 1 ;
4291+ }
4292+ break ;
4293+ case SQL_TYPE_DATE :
4294+ /* if origin is a timestamp (struct or string), the time part
4295+ * needs to be zeroed. */
4296+ if (colsize ) {
4297+ if (colsize != DATE_TEMPLATE_LEN ) {
4298+ ERRH (stmt , "invalid column size value: %llu; allowed: "
4299+ "%zu." , colsize , DATE_TEMPLATE_LEN );
4300+ RET_HDIAGS (stmt , SQL_STATE_HY104 );
4301+ }
4302+ colsize += /* ` `/`T` */ 1 + TIME_TEMPLATE_LEN (0 );
4303+ }
4304+ if (decdigits ) {
4305+ ERRH (stmt , "invalid decimal digits %hd for TIME type." ,
4306+ decdigits );
4307+ RET_HDIAGS (stmt , SQL_STATE_HY104 );
4308+ }
4309+ break ;
4310+ case SQL_TYPE_TIMESTAMP :
4311+ if (colsize && (colsize < TIMESTAMP_NOSEC_TEMPLATE_LEN ||
4312+ colsize == 17 || colsize == 18 )) {
4313+ ERRH (stmt , "invalid column size value: %llu; allowed: "
4314+ "16, 19 or 20 + fractions count." , colsize );
4315+ RET_HDIAGS (stmt , SQL_STATE_HY104 );
4316+ }
4317+ break ;
4318+ default :
4319+ assert (0 );
4320+ }
4321+
4322+ DBGH (stmt , "applying: column size: %llu, decimal digits: %hd." ,
4323+ colsize , decdigits );
4324+ * _colsize = colsize ;
4325+ * _decdigits = decdigits ;
4326+ return SQL_SUCCESS ;
4327+ }
4328+
42574329SQLRETURN c2sql_date_time (esodbc_rec_st * arec , esodbc_rec_st * irec ,
42584330 SQLULEN pos , char * dest , size_t * len )
42594331{
4260- # define ZERO_TIME_Z "00:00:00Z"
4332+ static const wstr_st time_0_z = WSTR_INIT ( "00:00:00Z" );
42614333 esodbc_stmt_st * stmt ;
42624334 void * data_ptr ;
42634335 SQLLEN * octet_len_ptr ;
@@ -4285,24 +4357,9 @@ SQLRETURN c2sql_date_time(esodbc_rec_st *arec, esodbc_rec_st *irec,
42854357 /* pointer to app's buffer */
42864358 data_ptr = deferred_address (SQL_DESC_DATA_PTR , pos , arec );
42874359
4288- /* apply corrections depending on the (column) size and decimal digits
4289- * values given at binding time: nullify or trim the resulted string:
4290- * https://docs.microsoft.com/en-us/sql/odbc/reference/appendixes/column-size
4291- * */
4292- colsize = get_param_size (irec );
4293- DBGH (stmt , "requested column size: %llu." , colsize );
4294- if (colsize && (colsize < sizeof ("yyyy-mm-dd hh:mm" ) - 1 ||
4295- colsize == 17 || colsize == 18 )) {
4296- ERRH (stmt , "invalid column size value: %llu; allowed: 16, 19, 20+f." ,
4297- colsize );
4298- RET_HDIAGS (stmt , SQL_STATE_HY104 );
4299- }
4300- decdigits = get_param_decdigits (irec );
4301- DBGH (stmt , "requested decimal digits: %llu." , decdigits );
4302- if (ESODBC_MAX_SEC_PRECISION < decdigits ) {
4303- WARNH (stmt , "requested decimal digits adjusted from %hd to %d (max)." ,
4304- decdigits , ESODBC_MAX_SEC_PRECISION );
4305- decdigits = ESODBC_MAX_SEC_PRECISION ;
4360+ ret = size_decdigits_for_iso8601 (irec , & colsize , & decdigits );
4361+ if (! SQL_SUCCEEDED (ret )) {
4362+ return ret ;
43064363 }
43074364
43084365 /*INDENT-OFF*/
@@ -4317,7 +4374,7 @@ SQLRETURN c2sql_date_time(esodbc_rec_st *arec, esodbc_rec_st *irec,
43174374 }
43184375 /* disallow DATE <-> TIME conversions */
43194376 if ((irec -> es_type -> data_type == SQL_C_TYPE_TIME &&
4320- format == SQL_C_TYPE_DATE ) || (format == SQL_C_TYPE_TIME &&
4377+ format == SQL_TYPE_DATE ) || (format == SQL_TYPE_TIME &&
43214378 irec -> es_type -> data_type == SQL_C_TYPE_DATE )) {
43224379 ERRH (stmt , "TIME-DATE conversions are not possible." );
43234380 RET_HDIAGS (stmt , SQL_STATE_22018 );
@@ -4353,27 +4410,27 @@ SQLRETURN c2sql_date_time(esodbc_rec_st *arec, esodbc_rec_st *irec,
43534410 * expense. */
43544411 /* Adapt the resulting ISO8601 value to the target data type */
43554412 switch (irec -> es_type -> data_type ) {
4356- case SQL_C_TYPE_TIME :
4413+ case SQL_TYPE_TIME :
43574414 /* shift value + \0 upwards over the DATE component */
43584415 /* Note: by the book, non-0 fractional seconds in timestamp should
43594416 * lead to 22008 a failure. However, ES/SQL's TIME supports
43604417 * fractions, so will just ignore this provision. */
43614418 cnt -= DATE_TEMPLATE_LEN + /*'T'*/ 1 ;
43624419 wmemmove (wbuff , wbuff + DATE_TEMPLATE_LEN + /*'T'*/ 1 , cnt + 1 );
43634420 break ;
4364- case SQL_C_TYPE_DATE :
4421+ case SQL_TYPE_DATE :
43654422 /* if origin is a timestamp (struct or string), the time part
43664423 * needs to be zeroed. */
43674424 if (ctype == SQL_C_TYPE_TIMESTAMP ||
4368- format == SQL_C_TYPE_TIMESTAMP ) {
4425+ format == SQL_TYPE_TIMESTAMP ) {
43694426 assert (ISO8601_TIMESTAMP_MIN_LEN <= cnt );
43704427 wmemcpy (wbuff + DATE_TEMPLATE_LEN + /*'T'*/ 1 ,
4371- MK_WPTR ( ZERO_TIME_Z ), sizeof ( ZERO_TIME_Z ) /*+ \0*/ );
4428+ ( wchar_t * ) time_0_z . str , time_0_z . cnt + /* \0*/1 );
43724429 cnt = ISO8601_TIMESTAMP_MIN_LEN ;
43734430 }
43744431 break ;
43754432 default :
4376- assert (irec -> es_type -> data_type == SQL_C_TYPE_TIMESTAMP );
4433+ assert (irec -> es_type -> data_type == SQL_TYPE_TIMESTAMP );
43774434 }
43784435 DBGH (stmt , "converted value: [%zu] `" LWPDL "`." , cnt , cnt , wbuff );
43794436
@@ -4383,7 +4440,6 @@ SQLRETURN c2sql_date_time(esodbc_rec_st *arec, esodbc_rec_st *irec,
43834440
43844441 dest [(* len ) ++ ] = '"' ;
43854442 return SQL_SUCCESS ;
4386- # undef ZERO_TIME_Z
43874443}
43884444
43894445/* parses an interval literal string from app's char/wchar_t buffer */
0 commit comments