edbPackageFunction *edbPackageFunctionFactory::AppendFunctions(pgObject *obj, edbPackage *package, ctlTree *browser, const wxString &restriction)
{
	edbPackageFunction *packageFunction = 0;
	pgSet *packageFunctions;

	// Caches
	cacheMap typeCache, exprCache;
	wxString sql, argDefsCol;

	if (obj->GetConnection()->HasFeature(FEATURE_FUNCTION_DEFAULTS))
	{
		if (obj->GetConnection()->EdbMinimumVersion(8, 4))
		{
			argDefsCol = wxT("pg_get_expr(proargdefaults, 'pg_catalog.pg_class'::regclass) AS argdefaults, pronargdefaults, ");
		}
		else
		{
			argDefsCol = wxT("proargdefvals AS argdefaults, COALESCE(substring(array_dims(proargdefvals), E'1:(.*)\\]')::integer, 0) AS pronargdefaults, ");
		}
	}

	if (obj->GetConnection()->EdbMinimumVersion(8, 2))
	{
		sql = wxT("SELECT pg_proc.oid, proname AS eltname, prorettype AS eltdatatype, pronargs AS nargs, proaccess AS visibility,\n")
		      wxT("       proallargtypes AS allargtypes, proargtypes AS argtypes, proargnames AS argnames, proargmodes AS argmodes,") + argDefsCol + wxT("\n")
		      wxT("       CASE WHEN format_type(prorettype, NULL) = 'void' THEN 'P' ELSE 'F' END AS eltclass\n")
		      wxT("  FROM pg_proc, pg_namespace\n")
		      + restriction + wxT("\n")
		      wxT("  AND pg_proc.pronamespace = pg_namespace.oid\n")
		      wxT("  ORDER BY eltname");
	}
	else
	{
		sql = wxT("SELECT oid, eltname, eltdatatype, eltclass, nargs, visibility,\n")
		      wxT("       allargtypes, argtypes, argnames, argmodes\n")
		      wxT("  FROM edb_pkgelements\n")
		      + restriction + wxT("\n")
		      wxT("  ORDER BY eltname");
	}

	packageFunctions = obj->GetDatabase()->ExecuteSet(sql);

	pgSet *types = obj->GetDatabase()->ExecuteSet(wxT(
	                   "SELECT oid, format_type(oid, NULL) AS typname FROM pg_type"));

	while(!types->Eof())
	{
		typeCache[types->GetVal(wxT("oid"))] = types->GetVal(wxT("typname"));
		types->MoveNext();
	}

	if (packageFunctions)
	{
		while (!packageFunctions->Eof())
		{
			size_t inModeCnt = 0;
			size_t defaultArgsCnt = 0;
			if (packageFunctions->GetVal(wxT("eltclass")) == wxT("F"))
				packageFunction = new edbPackageFunction(package, packageFunctions->GetVal(wxT("eltname")));
			else
				packageFunction = new edbPackageProcedure(package, packageFunctions->GetVal(wxT("eltname")));

			// Tokenize the arguments
			wxStringTokenizer argTypesTkz(wxEmptyString), argModesTkz(wxEmptyString);
			queryTokenizer argNamesTkz(wxEmptyString, (wxChar)',');
			wxArrayString argDefValArray;
			wxString tmp;

			// Types
			tmp = packageFunctions->GetVal(wxT("allargtypes"));
			if (!tmp.IsEmpty())
				argTypesTkz.SetString(tmp.Mid(1, tmp.Length() - 2), wxT(","));
			else
			{
				tmp = packageFunctions->GetVal(wxT("argtypes"));
				if (!tmp.IsEmpty())
					argTypesTkz.SetString(tmp);
			}

			// Names
			tmp = packageFunctions->GetVal(wxT("argnames"));
			if (!tmp.IsEmpty())
				argNamesTkz.SetString(tmp.Mid(1, tmp.Length() - 2), wxT(","));

			// Modes
			tmp = packageFunctions->GetVal(wxT("argmodes"));
			if (!tmp.IsEmpty())
				argModesTkz.SetString(tmp.Mid(1, tmp.Length() - 2), wxT(","));

			// Function defaults
			if (obj->GetConnection()->HasFeature(FEATURE_FUNCTION_DEFAULTS))
			{
				defaultArgsCnt = packageFunctions->GetLong(wxT("pronargdefaults"));

				if (defaultArgsCnt > 0)
				{
					tmp = packageFunctions->GetVal(wxT("argdefaults"));

					if (!tmp.IsEmpty())
					{
						getArrayFromCommaSeparatedList(tmp.Mid(1, tmp.Length() - 2), argDefValArray);
					}
				}
			}

			// Now iterate the arguments and build the arrays
			wxString type, name, mode;

			while (argTypesTkz.HasMoreTokens())
			{
				// Add the arg type. This is a type oid, so
				// look it up in the hashmap
				type = argTypesTkz.GetNextToken();
				packageFunction->iAddArgType(typeCache[type]);

				// Now add the name, stripping the quotes if
				// necessary.
				name = argNamesTkz.GetNextToken();
				if (!name.IsEmpty())
				{
					if (name[0] == '"')
						name = name.Mid(1, name.Length() - 2);
					packageFunction->iAddArgName(name);
				}
				else
					packageFunction->iAddArgName(wxEmptyString);

				// Now the mode
				mode = argModesTkz.GetNextToken();
				if (!mode.IsEmpty())
				{
					if (mode == wxT('o') || mode == wxT("2"))
						mode = wxT("OUT");
					else if (mode == wxT("b"))
						if (packageFunctions->GetVal(wxT("eltclass")) == wxT("F"))
							mode = wxT("IN OUT");
						else
							mode = wxT("INOUT");
					else if (mode == wxT("3"))
						mode = wxT("IN OUT");
					else if (mode == wxT("v"))
					{
						inModeCnt++;
						mode = wxT("VARIADIC");
					}
					else
					{
						inModeCnt++;
						mode = wxT("IN");
					}

					packageFunction->iAddArgMode(mode);
				}
				else
					packageFunction->iAddArgMode(wxEmptyString);
			}

			// Finally the defaults, as we got them.
			if (packageFunction->GetConnection()->HasFeature(FEATURE_FUNCTION_DEFAULTS))
			{
				size_t currINindex = 0;
				while (inModeCnt)
				{
					for (size_t index = 0; index < packageFunction->GetArgTypesArray().Count(); index++)
					{
						wxString def = wxEmptyString;
						if(packageFunction->GetArgModesArray()[index].IsEmpty() ||
						        packageFunction->GetArgModesArray()[index] == wxT("IN") ||
						        packageFunction->GetArgModesArray()[index] == wxT("VARIADIC"))
						{
							if (!argDefValArray.IsEmpty() && inModeCnt <= argDefValArray.GetCount())
							{
								def = argDefValArray[currINindex++];

								if (!def.IsEmpty() && def != wxT("-"))
								{
									// Only EDB 8.3 does not support get the
									// default value using pg_get_expr directly
									if (!packageFunction->GetConnection()->BackendMinimumVersion(8, 4))
									{
										// Check the cache first - if we don't
										// have a value, get it and cache for
										// next time
										wxString val = exprCache[def];

										if (val == wxEmptyString)
										{
											val = obj->GetDatabase()->ExecuteScalar(
											          wxT("SELECT pg_get_expr('") + def.Mid(1, def.Length() - 2) + wxT("', 'pg_catalog.pg_class'::regclass)"));
											exprCache[def] = val;
										}
										def = val;
									}
								}
								else
								{
									def = wxEmptyString;
								}
							}
							inModeCnt--;
						}
						packageFunction->iAddArgDef(def);
					}
				}
			}

			packageFunction->iSetOid(packageFunctions->GetOid(wxT("oid")));
			packageFunction->iSetArgCount(packageFunctions->GetOid(wxT("nargs")));
			packageFunction->iSetReturnType(typeCache[packageFunctions->GetVal(wxT("eltdatatype"))]);

			if (packageFunctions->GetVal(wxT("visibility")) == wxT("+"))
				packageFunction->iSetVisibility(_("Public"));
			else if (packageFunctions->GetVal(wxT("visibility")) == wxT("-"))
				packageFunction->iSetVisibility(_("Private"));
			else
				packageFunction->iSetVisibility(_("Unknown"));

			packageFunction->iSetSource(package->GetBodyInner());

			if (browser)
			{
				browser->AppendObject(obj, packageFunction);
				packageFunctions->MoveNext();
			}
			else
				break;
		}

		delete packageFunctions;
		delete types;
	}
	return packageFunction;
}
Exemple #2
0
pgFunction *pgFunctionFactory::AppendFunctions(pgObject *obj, pgSchema *schema, ctlTree *browser, const wxString &restriction)
{
	// Caches
	cacheMap typeCache, exprCache;

	pgFunction *function = 0;
	wxString argNamesCol, argDefsCol, proConfigCol, proType, seclab;
	if (obj->GetConnection()->BackendMinimumVersion(8, 0))
		argNamesCol = wxT("proargnames, ");
	if (obj->GetConnection()->HasFeature(FEATURE_FUNCTION_DEFAULTS) && !obj->GetConnection()->BackendMinimumVersion(8, 4))
		argDefsCol = wxT("proargdefvals, COALESCE(substring(array_dims(proargdefvals), E'1:(.*)\\]')::integer, 0) AS pronargdefaults, ");
	if (obj->GetConnection()->BackendMinimumVersion(8, 4))
		argDefsCol = wxT("pg_get_expr(proargdefaults, 'pg_catalog.pg_class'::regclass) AS proargdefaultvals, pronargdefaults, ");
	if (obj->GetConnection()->BackendMinimumVersion(8, 3))
		proConfigCol = wxT("proconfig, ");
	if (obj->GetConnection()->EdbMinimumVersion(8, 1))
		proType = wxT("protype, ");
	if (obj->GetConnection()->BackendMinimumVersion(9, 1))
	{
		seclab = wxT(",\n")
		         wxT("(SELECT array_agg(label) FROM pg_seclabels sl1 WHERE sl1.objoid=pr.oid) AS labels,\n")
		         wxT("(SELECT array_agg(provider) FROM pg_seclabels sl2 WHERE sl2.objoid=pr.oid) AS providers");
	}

	pgSet *functions = obj->GetDatabase()->ExecuteSet(
	                       wxT("SELECT pr.oid, pr.xmin, pr.*, format_type(TYP.oid, NULL) AS typname, typns.nspname AS typnsp, lanname, ") +
	                       argNamesCol  + argDefsCol + proConfigCol + proType +
	                       wxT("       pg_get_userbyid(proowner) as funcowner, description") + seclab + wxT("\n")
	                       wxT("  FROM pg_proc pr\n")
	                       wxT("  JOIN pg_type typ ON typ.oid=prorettype\n")
	                       wxT("  JOIN pg_namespace typns ON typns.oid=typ.typnamespace\n")
	                       wxT("  JOIN pg_language lng ON lng.oid=prolang\n")
	                       wxT("  LEFT OUTER JOIN pg_description des ON des.objoid=pr.oid\n")
	                       + restriction +
	                       wxT(" ORDER BY proname"));

	pgSet *types = obj->GetDatabase()->ExecuteSet(wxT(
	                   "SELECT oid, format_type(oid, NULL) AS typname FROM pg_type"));

	if (types)
	{
		while(!types->Eof())
		{
			typeCache[types->GetVal(wxT("oid"))] = types->GetVal(wxT("typname"));
			types->MoveNext();
		}
	}

	if (functions)
	{
		while (!functions->Eof())
		{
			bool isProcedure = false;
			wxString lanname = functions->GetVal(wxT("lanname"));
			wxString typname = functions->GetVal(wxT("typname"));

			// Is this an EDB Stored Procedure?
			if (obj->GetConnection()->EdbMinimumVersion(8, 1))
			{
				wxString protype = functions->GetVal(wxT("protype"));
				if (protype == wxT("1"))
					isProcedure = true;
			}
			else if (obj->GetConnection()->EdbMinimumVersion(8, 0) &&
			         lanname == wxT("edbspl") && typname == wxT("void"))
				isProcedure = true;

			// Create the new object
			if (isProcedure)
				function = new pgProcedure(schema, functions->GetVal(wxT("proname")));
			else if (typname == wxT("\"trigger\"") || typname == wxT("trigger"))
				function = new pgTriggerFunction(schema, functions->GetVal(wxT("proname")));
			else
				function = new pgFunction(schema, functions->GetVal(wxT("proname")));

			// Tokenize the arguments
			wxStringTokenizer argTypesTkz(wxEmptyString), argModesTkz(wxEmptyString);
			wxString tmp;

			wxArrayString argNamesArray;
			wxArrayString argDefValArray;

			// We always have types
			argTypesTkz.SetString(functions->GetVal(wxT("proargtypes")));

			// We only have names in 8.0+
			if (obj->GetConnection()->BackendMinimumVersion(8, 0))
			{
				tmp = functions->GetVal(wxT("proargnames"));
				if (!tmp.IsEmpty())
					getArrayFromCommaSeparatedList(tmp.Mid(1, tmp.Length() - 2), argNamesArray);
			}

			// EDB 8.0 had modes in pg_proc.proargdirs
			if (!obj->GetConnection()->EdbMinimumVersion(8, 1) && isProcedure)
				argModesTkz.SetString(functions->GetVal(wxT("proargdirs")));

			if (obj->GetConnection()->EdbMinimumVersion(8, 1))
				function->iSetProcType(functions->GetLong(wxT("protype")));

			// EDB 8.1 and PostgreSQL 8.1 have modes in pg_proc.proargmodes
			if (obj->GetConnection()->BackendMinimumVersion(8, 1))
			{
				tmp = functions->GetVal(wxT("proallargtypes"));
				if (!tmp.IsEmpty())
					argTypesTkz.SetString(tmp.Mid(1, tmp.Length() - 2), wxT(","));

				tmp = functions->GetVal(wxT("proargmodes"));
				if (!tmp.IsEmpty())
					argModesTkz.SetString(tmp.Mid(1, tmp.Length() - 2), wxT(","));
			}

			// EDB 8.3: Function defaults
			if (obj->GetConnection()->HasFeature(FEATURE_FUNCTION_DEFAULTS) &&
			        !obj->GetConnection()->BackendMinimumVersion(8, 4))
			{
				tmp = functions->GetVal(wxT("proargdefvals"));
				if (!tmp.IsEmpty())
					getArrayFromCommaSeparatedList(tmp.Mid(1, tmp.Length() - 2), argDefValArray);

				function->iSetArgDefValCount(functions->GetLong(wxT("pronargdefaults")));
			}

			if (obj->GetConnection()->BackendMinimumVersion(8, 4))
			{
				tmp = functions->GetVal(wxT("proargdefaultvals"));
				getArrayFromCommaSeparatedList(tmp, argDefValArray);

				function->iSetArgDefValCount(functions->GetLong(wxT("pronargdefaults")));

				// Check if it is a window function
				function->iSetIsWindow(functions->GetBool(wxT("proiswindow")));
			}
			else
				function->iSetIsWindow(false);

			// Now iterate the arguments and build the arrays
			wxString type, name, mode;
			size_t nArgsIN = 0;
			size_t nArgNames = 0;

			while (argTypesTkz.HasMoreTokens())
			{
				if (nArgNames < argNamesArray.GetCount())
				{
					name = argNamesArray[nArgNames++];
				}
				else
					name = wxEmptyString;
				if (!name.IsEmpty())
				{
					// Now add the name, stripping the quotes and \" if
					// necessary.
					if (name[0] == '"')
						name = name.Mid(1, name.Length() - 2);
					name.Replace(wxT("\\\""), wxT("\""));

					// In EDBAS 90, if an SPL-function has both an OUT-parameter
					// and a return value (which is not possible on PostgreSQL otherwise),
					// the return value is transformed into an extra OUT-parameter
					// named "_retval_"
					if (obj->GetConnection()->EdbMinimumVersion(9, 0))
					{
						if (name == wxT("_retval_"))
						{
							type = argTypesTkz.GetNextToken();
							// this will be the return type for this object
							function->iSetReturnType(typeCache[type]);

							// consume uniformly, mode will definitely be "OUT"
							mode = argModesTkz.GetNextToken();

							continue;
						}
					}
					function->iAddArgName(name);
				}
				else
					function->iAddArgName(wxEmptyString);

				// Add the arg type. This is a type oid, so
				// look it up in the hashmap
				type = argTypesTkz.GetNextToken();
				function->iAddArgType(typeCache[type]);

				// Now the mode
				mode = argModesTkz.GetNextToken();
				if (!mode.IsEmpty())
				{
					if (mode == wxT('o') || mode == wxT("2"))
						mode = wxT("OUT");
					else if (mode == wxT("b") || mode == wxT("3"))
					{
						if (isProcedure)
							mode = wxT("IN OUT");
						else
						{
							mode = wxT("INOUT");
							// 'edbspl' does not support default values for the
							// INOUT parameters.
							if (lanname != wxT("edbspl"))
							{
								nArgsIN++;
							}
						}
					}
					else if (mode == wxT("v"))
					{
						mode = wxT("VARIADIC");
						nArgsIN++;
					}
					else if (mode == wxT("t"))
						mode = wxT("TABLE");
					else
					{
						mode = wxT("IN");
						nArgsIN++;
					}

					function->iAddArgMode(mode);
				}
				else
				{
					function->iAddArgMode(wxEmptyString);
					nArgsIN++;
				}
			}

			function->iSetArgCount(functions->GetLong(wxT("pronargs")));

			wxString strReturnTableArgs;
			// Process default values
			size_t currINindex = 0;
			for (size_t index = 0; index < function->GetArgModesArray().Count(); index++)
			{
				wxString def = wxEmptyString;
				if(function->GetArgModesArray()[index].IsEmpty() ||
				        function->GetArgModesArray()[index] == wxT("IN") ||
				        (function->GetArgModesArray()[index] == wxT("INOUT") &&
				         lanname != wxT("edbspl")) ||
				        function->GetArgModesArray()[index] == wxT("VARIADIC"))
				{
					if (!argDefValArray.IsEmpty() && nArgsIN <= argDefValArray.GetCount())
					{
						def = argDefValArray[currINindex++];

						if (!def.IsEmpty() && def != wxT("-"))
						{
							// Only EDB 8.3 does not support get the default value
							// using pg_get_expr directly
							if (function->GetConnection()->HasFeature(FEATURE_FUNCTION_DEFAULTS) &&
							        !function->GetConnection()->BackendMinimumVersion(8, 4))
							{
								// Check the cache first - if we don't have a value, get it and cache for next time
								wxString val = exprCache[def];

								if (val == wxEmptyString)
								{
									val = obj->GetDatabase()->ExecuteScalar(
									          wxT("SELECT pg_get_expr('") + def.Mid(1, def.Length() - 2) + wxT("', 'pg_catalog.pg_class'::regclass)"));
									exprCache[def] = val;
								}
								def = val;
							}
						}
						else
						{
							def = wxEmptyString;
						}
					}
					nArgsIN--;
				}
				else if(function->GetConnection()->BackendMinimumVersion(8, 4) &&
				        function->GetArgModesArray()[index] == wxT("TABLE"))
				{
					if (strReturnTableArgs.Length() > 0)
						strReturnTableArgs += wxT(", ");
					wxString strName = function->GetArgNamesArray()[index];
					if (!strName.IsEmpty())
						strReturnTableArgs += qtIdent(strName) + wxT(" ");
					strReturnTableArgs += function->GetArgTypesArray()[index];
				}
				function->iAddArgDef(def);
			}

			function->iSetOid(functions->GetOid(wxT("oid")));
			function->iSetXid(functions->GetOid(wxT("xmin")));

			if (browser)
				function->UpdateSchema(browser, functions->GetOid(wxT("pronamespace")));

			function->iSetOwner(functions->GetVal(wxT("funcowner")));
			function->iSetAcl(functions->GetVal(wxT("proacl")));

			// set the return type only if not already set..
			if (function->GetReturnType().IsEmpty())
			{
				wxString strType = functions->GetVal(wxT("typname"));
				if (strType.Lower() == wxT("record") && !strReturnTableArgs.IsEmpty())
				{
					strType = wxT("TABLE(") + strReturnTableArgs + wxT(")");
				}
				function->iSetReturnType(strType);
			}
			function->iSetComment(functions->GetVal(wxT("description")));

			function->iSetLanguage(lanname);
			function->iSetSecureDefiner(functions->GetBool(wxT("prosecdef")));
			function->iSetReturnAsSet(functions->GetBool(wxT("proretset")));
			function->iSetIsStrict(functions->GetBool(wxT("proisstrict")));
			function->iSetSource(functions->GetVal(wxT("prosrc")));
			function->iSetBin(functions->GetVal(wxT("probin")));

			wxString vol = functions->GetVal(wxT("provolatile"));
			function->iSetVolatility(
			    vol.IsSameAs(wxT("i")) ? wxT("IMMUTABLE") :
			    vol.IsSameAs(wxT("s")) ? wxT("STABLE") :
			    vol.IsSameAs(wxT("v")) ? wxT("VOLATILE") : wxT("unknown"));

			// PostgreSQL 8.3 cost/row estimations
			if (obj->GetConnection()->BackendMinimumVersion(8, 3))
			{
				function->iSetCost(functions->GetLong(wxT("procost")));
				function->iSetRows(functions->GetLong(wxT("prorows")));
				wxString cfg = functions->GetVal(wxT("proconfig"));
				if (!cfg.IsEmpty())
					FillArray(function->GetConfigList(), cfg.Mid(1, cfg.Length() - 2));
			}

			if (obj->GetConnection()->BackendMinimumVersion(9, 1))
			{
				function->iSetProviders(functions->GetVal(wxT("providers")));
				function->iSetLabels(functions->GetVal(wxT("labels")));
			}

			if (obj->GetConnection()->BackendMinimumVersion(9, 2))
			{
				function->iSetIsLeakProof(functions->GetBool(wxT("proleakproof")));
			}

			if (browser)
			{
				browser->AppendObject(obj, function);
				functions->MoveNext();
			}
			else
				break;
		}

		delete functions;
		delete types;
	}
	return function;
}