#!/usr/bin/env perl

#
# PostGIS - Spatial Types for PostgreSQL
# http://postgis.net
#
# Copyright (C) 2004-2023 Sandro Santilli <strk@kbt.io>
# Copyright (C) 2011 OpenGeo.org
# Copyright (C) 2009 Paul Ramsey <pramsey@cleverelephant.ca>
#
# This is free software; you can redistribute and/or modify it under
# the terms of the GNU General Public Licence. See the COPYING file.
#
#---------------------------------------------------------------------
#
# This script is aimed at restoring postgis data
# from a dumpfile produced by pg_dump -Fc
#
# Basically it will restore all but things known to belong
# to postgis. Will also convert some old known constructs
# into new ones.
#
# Tested on:
#
#    pg-12/pgis-2.5.9 => pg-12/pgis-3.5.0dev
#    pg-8.4.9/pgis-1.4.3    => pg-8.4.9/pgis-2.0.0SVN
#    pg-8.4.9/pgis-2.0.0SVN => pg-8.4.9/pgis-2.0.0SVN
#    pg-8.4.9/pgis-2.0.0SVN => pg-9.1.2/pgis-2.0.0SVN
#    pg-9.1b3/pgis-1.5.3    => pg-9.1.1/pgis-2.0.0SVN
#
#---------------------------------------------------------------------

use warnings;
use strict;

my $me = $0;

my $usage = qq{
Usage:	$me [-v] [-L TOC] [-s schema] <dumpfile>
        Restore a custom dump (pg_dump -Fc) of a PostGIS-enabled database.
        First dump the old database: pg_dump -Fc MYDB > MYDB.dmp
        Then create a new database: createdb NEWDB
        Then install PostGIS in the new database:
           psql -f postgis/postgis.sql NEWDB
        Also install PostGIS topology and raster, if you were using them:
           psql -f topology/topology.sql NEWDB
           psql -f raster/rtpostgis.sql NEWDB
        Finally, pass the dump to this script and feed output to psql:
           $me MYDB.dmp | psql NEWDB
        The -v switch writes detailed report on stderr.
        Use -L to provide a TOC rather than extracting it from the dump.
        Use -s if you installed PostGIS in a custom schema.

};

my $DEBUG = 0;
my $POSTGIS_SCHEMA;
my $POSTGIS_TOC;

# NOTE: the SRID limits here are being discussed:
# http://lists.osgeo.org/pipermail/postgis-devel/2012-February/018440.html
my $SRID_MAXIMUM = @SRID_MAXIMUM@;
my $SRID_USER_MAXIMUM = @SRID_USER_MAXIMUM@;

while (@ARGV && $ARGV[0] =~ /^-/) {
  my $arg = shift(@ARGV);
  if ( $arg eq '-v' ) {
    $DEBUG = 1;
  }
  elsif ( $arg eq '-s' ) {
    $POSTGIS_SCHEMA = shift(@ARGV);
  }
  elsif ( $arg eq '-L' ) {
    $POSTGIS_TOC = shift(@ARGV);
  }
  elsif ( $arg eq '--' ) {
    last;
  }
  else {
    print STDERR "Unknown switch " . $arg;
    die $usage;
  }
}

die $usage if (@ARGV < 1);

my $dumpfile = $ARGV[0];
my $manifest = $dumpfile;
$manifest =~ s/\/$//; # strip trailing slash
$manifest .= ".lst";
my $hasTopology = 0;

die "$me:\tUnable to find 'pg_dump' on the path.\n" if ! `pg_dump --version`;
die "$me:\tUnable to find 'pg_restore' on the path.\n" if ! `pg_restore --version`;
die "$me:\tUnable to open dump file '$dumpfile'.\n" if ! -r $dumpfile;

print STDERR "Converting $dumpfile to ASCII on stdout...\n"
  if $DEBUG;

STDOUT->autoflush(1);

######################################################################
# Load the signatures of things to skip.
#

print STDERR "  Reading list of functions to ignore...\n"
  if $DEBUG;

my %skip = ();
while(my $l = <DATA>) {
  chop($l);
  print STDERR "DATA: $l\n" if $DEBUG;
  $l =~ s/\s//g;
  $skip{$l} = 1;
}

######################################################################
# Write a new manifest for the dump file, skipping the things that
# are part of PostGIS
#

if(!defined($POSTGIS_TOC)) {
  print STDERR "  Writing manifest of things to read from dump file...\n"
    if $DEBUG;

  open( DUMP, "pg_restore -f - -l $dumpfile |" ) || die "$me:\tCannot open dump file '$dumpfile'\n";
} else {
  open( DUMP, '<' . $POSTGIS_TOC) || die "$me:\tCannot open TOC file '$POSTGIS_TOC'\n";
}
open( MANIFEST, ">$manifest" ) || die "$me:\tCannot open manifest file '$manifest'\n";
while( my $l = <DUMP> ) {

  next if $l =~ /^\;/;
  my $sigHR = linesignature($l);
  next unless length($sigHR);
  my $sig = $sigHR;
  # always skip associated comments associated to objects,
  # see https://trac.osgeo.org/postgis/ticket/3078
  $sig =~ s/^COMMENT//g;
  # always skip shelltype when type is skipped
  # see https://trac.osgeo.org/postgis/ticket/5569#comment:16
  $sig =~ s/^SHELLTYPE/TYPE/g;
  $sig =~ s/\s//g;
  $hasTopology = 1 if $sig eq 'SCHEMAtopology';

	if ( not defined ($POSTGIS_SCHEMA) )
	{
		if ( $l =~ / TABLE DATA ([^ ]*) spatial_ref_sys / )
		{
			$POSTGIS_SCHEMA = $1;
			print STDERR "  Setting postgis schema to $POSTGIS_SCHEMA, as found in the dump\n"
			  if $DEBUG;
		}
	}

  if ( $skip{$sig} ) {
    print STDERR "SKIP: $sigHR\n" if $DEBUG;
    next
  }
  print STDERR "KEEP: $sigHR\n" if $DEBUG;
  print MANIFEST $l;

}
close(MANIFEST);
close(DUMP) || die "$me: pg_restore returned an error\n";

######################################################################
# Convert the dump file into an ASCII file, stripping out the
# unwanted bits.
#
print STDERR "  Writing ASCII to stdout...\n"
  if $DEBUG;
open( INPUT, "pg_restore -f - -L $manifest $dumpfile |") || die "$me:\tCan't run pg_restore\n";

if ( defined $POSTGIS_SCHEMA ) {
  print STDOUT "SET search_path = \"" . $POSTGIS_SCHEMA . "\";\n";
}

#
# Disable topology metadata tables triggers to allow for population
# in arbitrary order.
#
if ( $hasTopology ) {
  print STDOUT "ALTER TABLE topology.layer DISABLE TRIGGER ALL;\n";
}

# Drop the spatial_ref_sys_srid_check to allow for custom invalid SRIDs in the dump
print STDOUT "ALTER TABLE spatial_ref_sys DROP constraint "
           . "spatial_ref_sys_srid_check;\n";
# Drop the spatial_ref_sys primary key to allow for SRID conversions
# which possibly end up taking the same spot
print STDOUT "ALTER TABLE spatial_ref_sys DROP constraint "
           . "spatial_ref_sys_pkey;\n";

# Backup entries found in new spatial_ref_sys for later updating the
print STDOUT "CREATE TEMP TABLE _pgis_restore_spatial_ref_sys AS "
            ."SELECT * FROM spatial_ref_sys;\n";
print STDOUT "DELETE FROM spatial_ref_sys;\n";

my $inCopy;
while( my $l = <INPUT> ) {
  if ( $l =~ /^COPY .+ FROM stdin;$/ ) {
    $inCopy = 1;
  }
  elsif ( $inCopy && $l =~ /^\\\.$/ ) {
    $inCopy = 0;
  }

  next if !$inCopy && $l =~ /^ *--/;

  if ( $l =~ /^SET search_path/ ) {
    $l =~ s/; *$/, public;/;
  }

  # This is to avoid confusing OPERATOR CLASS and OPERATOR FAMILY
  # with OPERATOR below
  elsif ( $l =~ /CREATE OPERATOR CLASS/ || $l =~ /CREATE OPERATOR FAMILY/ )
  {
  }

  # We can't skip OPERATORS from the manifest file
  # because it doesn't contain enough information
  # about the type the operator is for
  elsif ( $l =~ /CREATE OPERATOR *([^ ,]*)/)
  {
    my $name = canonicalize_typename($1);
    my $larg = undef;
    my $rarg = undef;
    my @sublines = ($l);
    while( my $subline = <INPUT>)
    {
      push(@sublines, $subline);
      last if $subline =~ /;[\t ]*$/;
      if ( $subline =~ /leftarg *= *([^ ,]*)/i )
      {
        $larg=canonicalize_typename($1);
      }
      if ( $subline =~ /rightarg *= *([^ ,]*)/i )
      {
        $rarg=canonicalize_typename($1);
      }
    }

    if ( ! $larg ) {
      print STDERR "No larg, @sublines: [" . @sublines . "]\n";
    }

    my $sigHR = "OPERATOR " . $name .' ('.$larg.', '.$rarg.')';
    my $sig = $sigHR; $sig =~ s/\s//g;

    if ( $skip{$sig} )
    {
       print STDERR "SKIP: $sig\n" if $DEBUG;
       next;
    }

    print STDERR "KEEP: $sig\n" if $DEBUG;
    print STDOUT @sublines;
    next;
  }

  # Rewrite spatial table constraints
  #
  # Example 1:
  # CREATE TABLE geos_in (
  #     id integer NOT NULL,
  #     g public.geometry,
  #     CONSTRAINT enforce_dims_g CHECK ((public.st_ndims(g) = 2)),
  #     CONSTRAINT enforce_geotype_g CHECK (((public.geometrytype(g) = 'MULTILINESTRING'::text) OR (g IS NULL))),
  #     CONSTRAINT enforce_srid_g CHECK ((public.st_srid(g) = (-1)))
  # );
  #
  # Example 2:
  # CREATE TABLE boszip (
  #     gid integer NOT NULL,
  #     zip5 character(5),
  #     the_geom geometry,
  #     CONSTRAINT enforce_dims_the_geom CHECK ((ndims(the_geom) = 2)),
  #     CONSTRAINT enforce_geotype_the_geom CHECK (((geometrytype(the_geom) = 'MULTIPOLYGON'::text) OR (the_geom IS NULL))),
  #     CONSTRAINT enforce_srid_the_geom CHECK ((srid(the_geom) = 2249))
  # );
  #
  # Example 3:
  # CREATE TABLE "PIANIFICAZIONE__ELEMENTO_LINEA" (
  #     soft_gis_serial integer NOT NULL,
  #     "G" public.geometry,
  #     CONSTRAINT "enforce_dims_G" CHECK ((public.st_ndims("G") = 2)),
  #     CONSTRAINT "enforce_geotype_G" CHECK (((public.geometrytype("G") = 'MULTICURVE'::text) OR ("G" IS NULL))),
  #     CONSTRAINT "enforce_srid_G" CHECK ((public.st_srid("G") = (-1)))
  # );
  #
  #
  elsif ( $l =~ /CREATE TABLE *([^ ,]*)/)
  {
    print STDOUT $l;
    while( my $subline = <INPUT>)
    {
      if ( $subline =~ /CONSTRAINT "?enforce_dims_/i ) {
        $subline =~ s/\bndims\(/st_ndims(/;
      }
      if ( $subline =~ /CONSTRAINT "?enforce_srid_/i ) {
        $subline =~ s/\bsrid\(/st_srid(/;
        if ( $subline =~ /=\s\(?([-0-9][0-9]*)\)/ ) {
          my $oldsrid = $1;
          my $newsrid = clamp_srid($oldsrid);
          $subline =~ s/=\s*(\(?)[-0-9][0-9]*/= $1$newsrid/;
        } else {
          print STDERR "WARNING: could not find SRID value in: $subline";
        }
      }
      print STDOUT $subline;
      last if $subline =~ /;[\t ]*$/;
    }
    next;
  }

  # Parse comments, to avoid skipping quoted comments
  # See http://trac.osgeo.org/postgis/ticket/2759
  elsif ( $l =~ /^COMMENT ON .* IS '(.*)/)
  {
    print STDOUT $l;
    while( my $subline = <INPUT>)
    # A comment ends with an odd number of single quotes and a semicolon
    {
      print STDOUT $subline;
      last if ( $subline !~ /('*)[\t ]*;[\t ]*$/ || length($1) % 2 == 0)
    }
    next;
  }

  # Clamp SRIDS in spatial_ref_sys
  elsif ( $l =~ /COPY spatial_ref_sys /)
  {
    print STDOUT $l;
    while( my $subline = <INPUT>)
    {
      if ( $subline =~ /([0-9]*)\t/ ) {
        my $oldsrid = $1;
          my $newsrid = clamp_srid($oldsrid);
          $subline =~ s/^[0-9]*\t/${newsrid}\t/;
      }
      print STDOUT $subline;
      last if $subline =~ /^\\.$/;
    }
    next;
  }

  print STDOUT $l;

}

if ( defined $POSTGIS_SCHEMA ) {
  print STDOUT "SET search_path = \"" . $POSTGIS_SCHEMA . "\";\n";
}

if ( $hasTopology ) {

  # Re-enable topology.layer table triggers
  print STDOUT "ALTER TABLE topology.layer ENABLE TRIGGER ALL;\n";

  # Update topology SRID from geometry_columns view.
  # This is mainly to fix srids of -1
  # May be worth providing a "populate_topology_topology"
  print STDOUT "UPDATE topology.topology t set srid = g.srid "
             . "FROM geometry_columns g WHERE t.name = g.f_table_schema "
             . "AND g.f_table_name = 'face' and f_geometry_column = 'mbr';\n";

}

# Update spatial_ref_sys with entries found in new table
print STDOUT "UPDATE spatial_ref_sys o set auth_name = n.auth_name, "
           . "auth_srid = n.auth_srid, srtext = n.srtext, "
           . "proj4text = n.proj4text FROM "
           . "_pgis_restore_spatial_ref_sys n WHERE o.srid = n.srid;\n";
# Insert entries only found in new table
print STDOUT "INSERT INTO spatial_ref_sys SELECT * FROM "
           . "_pgis_restore_spatial_ref_sys n WHERE n.srid "
           . "NOT IN ( SELECT srid FROM spatial_ref_sys );\n";
# DROP TABLE _pgis_restore_spatial_ref_sys;
print STDOUT "DROP TABLE _pgis_restore_spatial_ref_sys;\n";

# Try re-enforcing spatial_ref_sys_srid_check, would fail if impossible
# but you'd still have your data
print STDOUT "ALTER TABLE spatial_ref_sys ADD constraint "
           . "spatial_ref_sys_srid_check check "
           . "( srid > 0 and srid < " . ($SRID_USER_MAXIMUM+1) ." ) ;\n";
# Try re-enforcing spatial_ref_sys primary key, would fail if impossible
# but you'd still have your data
print STDOUT "ALTER TABLE spatial_ref_sys ENABLE TRIGGER ALL;\n";
print STDOUT "ALTER TABLE spatial_ref_sys ADD PRIMARY KEY(srid);\n";


print STDERR "Done.\n";

######################################################################
# Strip a dump file manifest line down to the unique elements of
# type and signature.
#
sub linesignature {

  my $line = shift;
  my $sig;

  $line =~ s/\n$//;
  $line =~ s/\r$//;
  $line =~ s/OPERATOR CLASS/OPERATORCLASS/;
  $line =~ s/TABLE DATA/TABLEDATA/;
  $line =~ s/SHELL TYPE/SHELLTYPE/;
  $line =~ s/PROCEDURAL LANGUAGE/PROCEDURALLANGUAGE/;
  $line =~ s/SEQUENCE SET/SEQUENCE_SET/;

  if( $line =~ /^(\d+)\; (\d+) (\d+) FK (\w+) (\w+) (.*) (\w*)/ ) {
    $sig = "FK " . $4 . " " . $6;
  }
  # We strip argument names from function comments
  elsif ( $line =~ /COMMENT \w+ FUNCTION ([^\(]*)\((.*)\)/ )
  {
    my $name = $1;
    my $args = $2;
    my @args = split('\s*,\s*', $args);

    # No inout indicator or out parameters for function signatures
    my @inonly_args = clean_inout_arguments(@args);

    # For *function* signature we are supposed to strip argument names
    my @unnamed_args = strip_argument_names(@inonly_args);

    $sig = "COMMENT FUNCTION $name(" . join(', ', @unnamed_args) . ")";
  }
  elsif( $line =~ /^(\d+)\; (\d+) (\d+) (\w+) - (\w+) (.*) (\w*)/ ) {
    $sig = $4 . " " . $5 . " " . $6;
  }
  elsif( $line =~ /^(\d+)\; (\d+) (\d+) (\w+) (\w+) (.*) (\w*)/ ) {
    $sig = $4 . " " . $6;
  }
  elsif( $line =~ /PROCEDURALLANGUAGE.*plpgsql/ ) {
    $sig = "PROCEDURAL LANGUAGE plpgsql";
  }
  elsif ( $line =~ /SCHEMA - (\w+)/ ) {
    $sig = "SCHEMA $1";
  }
  elsif ( $line =~ /SEQUENCE - (\w+)/ ) {
    $sig = "SEQUENCE $1";
  }
  else {
    # TODO: something smarter here...
    $sig = $line
  }

  # Strip schema from signature
  # TODO: restrict to POSTGIS_SCHEMA
  $sig =~ s/[^\.(, ]*\.//g;

  return $sig;

}

#
# Canonicalize type names (they change between dump versions).
# Here we also strip schema qualification
#
sub
canonicalize_typename
{
	my $arg=shift;

	# Lower case
	$arg = lc($arg);

	# Trim whitespaces
	$arg =~ s/^\s*//;
	$arg =~ s/\s*$//;

	# Strip schema qualification
	#$arg =~ s/^public.//;
	$arg =~ s/^.*\.//;

	# Handle type name changes
	if ( $arg eq 'opaque' ) {
		$arg = 'internal';
	} elsif ( $arg eq 'boolean' ) {
		$arg = 'bool';
	} elsif ( $arg eq 'oldgeometry' ) {
		$arg = 'geometry';
	}

	# Timestamp with or without time zone
	if ( $arg =~ /timestamp .* time zone/ ) {
		$arg = 'timestamp';
	}

	return $arg;
}

# Change SRID to be within allowed ranges
sub
clamp_srid
{
  my $oldsrid = shift;
  my $newsrid = $oldsrid;

  if ( $oldsrid < 0 ) {
    $newsrid = 0;
    printf STDERR "  WARNING: SRID $oldsrid converted to $newsrid (official UNKNOWN)\n";
  } elsif ( $oldsrid > $SRID_MAXIMUM ) {
    $newsrid = $SRID_USER_MAXIMUM + 1 +
      # -1 is to reduce likelihood of clashes
      # NOTE: must match core implementation (lwutil.c)
      ( $oldsrid % ( $SRID_MAXIMUM - $SRID_USER_MAXIMUM - 1 ) );
    printf STDERR "  WARNING: SRID $oldsrid converted to $newsrid (in reserved zone)\n";
  } elsif ( $oldsrid > $SRID_USER_MAXIMUM ) {
    printf STDERR "  WARNING: SRID $newsrid is in reserved zone\n";
  }

  return $newsrid;
}

# Example:
#  INPUT: inout first double precision, second integer, OUT third text, fourth bool
# OUTPUT: first double precision, second integer, fourth bool
sub clean_inout_arguments {
	my @args = @_;
	my @out;
	#print STDERR "DEBUG: to inout strip: " . join(',', @args) . "\n";
	foreach ( @args )
	{
		my $a = $_;

		#print "  XXX arg: [$a]\n";
		# If the arg is composed by multiple words
		# check for out and inout indicators
		if ( $a =~ m/([^ ]*) (.*)/ )
		{
			# Skip from the first out parameter onward
			last if $1 eq 'OUT';

			# Hide the inout indicator
			$a = $2 if $1 eq 'INOUT';
		}
		#print "  XXX arg became: $a\n";
		push @out, $a;
	}
	#print STDERR "DEBUG: inout striped: " . join(',', @out) . "\n";
	return @out;
}

# Remove argument names from array of arguments
# Example:
#  INPUT: int,named double precision,named text
# OUTPUT: int,double precision,text
sub strip_argument_names {
	my @args = @_;
  my %reserved_sql_word = (
    'timestamp' => 1,
    'double' => 1,
    'character' => 1
  );
	my @out;
	#print "XXX to strip: " . join(',', @args) . "\n";
	foreach ( @args )
	{
		my $a = $_;

		#print "  XXX arg: $a\n";
		# Drop all but reserved words from multi-word arg
		while ( $a =~ m/^([^ ]*) (.*)/ )
		{
			last if $reserved_sql_word{$1};
			$a = $2;
			#print "  XXX arg became: $a\n";
		}
		push @out, $a;
	}
	#print "XXX striped: " . join(',', @out) . "\n";
	return @out;
}


######################################################################
# Here are all the signatures we want to skip but we cannot derive
# from current source
#
__END__
ACL SCHEMA topology
ACL TABLE geography_columns
ACL TABLE geometry_columns
ACL TABLE layer
ACL TABLE raster_columns
ACL TABLE raster_overviews
ACL TABLE spatial_ref_sys
ACL TABLE topology
CAST (box2d AS box3d)
CAST (box2d AS geometry)
CAST (box3d AS box)
CAST (box3d AS box2d)
CAST (box3d AS geometry)
CAST (bytea AS geography)
CAST (bytea AS geometry)
CAST CAST (box2d AS box3d)
CAST CAST (box2d AS geometry)
CAST CAST (box3d AS box)
CAST CAST (box3d AS box2d)
CAST CAST (box3d AS geometry)
CAST CAST (bytea AS geography)
CAST CAST (bytea AS geometry)
CAST CAST (geography AS bytea)
CAST CAST (geography AS geography)
CAST CAST (geography AS geometry)
CAST CAST (geometry AS box)
CAST CAST (geometry AS box2d)
CAST CAST (geometry AS box3d)
CAST CAST (geometry AS bytea)
CAST CAST (geometry AS geography)
CAST CAST (geometry AS geometry)
CAST CAST (geometry AS json)
CAST CAST (geometry AS jsonb)
CAST CAST (geometry AS path)
CAST CAST (geometry AS point)
CAST CAST (geometry AS polygon)
CAST CAST (geometry AS text)
CAST CAST (path AS geometry)
CAST CAST (point AS geometry)
CAST CAST (polygon AS geometry)
CAST CAST (raster AS box3d)
CAST CAST (raster AS bytea)
CAST CAST (raster AS geometry)
CAST CAST (text AS geometry)
CAST CAST (topogeometry AS geometry)
CAST CAST (topogeometry AS integer[])
CAST (geography AS bytea)
CAST (geography AS geography)
CAST (geography AS geometry)
CAST (geometry AS box)
CAST (geometry AS box2d)
CAST (geometry AS box3d)
CAST (geometry AS bytea)
CAST (geometry AS geography)
CAST (geometry AS geometry)
CAST (geometry AS json)
CAST (geometry AS jsonb)
CAST (geometry AS path)
CAST (geometry AS point)
CAST (geometry AS polygon)
CAST (geometry AS text)
CAST (path AS geometry)
CAST (point AS geometry)
CAST (polygon AS geometry)
CAST (text AS geometry)
CONSTRAINT layer layer_pkey
CONSTRAINT layer layer_schema_name_table_name_feature_column_key
CONSTRAINT spatial_ref_sys spatial_ref_sys_pkey
CONSTRAINT topology topology_name_key
CONSTRAINT topology topology_pkey
DEFAULT topology id
DOMAIN topoelement
DOMAIN topoelementarray
FK CONSTRAINT layer layer_topology_id_fkey
FUNCTION jsonb(geometry)
FUNCTION json(geometry)
FUNCTION lockrow(text, text, text, text, timestamp without time zone)
FUNCTION lockrow(text, text, text, timestamp without time zone)
FUNCTION _st_aspect4ma(double precision[], integer[], text[])
FUNCTION st_concavehull(geometry, double precision, boolean)
FUNCTION _st_convertarray4ma(double precision[])
FUNCTION st_distinct4ma(double precision[], integer[], text[])
FUNCTION st_distinct4ma(double precision[], text, text[])
FUNCTION _st_grayscale4ma(double precision[], integer[], text[])
FUNCTION _st_hillshade4ma(double precision[], integer[], text[])
FUNCTION st_invdistweight4ma(double precision[], integer[], text[])
FUNCTION _st_mapalgebra(rastbandarg[], regprocedure, text, integer, integer, text, raster, double precision[], boolean, text[])
FUNCTION st_mapalgebra(raster, integer, regprocedure, double precision[], boolean, text, text, raster, text[])
FUNCTION st_max4ma(double precision[], integer[], text[])
FUNCTION st_max4ma(double precision[], text, text[])
FUNCTION st_mean4ma(double precision[], integer[], text[])
FUNCTION st_mean4ma(double precision[], text, text[])
FUNCTION st_min4ma(double precision[], integer[], text[])
FUNCTION st_min4ma(double precision[], text, text[])
FUNCTION st_mindist4ma(double precision[], integer[], text[])
FUNCTION st_range4ma(double precision[], integer[], text[])
FUNCTION st_range4ma(double precision[], text, text[])
FUNCTION st_rastertoworldcoord(raster, integer, integer)
FUNCTION _st_roughness4ma(double precision[], integer[], text[])
FUNCTION st_setvalues(raster, integer, integer, integer, double precision[], boolean[], boolean)
FUNCTION _st_setvalues(raster, integer, integer, integer, double precision[], boolean[], boolean, double precision, boolean)
FUNCTION st_setvalues(raster, integer, integer, integer, double precision[], double precision, boolean)
FUNCTION _st_slope4ma(double precision[], integer[], text[])
FUNCTION st_stddev4ma(double precision[], integer[], text[])
FUNCTION st_stddev4ma(double precision[], text, text[])
FUNCTION st_sum4ma(double precision[], integer[], text[])
FUNCTION st_sum4ma(double precision[], text, text[])
FUNCTION _st_tpi4ma(double precision[], integer[], text[])
FUNCTION _st_tri4ma(double precision[], integer[], text[])
OPERATOR &&
OPERATOR &&&
OPERATOR &/&
OPERATOR &<
OPERATOR &<|
OPERATOR &>
OPERATOR <
OPERATOR <#>
OPERATOR <->
OPERATOR <<
OPERATOR <<->>
OPERATOR <<@
OPERATOR <<|
OPERATOR <=
OPERATOR =
OPERATOR >
OPERATOR >=
OPERATOR >>
OPERATOR @
OPERATOR @>>
OPERATOR @@
OPERATOR |&>
OPERATOR |=|
OPERATOR |>>
OPERATOR ~
OPERATOR ~=
OPERATOR ~==
OPERATOR ~~
OPERATOR ~~=
OPERATOR <>
OPERATOR&&(box2df,box2df)
OPERATOR@(box2df,box2df)
OPERATOR~(box2df,box2df)
OPERATOR&&(box2df,geometry)
OPERATOR@(box2df,geometry)
OPERATOR~(box2df,geometry)
OPERATORCLASS brin_geography_inclusion_ops
OPERATORCLASS brin_geometry_inclusion_ops_2d
OPERATORCLASS brin_geometry_inclusion_ops_3d
OPERATORCLASS brin_geometry_inclusion_ops_4d
OPERATORCLASS btree_geography_ops
OPERATORCLASS btree_geometry_ops
OPERATORCLASS gist_geography_ops
OPERATORCLASS gist_geometry_ops_2d
OPERATORCLASS gist_geometry_ops_nd
OPERATORCLASS hash_geometry_ops
OPERATORCLASS hash_raster_ops
OPERATORCLASS spgist_geography_ops_nd
OPERATORCLASS spgist_geometry_ops_2d
OPERATORCLASS spgist_geometry_ops_3d
OPERATORCLASS spgist_geometry_ops_nd
OPERATOR&&(geography,geography)
OPERATOR<(geography,geography)
OPERATOR<->(geography,geography)
OPERATOR<=(geography,geography)
OPERATOR=(geography,geography)
OPERATOR>(geography,geography)
OPERATOR>=(geography,geography)
OPERATOR&&(geography,gidx)
OPERATOR&&(geometry,box2df)
OPERATOR@(geometry,box2df)
OPERATOR~(geometry,box2df)
OPERATOR&&&(geometry,geometry)
OPERATOR&&(geometry,geometry)
OPERATOR&/&(geometry,geometry)
OPERATOR&<(geometry,geometry)
OPERATOR&<|(geometry,geometry)
OPERATOR&>(geometry,geometry)
OPERATOR<#>(geometry,geometry)
OPERATOR<(geometry,geometry)
OPERATOR<->(geometry,geometry)
OPERATOR<<(geometry,geometry)
OPERATOR<<->>(geometry,geometry)
OPERATOR<<@(geometry,geometry)
OPERATOR<<|(geometry,geometry)
OPERATOR<=(geometry,geometry)
OPERATOR=(geometry,geometry)
OPERATOR>(geometry,geometry)
OPERATOR>=(geometry,geometry)
OPERATOR>>(geometry,geometry)
OPERATOR@(geometry,geometry)
OPERATOR@>>(geometry,geometry)
OPERATOR@@(geometry,geometry)
OPERATOR|&>(geometry,geometry)
OPERATOR|=|(geometry,geometry)
OPERATOR|>>(geometry,geometry)
OPERATOR~(geometry,geometry)
OPERATOR~=(geometry,geometry)
OPERATOR~==(geometry,geometry)
OPERATOR~~(geometry,geometry)
OPERATOR~~=(geometry,geometry)
OPERATOR&&&(geometry,gidx)
OPERATOR&&(gidx,geography)
OPERATOR&&&(gidx,geometry)
OPERATOR&&&(gidx,gidx)
OPERATOR&&(gidx,gidx)
OPERATOR public brin_geography_inclusion_ops
OPERATOR public brin_geometry_inclusion_ops_2d
OPERATOR public brin_geometry_inclusion_ops_3d
OPERATOR public brin_geometry_inclusion_ops_4d
OPERATOR public btree_geography_ops
OPERATOR public btree_geometry_ops
OPERATOR public gist_geography_ops
OPERATOR public gist_geometry_ops_2d
OPERATOR public gist_geometry_ops_nd
OPERATOR public hash_geometry_ops
OPERATOR public hash_raster_ops
OPERATOR public spgist_geography_ops_nd
OPERATOR public spgist_geometry_ops_2d
OPERATOR public spgist_geometry_ops_3d
OPERATOR public spgist_geometry_ops_nd
RULE geometry_columns geometry_columns_delete
RULE geometry_columns geometry_columns_insert
RULE geometry_columns geometry_columns_update
SCHEMA topology
SEQUENCE BY topology topology_id_seq
SEQUENCE topology_id_seq
SEQUENCE topology topology_id_seq
SHELLTYPE box2d
SHELLTYPE box2df
SHELLTYPE box3d
SHELLTYPE geography
SHELLTYPE geometry
SHELLTYPE gidx
SHELLTYPE raster
SHELLTYPE spheroid
TABLEDATA spatial_ref_sys
TABLE layer
TABLE spatial_ref_sys
TABLE topology
TRIGGER layer layer_integrity_checks
VIEW geography_columns
VIEW geometry_columns
VIEW raster_columns
VIEW raster_overviews
