Triggers.cpp 10.0 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) = static_cast<short>(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. const size_t buflen = strlen(buffer);
  112. snprintf(buffer + buflen, sizeof(buffer) - buflen, "%u type of column \"%s\".\"%s\" = ?", i, table, name);
  113. }
  114. strcat(buffer,
  115. ")\n"
  116. "as\n"
  117. "begin\n"
  118. " execute statement ('insert into \"");
  119. strcat(buffer, table);
  120. strcat(buffer, "\" (");
  121. for (unsigned i = 0; i < count; ++i)
  122. {
  123. if (i > 0)
  124. strcat(buffer, ", ");
  125. const char* name = triggerMetadata->getField(status, i);
  126. strcat(buffer, "\"");
  127. strcat(buffer, name);
  128. strcat(buffer, "\"");
  129. }
  130. strcat(buffer, ") values (");
  131. for (unsigned i = 0; i < count; ++i)
  132. {
  133. if (i > 0)
  134. strcat(buffer, ", ");
  135. strcat(buffer, "?");
  136. }
  137. strcat(buffer, ")') (");
  138. for (unsigned i = 0; i < count; ++i)
  139. {
  140. if (i > 0)
  141. strcat(buffer, ", ");
  142. strcat(buffer, ":p");
  143. const size_t buflen = strlen(buffer);
  144. snprintf(buffer + buflen, sizeof(buffer) - buflen, "%u", i);
  145. }
  146. strcat(buffer, ")\n on external data source '");
  147. strcat(buffer, outSqlDa->sqlvar[0].sqldata + sizeof(short));
  148. strcat(buffer, "';\nend");
  149. AutoRelease<IAttachment> attachment(context->getAttachment(status));
  150. AutoRelease<ITransaction> transaction(context->getTransaction(status));
  151. stmt.reset(attachment->prepare(status, transaction, 0, buffer, SQL_DIALECT_CURRENT, 0));
  152. delete [] outSqlDa->sqlvar[0].sqldata;
  153. delete [] reinterpret_cast<char*>(outSqlDa);
  154. }
  155. /***
  156. FB_UDR_DESTRUCTOR
  157. {
  158. }
  159. ***/
  160. FB_UDR_EXECUTE_TRIGGER
  161. {
  162. AutoRelease<ITransaction> transaction(context->getTransaction(status));
  163. // This will not work if the table has computed fields.
  164. stmt->execute(status, transaction, triggerMetadata, newFields, NULL, NULL);
  165. }
  166. AutoRelease<IMessageMetadata> triggerMetadata;
  167. AutoRelease<IStatement> stmt;
  168. FB_UDR_END_TRIGGER
  169. FB_UDR_BEGIN_TRIGGER(replicate_persons)
  170. // Order of fields does not need to match the fields order in the table, but it should match
  171. // the order of fields in the SQL command constructed in the initialization.
  172. FB_UDR_TRIGGER_MESSAGE(FieldsMessage,
  173. (FB_INTEGER, id, "ID")
  174. (FB_BLOB, info, "INFO")
  175. ///(FB_VARCHAR(60 * 4), address, "ADDRESS")
  176. (FB_VARCHAR(60 * 4), name, "NAME")
  177. );
  178. FB_UDR_CONSTRUCTOR
  179. , triggerMetadata(metadata->getTriggerMetadata(status))
  180. {
  181. ISC_STATUS_ARRAY statusVector = {0};
  182. isc_db_handle dbHandle = Helper::getIscDbHandle(status, context);
  183. isc_tr_handle trHandle = Helper::getIscTrHandle(status, context);
  184. isc_stmt_handle stmtHandle = 0;
  185. FbException::check(isc_dsql_allocate_statement(
  186. statusVector, &dbHandle, &stmtHandle), status, statusVector);
  187. FbException::check(isc_dsql_prepare(statusVector, &trHandle, &stmtHandle, 0,
  188. "select data_source from replicate_config where name = ?",
  189. SQL_DIALECT_CURRENT, NULL), status, statusVector);
  190. // Skip the first exclamation point, separating the module name and entry point.
  191. const char* info = strchr(metadata->getEntryPoint(status), '!');
  192. // Skip the second exclamation point, separating the entry point and the misc info (config).
  193. if (info)
  194. info = strchr(info + 1, '!');
  195. if (info)
  196. ++info;
  197. else
  198. info = "";
  199. XSQLDA* inSqlDa = reinterpret_cast<XSQLDA*>(new char[(XSQLDA_LENGTH(1))]);
  200. inSqlDa->version = SQLDA_VERSION1;
  201. inSqlDa->sqln = 1;
  202. FbException::check(isc_dsql_describe_bind(
  203. statusVector, &stmtHandle, SQL_DIALECT_CURRENT, inSqlDa), status, statusVector);
  204. inSqlDa->sqlvar[0].sqldata = new char[sizeof(short) + inSqlDa->sqlvar[0].sqllen];
  205. strncpy(inSqlDa->sqlvar[0].sqldata + sizeof(short), info, inSqlDa->sqlvar[0].sqllen);
  206. *reinterpret_cast<short*>(inSqlDa->sqlvar[0].sqldata) = static_cast<short>(strlen(info));
  207. XSQLDA* outSqlDa = reinterpret_cast<XSQLDA*>(new char[(XSQLDA_LENGTH(1))]);
  208. outSqlDa->version = SQLDA_VERSION1;
  209. outSqlDa->sqln = 1;
  210. FbException::check(isc_dsql_describe(
  211. statusVector, &stmtHandle, SQL_DIALECT_CURRENT, outSqlDa), status, statusVector);
  212. outSqlDa->sqlvar[0].sqldata = new char[sizeof(short) + outSqlDa->sqlvar[0].sqllen + 1];
  213. outSqlDa->sqlvar[0].sqldata[sizeof(short) + outSqlDa->sqlvar[0].sqllen] = '\0';
  214. FbException::check(isc_dsql_execute2(statusVector, &trHandle, &stmtHandle,
  215. SQL_DIALECT_CURRENT, inSqlDa, outSqlDa), status, statusVector);
  216. FbException::check(isc_dsql_free_statement(
  217. statusVector, &stmtHandle, DSQL_unprepare), status, statusVector);
  218. delete [] inSqlDa->sqlvar[0].sqldata;
  219. delete [] reinterpret_cast<char*>(inSqlDa);
  220. char buffer[65536];
  221. strcpy(buffer,
  222. "execute block (\n"
  223. " id type of column PERSONS.ID = ?,\n"
  224. " info type of column PERSONS.INFO = ?,\n"
  225. ///" address type of column PERSONS.ADDRESS = ?,\n"
  226. " name type of column PERSONS.NAME = ?\n"
  227. ")"
  228. "as\n"
  229. "begin\n"
  230. " execute statement ('insert into persons (id, name/***, address***/, info)\n"
  231. " values (?, ?/***, ?***/, ?)') (:id, :name/***, :address***/, :info)\n"
  232. " on external data source '");
  233. strcat(buffer, outSqlDa->sqlvar[0].sqldata + sizeof(short));
  234. strcat(buffer, "';\nend");
  235. AutoRelease<IAttachment> attachment(context->getAttachment(status));
  236. AutoRelease<ITransaction> transaction(context->getTransaction(status));
  237. stmt.reset(attachment->prepare(status, transaction, 0, buffer, SQL_DIALECT_CURRENT, 0));
  238. delete [] outSqlDa->sqlvar[0].sqldata;
  239. delete [] reinterpret_cast<char*>(outSqlDa);
  240. }
  241. /***
  242. FB_UDR_DESTRUCTOR
  243. {
  244. }
  245. ***/
  246. FB_UDR_EXECUTE_TRIGGER
  247. {
  248. AutoRelease<ITransaction> transaction(context->getTransaction(status));
  249. stmt->execute(status, transaction, triggerMetadata, newFields, NULL, NULL);
  250. }
  251. AutoRelease<IMessageMetadata> triggerMetadata;
  252. AutoRelease<IStatement> stmt;
  253. FB_UDR_END_TRIGGER