Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/HuasoFoundries/phpPgAdmin6.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/Database/Traits/FunctionTrait.php')
-rw-r--r--src/Database/Traits/FunctionTrait.php507
1 files changed, 507 insertions, 0 deletions
diff --git a/src/Database/Traits/FunctionTrait.php b/src/Database/Traits/FunctionTrait.php
new file mode 100644
index 00000000..7a12d04a
--- /dev/null
+++ b/src/Database/Traits/FunctionTrait.php
@@ -0,0 +1,507 @@
+<?php
+
+/**
+ * PHPPgAdmin6
+ */
+
+namespace PHPPgAdmin\Database\Traits;
+
+/**
+ * Common trait for full text search manipulation.
+ */
+trait FunctionTrait
+{
+ /**
+ * Returns a list of all functions in the database.
+ *
+ * @param bool $all If true, will find all available functions, if false just those in search path
+ * @param mixed $type If truthy, will return functions of type trigger
+ *
+ * @return \ADORecordSet|bool|int|string
+ */
+ public function getFunctions($all = false, $type = null)
+ {
+ if ($all) {
+ $where = 'pg_catalog.pg_function_is_visible(p.oid)';
+ $distinct = 'DISTINCT ON (p.proname)';
+
+ if ($type) {
+ $where .= " AND p.prorettype = (select oid from pg_catalog.pg_type p where p.typname = 'trigger') ";
+ }
+ } else {
+ $c_schema = $this->_schema;
+ $this->clean($c_schema);
+ $where = \sprintf(
+ 'n.nspname = \'%s\'',
+ $c_schema
+ );
+ $distinct = '';
+ }
+
+ $sql = \sprintf(
+ '
+ SELECT
+ %s
+ p.oid AS prooid,
+ p.proname,
+ p.proretset,
+ pg_catalog.format_type(p.prorettype, NULL) AS proresult,
+ pg_catalog.oidvectortypes(p.proargtypes) AS proarguments,
+ pl.lanname AS prolanguage,
+ pg_catalog.obj_description(p.oid, \'pg_proc\') AS procomment,
+ p.proname || \' (\' || pg_catalog.oidvectortypes(p.proargtypes) || \')\' AS proproto,
+ CASE WHEN p.proretset THEN \'setof \' ELSE \'\' END || pg_catalog.format_type(p.prorettype, NULL) AS proreturns,
+ coalesce(u.usename::text,p.proowner::text) AS proowner
+
+ FROM pg_catalog.pg_proc p
+ INNER JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace
+ INNER JOIN pg_catalog.pg_language pl ON pl.oid = p.prolang
+ LEFT JOIN pg_catalog.pg_user u ON u.usesysid = p.proowner
+ WHERE NOT p.proisagg
+ AND %s
+ ORDER BY p.proname, proresult
+ ',
+ $distinct,
+ $where
+ );
+
+ return $this->selectSet($sql);
+ }
+
+ /**
+ * Returns a list of all functions that can be used in triggers.
+ *
+ * @return \ADORecordSet|bool|int|string Functions that can be used in a trigger
+ */
+ public function getTriggerFunctions()
+ {
+ return $this->getFunctions(true, 'trigger');
+ }
+
+ /**
+ * Returns an array containing a function's properties.
+ *
+ * @param array $f The array of data for the function
+ *
+ * @return int|string[]
+ *
+ * @psalm-return int|non-empty-list<string>
+ */
+ public function getFunctionProperties($f)
+ {
+ $temp = [];
+
+ // Volatility
+ if ('v' === $f['provolatile']) {
+ $temp[] = 'VOLATILE';
+ } elseif ('i' === $f['provolatile']) {
+ $temp[] = 'IMMUTABLE';
+ } elseif ('s' === $f['provolatile']) {
+ $temp[] = 'STABLE';
+ } else {
+ return -1;
+ }
+
+ // Null handling
+ $f['proisstrict'] = $this->phpBool($f['proisstrict']);
+
+ $temp[] = $f['proisstrict'] ? 'RETURNS NULL ON NULL INPUT' : 'CALLED ON NULL INPUT';
+
+ // Security
+ $f['prosecdef'] = $this->phpBool($f['prosecdef']);
+
+ $temp[] = $f['prosecdef'] ? 'SECURITY DEFINER' : 'SECURITY INVOKER';
+
+ return $temp;
+ }
+
+ /**
+ * Updates (replaces) a function.
+ *
+ * @param string $funcname The name of the function to create
+ * @param string $newname The new name for the function
+ * @param string $args imploded array of argument types
+ * @param string $returns The return type
+ * @param string $definition The definition for the new function
+ * @param string $language The language the function is written for
+ * @param array $flags An array of optional flags
+ * @param bool $setof True if returns a set, false otherwise
+ * @param string $funcown
+ * @param string $newown
+ * @param string $funcschema
+ * @param string $newschema
+ * @param float $cost
+ * @param int $rows
+ * @param string $comment The comment on the function
+ *
+ * @return int 0 success
+ */
+ public function setFunction(
+ $funcname,
+ $newname,
+ $args,
+ $returns,
+ $definition,
+ $language,
+ $flags,
+ $setof,
+ $funcown,
+ $newown,
+ $funcschema,
+ $newschema,
+ $cost,
+ $rows,
+ $comment
+ ) {
+ // Begin a transaction
+ $status = $this->beginTransaction();
+
+ if (0 !== $status) {
+ $this->rollbackTransaction();
+
+ return -1;
+ }
+
+ // Replace the existing function
+ $status = $this->createFunction($funcname, $args, $returns, $definition, $language, $flags, $setof, $cost, $rows, $comment, true);
+
+ if (0 !== $status) {
+ $this->rollbackTransaction();
+
+ return $status;
+ }
+
+ $f_schema = $this->_schema;
+ $this->fieldClean($f_schema);
+
+ // Rename the function, if necessary
+ $this->fieldClean($newname);
+ /* $funcname is escaped in createFunction */
+ if ($funcname !== $newname) {
+ $sql = \sprintf(
+ 'ALTER FUNCTION "%s"."%s"(%s) RENAME TO "%s"',
+ $f_schema,
+ $funcname,
+ $args,
+ $newname
+ );
+ $status = $this->execute($sql);
+
+ if (0 !== $status) {
+ $this->rollbackTransaction();
+
+ return -5;
+ }
+
+ $funcname = $newname;
+ }
+
+ // Alter the owner, if necessary
+ if ($this->hasFunctionAlterOwner()) {
+ $this->fieldClean($newown);
+
+ if ($funcown !== $newown) {
+ $sql = \sprintf(
+ 'ALTER FUNCTION "%s"."%s"(%s) OWNER TO "%s"',
+ $f_schema,
+ $funcname,
+ $args,
+ $newown
+ );
+ $status = $this->execute($sql);
+
+ if (0 !== $status) {
+ $this->rollbackTransaction();
+
+ return -6;
+ }
+ }
+ }
+
+ // Alter the schema, if necessary
+ if ($this->hasFunctionAlterSchema()) {
+ $this->fieldClean($newschema);
+ /* $funcschema is escaped in createFunction */
+ if ($funcschema !== $newschema) {
+ $sql = \sprintf(
+ 'ALTER FUNCTION "%s"."%s"(%s) SET SCHEMA "%s"',
+ $f_schema,
+ $funcname,
+ $args,
+ $newschema
+ );
+ $status = $this->execute($sql);
+
+ if (0 !== $status) {
+ $this->rollbackTransaction();
+
+ return -7;
+ }
+ }
+ }
+
+ return $this->endTransaction();
+ }
+
+ /**
+ * Creates a new function.
+ *
+ * @param string $funcname The name of the function to create
+ * @param string $args A comma separated string of types
+ * @param string $returns The return type
+ * @param string $definition The definition for the new function
+ * @param string $language The language the function is written for
+ * @param array $flags An array of optional flags
+ * @param bool $setof True if it returns a set, false otherwise
+ * @param string $cost cost the planner should use in the function execution step
+ * @param int $rows number of rows planner should estimate will be returned
+ * @param string $comment Comment for the function
+ * @param bool $replace (optional) True if OR REPLACE, false for
+ * normal
+ *
+ * @return int
+ *
+ * @psalm-return -4|-3|-1|0|1
+ */
+ public function createFunction($funcname, $args, $returns, $definition, $language, $flags, $setof, $cost, $rows, $comment, $replace = false)
+ {
+ // Begin a transaction
+ $status = $this->beginTransaction();
+
+ if (0 !== $status) {
+ $this->rollbackTransaction();
+
+ return -1;
+ }
+
+ $this->fieldClean($funcname);
+ $this->clean($args);
+ $this->fieldClean($language);
+ $this->arrayClean($flags);
+ $this->clean($cost);
+ $this->clean($rows);
+ $f_schema = $this->_schema;
+ $this->fieldClean($f_schema);
+
+ $sql = 'CREATE';
+
+ if ($replace) {
+ $sql .= ' OR REPLACE';
+ }
+
+ $sql .= \sprintf(
+ ' FUNCTION "%s"."%s" (',
+ $f_schema,
+ $funcname
+ );
+
+ if ('' !== $args) {
+ $sql .= $args;
+ }
+
+ // For some reason, the returns field cannot have quotes...
+ $sql .= ') RETURNS ';
+
+ if ($setof) {
+ $sql .= 'SETOF ';
+ }
+
+ $sql .= \sprintf(
+ '%s AS ',
+ $returns
+ );
+
+ if (\is_array($definition)) {
+ $this->arrayClean($definition);
+ $sql .= "'" . $definition[0] . "'";
+
+ if ($definition[1]) {
+ $sql .= ",'" . $definition[1] . "'";
+ }
+ } else {
+ $this->clean($definition);
+ $sql .= "'" . $definition . "'";
+ }
+
+ $sql .= \sprintf(
+ ' LANGUAGE "%s"',
+ $language
+ );
+
+ // Add costs
+ if (!empty($cost)) {
+ $sql .= \sprintf(
+ ' COST %s',
+ $cost
+ );
+ }
+
+ if (0 !== $rows) {
+ $sql .= \sprintf(
+ ' ROWS %s',
+ $rows
+ );
+ }
+
+ // Add flags
+ foreach ($flags as $v) {
+ // Skip default flags
+ if ('' === $v) {
+ continue;
+ }
+
+ $sql .= \PHP_EOL . $v;
+ }
+
+ $status = $this->execute($sql);
+
+ if (0 !== $status) {
+ $this->rollbackTransaction();
+
+ return -3;
+ }
+
+ /* set the comment */
+ $status = $this->setComment('FUNCTION', \sprintf(
+ '"%s"(%s)',
+ $funcname,
+ $args
+ ), null, $comment);
+
+ if (0 !== $status) {
+ $this->rollbackTransaction();
+
+ return -4;
+ }
+
+ return $this->endTransaction();
+ }
+
+ /**
+ * Drops a function.
+ *
+ * @param int $function_oid The OID of the function to drop
+ * @param bool $cascade True to cascade drop, false to restrict
+ *
+ * @return int|string
+ */
+ public function dropFunction($function_oid, $cascade)
+ {
+ // Function comes in with $object as function OID
+ $fn = $this->getFunction($function_oid);
+ $f_schema = $this->_schema;
+ $this->fieldClean($f_schema);
+ $this->fieldClean($fn->fields['proname']);
+
+ $sql = \sprintf(
+ 'DROP FUNCTION "%s"."%s"(%s)',
+ $f_schema,
+ $fn->fields['proname'],
+ $fn->fields['proarguments']
+ );
+
+ if ($cascade) {
+ $sql .= ' CASCADE';
+ }
+
+ return $this->execute($sql);
+ }
+
+ /**
+ * Returns all details for a particular function.
+ *
+ * @param int $function_oid
+ *
+ * @return \ADORecordSet|bool|int|string
+ *
+ * @internal param string The $func name of the function to retrieve
+ */
+ public function getFunction($function_oid)
+ {
+ $this->clean($function_oid);
+
+ $sql = \sprintf(
+ '
+ SELECT
+ pc.oid AS prooid, proname,
+ pg_catalog.pg_get_userbyid(proowner) AS proowner,
+ nspname as proschema, lanname as prolanguage, procost, prorows,
+ pg_catalog.format_type(prorettype, NULL) as proresult, prosrc,
+ probin, proretset, proisstrict, provolatile, prosecdef,
+ pg_catalog.oidvectortypes(pc.proargtypes) AS proarguments,
+ proargnames AS proargnames,
+ pg_catalog.obj_description(pc.oid, \'pg_proc\') AS procomment,
+ proconfig,
+ (select array_agg( (select typname from pg_type pt
+ where pt.oid = p.oid) ) from unnest(proallargtypes) p)
+ AS proallarguments,
+ proargmodes
+ FROM
+ pg_catalog.pg_proc pc, pg_catalog.pg_language pl,
+ pg_catalog.pg_namespace pn
+ WHERE
+ pc.oid = \'%s\'::oid AND pc.prolang = pl.oid
+ AND pc.pronamespace = pn.oid
+ ',
+ $function_oid
+ );
+
+ return $this->selectSet($sql);
+ }
+
+ /**
+ * Returns plain definition for a particular function.
+ *
+ * @param int $function_oid
+ *
+ * @return \ADORecordSet|bool|int|string
+ */
+ public function getFunctionDef($function_oid)
+ {
+ $this->clean($function_oid);
+ $sql = \sprintf(
+ '
+ SELECT
+ f.proname as relname,
+ n.nspname,
+ u.usename AS relowner,
+ pg_catalog.obj_description(f.oid, \'pg_proc\') as relcomment,
+ (SELECT spcname FROM pg_catalog.pg_tablespace pt WHERE pt.oid=f.pronamespace) AS tablespace,
+ pg_get_functiondef(f.oid),
+ pl.lanname AS prolanguage
+ FROM pg_catalog.pg_proc f
+ JOIN pg_catalog.pg_namespace n ON (f.pronamespace = n.oid)
+ JOIN pg_catalog.pg_language pl ON pl.oid = f.prolang
+ LEFT JOIN pg_catalog.pg_user u ON u.usesysid=f.proowner
+ WHERE f.oid=\'%s\'
+ ',
+ $function_oid
+ );
+
+ return $this->selectSet($sql);
+ }
+
+ abstract public function fieldClean(&$str);
+
+ abstract public function beginTransaction();
+
+ abstract public function rollbackTransaction();
+
+ abstract public function endTransaction();
+
+ abstract public function execute($sql);
+
+ abstract public function setComment($obj_type, $obj_name, $table, $comment, $basetype = null);
+
+ abstract public function selectSet($sql);
+
+ abstract public function clean(&$str);
+
+ abstract public function phpBool($parameter);
+
+ abstract public function hasFunctionAlterOwner();
+
+ abstract public function hasFunctionAlterSchema();
+
+ abstract public function arrayClean(&$arr);
+}