Packages

Z PostgreSQL
Skočit na navigaci Skočit na vyhledávání

Package contains package's variables, shared protected functions and initialisation part, which is executed when any public function attached to package is called. Every package has defined only one language. The reason for this is binary compatibility of package's variables. Private functions aren't SQL functions and it isn't possible to call them via SPI. Because PL/pgSQL can't call functions via different interface than SPI, PL/pgSQL won't support private functions. Package owner can attach any SQL funtions to package, even those written in different language. Package is similar to schema. Public package function can access package variables or private functions only if it has same language as package. Every function can be attached to just one package. Only owner can modify package.

Samples:

CREATE OR REPLACE PACKAGE foo_package 
AS $$
DECLARE my_var integer;
BEGIN
  my_var := 0;
END;
$$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION foo_package.counter() RETURNS integer 
AS $$
BEGIN
  my_var := my_var + 1;
  RETURN foo_package.my_var; -- optional namespace
END; 
$$ LANGUAGE plpgsql;

SELECT foo_package.counter();
This package has minimal impact on current implementation of PL languages.

CREATE OR REPLACE PACKAGE xml_parser AS $$
  USE XML::LibXML;

  my $parser = XML::LibXML->new;
  my $schema_file = '/home/postgres/comm.xsd';
  my $schema = XML::LibXML::Schema->new(location => $schema_file);

sub get_data 
{
	my ($root, $tag, $optional) = @_;
	my $aux = $root->getElementsByTag($tag);
	if (@aux eq 0)
	{
		elog(ERROR, "Missing value") if ! $optional;
		return undef;
	}
	return @aux[0]->getFirstChild->getData;
}
$$ LANGUAGE plperlu;

CREATE OR  REPLACE FUNCTION xml_parser.parse_document (
  IN body varchar,
  OUT name varchar,
  OUT document_type varchar) 
AS $$
	my $body = $_[0];

	my $doc = $parser->parse_string($body);
	$schema->validate($doc);
	$root = $doc->getDocumentElement();
	return {
		name => $root->nodeName;
		document_type => get_data($root, 'type') };
$$ LANGUAGE plperlu;

- using different language, can access only public functions
CREATE OR REPLACE FUNCTION xml_parser.validate_all_doc(
  OUT _name varchar,
  OUT _state boolean) RETURNS SETOF RECORD
AS $$
DECLARE _body varchar;
BEGIN
  FOR _body, _name IN SELECT body, path FROM xml_repository LOOP
    BEGIN
      -- use implicit search_path containing package_name
      _state := true;
      PERFORM parse_document(_r.body);
    EXCEPTION WHEN OTHERS THEN
      _state := false;
    END;
    RETURN NEXT;
  END LOOP;
  RETURN;
END;
$$ LANGUAGE plpgsql VOLATILE;

SELECT xml_parser.validate_all_doc();

This system is simple and minimalistic and doesn't copy package system from ADA or Oracle which are more complicated and don't allow multiple PL.

--- Dispozice pro PostgreSQL package

Obsahuje globální proměnné, sdílené skryté funkce, a inicializační část, která se zavede při aktivaci veřejné funkce modulu. Každý balíček má určený jazyk. Důvodem je binární kompatibilita proměnných, inicializačního kódu a kódu procedur. Neveřejné funkce nejsou deklarovány jako SQL funkce, tudíž je nelze volat prostřednictvím SPI (jelikož PL/pgSQL nemá žádný jiný druh aktivace funkce než pomocí SPI, tak PL/pgSQL nepodporuje skryté funkce). Majitel balíčku může k balíčku připojit libovolnou veřejnou funkci, a to i v jiném jazyce než je jazyk balíčku. Přístup k skrytým funkcím a globálním proměnným je možný pouze tehdy, když se jazyk balíčku shoduje s jazykem funkce. Skryté funkce a globální proměnné jsou přístupné jen funkcím připojeným k balíčku. Každá funkce může být připojena pouze k jednomu balíčku. Vložení funkce do balíčku je obdobné jako vložení funkce do schématu. V podstatě vytvoření balíčku znamená vytvoření nového schéma. Balíček může modifikovat (a přidávat funkce) pouze vlastník.

Řeší se závislost mezi balíčkem a relevantními veřejnými funkcemi. Odstranění balíčku způsobí odstranění veřejných funkcí. ALTER nebo nahrazení způsobí reset všech prováděcích plánů veřejných funkcí.

Po provedení zaváděcího kódu se uvolní všechny proměnné, které nejsou deklarovány na úrovni modulu. Balíček musí být přeložen jako první (musí se inicializovat variable namespace) . Zaváděcí kód se volá při prvním spuštění veřejné funkce.

příklad:

CREATE OR REPLACE PACKAGE foo_package 
AS $$
DECLARE my_var integer;
BEGIN
  my_var := 0;
END;
$$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION foo_package.counter() RETURNS integer 
AS $$
BEGIN
  my_var := my_var + 1;
  RETURN foo_package.my_var; -- optional namespace
END; 
$$ LANGUAGE plpgsql;

SELECT foo_package.counter();

Výhodou tohoto návrhu jsou minimální dopady do implementace stávajících jazyků. Např. změny v PL/pgSQL: rozšíření prohledávání namespace, zavaděč pro balíček (k triggeru a funkci přibude package).

CREATE OR REPLACE PACKAGE xml_parser AS $$
  USE XML::LibXML;

  my $parser = XML::LibXML->new;
  my $schema_file = '/home/postgres/comm.xsd';
  my $schema = XML::LibXML::Schema->new(location => $schema_file);

sub get_data 
{
	my ($root, $tag, $optional) = @_;
	my $aux = $root->getElementsByTag($tag);
	if (@aux eq 0)
	{
		elog(ERROR, "Missing value") if ! $optional;
		return undef;
	}
	return @aux[0]->getFirstChild->getData;
}
$$ LANGUAGE plperlu;

CREATE OR  REPLACE FUNCTION xml_parser.parse_document (
  IN body varchar,
  OUT name varchar,
  OUT document_type varchar) 
AS $$
	my $body = $_[0];

	my $doc = $parser->parse_string($body);
	$schema->validate($doc);
	$root = $doc->getDocumentElement();
	return {
		name => $root->nodeName;
		document_type => get_data($root, 'type') };
$$ LANGUAGE plperlu;

-- using different language, can access only public functions
CREATE OR REPLACE FUNCTION xml_parser.validate_all_doc(
  OUT _name varchar,
  OUT _state boolean) RETURNS SETOF RECORD
AS $$
DECLARE _body varchar;
BEGIN
  FOR _body, _name IN SELECT body, path FROM xml_repository LOOP
    BEGIN
      -- use implicit search_path containing package_name
      _state := true;
      PERFORM parse_document(_r.body);
    EXCEPTION WHEN OTHERS THEN
      _state := false;
    END;
    RETURN NEXT;
  END LOOP;
  RETURN;
END;
$$ LANGUAGE plpgsql VOLATILE;

SELECT xml_parser.validate_all_doc();

Cílem je získat co nejjednodušší nekomplikovaný systém umožňující umístění globálních proměnných včetně způsobu jejich inicializace, a umístění jazykovým prostředím omezených (dostupnost) sdílených funkcí. Koncept balíčků, který rozlišuje mezi hlavičkou a tělem balíčku, tak jak je v Adě nebo PL/SQL není s tímto návrhem kompatibilní a považuji jej za zbytečně komplikovaný.