1 /*****************************************************************************
2 
3 Copyright (c) 2016, 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 #ifndef lob0zip_h
27 #define lob0zip_h
28 
29 #include "lob0ins.h"
30 
31 namespace lob {
32 
33 /** Insert or write the compressed BLOB as a single zlib stream. */
34 class zInserter : private BaseInserter {
35  public:
36   /** Constructor.
37   @param[in]	ctx	blob operation context. */
zInserter(InsertContext * ctx)38   zInserter(InsertContext *ctx) : BaseInserter(ctx), m_heap(nullptr) {}
39 
40   /** Destructor. */
41   ~zInserter();
42 
43   /** Prepare to write a compressed BLOB. Setup the zlib
44   compression stream.
45   @return DB_SUCCESS on success, error code on failure. */
46   dberr_t prepare();
47 
48   /** Write all the BLOBs of the clustered index record.
49   @return DB_SUCCESS on success, error code on failure. */
50   dberr_t write();
51 
52   /** Write one blob field data.
53   @param[in]	blob_j	the blob field number
54   @return DB_SUCCESS on success, error code on failure. */
55   dberr_t write_one_blob(size_t blob_j);
56 
57   /** Cleanup after completing the write of compressed BLOB.
58   @param[in]	validate	if true, validate all the
59                                   lob references. if false,
60                                   skip this validation.
61   @return DB_SUCCESS on success, error code on failure. */
62   dberr_t finish(bool validate = true) {
63     int ret = deflateEnd(&m_stream);
64     ut_ad(ret == Z_OK);
65     ut_ad(!validate || validate_blobrefs());
66 
67     if (ret != Z_OK) {
68       m_err = DB_FAIL;
69     }
70     return (m_err);
71   }
72 
73   /** Write the page type of the BLOB page and also generate the
74   redo log record.
75   @param[in]	blob_page	the BLOB page
76   @param[in]	nth_blob_page	the count of BLOB page from
77                                   the beginning of the BLOB. */
log_page_type(page_t * blob_page,ulint nth_blob_page)78   void log_page_type(page_t *blob_page, ulint nth_blob_page) {
79     page_type_t page_type;
80 
81     if (is_index_sdi()) {
82       page_type = FIL_PAGE_SDI_ZBLOB;
83     } else if (nth_blob_page == 0) {
84       page_type = FIL_PAGE_TYPE_ZBLOB;
85     } else {
86       page_type = FIL_PAGE_TYPE_ZBLOB2;
87     }
88 
89     mlog_write_ulint(blob_page + FIL_PAGE_TYPE, page_type, MLOG_2BYTES,
90                      &m_blob_mtr);
91   }
92 
93   /** Calculate the total number of pages needed to store
94   the given blobs */
calc_total_pages()95   ulint calc_total_pages() {
96     const page_size_t page_size = m_ctx->page_size();
97 
98     /* Space available in compressed page to carry blob data */
99     const ulint payload_size_zip = page_size.physical() - FIL_PAGE_DATA;
100 
101     const big_rec_t *vec = m_ctx->get_big_rec_vec();
102 
103     ulint total_blob_pages = 0;
104     for (ulint i = 0; i < vec->n_fields; i++) {
105       total_blob_pages +=
106           static_cast<ulint>(
107               deflateBound(&m_stream, static_cast<uLong>(vec->fields[i].len)) +
108               payload_size_zip - 1) /
109           payload_size_zip;
110     }
111 
112     return (total_blob_pages);
113   }
114 
115   /** Write contents into a single BLOB page.
116   @return code as returned by zlib. */
117   int write_into_single_page();
118 
119   /** Commit the BLOB mtr. */
commit_blob_mtr()120   void commit_blob_mtr() { mtr_commit(&m_blob_mtr); }
121 
122   /** Write one blob page.  This function will be repeatedly called
123   with an increasing nth_blob_page to completely write a BLOB.
124   @param[in]	blob_j		the jth blob object of the record.
125   @param[in]	field		the big record field.
126   @param[in]	nth_blob_page	count of the BLOB page (starting from 1).
127   @return code as returned by the zlib. */
128   int write_single_blob_page(size_t blob_j, big_rec_field_t &field,
129                              ulint nth_blob_page);
130 
131   /** Write first blob page.
132   @param[in]	blob_j		the jth blob object of the record.
133   @param[in]	field		the big record field.
134   @return code as returned by the zlib. */
135   int write_first_page(size_t blob_j, big_rec_field_t &field);
136 
137   /** Verify that all pointers to externally stored columns in the record
138   is be valid.  If validation fails, this function doesn't return.
139   @return true if valid. */
validate_blobrefs()140   bool validate_blobrefs() const {
141     const ulint *offsets = m_ctx->get_offsets();
142 
143     for (ulint i = 0; i < rec_offs_n_fields(offsets); i++) {
144       if (!rec_offs_nth_extern(offsets, i)) {
145         continue;
146       }
147 
148       byte *field_ref = btr_rec_get_field_ref(m_ctx->rec(), offsets, i);
149 
150       ref_t blobref(field_ref);
151 
152       /* The pointer must not be zero if the operation
153       succeeded. */
154       ut_a(!blobref.is_null() || m_err != DB_SUCCESS);
155 
156       /* The column must not be disowned by this record. */
157       ut_a(blobref.is_owner());
158     }
159     return (true);
160   }
161 
162   /** For the given blob field, update its length in the blob reference
163   which is available in the clustered index record.
164   @param[in]	field	the concerned blob field. */
165   void update_length_in_blobref(big_rec_field_t &field);
166 
167   /** Make the current page as next page of previous page.  In other
168   words, make the page m_cur_blob_page_no as the next page
169   (FIL_PAGE_NEXT) of page m_prev_page_no.
170   @return DB_SUCCESS on success, or error code on failure. */
171   dberr_t set_page_next();
172 
173   /** Write one small blob field data. Refer to ref_t to determine
174   the definition of small blob.
175   @param[in]	blob_j	the blob field number
176   @return DB_SUCCESS on success, error code on failure. */
177   dberr_t write_one_small_blob(size_t blob_j);
178 
179  private:
180   /** Add the BLOB page information to the directory
181   @param[in]	page_info	BLOB page information. */
add_to_blob_dir(const blob_page_info_t & page_info)182   void add_to_blob_dir(const blob_page_info_t &page_info) {
183     m_dir.add(page_info);
184   }
185 
186   mem_heap_t *m_heap;
187   z_stream m_stream;
188 
189   /** The BLOB directory information. */
190   blob_dir_t m_dir;
191 };
192 
~zInserter()193 inline zInserter::~zInserter() {
194   if (m_heap != nullptr) {
195     mem_heap_free(m_heap);
196   }
197 }
198 
199 }  // namespace lob
200 
201 #endif  // lob0zip_h
202