36
36
#define CHK_RES (_hnd , _fmt , ...) \
37
37
JUMP_ON_CBOR_ERR(res, err, _hnd, _fmt, __VA_ARGS__)
38
38
39
+ /* fwd decl */
40
+ static SQLRETURN statement_params_len_cbor (esodbc_stmt_st * stmt ,
41
+ size_t * enc_len , size_t * conv_len );
42
+
39
43
static thread_local cstr_st tz_param ;
40
44
41
45
static BOOL print_tz_param (long tz_dst_offt )
@@ -677,6 +681,9 @@ static SQLRETURN attach_answer_cbor(esodbc_stmt_st *stmt)
677
681
"answer: `%s`." , cstr_hex_dump (& stmt -> rset .body ));
678
682
goto err ;
679
683
}
684
+ /* save the object, as it might be required by EsSQLRowCount() */
685
+ stmt -> rset .pack .cbor .rows_obj = rows_obj ;
686
+
680
687
/* ES uses indefinite-length arrays -- meh. */
681
688
res = cbor_container_is_empty (rows_obj , & empty );
682
689
CHK_RES (stmt , "failed to check if '" PACK_PARAM_ROWS "' array is empty" );
@@ -691,9 +698,6 @@ static SQLRETURN attach_answer_cbor(esodbc_stmt_st *stmt)
691
698
INFOH (stmt , "rows received in current (#%zu) result set: %zu." ,
692
699
stmt -> nset + 1 , nrows );
693
700
# endif /* NDEBUG */
694
- /* save the object, as it might be required by EsSQLRowCount() */
695
- stmt -> rset .pack .cbor .rows_obj = rows_obj ;
696
-
697
701
/* prepare iterator for EsSQLFetch(); recursing object and iterator
698
702
* can be the same, since there's no need to "leave" the container. */
699
703
res = cbor_value_enter_container (& rows_obj , & rows_obj );
@@ -2832,7 +2836,6 @@ static SQLRETURN convert_param_val(esodbc_rec_st *arec, esodbc_rec_st *irec,
2832
2836
ERRH (stmt , "conversion to SQL BINARY not implemented." );
2833
2837
RET_HDIAG (stmt , SQL_STATE_HYC00 , "conversion to SQL BINARY "
2834
2838
"not yet supported" , 0 );
2835
- break ;
2836
2839
2837
2840
default :
2838
2841
BUGH (arec -> desc -> hdr .stmt , "unexpected ES/SQL type %hd." ,
@@ -2842,6 +2845,7 @@ static SQLRETURN convert_param_val(esodbc_rec_st *arec, esodbc_rec_st *irec,
2842
2845
}
2843
2846
/*INDENT-ON*/
2844
2847
2848
+ assert (0 );
2845
2849
return SQL_SUCCESS ;
2846
2850
}
2847
2851
@@ -2851,9 +2855,8 @@ static SQLRETURN serialize_params_json(esodbc_stmt_st *stmt, char *dest,
2851
2855
size_t * len )
2852
2856
{
2853
2857
/* JSON keys for building one parameter object */
2854
- # define JSON_KEY_TYPE "{\"type\": \""
2855
- # define JSON_KEY_VALUE "\", \"value\": "
2856
-
2858
+ const static cstr_st j_type = CSTR_INIT ("{\"" REQ_KEY_PARAM_TYPE "\": \"" );
2859
+ const static cstr_st j_val = CSTR_INIT ("\", \"" REQ_KEY_PARAM_VAL "\": " );
2857
2860
esodbc_rec_st * arec , * irec ;
2858
2861
SQLRETURN ret ;
2859
2862
SQLSMALLINT i ;
@@ -2875,10 +2878,9 @@ static SQLRETURN serialize_params_json(esodbc_stmt_st *stmt, char *dest,
2875
2878
memcpy (dest + pos , ", " , 2 );
2876
2879
}
2877
2880
/* copy 'type' JSON key name */
2878
- memcpy (dest + pos + 2 * !!i , JSON_KEY_TYPE ,
2879
- sizeof (JSON_KEY_TYPE ) - 1 );
2881
+ memcpy (dest + pos + 2 * !!i , j_type .str , j_type .cnt );
2880
2882
}
2881
- pos += 2 * !!i + sizeof ( JSON_KEY_TYPE ) - 1 ;
2883
+ pos += 2 * !!i + j_type . cnt ;
2882
2884
2883
2885
/* copy/eval ES/SQL type name */
2884
2886
pos += json_escape (irec -> es_type -> type_name_c .str ,
@@ -2887,9 +2889,9 @@ static SQLRETURN serialize_params_json(esodbc_stmt_st *stmt, char *dest,
2887
2889
2888
2890
if (dest ) {
2889
2891
/* copy 'value' JSON key name */
2890
- memcpy (dest + pos , JSON_KEY_VALUE , sizeof ( JSON_KEY_VALUE ) - 1 );
2892
+ memcpy (dest + pos , j_val . str , j_val . cnt );
2891
2893
}
2892
- pos += sizeof ( JSON_KEY_VALUE ) - 1 ;
2894
+ pos += j_val . cnt ;
2893
2895
2894
2896
/* copy converted parameter value */
2895
2897
ret = convert_param_val (arec , irec , /*params array pos*/ 0LLU ,
@@ -2914,14 +2916,10 @@ static SQLRETURN serialize_params_json(esodbc_stmt_st *stmt, char *dest,
2914
2916
2915
2917
* len = pos ;
2916
2918
return SQL_SUCCESS ;
2917
-
2918
- # undef JSON_KEY_TYPE
2919
- # undef JSON_KEY_VALUE
2920
2919
}
2921
2920
2922
-
2923
- static SQLRETURN statement_len_cbor (esodbc_stmt_st * stmt , size_t * outlen ,
2924
- size_t * keys )
2921
+ static SQLRETURN statement_len_cbor (esodbc_stmt_st * stmt , size_t * enc_len ,
2922
+ size_t * conv_len , size_t * keys )
2925
2923
{
2926
2924
SQLRETURN ret ;
2927
2925
size_t bodylen , len , curslen ;
@@ -2951,12 +2949,8 @@ static SQLRETURN statement_len_cbor(esodbc_stmt_st *stmt, size_t *outlen,
2951
2949
/* does the statement have any parameters? */
2952
2950
if (stmt -> apd -> count ) {
2953
2951
bodylen += cbor_str_obj_len (sizeof (REQ_KEY_PARAMS ) - 1 );
2954
- // TODO:
2955
- // ret = serialize_params_cbor(stmt, /* no copy, just eval */NULL,
2956
- // &len);
2957
- ret = SQL_ERROR ;
2958
- len = 0 ;
2959
- FIXME ; // TODO
2952
+
2953
+ ret = statement_params_len_cbor (stmt , & len , conv_len );
2960
2954
if (! SQL_SUCCEEDED (ret )) {
2961
2955
ERRH (stmt , "failed to eval parameters length" );
2962
2956
return ret ;
@@ -2989,7 +2983,7 @@ static SQLRETURN statement_len_cbor(esodbc_stmt_st *stmt, size_t *outlen,
2989
2983
* keys += 2 ; /* mode, client_id */
2990
2984
/* TODO: request_/page_timeout */
2991
2985
2992
- * outlen = bodylen ;
2986
+ * enc_len = bodylen ;
2993
2987
return SQL_SUCCESS ;
2994
2988
}
2995
2989
@@ -3052,23 +3046,199 @@ static SQLRETURN statement_len_json(esodbc_stmt_st *stmt, size_t *outlen)
3052
3046
3053
3047
#define FAIL_ON_CBOR_ERR (_hnd , _cbor_err ) \
3054
3048
do { \
3055
- if (err != CborNoError) { \
3049
+ if (_cbor_err != CborNoError) { \
3056
3050
ERRH(_hnd, "CBOR: %s.", cbor_error_string(_cbor_err)); \
3057
3051
RET_HDIAG(_hnd, SQL_STATE_HY000, "CBOR serialization error", \
3058
3052
_cbor_err); \
3059
3053
} \
3060
3054
} while (0)
3061
3055
3056
+ static SQLRETURN statement_params_len_cbor (esodbc_stmt_st * stmt ,
3057
+ size_t * enc_len , size_t * conv_len )
3058
+ {
3059
+ SQLSMALLINT i ;
3060
+ size_t len , l , max ;
3061
+ esodbc_rec_st * arec , * irec ;
3062
+ SQLRETURN ret ;
3063
+
3064
+ /* Initial all-encompassing array preamble. */
3065
+ /* ~ [] */
3066
+ len = cbor_nn_hdr_len (stmt -> apd -> count );
3067
+
3068
+ max = 0 ;
3069
+ for (i = 0 ; i < stmt -> apd -> count ; i ++ ) {
3070
+ assert (stmt -> ipd -> count == stmt -> apd -> count );
3071
+ arec = & stmt -> apd -> recs [i ];
3072
+ irec = & stmt -> ipd -> recs [i ];
3073
+
3074
+ /* ~ {} */
3075
+ len = cbor_nn_hdr_len (stmt -> apd -> count );
3076
+
3077
+ /* ~ "type": "..." */
3078
+ len += cbor_str_obj_len (sizeof (REQ_KEY_PARAM_TYPE ) - 1 );
3079
+ len += cbor_str_obj_len (irec -> es_type -> type_name .cnt );
3080
+
3081
+ /* ~ "value": "..." */
3082
+ len += cbor_str_obj_len (sizeof (REQ_KEY_PARAM_VAL ) - 1 );
3083
+ assert (irec -> es_type );
3084
+
3085
+ /* assume quick maxes */
3086
+ ret = convert_param_val (arec , irec , /*params array pos*/ 0 ,
3087
+ /*dest: calc length*/ NULL , & l );
3088
+ if (! SQL_SUCCEEDED (ret )) {
3089
+ return ret ;
3090
+ }
3091
+ /* keep maximum space required for storing the converted obj */
3092
+ if (max < l ) {
3093
+ max = l ;
3094
+ }
3095
+ /* the values are going to be sent as strings
3096
+ * (see serialize_param_cbor() note) */
3097
+ len += cbor_str_obj_len (l );
3098
+ }
3099
+
3100
+ * conv_len = max ;
3101
+ * enc_len = len ;
3102
+ return SQL_SUCCESS ;
3103
+ }
3104
+
3105
+ /* Note: this implementation will encode numeric SQL types as strings (as it's
3106
+ * reusing the JSON converters). This is somewhat negating CBOR's intentions,
3107
+ * but: (1) it's a simplified and tested implementation; (2) the overall
3108
+ * performance impact is negligible with this driver's currently intended
3109
+ * usage pattern (SELECTs only, fetching data volume far outweighing that of
3110
+ * the queries); (3) the server will convert the received value according to
3111
+ * the correctly indicated type. XXX */
3112
+ static SQLRETURN serialize_param_cbor (esodbc_rec_st * arec ,
3113
+ esodbc_rec_st * irec , CborEncoder * pmap , size_t conv_len )
3114
+ {
3115
+ SQLRETURN ret ;
3116
+ CborError res ;
3117
+ size_t len ;
3118
+ SQLLEN * ind_ptr ;
3119
+ size_t skip_quote ;
3120
+ static SQLULEN param_array_pos = 0 ; /* parames array not yet supported */
3121
+ esodbc_stmt_st * stmt = HDRH (arec -> desc )-> stmt ;
3122
+
3123
+ ind_ptr = deferred_address (SQL_DESC_INDICATOR_PTR , param_array_pos , arec );
3124
+ if (ind_ptr && * ind_ptr == SQL_NULL_DATA ) {
3125
+ res = cbor_encode_null (pmap );
3126
+ FAIL_ON_CBOR_ERR (stmt , res );
3127
+ return SQL_SUCCESS ;
3128
+ }
3129
+ /* from here on, "input parameter value[ is] non-NULL" */
3130
+ assert (deferred_address (SQL_DESC_DATA_PTR , param_array_pos , arec ));
3131
+
3132
+ /* the pmap->end is the start of the conversion buffer */
3133
+ ret = convert_param_val (arec , irec , param_array_pos , (char * )pmap -> end ,
3134
+ & len );
3135
+ if (! SQL_SUCCEEDED (ret )) {
3136
+ return ret ;
3137
+ }
3138
+ assert (len <= conv_len );
3139
+
3140
+ /* need to skip the leading and trailing `"`? */
3141
+ switch (irec -> es_type -> meta_type ) {
3142
+ case METATYPE_EXACT_NUMERIC :
3143
+ case METATYPE_FLOAT_NUMERIC :
3144
+ case METATYPE_BIT :
3145
+ case METATYPE_BIN :
3146
+ skip_quote = 0 ;
3147
+ break ;
3148
+
3149
+ case METATYPE_STRING :
3150
+ case METATYPE_DATE_TIME :
3151
+ case METATYPE_INTERVAL_WSEC :
3152
+ case METATYPE_INTERVAL_WOSEC :
3153
+ case METATYPE_UID :
3154
+ skip_quote = 1 ;
3155
+ break ;
3156
+
3157
+ case METATYPE_MAX : /*DEFAULT, NULL*/
3158
+ case METATYPE_UNKNOWN :
3159
+ default :
3160
+ BUGH (stmt , "unexpected SQL meta %d / type %d." ,
3161
+ irec -> es_type -> meta_type , irec -> es_type -> data_type );
3162
+ RET_HDIAGS (stmt , SQL_STATE_HY000 );
3163
+ }
3164
+ res = cbor_encode_text_string (pmap , pmap -> end + skip_quote ,
3165
+ len - 2 * skip_quote );
3166
+ FAIL_ON_CBOR_ERR (stmt , res );
3167
+
3168
+ return SQL_SUCCESS ;
3169
+ }
3170
+
3171
+ static SQLRETURN serialize_params_cbor (esodbc_stmt_st * stmt , CborEncoder * map ,
3172
+ size_t conv_len )
3173
+ {
3174
+ const static cstr_st p_type = CSTR_INIT (REQ_KEY_PARAM_TYPE );
3175
+ const static cstr_st p_val = CSTR_INIT (REQ_KEY_PARAM_VAL );
3176
+ SQLSMALLINT i ;
3177
+ CborError res ;
3178
+ SQLRETURN ret ;
3179
+ CborEncoder array ; /* array for all params */
3180
+ CborEncoder pmap ; /* map for one param */
3181
+ esodbc_rec_st * arec , * irec ;
3182
+
3183
+ /* ~ [ */
3184
+ res = cbor_encoder_create_array (map , & array , stmt -> apd -> count );
3185
+ FAIL_ON_CBOR_ERR (stmt , res );
3186
+
3187
+ for (i = 0 ; i < stmt -> apd -> count ; i ++ ) {
3188
+ assert (stmt -> ipd -> count == stmt -> apd -> count );
3189
+ arec = & stmt -> apd -> recs [i ];
3190
+ irec = & stmt -> ipd -> recs [i ];
3191
+
3192
+ /* ~ { */
3193
+ res = cbor_encoder_create_map (& array , & pmap , /* type + value = */ 2 );
3194
+ FAIL_ON_CBOR_ERR (stmt , res );
3195
+
3196
+ /*
3197
+ * ~ "type": "..."
3198
+ */
3199
+ res = cbor_encode_text_string (& pmap , p_type .str , p_type .cnt );
3200
+ FAIL_ON_CBOR_ERR (stmt , res );
3201
+
3202
+ assert (irec -> es_type );
3203
+ res = cbor_encode_text_string (& pmap , irec -> es_type -> type_name_c .str ,
3204
+ irec -> es_type -> type_name_c .cnt );
3205
+ FAIL_ON_CBOR_ERR (stmt , res );
3206
+
3207
+ /*
3208
+ * ~ "value": "..."
3209
+ */
3210
+ res = cbor_encode_text_string (& pmap , p_val .str , p_val .cnt );
3211
+ FAIL_ON_CBOR_ERR (stmt , res );
3212
+
3213
+ ret = serialize_param_cbor (arec , irec , & pmap , conv_len );
3214
+ if (! SQL_SUCCEEDED (ret )) {
3215
+ ERRH (stmt , "converting parameter #%hd failed." , i + 1 );
3216
+ return ret ;
3217
+ }
3218
+
3219
+ /* ~ } */
3220
+ res = cbor_encoder_close_container (& array , & pmap );
3221
+ FAIL_ON_CBOR_ERR (stmt , res );
3222
+ }
3223
+
3224
+ /* ~ ] */
3225
+ res = cbor_encoder_close_container (map , & array );
3226
+ FAIL_ON_CBOR_ERR (stmt , res );
3227
+
3228
+ return SQL_SUCCESS ;
3229
+ }
3230
+
3062
3231
static SQLRETURN serialize_to_cbor (esodbc_stmt_st * stmt , cstr_st * dest ,
3063
- size_t keys )
3232
+ size_t conv_len , size_t keys )
3064
3233
{
3065
3234
CborEncoder encoder , map ;
3066
3235
CborError err ;
3067
3236
cstr_st tz , curs ;
3068
3237
esodbc_dbc_st * dbc = HDRH (stmt )-> dbc ;
3069
3238
size_t dest_cnt ;
3070
3239
3071
- cbor_encoder_init (& encoder , dest -> str , dest -> cnt , /*flags*/ 0 );
3240
+ assert (conv_len < dest -> cnt );
3241
+ cbor_encoder_init (& encoder , dest -> str , dest -> cnt - conv_len , /*flags*/ 0 );
3072
3242
err = cbor_encoder_create_map (& encoder , & map , keys );
3073
3243
FAIL_ON_CBOR_ERR (stmt , err );
3074
3244
@@ -3101,8 +3271,11 @@ static SQLRETURN serialize_to_cbor(esodbc_stmt_st *stmt, cstr_st *dest,
3101
3271
3102
3272
/* does the statement have any parameters? */
3103
3273
if (stmt -> apd -> count ) {
3104
- FIXME ; // TODO
3105
- return SQL_ERROR ;
3274
+ err = cbor_encode_text_string (& map , REQ_KEY_PARAMS ,
3275
+ sizeof (REQ_KEY_PARAMS ) - 1 );
3276
+ FAIL_ON_CBOR_ERR (stmt , err );
3277
+ err = serialize_params_cbor (stmt , & map , conv_len );
3278
+ FAIL_ON_CBOR_ERR (stmt , err );
3106
3279
}
3107
3280
/* does the statement have any fetch_size? */
3108
3281
if (dbc -> fetch .slen ) {
@@ -3157,7 +3330,7 @@ static SQLRETURN serialize_to_cbor(esodbc_stmt_st *stmt, cstr_st *dest,
3157
3330
dest_cnt = cbor_encoder_get_buffer_size (& encoder , dest -> str );
3158
3331
assert (dest_cnt <= dest -> cnt ); /* tinycbor should check this, but still */
3159
3332
dest -> cnt = dest_cnt ;
3160
- DBGH (stmt , "request serialized to CBOR: [%zd] `0x %s`." , dest -> cnt ,
3333
+ DBGH (stmt , "request serialized to CBOR: [%zd] `%s`." , dest -> cnt ,
3161
3334
cstr_hex_dump (dest ));
3162
3335
3163
3336
return SQL_SUCCESS ;
@@ -3278,7 +3451,7 @@ static SQLRETURN serialize_to_json(esodbc_stmt_st *stmt, cstr_st *dest)
3278
3451
SQLRETURN TEST_API serialize_statement (esodbc_stmt_st * stmt , cstr_st * dest )
3279
3452
{
3280
3453
SQLRETURN ret ;
3281
- size_t len , keys ;
3454
+ size_t enc_len , conv_len , alloc_len , keys ;
3282
3455
esodbc_dbc_st * dbc = HDRH (stmt )-> dbc ;
3283
3456
3284
3457
/* enforced in EsSQLSetDescFieldW(SQL_DESC_ARRAY_SIZE) */
@@ -3289,27 +3462,30 @@ SQLRETURN TEST_API serialize_statement(esodbc_stmt_st *stmt, cstr_st *dest)
3289
3462
"Failed to update the timezone parameter" , 0 );
3290
3463
}
3291
3464
3292
- ret = dbc -> pack_json ? statement_len_json (stmt , & len ) :
3293
- statement_len_cbor (stmt , & len , & keys );
3465
+ conv_len = 0 ;
3466
+ ret = dbc -> pack_json ? statement_len_json (stmt , & enc_len ) :
3467
+ statement_len_cbor (stmt , & enc_len , & conv_len , & keys );
3294
3468
if (! SQL_SUCCEEDED (ret )) {
3295
3469
return ret ;
3470
+ } else {
3471
+ alloc_len = enc_len + conv_len ;
3296
3472
}
3297
3473
3298
3474
/* allocate memory for the stringified statement, if needed */
3299
- if (dest -> cnt < len ) {
3300
- INFOH (dbc , "local buffer too small (%zd ), need %zdB ; will alloc." ,
3301
- dest -> cnt , len );
3475
+ if (dest -> cnt < alloc_len ) {
3476
+ INFOH (dbc , "local buffer too small (%zu ), need %zuB ; will alloc." ,
3477
+ dest -> cnt , alloc_len );
3302
3478
DBGH (dbc , "local buffer too small, SQL: `" LCPDL "`." ,
3303
3479
LCSTR (& stmt -> u8sql ));
3304
- if (! (dest -> str = malloc (len ))) {
3305
- ERRNH (stmt , "failed to alloc %zdB." , len );
3480
+ if (! (dest -> str = malloc (alloc_len ))) {
3481
+ ERRNH (stmt , "failed to alloc %zdB." , alloc_len );
3306
3482
RET_HDIAGS (stmt , SQL_STATE_HY001 );
3307
3483
}
3308
- dest -> cnt = len ;
3484
+ dest -> cnt = alloc_len ;
3309
3485
}
3310
3486
3311
3487
return dbc -> pack_json ? serialize_to_json (stmt , dest ) :
3312
- serialize_to_cbor (stmt , dest , keys );
3488
+ serialize_to_cbor (stmt , dest , conv_len , keys );
3313
3489
}
3314
3490
3315
3491
0 commit comments