PageRenderTime 65ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/replicator/src/java/com/continuent/tungsten/replicator/thl/serializer/ProtobufSerializer.java

http://tungsten-replicator.googlecode.com/
Java | 1155 lines | 987 code | 93 blank | 75 comment | 159 complexity | e3fd2f7f0bb1567ebdd03f0cc46e1865 MD5 | raw file
Possible License(s): GPL-2.0
  1. /**
  2. * Tungsten Scale-Out Stack
  3. * Copyright (C) 2010-2013 Continuent Inc.
  4. * Contact: tungsten@continuent.org
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of version 2 of the GNU General Public License as
  8. * published by the Free Software Foundation.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program; if not, write to the Free Software
  17. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
  18. *
  19. * Initial developer(s): Stephane Giron
  20. * Contributor(s):
  21. */
  22. package com.continuent.tungsten.replicator.thl.serializer;
  23. import java.io.IOException;
  24. import java.io.InputStream;
  25. import java.io.OutputStream;
  26. import java.io.Serializable;
  27. import java.math.BigDecimal;
  28. import java.math.BigInteger;
  29. import java.sql.Clob;
  30. import java.sql.Date;
  31. import java.sql.SQLException;
  32. import java.sql.Time;
  33. import java.sql.Timestamp;
  34. import java.sql.Types;
  35. import java.util.ArrayList;
  36. import java.util.List;
  37. import javax.sql.rowset.serial.SerialException;
  38. import org.apache.log4j.Logger;
  39. import com.continuent.tungsten.replicator.dbms.DBMSData;
  40. import com.continuent.tungsten.replicator.dbms.LoadDataFileFragment;
  41. import com.continuent.tungsten.replicator.dbms.LoadDataFileQuery;
  42. import com.continuent.tungsten.replicator.dbms.OneRowChange;
  43. import com.continuent.tungsten.replicator.dbms.OneRowChange.ColumnSpec;
  44. import com.continuent.tungsten.replicator.dbms.OneRowChange.ColumnVal;
  45. import com.continuent.tungsten.replicator.dbms.RowChangeData;
  46. import com.continuent.tungsten.replicator.dbms.RowIdData;
  47. import com.continuent.tungsten.replicator.dbms.StatementData;
  48. import com.continuent.tungsten.replicator.event.DBMSEvent;
  49. import com.continuent.tungsten.replicator.event.ReplDBMSEvent;
  50. import com.continuent.tungsten.replicator.event.ReplDBMSFilteredEvent;
  51. import com.continuent.tungsten.replicator.event.ReplEvent;
  52. import com.continuent.tungsten.replicator.event.ReplOption;
  53. import com.continuent.tungsten.replicator.extractor.mysql.SerialBlob;
  54. import com.continuent.tungsten.replicator.thl.THLEvent;
  55. import com.continuent.tungsten.replicator.thl.protobuf.TungstenProtos.Header;
  56. import com.continuent.tungsten.replicator.thl.protobuf.TungstenProtos.ProtobufEventOption;
  57. import com.continuent.tungsten.replicator.thl.protobuf.TungstenProtos.ProtobufLoadDataFileFragment;
  58. import com.continuent.tungsten.replicator.thl.protobuf.TungstenProtos.ProtobufLoadDataFileQuery;
  59. import com.continuent.tungsten.replicator.thl.protobuf.TungstenProtos.ProtobufOneChange;
  60. import com.continuent.tungsten.replicator.thl.protobuf.TungstenProtos.ProtobufOneChange.Builder;
  61. import com.continuent.tungsten.replicator.thl.protobuf.TungstenProtos.ProtobufOneRowChange;
  62. import com.continuent.tungsten.replicator.thl.protobuf.TungstenProtos.ProtobufOneRowChange.ActionType;
  63. import com.continuent.tungsten.replicator.thl.protobuf.TungstenProtos.ProtobufOneRowChange.ProtobufColumnSpec;
  64. import com.continuent.tungsten.replicator.thl.protobuf.TungstenProtos.ProtobufOneRowChange.ProtobufRowValue;
  65. import com.continuent.tungsten.replicator.thl.protobuf.TungstenProtos.ProtobufOneRowChange.ProtobufRowValue.ProtobufColumnVal;
  66. import com.continuent.tungsten.replicator.thl.protobuf.TungstenProtos.ProtobufOneRowChange.ProtobufRowValue.ProtobufColumnVal.Type;
  67. import com.continuent.tungsten.replicator.thl.protobuf.TungstenProtos.ProtobufReplDBMSEvent;
  68. import com.continuent.tungsten.replicator.thl.protobuf.TungstenProtos.ProtobufRowChangeData;
  69. import com.continuent.tungsten.replicator.thl.protobuf.TungstenProtos.ProtobufRowIdData;
  70. import com.continuent.tungsten.replicator.thl.protobuf.TungstenProtos.ProtobufStatementData;
  71. import com.google.protobuf.ByteString;
  72. import com.google.protobuf.Message;
  73. /**
  74. * @author <a href="mailto:stephane.giron@continuent.com">Stephane Giron</a>
  75. * @version 1.0
  76. */
  77. public class ProtobufSerializer implements Serializer
  78. {
  79. static Logger logger = Logger.getLogger(ProtobufSerializer.class);
  80. private int deserializeCount = 0;
  81. private long globalDeserTime = 0;
  82. private StringBuffer trace = new StringBuffer();
  83. /**
  84. * {@inheritDoc}
  85. *
  86. * @see com.continuent.tungsten.replicator.thl.serializer.Serializer#deserializeEvent(java.io.InputStream)
  87. */
  88. public THLEvent deserializeEvent(InputStream inStream) throws IOException
  89. {
  90. long startTime = 0;
  91. if (logger.isDebugEnabled())
  92. startTime = System.nanoTime();
  93. try
  94. {
  95. ReplDBMSEvent event = null;
  96. Header header = deserializeHeader(inStream);
  97. Timestamp sourceTstamp = new Timestamp(header.getSourceTstamp());
  98. if (header.getFilteredEvent())
  99. {
  100. event = new ReplDBMSFilteredEvent(header.getSeqno(),
  101. (short) header.getFragno(), header.getSeqnoEnd(),
  102. (short) header.getFragnoEnd(), header.getLastFrag(),
  103. header.getEventId(), header.getSourceId(),
  104. sourceTstamp, header.getEpochNumber());
  105. }
  106. else
  107. {
  108. ProtobufReplDBMSEvent protobufReplDBMSEvent = ProtobufReplDBMSEvent
  109. .parseDelimitedFrom(inStream);
  110. ArrayList<DBMSData> data = new ArrayList<DBMSData>();
  111. List<ProtobufOneChange> changeList = protobufReplDBMSEvent
  112. .getChangeList();
  113. for (ProtobufOneChange protobufOneChange : changeList)
  114. {
  115. DBMSData statementChange = deserializeOneChange(protobufOneChange);
  116. data.add(statementChange);
  117. }
  118. DBMSEvent dbmsEvent = new DBMSEvent(header.getEventId(), null,
  119. data, sourceTstamp);
  120. event = new ReplDBMSEvent(header.getSeqno(),
  121. (short) header.getFragno(), header.getLastFrag(),
  122. header.getSourceId(), header.getEpochNumber(),
  123. sourceTstamp, dbmsEvent);
  124. for (ProtobufEventOption protobufEventOption : protobufReplDBMSEvent
  125. .getMetadataList())
  126. {
  127. dbmsEvent.addMetadataOption(protobufEventOption.getName(),
  128. protobufEventOption.getValue());
  129. }
  130. for (ProtobufEventOption protobufEventOption : protobufReplDBMSEvent
  131. .getOptionsList())
  132. {
  133. dbmsEvent.addOption(protobufEventOption.getName(),
  134. protobufEventOption.getValue());
  135. }
  136. }
  137. return new THLEvent(header.getEventId(), event);
  138. }
  139. finally
  140. {
  141. if (logger.isDebugEnabled())
  142. {
  143. deserializeCount++;
  144. globalDeserTime += System.nanoTime() - startTime;
  145. if (deserializeCount % 1000 == 0)
  146. {
  147. logger.debug("Average extraction time = " + globalDeserTime
  148. / 1000 + " ns/event.");
  149. deserializeCount = 0;
  150. globalDeserTime = 0;
  151. }
  152. }
  153. }
  154. }
  155. /**
  156. * {@inheritDoc}
  157. *
  158. * @see com.continuent.tungsten.replicator.thl.serializer.Serializer#serializeEvent(com.continuent.tungsten.replicator.thl.THLEvent,
  159. * java.io.OutputStream)
  160. */
  161. public void serializeEvent(THLEvent thlEvent, OutputStream outStream)
  162. throws IOException
  163. {
  164. ProtobufReplDBMSEvent.Builder protoEventBuilder = ProtobufReplDBMSEvent
  165. .newBuilder();
  166. serializeHeader(thlEvent, outStream);
  167. ReplEvent event = thlEvent.getReplEvent();
  168. if (!(event instanceof ReplDBMSFilteredEvent))
  169. {
  170. // ReplDBMSFilteredEvent are serialized within the header
  171. ReplDBMSEvent ev = (ReplDBMSEvent) event;
  172. ProtobufEventOption.Builder metadataBuilder;
  173. for (ReplOption replOption : ev.getDBMSEvent().getMetadata())
  174. {
  175. metadataBuilder = ProtobufEventOption.newBuilder();
  176. metadataBuilder.setName(replOption.getOptionName());
  177. metadataBuilder.setValue(replOption.getOptionValue());
  178. protoEventBuilder.addMetadata(metadataBuilder);
  179. }
  180. for (ReplOption replOption : ev.getDBMSEvent().getOptions())
  181. {
  182. metadataBuilder = ProtobufEventOption.newBuilder();
  183. metadataBuilder.setName(replOption.getOptionName());
  184. metadataBuilder.setValue(replOption.getOptionValue());
  185. protoEventBuilder.addOptions(metadataBuilder);
  186. }
  187. ArrayList<DBMSData> evData = ev.getData();
  188. // Initializing protobuf builder
  189. ProtobufOneChange.Builder oneChangeBuilder = null;
  190. ProtobufRowChangeData.Builder rowDataBuilder = null;
  191. for (DBMSData dbmsData : evData)
  192. {
  193. oneChangeBuilder = ProtobufOneChange.newBuilder();
  194. if (dbmsData instanceof RowChangeData)
  195. {
  196. oneChangeBuilder.setType(ProtobufOneChange.Type.ROW_DATA);
  197. RowChangeData rowEv = (RowChangeData) dbmsData;
  198. rowDataBuilder = ProtobufRowChangeData.newBuilder();
  199. serializeRows(rowDataBuilder, rowEv);
  200. oneChangeBuilder.setData(rowDataBuilder);
  201. }
  202. else if (dbmsData instanceof LoadDataFileQuery)
  203. {
  204. oneChangeBuilder
  205. .setType(ProtobufOneChange.Type.LOAD_DATA_FILE_QUERY);
  206. LoadDataFileQuery data = (LoadDataFileQuery) dbmsData;
  207. serializeLoadDataFileQuery(oneChangeBuilder, data);
  208. }
  209. else if (dbmsData instanceof LoadDataFileFragment)
  210. {
  211. oneChangeBuilder
  212. .setType(ProtobufOneChange.Type.LOAD_DATA_FILE_FRAGMENT);
  213. LoadDataFileFragment data = (LoadDataFileFragment) dbmsData;
  214. serializeLoadDataFileFragment(oneChangeBuilder, data);
  215. }
  216. else if (dbmsData instanceof StatementData)
  217. {
  218. oneChangeBuilder
  219. .setType(ProtobufOneChange.Type.STATEMENT_DATA);
  220. StatementData data = (StatementData) dbmsData;
  221. oneChangeBuilder
  222. .setStatement((ProtobufStatementData.Builder) serializeStatement(data));
  223. }
  224. else if (dbmsData instanceof RowIdData)
  225. {
  226. RowIdData data = (RowIdData) dbmsData;
  227. serializeRowIdData(oneChangeBuilder, data);
  228. }
  229. else
  230. {
  231. if (dbmsData == null)
  232. logger.warn("Trying to serialize null object");
  233. else
  234. logger.warn("Type " + dbmsData.getClass().getName()
  235. + " is not implemented yet.");
  236. continue;
  237. }
  238. protoEventBuilder.addChange(oneChangeBuilder);
  239. }
  240. }
  241. protoEventBuilder.build().writeDelimitedTo(outStream);
  242. outStream.flush();
  243. }
  244. private DBMSData deserializeOneChange(ProtobufOneChange protobufOneChange)
  245. {
  246. logger.debug("Event type is : " + protobufOneChange.getType());
  247. if (protobufOneChange.getType().equals(
  248. ProtobufOneChange.Type.STATEMENT_DATA))
  249. {
  250. StatementData statement = deserializeStatement(protobufOneChange
  251. .getStatement());
  252. return statement;
  253. }
  254. else if (protobufOneChange.getType() == ProtobufOneChange.Type.ROW_ID_DATA)
  255. {
  256. ProtobufRowIdData rowId = protobufOneChange.getRowId();
  257. // Using type 2 as default (INSERT_ID -- this is the old incorrect
  258. // behavior) in case no type is found in THL
  259. return new RowIdData(rowId.getId(), (rowId.hasType()
  260. ? rowId.getType()
  261. : RowIdData.INSERT_ID));
  262. }
  263. else if (protobufOneChange.getType() == ProtobufOneChange.Type.ROW_DATA)
  264. {
  265. return deserializeRows(protobufOneChange.getData());
  266. }
  267. else if (protobufOneChange.getType().equals(
  268. ProtobufOneChange.Type.LOAD_DATA_FILE_FRAGMENT))
  269. {
  270. LoadDataFileFragment loadDataFragment = deserializeFileFragment(protobufOneChange
  271. .getFileFragment());
  272. return loadDataFragment;
  273. }
  274. else if (protobufOneChange.getType().equals(
  275. ProtobufOneChange.Type.LOAD_DATA_FILE_QUERY))
  276. {
  277. LoadDataFileQuery loadDataFileQuery = deserializeLoadDataFileQuery(protobufOneChange
  278. .getFileQuery());
  279. return loadDataFileQuery;
  280. }
  281. return null;
  282. }
  283. private LoadDataFileQuery deserializeLoadDataFileQuery(
  284. ProtobufLoadDataFileQuery fileQuery)
  285. {
  286. ProtobufStatementData statement = fileQuery.getStatement();
  287. LoadDataFileQuery loadFileQuery = new LoadDataFileQuery(
  288. statement.getQuery(), statement.getTimestamp(),
  289. (statement.hasDefaultSchema()
  290. ? statement.getDefaultSchema()
  291. : null), fileQuery.getFileId(),
  292. fileQuery.getFilenameStartPos(), fileQuery.getFilenameEndPos());
  293. loadFileQuery.setErrorCode(statement.getErrorCode());
  294. for (ProtobufEventOption statementDataOption : statement
  295. .getOptionsList())
  296. {
  297. loadFileQuery.addOption(statementDataOption.getName(),
  298. statementDataOption.getValue());
  299. }
  300. return loadFileQuery;
  301. }
  302. private LoadDataFileFragment deserializeFileFragment(
  303. ProtobufLoadDataFileFragment fileFragment)
  304. {
  305. return new LoadDataFileFragment(fileFragment.getFileId(), fileFragment
  306. .getData().toByteArray(), fileFragment.getDatabase());
  307. }
  308. public void serializeHeader(THLEvent event, OutputStream outStream)
  309. throws IOException
  310. {
  311. Header.Builder headerBuilder = Header.newBuilder();
  312. headerBuilder.setSeqno(event.getSeqno());
  313. headerBuilder.setFragno(event.getFragno());
  314. headerBuilder.setLastFrag(event.getLastFrag());
  315. headerBuilder.setSourceTstamp(event.getSourceTstamp().getTime());
  316. headerBuilder.setExtractedTstamp(System.currentTimeMillis());
  317. headerBuilder.setSourceId(event.getSourceId());
  318. headerBuilder.setEpochNumber(event.getEpochNumber());
  319. headerBuilder.setEventId(event.getEventId());
  320. headerBuilder.setShardId("#DEFAULT");
  321. if (event.getReplEvent() != null
  322. && event.getReplEvent() instanceof ReplDBMSFilteredEvent)
  323. {
  324. ReplDBMSFilteredEvent ev = (ReplDBMSFilteredEvent) event
  325. .getReplEvent();
  326. headerBuilder.setFilteredEvent(true);
  327. // Fix up time for filtered events, which don't have a proper
  328. // DBMSEvent.
  329. Timestamp extractedTs = ev.getExtractedTstamp();
  330. long extractedTime;
  331. if (extractedTs == null)
  332. extractedTime = System.currentTimeMillis();
  333. else
  334. extractedTime = extractedTs.getTime();
  335. headerBuilder.setExtractedTstamp(extractedTime);
  336. headerBuilder.setSourceTstamp(extractedTime);
  337. headerBuilder.setFragnoEnd(ev.getFragnoEnd());
  338. headerBuilder.setSeqnoEnd(ev.getSeqnoEnd());
  339. }
  340. else
  341. headerBuilder.setFilteredEvent(false);
  342. Header builder = headerBuilder.build();
  343. builder.writeDelimitedTo(outStream);
  344. }
  345. public Header deserializeHeader(InputStream is) throws IOException
  346. {
  347. return Header.parseDelimitedFrom(is);
  348. }
  349. private void serializeRows(ProtobufRowChangeData.Builder rowDataBuilder,
  350. RowChangeData rowEv)
  351. {
  352. ProtobufOneRowChange.Builder oneRowBuilder;
  353. ProtobufColumnSpec.Builder colSpecBuilder;
  354. ProtobufRowValue.Builder rowBuilder;
  355. ProtobufColumnVal.Builder valueBuilder;
  356. ArrayList<OneRowChange> rowChanges = rowEv.getRowChanges();
  357. List<ReplOption> options = rowEv.getOptions();
  358. if (options != null && !options.isEmpty())
  359. {
  360. ProtobufEventOption.Builder optionsBuilder;
  361. for (ReplOption replOption : options)
  362. {
  363. optionsBuilder = ProtobufEventOption.newBuilder();
  364. optionsBuilder.setName(replOption.getOptionName());
  365. optionsBuilder.setValue(replOption.getOptionValue());
  366. rowDataBuilder.addOptions(optionsBuilder);
  367. }
  368. }
  369. trace = new StringBuffer();
  370. try
  371. {
  372. for (OneRowChange oneRowChange : rowChanges)
  373. {
  374. oneRowBuilder = ProtobufOneRowChange.newBuilder();
  375. oneRowBuilder.setSchemaName(oneRowChange.getSchemaName());
  376. oneRowBuilder.setTableName(oneRowChange.getTableName());
  377. oneRowBuilder.setTableId(oneRowChange.getTableId());
  378. switch (oneRowChange.getAction())
  379. {
  380. case INSERT :
  381. if (logger.isDebugEnabled())
  382. trace.append("INSERT into ");
  383. oneRowBuilder.setAction(ActionType.INSERT);
  384. break;
  385. case DELETE :
  386. if (logger.isDebugEnabled())
  387. trace.append("DELETE from ");
  388. oneRowBuilder.setAction(ActionType.DELETE);
  389. break;
  390. case UPDATE :
  391. if (logger.isDebugEnabled())
  392. trace.append("UPDATE ");
  393. oneRowBuilder.setAction(ActionType.UPDATE);
  394. break;
  395. default :
  396. break;
  397. }
  398. if (logger.isDebugEnabled())
  399. {
  400. trace.append(oneRowChange.getSchemaName());
  401. trace.append(".");
  402. trace.append(oneRowChange.getTableName());
  403. trace.append("\n Columns spec :\n");
  404. }
  405. ArrayList<ColumnSpec> list = oneRowChange.getColumnSpec();
  406. for (ColumnSpec columnSpec : list)
  407. {
  408. traceColumnSpec(columnSpec);
  409. colSpecBuilder = ProtobufColumnSpec.newBuilder();
  410. colSpecBuilder.setIndex(columnSpec.getIndex());
  411. colSpecBuilder.setLength(columnSpec.getLength());
  412. if (columnSpec.getName() != null)
  413. {
  414. colSpecBuilder.setName(columnSpec.getName());
  415. }
  416. colSpecBuilder.setNotNull(columnSpec.isNotNull());
  417. colSpecBuilder.setSigned(!columnSpec.isUnsigned());
  418. colSpecBuilder.setType(columnSpec.getType());
  419. oneRowBuilder.addColumnSpec(colSpecBuilder);
  420. }
  421. if (logger.isDebugEnabled())
  422. trace.append("\n Keys spec :\n");
  423. list = oneRowChange.getKeySpec();
  424. for (ColumnSpec columnSpec : list)
  425. {
  426. traceColumnSpec(columnSpec);
  427. colSpecBuilder = ProtobufColumnSpec.newBuilder();
  428. colSpecBuilder.setIndex(columnSpec.getIndex());
  429. colSpecBuilder.setLength(columnSpec.getLength());
  430. if (columnSpec.getName() != null)
  431. {
  432. colSpecBuilder.setName(columnSpec.getName());
  433. }
  434. colSpecBuilder.setNotNull(columnSpec.isNotNull());
  435. colSpecBuilder.setSigned(!columnSpec.isUnsigned());
  436. colSpecBuilder.setType(columnSpec.getType());
  437. oneRowBuilder.addKeySpec(colSpecBuilder);
  438. }
  439. ArrayList<ArrayList<ColumnVal>> rowValues = oneRowChange
  440. .getColumnValues();
  441. if (logger.isDebugEnabled())
  442. trace.append("\n Columns values :\n");
  443. for (ArrayList<ColumnVal> row : rowValues)
  444. {
  445. rowBuilder = ProtobufRowValue.newBuilder();
  446. for (int i = 0; i < row.size(); i++)
  447. {
  448. if (logger.isDebugEnabled() && i > 0)
  449. trace.append(", ");
  450. valueBuilder = ProtobufColumnVal.newBuilder();
  451. ColumnVal colValue = row.get(i);
  452. ColumnSpec colSpec = oneRowChange.getColumnSpec()
  453. .get(i);
  454. serializeRowValue(valueBuilder, colValue, colSpec);
  455. rowBuilder.addColumnValue(valueBuilder);
  456. }
  457. oneRowBuilder.addColumnValues(rowBuilder);
  458. if (logger.isDebugEnabled())
  459. trace.append("\n");
  460. }
  461. if (logger.isDebugEnabled())
  462. trace.append("\n Keys values :\n");
  463. rowValues = oneRowChange.getKeyValues();
  464. for (ArrayList<ColumnVal> row : rowValues)
  465. {
  466. rowBuilder = ProtobufRowValue.newBuilder();
  467. for (int i = 0; i < row.size(); i++)
  468. {
  469. if (logger.isDebugEnabled() && i > 0)
  470. trace.append(", ");
  471. valueBuilder = ProtobufColumnVal.newBuilder();
  472. ColumnVal colValue = row.get(i);
  473. ColumnSpec colSpec = oneRowChange.getKeySpec().get(i);
  474. serializeRowValue(valueBuilder, colValue, colSpec);
  475. rowBuilder.addColumnValue(valueBuilder);
  476. }
  477. if (logger.isDebugEnabled())
  478. trace.append("\n");
  479. oneRowBuilder.addKeyValues(rowBuilder);
  480. }
  481. rowDataBuilder.addRowChange(oneRowBuilder);
  482. }
  483. }
  484. catch (Exception e)
  485. {
  486. if (logger.isDebugEnabled())
  487. logger.debug("Failure while storing " + trace.toString(), e);
  488. throw new RuntimeException(e);
  489. }
  490. if (logger.isDebugEnabled())
  491. logger.debug(trace.toString());
  492. }
  493. private void serializeRowValue(ProtobufColumnVal.Builder valueBuilder,
  494. ColumnVal colValue, ColumnSpec colSpec)
  495. {
  496. if (logger.isDebugEnabled())
  497. {
  498. trace.append("Type = ");
  499. trace.append(colSpec.getType());
  500. }
  501. if (colSpec.getType() == Types.NULL)
  502. {
  503. // This is an optimization : the whole column was null
  504. valueBuilder.setType(Type.NULL);
  505. if (logger.isDebugEnabled())
  506. {
  507. trace.append(" / NULL");
  508. }
  509. return;
  510. }
  511. else if (colValue.getValue() == null)
  512. {
  513. // This single value was null (as opposed to the whole column
  514. // above-case
  515. valueBuilder.setType(Type.NULL);
  516. if (logger.isDebugEnabled())
  517. {
  518. trace.append(" / NULL");
  519. }
  520. return;
  521. }
  522. Object value = colValue.getValue();
  523. switch (colSpec.getType())
  524. {
  525. case Types.INTEGER :
  526. switch (colSpec.getLength())
  527. {
  528. case 0 :
  529. if (value instanceof Integer)
  530. {
  531. valueBuilder.setIntValue((Integer) value);
  532. if (logger.isDebugEnabled())
  533. {
  534. trace.append(" / ");
  535. trace.append(value);
  536. }
  537. valueBuilder.setType(Type.INT);
  538. }
  539. else if (value instanceof Long)
  540. {
  541. valueBuilder.setLongValue((Long) value);
  542. if (logger.isDebugEnabled())
  543. {
  544. trace.append(" / ");
  545. trace.append(value);
  546. }
  547. valueBuilder.setType(Type.LONG);
  548. }
  549. break;
  550. case 1 :
  551. case 2 :
  552. case 3 :
  553. case 4 :
  554. valueBuilder.setIntValue((Integer) value);
  555. if (logger.isDebugEnabled())
  556. {
  557. trace.append(" / ");
  558. trace.append(value);
  559. }
  560. valueBuilder.setType(Type.INT);
  561. break;
  562. case 8 :
  563. valueBuilder.setLongValue((Long) value);
  564. if (logger.isDebugEnabled())
  565. {
  566. trace.append(" / ");
  567. trace.append(value);
  568. }
  569. valueBuilder.setType(Type.LONG);
  570. break;
  571. default :
  572. logger.warn("Undefined type");
  573. break;
  574. }
  575. break;
  576. case Types.DECIMAL :
  577. case Types.NUMERIC :
  578. BigDecimal bigDec = (BigDecimal) value;
  579. if (logger.isDebugEnabled())
  580. {
  581. trace.append(" / ");
  582. trace.append(bigDec);
  583. }
  584. BigInteger unscaledValue = bigDec.unscaledValue();
  585. int scale = bigDec.scale();
  586. valueBuilder.setType(Type.DECIMAL);
  587. valueBuilder.setBytesValue(ByteString.copyFrom(unscaledValue
  588. .toByteArray()));
  589. valueBuilder.setIntValue(scale);
  590. break;
  591. case Types.FLOAT :
  592. valueBuilder.setFloatValue((Float) value);
  593. if (logger.isDebugEnabled())
  594. {
  595. trace.append(" / ");
  596. trace.append(value);
  597. }
  598. valueBuilder.setType(Type.FLOAT);
  599. break;
  600. case Types.DOUBLE :
  601. valueBuilder.setDoubleValue((Double) value);
  602. if (logger.isDebugEnabled())
  603. {
  604. trace.append(" / ");
  605. trace.append(value);
  606. }
  607. valueBuilder.setType(Type.DOUBLE);
  608. break;
  609. case Types.BIT :
  610. valueBuilder.setIntValue((Integer) value);
  611. if (logger.isDebugEnabled())
  612. {
  613. trace.append(" / ");
  614. trace.append(value);
  615. }
  616. valueBuilder.setType(Type.BIT);
  617. break;
  618. case Types.TIMESTAMP :
  619. if (value instanceof Integer)
  620. {
  621. valueBuilder.setIntValue(0);
  622. }
  623. else
  624. {
  625. long time = ((Timestamp) value).getTime();
  626. // Need to check whether timestamp is negative to compute
  627. // milliseconds
  628. int millis = (int) (time % 1000);
  629. if (millis < 0)
  630. millis += 1000;
  631. int nanos = ((Timestamp) value).getNanos()
  632. - (millis * 1000000);
  633. if (logger.isDebugEnabled())
  634. {
  635. trace.append(" / ");
  636. trace.append(time);
  637. }
  638. valueBuilder.setLongValue(time);
  639. if (nanos > 0)
  640. valueBuilder.setIntValue(nanos);
  641. }
  642. valueBuilder.setType(Type.TIMESTAMP);
  643. break;
  644. case Types.TIME :
  645. long time = 0;
  646. if (value instanceof Time)
  647. time = ((Time) value).getTime();
  648. else if (value instanceof Timestamp)
  649. {
  650. Timestamp ts = (Timestamp) value;
  651. time = ts.getTime();
  652. // Need to check whether timestamp is negative to compute
  653. // milliseconds
  654. int millis = (int) (time % 1000);
  655. if (millis < 0)
  656. millis += 1000;
  657. int nanos = ts.getNanos() - (millis * 1000000);
  658. if (logger.isDebugEnabled())
  659. logger.debug("Serializing TIME2 : " + ts + " - " + time
  660. + " - " + millis + " - " + nanos);
  661. // Even if nanoseconds part is 0, store it in THL as
  662. // deserialization will check whether it exists or not in
  663. // order to distinguish old events from new events in THL
  664. valueBuilder.setIntValue(nanos);
  665. }
  666. if (logger.isDebugEnabled())
  667. {
  668. trace.append(" / ");
  669. trace.append(time);
  670. }
  671. valueBuilder.setLongValue(time);
  672. valueBuilder.setType(Type.TIME);
  673. break;
  674. case Types.DATE :
  675. if (value instanceof Integer)
  676. valueBuilder.setIntValue(0);
  677. else if (value instanceof Timestamp)
  678. {
  679. // Handling DATETIME datatype (using GMT to apply)
  680. time = ((Timestamp) value).getTime();
  681. valueBuilder.setLongValue(time);
  682. valueBuilder.setStringValue("GMT");
  683. // Need to check whether timestamp is negative to compute
  684. // milliseconds
  685. int millis = (int) (time % 1000);
  686. if (millis < 0)
  687. millis += 1000;
  688. int nanos = ((Timestamp) value).getNanos()
  689. - (millis * 1000000);
  690. if (nanos > 0)
  691. valueBuilder.setIntValue(nanos);
  692. }
  693. else
  694. {
  695. time = ((Date) value).getTime();
  696. if (logger.isDebugEnabled())
  697. {
  698. trace.append(" / ");
  699. trace.append(time);
  700. }
  701. valueBuilder.setLongValue(time);
  702. }
  703. valueBuilder.setType(Type.DATE);
  704. break;
  705. case Types.OTHER :
  706. valueBuilder.setIntValue((Integer) value);
  707. if (logger.isDebugEnabled())
  708. {
  709. trace.append(" / ");
  710. trace.append(value);
  711. }
  712. valueBuilder.setType(Type.INT);
  713. break;
  714. case Types.BLOB :
  715. if (logger.isDebugEnabled())
  716. {
  717. trace.append(" / ");
  718. trace.append(value);
  719. }
  720. try
  721. {
  722. byte[] blob = ((SerialBlob) value).getBytes(1,
  723. (int) ((SerialBlob) value).length());
  724. valueBuilder.setBytesValue(ByteString.copyFrom(blob));
  725. }
  726. catch (SerialException e)
  727. {
  728. logger.error("Failed to serialize blob", e);
  729. }
  730. valueBuilder.setType(Type.BLOB);
  731. break;
  732. case Types.CHAR :
  733. case Types.VARCHAR :
  734. case Types.NVARCHAR :
  735. if (value instanceof String)
  736. {
  737. valueBuilder.setStringValue((String) value);
  738. if (logger.isDebugEnabled())
  739. {
  740. trace.append(" / ");
  741. trace.append(value);
  742. }
  743. valueBuilder.setType(Type.STRING);
  744. }
  745. else
  746. {
  747. if (logger.isDebugEnabled())
  748. {
  749. trace.append(" / ");
  750. trace.append(value);
  751. }
  752. valueBuilder.setBytesValue(ByteString
  753. .copyFrom((byte[]) value));
  754. valueBuilder.setType(Type.BINARYSTRING);
  755. }
  756. break;
  757. case Types.CLOB :
  758. // CLOB
  759. if (value instanceof Clob)
  760. {
  761. valueBuilder.setStringValue(((Clob) value).toString());
  762. }
  763. else
  764. valueBuilder.setStringValue((String) value);
  765. if (logger.isDebugEnabled())
  766. {
  767. trace.append(" / ");
  768. trace.append(value);
  769. }
  770. valueBuilder.setType(Type.STRING);
  771. break;
  772. default :
  773. logger.warn("Unimplemented type " + colSpec.getType());
  774. break;
  775. }
  776. }
  777. private RowChangeData deserializeRows(ProtobufRowChangeData rows)
  778. {
  779. RowChangeData data = new RowChangeData();
  780. List<ProtobufOneRowChange> rowChangeList = rows.getRowChangeList();
  781. for (ProtobufOneRowChange oneRowChange : rowChangeList)
  782. {
  783. OneRowChange rowChange = new OneRowChange(
  784. oneRowChange.getSchemaName(), oneRowChange.getTableName(),
  785. RowChangeData.ActionType.valueOf(oneRowChange.getAction()
  786. .name()));
  787. if (oneRowChange.hasTableId())
  788. rowChange.setTableId(oneRowChange.getTableId());
  789. for (ProtobufColumnSpec columnSpec : oneRowChange.getKeySpecList())
  790. {
  791. ColumnSpec c = rowChange.new ColumnSpec();
  792. c.setIndex(columnSpec.getIndex());
  793. c.setLength(columnSpec.getLength());
  794. c.setName(columnSpec.getName());
  795. c.setNotNull(columnSpec.getNotNull());
  796. c.setSigned(columnSpec.getSigned());
  797. c.setType(columnSpec.getType());
  798. rowChange.getKeySpec().add(c);
  799. }
  800. for (ProtobufColumnSpec columnSpec : oneRowChange
  801. .getColumnSpecList())
  802. {
  803. ColumnSpec c = rowChange.new ColumnSpec();
  804. c.setIndex(columnSpec.getIndex());
  805. c.setLength(columnSpec.getLength());
  806. c.setName(columnSpec.getName());
  807. c.setNotNull(columnSpec.getNotNull());
  808. c.setSigned(columnSpec.getSigned());
  809. c.setType(columnSpec.getType());
  810. rowChange.getColumnSpec().add(c);
  811. }
  812. ArrayList<ColumnVal> colValues = null;
  813. for (ProtobufRowValue rowValue : oneRowChange.getColumnValuesList())
  814. {
  815. colValues = new ArrayList<ColumnVal>();
  816. for (ProtobufColumnVal columnVal : rowValue
  817. .getColumnValueList())
  818. {
  819. ColumnVal v = rowChange.new ColumnVal();
  820. Serializable value = deserializeColumnValue(columnVal);
  821. if (value == null)
  822. v.setValueNull();
  823. else
  824. v.setValue(value);
  825. colValues.add(v);
  826. }
  827. rowChange.getColumnValues().add(colValues);
  828. }
  829. for (ProtobufRowValue rowValue : oneRowChange.getKeyValuesList())
  830. {
  831. colValues = new ArrayList<ColumnVal>();
  832. for (ProtobufColumnVal columnVal : rowValue
  833. .getColumnValueList())
  834. {
  835. ColumnVal v = rowChange.new ColumnVal();
  836. Serializable value = deserializeColumnValue(columnVal);
  837. if (value == null)
  838. v.setValueNull();
  839. else
  840. v.setValue(value);
  841. colValues.add(v);
  842. }
  843. rowChange.getKeyValues().add(colValues);
  844. }
  845. data.appendOneRowChange(rowChange);
  846. }
  847. for (ProtobufEventOption rowsDataOption : rows.getOptionsList())
  848. {
  849. data.addOption(rowsDataOption.getName(), rowsDataOption.getValue());
  850. }
  851. return data;
  852. }
  853. /**
  854. * TODO: deserializeColumnValue definition.
  855. *
  856. * @param columnVal
  857. * @return
  858. */
  859. private Serializable deserializeColumnValue(ProtobufColumnVal columnVal)
  860. {
  861. switch (columnVal.getType())
  862. {
  863. case NULL :
  864. return null;
  865. case INT :
  866. return Integer.valueOf(columnVal.getIntValue());
  867. case LONG :
  868. if (columnVal.hasLongValue())
  869. return columnVal.getLongValue();
  870. else
  871. return null;
  872. case STRING :
  873. return columnVal.getStringValue();
  874. case TIMESTAMP :
  875. if (columnVal.hasLongValue())
  876. {
  877. Timestamp timestamp = new Timestamp(
  878. columnVal.getLongValue());
  879. if (columnVal.hasIntValue())
  880. {
  881. // When setting nanos, don't forget millis that are
  882. // already stored in timestamp object
  883. timestamp.setNanos(timestamp.getNanos()
  884. + columnVal.getIntValue());
  885. }
  886. return timestamp;
  887. }
  888. else if (columnVal.hasIntValue())
  889. return Integer.valueOf(0);
  890. break;
  891. case DATE :
  892. if (columnVal.hasLongValue())
  893. {
  894. if (columnVal.hasStringValue())
  895. {
  896. // Handling DATETIME datatype (using GMT to apply)
  897. Timestamp timestamp = new Timestamp(
  898. columnVal.getLongValue());
  899. if (columnVal.hasIntValue())
  900. timestamp.setNanos(timestamp.getNanos()
  901. + columnVal.getIntValue());
  902. return timestamp;
  903. }
  904. else
  905. return new Date(columnVal.getLongValue());
  906. }
  907. else if (columnVal.hasIntValue())
  908. return Integer.valueOf(0);
  909. break;
  910. case BLOB :
  911. byte[] blob = columnVal.getBytesValue().toByteArray();
  912. try
  913. {
  914. return new SerialBlob(blob);
  915. }
  916. catch (SerialException e)
  917. {
  918. logger.warn("Failed to deserialize blob", e);
  919. }
  920. catch (SQLException e)
  921. {
  922. logger.warn("Failed to deserialize blob", e);
  923. }
  924. break;
  925. case TIME :
  926. if (columnVal.hasIntValue())
  927. {
  928. // This is time with microseconds (since MySQL 5.6)
  929. // We need to use timestamps in order to save microseconds
  930. Timestamp time = new Timestamp(columnVal.getLongValue());
  931. // When setting nanos, don't forget millis that are already
  932. // stored in timestamp object
  933. time.setNanos(time.getNanos() + columnVal.getIntValue());
  934. return time;
  935. }
  936. else
  937. {
  938. // Fall back on previous behavior
  939. return new Time(columnVal.getLongValue());
  940. }
  941. case FLOAT :
  942. return Float.valueOf(columnVal.getFloatValue());
  943. case DOUBLE :
  944. return Double.valueOf(columnVal.getDoubleValue());
  945. case BIT :
  946. return Integer.valueOf(columnVal.getIntValue());
  947. case DECIMAL :
  948. return new BigDecimal(new BigInteger(columnVal.getBytesValue()
  949. .toByteArray()), columnVal.getIntValue());
  950. case BINARYSTRING :
  951. return columnVal.getBytesValue().toByteArray();
  952. default :
  953. break;
  954. }
  955. return null;
  956. }
  957. private Message.Builder serializeStatement(StatementData data)
  958. {
  959. ProtobufStatementData.Builder statementBuilder = ProtobufStatementData
  960. .newBuilder();
  961. if (data.getDefaultSchema() != null)
  962. statementBuilder.setDefaultSchema(data.getDefaultSchema());
  963. if (data.getTimestamp() != null)
  964. statementBuilder.setTimestamp(data.getTimestamp());
  965. if (data.getQueryAsBytes() == null)
  966. statementBuilder.setQuery(data.getQuery());
  967. else
  968. statementBuilder.setQueryBytes(ByteString.copyFrom(data
  969. .getQueryAsBytes()));
  970. statementBuilder.setErrorCode(data.getErrorCode());
  971. List<ReplOption> options = data.getOptions();
  972. if (options != null && !options.isEmpty())
  973. {
  974. ProtobufEventOption.Builder optionsBuilder;
  975. for (ReplOption replOption : options)
  976. {
  977. optionsBuilder = ProtobufEventOption.newBuilder();
  978. optionsBuilder.setName(replOption.getOptionName());
  979. optionsBuilder.setValue(replOption.getOptionValue());
  980. statementBuilder.addOptions(optionsBuilder);
  981. }
  982. }
  983. return statementBuilder;
  984. }
  985. private void serializeLoadDataFileQuery(Builder oneChangeBuilder,
  986. LoadDataFileQuery data)
  987. {
  988. ProtobufLoadDataFileQuery.Builder loadDataQueryBuilder = ProtobufLoadDataFileQuery
  989. .newBuilder();
  990. loadDataQueryBuilder
  991. .setStatement((ProtobufStatementData.Builder) serializeStatement(data));
  992. loadDataQueryBuilder.setFileId(data.getFileID());
  993. loadDataQueryBuilder.setFilenameStartPos(data.getFilenameStartPos());
  994. loadDataQueryBuilder.setFilenameEndPos(data.getFilenameEndPos());
  995. oneChangeBuilder.setFileQuery(loadDataQueryBuilder);
  996. }
  997. private void serializeLoadDataFileFragment(Builder oneChangeBuilder,
  998. LoadDataFileFragment data)
  999. {
  1000. ProtobufLoadDataFileFragment.Builder loadDataFragBuilder = ProtobufLoadDataFileFragment
  1001. .newBuilder();
  1002. loadDataFragBuilder.setFileId(data.getFileID());
  1003. loadDataFragBuilder.setData(ByteString.copyFrom(data.getData()));
  1004. if (data.getDefaultSchema() != null)
  1005. loadDataFragBuilder.setDatabase(data.getDefaultSchema());
  1006. oneChangeBuilder.setFileFragment(loadDataFragBuilder);
  1007. }
  1008. private StatementData deserializeStatement(ProtobufStatementData statement)
  1009. {
  1010. StatementData statementData = new StatementData(null,
  1011. (statement.hasTimestamp() ? statement.getTimestamp() : null),
  1012. (statement.hasDefaultSchema()
  1013. ? statement.getDefaultSchema()
  1014. : null));
  1015. if (statement.hasQuery())
  1016. statementData.setQuery(statement.getQuery());
  1017. else if (statement.hasQueryBytes())
  1018. statementData.setQuery(statement.getQueryBytes().toByteArray());
  1019. else
  1020. logger.warn("Logged statement did not contain any query");
  1021. statementData.setErrorCode(statement.getErrorCode());
  1022. for (ProtobufEventOption statementDataOption : statement
  1023. .getOptionsList())
  1024. {
  1025. statementData.addOption(statementDataOption.getName(),
  1026. statementDataOption.getValue());
  1027. }
  1028. return statementData;
  1029. }
  1030. private void serializeRowIdData(Builder oneChangeBuilder, RowIdData data)
  1031. {
  1032. oneChangeBuilder.setType(ProtobufOneChange.Type.ROW_ID_DATA);
  1033. ProtobufRowIdData.Builder rowIdBuilder = ProtobufRowIdData.newBuilder();
  1034. rowIdBuilder.setId(data.getRowId());
  1035. rowIdBuilder.setType(data.getType());
  1036. oneChangeBuilder.setRowId(rowIdBuilder);
  1037. }
  1038. /**
  1039. * Debug method that add the column specification into the trace.
  1040. *
  1041. * @param columnSpec
  1042. */
  1043. private void traceColumnSpec(ColumnSpec columnSpec)
  1044. {
  1045. if (logger.isDebugEnabled())
  1046. {
  1047. trace.append(" - ");
  1048. trace.append("col #");
  1049. trace.append(columnSpec.getIndex());
  1050. trace.append(" - length = ");
  1051. trace.append(columnSpec.getLength());
  1052. if (columnSpec.getName() != null)
  1053. {
  1054. trace.append(" - ");
  1055. trace.append(columnSpec.getName());
  1056. }
  1057. trace.append(" - Type :");
  1058. trace.append(columnSpec.getType());
  1059. trace.append("\n");
  1060. }
  1061. }
  1062. }