Drizzled Public API Documentation

pbms_enabled.cc
00001 /* Copyright (C) 2010 PrimeBase Technologies GmbH
00002  * All rights reserved.
00003  * 
00004  * Redistribution and use in source and binary forms, with or without 
00005  * modification, are permitted provided that the following conditions are met:
00006  * 
00007  *     * Redistributions of source code must retain the above copyright notice, 
00008  *    this list of conditions and the following disclaimer.
00009  *     * Redistributions in binary form must reproduce the above copyright notice, 
00010  *    this list of conditions and the following disclaimer in the documentation 
00011  *    and/or other materials provided with the distribution.
00012  *     * Neither the name of the "PrimeBase Technologies GmbH" nor the names of its 
00013  *    contributors may be used to endorse or promote products derived from this 
00014  *    software without specific prior written permission.
00015  * 
00016  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
00017  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
00018  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
00019  * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
00020  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
00021  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
00022  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
00023  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
00024  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
00025  * POSSIBILITY OF SUCH DAMAGE. 
00026  *  
00027  *
00028  * PrimeBase Media Stream for MySQL and Drizzle
00029  *
00030  *
00031  * Barry Leslie
00032  *
00033  * 2009-07-16
00034  *
00035  * H&G2JCtL
00036  *
00037  * PBMS interface used to enable engines for use with the PBMS daemon.
00038  *
00039  * For an example on how to build this into an engine have a look at the PBXT engine
00040  * in file ha_pbxt.cc. Search for 'PBMS_ENABLED'.
00041  *
00042  */
00043 
00044 #ifndef DRIZZLED
00045 #if defined(MSDOS) || defined(__WIN__)
00046 #include "pbms_enabled.h"
00047 
00048 // Windows is not supported yet so just stub out the functions..
00049 bool pbms_initialize(const char *engine_name __attribute__((unused)), 
00050           bool isServer __attribute__((unused)), 
00051           bool isTransactional __attribute__((unused)), 
00052           PBMSResultPtr result __attribute__((unused)), 
00053           IsPBMSFilterFunc is_pbms_blob __attribute__((unused))
00054           ) { return true;}
00055 void pbms_finalize() {}
00056 int pbms_write_row_blobs(const TABLE *table __attribute__((unused)), 
00057             unsigned char *buf __attribute__((unused)), 
00058             PBMSResultPtr result __attribute__((unused))
00059             ){ return 0;}
00060 int pbms_update_row_blobs(const TABLE *table __attribute__((unused)), 
00061             const unsigned char *old_row __attribute__((unused)), 
00062             unsigned char *new_row __attribute__((unused)), 
00063             PBMSResultPtr result __attribute__((unused))
00064             ){ return 0;}
00065 int pbms_delete_row_blobs(const TABLE *table __attribute__((unused)), 
00066             const unsigned char *buf __attribute__((unused)), 
00067             PBMSResultPtr result __attribute__((unused))
00068             ){ return 0;}
00069 int pbms_rename_table_with_blobs(const char *old_table_path __attribute__((unused)), 
00070                 const char *new_table_path __attribute__((unused)), 
00071                 PBMSResultPtr result __attribute__((unused))
00072                 ){ return 0;}
00073 int pbms_delete_table_with_blobs(const char *table_path __attribute__((unused)), 
00074                 PBMSResultPtr result __attribute__((unused))
00075                 ){ return 0;}
00076 void pbms_completed(TABLE *table __attribute__((unused)), 
00077           bool ok __attribute__((unused))
00078           ){}
00079 #else
00080 #define PBMS_API  pbms_enabled_api
00081 
00082 #include "pbms_enabled.h"
00083 #include "mysql_priv.h"
00084 #include <mysql/plugin.h>
00085 #define session_alloc(sess, size) thd_alloc(sess, size);
00086 #define current_session current_thd
00087 
00088 #define GET_BLOB_FIELD(t, i) (Field_blob *)(t->field[t->s->blob_field[i]])
00089 #define DB_NAME(f) (f->table->s->db.str)
00090 #define TAB_NAME(f) (*(f->table_name))
00091 
00092 static PBMS_API pbms_api;
00093 
00094 /* 
00095  * A callback function to check if the column is a PBMS BLOB. 
00096  * Can be NULL if no check is to be done. 
00097  */ 
00098 static IsPBMSFilterFunc is_pbms_blob = NULL; 
00099 
00100 //====================
00101 bool pbms_initialize(const char *engine_name, bool isServer, bool isTransactional, PBMSResultPtr result, IsPBMSFilterFunc is_pbms_blob_arg)
00102 {
00103   int err;
00104   PBMSEngineRec enabled_engine = {
00105     MS_ENGINE_VERSION,
00106     0,
00107     0,
00108     0,
00109     {0},  
00110     0
00111   };
00112 
00113   strncpy(enabled_engine.ms_engine_name, engine_name, 32);
00114   enabled_engine.ms_internal = isServer;
00115   enabled_engine.ms_has_transactions = isTransactional;
00116   enabled_engine.ms_engine_name[31] = 0;
00117 
00118   err = pbms_api.registerEngine(&enabled_engine, result);
00119   is_pbms_blob = is_pbms_blob_arg;
00120 
00121   return (err == 0);
00122 }
00123 
00124 
00125 //====================
00126 void pbms_finalize(const char *engine_name)
00127 {
00128   pbms_api.deregisterEngine(engine_name);
00129 }
00130 
00131 //==================================
00132 static int insertRecord(Field_blob *field, char *blob,  size_t org_length, unsigned char *blob_rec, size_t packlength, PBMSResultPtr result)
00133 {
00134   int err;
00135   size_t length;
00136   PBMSBlobURLRec blob_url;
00137   
00138   err = pbms_api.retainBlob(DB_NAME(field), TAB_NAME(field), &blob_url, blob, org_length, field->position(), result);
00139   if (err)
00140     return err;
00141     
00142   // If the BLOB length changed reset it. 
00143   // This will happen if the BLOB data was replaced with a BLOB reference. 
00144   length = strlen(blob_url.bu_data)  +1;
00145   if ((length != org_length) || memcmp(blob_url.bu_data, blob, length)) {
00146     if (length != org_length) {
00147       field->store_length(blob_rec, packlength, length);
00148     }
00149     
00150     if (length > org_length) {
00151       // This can only happen if the BLOB URL is actually larger than the BLOB itself.
00152       blob = (char *) session_alloc(current_session, length);
00153       memcpy(blob_rec+packlength, &blob, sizeof(char*));
00154     }     
00155     memcpy(blob, blob_url.bu_data, length);
00156   } 
00157     
00158   return 0;
00159 }
00160 
00161 //====================
00162 int pbms_update_row_blobs(const TABLE *table, const unsigned char *old_row, unsigned char *new_row, PBMSResultPtr result)
00163 {
00164   Field_blob *field;
00165   uint32_t field_offset;
00166   const unsigned char *old_blob_rec;
00167   unsigned char *new_blob_rec;
00168   char *old_blob_url, *new_blob_url;
00169   size_t packlength, i, old_length, new_length;
00170   int err;
00171   bool old_null_blob, new_null_blob;
00172 
00173   result->mr_had_blobs = false;
00174   
00175   if (!pbms_api.isPBMSLoaded())
00176     return 0;
00177     
00178   if (table->s->blob_fields == 0)
00179     return 0;
00180     
00181   for (i= 0; i < table->s->blob_fields; i++) {
00182     field = GET_BLOB_FIELD(table, i);
00183 
00184     old_null_blob = field->is_null_in_record(old_row);
00185     new_null_blob = field->is_null_in_record(new_row);
00186     if (old_null_blob && new_null_blob)
00187       continue;
00188 
00189     {
00190       String type_name;
00191       // Note: field->type() always returns MYSQL_TYPE_BLOB regardless of the type of BLOB
00192       field->sql_type(type_name);
00193       if (strcasecmp(type_name.c_ptr(), "LongBlob"))
00194         continue;
00195     }
00196       
00197     if( is_pbms_blob && !is_pbms_blob(field) )
00198       continue;
00199       
00200       
00201     // Get the blob record:
00202     field_offset = field->offset(field->table->record[0]);
00203     packlength = field->pack_length() - field->table->s->blob_ptr_size;
00204 
00205     if (new_null_blob) {
00206       new_blob_url = NULL;
00207     } else {
00208       new_blob_rec = new_row + field_offset;
00209       new_length = field->get_length(new_blob_rec);
00210       memcpy(&new_blob_url, new_blob_rec +packlength, sizeof(char*));
00211     }
00212     
00213     if (old_null_blob) {
00214       old_blob_url = NULL;
00215     } else {
00216       old_blob_rec = old_row + field_offset;
00217       old_length = field->get_length(old_blob_rec);
00218       memcpy(&old_blob_url, old_blob_rec +packlength, sizeof(char*));
00219     }
00220     
00221     // Check to see if the BLOBs are the same.
00222     // I am assuming that if the BLOB pointer is different then teh BLOB has changed.
00223     // Zero length BLOBs are a special case because they may have a NULL data pointer,
00224     // to catch this and distiguish it from a NULL BLOB I do a check to see if one field was NULL:
00225     // (old_null_blob != new_null_blob)
00226     if ((old_blob_url != new_blob_url) || (old_null_blob != new_null_blob)) {
00227       
00228       result->mr_had_blobs = true;
00229 
00230       // The BLOB was updated so delete the old one and insert the new one.
00231       if ((old_null_blob == false) && (err = pbms_api.releaseBlob(DB_NAME(field), TAB_NAME(field), old_blob_url, old_length, result)))
00232         return err;
00233         
00234       if ((new_null_blob == false) && (err = insertRecord(field, new_blob_url, new_length, new_blob_rec, packlength, result)))
00235         return err;
00236     } 
00237   }
00238   
00239   return 0;
00240 }
00241 
00242 //====================
00243 int pbms_write_row_blobs(const TABLE *table, unsigned char *row_buffer, PBMSResultPtr result)
00244 {
00245 
00246   Field_blob *field;
00247   unsigned char *blob_rec;
00248   char *blob_url;
00249   size_t packlength, i, length;
00250   int err;
00251 
00252   result->mr_had_blobs = false;
00253 
00254   if (!pbms_api.isPBMSLoaded())
00255     return 0;
00256     
00257   if (table->s->blob_fields == 0)
00258     return 0;
00259     
00260   for (i= 0; i <  table->s->blob_fields; i++) {
00261     field =  GET_BLOB_FIELD(table, i);
00262     
00263     if (field->is_null_in_record(row_buffer))
00264       continue;
00265       
00266     {
00267       String type_name;
00268       // Note: field->type() always returns MYSQL_TYPE_BLOB regardless of the type of BLOB
00269       field->sql_type(type_name);
00270       if (strcasecmp(type_name.c_ptr(), "LongBlob"))
00271         continue;
00272     }
00273       
00274     if( is_pbms_blob && !is_pbms_blob(field) )
00275       continue;
00276 
00277     result->mr_had_blobs = true;
00278 
00279     // Get the blob record:
00280     packlength = field->pack_length() - field->table->s->blob_ptr_size;
00281     blob_rec = row_buffer + field->offset(field->table->record[0]);
00282     
00283     length = field->get_length(blob_rec);
00284     memcpy(&blob_url, blob_rec +packlength, sizeof(char*));
00285 
00286     if ((err = insertRecord(field, blob_url, length, blob_rec, packlength, result)))
00287       return err;
00288   }
00289 
00290   return 0;
00291 }
00292 
00293 //====================
00294 int pbms_delete_row_blobs(const TABLE *table, const unsigned char *row_buffer, PBMSResultPtr result)
00295 {
00296   Field_blob *field;
00297   const unsigned char *blob_rec;
00298   char *blob;
00299   size_t packlength, i, length;
00300   bool call_failed = false;
00301   int err;
00302   
00303   result->mr_had_blobs = false;
00304 
00305   if (!pbms_api.isPBMSLoaded())
00306     return 0;
00307     
00308   if (table->s->blob_fields == 0)
00309     return 0;
00310     
00311   for (i= 0; i < table->s->blob_fields; i++) {
00312     field = GET_BLOB_FIELD(table, i);
00313     
00314     if (field->is_null_in_record(row_buffer))
00315       continue;
00316       
00317     {
00318       String type_name;
00319       // Note: field->type() always returns MYSQL_TYPE_BLOB regardless of the type of BLOB
00320       field->sql_type(type_name);
00321       if (strcasecmp(type_name.c_ptr(), "LongBlob"))
00322         continue;
00323     }
00324       
00325     if(is_pbms_blob && !is_pbms_blob(field) )
00326       continue;
00327 
00328     result->mr_had_blobs = true;  
00329     
00330     // Get the blob record:
00331     packlength = field->pack_length() - field->table->s->blob_ptr_size;
00332 
00333     blob_rec = row_buffer + field->offset(field->table->record[0]);
00334     length = field->get_length(blob_rec);
00335     memcpy(&blob, blob_rec +packlength, sizeof(char*));
00336 
00337     // Signal PBMS to delete the reference to the BLOB.
00338     err = pbms_api.releaseBlob(DB_NAME(field), TAB_NAME(field), blob, length, result);
00339     if (err)
00340       return err;
00341   }
00342   
00343   return 0;
00344 }
00345 
00346 #define MAX_NAME_SIZE 64
00347 static void parse_table_path(const char *path, char *db_name, char *tab_name)
00348 {
00349   const char *ptr = path + strlen(path) -1, *eptr;
00350   int len;
00351   
00352   *db_name = *tab_name = 0;
00353   
00354   while ((ptr > path) && (*ptr != '/'))ptr --;
00355   if (*ptr != '/') 
00356     return;
00357     
00358   strncpy(tab_name, ptr+1, MAX_NAME_SIZE);
00359   tab_name[MAX_NAME_SIZE-1] = 0;
00360   eptr = ptr;
00361   ptr--;
00362   
00363   while ((ptr > path) && (*ptr != '/'))ptr --;
00364   if (*ptr != '/') 
00365     return;
00366   ptr++;
00367   
00368   len = eptr - ptr;
00369   if (len >= MAX_NAME_SIZE)
00370     len = MAX_NAME_SIZE-1;
00371     
00372   memcpy(db_name, ptr, len);
00373   db_name[len] = 0;
00374   
00375 }
00376 
00377 //====================
00378 int pbms_rename_table_with_blobs(const char *old_table_path, const char *new_table_path, PBMSResultPtr result)
00379 {
00380   char o_db_name[MAX_NAME_SIZE], n_db_name[MAX_NAME_SIZE], o_tab_name[MAX_NAME_SIZE], n_tab_name[MAX_NAME_SIZE];
00381 
00382   result->mr_had_blobs = false; 
00383   if (!pbms_api.isPBMSLoaded())
00384     return 0;
00385     
00386   result->mr_had_blobs = true; // Assume it has blobs.
00387   
00388   parse_table_path(old_table_path, o_db_name, o_tab_name);
00389   parse_table_path(new_table_path, n_db_name, n_tab_name);
00390   
00391   return pbms_api.renameTable(o_db_name, o_tab_name, n_db_name, n_tab_name, result);
00392 }
00393 
00394 //====================
00395 int pbms_delete_table_with_blobs(const char *table_path, PBMSResultPtr result)
00396 {
00397   char db_name[MAX_NAME_SIZE], tab_name[MAX_NAME_SIZE];
00398     
00399   result->mr_had_blobs = false; 
00400   if (!pbms_api.isPBMSLoaded())
00401     return 0;
00402     
00403   result->mr_had_blobs = true; // Assume it has blobs.
00404   parse_table_path(table_path, db_name, tab_name);
00405 
00406   return pbms_api.dropTable(db_name, tab_name, result);
00407 }
00408 
00409 //====================
00410 void pbms_completed(const TABLE *table, bool ok)
00411 {
00412   if (!pbms_api.isPBMSLoaded())
00413     return;
00414     
00415   if ((!table) || (table->s->blob_fields != 0))
00416     pbms_api.completed(ok) ;
00417     
00418    return ;
00419 }
00420 #endif
00421 #endif // DRIZZLED