@@ -913,6 +913,241 @@ bool Protocol_text::store_field_metadata(const THD * thd,
913
913
}
914
914
915
915
916
+ /*
917
+ MARIADB_CLIENT_CACHE_METADATA support.
918
+
919
+ Bulk of the code below is dedicated to detecting whether column metadata has
920
+ changed after prepare, or between executions of a prepared statement.
921
+
922
+ For some prepared statements, metadata can't change without going through
923
+ Prepared_Statement::reprepare(), which makes detecting changes easy.
924
+
925
+ Others, "SELECT ?" & Co, are more fragile, and sensitive to input parameters,
926
+ or user variables. Detecting metadata change for this class of PS is harder,
927
+ we calculate signature (hash value), and check whether this changes between
928
+ executions. This is a more expensive method.
929
+ */
930
+
931
+
932
+ /* *
933
+ Detect whether column info can be changed without
934
+ PS repreparing.
935
+
936
+ Such colum info is called fragile. The opposite of
937
+ fragile is.
938
+
939
+
940
+ @param it - Item representing column info
941
+ @return true, if columninfo is "fragile", false if it is stable
942
+
943
+
944
+ @todo does not work due to MDEV-23913. Currently,
945
+ everything about prepared statements is fragile.
946
+ */
947
+
948
+ static bool is_fragile_columnifo (Item *it)
949
+ {
950
+ #define MDEV_23913_FIXED 0
951
+ #if MDEV_23913_FIXED
952
+ if (dynamic_cast <Item_param *>(it))
953
+ return true ;
954
+
955
+ if (dynamic_cast <Item_func_user_var *>(it))
956
+ return true ;
957
+
958
+ if (dynamic_cast <Item_sp_variable*>(it))
959
+ return true ;
960
+
961
+ /* Check arguments of functions.*/
962
+ auto item_args= dynamic_cast <Item_args *>(it);
963
+ if (!item_args)
964
+ return false ;
965
+ auto args= item_args->arguments ();
966
+ auto arg_count= item_args->argument_count ();
967
+ for (uint i= 0 ; i < arg_count; i++)
968
+ {
969
+ if (is_fragile_columnifo (args[i]))
970
+ return true ;
971
+ }
972
+ return false ;
973
+ #else /* MDEV-23913 fixed*/
974
+ return true ;
975
+ #endif
976
+ }
977
+
978
+
979
+ #define INVALID_METADATA_CHECKSUM 0
980
+
981
+
982
+ /* *
983
+ Calculate signature for column info sent to the client as CRC32 over data,
984
+ that goes into the column info packet.
985
+ We assume that if checksum does not change, then column info was not
986
+ modified.
987
+
988
+ @param thd THD
989
+ @param list column info
990
+
991
+ @return CRC32 of the metadata
992
+ */
993
+
994
+ static uint32 calc_metadata_hash (THD *thd, List<Item> *list)
995
+ {
996
+ List_iterator_fast<Item> it (*list);
997
+ Item *item;
998
+ uint32 crc32_c= 0 ;
999
+ while ((item= it++))
1000
+ {
1001
+ Send_field field (thd, item);
1002
+ auto field_type= item->type_handler ()->field_type ();
1003
+ auto charset= item->charset_for_protocol ();
1004
+ /*
1005
+ The data below should contain everything that influences
1006
+ content of the column info packet.
1007
+ */
1008
+ LEX_CSTRING data[]=
1009
+ {
1010
+ field.table_name ,
1011
+ field.org_table_name ,
1012
+ field.col_name ,
1013
+ field.org_col_name ,
1014
+ field.db_name ,
1015
+ field.attr (MARIADB_FIELD_ATTR_DATA_TYPE_NAME),
1016
+ field.attr (MARIADB_FIELD_ATTR_FORMAT_NAME),
1017
+ {(const char *) &field.length , sizeof (field.length )},
1018
+ {(const char *) &field.flags , sizeof (field.flags )},
1019
+ {(const char *) &field.decimals , sizeof (field.decimals )},
1020
+ {(const char *) &charset, sizeof (charset)},
1021
+ {(const char *) &field_type, sizeof (field_type)},
1022
+ };
1023
+ for (const auto &chunk : data)
1024
+ crc32_c= my_crc32c (crc32_c, chunk.str , chunk.length );
1025
+ }
1026
+
1027
+ if (crc32_c == INVALID_METADATA_CHECKSUM)
1028
+ return 1 ;
1029
+ return crc32_c;
1030
+ }
1031
+
1032
+
1033
+
1034
+ /* *
1035
+ Check if metadata columns have changed since last call to this
1036
+ function.
1037
+
1038
+ @param send_column_info_state saved state, changed if the function
1039
+ return true.
1040
+ @param thd THD
1041
+ @param list columninfo Items
1042
+ @return true,if metadata columns have changed since last call,
1043
+ false otherwise
1044
+ */
1045
+
1046
+ static bool metadata_columns_changed (send_column_info_state &state, THD *thd,
1047
+ List<Item> &list)
1048
+ {
1049
+ if (!state.initialized )
1050
+ {
1051
+ state.initialized = true ;
1052
+ state.immutable = true ;
1053
+ Item *item;
1054
+ List_iterator_fast<Item> it (list);
1055
+ while ((item= it++))
1056
+ {
1057
+ if (is_fragile_columnifo (item))
1058
+ {
1059
+ state.immutable = false ;
1060
+ state.checksum = calc_metadata_hash (thd, &list);
1061
+ break ;
1062
+ }
1063
+ }
1064
+ state.last_charset = thd->variables .character_set_client ;
1065
+ return true ;
1066
+ }
1067
+
1068
+ /*
1069
+ Since column info can change under our feet, we use more expensive
1070
+ checksumming to check if column metadata has not changed since last time.
1071
+ */
1072
+ if (!state.immutable )
1073
+ {
1074
+ uint32 checksum= calc_metadata_hash (thd, &list);
1075
+ if (checksum != state.checksum )
1076
+ {
1077
+ state.checksum = checksum;
1078
+ state.last_charset = thd->variables .character_set_client ;
1079
+ return true ;
1080
+ }
1081
+ }
1082
+
1083
+ /*
1084
+ Character_set_client influences result set metadata, thus resend metadata
1085
+ whenever it changes.
1086
+ */
1087
+ if (state.last_charset != thd->variables .character_set_client )
1088
+ {
1089
+ state.last_charset = thd->variables .character_set_client ;
1090
+ return true ;
1091
+ }
1092
+
1093
+ return false ;
1094
+ }
1095
+
1096
+
1097
+ /* *
1098
+ Determine whether column info must be sent to the client.
1099
+ Skip column info, if client supports caching, and (prepared) statement
1100
+ output fields have not changed.
1101
+
1102
+ @param thd THD
1103
+ @param list column info
1104
+ @param flags send flags. If Protocol::SEND_FORCE_COLUMN_INFO is set,
1105
+ this function will return true
1106
+ @return true, if column info must be sent to the client.
1107
+ false otherwise
1108
+ */
1109
+
1110
+ static bool should_send_column_info (THD* thd, List<Item>* list, uint flags)
1111
+ {
1112
+ if (!(thd->client_capabilities & MARIADB_CLIENT_CACHE_METADATA))
1113
+ {
1114
+ /* Client does not support abbreviated metadata.*/
1115
+ return true ;
1116
+ }
1117
+
1118
+ if (!thd->cur_stmt )
1119
+ {
1120
+ /* Neither COM_PREPARE nor COM_EXECUTE run.*/
1121
+ return true ;
1122
+ }
1123
+
1124
+ if (thd->spcont )
1125
+ {
1126
+ /* Always sent full metadata from inside the stored procedure.*/
1127
+ return true ;
1128
+ }
1129
+
1130
+ if (flags & Protocol::SEND_FORCE_COLUMN_INFO)
1131
+ return true ;
1132
+
1133
+ auto &column_info_state= thd->cur_stmt ->column_info_state ;
1134
+ #ifndef DBUG_OFF
1135
+ auto cmd= thd->get_command ();
1136
+ #endif
1137
+
1138
+ DBUG_ASSERT (cmd == COM_STMT_EXECUTE || cmd == COM_STMT_PREPARE);
1139
+ DBUG_ASSERT (cmd != COM_STMT_PREPARE || !column_info_state.initialized );
1140
+
1141
+ bool ret= metadata_columns_changed (column_info_state, thd, *list);
1142
+
1143
+ DBUG_ASSERT (cmd != COM_STMT_PREPARE || ret);
1144
+ if (!ret)
1145
+ thd->status_var .skip_metadata_count ++;
1146
+
1147
+ return ret;
1148
+ }
1149
+
1150
+
916
1151
/* *
917
1152
Send name and type of result to client.
918
1153
@@ -938,30 +1173,44 @@ bool Protocol::send_result_set_metadata(List<Item> *list, uint flags)
938
1173
Protocol_text prot (thd, thd->variables .net_buffer_length );
939
1174
DBUG_ENTER (" Protocol::send_result_set_metadata" );
940
1175
1176
+ bool send_column_info= should_send_column_info (thd, list, flags);
1177
+
941
1178
if (flags & SEND_NUM_ROWS)
942
- { // Packet with number of elements
943
- uchar buff[MAX_INT_WIDTH];
1179
+ {
1180
+ /*
1181
+ Packet with number of columns.
1182
+
1183
+ Will also have a 1 byte column info indicator, in case
1184
+ MARIADB_CLIENT_CACHE_METADATA client capability is set.
1185
+ */
1186
+ uchar buff[MAX_INT_WIDTH+1 ];
944
1187
uchar *pos= net_store_length (buff, list->elements );
1188
+ if (thd->client_capabilities & MARIADB_CLIENT_CACHE_METADATA)
1189
+ *pos++= (uchar)send_column_info;
1190
+
945
1191
DBUG_ASSERT (pos <= buff + sizeof (buff));
946
1192
if (my_net_write (&thd->net , buff, (size_t ) (pos-buff)))
947
1193
DBUG_RETURN (1 );
948
1194
}
949
1195
1196
+ if (send_column_info)
1197
+ {
950
1198
#ifndef DBUG_OFF
951
- field_handlers= (const Type_handler**) thd->alloc (sizeof (field_handlers[ 0 ]) *
952
- list->elements );
1199
+ field_handlers= (const Type_handler **) thd->alloc (
1200
+ sizeof (field_handlers[ 0 ]) * list->elements );
953
1201
#endif
954
1202
955
- for (uint pos= 0 ; (item=it++); pos++)
956
- {
957
- prot.prepare_for_resend ();
958
- if (prot.store_item_metadata (thd, item, pos))
959
- goto err;
960
- if (prot.write ())
961
- DBUG_RETURN (1 );
1203
+ for (uint pos= 0 ; (item= it++); pos++)
1204
+ {
1205
+ prot.prepare_for_resend ();
1206
+ if (prot.store_item_metadata (thd, item, pos))
1207
+ goto err;
1208
+ if (prot.write ())
1209
+ DBUG_RETURN (1 );
962
1210
#ifndef DBUG_OFF
963
- field_handlers[pos]= item->type_handler ();
1211
+ field_handlers[pos]= item->type_handler ();
964
1212
#endif
1213
+ }
965
1214
}
966
1215
967
1216
if (flags & SEND_EOF)
@@ -1685,7 +1934,8 @@ bool Protocol_binary::send_out_parameters(List<Item_param> *sp_params)
1685
1934
thd->server_status |= SERVER_PS_OUT_PARAMS | SERVER_MORE_RESULTS_EXISTS;
1686
1935
1687
1936
/* Send meta-data. */
1688
- if (send_result_set_metadata (&out_param_lst, SEND_NUM_ROWS | SEND_EOF))
1937
+ if (send_result_set_metadata (&out_param_lst,
1938
+ SEND_NUM_ROWS | SEND_EOF | SEND_FORCE_COLUMN_INFO))
1689
1939
return TRUE ;
1690
1940
1691
1941
/* Send data. */
0 commit comments