/** * decode a packet into proto_field according to field->fielddef->type * * @param field field definition * @returns 0 on success, -1 on error */ int network_mysqld_proto_get_myisam_field(network_packet *packet, network_mysqld_myisam_field *field) { guint64 length; guint8 i8; guint16 i16; guint32 i32; guint64 i64; double d; network_mysqld_column *column = field->column; int err = 0; switch ((guchar)column->type) { case MYSQL_TYPE_DOUBLE: /* a DOUBLE is stored in IEEE notation by just copying all 8 bytes */ err = err || (column->max_length != 8); /* has to be 8 bytes */ err = err || (packet->offset + 8 > packet->data->len); if (!err) { memcpy(&d, packet->data->str + packet->offset, 8); err = err || network_mysqld_proto_skip(packet, 8); } if (!err) field->data.f = d; break; case MYSQL_TYPE_TIMESTAMP: /* int4store */ case MYSQL_TYPE_LONG: err = err || network_mysqld_proto_get_int32(packet, &i32); if (!err) field->data.i = i32; break; case MYSQL_TYPE_DATETIME: /* int8store */ case MYSQL_TYPE_LONGLONG: err = err || network_mysqld_proto_get_int64(packet, &i64); if (!err) field->data.i = i64; break; case MYSQL_TYPE_INT24: case MYSQL_TYPE_DATE: /* int3store, a newdate, old-data is 4 byte */ err = err || network_mysqld_proto_get_int24(packet, &i32); if (!err) field->data.i = i32; break; case MYSQL_TYPE_SHORT: err = err || network_mysqld_proto_get_int16(packet, &i16); if (!err) field->data.i = i16; break; case MYSQL_TYPE_TINY: err = err || network_mysqld_proto_get_int8(packet, &i8); if (!err) field->data.i = i8; break; case MYSQL_TYPE_ENUM: switch (column->max_length) { case 1: err = err || network_mysqld_proto_get_int8(packet, &i8); if (!err) field->data.i = i8; break; case 2: err = err || network_mysqld_proto_get_int16(packet, &i16); if (!err) field->data.i = i16; break; default: g_error("%s: enum-length = %lu", G_STRLOC, column->max_length); break; } break; case MYSQL_TYPE_BLOB: switch (column->max_length) { case 1: err = err || network_mysqld_proto_get_int8(packet, &i8); if (!err) length = i8; break; case 2: err = err || network_mysqld_proto_get_int16(packet, &i16); if (!err) length = i16; break; case 3: err = err || network_mysqld_proto_get_int24(packet, &i32); if (!err) length = i32; break; case 4: err = err || network_mysqld_proto_get_int32(packet, &i32); if (!err) length = i32; break; default: /* unknown blob-length */ g_debug_hexdump(G_STRLOC, S(packet->data)); g_error("%s: blob-length = %lu", G_STRLOC, column->max_length); break; } err = err || network_mysqld_proto_get_string_len(packet, &field->data.s, length); break; case MYSQL_TYPE_VARCHAR: case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_STRING: if (column->max_length < 256) { err = err || network_mysqld_proto_get_int8(packet, &i8); err = err || network_mysqld_proto_get_string_len(packet, &field->data.s, i8); } else { err = err || network_mysqld_proto_get_int16(packet, &i16); err = err || network_mysqld_proto_get_string_len(packet, &field->data.s, i16); } break; case MYSQL_TYPE_NEWDECIMAL: { /* the decimal is binary encoded */ guchar digits_per_bytes[] = { 0, 1, 1, 2, 2, 3, 3, 4, 4, 4 }; /* how many bytes are needed to store x decimal digits */ guint i_digits = column->max_length - column->decimals; guint f_digits = column->decimals; guint decimal_full_blocks = i_digits / 9; /* 9 decimal digits in 4 bytes */ guint decimal_last_block_digits = i_digits % 9; /* how many digits are left ? */ guint scale_full_blocks = f_digits / 9; /* 9 decimal digits in 4 bytes */ guint scale_last_block_digits = f_digits % 9; /* how many digits are left ? */ guint size = 0; size += decimal_full_blocks * digits_per_bytes[9] + digits_per_bytes[decimal_last_block_digits]; size += scale_full_blocks * digits_per_bytes[9] + digits_per_bytes[scale_last_block_digits]; #if 0 g_debug_hexdump(G_STRLOC " (NEWDECIMAL)", packet->data->str, packet->data->len); #endif #if 0 g_critical("%s: don't know how to decode NEWDECIMAL(%lu, %u) at offset %u (%d)", G_STRLOC, column->max_length, column->decimals, packet->offset, size ); #endif err = err || network_mysqld_proto_skip(packet, size); break; } default: g_debug_hexdump(G_STRLOC, packet->data->str, packet->data->len); g_error("%s: unknown field-type to fetch: %d", G_STRLOC, column->type); break; } return err ? -1 : 0; }
/** * get the next row from the resultset * * returns a lua-table with the fields (starting at 1) * * @return 0 on error, 1 on success * */ static int proxy_resultset_rows_iter(lua_State *L) { GRef *ref = *(GRef **)lua_touserdata(L, lua_upvalueindex(1)); proxy_resultset_t *res = ref->udata; network_packet packet; GPtrArray *fields = res->fields; gsize i; int err = 0; network_mysqld_lenenc_type lenenc_type; GList *chunk = res->row; g_return_val_if_fail(chunk != NULL, 0); packet.data = chunk->data; packet.offset = 0; err = err || network_mysqld_proto_skip_network_header(&packet); err = err || network_mysqld_proto_peek_lenenc_type(&packet, &lenenc_type); g_return_val_if_fail(err == 0, 0); /* protocol error */ switch (lenenc_type) { case NETWORK_MYSQLD_LENENC_TYPE_ERR: /* a ERR packet instead of real rows * * like "explain select fld3 from t2 ignore index (fld3,not_existing)" * * see mysql-test/t/select.test */ case NETWORK_MYSQLD_LENENC_TYPE_EOF: /* if we find the 2nd EOF packet we are done */ return 0; case NETWORK_MYSQLD_LENENC_TYPE_INT: case NETWORK_MYSQLD_LENENC_TYPE_NULL: break; } lua_newtable(L); for (i = 0; i < fields->len; i++) { guint64 field_len; err = err || network_mysqld_proto_peek_lenenc_type(&packet, &lenenc_type); g_return_val_if_fail(err == 0, 0); /* protocol error */ switch (lenenc_type) { case NETWORK_MYSQLD_LENENC_TYPE_NULL: network_mysqld_proto_skip(&packet, 1); lua_pushnil(L); break; case NETWORK_MYSQLD_LENENC_TYPE_INT: err = err || network_mysqld_proto_get_lenenc_int(&packet, &field_len); err = err || !(field_len <= packet.data->len); /* just to check that we don't overrun by the addition */ err = err || !(packet.offset + field_len <= packet.data->len); /* check that we have enough string-bytes for the length-encoded string */ if (err) return luaL_error(L, "%s: row-data is invalid", G_STRLOC); lua_pushlstring(L, packet.data->str + packet.offset, field_len); err = err || network_mysqld_proto_skip(&packet, field_len); break; default: /* EOF and ERR should come up here */ err = 1; break; } /* lua starts its tables at 1 */ lua_rawseti(L, -2, i + 1); g_return_val_if_fail(err == 0, 0); /* protocol error */ } res->row = res->row->next; return 1; }
int network_mysqld_proto_get_binlog_event(network_packet *packet, network_mysqld_binlog *binlog, network_mysqld_binlog_event *event) { int err = 0; int maj, min, pat; if (binlog->checksum == NETWORK_MYSQLD_BINLOG_CHECKSUM_CRC32) { /* patch the packet-len for the decoders as if there would be no checksum */ packet->data->len -= 4; } switch ((guchar)event->event_type) { case QUERY_EVENT: err = err || network_mysqld_proto_get_int32(packet, &event->event.query_event.thread_id); err = err || network_mysqld_proto_get_int32(packet, &event->event.query_event.exec_time); err = err || network_mysqld_proto_get_int8(packet, &event->event.query_event.db_name_len); err = err || network_mysqld_proto_get_int16(packet, &event->event.query_event.error_code); /* 5.0 has more flags */ if (packet->data->len > packet->offset) { guint16 var_size = 0; err = err || network_mysqld_proto_get_int16(packet, &var_size); if (var_size) { /* skip the variable size part for now */ err = err || network_mysqld_proto_skip(packet, var_size); } /* default db has <db_name_len> chars */ err = err || network_mysqld_proto_get_string_len(packet, &event->event.query_event.db_name, event->event.query_event.db_name_len); err = err || network_mysqld_proto_skip(packet, 1); /* the \0 */ err = err || network_mysqld_proto_get_string_len(packet, &event->event.query_event.query, packet->data->len - packet->offset); } break; case ROTATE_EVENT: err = err || network_mysqld_proto_get_int32(packet, &event->event.rotate_event.binlog_pos); err = err || network_mysqld_proto_skip(packet, 4); err = err || network_mysqld_proto_get_string_len( packet, &event->event.rotate_event.binlog_file, packet->data->len - packet->offset); break; case STOP_EVENT: /* is empty */ break; case FORMAT_DESCRIPTION_EVENT: err = err || network_mysqld_proto_get_int16(packet, &event->event.format_event.binlog_version); err = err || network_mysqld_proto_get_string_len( /* NUL-term string */ packet, &event->event.format_event.master_version, ST_SERVER_VER_LEN); err = err || network_mysqld_proto_get_int32(packet, &event->event.format_event.created_ts); /* the header length may change in the future, for now we assume it is 19 */ err = err || network_mysqld_proto_get_int8(packet, &event->event.format_event.log_header_len); g_assert_cmpint(event->event.format_event.log_header_len, ==, 19); /* decode the server-version string into a integer */ err = err || (3 != sscanf(event->event.format_event.master_version, "%d.%d.%d%*s", &maj, &min, &pat)); err = err || (maj > 100 || maj < 0); err = err || (min > 100 || min < 0); err = err || (pat > 100 || pat < 0); if ((maj << 16) + (min << 8) + (pat << 0) >= 0x050602) { event->event.format_event.perm_events_len = packet->data->len - packet->offset; g_assert_cmpint(event->event.format_event.perm_events_len, ==, ENUM_END_EVENT - 1 + 1 + 4); event->event.format_event.perm_events_len -= 1 + 4; /* FIXME: check how it is implemented without CRC32 */ } else {