MySQL  8.0.18
Source Code Documentation
trx0rec.h
Go to the documentation of this file.
1 /*****************************************************************************
2 
3 Copyright (c) 1996, 2019, Oracle and/or its affiliates. All Rights Reserved.
4 
5 This program is free software; you can redistribute it and/or modify it under
6 the terms of the GNU General Public License, version 2.0, as published by the
7 Free Software Foundation.
8 
9 This program is also distributed with certain software (including but not
10 limited to OpenSSL) that is licensed under separate terms, as designated in a
11 particular file or component or in included license documentation. The authors
12 of MySQL hereby grant you an additional permission to link the program and
13 your derivative works with the separately licensed software that they have
14 included with MySQL.
15 
16 This program is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18 FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
19 for more details.
20 
21 You should have received a copy of the GNU General Public License along with
22 this program; if not, write to the Free Software Foundation, Inc.,
23 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24 
25 *****************************************************************************/
26 
27 /** @file include/trx0rec.h
28  Transaction undo log record
29 
30  Created 3/26/1996 Heikki Tuuri
31  *******************************************************/
32 
33 #ifndef trx0rec_h
34 #define trx0rec_h
35 
36 #include "univ.i"
37 
38 #include "data0data.h"
39 #include "dict0types.h"
40 #include "lob0undo.h"
41 #include "mtr0mtr.h"
42 #include "page0types.h"
43 #include "rem0types.h"
44 #include "row0log.h"
45 #include "row0types.h"
46 #include "trx0types.h"
47 
48 #ifndef UNIV_HOTBACKUP
49 #include "que0types.h"
50 
51 /** Copies the undo record to the heap.
52 @param[in] undo_rec undo log record
53 @param[in] heap heap where copied
54 @return own: copy of undo log record */
55 UNIV_INLINE
57  mem_heap_t *heap);
58 
59 /** Reads the undo log record type.
60  @return record type */
61 UNIV_INLINE
63  const trx_undo_rec_t *undo_rec); /*!< in: undo log record */
64 /** Reads from an undo log record the record compiler info.
65  @return compiler info */
66 UNIV_INLINE
68  const trx_undo_rec_t *undo_rec); /*!< in: undo log record */
69 /** Returns TRUE if an undo log record contains an extern storage field.
70  @return true if extern */
71 UNIV_INLINE
73  const trx_undo_rec_t *undo_rec); /*!< in: undo log record */
74 /** Reads the undo log record number.
75  @return undo no */
76 UNIV_INLINE
78  const trx_undo_rec_t *undo_rec); /*!< in: undo log record */
79 
80 /** Returns the start of the undo record data area. */
81 #define trx_undo_rec_get_ptr(undo_rec, undo_no) \
82  ((undo_rec) + trx_undo_rec_get_offset(undo_no))
83 
84 /** Reads from an undo log record the table ID
85 @param[in] undo_rec Undo log record
86 @return the table ID */
88  MY_ATTRIBUTE((warn_unused_result));
89 
90 /** Builds a row reference from an undo log record.
91  @return pointer to remaining part of undo record */
93  byte *ptr, /*!< in: remaining part of a copy of an undo log
94  record, at the start of the row reference;
95  NOTE that this copy of the undo log record must
96  be preserved as long as the row reference is
97  used, as we do NOT copy the data in the
98  record! */
99  dict_index_t *index, /*!< in: clustered index */
100  dtuple_t **ref, /*!< out, own: row reference */
101  mem_heap_t *heap); /*!< in: memory heap from which the memory
102  needed is allocated */
103 /** Reads from an undo log update record the system field values of the old
104  version.
105  @return remaining part of undo log record after reading these values */
107  const byte *ptr, /*!< in: remaining part of undo
108  log record after reading
109  general parameters */
110  trx_id_t *trx_id, /*!< out: trx id */
111  roll_ptr_t *roll_ptr, /*!< out: roll ptr */
112  ulint *info_bits); /*!< out: info bits state */
113 
114 struct type_cmpl_t;
115 
116 /** Builds an update vector based on a remaining part of an undo log record.
117  @return remaining part of the record, NULL if an error detected, which
118  means that the record is corrupted. */
120  const byte *ptr, /*!< in: remaining part in update undo log
121  record, after reading the row reference
122  NOTE that this copy of the undo log record must
123  be preserved as long as the update vector is
124  used, as we do NOT copy the data in the
125  record! */
126  const dict_index_t *index, /*!< in: clustered index */
127  ulint type, /*!< in: TRX_UNDO_UPD_EXIST_REC,
128  TRX_UNDO_UPD_DEL_REC, or
129  TRX_UNDO_DEL_MARK_REC; in the last case,
130  only trx id and roll ptr fields are added to
131  the update vector */
132  trx_id_t trx_id, /*!< in: transaction id from this undo record */
133  roll_ptr_t roll_ptr, /*!< in: roll pointer from this undo record */
134  ulint info_bits, /*!< in: info bits from this undo record */
135  trx_t *trx, /*!< in: transaction */
136  mem_heap_t *heap, /*!< in: memory heap from which the memory
137  needed is allocated */
138  upd_t **upd, /*!< out, own: update vector */
139  lob::undo_vers_t *lob_undo, /*!< out: LOB undo information. */
140  type_cmpl_t &type_cmpl); /*!< out: type compilation info */
141 
142 /** Builds a partial row from an update undo log record, for purge.
143  It contains the columns which occur as ordering in any index of the table.
144  Any missing columns are indicated by col->mtype == DATA_MISSING.
145  @return pointer to remaining part of undo record */
147  const byte *ptr, /*!< in: remaining part in update undo log
148  record of a suitable type, at the start of
149  the stored index columns;
150  NOTE that this copy of the undo log record must
151  be preserved as long as the partial row is
152  used, as we do NOT copy the data in the
153  record! */
154  dict_index_t *index, /*!< in: clustered index */
155  dtuple_t **row, /*!< out, own: partial row */
156  ibool ignore_prefix, /*!< in: flag to indicate if we
157  expect blob prefixes in undo. Used
158  only in the assertion. */
159  mem_heap_t *heap) /*!< in: memory heap from which the memory
160  needed is allocated */
161  MY_ATTRIBUTE((warn_unused_result));
162 /** Writes information to an undo log about an insert, update, or a delete
163  marking of a clustered index record. This information is used in a rollback of
164  the transaction and in consistent reads that must look to the history of this
165  transaction.
166  @return DB_SUCCESS or error code */
168  ulint flags, /*!< in: if BTR_NO_UNDO_LOG_FLAG bit is
169  set, does nothing */
170  ulint op_type, /*!< in: TRX_UNDO_INSERT_OP or
171  TRX_UNDO_MODIFY_OP */
172  que_thr_t *thr, /*!< in: query thread */
173  dict_index_t *index, /*!< in: clustered index */
174  const dtuple_t *clust_entry, /*!< in: in the case of an insert,
175  index entry to insert into the
176  clustered index, otherwise NULL */
177  const upd_t *update, /*!< in: in the case of an update,
178  the update vector, otherwise NULL */
179  ulint cmpl_info, /*!< in: compiler info on secondary
180  index updates */
181  const rec_t *rec, /*!< in: case of an update or delete
182  marking, the record in the clustered
183  index, otherwise NULL */
184  const ulint *offsets, /*!< in: rec_get_offsets(rec) */
185  roll_ptr_t *roll_ptr) /*!< out: rollback pointer to the
186  inserted undo log record,
187  0 if BTR_NO_UNDO_LOG
188  flag was specified */
189  MY_ATTRIBUTE((warn_unused_result));
190 
191 /** status bit used for trx_undo_prev_version_build() */
192 
193 /** TRX_UNDO_PREV_IN_PURGE tells trx_undo_prev_version_build() that it
194 is being called purge view and we would like to get the purge record
195 even it is in the purge view (in normal case, it will return without
196 fetching the purge record */
197 #define TRX_UNDO_PREV_IN_PURGE 0x1
198 
199 /** This tells trx_undo_prev_version_build() to fetch the old value in
200 the undo log (which is the after image for an update) */
201 #define TRX_UNDO_GET_OLD_V_VALUE 0x2
202 
203 /** Build a previous version of a clustered index record. The caller must hold
204 a latch on the index page of the clustered index record.
205 If the vrow passed to this function is not null, then this function will store
206 information about virtual columns from the requested version in vrow, unless the
207 change did not affect any secondary index nor ordering field of clustered index
208 (the change has UPD_NODE_NO_ORD_CHANGE flag) in which case the requested
209 information can not be reconstructed from undo log, and the caller may assume
210 that the (virtual) columns of secondary index have the same values they have in
211 the more recent version (the one `rec` comes from).
212 Equivalently, if the vrow is not returned, it is either because it was not
213 requested, or not available due to UPD_NODE_NO_ORD_CHANGE.
214 Obviously vrow is also not set in case rec is the oldest version in history,
215 in which case we also set old_vers to NULL.
216 @param[in] index_rec clustered index record in the index tree
217 @param[in] index_mtr mtr which contains the latch to index_rec page
218  and purge_view
219 @param[in] rec version of a clustered index record
220 @param[in] index clustered index
221 @param[in,out] offsets rec_get_offsets(rec, index)
222 @param[in] heap memory heap from which the memory needed is
223  allocated
224 @param[out] old_vers previous version, or NULL if rec is the first
225  inserted version, or if history data has been
226  deleted
227 @param[in] v_heap memory heap used to create vrow dtuple if it is
228  not yet created. This heap diffs from "heap"
229  above in that it could be
230  prebuilt->old_vers_heap for selection
231 @param[out] vrow virtual column info, if any
232 @param[in] v_status status determine if it is going into this
233  function by purge thread or not. And if we read
234  "after image" of undo log has been rebuilt
235 @param[in] lob_undo LOB undo information.
236 @retval true if previous version was built, or if it was an insert or the table
237 has been rebuilt
238 @retval false if the previous version is earlier than purge_view, or being
239 purged, which means that it may have been removed */
240 bool trx_undo_prev_version_build(const rec_t *index_rec, mtr_t *index_mtr,
241  const rec_t *rec, const dict_index_t *index,
242  ulint *offsets, mem_heap_t *heap,
243  rec_t **old_vers, mem_heap_t *v_heap,
244  const dtuple_t **vrow, ulint v_status,
245  lob::undo_vers_t *lob_undo);
246 
247 #endif /* !UNIV_HOTBACKUP */
248 /** Parses a redo log record of adding an undo log record.
249  @return end of log record or NULL */
250 byte *trx_undo_parse_add_undo_rec(byte *ptr, /*!< in: buffer */
251  byte *end_ptr, /*!< in: buffer end */
252  page_t *page); /*!< in: page or NULL */
253 /** Parses a redo log record of erasing of an undo page end.
254  @return end of log record or NULL */
255 byte *trx_undo_parse_erase_page_end(byte *ptr, /*!< in: buffer */
256  byte *end_ptr, /*!< in: buffer end */
257  page_t *page, /*!< in: page or NULL */
258  mtr_t *mtr); /*!< in: mtr or NULL */
259 
260 /** Read from an undo log record a non-virtual column value.
261 @param[in,out] ptr pointer to remaining part of the undo record
262 @param[in,out] field stored field
263 @param[in,out] len length of the field, or UNIV_SQL_NULL
264 @param[in,out] orig_len original length of the locally stored part
265 of an externally stored column, or 0
266 @return remaining part of undo log record after reading these values */
267 byte *trx_undo_rec_get_col_val(const byte *ptr, const byte **field, ulint *len,
268  ulint *orig_len);
269 
270 /** Read virtual column value from undo log
271 @param[in] table the table
272 @param[in] ptr undo log pointer
273 @param[in,out] row the dtuple to fill
274 @param[in] in_purge called by purge thread
275 @param[in] online true if this is from online DDL log
276 @param[in] col_map online rebuild column map
277 @param[in,out] heap memory heap to keep value when necessary */
278 void trx_undo_read_v_cols(const dict_table_t *table, const byte *ptr,
279  const dtuple_t *row, bool in_purge, bool online,
280  const ulint *col_map, mem_heap_t *heap);
281 
282 /** Read virtual column index from undo log if the undo log contains such
283 info, and verify the column is still indexed, and output its position
284 @param[in] table the table
285 @param[in] ptr undo log pointer
286 @param[in] first_v_col if this is the first virtual column, which
287  has the version marker
288 @param[in,out] is_undo_log his function is used to parse both undo log,
289  and online log for virtual columns. So
290  check to see if this is undo log
291 @param[out] field_no the column number
292 @return remaining part of undo log record after reading these values */
293 const byte *trx_undo_read_v_idx(const dict_table_t *table, const byte *ptr,
294  bool first_v_col, bool *is_undo_log,
295  ulint *field_no);
296 
297 #ifndef UNIV_HOTBACKUP
298 
299 /* Types of an undo log record: these have to be smaller than 16, as the
300 compilation info multiplied by 16 is ORed to this value in an undo log
301 record */
302 
303 #define TRX_UNDO_INSERT_REC 11 /* fresh insert into clustered index */
304 #define TRX_UNDO_UPD_EXIST_REC \
305  12 /* update of a non-delete-marked \
306  record */
307 #define TRX_UNDO_UPD_DEL_REC \
308  13 /* update of a delete marked record to \
309  a not delete marked record; also the \
310  fields of the record can change */
311 #define TRX_UNDO_DEL_MARK_REC \
312  14 /* delete marking of a record; fields \
313  do not change */
314 #define TRX_UNDO_CMPL_INFO_MULT \
315  16 /* compilation info is multiplied by \
316  this and ORed to the type above */
317 
318 #define TRX_UNDO_MODIFY_BLOB \
319  64 /* If this bit is set in type_cmpl, \
320  then the undo log record has support \
321  for partial update of BLOBs. Also to \
322  make the undo log format extensible, \
323  introducing a new flag next to the \
324  type_cmpl flag. */
325 
326 #define TRX_UNDO_UPD_EXTERN \
327  128 /* This bit can be ORed to type_cmpl \
328  to denote that we updated external \
329  storage fields: used by purge to \
330  free the external storage */
331 
332 /* Operation type flags used in trx_undo_report_row_operation */
333 #define TRX_UNDO_INSERT_OP 1
334 #define TRX_UNDO_MODIFY_OP 2
335 
336 /** The type and compilation info flag in the undo record for update.
337 For easier understanding let the 8 bits be numbered as
338 7, 6, 5, 4, 3, 2, 1, 0. */
339 struct type_cmpl_t {
340  type_cmpl_t() : m_flag(0) {}
341 
342  const byte *read(const byte *ptr) {
343  m_flag = mach_read_from_1(ptr);
344  return (ptr + 1);
345  }
346 
347  ulint type_info() {
348  /* Get 0-3 */
349  return (m_flag & 0x0F);
350  }
351 
352  ulint cmpl_info() {
353  /* Get bits 5 and 4 */
354  return ((m_flag >> 4) & 0x03);
355  }
356 
357  /** Is an LOB updated by this update operation.
358  @return true if LOB is updated, false otherwise. */
359  bool is_lob_updated() {
360  /* Check if bit 7 is set. */
361  return (m_flag & TRX_UNDO_UPD_EXTERN);
362  }
363 
364  /** Does the undo log record contains information about LOB partial
365  update vector.
366  @return true if undo contains LOB update info. */
367  bool is_lob_undo() const {
368  /* Check if bit 6 is set. */
369  return (m_flag & TRX_UNDO_MODIFY_BLOB);
370  }
371 
372  private:
373  uint8_t m_flag;
374 };
375 
376 /** Reads from an undo log record the general parameters.
377  @return remaining part of undo log record after reading these values */
379  trx_undo_rec_t *undo_rec, /*!< in: undo log record */
380  ulint *type, /*!< out: undo record type:
381  TRX_UNDO_INSERT_REC, ... */
382  ulint *cmpl_info, /*!< out: compiler info, relevant only
383  for update type records */
384  bool *updated_extern, /*!< out: true if we updated an
385  externally stored fild */
386  undo_no_t *undo_no, /*!< out: undo log record number */
387  table_id_t *table_id, /*!< out: table id */
388  type_cmpl_t &type_cmpl); /*!< out: type compilation info. */
389 
390 /** Get the max free space of undo log by assuming it's a fresh new page
391 and the free space doesn't count for the undo log header too. */
392 size_t trx_undo_max_free_space();
393 
394 /** Decide if the following undo log record is a multi-value virtual column
395 @param[in] undo_rec undo log record
396 @return true if this is a multi-value virtual column log, otherwise false */
397 bool trx_undo_rec_is_multi_value(const byte *undo_rec);
398 
399 /** Read from an undo log record a non-virtual column value.
400 @param[in,out] ptr pointer to remaining part of the undo record
401 @param[in,out] field stored field
402 @param[in,out] heap memory heap
403 @return remaining part of undo log record after reading these values */
404 const byte *trx_undo_rec_get_multi_value(const byte *ptr, dfield_t *field,
405  mem_heap_t *heap);
406 #include "trx0rec.ic"
407 
408 #endif /* !UNIV_HOTBACKUP */
409 
410 #endif /* trx0rec_h */
Record manager global types.
byte trx_undo_rec_t
Undo log record.
Definition: trx0types.h:177
type_cmpl_t()
Definition: trx0rec.h:340
ib_id_t roll_ptr_t
Rollback pointer (DB_ROLL_PTR, DATA_ROLL_PTR)
Definition: trx0types.h:150
Undo logging small changes to BLOBs.
UNIV_INLINE ulint trx_undo_rec_get_type(const trx_undo_rec_t *undo_rec)
Reads the undo log record type.
Definition: trx0trx.h:780
byte * trx_undo_rec_get_partial_row(const byte *ptr, dict_index_t *index, dtuple_t **row, ibool ignore_prefix, mem_heap_t *heap)
Builds a partial row from an update undo log record, for purge.
Definition: trx0rec.cc:1929
byte * trx_undo_parse_add_undo_rec(byte *ptr, byte *end_ptr, page_t *page)
Parses a redo log record of adding an undo log record.
Definition: trx0rec.cc:104
byte * trx_undo_rec_get_row_ref(byte *ptr, dict_index_t *index, dtuple_t **ref, mem_heap_t *heap)
Builds a row reference from an undo log record.
Definition: trx0rec.cc:680
ulint cmpl_info()
Definition: trx0rec.h:352
dberr_t trx_undo_report_row_operation(ulint flags, ulint op_type, que_thr_t *thr, dict_index_t *index, const dtuple_t *clust_entry, const upd_t *update, ulint cmpl_info, const rec_t *rec, const ulint *offsets, roll_ptr_t *roll_ptr)
Writes information to an undo log about an insert, update, or a delete marking of a clustered index r...
Definition: trx0rec.cc:2120
void trx_undo_read_v_cols(const dict_table_t *table, const byte *ptr, const dtuple_t *row, bool in_purge, bool online, const ulint *col_map, mem_heap_t *heap)
Read virtual column value from undo log.
Definition: trx0rec.cc:2672
Data structure for a database table.
Definition: dict0mem.h:1529
UNIV_INLINE uint8_t mach_read_from_1(const byte *b)
The following function is used to fetch data from one byte.
byte * trx_undo_rec_get_col_val(const byte *ptr, const byte **field, ulint *len, ulint *orig_len)
Read from an undo log record a non-virtual column value.
Definition: trx0rec.cc:638
Index page routines.
byte * trx_undo_rec_get_pars(trx_undo_rec_t *undo_rec, ulint *type, ulint *cmpl_info, bool *updated_extern, undo_no_t *undo_no, table_id_t *table_id, type_cmpl_t &type_cmpl)
Reads from an undo log record the general parameters.
Definition: trx0rec.cc:552
The info structure stored at the beginning of a heap block.
Definition: mem0mem.h:343
UNIV_INLINE ulint trx_undo_rec_get_cmpl_info(const trx_undo_rec_t *undo_rec)
Reads from an undo log record the record compiler info.
SQL data field and tuple.
Query graph global types.
const byte * read(const byte *ptr)
Definition: trx0rec.h:342
size_t trx_undo_max_free_space()
Get the max free space of undo log by assuming it&#39;s a fresh new page and the free space doesn&#39;t count...
Definition: trx0rec.cc:161
int page
Definition: ctype-mb.cc:1231
char * index(const char *, int c)
Definition: mysql.cc:2862
Data dictionary global types.
ulint type_info()
Definition: trx0rec.h:347
byte rec_t
Definition: rem0types.h:39
Structure for an SQL data tuple of fields (logical record)
Definition: data0data.h:714
dberr_t
Definition: db0err.h:38
byte * trx_undo_parse_erase_page_end(byte *ptr, byte *end_ptr, page_t *page, mtr_t *mtr)
Parses a redo log record of erasing of an undo page end.
Definition: trx0rec.cc:2096
bool is_lob_undo() const
Does the undo log record contains information about LOB partial update vector.
Definition: trx0rec.h:367
Row operation global types.
Definition: que0que.h:246
Structure for an SQL data field.
Definition: data0data.h:644
static uint update
Definition: myisamlog.cc:88
Transaction system global type definitions.
ib_id_t table_id_t
Table or partition identifier (unique within an InnoDB instance).
Definition: dict0types.h:61
bool is_lob_updated()
Is an LOB updated by this update operation.
Definition: trx0rec.h:359
bool trx_undo_prev_version_build(const rec_t *index_rec, mtr_t *index_mtr, const rec_t *rec, const dict_index_t *index, ulint *offsets, mem_heap_t *heap, rec_t **old_vers, mem_heap_t *v_heap, const dtuple_t **vrow, ulint v_status, lob::undo_vers_t *lob_undo)
Build a previous version of a clustered index record.
Definition: trx0rec.cc:2452
UNIV_INLINE undo_no_t trx_undo_rec_get_undo_no(const trx_undo_rec_t *undo_rec)
Reads the undo log record number.
#define TRX_UNDO_UPD_EXTERN
Definition: trx0rec.h:326
ib_id_t trx_id_t
Transaction identifier (DB_TRX_ID, DATA_TRX_ID)
Definition: trx0types.h:148
int type
Definition: http_common.h:411
byte * trx_undo_update_rec_get_update(const byte *ptr, const dict_index_t *index, ulint type, trx_id_t trx_id, roll_ptr_t roll_ptr, ulint info_bits, trx_t *trx, mem_heap_t *heap, upd_t **upd, lob::undo_vers_t *lob_undo, type_cmpl_t &type_cmpl)
Builds an update vector based on a remaining part of an undo log record.
Definition: trx0rec.cc:1722
byte * trx_undo_update_rec_get_sys_cols(const byte *ptr, trx_id_t *trx_id, roll_ptr_t *roll_ptr, ulint *info_bits)
Reads from an undo log update record the system field values of the old version.
Definition: trx0rec.cc:1699
UNIV_INLINE trx_undo_rec_t * trx_undo_rec_copy(const trx_undo_rec_t *undo_rec, mem_heap_t *heap)
Copies the undo record to the heap.
Modification log for online index creation and online table rebuild.
const byte * trx_undo_read_v_idx(const dict_table_t *table, const byte *ptr, bool first_v_col, bool *is_undo_log, ulint *field_no)
Read virtual column index from undo log if the undo log contains such info, and verify the column is ...
Definition: trx0rec.cc:353
const byte * trx_undo_rec_get_multi_value(const byte *ptr, dfield_t *field, mem_heap_t *heap)
Read from an undo log record a non-virtual column value.
Definition: trx0rec.cc:622
byte page_t
Type of the index page.
Definition: page0types.h:148
Mini-transaction buffer.
The type and compilation info flag in the undo record for update.
Definition: trx0rec.h:339
The list of modifications to be applied on LOBs to get older versions.
Definition: lob0undo.h:144
static int flags[50]
Definition: hp_test1.cc:39
unsigned char byte
Blob class.
Definition: common.h:159
#define TRX_UNDO_MODIFY_BLOB
Definition: trx0rec.h:318
Mini-transaction handle and buffer.
Definition: mtr0mtr.h:169
UNIV_INLINE ibool trx_undo_rec_get_extern_storage(const trx_undo_rec_t *undo_rec)
Returns TRUE if an undo log record contains an extern storage field.
bool trx_undo_rec_is_multi_value(const byte *undo_rec)
Decide if the following undo log record is a multi-value virtual column.
Definition: trx0rec.cc:233
table_id_t trx_undo_rec_get_table_id(const trx_undo_rec_t *undo_rec)
Reads from an undo log record the table ID.
Definition: trx0rec.cc:592
ib_id_t undo_no_t
Undo number.
Definition: trx0types.h:152
uint8_t m_flag
Definition: trx0rec.h:373
Definition: row0upd.h:554
Data structure for an index.
Definition: dict0mem.h:875