1 //////////////////////////////////////////////////////////////////////////
2 //
3 // pgAdmin III - PostgreSQL Tools
4 //
5 // Copyright (C) 2002 - 2016, The pgAdmin Development Team
6 // This software is released under the PostgreSQL Licence
7 //
8 // dbgTargetInfo.cpp - debugger
9 //
10 //////////////////////////////////////////////////////////////////////////
11 
12 #include "pgAdmin3.h"
13 #include "utils/pgDefs.h"
14 
15 // wxWindows headers
16 #include <wx/wx.h>
17 
18 // App headers
19 #include "debugger/dbgTargetInfo.h"
20 #include "debugger/dbgConst.h"
21 #include "utils/misc.h"
22 #include "utils/pgfeatures.h"
23 
24 #include <stdexcept>
25 
26 ////////////////////////////////////////////////////////////////////////////////
27 // dbgTargetInfo constructor
28 //
29 //    This class implements a container that holds information necessary to invoke
30 //  a debugger target (a function or procedure).
31 //
32 //  When the constructor is called, it sends a query to the server to retreive:
33 //    	the OID of the target,
34 //    	the name of the target,
35 //    	the name of the schema in which the target is defined
36 //    	the name of the language in which the target is defined
37 //    	the number of arguments expected by the target
38 //    	the argument names
39 //    	the argument types
40 //    	the argument modes (IN, OUT, or INOUT)
41 //    	the target type (function or procedure)
42 //
43 //    This class offers a number of (inline) member functions that you can call
44 //  to extract the above information after it's been queried from the server.
45 
dbgTargetInfo(Oid _target,pgConn * _conn)46 dbgTargetInfo::dbgTargetInfo(Oid _target, pgConn *_conn)
47 	: m_args(NULL), m_inputParamCnt(0), m_hasVariadic(false)
48 {
49 	wxMBConv *conv = _conn->GetConv();
50 	wxString targetQuery =
51 	    wxT("SELECT\n")
52 	    wxT("	p.proname AS name, l.lanname, p.proretset, p.prorettype, y.typname AS rettype,\n")
53 	    wxT("	CASE WHEN proallargtypes IS NOT NULL THEN\n")
54 	    wxT("			pg_catalog.array_to_string(ARRAY(\n")
55 	    wxT("				SELECT\n")
56 	    wxT("					pg_catalog.format_type(p.proallargtypes[s.i], NULL)\n")
57 	    wxT("				FROM\n")
58 	    wxT("					pg_catalog.generate_series(0, pg_catalog.array_upper(\n")
59 	    wxT("						p.proallargtypes, 1)) AS s(i)), ',')\n")
60 	    wxT("		ELSE\n")
61 	    wxT("			pg_catalog.array_to_string(ARRAY(\n")
62 	    wxT("				SELECT\n")
63 	    wxT("					pg_catalog.format_type(p.proargtypes[s.i], NULL)\n")
64 	    wxT("				FROM\n")
65 	    wxT("					pg_catalog.generate_series(0, pg_catalog.array_upper(\n")
66 	    wxT("						p.proargtypes, 1)) AS s(i)), ',')\n")
67 	    wxT("	END AS proargtypenames,\n")
68 	    wxT("	CASE WHEN proallargtypes IS NOT NULL THEN\n")
69 	    wxT("			pg_catalog.array_to_string(ARRAY(\n")
70 	    wxT("				SELECT proallargtypes[s.i] FROM\n")
71 	    wxT("					pg_catalog.generate_series(0, pg_catalog.array_upper(proallargtypes, 1)) s(i)), ',')\n")
72 	    wxT("		ELSE\n")
73 	    wxT("			pg_catalog.array_to_string(ARRAY(\n")
74 	    wxT("				SELECT proargtypes[s.i] FROM\n")
75 	    wxT("					pg_catalog.generate_series(0, pg_catalog.array_upper(proargtypes, 1)) s(i)), ',')\n")
76 	    wxT("	END AS proargtypes,\n")
77 	    wxT("	pg_catalog.array_to_string(p.proargnames, ',') AS proargnames,\n")
78 	    wxT("	pg_catalog.array_to_string(proargmodes, ',') AS proargmodes,\n");
79 
80 	if (_conn->GetIsEdb())
81 	{
82 		targetQuery +=
83 		    wxT("	CASE WHEN n.nspparent <> 0 THEN n.oid ELSE 0 END AS pkg,\n")
84 		    wxT("	CASE WHEN n.nspparent <> 0 THEN n.nspname ELSE '' END AS pkgname,\n")
85 		    wxT("	CASE WHEN n.nspparent <> 0 THEN (SELECT oid FROM pg_proc WHERE pronamespace=n.oid AND proname='cons') ELSE 0 END AS pkgconsoid,\n")
86 		    wxT("	CASE WHEN n.nspparent <> 0 THEN g.oid ELSE n.oid END AS schema,\n")
87 		    wxT("	CASE WHEN n.nspparent <> 0 THEN g.nspname ELSE n.nspname END AS schemaname,\n")
88 		    wxT("	NOT (l.lanname = 'edbspl' AND protype = '1') AS isfunc,\n");
89 	}
90 	else
91 	{
92 		targetQuery +=
93 		    wxT("	0 AS pkg,\n")
94 		    wxT("	'' AS pkgname,\n")
95 		    wxT("	0 AS pkgconsoid,\n")
96 		    wxT("	n.oid     AS schema,\n")
97 		    wxT("	n.nspname AS schemaname,\n")
98 		    wxT("	true AS isfunc,\n");
99 	}
100 	if (_conn->BackendMinimumVersion(8, 4))
101 	{
102 		targetQuery += wxT("	pg_catalog.pg_get_function_identity_arguments(p.oid) AS signature,");
103 	}
104 	else if (_conn->BackendMinimumVersion(8, 1))
105 	{
106 		targetQuery +=
107 		    wxT("	CASE\n")
108 		    wxT("		WHEN proallargtypes IS NOT NULL THEN pg_catalog.array_to_string(ARRAY(\n")
109 		    wxT("			SELECT\n")
110 		    wxT("				CASE\n")
111 		    wxT("					WHEN p.proargmodes[s.i] = 'i' THEN ''\n")
112 		    wxT("					WHEN p.proargmodes[s.i] = 'o' THEN 'OUT '\n")
113 		    wxT("					WHEN p.proargmodes[s.i] = 'b' THEN 'INOUT '\n")
114 		    wxT("					WHEN p.proargmodes[s.i] = 'v' THEN 'VARIADIC '\n")
115 		    wxT("				END ||\n")
116 		    wxT("				CASE WHEN COALESCE(p.proargnames[s.i], '') = '' THEN ''\n")
117 		    wxT("					ELSE p.proargnames[s.i] || ' '\n")
118 		    wxT("				END ||\n")
119 		    wxT("				pg_catalog.format_type(p.proallargtypes[s.i], NULL)\n")
120 		    wxT("			FROM\n")
121 		    wxT("				pg_catalog.generate_series(1, pg_catalog.array_upper(p.proallargtypes, 1)) AS s(i)\n")
122 		    wxT("			WHERE p.proargmodes[s.i] != 't'\n")
123 		    wxT("			), ', ')\n")
124 		    wxT("		ELSE\n")
125 		    wxT("			pg_catalog.array_to_string(ARRAY(\n")
126 		    wxT("				SELECT\n")
127 		    wxT("					CASE\n")
128 		    wxT("						WHEN COALESCE(p.proargnames[s.i+1], '') = '' THEN ''\n")
129 		    wxT("						ELSE p.proargnames[s.i+1] || ' '\n")
130 		    wxT("					END ||\n")
131 		    wxT("					pg_catalog.format_type(p.proargtypes[s.i], NULL)\n")
132 		    wxT("				FROM\n")
133 		    wxT("					pg_catalog.generate_series(1, pg_catalog.array_upper(p.proargtypes, 1)) AS s(i)\n")
134 		    wxT("				), ', ')\n")
135 		    wxT("	END AS signature,\n");
136 	}
137 	else
138 	{
139 		targetQuery += wxT("	'' AS signature,");
140 	}
141 
142 	if (_conn->HasFeature(FEATURE_FUNCTION_DEFAULTS))
143 	{
144 		// EnterpriseDB 8.3R2
145 		if(!_conn->BackendMinimumVersion(8, 4))
146 		{
147 			targetQuery +=
148 			    wxT("	pg_catalog.array_to_string(ARRAY(\n")
149 			    wxT("	SELECT\n")
150 			    wxT("		CASE WHEN p.proargdefvals[x.j] != '-' THEN\n")
151 			    wxT("			pg_catalog.pg_get_expr(p.proargdefvals[x.j], 'pg_catalog.pg_class'::regclass, true)\n")
152 			    wxT("		ELSE '-' END\n")
153 			    wxT("	FROM\n")
154 			    wxT("		pg_catalog.generate_series(1, pg_catalog.array_upper(p.proargdefvals, 1)) AS x(j)\n")
155 			    wxT("	), ',') AS proargdefaults,\n")
156 			    wxT("	CASE WHEN p.proargdefvals IS NULL THEN '0'\n")
157 			    wxT("		ELSE pg_catalog.array_upper(p.proargdefvals, 1)::text END AS pronargdefaults\n");
158 		}
159 		else
160 		{
161 			targetQuery +=
162 			    wxT("	pg_catalog.pg_get_expr(p.proargdefaults, 'pg_catalog.pg_class'::regclass, false) AS proargdefaults,\n")
163 			    wxT("	p.pronargdefaults\n");
164 		}
165 	}
166 	else
167 	{
168 		targetQuery +=
169 		    wxT("	'' AS proargdefaults, 0 AS pronargdefaults\n");
170 	}
171 	targetQuery +=
172 	    wxT("FROM\n")
173 	    wxT("	pg_catalog.pg_proc p\n")
174 	    wxT("	LEFT JOIN pg_catalog.pg_namespace n ON p.pronamespace = n.oid\n")
175 	    wxT("	LEFT JOIN pg_catalog.pg_language l ON p.prolang = l.oid\n")
176 	    wxT("	LEFT JOIN pg_catalog.pg_type y ON p.prorettype = y.oid\n");
177 	if(_conn->GetIsEdb())
178 	{
179 		targetQuery +=
180 		    wxT("	LEFT JOIN pg_catalog.pg_namespace g ON n.nspparent = g.oid\n");
181 	}
182 	targetQuery +=
183 	    wxString::Format(wxT("WHERE p.oid = %ld"), (long)_target);
184 
185 	pgSet *set = _conn->ExecuteSet(targetQuery);
186 
187 	if (conv == NULL)
188 	{
189 		conv = &wxConvLibc;
190 	}
191 
192 	if (!set || _conn->GetLastResultStatus() != PGRES_TUPLES_OK)
193 	{
194 		if (set)
195 			delete set;
196 		wxLogError(_("Could not fetch information about the debugger target.\n") +
197 		           _conn->GetLastError());
198 
199 		throw (std::runtime_error(
200 		           (const char *)(_conn->GetLastError().c_str())));
201 	}
202 
203 	if (set->NumRows() == 0)
204 	{
205 		delete set;
206 
207 		wxLogError(_("Can't find the debugging target"));
208 		throw (std::runtime_error("Can't find target!"));
209 	}
210 
211 	m_oid           = _target;
212 	m_name          = set->GetVal(wxT("name"));
213 	m_schema        = set->GetVal(wxT("schemaname"));
214 	m_package       = set->GetVal(wxT("pkgname"));
215 	m_language      = set->GetVal(wxT("lanname"));
216 	m_returnType    = set->GetVal(wxT("rettype"));
217 	m_funcSignature = set->GetVal(wxT("signature"));
218 	m_isFunction    = set->GetBool(wxT("isfunc"));
219 	m_returnsSet    = set->GetBool(wxT("proretset"));
220 	m_pkgOid        = set->GetOid(wxT("pkg"));
221 	m_pkgInitOid    = set->GetOid(wxT("pkgconsoid"));
222 	m_schemaOid     = set->GetOid(wxT("schema"));
223 	m_fqName        = qtIdent(m_schema) + wxT(".") +
224 	                  (m_pkgOid == 0 ? wxT("") : (qtIdent(m_package) + wxT("."))) + qtIdent(m_name);
225 
226 	wxArrayString argModes, argNames, argTypes, argTypeOids, argDefVals,
227 	              argBaseTypes;
228 
229 	// Fetch Argument Modes (if available)
230 	if (!set->IsNull(set->ColNumber(wxT("proargmodes"))))
231 	{
232 		wxString tmp;
233 		tmp = set->GetVal(wxT("proargmodes"));
234 
235 		if (!tmp.IsEmpty())
236 			getArrayFromCommaSeparatedList(tmp, argModes);
237 	}
238 	// Fetch Argument Names (if available)
239 	if (!set->IsNull(set->ColNumber(wxT("proargnames"))))
240 	{
241 		wxString tmp;
242 		tmp = set->GetVal(wxT("proargnames"));
243 
244 		if (!tmp.IsEmpty())
245 			getArrayFromCommaSeparatedList(tmp, argNames);
246 	}
247 	// Fetch Argument Type-Names (if available)
248 	if (!set->IsNull(set->ColNumber(wxT("proargtypenames"))))
249 	{
250 		wxString tmp;
251 		tmp = set->GetVal(wxT("proargtypenames"));
252 
253 		if (!tmp.IsEmpty())
254 			getArrayFromCommaSeparatedList(tmp, argTypes);
255 	}
256 	// Fetch Argument Type-Names (if available)
257 	if (!set->IsNull(set->ColNumber(wxT("proargtypes"))))
258 	{
259 		wxString tmp;
260 		tmp = set->GetVal(wxT("proargtypes"));
261 		if (!tmp.IsEmpty())
262 			getArrayFromCommaSeparatedList(tmp, argTypeOids);
263 	}
264 
265 	size_t nArgDefs = (size_t)set->GetLong(wxT("pronargdefaults"));
266 	// Fetch Argument Default Values (if available)
267 	if (!set->IsNull(set->ColNumber(wxT("proargdefaults"))) && nArgDefs != 0)
268 	{
269 		wxString tmp;
270 		tmp = set->GetVal(wxT("proargdefaults"));
271 
272 		if (!tmp.IsEmpty())
273 			getArrayFromCommaSeparatedList(tmp, argDefVals);
274 	}
275 
276 	wxString argName, argDefVal;
277 	short    argMode;
278 	Oid      argTypeOid;
279 	size_t   argCnt = argTypes.Count();
280 
281 	// This function/procedure does not take any arguments
282 	if (argCnt == 0)
283 	{
284 		return;
285 	}
286 
287 	size_t idx = 0;
288 	m_args = new pgDbgArgs();
289 
290 	for (; idx < argCnt; idx++)
291 	{
292 		argTypeOid = (Oid)strtoul(argTypeOids[idx].mb_str(wxConvUTF8), 0, 10);
293 		argDefVal  = wxEmptyString;
294 
295 		argName = wxEmptyString;
296 		if (idx < argNames.Count())
297 			argName = argNames[idx];
298 
299 		if (argName.IsEmpty())
300 			argName.Printf( wxT( "dbgParam%d" ), (idx + 1));
301 
302 		if (idx < argModes.Count())
303 		{
304 			wxString tmp = argModes[idx];
305 			switch ((char)(tmp.c_str())[0])
306 			{
307 				case 'i':
308 					argMode = pgParam::PG_PARAM_IN;
309 					m_inputParamCnt++;
310 					break;
311 				case 'b':
312 					m_inputParamCnt++;
313 					argMode = pgParam::PG_PARAM_INOUT;
314 					break;
315 				case 'o':
316 					argMode = pgParam::PG_PARAM_OUT;
317 					break;
318 				case 'v':
319 					m_inputParamCnt++;
320 					argMode = pgParam::PG_PARAM_VARIADIC;
321 					m_hasVariadic = true;
322 					break;
323 				case 't':
324 					continue;
325 				default:
326 					m_inputParamCnt++;
327 					argMode = pgParam::PG_PARAM_IN;
328 					break;
329 			}
330 		}
331 		else
332 		{
333 			m_inputParamCnt++;
334 			argMode = pgParam::PG_PARAM_IN;
335 		}
336 
337 		// In EDBAS 90, if an SPL-function has both an OUT-parameter
338 		// and a return value (which is not possible on PostgreSQL otherwise),
339 		// the return value is transformed into an extra OUT-parameter
340 		// named "_retval_"
341 		if (argName == wxT("_retval_") && _conn->EdbMinimumVersion(9, 0))
342 		{
343 			// this will be the return type for this object
344 			m_returnType = argTypes[idx];
345 
346 			continue;
347 		}
348 
349 		m_args->Add(new dbgArgInfo(argName, argTypes[idx], argTypeOid, argMode));
350 	}
351 
352 	if (m_args->GetCount() == 0)
353 	{
354 		delete m_args;
355 		m_args = NULL;
356 
357 		return;
358 	}
359 
360 	if (nArgDefs != 0)
361 	{
362 		argCnt = m_args->GetCount();
363 
364 		// Set the default as the value for the argument
365 		for (idx = argCnt - 1;; idx--)
366 		{
367 			dbgArgInfo *arg = (dbgArgInfo *)((*m_args)[idx]);
368 
369 			if (arg->GetMode() == pgParam::PG_PARAM_INOUT ||
370 			        arg->GetMode() == pgParam::PG_PARAM_IN)
371 			{
372 				nArgDefs--;
373 
374 				if (argDefVals[nArgDefs] != wxT("-"))
375 				{
376 					arg->SetDefault(argDefVals[nArgDefs]);
377 				}
378 
379 				if (nArgDefs == 0)
380 				{
381 					break;
382 				}
383 			}
384 			if (idx == 0)
385 				break;
386 		}
387 	}
388 }
389 
390 ////////////////////////////////////////////////////////////////////////////////
391 // operator[]
392 //
393 //    This operator function makes it easy to index into the m_args array
394 //    using concise syntax.
operator [](int index)395 dbgArgInfo *dbgTargetInfo::operator[](int index)
396 {
397 	if (m_args == NULL)
398 		return (dbgArgInfo *)NULL;
399 
400 	if (index < 0 || index >= (int)m_args->GetCount())
401 		return (dbgArgInfo *)NULL;
402 
403 	return (dbgArgInfo *)((*m_args)[index]);
404 }
405 
dbgArgInfo(const wxString & _name,const wxString & _type,Oid _typeOid,short _mode)406 dbgArgInfo::dbgArgInfo(const wxString &_name, const wxString &_type, Oid _typeOid,
407                        short _mode)
408 	: m_name(_name), m_type(_type), m_typeOid(_typeOid), m_mode(_mode),
409 	  m_hasDefault(false), m_useDefault(false), m_null(false)
410 {
411 	if (!_type.EndsWith(wxT("[]"), &m_baseType))
412 	{
413 		m_baseType = wxEmptyString;
414 	}
415 }
416 
417 
GetParam(wxMBConv * _conv)418 pgParam *dbgArgInfo::GetParam(wxMBConv *_conv)
419 {
420 	return new pgParam(m_typeOid,
421 	                   (m_null ? (wxString *)NULL : &m_val),
422 	                   _conv, m_mode);
423 }
424 
AddForExecution(pgQueryThread * _thread)425 bool dbgTargetInfo::AddForExecution(pgQueryThread *_thread)
426 {
427 	wxASSERT(_thread != NULL);
428 
429 	if (_thread == NULL)
430 		return false;
431 
432 	pgConn *conn = _thread->GetConn();
433 
434 	pgParamsArray *params   = NULL;
435 	wxString       strQuery;
436 	bool           useCallable = false;
437 
438 	// Basically - we can call the function/target three ways:
439 	// 1. If it is a edbspl procedure, we can use callable statement
440 	// 2. If the database server is of type EnterpriseDB, and
441 	//    function/procedure is type 'edbspl', we should use the anonymous
442 	//    function block for:
443 	//    a. Version < 9.0
444 	//    b. Package function/procedure
445 	// 3. Otherwise, we should use the simple function call (except using EXEC
446 	//    for the procedure in 'edbspl' instead of using SELECT)
447 	if (_thread->SupportCallableStatement() &&
448 	        m_language == wxT("edbspl") &&
449 	        !m_isFunction)
450 	{
451 		useCallable = true;
452 		strQuery = wxT("CALL ") + m_fqName + wxT("(");
453 
454 		if (m_args)
455 		{
456 			params = new pgParamsArray();
457 			wxMBConv *conv = conn->GetConv();
458 
459 			for(int idx = 0; idx < (int)m_args->GetCount(); idx++)
460 			{
461 				params->Add(((*m_args)[idx])->GetParam(conv));
462 
463 				if (idx != 0)
464 					strQuery += wxT(", ");
465 				strQuery += wxString::Format(wxT("$%d::"), idx + 1) +
466 				            ((*m_args)[idx])->GetTypeName();
467 			}
468 		}
469 		strQuery += wxT(")");
470 	}
471 	else if (conn->GetIsEdb() && !conn->BackendMinimumVersion(9, 3))
472 	{
473 		wxString strDeclare, strStatement, strResult;
474 		bool useAnonymousBlock = false;
475 
476 		if (m_language == wxT("edbspl"))
477 		{
478 			useAnonymousBlock = true;
479 			if (!m_isFunction)
480 			{
481 				strStatement = wxT("\tEXEC ") + m_fqName;
482 			}
483 			else if (m_returnType == wxT("void") || m_returnsSet ||
484 			         !conn->BackendMinimumVersion(8, 4))
485 			{
486 				strStatement = wxT("\tPERFORM ") + m_fqName;
487 			}
488 			else
489 			{
490 				wxString resultVar = wxT("v_retVal");
491 				strStatement = wxT("\t") + resultVar + wxT(" := ") + m_fqName;
492 				strDeclare.Append(wxT("\t"))
493 				.Append(resultVar)
494 				.Append(wxT(" "))
495 				.Append(m_returnType)
496 				.Append(wxT(";\n"));
497 				strResult = wxT("\tDBMS_OUTPUT.PUT_LINE(E'\\n\\nResult:\\n--------\\n' || ") +
498 				            resultVar +
499 				            wxT("::text || E'\\n\\nNOTE: This is the result generated during the function execution by the debugger.\\n');\n");
500 			}
501 		}
502 		else
503 		{
504 			if (m_returnType == wxT("record"))
505 			{
506 				strStatement = wxT("\tSELECT ") + m_fqName;
507 			}
508 			else
509 			{
510 				strStatement = wxT("\tSELECT * FROM ") + m_fqName;
511 			}
512 		}
513 
514 		if (m_args && m_args->GetCount() > 0)
515 		{
516 			strStatement.Append(wxT("("));
517 
518 			for(int idx = 0, firstProcessed = false; idx < (int)m_args->GetCount(); idx++)
519 			{
520 				dbgArgInfo *arg = (*m_args)[idx];
521 
522 				if (!conn->EdbMinimumVersion(8, 4) &&
523 				        arg->GetMode() == pgParam::PG_PARAM_OUT &&
524 				        (!m_isFunction || m_language == wxT("edbspl")))
525 				{
526 					if (firstProcessed)
527 						strStatement.Append(wxT(", "));
528 					firstProcessed = true;
529 
530 					strStatement.Append(wxT("NULL::")).Append(arg->GetTypeName());
531 				}
532 				else if (conn->EdbMinimumVersion(8, 4) && useAnonymousBlock &&
533 				         (arg->GetMode() == pgParam::PG_PARAM_OUT ||
534 				          arg->GetMode() == pgParam::PG_PARAM_INOUT))
535 				{
536 					wxString strParam = wxString::Format(wxT("p_param%d"), idx);
537 
538 					strDeclare.Append(wxT("\t"))
539 					.Append(strParam)
540 					.Append(wxT(" "))
541 					.Append(arg->GetTypeName());
542 
543 					if (arg->GetMode() == pgParam::PG_PARAM_INOUT)
544 					{
545 						strDeclare.Append(wxT(" := "))
546 						.Append(arg->Null() ? wxT("NULL") : conn->qtDbString(arg->Value()))
547 						.Append(wxT("::"))
548 						.Append(arg->GetTypeName());
549 					}
550 					strDeclare.Append(wxT(";\n"));
551 
552 					if (firstProcessed)
553 						strStatement.Append(wxT(", "));
554 					firstProcessed = true;
555 
556 					strStatement.Append(strParam);
557 				}
558 				else if (arg->GetMode() != pgParam::PG_PARAM_OUT)
559 				{
560 					if (firstProcessed)
561 						strStatement.Append(wxT(", "));
562 					firstProcessed = true;
563 
564 					if (arg->GetMode() == pgParam::PG_PARAM_VARIADIC)
565 						strStatement += wxT("VARIADIC ");
566 
567 					strStatement
568 					.Append(arg->Null() ? wxT("NULL") : conn->qtDbString(arg->Value()))
569 					.Append(wxT("::"))
570 					.Append(arg->GetTypeName());
571 				}
572 			}
573 			strStatement.Append(wxT(")"));
574 			strQuery = strStatement;
575 		}
576 		else if (!m_isFunction && m_language == wxT("edbspl"))
577 		{
578 			strQuery = strStatement;
579 		}
580 		else
581 		{
582 			strQuery = strStatement.Append(wxT("()"));
583 		}
584 		if (useAnonymousBlock)
585 		{
586 			strQuery = wxT("DECLARE\n") +
587 			           strDeclare + wxT("BEGIN\n") + strStatement + wxT(";\n") + strResult + wxT("END;");
588 		}
589 	}
590 	else
591 	{
592 		if (!m_isFunction)
593 		{
594 			strQuery = wxT("EXEC ") + m_fqName + wxT("(");
595 		}
596 		else if (m_returnType == wxT("record"))
597 		{
598 			strQuery = wxT("SELECT ") + m_fqName + wxT("(");
599 		}
600 		else
601 		{
602 			strQuery = wxT("SELECT * FROM ") + m_fqName + wxT("(");
603 		}
604 
605 		if (m_args)
606 		{
607 			params = new pgParamsArray();
608 			wxMBConv *conv = conn->GetConv();
609 			unsigned int noInParams = 0;
610 
611 			for(int idx = 0; idx < (int)m_args->GetCount(); idx++)
612 			{
613 				dbgArgInfo *arg = (*m_args)[idx];
614 
615 				if (arg->GetMode() != pgParam::PG_PARAM_OUT)
616 				{
617 					params->Add(arg->GetParam(conv));
618 
619 					if (noInParams != 0)
620 						strQuery += wxT(", ");
621 
622 					if (arg->GetMode() == pgParam::PG_PARAM_VARIADIC)
623 						strQuery += wxT("VARIADIC ");
624 
625 					noInParams++;
626 					strQuery += wxString::Format(wxT("$%d::"), noInParams) +
627 					            ((*m_args)[idx])->GetTypeName();
628 				}
629 			}
630 
631 			/*
632 			 * The function may not have IN/IN OUT/VARIADIC arguments, but only
633 			 * OUT one(s).
634 			 */
635 			if (params->GetCount() == 0)
636 			{
637 				delete params;
638 				params = NULL;
639 			}
640 		}
641 		strQuery += wxT(")");
642 	}
643 
644 	_thread->AddQuery(strQuery, params, RESULT_ID_DIRECT_TARGET_COMPLETE, NULL, useCallable);
645 
646 	return true;
647 }
648