summaryrefslogtreecommitdiff
path: root/innobase/rem/rem0rec.c
diff options
context:
space:
mode:
authorunknown <monty@donna.mysql.com>2001-02-17 14:19:19 +0200
committerunknown <monty@donna.mysql.com>2001-02-17 14:19:19 +0200
commit132e667b0bbbe33137b6baeb59f3f22b7524f066 (patch)
treebfe39951a73e906579ab819bf5198ad8f3a64a36 /innobase/rem/rem0rec.c
parentb084cf1951eb271f662a9326c950f4cf0570258d (diff)
downloadmariadb-git-132e667b0bbbe33137b6baeb59f3f22b7524f066.tar.gz
Added Innobase to source distribution
Docs/manual.texi: Added Innobase documentation configure.in: Incremented version include/my_base.h: Added option for Innobase myisam/mi_check.c: cleanup mysql-test/t/bdb.test: cleanup mysql-test/t/innobase.test: Extended with new tests from bdb.test mysql-test/t/merge.test: Added test of SHOW create mysys/my_init.c: Fix for UNIXWARE 7 scripts/mysql_install_db.sh: Always write how to start mysqld scripts/safe_mysqld.sh: Fixed type sql/ha_innobase.cc: Update to new version sql/ha_innobase.h: Update to new version sql/handler.h: Added 'update_table_comment()' and 'append_create_info()' sql/sql_delete.cc: Fixes for Innobase sql/sql_select.cc: Fixes for Innobase sql/sql_show.cc: Append create information (for MERGE tables) sql/sql_update.cc: Fixes for Innobase
Diffstat (limited to 'innobase/rem/rem0rec.c')
-rw-r--r--innobase/rem/rem0rec.c541
1 files changed, 541 insertions, 0 deletions
diff --git a/innobase/rem/rem0rec.c b/innobase/rem/rem0rec.c
new file mode 100644
index 00000000000..9ddfe7a4b9a
--- /dev/null
+++ b/innobase/rem/rem0rec.c
@@ -0,0 +1,541 @@
+/************************************************************************
+Record manager
+
+(c) 1994-1996 Innobase Oy
+
+Created 5/30/1994 Heikki Tuuri
+*************************************************************************/
+
+#include "rem0rec.h"
+
+#ifdef UNIV_NONINL
+#include "rem0rec.ic"
+#endif
+
+/* PHYSICAL RECORD
+ ===============
+
+The physical record, which is the data type of all the records
+found in index pages of the database, has the following format
+(lower addresses and more significant bits inside a byte are below
+represented on a higher text line):
+
+| offset of the end of the last field of data, the most significant
+ bit is set to 1 if and only if the field is SQL-null |
+...
+| offset of the end of the first field of data + the SQL-null bit |
+| 4 bits used to delete mark a record, and mark a predefined
+ minimum record in alphabetical order |
+| 4 bits giving the number of records owned by this record
+ (this term is explained in page0page.h) |
+| 13 bits giving the order number of this record in the
+ heap of the index page |
+| 10 bits giving the number of fields in this record |
+| 1 bit which is set to 1 if the offsets above are given in
+ one byte format, 0 if in two byte format |
+| two bytes giving the pointer to the next record in the page |
+ORIGIN of the record
+| first field of data |
+...
+| last field of data |
+
+The origin of the record is the start address of the first field
+of data. The offsets are given relative to the origin.
+The offsets of the data fields are stored in an inverted
+order because then the offset of the first fields are near the
+origin, giving maybe a better processor cache hit rate in searches.
+
+The offsets of the data fields are given as one-byte
+(if there are less than 127 bytes of data in the record)
+or two-byte unsigned integers. The most significant bit
+is not part of the offset, instead it indicates the SQL-null
+if the bit is set to 1.
+
+CANONICAL COORDINATES. A record can be seen as a single
+string of 'characters' in the following way: catenate the bytes
+in each field, in the order of fields. An SQL-null field
+is taken to be an empty sequence of bytes. Then after
+the position of each field insert in the string
+the 'character' <FIELD-END>, except that after an SQL-null field
+insert <NULL-FIELD-END>. Now the ordinal position of each
+byte in this canonical string is its canonical coordinate.
+So, for the record ("AA", SQL-NULL, "BB", ""), the canonical
+string is "AA<FIELD_END><NULL-FIELD-END>BB<FIELD-END><FIELD-END>".
+We identify prefixes (= initial segments) of a record
+with prefixes of the canonical string. The canonical
+length of the prefix is the length of the corresponding
+prefix of the canonical string. The canonical length of
+a record is the length of its canonical string.
+
+For example, the maximal common prefix of records
+("AA", SQL-NULL, "BB", "C") and ("AA", SQL-NULL, "B", "C")
+is "AA<FIELD-END><NULL-FIELD-END>B", and its canonical
+length is 5.
+
+A complete-field prefix of a record is a prefix which ends at the
+end of some field (containing also <FIELD-END>).
+A record is a complete-field prefix of another record, if
+the corresponding canonical strings have the same property. */
+
+ulint rec_dummy; /* this is used to fool compiler in
+ rec_validate */
+
+/****************************************************************
+The following function is used to get a pointer to the nth data field in a
+record. */
+
+byte*
+rec_get_nth_field(
+/*==============*/
+ /* out: pointer to the field */
+ rec_t* rec, /* in: record */
+ ulint n, /* in: index of the field */
+ ulint* len) /* out: length of the field; UNIV_SQL_NULL if SQL
+ null */
+{
+ ulint os;
+ ulint next_os;
+
+ ut_ad(rec && len);
+ ut_ad(n < rec_get_n_fields(rec));
+
+ if (rec_get_1byte_offs_flag(rec)) {
+ os = rec_1_get_field_start_offs(rec, n);
+
+ next_os = rec_1_get_field_end_info(rec, n);
+
+ if (next_os & REC_1BYTE_SQL_NULL_MASK) {
+ *len = UNIV_SQL_NULL;
+
+ return(rec + os);
+ }
+
+ next_os = next_os & ~REC_1BYTE_SQL_NULL_MASK;
+ } else {
+ os = rec_2_get_field_start_offs(rec, n);
+
+ next_os = rec_2_get_field_end_info(rec, n);
+
+ if (next_os & REC_2BYTE_SQL_NULL_MASK) {
+ *len = UNIV_SQL_NULL;
+
+ return(rec + os);
+ }
+
+ next_os = next_os & ~REC_2BYTE_SQL_NULL_MASK;
+ }
+
+ *len = next_os - os;
+
+ ut_ad(*len < UNIV_PAGE_SIZE);
+
+ return(rec + os);
+}
+
+/***************************************************************
+Sets the value of the ith field SQL null bit. */
+
+void
+rec_set_nth_field_null_bit(
+/*=======================*/
+ rec_t* rec, /* in: record */
+ ulint i, /* in: ith field */
+ ibool val) /* in: value to set */
+{
+ ulint info;
+
+ if (rec_get_1byte_offs_flag(rec)) {
+
+ info = rec_1_get_field_end_info(rec, i);
+
+ if (val) {
+ info = info | REC_1BYTE_SQL_NULL_MASK;
+ } else {
+ info = info & ~REC_1BYTE_SQL_NULL_MASK;
+ }
+
+ rec_1_set_field_end_info(rec, i, info);
+
+ return;
+ }
+
+ info = rec_2_get_field_end_info(rec, i);
+
+ if (val) {
+ info = info | REC_2BYTE_SQL_NULL_MASK;
+ } else {
+ info = info & ~REC_2BYTE_SQL_NULL_MASK;
+ }
+
+ rec_2_set_field_end_info(rec, i, info);
+}
+
+/***************************************************************
+Sets a record field to SQL null. The physical size of the field is not
+changed. */
+
+void
+rec_set_nth_field_sql_null(
+/*=======================*/
+ rec_t* rec, /* in: record */
+ ulint n) /* in: index of the field */
+{
+ ulint offset;
+
+ offset = rec_get_field_start_offs(rec, n);
+
+ data_write_sql_null(rec + offset, rec_get_nth_field_size(rec, n));
+
+ rec_set_nth_field_null_bit(rec, n, TRUE);
+}
+
+/*************************************************************
+Builds a physical record out of a data tuple and stores it beginning from
+address destination. */
+
+rec_t*
+rec_convert_dtuple_to_rec_low(
+/*==========================*/
+ /* out: pointer to the origin of physical
+ record */
+ byte* destination, /* in: start address of the physical record */
+ dtuple_t* dtuple, /* in: data tuple */
+ ulint data_size) /* in: data size of dtuple */
+{
+ dfield_t* field;
+ ulint n_fields;
+ rec_t* rec;
+ ulint end_offset;
+ ulint ored_offset;
+ byte* data;
+ ulint len;
+ ulint i;
+
+ ut_ad(destination && dtuple);
+ ut_ad(dtuple_validate(dtuple));
+ ut_ad(dtuple_check_typed(dtuple));
+ ut_ad(dtuple_get_data_size(dtuple) == data_size);
+
+ n_fields = dtuple_get_n_fields(dtuple);
+
+ ut_ad(n_fields > 0);
+
+ /* Calculate the offset of the origin in the physical record */
+
+ rec = destination + rec_get_converted_extra_size(data_size, n_fields);
+
+ /* Store the number of fields */
+ rec_set_n_fields(rec, n_fields);
+
+ /* Set the info bits of the record */
+ rec_set_info_bits(rec, dtuple_get_info_bits(dtuple));
+
+ /* Store the data and the offsets */
+
+ end_offset = 0;
+
+ if (data_size <= REC_1BYTE_OFFS_LIMIT) {
+
+ rec_set_1byte_offs_flag(rec, TRUE);
+
+ for (i = 0; i < n_fields; i++) {
+
+ field = dtuple_get_nth_field(dtuple, i);
+
+ data = dfield_get_data(field);
+ len = dfield_get_len(field);
+
+ if (len == UNIV_SQL_NULL) {
+ len = dtype_get_sql_null_size(dfield_get_type(field));
+ data_write_sql_null(rec + end_offset, len);
+
+ end_offset += len;
+ ored_offset = end_offset | REC_1BYTE_SQL_NULL_MASK;
+ } else {
+ /* If the data is not SQL null, store it */
+ ut_memcpy(rec + end_offset, data, len);
+
+ end_offset += len;
+ ored_offset = end_offset;
+ }
+
+ rec_1_set_field_end_info(rec, i, ored_offset);
+ }
+ } else {
+ rec_set_1byte_offs_flag(rec, FALSE);
+
+ for (i = 0; i < n_fields; i++) {
+
+ field = dtuple_get_nth_field(dtuple, i);
+
+ data = dfield_get_data(field);
+ len = dfield_get_len(field);
+
+ if (len == UNIV_SQL_NULL) {
+ len = dtype_get_sql_null_size(dfield_get_type(field));
+ data_write_sql_null(rec + end_offset, len);
+
+ end_offset += len;
+ ored_offset = end_offset | REC_2BYTE_SQL_NULL_MASK;
+ } else {
+ /* If the data is not SQL null, store it */
+ ut_memcpy(rec + end_offset, data, len);
+
+ end_offset += len;
+ ored_offset = end_offset;
+ }
+
+ rec_2_set_field_end_info(rec, i, ored_offset);
+ }
+ }
+
+ ut_ad(rec_validate(rec));
+
+ return(rec);
+}
+
+/******************************************************************
+Copies the first n fields of a physical record to a data tuple. The fields
+are copied to the memory heap. */
+
+void
+rec_copy_prefix_to_dtuple(
+/*======================*/
+ dtuple_t* tuple, /* in: data tuple */
+ rec_t* rec, /* in: physical record */
+ ulint n_fields, /* in: number of fields to copy */
+ mem_heap_t* heap) /* in: memory heap */
+{
+ dfield_t* field;
+ byte* data;
+ ulint len;
+ byte* buf = NULL;
+ ulint i;
+
+ ut_ad(rec_validate(rec));
+ ut_ad(dtuple_check_typed(tuple));
+
+ dtuple_set_info_bits(tuple, rec_get_info_bits(rec));
+
+ for (i = 0; i < n_fields; i++) {
+
+ field = dtuple_get_nth_field(tuple, i);
+ data = rec_get_nth_field(rec, i, &len);
+
+ if (len != UNIV_SQL_NULL) {
+ buf = mem_heap_alloc(heap, len);
+
+ ut_memcpy(buf, data, len);
+ }
+
+ dfield_set_data(field, buf, len);
+ }
+}
+
+/******************************************************************
+Copies the first n fields of a physical record to a new physical record in
+a buffer. */
+
+rec_t*
+rec_copy_prefix_to_buf(
+/*===================*/
+ /* out, own: copied record */
+ rec_t* rec, /* in: physical record */
+ ulint n_fields, /* in: number of fields to copy */
+ byte** buf, /* in/out: memory buffer for the copied prefix,
+ or NULL */
+ ulint* buf_size) /* in/out: buffer size */
+{
+ rec_t* copy_rec;
+ ulint area_start;
+ ulint area_end;
+ ulint prefix_len;
+
+ ut_ad(rec_validate(rec));
+
+ area_end = rec_get_field_start_offs(rec, n_fields);
+
+ if (rec_get_1byte_offs_flag(rec)) {
+ area_start = REC_N_EXTRA_BYTES + n_fields;
+ } else {
+ area_start = REC_N_EXTRA_BYTES + 2 * n_fields;
+ }
+
+ prefix_len = area_start + area_end;
+
+ if ((*buf == NULL) || (*buf_size < prefix_len)) {
+ if (*buf != NULL) {
+ mem_free(*buf);
+ }
+
+ *buf = mem_alloc(prefix_len);
+ *buf_size = prefix_len;
+ }
+
+ ut_memcpy(*buf, rec - area_start, prefix_len);
+
+ copy_rec = *buf + area_start;
+
+ rec_set_n_fields(copy_rec, n_fields);
+
+ return(copy_rec);
+}
+
+/*******************************************************************
+Validates the consistency of a physical record. */
+
+ibool
+rec_validate(
+/*=========*/
+ /* out: TRUE if ok */
+ rec_t* rec) /* in: physical record */
+{
+ ulint i;
+ byte* data;
+ ulint len;
+ ulint n_fields;
+ ulint len_sum = 0;
+ ulint sum = 0;
+
+ ut_a(rec);
+ n_fields = rec_get_n_fields(rec);
+
+ if ((n_fields == 0) || (n_fields > REC_MAX_N_FIELDS)) {
+ ut_a(0);
+ }
+
+ for (i = 0; i < n_fields; i++) {
+ data = rec_get_nth_field(rec, i, &len);
+
+ ut_a((len < UNIV_PAGE_SIZE) || (len == UNIV_SQL_NULL));
+
+ if (len != UNIV_SQL_NULL) {
+ len_sum += len;
+ sum += *(data + len -1); /* dereference the
+ end of the field to
+ cause a memory trap
+ if possible */
+ } else {
+ len_sum += rec_get_nth_field_size(rec, i);
+ }
+ }
+
+ ut_a(len_sum == (ulint)(rec_get_end(rec) - rec));
+
+ rec_dummy = sum; /* This is here only to fool the compiler */
+
+ return(TRUE);
+}
+
+/*******************************************************************
+Prints a physical record. */
+
+void
+rec_print(
+/*======*/
+ rec_t* rec) /* in: physical record */
+{
+ byte* data;
+ ulint len;
+ char* offs;
+ ulint n;
+ ulint i;
+
+ ut_ad(rec);
+
+ if (rec_get_1byte_offs_flag(rec)) {
+ offs = "TRUE";
+ } else {
+ offs = "FALSE";
+ }
+
+ n = rec_get_n_fields(rec);
+
+ printf(
+ "PHYSICAL RECORD: n_fields %lu; 1-byte offs %s; info bits %lu\n",
+ n, offs, rec_get_info_bits(rec));
+
+ for (i = 0; i < n; i++) {
+
+ data = rec_get_nth_field(rec, i, &len);
+
+ printf(" %lu:", i);
+
+ if (len != UNIV_SQL_NULL) {
+ if (len <= 30) {
+
+ ut_print_buf(data, len);
+ } else {
+ ut_print_buf(data, 30);
+
+ printf("...(truncated)");
+ }
+ } else {
+ printf(" SQL NULL, size %lu ",
+ rec_get_nth_field_size(rec, i));
+
+ }
+ printf(";");
+ }
+
+ printf("\n");
+
+ rec_validate(rec);
+}
+
+/*******************************************************************
+Prints a physical record to a buffer. */
+
+ulint
+rec_sprintf(
+/*========*/
+ /* out: printed length in bytes */
+ char* buf, /* in: buffer to print to */
+ ulint buf_len,/* in: buffer length */
+ rec_t* rec) /* in: physical record */
+{
+ byte* data;
+ ulint len;
+ ulint k;
+ ulint n;
+ ulint i;
+
+ ut_ad(rec);
+
+ n = rec_get_n_fields(rec);
+ k = 0;
+
+ if (k + 30 > buf_len) {
+
+ return(k);
+ }
+
+ k += sprintf(buf + k, "RECORD: info bits %lu", rec_get_info_bits(rec));
+
+ for (i = 0; i < n; i++) {
+
+ if (k + 30 > buf_len) {
+
+ return(k);
+ }
+
+ data = rec_get_nth_field(rec, i, &len);
+
+ k += sprintf(buf + k, " %lu:", i);
+
+ if (len != UNIV_SQL_NULL) {
+ if (k + 30 + 5 * len > buf_len) {
+
+ return(k);
+ }
+
+ k += ut_sprintf_buf(buf + k, data, len);
+ } else {
+ k += sprintf(buf + k, " SQL NULL");
+ }
+
+ k += sprintf(buf + k, ";");
+ }
+
+ return(k);
+}