~ubuntu-branches/ubuntu/trusty/drizzle/trusty

« back to all changes in this revision

Viewing changes to plugin/filtered_replicator/filtered_replicator.cc

  • Committer: Bazaar Package Importer
  • Author(s): Monty Taylor
  • Date: 2010-03-18 12:12:31 UTC
  • Revision ID: james.westby@ubuntu.com-20100318121231-k6g1xe6cshbwa0f8
Tags: upstream-2010.03.1347
ImportĀ upstreamĀ versionĀ 2010.03.1347

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* - mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*-
 
2
 *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
 
3
 *
 
4
 *  Copyright (C) 2009 Sun Microsystems
 
5
 *
 
6
 *  This program is free software; you can redistribute it and/or modify
 
7
 *  it under the terms of the GNU General Public License as published by
 
8
 *  the Free Software Foundation; either version 2 of the License, or
 
9
 *  (at your option) any later version.
 
10
 *
 
11
 *  This program is distributed in the hope that it will be useful,
 
12
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 *  GNU General Public License for more details.
 
15
 *
 
16
 *  You should have received a copy of the GNU General Public License
 
17
 *  along with this program; if not, write to the Free Software
 
18
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
19
 */
 
20
 
 
21
/**
 
22
 * @file
 
23
 *
 
24
 * Defines the implementation of a simple replicator that can filter
 
25
 * events based on a schema or table name.
 
26
 *
 
27
 * @details
 
28
 *
 
29
 * This is a very simple implementation.  All we do is maintain two
 
30
 * std::vectors:
 
31
 *
 
32
 *  1) contains all the schema names to filter
 
33
 *  2) contains all the table names to filter
 
34
 *
 
35
 * If an event is on a schema or table in the vectors described above, then
 
36
 * the event will not be passed along to the applier.
 
37
 */
 
38
 
 
39
#include "config.h"
 
40
#include <drizzled/gettext.h>
 
41
#include <drizzled/plugin/transaction_applier.h>
 
42
#include <drizzled/message/transaction.pb.h>
 
43
#include <drizzled/plugin.h>
 
44
#include <drizzled/plugin/registry.h>
 
45
 
 
46
#include "filtered_replicator.h"
 
47
 
 
48
#include <vector>
 
49
#include <string>
 
50
 
 
51
using namespace std;
 
52
using namespace drizzled;
 
53
 
 
54
static bool sysvar_filtered_replicator_enabled= false;
 
55
static char *sysvar_filtered_replicator_sch_filters= NULL;
 
56
static char *sysvar_filtered_replicator_tab_filters= NULL;
 
57
static char *sysvar_filtered_replicator_sch_regex= NULL;
 
58
static char *sysvar_filtered_replicator_tab_regex= NULL;
 
59
 
 
60
FilteredReplicator::FilteredReplicator(string name_arg,
 
61
                                       const char *in_sch_filters,
 
62
                                       const char *in_tab_filters)
 
63
  :
 
64
    plugin::TransactionReplicator(name_arg),
 
65
    schemas_to_filter(),
 
66
    tables_to_filter(),
 
67
    sch_filter_string(in_sch_filters),
 
68
    tab_filter_string(in_tab_filters),
 
69
    sch_regex_enabled(false),
 
70
    tab_regex_enabled(false),
 
71
    sch_re(NULL),
 
72
    tab_re(NULL)
 
73
{
 
74
  /* 
 
75
   * Add each of the specified schemas to the vector of schemas
 
76
   * to filter.
 
77
   */
 
78
  if (in_sch_filters)
 
79
  {
 
80
    populateFilter(sch_filter_string, schemas_to_filter);
 
81
  }
 
82
 
 
83
  /* 
 
84
   * Add each of the specified tables to the vector of tables
 
85
   * to filter.
 
86
   */
 
87
  if (in_tab_filters)
 
88
  {
 
89
    populateFilter(tab_filter_string, tables_to_filter);
 
90
  }
 
91
 
 
92
  /* 
 
93
   * Compile the regular expression for schema's to filter
 
94
   * if one is specified.
 
95
   */
 
96
  if (sysvar_filtered_replicator_sch_regex)
 
97
  {
 
98
    const char *error= NULL;
 
99
    int32_t error_offset= 0;
 
100
    sch_re= pcre_compile(sysvar_filtered_replicator_sch_regex,
 
101
                         0,
 
102
                         &error,
 
103
                         &error_offset,
 
104
                         NULL);
 
105
    sch_regex_enabled= true;
 
106
  }
 
107
 
 
108
  /* 
 
109
   * Compile the regular expression for table's to filter
 
110
   * if one is specified.
 
111
   */
 
112
  if (sysvar_filtered_replicator_tab_regex)
 
113
  {
 
114
    const char *error= NULL;
 
115
    int32_t error_offset= 0;
 
116
    tab_re= pcre_compile(sysvar_filtered_replicator_tab_regex,
 
117
                         0,
 
118
                         &error,
 
119
                         &error_offset,
 
120
                         NULL);
 
121
    tab_regex_enabled= true;
 
122
  }
 
123
 
 
124
  pthread_mutex_init(&sch_vector_lock, NULL);
 
125
  pthread_mutex_init(&tab_vector_lock, NULL);
 
126
  pthread_mutex_init(&sysvar_sch_lock, NULL);
 
127
  pthread_mutex_init(&sysvar_tab_lock, NULL);
 
128
}
 
129
 
 
130
bool FilteredReplicator::isEnabled() const
 
131
{
 
132
  return sysvar_filtered_replicator_enabled;
 
133
}
 
134
 
 
135
void FilteredReplicator::parseStatementTableMetadata(const message::Statement &in_statement,
 
136
                                                     string &in_schema_name,
 
137
                                                     string &in_table_name) const
 
138
{
 
139
  switch (in_statement.type())
 
140
  {
 
141
    case message::Statement::INSERT:
 
142
    {
 
143
      const message::TableMetadata &metadata= in_statement.insert_header().table_metadata();
 
144
      in_schema_name.assign(metadata.schema_name());
 
145
      in_table_name.assign(metadata.table_name());
 
146
      break;
 
147
    }
 
148
    case message::Statement::UPDATE:
 
149
    {
 
150
      const message::TableMetadata &metadata= in_statement.update_header().table_metadata();
 
151
      in_schema_name.assign(metadata.schema_name());
 
152
      in_table_name.assign(metadata.table_name());
 
153
      break;
 
154
    }
 
155
    case message::Statement::DELETE:
 
156
    {
 
157
      const message::TableMetadata &metadata= in_statement.delete_header().table_metadata();
 
158
      in_schema_name.assign(metadata.schema_name());
 
159
      in_table_name.assign(metadata.table_name());
 
160
      break;
 
161
    }
 
162
    case message::Statement::CREATE_SCHEMA:
 
163
    {
 
164
      in_schema_name.assign(in_statement.create_schema_statement().schema().name());
 
165
      in_table_name.clear();
 
166
      break;
 
167
    }
 
168
    case message::Statement::ALTER_SCHEMA:
 
169
    {
 
170
      in_schema_name.assign(in_statement.alter_schema_statement().after().name());
 
171
      in_table_name.clear();
 
172
      break;
 
173
    }
 
174
    case message::Statement::DROP_SCHEMA:
 
175
    {
 
176
      in_schema_name.assign(in_statement.drop_schema_statement().schema_name());
 
177
      in_table_name.clear();
 
178
      break;
 
179
    }
 
180
    case message::Statement::CREATE_TABLE:
 
181
    {
 
182
      // in_schema_name.assign(in_statement.create_table_statement().table().name());
 
183
      in_table_name.assign(in_statement.create_table_statement().table().name());
 
184
      break;
 
185
    }
 
186
    case message::Statement::ALTER_TABLE:
 
187
    {
 
188
      // in_schema_name.assign(in_statement.alter_table_statement().table().name());
 
189
      in_table_name.assign(in_statement.alter_table_statement().after().name());
 
190
      break;
 
191
    }
 
192
    case message::Statement::DROP_TABLE:
 
193
    {
 
194
      const message::TableMetadata &metadata= in_statement.drop_table_statement().table_metadata();
 
195
      in_schema_name.assign(metadata.schema_name());
 
196
      in_table_name.assign(metadata.table_name());
 
197
      break;
 
198
    }
 
199
    default:
 
200
    {
 
201
      /* All other types have no schema and table information */
 
202
      in_schema_name.clear();
 
203
      in_table_name.clear();
 
204
      break;
 
205
    }
 
206
  }  
 
207
}
 
208
void FilteredReplicator::enable()
 
209
{
 
210
  sysvar_filtered_replicator_enabled= true;
 
211
}
 
212
 
 
213
void FilteredReplicator::disable()
 
214
{
 
215
  sysvar_filtered_replicator_enabled= false;
 
216
}
 
217
 
 
218
void FilteredReplicator::replicate(plugin::TransactionApplier *in_applier, 
 
219
                                   message::Transaction &to_replicate)
 
220
{
 
221
  string schema_name;
 
222
  string table_name;
 
223
 
 
224
  size_t num_statements= to_replicate.statement_size();
 
225
  size_t x;
 
226
 
 
227
  /* 
 
228
   * We build a new transaction message containing only Statement
 
229
   * messages that have not been filtered.
 
230
   *
 
231
   * @todo A more efficient method would be to rework the pointers
 
232
   * that the to_replicate.statement() vector contains and remove
 
233
   * the statement pointers that are filtered...
 
234
   */
 
235
  message::Transaction filtered_transaction;
 
236
 
 
237
  for (x= 0; x < num_statements; ++x)
 
238
  {
 
239
    schema_name.clear();
 
240
    table_name.clear();
 
241
 
 
242
    const message::Statement &statement= to_replicate.statement(x);
 
243
 
 
244
    /*
 
245
     * First, we check to see if the command consists of raw SQL. If so,
 
246
     * we need to parse this SQL and determine whether to filter the event
 
247
     * based on the information we obtain from the parsed SQL.
 
248
     * If not raw SQL, check if this event should be filtered or not
 
249
     * based on the schema and table names in the command message.
 
250
     *
 
251
     * The schema and table names are stored in TableMetadata headers
 
252
     * for most types of Statement messages.
 
253
     */
 
254
    if (statement.type() == message::Statement::RAW_SQL)
 
255
    {
 
256
      parseQuery(statement.sql(), schema_name, table_name);
 
257
    }
 
258
    else
 
259
    {
 
260
      parseStatementTableMetadata(statement, schema_name, table_name);
 
261
    }
 
262
 
 
263
    /*
 
264
     * Convert the schema name and table name strings to lowercase so that it
 
265
     * does not matter what case the table or schema name was specified in. We
 
266
     * also keep all entries in the vectors of schemas and tables to filter in
 
267
     * lowercase.
 
268
     */
 
269
    std::transform(schema_name.begin(), schema_name.end(),
 
270
                  schema_name.begin(), ::tolower);
 
271
    std::transform(table_name.begin(), table_name.end(),
 
272
                  table_name.begin(), ::tolower);
 
273
 
 
274
    if (! isSchemaFiltered(schema_name) &&
 
275
        ! isTableFiltered(table_name))
 
276
    {
 
277
      message::Statement *s= filtered_transaction.add_statement();
 
278
      *s= statement; /* copy contruct */
 
279
    }
 
280
  }
 
281
 
 
282
  if (filtered_transaction.statement_size() > 0)
 
283
  {
 
284
 
 
285
    /*
 
286
     * We can now simply call the applier's apply() method, passing
 
287
     * along the supplied command.
 
288
     */
 
289
    message::TransactionContext *tc= filtered_transaction.mutable_transaction_context();
 
290
    *tc= to_replicate.transaction_context(); /* copy construct */
 
291
    in_applier->apply(filtered_transaction);
 
292
  }
 
293
}
 
294
 
 
295
void FilteredReplicator::populateFilter(std::string input,
 
296
                                        vector<string> &filter)
 
297
{
 
298
  /*
 
299
   * Convert the input string to lowercase so that all entries in the vector
 
300
   * will be in lowercase.
 
301
   */
 
302
  std::transform(input.begin(), input.end(),
 
303
                 input.begin(), ::tolower);
 
304
  string::size_type last_pos= input.find_first_not_of(',', 0);
 
305
  string::size_type pos= input.find_first_of(',', last_pos);
 
306
 
 
307
  while (pos != string::npos || last_pos != string::npos)
 
308
  {
 
309
    filter.push_back(input.substr(last_pos, pos - last_pos));
 
310
    last_pos= input.find_first_not_of(',', pos);
 
311
    pos= input.find_first_of(',', last_pos);
 
312
  }
 
313
}
 
314
 
 
315
bool FilteredReplicator::isSchemaFiltered(const string &schema_name)
 
316
{
 
317
  pthread_mutex_lock(&sch_vector_lock);
 
318
  vector<string>::iterator it= find(schemas_to_filter.begin(),
 
319
                                    schemas_to_filter.end(),
 
320
                                    schema_name);
 
321
  if (it != schemas_to_filter.end())
 
322
  {
 
323
    pthread_mutex_unlock(&sch_vector_lock);
 
324
    return true;
 
325
  }
 
326
  pthread_mutex_unlock(&sch_vector_lock);
 
327
 
 
328
  /* 
 
329
   * If regular expression matching is enabled for schemas to filter, then
 
330
   * we check to see if this schema name matches the regular expression that
 
331
   * has been specified. 
 
332
   */
 
333
  if (sch_regex_enabled)
 
334
  {
 
335
    int32_t result= pcre_exec(sch_re,
 
336
                              NULL,
 
337
                              schema_name.c_str(),
 
338
                              schema_name.length(),
 
339
                              0,
 
340
                              0,
 
341
                              NULL,
 
342
                              0);
 
343
    if (result >= 0)
 
344
    {
 
345
      return true;
 
346
    }
 
347
  }
 
348
 
 
349
  return false;
 
350
}
 
351
 
 
352
bool FilteredReplicator::isTableFiltered(const string &table_name)
 
353
{
 
354
  pthread_mutex_lock(&tab_vector_lock);
 
355
  vector<string>::iterator it= find(tables_to_filter.begin(),
 
356
                                    tables_to_filter.end(),
 
357
                                    table_name);
 
358
  if (it != tables_to_filter.end())
 
359
  {
 
360
    pthread_mutex_unlock(&tab_vector_lock);
 
361
    return true;
 
362
  }
 
363
  pthread_mutex_unlock(&tab_vector_lock);
 
364
 
 
365
  /* 
 
366
   * If regular expression matching is enabled for tables to filter, then
 
367
   * we check to see if this table name matches the regular expression that
 
368
   * has been specified. 
 
369
   */
 
370
  if (tab_regex_enabled)
 
371
  {
 
372
    int32_t result= pcre_exec(tab_re,
 
373
                              NULL,
 
374
                              table_name.c_str(),
 
375
                              table_name.length(),
 
376
                              0,
 
377
                              0,
 
378
                              NULL,
 
379
                              0);
 
380
    if (result >= 0)
 
381
    {
 
382
      return true;
 
383
    }
 
384
  }
 
385
 
 
386
  return false;
 
387
}
 
388
 
 
389
void FilteredReplicator::parseQuery(const string &sql,
 
390
                                    string &schema_name,
 
391
                                    string &table_name)
 
392
{
 
393
  /*
 
394
   * Determine what type of SQL we are dealing with e.g. create table,
 
395
   * drop table, etc.
 
396
   */
 
397
  string::size_type pos= sql.find_first_of(' ', 0);
 
398
  string type= sql.substr(0, pos);
 
399
 
 
400
  /*
 
401
   * Convert the type string to uppercase here so that it doesn't
 
402
   * matter what case the user entered the statement in.
 
403
   */
 
404
  std::transform(type.begin(), type.end(),
 
405
                 type.begin(), ::toupper);
 
406
 
 
407
  if (type.compare("DROP") == 0)
 
408
  {
 
409
    /*
 
410
     * The schema and table name can be either the third word
 
411
     * or the fifth word in a DROP TABLE statement...so we extract
 
412
     * the third word from the SQL and see whether it is and IF or
 
413
     * not.
 
414
     */
 
415
    pos= sql.find_first_of(' ', 11);
 
416
    string cmp_str= sql.substr(11, pos - 11);
 
417
    string target_name("");
 
418
    if (cmp_str.compare("IF") == 0)
 
419
    {
 
420
      /* the name must be the fifth word */
 
421
      pos= sql.find_first_of(' ', 21);
 
422
      target_name.assign(sql.substr(21, pos - 21));
 
423
    }
 
424
    else
 
425
    {
 
426
      target_name.assign(cmp_str);
 
427
    }
 
428
    /*
 
429
     * Determine whether the name is a concatenation of the schema
 
430
     * name and table name i.e. schema.table or just the table name
 
431
     * on its own.
 
432
     */
 
433
    pos= target_name.find_first_of('.', 0);
 
434
    if (pos != string::npos)
 
435
    {
 
436
      /*
 
437
       * There is a schema name here...
 
438
       */
 
439
      schema_name.assign(target_name.substr(0, pos));
 
440
      /*
 
441
       * The rest of the name string is the table name.
 
442
       */
 
443
      table_name.assign(target_name.substr(pos + 1));
 
444
    }
 
445
    else
 
446
    {
 
447
      table_name.assign(target_name);
 
448
    }
 
449
  }
 
450
  else if (type.compare("CREATE") == 0)
 
451
  {
 
452
    /*
 
453
     * The schema and table name are always the third word
 
454
     * in a CREATE TABLE statement...always (unless there is
 
455
     * some crazy syntax I am unaware of).
 
456
     */
 
457
    pos= sql.find_first_of(' ', 13);
 
458
    string target_name= sql.substr(13, pos - 13);
 
459
    /*
 
460
     * Determine whether the name is a concatenation of the schema
 
461
     * name and table name i.e. schema.table or just the table name
 
462
     * on its own.
 
463
     */
 
464
    pos= target_name.find_first_of('.', 0);
 
465
    if (pos != string::npos)
 
466
    {
 
467
      /*
 
468
       * There is a schema name here...
 
469
       */
 
470
      schema_name.assign(target_name.substr(0, pos));
 
471
      /*
 
472
       * The rest of the name string is the table name.
 
473
       */
 
474
      table_name.assign(target_name.substr(pos + 1));
 
475
    }
 
476
    else
 
477
    {
 
478
      table_name.assign(target_name);
 
479
    }
 
480
  }
 
481
  else
 
482
  {
 
483
    /* we only deal with DROP and CREATE table for the moment */
 
484
    return;
 
485
  }
 
486
}
 
487
 
 
488
void FilteredReplicator::setSchemaFilter(const string &input)
 
489
{
 
490
  pthread_mutex_lock(&sch_vector_lock);
 
491
  pthread_mutex_lock(&sysvar_sch_lock);
 
492
  sch_filter_string.assign(input);
 
493
  schemas_to_filter.clear();
 
494
  populateFilter(sch_filter_string, schemas_to_filter);
 
495
  pthread_mutex_unlock(&sch_vector_lock);
 
496
}
 
497
 
 
498
void FilteredReplicator::setTableFilter(const string &input)
 
499
{
 
500
  pthread_mutex_lock(&tab_vector_lock);
 
501
  pthread_mutex_lock(&sysvar_tab_lock);
 
502
  tab_filter_string.assign(input);
 
503
  tables_to_filter.clear();
 
504
  populateFilter(tab_filter_string, tables_to_filter);
 
505
  pthread_mutex_unlock(&tab_vector_lock);
 
506
}
 
507
 
 
508
static FilteredReplicator *filtered_replicator= NULL; /* The singleton replicator */
 
509
 
 
510
static int init(plugin::Registry &registry)
 
511
{
 
512
  if (sysvar_filtered_replicator_enabled)
 
513
  {
 
514
    filtered_replicator= new(std::nothrow) 
 
515
      FilteredReplicator("filtered_replicator",
 
516
                         sysvar_filtered_replicator_sch_filters,
 
517
                         sysvar_filtered_replicator_tab_filters);
 
518
    if (filtered_replicator == NULL)
 
519
    {
 
520
      return 1;
 
521
    }
 
522
    registry.add(filtered_replicator);
 
523
  }
 
524
  return 0;
 
525
}
 
526
 
 
527
static int deinit(plugin::Registry &registry)
 
528
{
 
529
  if (filtered_replicator)
 
530
  {
 
531
    registry.remove(filtered_replicator);
 
532
    delete filtered_replicator;
 
533
  }
 
534
  return 0;
 
535
}
 
536
 
 
537
static int check_filtered_schemas(Session *, 
 
538
                                  drizzle_sys_var *,
 
539
                                  void *,
 
540
                                  drizzle_value *value)
 
541
{
 
542
  char buff[STRING_BUFFER_USUAL_SIZE];
 
543
  int len= sizeof(buff);
 
544
  const char *input= value->val_str(value, buff, &len);
 
545
 
 
546
  if (input && filtered_replicator)
 
547
  {
 
548
    filtered_replicator->setSchemaFilter(input);
 
549
    return 0;
 
550
  }
 
551
  return 1;
 
552
}
 
553
 
 
554
static void set_filtered_schemas(Session *,
 
555
                                 drizzle_sys_var *,
 
556
                                 void *var_ptr,
 
557
                                 const void *save)
 
558
{
 
559
  if (filtered_replicator)
 
560
  {
 
561
    if (*(bool *)save != true)
 
562
    {
 
563
      /* update the value of the system variable */
 
564
      filtered_replicator->updateSchemaSysvar((const char **) var_ptr);
 
565
    }
 
566
  }
 
567
}
 
568
 
 
569
static int check_filtered_tables(Session *, 
 
570
                                 drizzle_sys_var *,
 
571
                                 void *save,
 
572
                                 drizzle_value *value)
 
573
{
 
574
  char buff[STRING_BUFFER_USUAL_SIZE];
 
575
  int len= sizeof(buff);
 
576
  const char *input= value->val_str(value, buff, &len);
 
577
 
 
578
  if (input && filtered_replicator)
 
579
  {
 
580
    filtered_replicator->setTableFilter(input);
 
581
    *(bool *) save= (bool) true;
 
582
    return 0;
 
583
  }
 
584
  *(bool *) save= (bool) false;
 
585
  return 1;
 
586
}
 
587
 
 
588
static void set_filtered_tables(Session *,
 
589
                                drizzle_sys_var *,
 
590
                                void *var_ptr,
 
591
                                const void *save)
 
592
{
 
593
  if (filtered_replicator)
 
594
  {
 
595
    if (*(bool *)save != false)
 
596
    {
 
597
      /* update the value of the system variable */
 
598
      filtered_replicator->updateTableSysvar((const char **) var_ptr);
 
599
    }
 
600
  }
 
601
}
 
602
 
 
603
static DRIZZLE_SYSVAR_BOOL(enable,
 
604
                           sysvar_filtered_replicator_enabled,
 
605
                           PLUGIN_VAR_NOCMDARG,
 
606
                           N_("Enable filtered replicator"),
 
607
                           NULL, /* check func */
 
608
                           NULL, /* update func */
 
609
                           false /*default */);
 
610
static DRIZZLE_SYSVAR_STR(filteredschemas,
 
611
                          sysvar_filtered_replicator_sch_filters,
 
612
                          PLUGIN_VAR_OPCMDARG,
 
613
                          N_("List of schemas to filter"),
 
614
                          check_filtered_schemas,
 
615
                          set_filtered_schemas,
 
616
                          "");
 
617
static DRIZZLE_SYSVAR_STR(filteredtables,
 
618
                          sysvar_filtered_replicator_tab_filters,
 
619
                          PLUGIN_VAR_OPCMDARG,
 
620
                          N_("List of tables to filter"),
 
621
                          check_filtered_tables,
 
622
                          set_filtered_tables,
 
623
                          "");
 
624
static DRIZZLE_SYSVAR_STR(schemaregex,
 
625
                          sysvar_filtered_replicator_sch_regex,
 
626
                          PLUGIN_VAR_READONLY,
 
627
                          N_("Regular expression to apply to schemas to filter"),
 
628
                          NULL,
 
629
                          NULL,
 
630
                          NULL);
 
631
static DRIZZLE_SYSVAR_STR(tableregex,
 
632
                          sysvar_filtered_replicator_tab_regex,
 
633
                          PLUGIN_VAR_READONLY,
 
634
                          N_("Regular expression to apply to tables to filter"),
 
635
                          NULL,
 
636
                          NULL,
 
637
                          NULL);
 
638
 
 
639
static drizzle_sys_var* filtered_replicator_system_variables[]= {
 
640
  DRIZZLE_SYSVAR(enable),
 
641
  DRIZZLE_SYSVAR(filteredschemas),
 
642
  DRIZZLE_SYSVAR(filteredtables),
 
643
  DRIZZLE_SYSVAR(schemaregex),
 
644
  DRIZZLE_SYSVAR(tableregex),
 
645
  NULL
 
646
};
 
647
 
 
648
DRIZZLE_DECLARE_PLUGIN
 
649
{
 
650
  DRIZZLE_VERSION_ID,
 
651
  "filtered_replicator",
 
652
  "0.2",
 
653
  "Padraig O'Sullivan",
 
654
  N_("Filtered Replicator"),
 
655
  PLUGIN_LICENSE_GPL,
 
656
  init, /* Plugin Init */
 
657
  deinit, /* Plugin Deinit */
 
658
  filtered_replicator_system_variables, /* system variables */
 
659
  NULL    /* config options */
 
660
}
 
661
DRIZZLE_DECLARE_PLUGIN_END;