Esempio n. 1
0
void TableTailer::waitForEvents() {
    NdbEventOperation* op;
    LOG_INFO("create EventOperation for [" << mEventName << "]");
    if ((op = mNdbConnection->createEventOperation(mEventName.c_str())) == NULL)
        LOG_NDB_API_ERROR(mNdbConnection->getNdbError());

    NdbRecAttr * recAttr[mTable.mNoColumns];
    NdbRecAttr * recAttrPre[mTable.mNoColumns];

    // primary keys should always be a part of the result
    for (int i = 0; i < mTable.mNoColumns; i++) {
        recAttr[i] = op->getValue(mTable.mColumnNames[i].c_str());
        recAttrPre[i] = op->getPreValue(mTable.mColumnNames[i].c_str());
    }

    LOG_INFO("Execute");
    // This starts changes to "start flowing"
    if (op->execute())
        LOG_NDB_API_ERROR(op->getNdbError());
    while (true) {
        int r = mNdbConnection->pollEvents2(mPollMaxTimeToWait);
        if (r > 0) {
            while ((op = mNdbConnection->nextEvent2())) {
                NdbDictionary::Event::TableEvent event = op->getEventType2();
                if(event != NdbDictionary::Event::TE_EMPTY){
                    LOG_TRACE("Got Event [" << event << ","  << getEventName(event) << "] Epoch " << op->getEpoch());
                }
                switch (event) {
                    case NdbDictionary::Event::TE_INSERT:
                    case NdbDictionary::Event::TE_DELETE:
                    case NdbDictionary::Event::TE_UPDATE:
                        handleEvent(event, recAttrPre, recAttr);
                        break;
                    default:
                        break;
                }

            }
        }
        //boost::this_thread::sleep(boost::posix_time::milliseconds(mPollMaxTimeToWait));
    }

}
Esempio n. 2
0
static int copy_events(Ndb *ndb)
{
  DBUG_ENTER("copy_events");
  int r= 0;
  NdbDictionary::Dictionary * dict = ndb->getDictionary();
  while (1)
  {
    int res= ndb->pollEvents(1000); // wait for event or 1000 ms
    DBUG_PRINT("info", ("pollEvents res=%d", res));
    if (res <= 0)
    {
      break;
    }
    int error= 0;
    NdbEventOperation *pOp;
    while ((pOp= ndb->nextEvent(&error)))
    {
      char buf[1024];
      sprintf(buf, "%s_SHADOW", pOp->getTable()->getName());
      const NdbDictionary::Table *table= dict->getTable(buf);

      if (table == 0)
      {
	g_err << "unable to find table " << buf << endl;
	DBUG_RETURN(-1);
      }

      if (pOp->isOverrun())
      {
	g_err << "buffer overrun\n";
	DBUG_RETURN(-1);
      }
      r++;
      
      Uint32 gci= pOp->getGCI();

      if (!pOp->isConsistent()) {
	g_err << "A node failure has occured and events might be missing\n";
	DBUG_RETURN(-1);
      }
	
      int noRetries= 0;
      do
      {
	NdbTransaction *trans= ndb->startTransaction();
	if (trans == 0)
	{
	  g_err << "startTransaction failed "
		<< ndb->getNdbError().code << " "
		<< ndb->getNdbError().message << endl;
	  DBUG_RETURN(-1);
	}
	
	NdbOperation *op= trans->getNdbOperation(table);
	if (op == 0)
	{
	  g_err << "getNdbOperation failed "
		<< trans->getNdbError().code << " "
		<< trans->getNdbError().message << endl;
	  DBUG_RETURN(-1);
	}
	
	switch (pOp->getEventType()) {
	case NdbDictionary::Event::TE_INSERT:
	  if (op->insertTuple())
	  {
	    g_err << "insertTuple "
		  << op->getNdbError().code << " "
		  << op->getNdbError().message << endl;
	    DBUG_RETURN(-1);
	  }
	  break;
	case NdbDictionary::Event::TE_DELETE:
	  if (op->deleteTuple())
	  {
	    g_err << "deleteTuple "
		  << op->getNdbError().code << " "
		  << op->getNdbError().message << endl;
	    DBUG_RETURN(-1);
	  }
	  break;
	case NdbDictionary::Event::TE_UPDATE:
	  if (op->updateTuple())
	  {
	    g_err << "updateTuple "
		  << op->getNdbError().code << " "
		  << op->getNdbError().message << endl;
	    DBUG_RETURN(-1);
	  }
	  break;
	default:
	  abort();
	}
	
	{
	  for (const NdbRecAttr *pk= pOp->getFirstPkAttr(); pk; pk= pk->next())
	  {
	    if (pk->isNULL())
	    {
	      g_err << "internal error: primary key isNull()="
		    << pk->isNULL() << endl;
	      DBUG_RETURN(NDBT_FAILED);
	    }
	    if (op->equal(pk->getColumn()->getColumnNo(),pk->aRef()))
	    {
	      g_err << "equal " << pk->getColumn()->getColumnNo() << " "
		    << op->getNdbError().code << " "
		    << op->getNdbError().message << endl;
	      DBUG_RETURN(NDBT_FAILED);
	    }
	  }
	}
	
	switch (pOp->getEventType()) {
	case NdbDictionary::Event::TE_INSERT:
	{
	  for (const NdbRecAttr *data= pOp->getFirstDataAttr(); data; data= data->next())
	  {
	    if (data->isNULL() < 0 ||
		op->setValue(data->getColumn()->getColumnNo(),
			     data->isNULL() ? 0:data->aRef()))
	    {
	      g_err << "setValue(insert) " << data->getColumn()->getColumnNo() << " "
		    << op->getNdbError().code << " "
		    << op->getNdbError().message << endl;
	      DBUG_RETURN(-1);
	    }
	  }
	  break;
	}
	case NdbDictionary::Event::TE_DELETE:
	  break;
	case NdbDictionary::Event::TE_UPDATE:
	{
	  for (const NdbRecAttr *data= pOp->getFirstDataAttr(); data; data= data->next())
	  {
	    if (data->isNULL() >= 0 &&
		op->setValue(data->getColumn()->getColumnNo(),
			     data->isNULL() ? 0:data->aRef()))
	    {
	      g_err << "setValue(update) " << data->getColumn()->getColumnNo() << " "
		    << op->getNdbError().code << " "
		    << op->getNdbError().message << endl;
	      DBUG_RETURN(NDBT_FAILED);
	    }
	  }
	  break;
	}
	case NdbDictionary::Event::TE_ALL:
	  abort();
	}
	if (trans->execute(Commit) == 0)
	{
	  trans->close();
	  // everything ok
	  break;
	}
	if (noRetries++ == 10 ||
	    trans->getNdbError().status != NdbError::TemporaryError)
	{
	  g_err << "execute " << r << " failed "
		<< trans->getNdbError().code << " "
		<< trans->getNdbError().message << endl;
	  trans->close();
	  DBUG_RETURN(-1);
	}
	trans->close();
	NdbSleep_MilliSleep(100); // sleep before retying
      } while(1);
    } // for
    if (error)
    {
      g_err << "nextEvent()\n";
      DBUG_RETURN(-1);
    }
  } // while(1)
  DBUG_RETURN(r);
}
Esempio n. 3
0
int main(int argc, char** argv){
  NDB_INIT(argv[0]);

  const char *load_default_groups[]= { "mysql_cluster",0 };
  load_defaults("my",load_default_groups,&argc,&argv);
  int ho_error;
#ifndef DBUG_OFF
  opt_debug= "d:t:O,/tmp/ndb_desc.trace";
#endif
  if ((ho_error=handle_options(&argc, &argv, my_long_options, 
			       ndb_std_get_one_option)))
    return NDBT_ProgramExit(NDBT_WRONGARGS);

  for (int i = 0; i<_loop; i++)
  {
    Ndb_cluster_connection con(opt_connect_str);
    if(con.connect(12, 5, 1) != 0)
    {
      ndbout << "Unable to connect to management server." << endl;
      return NDBT_ProgramExit(NDBT_FAILED);
    }
    if (con.wait_until_ready(30,30) != 0)
    {
      ndbout << "Cluster nodes not ready in 30 seconds." << endl;
      return NDBT_ProgramExit(NDBT_FAILED);
    }
    
    Ndb MyNdb(&con, "TEST_DB");
    if(MyNdb.init() != 0){
      ERR(MyNdb.getNdbError());
      return NDBT_ProgramExit(NDBT_FAILED);
    }

    Vector<NdbEventOperation*> ops;
    const NdbDictionary::Dictionary * dict= MyNdb.getDictionary();
    for (int j = 0; j < argc; j++) 
    {
      const NdbDictionary::Table * pTab = dict->getTable(argv[j]);
      if (pTab == 0)
      {
        ndbout_c("Failed to retreive table: \"%s\"", argv[j]);
      }

      BaseString tmp;
      tmp.appfmt("EV-%s", argv[j]);
      NdbEventOperation* pOp = MyNdb.createEventOperation(tmp.c_str());
      if ( pOp == NULL ) 
      {
        ndbout << "Event operation creation failed: " << 
          MyNdb.getNdbError() << endl;
        return NDBT_ProgramExit(NDBT_FAILED);
      }

      for (int a = 0; a < pTab->getNoOfColumns(); a++) 
      {
        pOp->getValue(pTab->getColumn(a)->getName());
        pOp->getPreValue(pTab->getColumn(a)->getName());
      }
      
      if (pOp->execute())
      { 
        ndbout << "operation execution failed: " << pOp->getNdbError()
               << endl;
        return NDBT_ProgramExit(NDBT_FAILED);
      }
      ops.push_back(pOp);
    }
    
    if (_sleep)
    {
      NdbSleep_MilliSleep(10 + rand() % _sleep);
    }
    
    for (Uint32 i = 0; i<ops.size(); i++)
    {
      switch(_drop){
      case 0:
        break;
      do_drop:
      case 1:
        if (MyNdb.dropEventOperation(ops[i]))
        {
          ndbout << "drop event operation failed " 
                 << MyNdb.getNdbError() << endl;
          return NDBT_ProgramExit(NDBT_FAILED);
        }
        break;
      default:
        if ((rand() % 100) > 50)
          goto do_drop;
      }
    }
  }
  
  return NDBT_ProgramExit(NDBT_OK);
}
Esempio n. 4
0
int runEventApplier(NDBT_Context* ctx, NDBT_Step* step)
{
  DBUG_ENTER("runEventApplier");

  int records = ctx->getNumRecords();
  int loops = ctx->getNumLoops();
  const NdbDictionary::Table * table= ctx->getTab();
  char buf[1024];

  sprintf(buf, "%s_SHADOW", table->getName());
  const NdbDictionary::Table * table_shadow;
  if ((table_shadow = GETNDB(step)->getDictionary()->getTable(buf)) == 0)
  {
    g_err << "Unable to get table " << buf << endl;
    DBUG_RETURN(NDBT_FAILED);
  }

  sprintf(buf, "%s_EVENT", table->getName());
  NdbEventOperation    *pOp;
  pOp = GETNDB(step)->createEventOperation(buf, 10*records);
  if ( pOp == NULL ) {
    g_err << "Event operation creation failed on %s" << buf << endl;
    DBUG_RETURN(NDBT_FAILED);
  }

  int i;
  int n_columns= table->getNoOfColumns();
  NdbRecAttr* recAttr[1024];
  NdbRecAttr* recAttrPre[1024];
  for (i = 0; i < n_columns; i++) {
    recAttr[i]    = pOp->getValue(table->getColumn(i)->getName());
    recAttrPre[i] = pOp->getPreValue(table->getColumn(i)->getName());
  }

  if (pOp->execute()) { // This starts changes to "start flowing"
    g_err << "execute operation execution failed: \n";
    g_err << pOp->getNdbError().code << " "
	  << pOp->getNdbError().message << endl;
    DBUG_RETURN(NDBT_FAILED);
  }

  int r= 0;
  int res;
  while (r < 10*records){
    //printf("now waiting for event...\n");
    res= GETNDB(step)->pollEvents(1000); // wait for event or 1000 ms
    if (res <= 0)
    {
      ndbout_c("********************");
      continue;
    }

    //printf("got data! %d\n", r);
    int overrun= 0;
    while (pOp->next(&overrun) > 0)
    {
      if (overrun)
      {
	g_err << "buffer overrun\n";
	DBUG_RETURN(NDBT_FAILED);
      }
      r++;

      Uint32 gci= pOp->getGCI();

      if (!pOp->isConsistent()) {
	g_err << "A node failure has occured and events might be missing\n";
	DBUG_RETURN(NDBT_FAILED);
      }

      int noRetries= 0;
      do
      {
	NdbTransaction *trans= GETNDB(step)->startTransaction();
	if (trans == 0)
	{
	  g_err << "startTransaction failed "
		<< GETNDB(step)->getNdbError().code << " "
		<< GETNDB(step)->getNdbError().message << endl;
	  DBUG_RETURN(NDBT_FAILED);
	}
	
	NdbOperation *op= trans->getNdbOperation(table_shadow);
	if (op == 0)
	{
	  g_err << "getNdbOperation failed "
		<< trans->getNdbError().code << " "
		<< trans->getNdbError().message << endl;
	  DBUG_RETURN(NDBT_FAILED);
	}
	
	switch (pOp->getEventType()) {
	case NdbDictionary::Event::TE_INSERT:
	  if (op->insertTuple())
	  {
	    g_err << "insertTuple "
		  << op->getNdbError().code << " "
		  << op->getNdbError().message << endl;
	    DBUG_RETURN(NDBT_FAILED);
	  }
	  break;
	case NdbDictionary::Event::TE_DELETE:
	  if (op->deleteTuple())
	  {
	    g_err << "deleteTuple "
		  << op->getNdbError().code << " "
		  << op->getNdbError().message << endl;
	    DBUG_RETURN(NDBT_FAILED);
	  }
	  break;
	case NdbDictionary::Event::TE_UPDATE:
	  if (op->updateTuple())
	  {
	    g_err << "updateTuple "
		  << op->getNdbError().code << " "
		  << op->getNdbError().message << endl;
	    DBUG_RETURN(NDBT_FAILED);
	  }
	  break;
	default:
	  abort();
	}

	for (i= 0; i < n_columns; i++)
	{
	  if (recAttr[i]->isNULL())
	  {
	    if (table->getColumn(i)->getPrimaryKey())
	    {
	      g_err << "internal error: primary key isNull()="
		    << recAttr[i]->isNULL() << endl;
	      DBUG_RETURN(NDBT_FAILED);
	    }
	    switch (pOp->getEventType()) {
	    case NdbDictionary::Event::TE_INSERT:
	      if (recAttr[i]->isNULL() < 0)
	      {
		g_err << "internal error: missing value for insert\n";
		DBUG_RETURN(NDBT_FAILED);
	      }
	      break;
	    case NdbDictionary::Event::TE_DELETE:
	      break;
	    case NdbDictionary::Event::TE_UPDATE:
	      break;
	    default:
	      abort();
	    }
	  }
	  if (table->getColumn(i)->getPrimaryKey() &&
	      op->equal(i,recAttr[i]->aRef()))
	  {
	    g_err << "equal " << i << " "
		  << op->getNdbError().code << " "
		  << op->getNdbError().message << endl;
	    DBUG_RETURN(NDBT_FAILED);
	  }
	}
	
	switch (pOp->getEventType()) {
	case NdbDictionary::Event::TE_INSERT:
	  for (i= 0; i < n_columns; i++)
	  {
	    if (!table->getColumn(i)->getPrimaryKey() &&
		op->setValue(i,recAttr[i]->isNULL() ? 0:recAttr[i]->aRef()))
	    {
	      g_err << "setValue(insert) " << i << " "
		    << op->getNdbError().code << " "
		    << op->getNdbError().message << endl;
	      DBUG_RETURN(NDBT_FAILED);
	    }
	  }
	  break;
	case NdbDictionary::Event::TE_DELETE:
	  break;
	case NdbDictionary::Event::TE_UPDATE:
	  for (i= 0; i < n_columns; i++)
	  {
	    if (!table->getColumn(i)->getPrimaryKey() &&
		recAttr[i]->isNULL() >= 0 &&
		op->setValue(i,recAttr[i]->isNULL() ? 0:recAttr[i]->aRef()))
	    {
	      g_err << "setValue(update) " << i << " "
		    << op->getNdbError().code << " "
		    << op->getNdbError().message << endl;
	      DBUG_RETURN(NDBT_FAILED);
	    }
	  }
	  break;
	case NdbDictionary::Event::TE_ALL:
	  abort();
	}
	if (trans->execute(Commit) == 0)
	{
	  trans->close();
	  // everything ok
	  break;
	}
	if (noRetries++ == 10 ||
	    trans->getNdbError().status != NdbError::TemporaryError)
	{
	  g_err << "execute " << r << " failed "
		<< trans->getNdbError().code << " "
		<< trans->getNdbError().message << endl;
	  trans->close();
	  DBUG_RETURN(NDBT_FAILED);
	}
	trans->close();
	NdbSleep_MilliSleep(100); // sleep before retying
      } while(1);
    }
  }

  if (GETNDB(step)->dropEventOperation(pOp)) {
    g_err << "dropEventOperation execution failed "
	  << GETNDB(step)->getNdbError().code << " "
	  << GETNDB(step)->getNdbError().message << endl;
    DBUG_RETURN(NDBT_FAILED);
  }
  
  DBUG_RETURN(NDBT_OK);
}
int main(int argc, char** argv)
{
  if (argc < 3)
  {
    std::cout << "Arguments are <connect_string cluster> <timeout> [m(merge events)|d(debug)].\n";
    exit(-1);
  }
  const char *connectstring = argv[1];
  int timeout = atoi(argv[2]);
  ndb_init();
  bool merge_events = argc > 3 && strchr(argv[3], 'm') != 0;
#ifdef VM_TRACE
  bool dbug = argc > 3 && strchr(argv[3], 'd') != 0;
  if (dbug) DBUG_PUSH("d:t:");
  if (dbug) putenv("API_SIGNAL_LOG=-");
#endif

  Ndb_cluster_connection *cluster_connection=
    new Ndb_cluster_connection(connectstring); // Object representing the cluster

  int r= cluster_connection->connect(5 /* retries               */,
				     3 /* delay between retries */,
				     1 /* verbose               */);
  if (r > 0)
  {
    std::cout
      << "Cluster connect failed, possibly resolved with more retries.\n";
    exit(-1);
  }
  else if (r < 0)
  {
    std::cout
      << "Cluster connect failed.\n";
    exit(-1);
  }
					   
  if (cluster_connection->wait_until_ready(30,30))
  {
    std::cout << "Cluster was not ready within 30 secs." << std::endl;
    exit(-1);
  }

  Ndb* myNdb= new Ndb(cluster_connection,
		      "TEST_DB");  // Object representing the database

  if (myNdb->init() == -1) APIERROR(myNdb->getNdbError());

  const char *eventName= "CHNG_IN_t0";
  const char *eventTableName= "t0";
  const int noEventColumnName= 5;
  const char *eventColumnName[noEventColumnName]=
    {"c0",
     "c1",
     "c2",
     "c3",
     "c4"
    };
  
  // Create events
  myCreateEvent(myNdb,
		eventName,
		eventTableName,
		eventColumnName,
		noEventColumnName,
                merge_events);

  // Normal values and blobs are unfortunately handled differently..
  typedef union { NdbRecAttr* ra; NdbBlob* bh; } RA_BH;

  int i, j, k, l;
  j = 0;
  while (j < timeout) {

    // Start "transaction" for handling events
    NdbEventOperation* op;
    printf("create EventOperation\n");
    if ((op = myNdb->createEventOperation(eventName)) == NULL)
      APIERROR(myNdb->getNdbError());
    op->mergeEvents(merge_events);

    printf("get values\n");
    RA_BH recAttr[noEventColumnName];
    RA_BH recAttrPre[noEventColumnName];
    // primary keys should always be a part of the result
    for (i = 0; i < noEventColumnName; i++) {
      if (i < 4) {
        recAttr[i].ra    = op->getValue(eventColumnName[i]);
        recAttrPre[i].ra = op->getPreValue(eventColumnName[i]);
      } else if (merge_events) {
        recAttr[i].bh    = op->getBlobHandle(eventColumnName[i]);
        recAttrPre[i].bh = op->getPreBlobHandle(eventColumnName[i]);
      }
    }

    // set up the callbacks
    printf("execute\n");
    // This starts changes to "start flowing"
    if (op->execute())
      APIERROR(op->getNdbError());

    NdbEventOperation* the_op = op;

    i= 0;
    while (i < timeout) {
      // printf("now waiting for event...\n");
      int r = myNdb->pollEvents(1000); // wait for event or 1000 ms
      if (r > 0) {
	// printf("got data! %d\n", r);
	while ((op= myNdb->nextEvent())) {
          assert(the_op == op);
	  i++;
	  switch (op->getEventType()) {
	  case NdbDictionary::Event::TE_INSERT:
	    printf("%u INSERT", i);
	    break;
	  case NdbDictionary::Event::TE_DELETE:
	    printf("%u DELETE", i);
	    break;
	  case NdbDictionary::Event::TE_UPDATE:
	    printf("%u UPDATE", i);
	    break;
	  default:
	    abort(); // should not happen
	  }
          printf(" gci=%d\n", (int)op->getGCI());
          for (k = 0; k <= 1; k++) {
            printf(k == 0 ? "post: " : "pre : ");
            for (l = 0; l < noEventColumnName; l++) {
              if (l < 4) {
                NdbRecAttr* ra = k == 0 ? recAttr[l].ra : recAttrPre[l].ra;
                if (ra->isNULL() >= 0) { // we have a value
                  if (ra->isNULL() == 0) { // we have a non-null value
                    if (l < 2)
                      printf("%-5u", ra->u_32_value());
                    else
                      printf("%-5.4s", ra->aRef());
                  } else
                    printf("%-5s", "NULL");
                } else
                  printf("%-5s", "-"); // no value
              } else if (merge_events) {
                int isNull;
                NdbBlob* bh = k == 0 ? recAttr[l].bh : recAttrPre[l].bh;
                bh->getDefined(isNull);
                if (isNull >= 0) { // we have a value
                  if (! isNull) { // we have a non-null value
                    Uint64 length = 0;
                    bh->getLength(length);
                    // read into buffer
                    unsigned char* buf = new unsigned char [length];
                    memset(buf, 'X', length);
                    Uint32 n = length;
                    bh->readData(buf, n); // n is in/out
                    assert(n == length);
                    // pretty-print
                    bool first = true;
                    Uint32 i = 0;
                    while (i < n) {
                      unsigned char c = buf[i++];
                      Uint32 m = 1;
                      while (i < n && buf[i] == c)
                        i++, m++;
                      if (! first)
                        printf("+");
                      printf("%u%c", m, c);
                      first = false;
                    }
                    printf("[%u]", n);
                    delete [] buf;
                  } else
                    printf("%-5s", "NULL");
                } else
                  printf("%-5s", "-"); // no value
              }
            }
            printf("\n");
          }
	}
      } else
	printf("timed out (%i)\n", timeout);
    }
    // don't want to listen to events anymore
    if (myNdb->dropEventOperation(the_op)) APIERROR(myNdb->getNdbError());
    the_op = 0;

    j++;
  }

  {
    NdbDictionary::Dictionary *myDict = myNdb->getDictionary();
    if (!myDict) APIERROR(myNdb->getNdbError());
    // remove event from database
    if (myDict->dropEvent(eventName)) APIERROR(myDict->getNdbError());
  }

  delete myNdb;
  delete cluster_connection;
  ndb_end(0);
  return 0;
}
Esempio n. 6
0
int 
main(int argc, const char** argv){
  ndb_init();

  
  int _help = 0;
  const char* db = 0;
  const char* connectstring1 = 0;
  const char* connectstring2 = 0;

  struct getargs args[] = {
    { "connectstring1", 'c',
      arg_string, &connectstring1, "connectstring1", "" },
    { "connectstring2", 'C',
      arg_string, &connectstring2, "connectstring2", "" },
    { "database", 'd', arg_string, &db, "Database", "" },
    { "usage", '?', arg_flag, &_help, "Print help", "" }
  };
  int num_args = sizeof(args) / sizeof(args[0]);
  int optind = 0, i;
  char desc[] = 
    "<tabname>+ \nThis program listen to events on specified tables\n";
  
  if(getarg(args, num_args, argc, argv, &optind) ||
     argv[optind] == NULL || _help) {
    arg_printusage(args, num_args, argv[0], desc);
    return NDBT_ProgramExit(NDBT_WRONGARGS);
  }

  // Connect to Ndb
  Ndb_cluster_connection con(connectstring1);
  if(con.connect(12, 5, 1) != 0)
  {
    return NDBT_ProgramExit(NDBT_FAILED);
  }
  Ndb MyNdb( &con, db ? db : "TEST_DB" );

  if(MyNdb.init() != 0){
    ERR(MyNdb.getNdbError());
    return NDBT_ProgramExit(NDBT_FAILED);
  }

  // Connect to Ndb and wait for it to become ready
  while(MyNdb.waitUntilReady() != 0)
    ndbout << "Waiting for ndb to become ready..." << endl;

  Ndb_cluster_connection *con2 = NULL;
  Ndb *ndb2 =  NULL;
  if (connectstring2)
  {
    con2 = new Ndb_cluster_connection(connectstring2);

    if(con2->connect(12, 5, 1) != 0)
    {
      return NDBT_ProgramExit(NDBT_FAILED);
    }
    ndb2 = new Ndb( con2, db ? db : "TEST_DB" );

    if(ndb2->init() != 0){
      ERR(ndb2->getNdbError());
      return NDBT_ProgramExit(NDBT_FAILED);
    }

    // Connect to Ndb and wait for it to become ready
    while(ndb2->waitUntilReady() != 0)
      ndbout << "Waiting for ndb to become ready..." << endl;
  }

  int result = 0;
  
  NdbDictionary::Dictionary *myDict = MyNdb.getDictionary();
  Vector<NdbDictionary::Event*> events;
  Vector<NdbEventOperation*> event_ops;
  int sz = 0;
  for(i= optind; i<argc; i++)
  {
    const NdbDictionary::Table* table= myDict->getTable(argv[i]);
    if(!table)
    {
      ndbout_c("Could not find table: %s, skipping", argv[i]);
      continue;
    }

    BaseString name;
    name.appfmt("EV-%s", argv[i]);
    NdbDictionary::Event *myEvent= new NdbDictionary::Event(name.c_str());
    myEvent->setTable(table->getName());
    myEvent->addTableEvent(NdbDictionary::Event::TE_ALL); 
    for(int a = 0; a < table->getNoOfColumns(); a++){
      myEvent->addEventColumn(a);
    }

    if (myDict->createEvent(* myEvent))
    {
      if(myDict->getNdbError().classification == NdbError::SchemaObjectExists) 
      {
	g_info << "Event creation failed event exists. Removing...\n";
	if (myDict->dropEvent(name.c_str()))
	{
	  g_err << "Failed to drop event: " << myDict->getNdbError() << endl;
	  result = 1;
	  goto end;
	}
	// try again
	if (myDict->createEvent(* myEvent)) 
	{
	  g_err << "Failed to create event: " << myDict->getNdbError() << endl;
	  result = 1;
	  goto end;
	}
      }
      else
      {
	g_err << "Failed to create event: " << myDict->getNdbError() << endl;
	result = 1;
	goto end;
      }
    }
    
    events.push_back(myEvent);

    NdbEventOperation* pOp = MyNdb.createEventOperation(name.c_str());
    if ( pOp == NULL ) {
      g_err << "Event operation creation failed" << endl;
      result = 1;
      goto end;
    }

    event_values.push_back(Vector<NdbRecAttr *>());
    event_pre_values.push_back(Vector<NdbRecAttr *>());
    for (int a = 0; a < table->getNoOfColumns(); a++) 
    {
      event_values[sz].
        push_back(pOp->getValue(table->getColumn(a)->getName()));
      event_pre_values[sz].
        push_back(pOp->getPreValue(table->getColumn(a)->getName()));
    }
    event_ops.push_back(pOp);
    {
      struct Table_info ti;
      ti.id = sz;
      table_infos.push_back(ti);
    }
    pOp->setCustomData((void *)&table_infos[sz]);
    sz++;
  }

  for(i= 0; i<(int)event_ops.size(); i++)
  {
    if (event_ops[i]->execute())
    { 
      g_err << "operation execution failed: " << event_ops[i]->getNdbError()
	    << endl;
      result = 1;
      goto end;
    }
  }

  struct Trans_arg trans_arg;
  while(true)
  {
    while(MyNdb.pollEvents(100) == 0);
    
    NdbEventOperation* pOp= MyNdb.nextEvent();
    while(pOp)
    {
      Uint64 gci= pOp->getGCI();
      Uint64 cnt_i= 0, cnt_u= 0, cnt_d= 0;
      if (ndb2)
        do_begin(ndb2, trans_arg);
      do
      {
	switch(pOp->getEventType())
	{
	case NdbDictionary::Event::TE_INSERT:
	  cnt_i++;
          if (ndb2)
            do_insert(trans_arg, pOp);
	  break;
	case NdbDictionary::Event::TE_DELETE:
	  cnt_d++;
          if (ndb2)
            do_delete(trans_arg, pOp);
	  break;
	case NdbDictionary::Event::TE_UPDATE:
	  cnt_u++;
          if (ndb2)
            do_update(trans_arg, pOp);
	  break;
	case NdbDictionary::Event::TE_CLUSTER_FAILURE:
	  break;
	case NdbDictionary::Event::TE_ALTER:
	  break;
	case NdbDictionary::Event::TE_DROP:
	  break;
	case NdbDictionary::Event::TE_NODE_FAILURE:
	  break;
	case NdbDictionary::Event::TE_SUBSCRIBE:
	case NdbDictionary::Event::TE_UNSUBSCRIBE:
	  break;
	default:
	  /* We should REALLY never get here. */
	  ndbout_c("Error: unknown event type: %u", 
		   (Uint32)pOp->getEventType());
	  abort();
	}
      } while ((pOp= MyNdb.nextEvent()) && gci == pOp->getGCI());
      if (ndb2)
        do_commit(trans_arg);
      ndbout_c("GCI: %lld events: %lld(I) %lld(U) %lld(D)", gci, cnt_i, cnt_u, cnt_d);
    }
  }
end:
  for(i= 0; i<(int)event_ops.size(); i++)
    MyNdb.dropEventOperation(event_ops[i]);

  if (ndb2)
    delete ndb2;
  if (con2)
    delete con2;
  return NDBT_ProgramExit(NDBT_OK);
}