@@ -3118,24 +3118,26 @@ static int print_timestamp(TIMESTAMP_STRUCT *tss, BOOL iso8601,
3118
3118
tss -> hour , tss -> minute , tss -> second ,
3119
3119
/* fraction is always provided, but only printed if 'decdigits' */
3120
3120
decdigits , nsec );
3121
+ if (n <= 0 ) {
3122
+ return n ;
3123
+ }
3124
+
3121
3125
if ((int )lim < n ) {
3122
3126
n = (int )lim ;
3123
3127
}
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 ++ ;
3138
3136
}
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 );
3139
3141
3140
3142
return n ;
3141
3143
}
@@ -4252,10 +4254,80 @@ static SQLRETURN struct_to_iso8601_timestamp(esodbc_stmt_st *stmt,
4252
4254
return SQL_SUCCESS ;
4253
4255
}
4254
4256
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
+
4255
4327
SQLRETURN c2sql_date_time (esodbc_rec_st * arec , esodbc_rec_st * irec ,
4256
4328
SQLULEN pos , char * dest , size_t * len )
4257
4329
{
4258
- # define ZERO_TIME_Z "00:00:00Z"
4330
+ static const wstr_st time_0_z = WSTR_INIT ( "00:00:00Z" );
4259
4331
esodbc_stmt_st * stmt ;
4260
4332
void * data_ptr ;
4261
4333
SQLLEN * octet_len_ptr ;
@@ -4283,24 +4355,9 @@ SQLRETURN c2sql_date_time(esodbc_rec_st *arec, esodbc_rec_st *irec,
4283
4355
/* pointer to app's buffer */
4284
4356
data_ptr = deferred_address (SQL_DESC_DATA_PTR , pos , arec );
4285
4357
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 ;
4304
4361
}
4305
4362
4306
4363
/*INDENT-OFF*/
@@ -4315,7 +4372,7 @@ SQLRETURN c2sql_date_time(esodbc_rec_st *arec, esodbc_rec_st *irec,
4315
4372
}
4316
4373
/* disallow DATE <-> TIME conversions */
4317
4374
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 &&
4319
4376
irec -> es_type -> data_type == SQL_C_TYPE_DATE )) {
4320
4377
ERRH (stmt , "TIME-DATE conversions are not possible." );
4321
4378
RET_HDIAGS (stmt , SQL_STATE_22018 );
@@ -4351,27 +4408,27 @@ SQLRETURN c2sql_date_time(esodbc_rec_st *arec, esodbc_rec_st *irec,
4351
4408
* expense. */
4352
4409
/* Adapt the resulting ISO8601 value to the target data type */
4353
4410
switch (irec -> es_type -> data_type ) {
4354
- case SQL_C_TYPE_TIME :
4411
+ case SQL_TYPE_TIME :
4355
4412
/* shift value + \0 upwards over the DATE component */
4356
4413
/* Note: by the book, non-0 fractional seconds in timestamp should
4357
4414
* lead to 22008 a failure. However, ES/SQL's TIME supports
4358
4415
* fractions, so will just ignore this provision. */
4359
4416
cnt -= DATE_TEMPLATE_LEN + /*'T'*/ 1 ;
4360
4417
wmemmove (wbuff , wbuff + DATE_TEMPLATE_LEN + /*'T'*/ 1 , cnt + 1 );
4361
4418
break ;
4362
- case SQL_C_TYPE_DATE :
4419
+ case SQL_TYPE_DATE :
4363
4420
/* if origin is a timestamp (struct or string), the time part
4364
4421
* needs to be zeroed. */
4365
4422
if (ctype == SQL_C_TYPE_TIMESTAMP ||
4366
- format == SQL_C_TYPE_TIMESTAMP ) {
4423
+ format == SQL_TYPE_TIMESTAMP ) {
4367
4424
assert (ISO8601_TIMESTAMP_MIN_LEN <= cnt );
4368
4425
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 );
4370
4427
cnt = ISO8601_TIMESTAMP_MIN_LEN ;
4371
4428
}
4372
4429
break ;
4373
4430
default :
4374
- assert (irec -> es_type -> data_type == SQL_C_TYPE_TIMESTAMP );
4431
+ assert (irec -> es_type -> data_type == SQL_TYPE_TIMESTAMP );
4375
4432
}
4376
4433
DBGH (stmt , "converted value: [%zu] `" LWPDL "`." , cnt , cnt , wbuff );
4377
4434
@@ -4381,7 +4438,6 @@ SQLRETURN c2sql_date_time(esodbc_rec_st *arec, esodbc_rec_st *irec,
4381
4438
4382
4439
dest [(* len ) ++ ] = '"' ;
4383
4440
return SQL_SUCCESS ;
4384
- # undef ZERO_TIME_Z
4385
4441
}
4386
4442
4387
4443
/* parses an interval literal string from app's char/wchar_t buffer */
0 commit comments