Below is the list of changes that have just been committed into a local
5.1 repository of sven. When sven does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html
ChangeSet@stripped, 2007-10-31 13:29:49+01:00, sven@murkla.(none) +2 -0
BUG#31581: 5.1-telco-6.1 -> 5.1.22. Slave crashes during starting
- Improved code formatting. (Even if that this changes many lines of the
file, it should be safe against conflicts because the code was introduced
in my previous patch, so noone else edited it.)
- Added comments to header file explaining what the file contains and what
the classes do.
- Added assert(0) to code that should not be reached.
sql/log_event_old.cc@stripped, 2007-10-31 13:29:45+01:00, sven@murkla.(none) +192 -142
- Made code follow our conventions with two empty lines between functions.
- Moved methods in Old_rows_log_event that were in the sections for methods
in {Write|Delete}_rows_log_event, to the section for methods in
Old_rows_log_event.
- added assert(0) to methods for writing old type events.
sql/log_event_old.h@stripped, 2007-10-31 13:29:45+01:00, sven@murkla.(none) +59 -1
Added per-file and per-class comments explaining what this code is about.
diff -Nrup a/sql/log_event_old.cc b/sql/log_event_old.cc
--- a/sql/log_event_old.cc 2007-10-31 11:43:31 +01:00
+++ b/sql/log_event_old.cc 2007-10-31 13:29:45 +01:00
@@ -336,6 +336,7 @@ Old_rows_log_event::do_apply_event(Old_r
}
#endif
+
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
/*
@@ -350,6 +351,7 @@ last_uniq_key(TABLE *table, uint keyno)
return 1;
}
+
/*
Compares table->record[0] and table->record[1]
@@ -428,6 +430,7 @@ record_compare_exit:
return result;
}
+
/*
Copy "extra" columns from record[1] to record[0].
@@ -516,6 +519,7 @@ copy_extra_record_fields(TABLE *table,
DBUG_RETURN(0); // All OK
}
+
/*
Replace the provided record in the database.
@@ -668,6 +672,7 @@ replace_record(THD *thd, TABLE *table,
DBUG_RETURN(error);
}
+
/**
Find the row given by 'key', if the table has keys, or else use a table scan
to find (and fetch) the row.
@@ -879,6 +884,7 @@ static int find_and_fetch_row(TABLE *tab
DBUG_RETURN(0);
}
+
/**********************************************************
Row handling primitives for Write_rows_log_event_old
**********************************************************/
@@ -944,6 +950,7 @@ int Write_rows_log_event_old::do_before_
return error;
}
+
int Write_rows_log_event_old::do_after_row_operations(TABLE *table, int error)
{
int local_error= 0;
@@ -962,6 +969,7 @@ int Write_rows_log_event_old::do_after_r
return error? error : local_error;
}
+
int
Write_rows_log_event_old::do_prepare_row(THD *thd_arg,
Relay_log_info const *rli,
@@ -981,6 +989,7 @@ Write_rows_log_event_old::do_prepare_row
return error;
}
+
int Write_rows_log_event_old::do_exec_row(TABLE *table)
{
DBUG_ASSERT(table != NULL);
@@ -988,6 +997,7 @@ int Write_rows_log_event_old::do_exec_ro
return error;
}
+
/**********************************************************
Row handling primitives for Delete_rows_log_event_old
**********************************************************/
@@ -1029,6 +1039,7 @@ int Delete_rows_log_event_old::do_before
return error;
}
+
int Delete_rows_log_event_old::do_after_row_operations(TABLE *table, int error)
{
/*error= ToDo:find out what this should really be, this triggers close_scan in nbd,
returning error?*/
@@ -1041,6 +1052,7 @@ int Delete_rows_log_event_old::do_after_
return error;
}
+
int
Delete_rows_log_event_old::do_prepare_row(THD *thd_arg,
Relay_log_info const *rli,
@@ -1074,6 +1086,7 @@ Delete_rows_log_event_old::do_prepare_ro
return error;
}
+
int Delete_rows_log_event_old::do_exec_row(TABLE *table)
{
int error;
@@ -1091,6 +1104,7 @@ int Delete_rows_log_event_old::do_exec_r
return error;
}
+
/**********************************************************
Row handling primitives for Update_rows_log_event_old
**********************************************************/
@@ -1124,6 +1138,7 @@ int Update_rows_log_event_old::do_before
return error;
}
+
int Update_rows_log_event_old::do_after_row_operations(TABLE *table, int error)
{
/*error= ToDo:find out what this should really be, this triggers close_scan in nbd,
returning error?*/
@@ -1136,6 +1151,7 @@ int Update_rows_log_event_old::do_after_
return error;
}
+
int Update_rows_log_event_old::do_prepare_row(THD *thd_arg,
Relay_log_info const *rli,
TABLE *table,
@@ -1179,6 +1195,7 @@ int Update_rows_log_event_old::do_prepar
return error;
}
+
int Update_rows_log_event_old::do_exec_row(TABLE *table)
{
DBUG_ASSERT(table != NULL);
@@ -1271,6 +1288,7 @@ Old_rows_log_event::Old_rows_log_event(T
}
#endif
+
Old_rows_log_event::Old_rows_log_event(const char *buf, uint event_len,
Log_event_type event_type,
const Format_description_log_event
@@ -1385,6 +1403,7 @@ Old_rows_log_event::Old_rows_log_event(c
DBUG_VOID_RETURN;
}
+
Old_rows_log_event::~Old_rows_log_event()
{
if (m_cols.bitmap == m_bitbuf) // no my_malloc happened
@@ -1393,6 +1412,7 @@ Old_rows_log_event::~Old_rows_log_event(
my_free((uchar*)m_rows_buf, MYF(MY_ALLOW_ZERO_PTR));
}
+
int Old_rows_log_event::get_data_size()
{
int const type_code= get_type_code();
@@ -1474,6 +1494,7 @@ int Old_rows_log_event::do_add_row_data(
}
#endif
+
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
{
@@ -1840,6 +1861,7 @@ int Old_rows_log_event::do_apply_event(R
DBUG_RETURN(0);
}
+
Log_event::enum_skip_reason
Old_rows_log_event::do_shall_skip(Relay_log_info *rli)
{
@@ -1941,10 +1963,15 @@ Old_rows_log_event::do_update_pos(Relay_
#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
+
#ifndef MYSQL_CLIENT
bool Old_rows_log_event::write_data_header(IO_CACHE *file)
{
uchar buf[ROWS_HEADER_LEN]; // No need to init the buffer
+
+ // This method should not be reached.
+ assert(0);
+
DBUG_ASSERT(m_table_id != ~0UL);
DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master",
{
@@ -1957,6 +1984,7 @@ bool Old_rows_log_event::write_data_head
return (my_b_safe_write(file, buf, ROWS_HEADER_LEN));
}
+
bool Old_rows_log_event::write_data_body(IO_CACHE*file)
{
/*
@@ -1965,6 +1993,10 @@ bool Old_rows_log_event::write_data_body
*/
uchar sbuf[sizeof(m_width)];
my_ptrdiff_t const data_size= m_rows_cur - m_rows_buf;
+
+ // This method should not be reached.
+ assert(0);
+
bool res= false;
uchar *const sbuf_end= net_store_length(sbuf, (size_t) m_width);
DBUG_ASSERT(static_cast<size_t>(sbuf_end - sbuf) <= sizeof(sbuf));
@@ -1993,6 +2025,7 @@ bool Old_rows_log_event::write_data_body
}
#endif
+
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
void Old_rows_log_event::pack_info(Protocol *protocol)
{
@@ -2005,6 +2038,7 @@ void Old_rows_log_event::pack_info(Proto
}
#endif
+
#ifdef MYSQL_CLIENT
void Old_rows_log_event::print_helper(FILE *file,
PRINT_EVENT_INFO *print_event_info,
@@ -2030,120 +2064,6 @@ void Old_rows_log_event::print_helper(FI
}
#endif
-/**************************************************************************
- Write_rows_log_event member functions
-**************************************************************************/
-
-/*
- Constructor used to build an event for writing to the binary log.
- */
-#if !defined(MYSQL_CLIENT)
-Write_rows_log_event_old::Write_rows_log_event_old(THD *thd_arg,
- TABLE *tbl_arg,
- ulong tid_arg,
- MY_BITMAP const *cols,
- bool is_transactional)
- : Old_rows_log_event(thd_arg, tbl_arg, tid_arg, cols, is_transactional)
-{
-}
-#endif
-
-/*
- Constructor used by slave to read the event from the binary log.
- */
-#ifdef HAVE_REPLICATION
-Write_rows_log_event_old::Write_rows_log_event_old(const char *buf,
- uint event_len,
- const Format_description_log_event
- *description_event)
-: Old_rows_log_event(buf, event_len, PRE_GA_WRITE_ROWS_EVENT,
- description_event)
-{
-}
-#endif
-
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
-int
-Write_rows_log_event_old::do_before_row_operations(const Slave_reporting_capability
*const)
-{
- int error= 0;
-
- /*
- We are using REPLACE semantics and not INSERT IGNORE semantics
- when writing rows, that is: new rows replace old rows. We need to
- inform the storage engine that it should use this behaviour.
- */
-
- /* Tell the storage engine that we are using REPLACE semantics. */
- thd->lex->duplicates= DUP_REPLACE;
-
- /*
- Pretend we're executing a REPLACE command: this is needed for
- InnoDB and NDB Cluster since they are not (properly) checking the
- lex->duplicates flag.
- */
- thd->lex->sql_command= SQLCOM_REPLACE;
- /*
- Do not raise the error flag in case of hitting to an unique attribute
- */
- m_table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
- /*
- NDB specific: update from ndb master wrapped as Write_rows
- */
- /*
- so that the event should be applied to replace slave's row
- */
- m_table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
- /*
- NDB specific: if update from ndb master wrapped as Write_rows
- does not find the row it's assumed idempotent binlog applying
- is taking place; don't raise the error.
- */
- m_table->file->extra(HA_EXTRA_IGNORE_NO_KEY);
- /*
- TODO: the cluster team (Tomas?) says that it's better if the engine knows
- how many rows are going to be inserted, then it can allocate needed memory
- from the start.
- */
- m_table->file->ha_start_bulk_insert(0);
- /*
- We need TIMESTAMP_NO_AUTO_SET otherwise ha_write_row() will not use fill
- any TIMESTAMP column with data from the row but instead will use
- the event's current time.
- As we replicate from TIMESTAMP to TIMESTAMP and slave has no extra
- columns, we know that all TIMESTAMP columns on slave will receive explicit
- data from the row, so TIMESTAMP_NO_AUTO_SET is ok.
- When we allow a table without TIMESTAMP to be replicated to a table having
- more columns including a TIMESTAMP column, or when we allow a TIMESTAMP
- column to be replicated into a BIGINT column and the slave's table has a
- TIMESTAMP column, then the slave's TIMESTAMP column will take its value
- from set_time() which we called earlier (consistent with SBR). And then in
- some cases we won't want TIMESTAMP_NO_AUTO_SET (will require some code to
- analyze if explicit data is provided for slave's TIMESTAMP columns).
- */
- m_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
- return error;
-}
-
-int
-Write_rows_log_event_old::do_after_row_operations(const Slave_reporting_capability
*const,
- int error)
-{
- int local_error= 0;
- m_table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
- m_table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
- /*
- reseting the extra with
- table->file->extra(HA_EXTRA_NO_IGNORE_NO_KEY);
- fires bug#27077
- todo: explain or fix
- */
- if ((local_error= m_table->file->ha_end_bulk_insert()))
- {
- m_table->file->print_error(local_error, MYF(0));
- }
- return error? error : local_error;
-}
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
/**
@@ -2366,35 +2286,7 @@ Old_rows_log_event::write_row(const Rela
DBUG_RETURN(error);
}
-#endif
-
-int
-Write_rows_log_event_old::do_exec_row(const Relay_log_info *const rli)
-{
- DBUG_ASSERT(m_table != NULL);
- int error= write_row(rli, TRUE /* overwrite */);
-
- if (error && !thd->net.last_errno)
- thd->net.last_errno= error;
-
- return error;
-}
-
-#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
-
-#ifdef MYSQL_CLIENT
-void Write_rows_log_event_old::print(FILE *file,
- PRINT_EVENT_INFO* print_event_info)
-{
- Old_rows_log_event::print_helper(file, print_event_info, "Write_rows_old");
-}
-#endif
-
-/**************************************************************************
- Delete_rows_log_event member functions
-**************************************************************************/
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
/**
Locate the current row in event's table.
@@ -2662,6 +2554,154 @@ int Old_rows_log_event::find_row(const R
#endif
+
+/**************************************************************************
+ Write_rows_log_event member functions
+**************************************************************************/
+
+/*
+ Constructor used to build an event for writing to the binary log.
+ */
+#if !defined(MYSQL_CLIENT)
+Write_rows_log_event_old::Write_rows_log_event_old(THD *thd_arg,
+ TABLE *tbl_arg,
+ ulong tid_arg,
+ MY_BITMAP const *cols,
+ bool is_transactional)
+ : Old_rows_log_event(thd_arg, tbl_arg, tid_arg, cols, is_transactional)
+{
+}
+#endif
+
+
+/*
+ Constructor used by slave to read the event from the binary log.
+ */
+#ifdef HAVE_REPLICATION
+Write_rows_log_event_old::Write_rows_log_event_old(const char *buf,
+ uint event_len,
+ const Format_description_log_event
+ *description_event)
+: Old_rows_log_event(buf, event_len, PRE_GA_WRITE_ROWS_EVENT,
+ description_event)
+{
+}
+#endif
+
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+int
+Write_rows_log_event_old::do_before_row_operations(const Slave_reporting_capability
*const)
+{
+ int error= 0;
+
+ /*
+ We are using REPLACE semantics and not INSERT IGNORE semantics
+ when writing rows, that is: new rows replace old rows. We need to
+ inform the storage engine that it should use this behaviour.
+ */
+
+ /* Tell the storage engine that we are using REPLACE semantics. */
+ thd->lex->duplicates= DUP_REPLACE;
+
+ /*
+ Pretend we're executing a REPLACE command: this is needed for
+ InnoDB and NDB Cluster since they are not (properly) checking the
+ lex->duplicates flag.
+ */
+ thd->lex->sql_command= SQLCOM_REPLACE;
+ /*
+ Do not raise the error flag in case of hitting to an unique attribute
+ */
+ m_table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
+ /*
+ NDB specific: update from ndb master wrapped as Write_rows
+ */
+ /*
+ so that the event should be applied to replace slave's row
+ */
+ m_table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
+ /*
+ NDB specific: if update from ndb master wrapped as Write_rows
+ does not find the row it's assumed idempotent binlog applying
+ is taking place; don't raise the error.
+ */
+ m_table->file->extra(HA_EXTRA_IGNORE_NO_KEY);
+ /*
+ TODO: the cluster team (Tomas?) says that it's better if the engine knows
+ how many rows are going to be inserted, then it can allocate needed memory
+ from the start.
+ */
+ m_table->file->ha_start_bulk_insert(0);
+ /*
+ We need TIMESTAMP_NO_AUTO_SET otherwise ha_write_row() will not use fill
+ any TIMESTAMP column with data from the row but instead will use
+ the event's current time.
+ As we replicate from TIMESTAMP to TIMESTAMP and slave has no extra
+ columns, we know that all TIMESTAMP columns on slave will receive explicit
+ data from the row, so TIMESTAMP_NO_AUTO_SET is ok.
+ When we allow a table without TIMESTAMP to be replicated to a table having
+ more columns including a TIMESTAMP column, or when we allow a TIMESTAMP
+ column to be replicated into a BIGINT column and the slave's table has a
+ TIMESTAMP column, then the slave's TIMESTAMP column will take its value
+ from set_time() which we called earlier (consistent with SBR). And then in
+ some cases we won't want TIMESTAMP_NO_AUTO_SET (will require some code to
+ analyze if explicit data is provided for slave's TIMESTAMP columns).
+ */
+ m_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
+ return error;
+}
+
+
+int
+Write_rows_log_event_old::do_after_row_operations(const Slave_reporting_capability
*const,
+ int error)
+{
+ int local_error= 0;
+ m_table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
+ m_table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
+ /*
+ reseting the extra with
+ table->file->extra(HA_EXTRA_NO_IGNORE_NO_KEY);
+ fires bug#27077
+ todo: explain or fix
+ */
+ if ((local_error= m_table->file->ha_end_bulk_insert()))
+ {
+ m_table->file->print_error(local_error, MYF(0));
+ }
+ return error? error : local_error;
+}
+
+
+int
+Write_rows_log_event_old::do_exec_row(const Relay_log_info *const rli)
+{
+ DBUG_ASSERT(m_table != NULL);
+ int error= write_row(rli, TRUE /* overwrite */);
+
+ if (error && !thd->net.last_errno)
+ thd->net.last_errno= error;
+
+ return error;
+}
+
+#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
+
+
+#ifdef MYSQL_CLIENT
+void Write_rows_log_event_old::print(FILE *file,
+ PRINT_EVENT_INFO* print_event_info)
+{
+ Old_rows_log_event::print_helper(file, print_event_info, "Write_rows_old");
+}
+#endif
+
+
+/**************************************************************************
+ Delete_rows_log_event member functions
+**************************************************************************/
+
/*
Constructor used to build an event for writing to the binary log.
*/
@@ -2678,6 +2718,7 @@ Delete_rows_log_event_old::Delete_rows_l
}
#endif /* #if !defined(MYSQL_CLIENT) */
+
/*
Constructor used by slave to read the event from the binary log.
*/
@@ -2693,6 +2734,7 @@ Delete_rows_log_event_old::Delete_rows_l
}
#endif
+
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
int
@@ -2717,6 +2759,7 @@ Delete_rows_log_event_old::do_before_row
return 0;
}
+
int
Delete_rows_log_event_old::do_after_row_operations(const Slave_reporting_capability
*const,
int error)
@@ -2729,6 +2772,7 @@ Delete_rows_log_event_old::do_after_row_
return error;
}
+
int Delete_rows_log_event_old::do_exec_row(const Relay_log_info *const rli)
{
int error;
@@ -2746,6 +2790,7 @@ int Delete_rows_log_event_old::do_exec_r
#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
+
#ifdef MYSQL_CLIENT
void Delete_rows_log_event_old::print(FILE *file,
PRINT_EVENT_INFO* print_event_info)
@@ -2774,6 +2819,7 @@ Update_rows_log_event_old::Update_rows_l
init(cols);
}
+
void Update_rows_log_event_old::init(MY_BITMAP const *cols)
{
/* if bitmap_init fails, caught in is_valid() */
@@ -2817,6 +2863,7 @@ Update_rows_log_event_old::Update_rows_l
}
#endif
+
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
int
@@ -2835,6 +2882,7 @@ Update_rows_log_event_old::do_before_row
return 0;
}
+
int
Update_rows_log_event_old::do_after_row_operations(const Slave_reporting_capability
*const,
int error)
@@ -2847,6 +2895,7 @@ Update_rows_log_event_old::do_after_row_
return error;
}
+
int
Update_rows_log_event_old::do_exec_row(const Relay_log_info *const rli)
{
@@ -2902,6 +2951,7 @@ Update_rows_log_event_old::do_exec_row(c
}
#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
+
#ifdef MYSQL_CLIENT
void Update_rows_log_event_old::print(FILE *file,
diff -Nrup a/sql/log_event_old.h b/sql/log_event_old.h
--- a/sql/log_event_old.h 2007-10-31 11:43:31 +01:00
+++ b/sql/log_event_old.h 2007-10-31 13:29:45 +01:00
@@ -20,7 +20,37 @@
Need to include this file at the proper position of log_event.h
*/
+
+/**
+ @file
+
+ @brief This file contains classes handling old formats of row-based
+ binlog events.
+*/
+/*
+ Around 2007-10-31, I made these classes completely separated from
+ the new classes (before, there was a complex class hierarchy
+ involving multiple inheritance; see BUG#31581), by simply copying
+ and pasting the entire contents of Rows_log_event into
+ Old_rows_log_event and the entire contents of
+ {Write|Update|Delete}_rows_log_event into
+ {Write|Update|Delete}_rows_log_event_old. For clarity, I will keep
+ the comments marking which code was cut-and-pasted for some time.
+ With the classes collapsed into one, there is probably some
+ redundancy (maybe some methods can be simplified and/or removed),
+ but we keep them this way for now. /Sven
+*/
+
+
+/**
+ @class Old_rows_log_event
+ Base class for the three types of row-based events
+ {Write|Update|Delete}_row_log_event_old, with event type codes
+ PRE_GA_{WRITE|UPDATE|DELETE}_ROWS_EVENT. These events are never
+ created any more, except when reading a relay log created by an old
+ server.
+*/
class Old_rows_log_event : public Log_event
{
/********** BEGIN CUT & PASTE FROM Rows_log_event **********/
@@ -321,6 +351,15 @@ private:
};
+/**
+ @class Write_rows_log_event_old
+
+ Old class for binlog events that write new rows to a table (event
+ type code PRE_GA_WRITE_ROWS_EVENT). Such events are never produced
+ by this version of the server, but they may be read from a relay log
+ created by an old server. New servers create events of class
+ Write_rows_log_event (event type code WRITE_ROWS_EVENT) instead.
+*/
class Write_rows_log_event_old : public Old_rows_log_event
{
/********** BEGIN CUT & PASTE FROM Write_rows_log_event **********/
@@ -385,6 +424,16 @@ private:
};
+/**
+ @class Update_rows_log_event_old
+
+ Old class for binlog events that modify existing rows to a table
+ (event type code PRE_GA_UPDATE_ROWS_EVENT). Such events are never
+ produced by this version of the server, but they may be read from a
+ relay log created by an old server. New servers create events of
+ class Update_rows_log_event (event type code UPDATE_ROWS_EVENT)
+ instead.
+*/
class Update_rows_log_event_old : public Old_rows_log_event
{
/********** BEGIN CUT & PASTE FROM Update_rows_log_event **********/
@@ -461,6 +510,16 @@ private:
};
+/**
+ @class Delete_rows_log_event_old
+
+ Old class for binlog events that delete existing rows from a table
+ (event type code PRE_GA_DELETE_ROWS_EVENT). Such events are never
+ produced by this version of the server, but they may be read from a
+ relay log created by an old server. New servers create events of
+ class Delete_rows_log_event (event type code DELETE_ROWS_EVENT)
+ instead.
+*/
class Delete_rows_log_event_old : public Old_rows_log_event
{
/********** BEGIN CUT & PASTE FROM Update_rows_log_event **********/
@@ -527,4 +586,3 @@ private:
#endif
-
| Thread |
|---|
| • bk commit into 5.1 tree (sven:1.2587) BUG#31581 | Sven Sandberg | 31 Oct |