/**
 * 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 {