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