1 // Copyright (c) 1999-2018 David Muse
2 // See the file COPYING for more information
3 
4 #include <sqlrelay/sqlrserver.h>
5 
6 #include <rudiments/domnode.h>
7 #include <rudiments/stdio.h>
8 //#define DEBUG_MESSAGES 1
9 #include <rudiments/debugprint.h>
10 
11 #include <config.h>
12 
13 #ifndef SQLRELAY_ENABLE_SHARED
14 	extern "C" {
15 		#include "sqlrtriggerdeclarations.cpp"
16 	}
17 #endif
18 
19 class sqlrtriggerplugin {
20 	public:
21 		sqlrtrigger	*tr;
22 		dynamiclib	*dl;
23 };
24 
25 class sqlrtriggersprivate {
26 	friend class sqlrtriggers;
27 	private:
28 		sqlrservercontroller	*_cont;
29 
30 		bool	_debug;
31 
32 		singlylinkedlist< sqlrtriggerplugin * >	_beforetriggers;
33 		singlylinkedlist< sqlrtriggerplugin * >	_aftertriggers;
34 };
35 
sqlrtriggers(sqlrservercontroller * cont)36 sqlrtriggers::sqlrtriggers(sqlrservercontroller *cont) {
37 	debugFunction();
38 	pvt=new sqlrtriggersprivate;
39 	pvt->_cont=cont;
40 	pvt->_debug=cont->getConfig()->getDebugTriggers();
41 }
42 
~sqlrtriggers()43 sqlrtriggers::~sqlrtriggers() {
44 	debugFunction();
45 	unload();
46 	delete pvt;
47 }
48 
load(domnode * parameters)49 bool sqlrtriggers::load(domnode *parameters) {
50 	debugFunction();
51 
52 	unload();
53 
54 	// run through the trigger list
55 	for (domnode *trigger=parameters->getFirstTagChild();
56 		!trigger->isNullNode(); trigger=trigger->getNextTagSibling()) {
57 
58 		bool	before=(charstring::contains(
59 					trigger->getAttributeValue("when"),
60 					"before") ||
61 				charstring::contains(
62 					trigger->getAttributeValue("when"),
63 					"both"));
64 		bool	after=(charstring::contains(
65 					trigger->getAttributeValue("when"),
66 					"after") ||
67 				charstring::contains(
68 					trigger->getAttributeValue("when"),
69 					"both"));
70 
71 		// load the trigger
72 		sqlrtriggerplugin	*p=loadTrigger(trigger);
73 		if (!p) {
74 			continue;
75 		}
76 
77 		// add trigger to before list
78 		if (before) {
79 			if (pvt->_debug) {
80 				stdoutput.printf("before trigger\n");
81 			}
82 			pvt->_beforetriggers.append(p);
83 		}
84 
85 		// add trigger to after list
86 		if (after) {
87 			if (pvt->_debug) {
88 				stdoutput.printf("after trigger\n");
89 			}
90 			pvt->_aftertriggers.append(p);
91 		}
92 	}
93 	return true;
94 }
95 
unload()96 void sqlrtriggers::unload() {
97 	debugFunction();
98 	for (singlylinkedlistnode< sqlrtriggerplugin * > *bnode=
99 				pvt->_beforetriggers.getFirst();
100 					bnode; bnode=bnode->getNext()) {
101 		sqlrtriggerplugin	*sqlt=bnode->getValue();
102 		if (!pvt->_aftertriggers.find(sqlt)) {
103 			delete sqlt->tr;
104 			delete sqlt->dl;
105 			delete sqlt;
106 		}
107 	}
108 	pvt->_beforetriggers.clear();
109 	for (singlylinkedlistnode< sqlrtriggerplugin * > *anode=
110 				pvt->_aftertriggers.getFirst();
111 					anode; anode=anode->getNext()) {
112 		sqlrtriggerplugin	*sqlt=anode->getValue();
113 		delete sqlt->tr;
114 		delete sqlt->dl;
115 		delete sqlt;
116 	}
117 	pvt->_aftertriggers.clear();
118 }
119 
loadTrigger(domnode * trigger)120 sqlrtriggerplugin *sqlrtriggers::loadTrigger(domnode *trigger) {
121 
122 	debugFunction();
123 
124 	// ignore non-triggers
125 	if (charstring::compare(trigger->getName(),"trigger")) {
126 		return NULL;
127 	}
128 
129 	// get the trigger name
130 	const char	*module=trigger->getAttributeValue("module");
131 	if (!charstring::length(module)) {
132 		// try "file", that's what it used to be called
133 		module=trigger->getAttributeValue("file");
134 		if (!charstring::length(module)) {
135 			return NULL;
136 		}
137 	}
138 
139 	if (pvt->_debug) {
140 		stdoutput.printf("loading trigger: %s\n",module);
141 	}
142 
143 #ifdef SQLRELAY_ENABLE_SHARED
144 	// load the trigger module
145 	stringbuffer	modulename;
146 	modulename.append(pvt->_cont->getPaths()->getLibExecDir());
147 	modulename.append(SQLR);
148 	modulename.append("trigger_");
149 	modulename.append(module)->append(".")->append(SQLRELAY_MODULESUFFIX);
150 	dynamiclib	*dl=new dynamiclib();
151 	if (!dl->open(modulename.getString(),true,true)) {
152 		stdoutput.printf("failed to load trigger module: %s\n",module);
153 		char	*error=dl->getError();
154 		stdoutput.printf("%s\n",(error)?error:"");
155 		delete[] error;
156 		delete dl;
157 		return NULL;
158 	}
159 
160 	// load the trigger itself
161 	stringbuffer	functionname;
162 	functionname.append("new_sqlrtrigger_")->append(module);
163 	sqlrtrigger *(*newTrigger)(sqlrservercontroller *,
164 						sqlrtriggers *,
165 						domnode *)=
166 			(sqlrtrigger *(*)(sqlrservercontroller *,
167 						sqlrtriggers *,
168 						domnode *))
169 				dl->getSymbol(functionname.getString());
170 	if (!newTrigger) {
171 		stdoutput.printf("failed to load trigger: %s\n",module);
172 		char	*error=dl->getError();
173 		stdoutput.printf("%s\n",(error)?error:"");
174 		delete[] error;
175 		dl->close();
176 		delete dl;
177 		return NULL;
178 	}
179 	sqlrtrigger	*tr=(*newTrigger)(pvt->_cont,this,trigger);
180 
181 #else
182 
183 	dynamiclib	*dl=NULL;
184 	sqlrtrigger	*tr;
185 	#include "sqlrtriggerassignments.cpp"
186 	{
187 		tr=NULL;
188 	}
189 #endif
190 
191 	if (pvt->_debug) {
192 		stdoutput.printf("success\n");
193 	}
194 
195 	// build and return the plugin
196 	sqlrtriggerplugin	*sqltp=new sqlrtriggerplugin;
197 	sqltp->tr=tr;
198 	sqltp->dl=dl;
199 	return sqltp;
200 }
201 
runBeforeTriggers(sqlrserverconnection * sqlrcon,sqlrservercursor * sqlrcur)202 void sqlrtriggers::runBeforeTriggers(sqlrserverconnection *sqlrcon,
203 					sqlrservercursor *sqlrcur) {
204 	debugFunction();
205 	run(sqlrcon,sqlrcur,&pvt->_beforetriggers,true,NULL);
206 }
207 
runAfterTriggers(sqlrserverconnection * sqlrcon,sqlrservercursor * sqlrcur,bool * success)208 void sqlrtriggers::runAfterTriggers(sqlrserverconnection *sqlrcon,
209 						sqlrservercursor *sqlrcur,
210 						bool *success) {
211 	debugFunction();
212 	run(sqlrcon,sqlrcur,&pvt->_aftertriggers,false,success);
213 }
214 
run(sqlrserverconnection * sqlrcon,sqlrservercursor * sqlrcur,singlylinkedlist<sqlrtriggerplugin * > * list,bool before,bool * success)215 void sqlrtriggers::run(sqlrserverconnection *sqlrcon,
216 				sqlrservercursor *sqlrcur,
217 				singlylinkedlist< sqlrtriggerplugin * > *list,
218 				bool before,
219 				bool *success) {
220 	debugFunction();
221 	for (singlylinkedlistnode< sqlrtriggerplugin * > *node=list->getFirst();
222 						node; node=node->getNext()) {
223 		if (pvt->_debug) {
224 			stdoutput.printf("\nrunning %s trigger...\n\n",
225 						(before)?"before":"after");
226 		}
227 		node->getValue()->tr->run(sqlrcon,sqlrcur,before,success);
228 	}
229 }
230 
endTransaction(bool commit)231 void sqlrtriggers::endTransaction(bool commit) {
232 	for (singlylinkedlistnode< sqlrtriggerplugin * >
233 				*node=pvt->_beforetriggers.getFirst();
234 				node; node=node->getNext()) {
235 		node->getValue()->tr->endTransaction(commit);
236 	}
237 	for (singlylinkedlistnode< sqlrtriggerplugin * >
238 				*node=pvt->_aftertriggers.getFirst();
239 				node; node=node->getNext()) {
240 		node->getValue()->tr->endTransaction(commit);
241 	}
242 }
243 
endSession()244 void sqlrtriggers::endSession() {
245 	for (singlylinkedlistnode< sqlrtriggerplugin * >
246 				*node=pvt->_beforetriggers.getFirst();
247 				node; node=node->getNext()) {
248 		node->getValue()->tr->endSession();
249 	}
250 	for (singlylinkedlistnode< sqlrtriggerplugin * >
251 				*node=pvt->_aftertriggers.getFirst();
252 				node; node=node->getNext()) {
253 		node->getValue()->tr->endSession();
254 	}
255 }
256