Triggers.cpp 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. /*
  2. * The contents of this file are subject to the Initial
  3. * Developer's Public License Version 1.0 (the "License");
  4. * you may not use this file except in compliance with the
  5. * License. You may obtain a copy of the License at
  6. * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
  7. *
  8. * Software distributed under the License is distributed AS IS,
  9. * WITHOUT WARRANTY OF ANY KIND, either express or implied.
  10. * See the License for the specific language governing rights
  11. * and limitations under the License.
  12. *
  13. * The Original Code was created by Adriano dos Santos Fernandes
  14. * for the Firebird Open Source RDBMS project.
  15. *
  16. * Copyright (c) 2008 Adriano dos Santos Fernandes <[email protected]>
  17. * and all contributors signed below.
  18. *
  19. * All Rights Reserved.
  20. * Contributor(s): ______________________________________.
  21. */
  22. #include "UdrCppExample.h"
  23. using namespace Firebird;
  24. //------------------------------------------------------------------------------
  25. /***
  26. Sample usage:
  27. create database 'c:\temp\replica.fdb';
  28. create table persons (
  29. id integer not null,
  30. name varchar(60) not null,
  31. address varchar(60),
  32. info blob sub_type text
  33. );
  34. commit;
  35. create database 'c:\temp\main.fdb';
  36. create table persons (
  37. id integer not null,
  38. name varchar(60) not null,
  39. address varchar(60),
  40. info blob sub_type text
  41. );
  42. create table replicate_config (
  43. name varchar(31) not null,
  44. data_source varchar(255) not null
  45. );
  46. insert into replicate_config (name, data_source)
  47. values ('ds1', 'c:\temp\replica.fdb');
  48. create trigger persons_replicate
  49. after insert on persons
  50. external name 'udrcpp_example!replicate!ds1'
  51. engine udr;
  52. create trigger persons_replicate2
  53. after insert on persons
  54. external name 'udrcpp_example!replicate_persons!ds1'
  55. engine udr;
  56. ***/
  57. FB_UDR_BEGIN_TRIGGER(replicate)
  58. // Without FieldsMessage definition, messages will be byte-based.
  59. FB_UDR_CONSTRUCTOR
  60. , triggerMetadata(metadata->getTriggerMetadata(status))
  61. {
  62. ISC_STATUS_ARRAY statusVector = {0};
  63. isc_db_handle dbHandle = Helper::getIscDbHandle(status, context);
  64. isc_tr_handle trHandle = Helper::getIscTrHandle(status, context);
  65. isc_stmt_handle stmtHandle = 0;
  66. FbException::check(isc_dsql_allocate_statement(
  67. statusVector, &dbHandle, &stmtHandle), status, statusVector);
  68. FbException::check(isc_dsql_prepare(statusVector, &trHandle, &stmtHandle, 0,
  69. "select data_source from replicate_config where name = ?",
  70. SQL_DIALECT_CURRENT, NULL), status, statusVector);
  71. const char* table = metadata->getTriggerTable(status);
  72. // Skip the first exclamation point, separating the module name and entry point.
  73. const char* info = strchr(metadata->getEntryPoint(status), '!');
  74. // Skip the second exclamation point, separating the entry point and the misc info (config).
  75. if (info)
  76. info = strchr(info + 1, '!');
  77. if (info)
  78. ++info;
  79. else
  80. info = "";
  81. XSQLDA* inSqlDa = reinterpret_cast<XSQLDA*>(new char[(XSQLDA_LENGTH(1))]);
  82. inSqlDa->version = SQLDA_VERSION1;
  83. inSqlDa->sqln = 1;
  84. FbException::check(isc_dsql_describe_bind(statusVector, &stmtHandle,
  85. SQL_DIALECT_CURRENT, inSqlDa), status, statusVector);
  86. inSqlDa->sqlvar[0].sqldata = new char[sizeof(short) + inSqlDa->sqlvar[0].sqllen];
  87. strncpy(inSqlDa->sqlvar[0].sqldata + sizeof(short), info, inSqlDa->sqlvar[0].sqllen);
  88. *reinterpret_cast<short*>(inSqlDa->sqlvar[0].sqldata) = strlen(info);
  89. XSQLDA* outSqlDa = reinterpret_cast<XSQLDA*>(new char[(XSQLDA_LENGTH(1))]);
  90. outSqlDa->version = SQLDA_VERSION1;
  91. outSqlDa->sqln = 1;
  92. FbException::check(isc_dsql_describe(statusVector, &stmtHandle,
  93. SQL_DIALECT_CURRENT, outSqlDa), status, statusVector);
  94. outSqlDa->sqlvar[0].sqldata = new char[sizeof(short) + outSqlDa->sqlvar[0].sqllen + 1];
  95. outSqlDa->sqlvar[0].sqldata[sizeof(short) + outSqlDa->sqlvar[0].sqllen] = '\0';
  96. FbException::check(isc_dsql_execute2(statusVector, &trHandle, &stmtHandle,
  97. SQL_DIALECT_CURRENT, inSqlDa, outSqlDa), status, statusVector);
  98. FbException::check(isc_dsql_free_statement(
  99. statusVector, &stmtHandle, DSQL_unprepare), status, statusVector);
  100. delete [] inSqlDa->sqlvar[0].sqldata;
  101. delete [] reinterpret_cast<char*>(inSqlDa);
  102. unsigned count = triggerMetadata->getCount(status);
  103. char buffer[65536];
  104. strcpy(buffer, "execute block (\n");
  105. for (unsigned i = 0; i < count; ++i)
  106. {
  107. if (i > 0)
  108. strcat(buffer, ",\n");
  109. const char* name = triggerMetadata->getField(status, i);
  110. strcat(buffer, " p");
  111. sprintf(buffer + strlen(buffer), "%d type of column \"%s\".\"%s\" = ?", i, table, name);
  112. }
  113. strcat(buffer,
  114. ")\n"
  115. "as\n"
  116. "begin\n"
  117. " execute statement ('insert into \"");
  118. strcat(buffer, table);
  119. strcat(buffer, "\" (");
  120. for (unsigned i = 0; i < count; ++i)
  121. {
  122. if (i > 0)
  123. strcat(buffer, ", ");
  124. const char* name = triggerMetadata->getField(status, i);
  125. strcat(buffer, "\"");
  126. strcat(buffer, name);
  127. strcat(buffer, "\"");
  128. }
  129. strcat(buffer, ") values (");
  130. for (unsigned i = 0; i < count; ++i)
  131. {
  132. if (i > 0)
  133. strcat(buffer, ", ");
  134. strcat(buffer, "?");
  135. }
  136. strcat(buffer, ")') (");
  137. for (unsigned i = 0; i < count; ++i)
  138. {
  139. if (i > 0)
  140. strcat(buffer, ", ");
  141. strcat(buffer, ":p");
  142. sprintf(buffer + strlen(buffer), "%d", i);
  143. }
  144. strcat(buffer, ")\n on external data source '");
  145. strcat(buffer, outSqlDa->sqlvar[0].sqldata + sizeof(short));
  146. strcat(buffer, "';\nend");
  147. AutoRelease<IAttachment> attachment(context->getAttachment(status));
  148. AutoRelease<ITransaction> transaction(context->getTransaction(status));
  149. stmt.reset(attachment->prepare(status, transaction, 0, buffer, SQL_DIALECT_CURRENT, 0));
  150. delete [] outSqlDa->sqlvar[0].sqldata;
  151. delete [] reinterpret_cast<char*>(outSqlDa);
  152. }
  153. /***
  154. FB_UDR_DESTRUCTOR
  155. {
  156. }
  157. ***/
  158. FB_UDR_EXECUTE_TRIGGER
  159. {
  160. AutoRelease<ITransaction> transaction(context->getTransaction(status));
  161. // This will not work if the table has computed fields.
  162. stmt->execute(status, transaction, triggerMetadata, newFields, NULL, NULL);
  163. }
  164. AutoRelease<IMessageMetadata> triggerMetadata;
  165. AutoRelease<IStatement> stmt;
  166. FB_UDR_END_TRIGGER
  167. FB_UDR_BEGIN_TRIGGER(replicate_persons)
  168. // Order of fields does not need to match the fields order in the table, but it should match
  169. // the order of fields in the SQL command constructed in the initialization.
  170. FB_UDR_TRIGGER_MESSAGE(FieldsMessage,
  171. (FB_INTEGER, id, "ID")
  172. (FB_BLOB, info, "INFO")
  173. ///(FB_VARCHAR(60 * 4), address, "ADDRESS")
  174. (FB_VARCHAR(60 * 4), name, "NAME")
  175. );
  176. FB_UDR_CONSTRUCTOR
  177. , triggerMetadata(metadata->getTriggerMetadata(status))
  178. {
  179. ISC_STATUS_ARRAY statusVector = {0};
  180. isc_db_handle dbHandle = Helper::getIscDbHandle(status, context);
  181. isc_tr_handle trHandle = Helper::getIscTrHandle(status, context);
  182. isc_stmt_handle stmtHandle = 0;
  183. FbException::check(isc_dsql_allocate_statement(
  184. statusVector, &dbHandle, &stmtHandle), status, statusVector);
  185. FbException::check(isc_dsql_prepare(statusVector, &trHandle, &stmtHandle, 0,
  186. "select data_source from replicate_config where name = ?",
  187. SQL_DIALECT_CURRENT, NULL), status, statusVector);
  188. const char* table = metadata->getTriggerTable(status);
  189. // Skip the first exclamation point, separating the module name and entry point.
  190. const char* info = strchr(metadata->getEntryPoint(status), '!');
  191. // Skip the second exclamation point, separating the entry point and the misc info (config).
  192. if (info)
  193. info = strchr(info + 1, '!');
  194. if (info)
  195. ++info;
  196. else
  197. info = "";
  198. XSQLDA* inSqlDa = reinterpret_cast<XSQLDA*>(new char[(XSQLDA_LENGTH(1))]);
  199. inSqlDa->version = SQLDA_VERSION1;
  200. inSqlDa->sqln = 1;
  201. FbException::check(isc_dsql_describe_bind(
  202. statusVector, &stmtHandle, SQL_DIALECT_CURRENT, inSqlDa), status, statusVector);
  203. inSqlDa->sqlvar[0].sqldata = new char[sizeof(short) + inSqlDa->sqlvar[0].sqllen];
  204. strncpy(inSqlDa->sqlvar[0].sqldata + sizeof(short), info, inSqlDa->sqlvar[0].sqllen);
  205. *reinterpret_cast<short*>(inSqlDa->sqlvar[0].sqldata) = strlen(info);
  206. XSQLDA* outSqlDa = reinterpret_cast<XSQLDA*>(new char[(XSQLDA_LENGTH(1))]);
  207. outSqlDa->version = SQLDA_VERSION1;
  208. outSqlDa->sqln = 1;
  209. FbException::check(isc_dsql_describe(
  210. statusVector, &stmtHandle, SQL_DIALECT_CURRENT, outSqlDa), status, statusVector);
  211. outSqlDa->sqlvar[0].sqldata = new char[sizeof(short) + outSqlDa->sqlvar[0].sqllen + 1];
  212. outSqlDa->sqlvar[0].sqldata[sizeof(short) + outSqlDa->sqlvar[0].sqllen] = '\0';
  213. FbException::check(isc_dsql_execute2(statusVector, &trHandle, &stmtHandle,
  214. SQL_DIALECT_CURRENT, inSqlDa, outSqlDa), status, statusVector);
  215. FbException::check(isc_dsql_free_statement(
  216. statusVector, &stmtHandle, DSQL_unprepare), status, statusVector);
  217. delete [] inSqlDa->sqlvar[0].sqldata;
  218. delete [] reinterpret_cast<char*>(inSqlDa);
  219. char buffer[65536];
  220. strcpy(buffer,
  221. "execute block (\n"
  222. " id type of column PERSONS.ID = ?,\n"
  223. " info type of column PERSONS.INFO = ?,\n"
  224. ///" address type of column PERSONS.ADDRESS = ?,\n"
  225. " name type of column PERSONS.NAME = ?\n"
  226. ")"
  227. "as\n"
  228. "begin\n"
  229. " execute statement ('insert into persons (id, name/***, address***/, info)\n"
  230. " values (?, ?/***, ?***/, ?)') (:id, :name/***, :address***/, :info)\n"
  231. " on external data source '");
  232. strcat(buffer, outSqlDa->sqlvar[0].sqldata + sizeof(short));
  233. strcat(buffer, "';\nend");
  234. AutoRelease<IAttachment> attachment(context->getAttachment(status));
  235. AutoRelease<ITransaction> transaction(context->getTransaction(status));
  236. stmt.reset(attachment->prepare(status, transaction, 0, buffer, SQL_DIALECT_CURRENT, 0));
  237. delete [] outSqlDa->sqlvar[0].sqldata;
  238. delete [] reinterpret_cast<char*>(outSqlDa);
  239. }
  240. /***
  241. FB_UDR_DESTRUCTOR
  242. {
  243. }
  244. ***/
  245. FB_UDR_EXECUTE_TRIGGER
  246. {
  247. AutoRelease<ITransaction> transaction(context->getTransaction(status));
  248. stmt->execute(status, transaction, triggerMetadata, newFields, NULL, NULL);
  249. }
  250. AutoRelease<IMessageMetadata> triggerMetadata;
  251. AutoRelease<IStatement> stmt;
  252. FB_UDR_END_TRIGGER