116
116
using namespace google;
117
117
using namespace drizzled;
119
int read_row_from_haildb(Session *session, unsigned char* buf, ib_crsr_t cursor, ib_tpl_t tuple, Table* table, bool has_hidden_primary_key, uint64_t *hidden_pkey, drizzled::memory::Root **blobroot= NULL);
119
int read_row_from_haildb(unsigned char* buf, ib_crsr_t cursor, ib_tpl_t tuple, Table* table, bool has_hidden_primary_key, uint64_t *hidden_pkey, drizzled::memory::Root **blobroot= NULL);
120
120
static void fill_ib_search_tpl_from_drizzle_key(ib_tpl_t search_tuple,
121
121
const drizzled::KeyInfo *key_info,
122
122
const unsigned char *key_ptr,
166
166
int doCreateTable(Session&,
167
167
Table& table_arg,
168
const drizzled::identifier::Table &identifier,
168
const drizzled::TableIdentifier &identifier,
169
169
drizzled::message::Table& proto);
171
int doDropTable(Session&, const identifier::Table &identifier);
171
int doDropTable(Session&, const TableIdentifier &identifier);
173
173
int doRenameTable(drizzled::Session&,
174
const drizzled::identifier::Table&,
175
const drizzled::identifier::Table&);
174
const drizzled::TableIdentifier&,
175
const drizzled::TableIdentifier&);
177
177
int doGetTableDefinition(Session& session,
178
const identifier::Table &identifier,
178
const TableIdentifier &identifier,
179
179
drizzled::message::Table &table_proto);
181
bool doDoesTableExist(Session&, const identifier::Table &identifier);
181
bool doDoesTableExist(Session&, const TableIdentifier &identifier);
184
void getTableNamesInSchemaFromHailDB(const drizzled::identifier::Schema &schema,
184
void getTableNamesInSchemaFromHailDB(const drizzled::SchemaIdentifier &schema,
185
185
drizzled::plugin::TableNameList *set_of_names,
186
drizzled::identifier::Table::vector *identifiers);
186
drizzled::TableIdentifier::vector *identifiers);
189
189
void doGetTableIdentifiers(drizzled::CachedDirectory &,
190
const drizzled::identifier::Schema &schema,
191
drizzled::identifier::Table::vector &identifiers);
190
const drizzled::SchemaIdentifier &schema,
191
drizzled::TableIdentifier::vector &identifiers);
193
193
/* The following defines can be increased if necessary */
194
194
uint32_t max_supported_keys() const { return 1000; }
239
239
/* This is a superset of the map from innobase plugin.
240
240
Unlike innobase plugin we don't act on errors here, we just
241
241
map error codes. */
242
static int ib_err_t_to_drizzle_error(Session* session, ib_err_t err)
242
static int ib_err_t_to_drizzle_error(ib_err_t err)
269
269
return HA_ERR_NO_ACTIVE_RECORD;
271
271
case DB_DEADLOCK:
272
/* HailDB will roll back a transaction itself due to DB_DEADLOCK.
273
This means we have to tell Drizzle about it */
274
session->markTransactionForRollback(true);
275
272
return HA_ERR_LOCK_DEADLOCK;
277
274
case DB_LOCK_WAIT_TIMEOUT:
355
352
transaction= get_trx(session);
356
isolation_level= tx_isolation_to_ib_trx_level(session->getTxIsolation());
353
isolation_level= tx_isolation_to_ib_trx_level((enum_tx_isolation)session_tx_isolation(session));
357
354
*transaction= ib_trx_begin(isolation_level);
359
356
return *transaction == NULL;
390
387
err= ib_savepoint_rollback(*transaction, savepoint.getName().c_str(),
391
388
savepoint.getName().length());
393
return ib_err_t_to_drizzle_error(session, err);
390
return ib_err_t_to_drizzle_error(err);
396
393
int HailDBEngine::doReleaseSavepoint(Session* session,
402
399
err= ib_savepoint_release(*transaction, savepoint.getName().c_str(),
403
400
savepoint.getName().length());
404
401
if (err != DB_SUCCESS)
405
return ib_err_t_to_drizzle_error(session, err);
402
return ib_err_t_to_drizzle_error(err);
413
410
ib_trx_t *transaction= get_trx(session);
412
if (all || (!session_test_options(session, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))
417
414
err= ib_trx_commit(*transaction);
419
416
if (err != DB_SUCCESS)
420
return ib_err_t_to_drizzle_error(session, err);
417
return ib_err_t_to_drizzle_error(err);
422
419
*transaction= NULL;
431
428
ib_trx_t *transaction= get_trx(session);
430
if (all || !session_test_options(session, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))
435
if (ib_trx_state(*transaction) == IB_TRX_NOT_STARTED)
436
err= ib_trx_release(*transaction);
438
err= ib_trx_rollback(*transaction);
432
err= ib_trx_rollback(*transaction);
440
434
if (err != DB_SUCCESS)
441
return ib_err_t_to_drizzle_error(session, err);
435
return ib_err_t_to_drizzle_error(err);
443
437
*transaction= NULL;
447
if (ib_trx_state(*transaction) == IB_TRX_NOT_STARTED)
450
441
err= ib_savepoint_rollback(*transaction, statement_savepoint_name.c_str(),
451
442
statement_savepoint_name.length());
452
443
if (err != DB_SUCCESS)
453
return ib_err_t_to_drizzle_error(session, err);
444
return ib_err_t_to_drizzle_error(err);
496
487
assert (err == DB_SUCCESS);
497
err= ib_tuple_read_u64(tuple, getTable()->getShare()->sizeFields(), &nr);
488
err= ib_tuple_read_u64(tuple, getTable()->getShare()->fields, &nr);
500
491
ib_tuple_delete(tuple);
535
526
doEndIndexScan();
536
527
(void) extra(HA_EXTRA_NO_KEYREAD);
538
if (getTable()->getShare()->getTableMessage()->options().auto_increment_value() > nr)
539
nr= getTable()->getShare()->getTableMessage()->options().auto_increment_value();
529
if (getTable()->getShare()->getTableProto()->options().auto_increment_value() > nr)
530
nr= getTable()->getShare()->getTableProto()->options().auto_increment_value();
680
671
unexpected if an obsolete consistent read view would be
683
enum_tx_isolation isolation_level= session->getTxIsolation();
674
enum_tx_isolation isolation_level= session_tx_isolation(session);
685
676
if (isolation_level != ISO_SERIALIZABLE
686
677
&& (lock_type == TL_READ || lock_type == TL_READ_NO_INSERT)
724
715
if ((lock_type >= TL_WRITE_CONCURRENT_INSERT
725
716
&& lock_type <= TL_WRITE)
726
&& ! session->doing_tablespace_operation()
717
&& !session_tablespace_op(session)
727
718
&& sql_command != SQLCOM_TRUNCATE
728
719
&& sql_command != SQLCOM_CREATE_TABLE) {
803
static void TableIdentifier_to_haildb_name(const identifier::Table &identifier, std::string *str)
794
static void TableIdentifier_to_haildb_name(const TableIdentifier &identifier, std::string *str)
805
796
str->assign(table_path_to_haildb_name(identifier.getPath().c_str()));
836
827
if (err != DB_SUCCESS)
837
return ib_err_t_to_drizzle_error(getTable()->getSession(), err);
828
return ib_err_t_to_drizzle_error(err);
839
830
err= ib_cursor_open_table_using_id(table_id, NULL, &cursor);
840
831
cursor_is_sec_index= false;
842
833
if (err != DB_SUCCESS)
843
return ib_err_t_to_drizzle_error(getTable()->getSession(), err);
834
return ib_err_t_to_drizzle_error(err);
845
836
err= ib_index_get_id(haildb_table_name, "HIDDEN_PRIMARY", &idx_id);
1036
1027
int HailDBEngine::doCreateTable(Session &session,
1037
1028
Table& table_obj,
1038
const drizzled::identifier::Table &identifier,
1029
const drizzled::TableIdentifier &identifier,
1039
1030
drizzled::message::Table& table_message)
1041
1032
ib_tbl_sch_t haildb_table_schema= NULL;
1079
1070
ER_CANT_CREATE_TABLE,
1080
1071
_("Cannot create table %s. HailDB Error %d (%s)\n"),
1081
1072
haildb_table_name.c_str(), haildb_err, ib_strerror(haildb_err));
1082
return ib_err_t_to_drizzle_error(&session, haildb_err);
1073
return ib_err_t_to_drizzle_error(haildb_err);
1085
1076
for (int colnr= 0; colnr < table_message.field_size() ; colnr++)
1100
1091
" HailDB Error %d (%s)\n"),
1101
1092
field.name().c_str(), haildb_table_name.c_str(),
1102
1093
haildb_err, ib_strerror(haildb_err));
1103
return ib_err_t_to_drizzle_error(&session, haildb_err);
1094
return ib_err_t_to_drizzle_error(haildb_err);
1105
1096
if (field_err != 0)
1106
1097
return field_err;
1248
1239
_("Cannot create table %s. HailDB Error %d (%s)\n"),
1249
1240
haildb_table_name.c_str(),
1250
1241
haildb_err, ib_strerror(haildb_err));
1251
return ib_err_t_to_drizzle_error(&session, haildb_err);
1242
return ib_err_t_to_drizzle_error(haildb_err);
1293
1284
int HailDBEngine::doDropTable(Session &session,
1294
const identifier::Table &identifier)
1285
const TableIdentifier &identifier)
1296
1287
ib_trx_t haildb_schema_transaction;
1297
1288
ib_err_t haildb_err;
1376
static ib_err_t rename_table_message(ib_trx_t transaction, const identifier::Table &from_identifier, const identifier::Table &to_identifier)
1367
static ib_err_t rename_table_message(ib_trx_t transaction, const TableIdentifier &from_identifier, const TableIdentifier &to_identifier)
1378
1369
ib_crsr_t cursor;
1379
1370
ib_tpl_t search_tuple;
1466
1457
int HailDBEngine::doRenameTable(drizzled::Session &session,
1467
const drizzled::identifier::Table &from,
1468
const drizzled::identifier::Table &to)
1458
const drizzled::TableIdentifier &from,
1459
const drizzled::TableIdentifier &to)
1470
1461
ib_trx_t haildb_schema_transaction;
1515
1506
assert(rollback_err == DB_SUCCESS);
1516
1507
rollback_err= ib_trx_rollback(haildb_schema_transaction);
1517
1508
assert(rollback_err == DB_SUCCESS);
1518
return ib_err_t_to_drizzle_error(&session, err);
1509
return ib_err_t_to_drizzle_error(err);
1521
1512
void HailDBEngine::getTableNamesInSchemaFromHailDB(
1522
const drizzled::identifier::Schema &schema,
1513
const drizzled::SchemaIdentifier &schema,
1523
1514
drizzled::plugin::TableNameList *set_of_names,
1524
drizzled::identifier::Table::vector *identifiers)
1515
drizzled::TableIdentifier::vector *identifiers)
1526
1517
ib_trx_t transaction;
1527
1518
ib_crsr_t cursor;
1552
1543
BOOST_FOREACH(std::string table_name, haildb_system_table_names)
1554
identifiers->push_back(identifier::Table(schema.getSchemaName(),
1545
identifiers->push_back(TableIdentifier(schema.getSchemaName(),
1615
1606
void HailDBEngine::doGetTableIdentifiers(drizzled::CachedDirectory &,
1616
const drizzled::identifier::Schema &schema,
1617
drizzled::identifier::Table::vector &identifiers)
1607
const drizzled::SchemaIdentifier &schema,
1608
drizzled::TableIdentifier::vector &identifiers)
1619
1610
getTableNamesInSchemaFromHailDB(schema, NULL, &identifiers);
1741
1732
int HailDBEngine::doGetTableDefinition(Session &session,
1742
const identifier::Table &identifier,
1733
const TableIdentifier &identifier,
1743
1734
drizzled::message::Table &table)
1745
1736
ib_crsr_t haildb_cursor= NULL;
1770
1761
bool HailDBEngine::doDoesTableExist(Session &,
1771
const identifier::Table& identifier)
1762
const TableIdentifier& identifier)
1773
1764
ib_crsr_t haildb_cursor;
1774
1765
string haildb_table_name;
1910
1901
err= ib_cursor_open_table_using_id(table_id, transaction, &cursor);
1912
1903
if (err != DB_SUCCESS)
1913
return ib_err_t_to_drizzle_error(getTable()->getSession(), err);
1904
return ib_err_t_to_drizzle_error(err);
1915
1906
cursor_is_sec_index= false;
1981
1972
if (share->has_hidden_primary_key)
1983
err= ib_tuple_write_u64(tuple, getTable()->getShare()->sizeFields(),
1984
share->hidden_pkey_auto_increment_value.fetch_and_increment());
1974
err= ib_tuple_write_u64(tuple, getTable()->getShare()->fields, share->hidden_pkey_auto_increment_value.fetch_and_increment());
1987
1977
err= ib_cursor_insert_row(cursor, tuple);
2019
2009
ret= HA_ERR_FOUND_DUPP_KEY;
2021
2011
else if (err != DB_SUCCESS)
2022
ret= ib_err_t_to_drizzle_error(getTable()->getSession(), err);
2012
ret= ib_err_t_to_drizzle_error(err);
2024
2014
tuple= ib_tuple_clear(tuple);
2025
2015
ib_tuple_delete(tuple);
2050
2040
err= ib_cursor_open_table_using_id(table_id, transaction, &cursor);
2052
2042
if (err != DB_SUCCESS)
2053
return ib_err_t_to_drizzle_error(getTable()->getSession(), err);
2043
return ib_err_t_to_drizzle_error(err);
2054
2044
cursor_is_sec_index= false;
2123
2113
so only support TRUNCATE and not DELETE FROM t;
2124
2114
(this is what ha_innodb does)
2126
if (getTable()->in_use->getSqlCommand() != SQLCOM_TRUNCATE)
2116
if (session_sql_command(getTable()->in_use) != SQLCOM_TRUNCATE)
2127
2117
return HA_ERR_WRONG_COMMAND;
2139
2129
err= ib_cursor_open_table_using_id(table_id, transaction, &cursor);
2141
2131
if (err != DB_SUCCESS)
2142
return ib_err_t_to_drizzle_error(getTable()->getSession(), err);
2132
return ib_err_t_to_drizzle_error(err);
2143
2133
cursor_is_sec_index= false;
2181
2171
ib_schema_unlock(transaction);
2182
2172
ib_err_t rollback_err= ib_trx_rollback(transaction);
2183
2173
assert(rollback_err == DB_SUCCESS);
2184
return ib_err_t_to_drizzle_error(getTable()->getSession(), err);
2174
return ib_err_t_to_drizzle_error(err);
2187
2177
int HailDBCursor::doStartTableScan(bool)
2213
2203
if (err != DB_SUCCESS)
2214
return ib_err_t_to_drizzle_error(getTable()->getSession(), err);
2204
return ib_err_t_to_drizzle_error(err);
2216
2206
err= ib_cursor_set_lock_mode(cursor, ib_lock_mode);
2217
2207
assert(err == DB_SUCCESS); // FIXME
2221
2211
err= ib_cursor_first(cursor);
2222
2212
if (err != DB_SUCCESS && err != DB_END_OF_INDEX)
2224
int reset_err= ib_cursor_reset(cursor);
2225
assert(reset_err == DB_SUCCESS);
2226
return ib_err_t_to_drizzle_error(getTable()->getSession(), err);
2214
previous_error= ib_err_t_to_drizzle_error(err);
2215
err= ib_cursor_reset(cursor);
2216
return previous_error;
2229
2219
advance_cursor= false;
2234
int read_row_from_haildb(Session *session, unsigned char* buf, ib_crsr_t cursor, ib_tpl_t tuple, Table* table, bool has_hidden_primary_key, uint64_t *hidden_pkey, drizzled::memory::Root **blobroot)
2225
int read_row_from_haildb(unsigned char* buf, ib_crsr_t cursor, ib_tpl_t tuple, Table* table, bool has_hidden_primary_key, uint64_t *hidden_pkey, drizzled::memory::Root **blobroot)
2237
2228
ptrdiff_t row_offset= buf - table->getInsertRecord();
2241
2232
if (err == DB_RECORD_NOT_FOUND)
2242
2233
return HA_ERR_END_OF_FILE;
2243
2234
if (err != DB_SUCCESS)
2244
return ib_err_t_to_drizzle_error(session, err);
2235
return ib_err_t_to_drizzle_error(err);
2307
2298
(**field).move_field_offset(-row_offset);
2309
2300
if (err != DB_SUCCESS)
2310
return ib_err_t_to_drizzle_error(session, err);
2301
return ib_err_t_to_drizzle_error(err);
2313
2304
if (has_hidden_primary_key)
2315
2306
err= ib_tuple_read_u64(tuple, colnr, hidden_pkey);
2318
return ib_err_t_to_drizzle_error(session, err);
2309
return ib_err_t_to_drizzle_error(err);
2321
2312
int HailDBCursor::rnd_next(unsigned char *buf)
2318
return previous_error;
2326
2320
if (advance_cursor)
2328
2322
err= ib_cursor_next(cursor);
2329
2323
if (err != DB_SUCCESS)
2330
return ib_err_t_to_drizzle_error(getTable()->getSession(), err);
2324
return ib_err_t_to_drizzle_error(err);
2333
2327
tuple= ib_tuple_clear(tuple);
2334
ret= read_row_from_haildb(getTable()->getSession(), buf, cursor, tuple,
2328
ret= read_row_from_haildb(buf, cursor, tuple, getTable(),
2336
2329
share->has_hidden_primary_key,
2337
2330
&hidden_autoinc_pkey_position);
2349
2342
err= ib_cursor_reset(cursor);
2350
2343
assert(err == DB_SUCCESS);
2351
2344
in_table_scan= false;
2352
return ib_err_t_to_drizzle_error(getTable()->getSession(), err);
2346
return ib_err_t_to_drizzle_error(err);
2355
2349
int HailDBCursor::rnd_pos(unsigned char *buf, unsigned char *pos)
2364
2358
err= ib_col_set_value(search_tuple, 0,
2365
2359
((uint64_t*)(pos)), sizeof(uint64_t));
2366
2360
if (err != DB_SUCCESS)
2367
return ib_err_t_to_drizzle_error(getTable()->getSession(), err);
2361
return ib_err_t_to_drizzle_error(err);
2382
2376
err= ib_cursor_moveto(cursor, search_tuple, IB_CUR_GE, &res);
2383
2377
if (err != DB_SUCCESS)
2384
return ib_err_t_to_drizzle_error(getTable()->getSession(), err);
2378
return ib_err_t_to_drizzle_error(err);
2386
2380
assert(res==0);
2392
2386
tuple= ib_tuple_clear(tuple);
2395
ret= read_row_from_haildb(getTable()->getSession(), buf, cursor, tuple,
2389
ret= read_row_from_haildb(buf, cursor, tuple, getTable(),
2397
2390
share->has_hidden_primary_key,
2398
2391
&hidden_autoinc_pkey_position);
2535
if (flag & HA_STATUS_CONST)
2537
for (unsigned int i = 0; i < getTable()->getShare()->sizeKeys(); i++)
2539
const char* index_name= getTable()->key_info[i].name;
2542
ha_rows rec_per_key;
2544
err= ib_get_index_stat_n_diff_key_vals(cursor, index_name,
2547
if (err != DB_SUCCESS)
2548
return ib_err_t_to_drizzle_error(getTable()->getSession(), err);
2550
for (unsigned int j=0; j < getTable()->key_info[i].key_parts; j++)
2552
if (n_diff[j+1] == 0)
2553
rec_per_key= stats.records;
2555
rec_per_key= stats.records / n_diff[j+1];
2557
/* We import this heuristic from ha_innodb, which says
2558
that MySQL favours table scans too much over index searches,
2559
so we pretend our index selectivity is 2 times better. */
2561
rec_per_key= rec_per_key / 2;
2563
if (rec_per_key == 0)
2566
getTable()->key_info[i].rec_per_key[j]=
2567
rec_per_key >= ~(ulong) 0 ? ~(ulong) 0 :
2568
(ulong) rec_per_key;
2592
2545
err= ib_cursor_open_table_using_id(table_id, transaction, &cursor);
2594
2547
if (err != DB_SUCCESS)
2595
return ib_err_t_to_drizzle_error(getTable()->getSession(), err);
2548
return ib_err_t_to_drizzle_error(err);
2610
2563
getShare()->getKeyInfo(keynr).name,
2612
2565
if (err != DB_SUCCESS)
2613
return ib_err_t_to_drizzle_error(getTable()->getSession(), err);
2566
return ib_err_t_to_drizzle_error(err);
2615
2568
err= ib_cursor_close(cursor);
2616
2569
assert(err == DB_SUCCESS);
2618
2571
err= ib_cursor_open_index_using_id(index_id, transaction, &cursor);
2620
2573
if (err != DB_SUCCESS)
2621
return ib_err_t_to_drizzle_error(getTable()->getSession(), err);
2574
return ib_err_t_to_drizzle_error(err);
2623
2576
cursor_is_sec_index= true;
2772
2725
if (err != DB_SUCCESS)
2774
return ib_err_t_to_drizzle_error(getTable()->getSession(), err);
2727
return ib_err_t_to_drizzle_error(err);
2777
2730
tuple= ib_tuple_clear(tuple);
2778
ret= read_row_from_haildb(getTable()->getSession(), buf, cursor, tuple,
2731
ret= read_row_from_haildb(buf, cursor, tuple, getTable(),
2780
2732
share->has_hidden_primary_key,
2781
2733
&hidden_autoinc_pkey_position,
2782
2734
(allocate_blobs)? &blobroot : NULL);
2856
int HailDBCursor::analyze(Session*)
2860
err= ib_update_table_statistics(cursor);
2862
return ib_err_t_to_drizzle_error(getTable()->getSession(), err);
2865
2808
int HailDBCursor::index_next(unsigned char *buf)
2867
2810
int ret= HA_ERR_END_OF_FILE;
2876
2819
tuple= ib_tuple_clear(tuple);
2877
ret= read_row_from_haildb(getTable()->getSession(), buf, cursor, tuple,
2820
ret= read_row_from_haildb(buf, cursor, tuple, getTable(),
2879
2821
share->has_hidden_primary_key,
2880
2822
&hidden_autoinc_pkey_position);
2903
2845
if (err == DB_END_OF_INDEX)
2904
2846
return HA_ERR_END_OF_FILE;
2906
return ib_err_t_to_drizzle_error(getTable()->getSession(), err);
2848
return ib_err_t_to_drizzle_error(err);
2910
2852
tuple= ib_tuple_clear(tuple);
2911
ret= read_row_from_haildb(getTable()->getSession(), buf, cursor, tuple,
2853
ret= read_row_from_haildb(buf, cursor, tuple, getTable(),
2913
2854
share->has_hidden_primary_key,
2914
2855
&hidden_autoinc_pkey_position);
2927
2868
err= ib_cursor_first(cursor);
2928
2869
if (err != DB_SUCCESS)
2929
return ib_err_t_to_drizzle_error(getTable()->getSession(), err);
2870
return ib_err_t_to_drizzle_error(err);
2931
2872
tuple= ib_tuple_clear(tuple);
2932
ret= read_row_from_haildb(getTable()->getSession(), buf, cursor, tuple,
2873
ret= read_row_from_haildb(buf, cursor, tuple, getTable(),
2934
2874
share->has_hidden_primary_key,
2935
2875
&hidden_autoinc_pkey_position);
2948
2888
err= ib_cursor_last(cursor);
2949
2889
if (err != DB_SUCCESS)
2950
return ib_err_t_to_drizzle_error(getTable()->getSession(), err);
2890
return ib_err_t_to_drizzle_error(err);
2952
2892
tuple= ib_tuple_clear(tuple);
2953
ret= read_row_from_haildb(getTable()->getSession(), buf, cursor, tuple,
2893
ret= read_row_from_haildb(buf, cursor, tuple, getTable(),
2955
2894
share->has_hidden_primary_key,
2956
2895
&hidden_autoinc_pkey_position);
2957
2896
advance_cursor= true;
3146
3085
va_start(args, fmt);
3147
if (not shutdown_in_progress)
3149
r= plugin::ErrorMessage::vprintf(error::WARN, fmt, args);
3086
if (! shutdown_in_progress)
3087
r= plugin::ErrorMessage::vprintf(NULL, ERRMSG_LVL_WARN, fmt, args);
3153
3089
vfprintf(stderr, fmt, args);
3157
3092
return (! r==true);
3536
3471
"Transactional Storage Engine using the HailDB Library",
3537
3472
PLUGIN_LICENSE_GPL,
3538
3473
haildb_init, /* Plugin Init */
3474
NULL, /* system variables */
3540
3475
init_options /* config options */
3542
3477
DRIZZLE_DECLARE_PLUGIN_END;