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 <adrianosf@uol.com.br> |
17 | * and all contributors signed below. |
18 | * |
19 | * All Rights Reserved. |
20 | * Contributor(s): ______________________________________. |
21 | */ |
22 | |
23 | #include "ibase.h" |
24 | #include "firebird/UdrCppEngine.h" |
25 | #include <assert.h> |
26 | #include <stdio.h> |
27 | |
28 | |
29 | using namespace Firebird; |
30 | using namespace Firebird::Udr; |
31 | |
32 | |
33 | namespace |
34 | { |
35 | template <typename T> |
36 | class AutoReleaseClear |
37 | { |
38 | public: |
39 | static void clear(T* ptr) |
40 | { |
41 | if (ptr) |
42 | ptr->release(); |
43 | } |
44 | }; |
45 | |
46 | template <typename T> |
47 | class AutoDisposeClear |
48 | { |
49 | public: |
50 | static void clear(T* ptr) |
51 | { |
52 | if (ptr) |
53 | ptr->dispose(); |
54 | } |
55 | }; |
56 | |
57 | template <typename T> |
58 | class AutoDeleteClear |
59 | { |
60 | public: |
61 | static void clear(T* ptr) |
62 | { |
63 | delete ptr; |
64 | } |
65 | }; |
66 | |
67 | template <typename T> |
68 | class AutoArrayDeleteClear |
69 | { |
70 | public: |
71 | static void clear(T* ptr) |
72 | { |
73 | delete [] ptr; |
74 | } |
75 | }; |
76 | |
77 | template <typename T, typename Clear> |
78 | class AutoImpl |
79 | { |
80 | public: |
81 | AutoImpl<T, Clear>(T* aPtr = NULL) |
82 | : ptr(aPtr) |
83 | { |
84 | } |
85 | |
86 | ~AutoImpl() |
87 | { |
88 | Clear::clear(ptr); |
89 | } |
90 | |
91 | AutoImpl<T, Clear>& operator =(T* aPtr) |
92 | { |
93 | Clear::clear(ptr); |
94 | ptr = aPtr; |
95 | return *this; |
96 | } |
97 | |
98 | operator T*() |
99 | { |
100 | return ptr; |
101 | } |
102 | |
103 | operator const T*() const |
104 | { |
105 | return ptr; |
106 | } |
107 | |
108 | bool operator !() const |
109 | { |
110 | return !ptr; |
111 | } |
112 | |
113 | bool hasData() const |
114 | { |
115 | return ptr != NULL; |
116 | } |
117 | |
118 | T* operator ->() |
119 | { |
120 | return ptr; |
121 | } |
122 | |
123 | T* release() |
124 | { |
125 | T* tmp = ptr; |
126 | ptr = NULL; |
127 | return tmp; |
128 | } |
129 | |
130 | void reset(T* aPtr = NULL) |
131 | { |
132 | if (aPtr != ptr) |
133 | { |
134 | Clear::clear(ptr); |
135 | ptr = aPtr; |
136 | } |
137 | } |
138 | |
139 | private: |
140 | // not implemented |
141 | AutoImpl<T, Clear>(AutoImpl<T, Clear>&); |
142 | void operator =(AutoImpl<T, Clear>&); |
143 | |
144 | private: |
145 | T* ptr; |
146 | }; |
147 | |
148 | template <typename T> class AutoDispose : public AutoImpl<T, AutoDisposeClear<T> > |
149 | { |
150 | public: |
151 | AutoDispose(T* ptr = NULL) |
152 | : AutoImpl<T, AutoDisposeClear<T> >(ptr) |
153 | { |
154 | } |
155 | }; |
156 | |
157 | template <typename T> class AutoRelease : public AutoImpl<T, AutoReleaseClear<T> > |
158 | { |
159 | public: |
160 | AutoRelease(T* ptr = NULL) |
161 | : AutoImpl<T, AutoReleaseClear<T> >(ptr) |
162 | { |
163 | } |
164 | }; |
165 | |
166 | template <typename T> class AutoDelete : public AutoImpl<T, AutoDeleteClear<T> > |
167 | { |
168 | public: |
169 | AutoDelete(T* ptr = NULL) |
170 | : AutoImpl<T, AutoDeleteClear<T> >(ptr) |
171 | { |
172 | } |
173 | }; |
174 | |
175 | template <typename T> class AutoArrayDelete : public AutoImpl<T, AutoArrayDeleteClear<T> > |
176 | { |
177 | public: |
178 | AutoArrayDelete(T* ptr = NULL) |
179 | : AutoImpl<T, AutoArrayDeleteClear<T> >(ptr) |
180 | { |
181 | } |
182 | }; |
183 | } |
184 | |
185 | |
186 | //------------------------------------------------------------------------------ |
187 | |
188 | |
189 | /*** |
190 | create function wait_event ( |
191 | event_name varchar(31) character set utf8 not null |
192 | ) returns integer not null |
193 | external name 'udrcpp_example!wait_event' |
194 | engine udr; |
195 | ***/ |
196 | FB_UDR_BEGIN_FUNCTION(wait_event) |
197 | FB_MESSAGE(InMessage, |
198 | (FB_VARCHAR(31 * 4), name) |
199 | ); |
200 | |
201 | FB_MESSAGE(OutMessage, |
202 | (FB_INTEGER, result) |
203 | ); |
204 | |
205 | FB_UDR_EXECUTE_FUNCTION |
206 | { |
207 | char* s = new char[in->name.length + 1]; |
208 | memcpy(s, in->name.str, in->name.length); |
209 | s[in->name.length] = '\0'; |
210 | |
211 | unsigned char* eveBuffer; |
212 | unsigned char* eveResult; |
213 | int eveLen = isc_event_block(&eveBuffer, &eveResult, 1, s); |
214 | |
215 | delete [] s; |
216 | |
217 | ISC_STATUS_ARRAY statusVector = {0}; |
218 | isc_db_handle dbHandle = getIscDbHandle(context); |
219 | ISC_ULONG counter = 0; |
220 | |
221 | StatusException::checkStatus(isc_wait_for_event( |
222 | statusVector, &dbHandle, eveLen, eveBuffer, eveResult), statusVector); |
223 | isc_event_counts(&counter, eveLen, eveBuffer, eveResult); |
224 | StatusException::checkStatus(isc_wait_for_event( |
225 | statusVector, &dbHandle, eveLen, eveBuffer, eveResult), statusVector); |
226 | isc_event_counts(&counter, eveLen, eveBuffer, eveResult); |
227 | |
228 | isc_free((char*) eveBuffer); |
229 | isc_free((char*) eveResult); |
230 | |
231 | out->resultNull = FB_FALSE; |
232 | out->result = counter; |
233 | } |
234 | FB_UDR_END_FUNCTION |
235 | |
236 | |
237 | /*** |
238 | create function sum_args ( |
239 | n1 integer, |
240 | n2 integer, |
241 | n3 integer |
242 | ) returns integer |
243 | external name 'udrcpp_example!sum_args' |
244 | engine udr; |
245 | ***/ |
246 | FB_UDR_BEGIN_FUNCTION(sum_args) |
247 | // Without InMessage/OutMessage definitions, messages will be byte-based. |
248 | |
249 | FB_UDR_CONSTRUCTOR |
250 | // , inCount(0) |
251 | { |
252 | // Get input metadata. |
253 | AutoRelease<IMessageMetadata> inMetadata(StatusException::check(status, |
254 | metadata->getInputMetadata(status))); |
255 | |
256 | // Get count of input parameters. |
257 | inCount = StatusException::check(status, inMetadata->getCount(status)); |
258 | |
259 | inNullOffsets.reset(new unsigned[inCount]); |
260 | inOffsets.reset(new unsigned[inCount]); |
261 | |
262 | for (unsigned i = 0; i < inCount; ++i) |
263 | { |
264 | // Get null offset of the i-th input parameter. |
265 | inNullOffsets[i] = StatusException::check(status, inMetadata->getNullOffset(status, i)); |
266 | |
267 | // Get the offset of the i-th input parameter. |
268 | inOffsets[i] = StatusException::check(status, inMetadata->getOffset(status, i)); |
269 | } |
270 | |
271 | // Get output metadata. |
272 | AutoRelease<IMessageMetadata> outMetadata(StatusException::check(status, |
273 | metadata->getOutputMetadata(status))); |
274 | |
275 | // Get null offset of the return value. |
276 | outNullOffset = StatusException::check(status, outMetadata->getNullOffset(status, 0)); |
277 | |
278 | // Get offset of the return value. |
279 | outOffset = StatusException::check(status, outMetadata->getOffset(status, 0)); |
280 | } |
281 | |
282 | // This function requires the INTEGER parameters and return value, otherwise it will crash. |
283 | // Metadata is inspected dynamically (in execute). This is not the fastest method. |
284 | FB_UDR_EXECUTE_FUNCTION |
285 | { |
286 | *(ISC_SHORT*) (out + outNullOffset) = FB_FALSE; |
287 | |
288 | // Get a reference to the return value. |
289 | ISC_LONG& ret = *(ISC_LONG*) (out + outOffset); |
290 | |
291 | // The return value is automatically initialized to 0. |
292 | ///ret = 0; |
293 | |
294 | for (unsigned i = 0; i < inCount; ++i) |
295 | { |
296 | // If the i-th input parameter is NULL, set the output to NULL and finish. |
297 | if (*(ISC_SHORT*) (in + inNullOffsets[i])) |
298 | { |
299 | *(ISC_SHORT*) (out + outNullOffset) = FB_TRUE; |
300 | return; |
301 | } |
302 | |
303 | // Read the i-th input parameter value and sum it in the referenced return value. |
304 | ret += *(ISC_LONG*) (in + inOffsets[i]); |
305 | } |
306 | } |
307 | |
308 | unsigned inCount; |
309 | AutoArrayDelete<unsigned> inNullOffsets; |
310 | AutoArrayDelete<unsigned> inOffsets; |
311 | unsigned outNullOffset; |
312 | unsigned outOffset; |
313 | FB_UDR_END_FUNCTION |
314 | |
315 | |
316 | /*** |
317 | create procedure gen_rows ( |
318 | start_n integer not null, |
319 | end_n integer not null |
320 | ) returns ( |
321 | n integer not null |
322 | ) |
323 | external name 'udrcpp_example!gen_rows' |
324 | engine udr; |
325 | ***/ |
326 | FB_UDR_BEGIN_PROCEDURE(gen_rows) |
327 | // Without InMessage/OutMessage definitions, messages will be byte-based. |
328 | |
329 | // Procedure variables. |
330 | unsigned inOffsetStart, inOffsetEnd, outNullOffset, outOffset; |
331 | |
332 | // Get offsets once per procedure. |
333 | FB_UDR_CONSTRUCTOR |
334 | { |
335 | AutoRelease<IMessageMetadata> inMetadata(StatusException::check(status, |
336 | metadata->getInputMetadata(status))); |
337 | |
338 | inOffsetStart = StatusException::check(status, inMetadata->getOffset(status, 0)); |
339 | inOffsetEnd = StatusException::check(status, inMetadata->getOffset(status, 1)); |
340 | |
341 | AutoRelease<IMessageMetadata> outMetadata(StatusException::check(status, |
342 | metadata->getOutputMetadata(status))); |
343 | |
344 | outNullOffset = StatusException::check(status, outMetadata->getNullOffset(status, 0)); |
345 | outOffset = StatusException::check(status, outMetadata->getOffset(status, 0)); |
346 | } |
347 | |
348 | /*** Procedure destructor. |
349 | FB_UDR_DESTRUCTOR |
350 | { |
351 | } |
352 | ***/ |
353 | |
354 | FB_UDR_EXECUTE_PROCEDURE |
355 | { |
356 | counter = *(ISC_LONG*) (in + procedure->inOffsetStart); |
357 | end = *(ISC_LONG*) (in + procedure->inOffsetEnd); |
358 | |
359 | *(ISC_SHORT*) (out + procedure->outNullOffset) = FB_FALSE; |
360 | } |
361 | |
362 | // After procedure's execute definition, starts the result set definition. |
363 | |
364 | FB_UDR_FETCH_PROCEDURE |
365 | { |
366 | if (counter > end) |
367 | return false; |
368 | |
369 | *(ISC_LONG*) (out + procedure->outOffset) = counter++; |
370 | return true; |
371 | } |
372 | |
373 | /*** ResultSet destructor. |
374 | ~ResultSet() |
375 | { |
376 | } |
377 | ***/ |
378 | |
379 | // ResultSet variables. |
380 | ISC_LONG counter; |
381 | ISC_LONG end; |
382 | FB_UDR_END_PROCEDURE |
383 | |
384 | |
385 | /*** |
386 | create procedure gen_rows2 ( |
387 | start_n integer not null, |
388 | end_n integer not null |
389 | ) returns ( |
390 | n integer not null |
391 | ) |
392 | external name 'udrcpp_example!gen_rows2' |
393 | engine udr; |
394 | ***/ |
395 | FB_UDR_BEGIN_PROCEDURE(gen_rows2) |
396 | FB_MESSAGE(InMessage, |
397 | (FB_INTEGER, start) |
398 | (FB_INTEGER, end) |
399 | ); |
400 | |
401 | FB_MESSAGE(OutMessage, |
402 | (FB_INTEGER, result) |
403 | ); |
404 | |
405 | FB_UDR_EXECUTE_PROCEDURE |
406 | { |
407 | out->resultNull = FB_FALSE; |
408 | out->result = in->start - 1; |
409 | } |
410 | |
411 | FB_UDR_FETCH_PROCEDURE |
412 | { |
413 | return out->result++ < in->end; |
414 | } |
415 | FB_UDR_END_PROCEDURE |
416 | |
417 | |
418 | /*** |
419 | create procedure inc ( |
420 | count_n integer not null |
421 | ) returns ( |
422 | n0 integer not null, |
423 | n1 integer not null, |
424 | n2 integer not null, |
425 | n3 integer not null, |
426 | n4 integer not null |
427 | ) |
428 | external name 'udrcpp_example!inc' |
429 | engine udr; |
430 | ***/ |
431 | // This is a sample procedure demonstrating how the scopes of variables works. |
432 | // n1 and n2 are on the Procedure scope, i.e., they're shared for each execution of the same cached |
433 | // metadata object. |
434 | // n3 and n4 are on the ResultSet scope, i.e., each procedure execution have they own instances. |
435 | FB_UDR_BEGIN_PROCEDURE(inc) |
436 | FB_MESSAGE(InMessage, |
437 | (FB_INTEGER, count) |
438 | ); |
439 | |
440 | FB_MESSAGE(OutMessage, |
441 | (FB_INTEGER, n0) |
442 | (FB_INTEGER, n1) |
443 | (FB_INTEGER, n2) |
444 | (FB_INTEGER, n3) |
445 | (FB_INTEGER, n4) |
446 | ); |
447 | |
448 | ISC_LONG n1; |
449 | |
450 | // This is how a procedure (class) initializer is written. |
451 | // ResultSet variables are not accessible here. |
452 | // If there is nothing to initialize, it can be completelly suppressed. |
453 | FB_UDR_CONSTRUCTOR |
454 | , n1(0), |
455 | n2(0) |
456 | { |
457 | } |
458 | |
459 | ISC_LONG n2; |
460 | |
461 | // FB_UDR_EXECUTE_PROCEDURE starts the ResultSet scope. |
462 | FB_UDR_EXECUTE_PROCEDURE |
463 | // This is the ResultSet (class) initializer. |
464 | , n3(procedure->n1), // n3 will start with the next value for n1 of the last execution |
465 | n4(0) |
466 | { |
467 | out->n0Null = out->n1Null = out->n2Null = out->n3Null = out->n4Null = FB_FALSE; |
468 | |
469 | out->n0 = 0; |
470 | |
471 | // In the execute method, the procedure scope must be accessed using the 'procedure' pointer. |
472 | procedure->n1 = 0; |
473 | |
474 | // We don't touch n2 here, so it incremented counter will be kept after each execution. |
475 | |
476 | // The ResultSet scope must be accessed directly, i.e., they're member variables of the |
477 | // 'this' pointer. |
478 | ++n4; |
479 | } |
480 | |
481 | ISC_LONG n3; |
482 | |
483 | // FB_UDR_FETCH_PROCEDURE must be always after FB_UDR_EXECUTE_PROCEDURE. |
484 | FB_UDR_FETCH_PROCEDURE |
485 | { |
486 | if (out->n0++ <= in->count) |
487 | { |
488 | out->n1 = ++procedure->n1; |
489 | out->n2 = ++procedure->n2; |
490 | out->n3 = ++n3; |
491 | out->n4 = ++n4; |
492 | return true; |
493 | } |
494 | |
495 | return false; |
496 | } |
497 | |
498 | ISC_LONG n4; |
499 | FB_UDR_END_PROCEDURE |
500 | |
501 | |
502 | /*** |
503 | Sample usage: |
504 | |
505 | create database 'c:\temp\slave.fdb'; |
506 | create table persons ( |
507 | id integer not null, |
508 | name varchar(60) not null, |
509 | address varchar(60), |
510 | info blob sub_type text |
511 | ); |
512 | commit; |
513 | |
514 | create database 'c:\temp\master.fdb'; |
515 | create table persons ( |
516 | id integer not null, |
517 | name varchar(60) not null, |
518 | address varchar(60), |
519 | info blob sub_type text |
520 | ); |
521 | |
522 | create table replicate_config ( |
523 | name varchar(31) not null, |
524 | data_source varchar(255) not null |
525 | ); |
526 | |
527 | insert into replicate_config (name, data_source) |
528 | values ('ds1', 'c:\temp\slave.fdb'); |
529 | |
530 | create trigger persons_replicate |
531 | after insert on persons |
532 | external name 'udrcpp_example!replicate!ds1' |
533 | engine udr; |
534 | |
535 | create trigger persons_replicate2 |
536 | after insert on persons |
537 | external name 'udrcpp_example!replicate_persons!ds1' |
538 | engine udr; |
539 | ***/ |
540 | FB_UDR_BEGIN_TRIGGER(replicate) |
541 | // Without FieldsMessage definition, messages will be byte-based. |
542 | |
543 | FB_UDR_CONSTRUCTOR |
544 | , triggerMetadata(StatusException::check(status, metadata->getTriggerMetadata(status))) |
545 | { |
546 | ISC_STATUS_ARRAY statusVector = {0}; |
547 | isc_db_handle dbHandle = getIscDbHandle(context); |
548 | isc_tr_handle trHandle = getIscTrHandle(context); |
549 | |
550 | isc_stmt_handle stmtHandle = 0; |
551 | StatusException::checkStatus(isc_dsql_allocate_statement( |
552 | statusVector, &dbHandle, &stmtHandle), statusVector); |
553 | StatusException::checkStatus(isc_dsql_prepare(statusVector, &trHandle, &stmtHandle, 0, |
554 | "select data_source from replicate_config where name = ?" , |
555 | SQL_DIALECT_CURRENT, NULL), statusVector); |
556 | |
557 | const char* table = StatusException::check(status, metadata->getTriggerTable(status)); |
558 | |
559 | // Skip the first exclamation point, separating the module name and entry point. |
560 | const char* info = StatusException::check(status, |
561 | strchr(metadata->getEntryPoint(status), '!')); |
562 | |
563 | // Skip the second exclamation point, separating the entry point and the misc info (config). |
564 | if (info) |
565 | info = strchr(info + 1, '!'); |
566 | |
567 | if (info) |
568 | ++info; |
569 | else |
570 | info = "" ; |
571 | |
572 | XSQLDA* inSqlDa = reinterpret_cast<XSQLDA*>(new char[(XSQLDA_LENGTH(1))]); |
573 | inSqlDa->version = SQLDA_VERSION1; |
574 | inSqlDa->sqln = 1; |
575 | StatusException::checkStatus(isc_dsql_describe_bind(statusVector, &stmtHandle, |
576 | SQL_DIALECT_CURRENT, inSqlDa), statusVector); |
577 | inSqlDa->sqlvar[0].sqldata = new char[sizeof(short) + inSqlDa->sqlvar[0].sqllen]; |
578 | strncpy(inSqlDa->sqlvar[0].sqldata + sizeof(short), info, inSqlDa->sqlvar[0].sqllen); |
579 | *reinterpret_cast<short*>(inSqlDa->sqlvar[0].sqldata) = strlen(info); |
580 | |
581 | XSQLDA* outSqlDa = reinterpret_cast<XSQLDA*>(new char[(XSQLDA_LENGTH(1))]); |
582 | outSqlDa->version = SQLDA_VERSION1; |
583 | outSqlDa->sqln = 1; |
584 | StatusException::checkStatus(isc_dsql_describe(statusVector, &stmtHandle, |
585 | SQL_DIALECT_CURRENT, outSqlDa), statusVector); |
586 | outSqlDa->sqlvar[0].sqldata = new char[sizeof(short) + outSqlDa->sqlvar[0].sqllen + 1]; |
587 | outSqlDa->sqlvar[0].sqldata[sizeof(short) + outSqlDa->sqlvar[0].sqllen] = '\0'; |
588 | |
589 | StatusException::checkStatus(isc_dsql_execute2(statusVector, &trHandle, &stmtHandle, |
590 | SQL_DIALECT_CURRENT, inSqlDa, outSqlDa), statusVector); |
591 | StatusException::checkStatus(isc_dsql_free_statement( |
592 | statusVector, &stmtHandle, DSQL_unprepare), statusVector); |
593 | |
594 | delete [] inSqlDa->sqlvar[0].sqldata; |
595 | delete [] reinterpret_cast<char*>(inSqlDa); |
596 | |
597 | unsigned count = StatusException::check(status, triggerMetadata->getCount(status)); |
598 | |
599 | char buffer[65536]; |
600 | strcpy(buffer, "execute block (\n" ); |
601 | |
602 | for (unsigned i = 0; i < count; ++i) |
603 | { |
604 | if (i > 0) |
605 | strcat(buffer, ",\n" ); |
606 | |
607 | const char* name = StatusException::check(status, triggerMetadata->getField(status, i)); |
608 | |
609 | strcat(buffer, " p" ); |
610 | sprintf(buffer + strlen(buffer), "%d type of column \"%s\".\"%s\" = ?" , i, table, name); |
611 | } |
612 | |
613 | strcat(buffer, |
614 | ")\n" |
615 | "as\n" |
616 | "begin\n" |
617 | " execute statement ('insert into \"" ); |
618 | |
619 | strcat(buffer, table); |
620 | strcat(buffer, "\" (" ); |
621 | |
622 | for (unsigned i = 0; i < count; ++i) |
623 | { |
624 | if (i > 0) |
625 | strcat(buffer, ", " ); |
626 | |
627 | const char* name = StatusException::check(status, triggerMetadata->getField(status, i)); |
628 | |
629 | strcat(buffer, "\"" ); |
630 | strcat(buffer, name); |
631 | strcat(buffer, "\"" ); |
632 | } |
633 | |
634 | strcat(buffer, ") values (" ); |
635 | |
636 | for (unsigned i = 0; i < count; ++i) |
637 | { |
638 | if (i > 0) |
639 | strcat(buffer, ", " ); |
640 | strcat(buffer, "?" ); |
641 | } |
642 | |
643 | strcat(buffer, ")') (" ); |
644 | |
645 | for (unsigned i = 0; i < count; ++i) |
646 | { |
647 | if (i > 0) |
648 | strcat(buffer, ", " ); |
649 | strcat(buffer, ":p" ); |
650 | sprintf(buffer + strlen(buffer), "%d" , i); |
651 | } |
652 | |
653 | strcat(buffer, ")\n on external data source '" ); |
654 | strcat(buffer, outSqlDa->sqlvar[0].sqldata + sizeof(short)); |
655 | strcat(buffer, "';\nend" ); |
656 | |
657 | IAttachment* attachment = StatusException::check(status, context->getAttachment(status)); |
658 | ITransaction* transaction = StatusException::check(status, context->getTransaction(status)); |
659 | |
660 | stmt.reset(StatusException::check(status, |
661 | attachment->prepare(status, transaction, 0, buffer, SQL_DIALECT_CURRENT, 0))); |
662 | |
663 | delete [] outSqlDa->sqlvar[0].sqldata; |
664 | delete [] reinterpret_cast<char*>(outSqlDa); |
665 | } |
666 | |
667 | /*** |
668 | FB_UDR_DESTRUCTOR |
669 | { |
670 | } |
671 | ***/ |
672 | |
673 | FB_UDR_EXECUTE_TRIGGER |
674 | { |
675 | ITransaction* transaction = StatusException::check(status, context->getTransaction(status)); |
676 | |
677 | // This will not work if the table has computed fields. |
678 | stmt->execute(status, transaction, triggerMetadata, newFields, NULL, NULL); |
679 | StatusException::check(status->get()); |
680 | } |
681 | |
682 | AutoRelease<IMessageMetadata> triggerMetadata; |
683 | AutoRelease<IStatement> stmt; |
684 | FB_UDR_END_TRIGGER |
685 | |
686 | |
687 | FB_UDR_BEGIN_TRIGGER(replicate_persons) |
688 | // Order of fields does not need to match the fields order in the table, but it should match |
689 | // the order of fields in the SQL command constructed in the initialization. |
690 | FB_TRIGGER_MESSAGE(FieldsMessage, |
691 | (FB_INTEGER, id, "ID" ) |
692 | (FB_BLOB, info, "INFO" ) |
693 | ///(FB_VARCHAR(60 * 4), address, "ADDRESS") |
694 | (FB_VARCHAR(60 * 4), name, "NAME" ) |
695 | ); |
696 | |
697 | FB_UDR_CONSTRUCTOR |
698 | , triggerMetadata(StatusException::check(status, metadata->getTriggerMetadata(status))) |
699 | { |
700 | ISC_STATUS_ARRAY statusVector = {0}; |
701 | isc_db_handle dbHandle = getIscDbHandle(context); |
702 | isc_tr_handle trHandle = getIscTrHandle(context); |
703 | |
704 | isc_stmt_handle stmtHandle = 0; |
705 | StatusException::checkStatus(isc_dsql_allocate_statement( |
706 | statusVector, &dbHandle, &stmtHandle), statusVector); |
707 | StatusException::checkStatus(isc_dsql_prepare(statusVector, &trHandle, &stmtHandle, 0, |
708 | "select data_source from replicate_config where name = ?" , |
709 | SQL_DIALECT_CURRENT, NULL), statusVector); |
710 | |
711 | const char* table = StatusException::check(status, metadata->getTriggerTable(status)); |
712 | |
713 | // Skip the first exclamation point, separating the module name and entry point. |
714 | const char* info = StatusException::check(status, |
715 | strchr(metadata->getEntryPoint(status), '!')); |
716 | |
717 | // Skip the second exclamation point, separating the entry point and the misc info (config). |
718 | if (info) |
719 | info = strchr(info + 1, '!'); |
720 | |
721 | if (info) |
722 | ++info; |
723 | else |
724 | info = "" ; |
725 | |
726 | XSQLDA* inSqlDa = reinterpret_cast<XSQLDA*>(new char[(XSQLDA_LENGTH(1))]); |
727 | inSqlDa->version = SQLDA_VERSION1; |
728 | inSqlDa->sqln = 1; |
729 | StatusException::checkStatus(isc_dsql_describe_bind( |
730 | statusVector, &stmtHandle, SQL_DIALECT_CURRENT, inSqlDa), statusVector); |
731 | inSqlDa->sqlvar[0].sqldata = new char[sizeof(short) + inSqlDa->sqlvar[0].sqllen]; |
732 | strncpy(inSqlDa->sqlvar[0].sqldata + sizeof(short), info, inSqlDa->sqlvar[0].sqllen); |
733 | *reinterpret_cast<short*>(inSqlDa->sqlvar[0].sqldata) = strlen(info); |
734 | |
735 | XSQLDA* outSqlDa = reinterpret_cast<XSQLDA*>(new char[(XSQLDA_LENGTH(1))]); |
736 | outSqlDa->version = SQLDA_VERSION1; |
737 | outSqlDa->sqln = 1; |
738 | StatusException::checkStatus(isc_dsql_describe( |
739 | statusVector, &stmtHandle, SQL_DIALECT_CURRENT, outSqlDa), statusVector); |
740 | outSqlDa->sqlvar[0].sqldata = new char[sizeof(short) + outSqlDa->sqlvar[0].sqllen + 1]; |
741 | outSqlDa->sqlvar[0].sqldata[sizeof(short) + outSqlDa->sqlvar[0].sqllen] = '\0'; |
742 | |
743 | StatusException::checkStatus(isc_dsql_execute2(statusVector, &trHandle, &stmtHandle, |
744 | SQL_DIALECT_CURRENT, inSqlDa, outSqlDa), statusVector); |
745 | StatusException::checkStatus(isc_dsql_free_statement( |
746 | statusVector, &stmtHandle, DSQL_unprepare), statusVector); |
747 | |
748 | delete [] inSqlDa->sqlvar[0].sqldata; |
749 | delete [] reinterpret_cast<char*>(inSqlDa); |
750 | |
751 | char buffer[65536]; |
752 | strcpy(buffer, |
753 | "execute block (\n" |
754 | " id type of column PERSONS.ID = ?,\n" |
755 | " info type of column PERSONS.INFO = ?,\n" |
756 | ///" address type of column PERSONS.ADDRESS = ?,\n" |
757 | " name type of column PERSONS.NAME = ?\n" |
758 | ")" |
759 | "as\n" |
760 | "begin\n" |
761 | " execute statement ('insert into persons (id, name/***, address***/, info)\n" |
762 | " values (?, ?/***, ?***/, ?)') (:id, :name/***, :address***/, :info)\n" |
763 | " on external data source '" ); |
764 | strcat(buffer, outSqlDa->sqlvar[0].sqldata + sizeof(short)); |
765 | strcat(buffer, "';\nend" ); |
766 | |
767 | IAttachment* attachment = StatusException::check(status, context->getAttachment(status)); |
768 | ITransaction* transaction = StatusException::check(status, context->getTransaction(status)); |
769 | |
770 | stmt.reset(StatusException::check(status, |
771 | attachment->prepare(status, transaction, 0, buffer, SQL_DIALECT_CURRENT, 0))); |
772 | |
773 | delete [] outSqlDa->sqlvar[0].sqldata; |
774 | delete [] reinterpret_cast<char*>(outSqlDa); |
775 | } |
776 | |
777 | /*** |
778 | FB_UDR_DESTRUCTOR |
779 | { |
780 | } |
781 | ***/ |
782 | |
783 | FB_UDR_EXECUTE_TRIGGER |
784 | { |
785 | ITransaction* transaction = StatusException::check(status, context->getTransaction(status)); |
786 | |
787 | stmt->execute(status, transaction, triggerMetadata, newFields, NULL, NULL); |
788 | StatusException::check(status->get()); |
789 | } |
790 | |
791 | AutoRelease<IMessageMetadata> triggerMetadata; |
792 | AutoRelease<IStatement> stmt; |
793 | FB_UDR_END_TRIGGER |
794 | |