Hi

rebase

Regards

Pavel
diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml
index 11246aa653..89a07678ee 100644
--- a/doc/src/sgml/plpgsql.sgml
+++ b/doc/src/sgml/plpgsql.sgml
@@ -4606,6 +4606,99 @@ CREATE EVENT TRIGGER snitch ON ddl_command_start EXECUTE FUNCTION snitch();
 
   </sect1>
 
+ <sect1 id="plpgsql-window">
+  <title>Window Functions</title>
+
+  <indexterm zone="plpgsql-window">
+   <primary>window</primary>
+   <secondary>in PL/pgSQL</secondary>
+  </indexterm>
+
+  <para>
+   <application>PL/pgSQL</application> can be used to define window
+   functions. A window function is created with the <command>CREATE FUNCTION</command>
+   command with clause <literal>WINDOW</literal>. The specific feature of
+   this functions is a possibility to two special storages with 
+   sorted values of window function arguments and store with stored
+   one value of any type for currently processed partition (of window
+   function).
+  </para>
+
+  <para>
+   Access to both storages is done with special internal variable
+   <varname>WINDOWOBJECT</varname>. This variable is declared implicitly,
+   and it is available only in window functions.
+
+<programlisting>
+CREATE OR REPLACE FUNCTION plpgsql_rownum() RETURNS int8
+LANGUAGE plpgsql WINDOW
+AS $$
+DECLARE pos int8
+BEGIN
+    pos := get_current_position(WINDOWOBJECT);
+    pos := pos + 1;
+    PERFORM set_mark_position(WINDOWOBJECT, pos);
+RETURN pos;
+$$;
+
+SELECT plpgsql_rownum() OVER (), * FROM tab;
+</programlisting>
+  </para>
+
+  <para>
+   The arguments of window function cannot be accessed directly. The special
+   functions should be used. With these functions we can choose a scope of
+   buffered arguments, we can choose a wanted position against first, current, or
+   last row. The implementation of <function>lag</function> can looks like
+   (the window functions in plpgsql can use polymorphic types too):
+
+<programlisting>
+CREATE OR REPLACE FUNCTION plpgsql_lag(anyelement) RETURNS anyelement
+LANGUAGE plpgsql WINDOW
+AS $$
+BEGIN
+    RETURN
+      get_input_value_in_partition(WINDOWOBJECT,
+                                   1, -1,
+                                   'seek_current',
+                                   false);
+END;
+$$;
+
+SELECT v, plpgsql_lag(v) FROM generate_series(1, 10) g(v);
+</programlisting>
+
+  </para>
+
+  <para>
+   Second buffer that can be used in window function is a buffer for one value
+   assigned to partition. The content of this buffer can be read by function
+   <function>get_partition_context_value</function> or modified by function
+   <function>set_partition_context_value</function>. Next function replaces
+   missing values by previous non <literal>NULL</literal> value:
+
+<programlisting>
+CREATE OR REPLACE FUNCTION plpgsql_replace_missing(numeric) RETURNS numeric
+LANGUAGE plpgsql WINDOW
+AS $$
+DECLATE
+    v numeric;
+BEGIN
+    v := get_input_value_for_row(WINDOWOBJECT, 1);
+    IF v IS NULL THEN
+        v := get_partition_context_value(WINDOWOBJECT, NULL::numeric);
+    ELSE
+        PERFORM set_partition_context_value(WINDOWOBJECT, v);
+    END IF;
+    RETURN v;
+END;
+$$;
+</programlisting>
+
+  </para>
+
+  </sect1>
+
   <sect1 id="plpgsql-implementation">
    <title><application>PL/pgSQL</application> under the Hood</title>
 
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index b140c210bc..37d38d6e84 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -1442,6 +1442,18 @@ LANGUAGE internal
 STRICT IMMUTABLE PARALLEL SAFE
 AS 'unicode_is_normalized';
 
+CREATE OR REPLACE FUNCTION
+  get_partition_context_value(windowobjectproxy, anyelement, int4 DEFAULT NULL)
+  RETURNS anyelement
+LANGUAGE internal
+AS 'windowobject_get_partition_context_value';
+
+CREATE OR REPLACE FUNCTION
+  set_partition_context_value(windowobjectproxy, anyelement, int4 DEFAULT NULL)
+  RETURNS void
+LANGUAGE internal
+AS 'windowobject_set_partition_context_value';
+
 --
 -- The default permissions for functions mean that anyone can execute them.
 -- A number of functions shouldn't be executable by just anyone, but rather
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 82732146d3..4e10e2bde7 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -105,6 +105,7 @@ OBJS = \
 	tsvector.o \
 	tsvector_op.o \
 	tsvector_parser.o \
+	typedvalue.o \
 	uuid.o \
 	varbit.o \
 	varchar.o \
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index 99a93271fe..3745cc6515 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -372,6 +372,17 @@ pg_node_tree_send(PG_FUNCTION_ARGS)
 PSEUDOTYPE_DUMMY_IO_FUNCS(pg_ddl_command);
 PSEUDOTYPE_DUMMY_BINARY_IO_FUNCS(pg_ddl_command);
 
+/*
+ * windowobjectproxy
+ *
+ * This type is pointer to WindowObjectProxyData. It is communication
+ * mechanism between PL environment and WinFuncArgs functions. Due
+ * performance reason I prefer using indirect result processing against
+ * using function returning polymorphic composite value. The indirect
+ * mechanism is implemented with proxy object represented by type
+ * WindowObjectProxyData.
+ */
+PSEUDOTYPE_DUMMY_IO_FUNCS(windowobjectproxy);
 
 /*
  * Dummy I/O functions for various other pseudotypes.
diff --git a/src/backend/utils/adt/typedvalue.c b/src/backend/utils/adt/typedvalue.c
new file mode 100644
index 0000000000..370804a05a
--- /dev/null
+++ b/src/backend/utils/adt/typedvalue.c
@@ -0,0 +1,589 @@
+/*-------------------------------------------------------------------------
+ *
+ * typedvalue.c
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/utils/adt/typedvalue.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "catalog/pg_type.h"
+#include "utils/builtins.h"
+#include "utils/datum.h"
+#include "utils/lsyscache.h"
+#include "utils/numeric.h"
+#include "utils/typedvalue.h"
+#include "fmgr.h"
+
+/*
+ * Returns Datum value stored in TypedValue structure.
+ * This structure can hold byval or varlena values.
+ */
+static Datum
+TypedValueGetDatum(TypedValue tv)
+{
+	if (tv->typbyval)
+		return *((Datum *) tv->data);
+	else
+		return PointerGetDatum(tv->data);
+}
+
+/*
+ * IN function for TypedValue type. It store input value as text type
+ * value.
+ */
+Datum
+typedvalue_in(PG_FUNCTION_ARGS)
+{
+	char	   *str =  PG_GETARG_CSTRING(0);
+	int			len;
+	Size		size;
+	TypedValue	tv;
+	text	   *txt;
+
+	len = strlen(str);
+
+	size = MAXALIGN(offsetof(TypedValueData, data) + len + VARHDRSZ);
+
+	tv = (TypedValue) palloc(size);
+	SET_VARSIZE(tv, size);
+
+	txt = (text *) tv->data;
+
+	SET_VARSIZE(txt, VARHDRSZ + len);
+	memcpy(VARDATA(txt), str, len);
+
+	tv->typid = TEXTOID;
+	tv->typbyval = false;
+	tv->typlen = -1;
+
+	PG_RETURN_POINTER(tv);
+}
+
+/*
+ * OUT function for TypedValue type. It search related output
+ * function for stored type, execute it, and returns result.
+ */
+Datum
+typedvalue_out(PG_FUNCTION_ARGS)
+{
+	Oid			typOutput;
+	bool		isVarlena;
+	char	   *str;
+	TypedValue	tv;
+	Datum		value;
+
+	tv = (TypedValue) PG_GETARG_POINTER(0);
+
+	getTypeOutputInfo(tv->typid, &typOutput, &isVarlena);
+	value = TypedValueGetDatum(tv);
+	str = OidOutputFunctionCall(typOutput, value);
+
+	PG_RETURN_CSTRING(str);
+}
+
+/*
+ * Constructor function for TypedValue type. It serializes
+ * byval or varlena Datum values.
+ */
+Datum
+makeTypedValue(Datum value, Oid typid, int16 typlen, bool typbyval)
+{
+	TypedValue		tv;
+	Size			size;
+	Size			copy_bytes = 0;
+
+	if (typbyval)
+		size = MAXALIGN(offsetof(TypedValueData, data) + sizeof(Datum));
+	else
+	{
+		if (typlen != -1)
+		{
+			size = MAXALIGN(offsetof(TypedValueData, data) + typlen);
+			copy_bytes = typlen;
+		}
+		else
+		{
+			copy_bytes = VARSIZE_ANY((struct varlena *) DatumGetPointer(value));
+			size = MAXALIGN(offsetof(TypedValueData, data) + copy_bytes);
+		}
+	}
+
+	tv = (TypedValue) palloc(size);
+
+	SET_VARSIZE(tv, size);
+
+	tv->typid = typid;
+	tv->typlen = typlen;
+	tv->typbyval = typbyval;
+
+	if (typbyval)
+		*((Datum *) tv->data) = value;
+	else
+		memcpy(tv->data, DatumGetPointer(value), copy_bytes);
+
+	return PointerGetDatum(tv);
+}
+
+/*
+ * Fast cast finding of known (buildin) cast functions.
+ */
+static PGFunction
+get_direct_cast_func(Oid source_typid, Oid target_typid)
+{
+	switch (target_typid)
+	{
+		case NUMERICOID:
+		{
+			switch (source_typid)
+			{
+				case INT4OID:
+					return int4_numeric;
+
+				case INT8OID:
+					return int8_numeric;
+
+				case FLOAT4OID:
+					return float4_numeric;
+
+				case FLOAT8OID:
+					return float8_numeric;
+
+				default:
+					return NULL;
+			}
+		}
+
+		case INT8OID:
+		{
+			switch (source_typid)
+			{
+				case INT4OID:
+					return int48;
+
+				case NUMERICOID:
+					return numeric_int8;
+
+				case FLOAT4OID:
+					return ftoi8;
+
+				case FLOAT8OID:
+					return dtoi8;
+
+				default:
+					return NULL;
+			}
+		}
+
+		case INT4OID:
+		{
+			switch (source_typid)
+			{
+				case BOOLOID:
+					return bool_int4;
+
+				case INT8OID:
+					return int84;
+
+				case NUMERICOID:
+					return numeric_int4;
+
+				case FLOAT4OID:
+					return ftoi4;
+
+				case FLOAT8OID:
+					return dtoi4;
+
+				default:
+					return NULL;
+			}
+		}
+
+		case FLOAT8OID:
+		{
+			switch (source_typid)
+			{
+				case INT4OID:
+					return i4tod;
+
+				case INT8OID:
+					return i8tod;
+
+				case NUMERICOID:
+					return numeric_float8;
+
+				case FLOAT4OID:
+					return ftod;
+
+				default:
+					return NULL;
+			}
+		}
+
+		case FLOAT4OID:
+		{
+			switch (source_typid)
+			{
+				case INT4OID:
+					return i4tof;
+
+				case INT8OID:
+					return i8tof;
+
+				case NUMERICOID:
+					return numeric_float4;
+
+				case FLOAT8OID:
+					return dtof;
+
+				default:
+					return NULL;
+			}
+		}
+
+		case DATEOID:
+		{
+			switch (source_typid)
+			{
+				case TIMESTAMPOID:
+					return timestamp_date;
+
+				case TIMESTAMPTZOID:
+					return timestamptz_date;
+
+				default:
+					return NULL;
+			}
+		}
+
+		case TIMESTAMPOID:
+		{
+			switch (source_typid)
+			{
+				case DATEOID:
+					return date_timestamp;
+
+				case TIMESTAMPTZOID:
+					return timestamptz_timestamp;
+
+				default:
+					return NULL;
+			}
+		}
+
+		case TIMESTAMPTZOID:
+		{
+			switch (source_typid)
+			{
+				case DATEOID:
+					return date_timestamptz;
+
+				case TIMESTAMPOID:
+					return timestamp_timestamptz;
+
+				default:
+					return NULL;
+			}
+		}
+
+		case BOOLOID:
+		{
+			switch (source_typid)
+			{
+				case INT4OID:
+					return int4_bool;
+
+				default:
+					return NULL;
+			}
+		}
+
+		default:
+			return NULL;
+	}
+}
+
+static Datum
+cast_typedvalue_to(TypedValue tv, Oid target_typid)
+{
+	Datum		value = TypedValueGetDatum(tv);
+	Datum		result;
+	char	   *str;
+	PGFunction	cast_func;
+	Oid			cast_func_oid;
+	Oid			typinput;
+	Oid			typioparam;
+	Oid			typoutput;
+	bool		isvarlena;
+
+	if (tv->typid == target_typid)
+		return datumCopy(value,
+						 tv->typbyval,
+						 tv->typlen);
+
+	if (tv->typid == TEXTOID || tv->typid == BPCHAROID)
+	{
+		getTypeInputInfo(target_typid, &typinput, &typioparam);
+		str = TextDatumGetCString(value);
+		result = OidInputFunctionCall(typinput, str,
+									  typioparam, -1);
+		pfree(str);
+
+		return result;
+	}
+
+	if (target_typid == TEXTOID || target_typid == BPCHAROID)
+	{
+		getTypeOutputInfo(tv->typid, &typoutput, &isvarlena);
+		str = OidOutputFunctionCall(typoutput, value);
+		result = PointerGetDatum(cstring_to_text(str));
+
+		return result;
+	}
+
+	/* fast cast func detection, and direct call */
+	cast_func = get_direct_cast_func(tv->typid, target_typid);
+	if (cast_func)
+	{
+		result = DirectFunctionCall1(cast_func, value);
+
+		return result;
+	}
+
+	/* slower cast func, and indirect call */
+	cast_func_oid = get_cast_oid(tv->typid, target_typid, false);
+	if (OidIsValid(cast_func_oid))
+	{
+		result = OidFunctionCall1(cast_func_oid, value);
+
+		return result;
+	}
+
+	/* IO cast - most slow */
+	getTypeOutputInfo(tv->typid, &typoutput, &isvarlena);
+	str = OidOutputFunctionCall(typoutput, value);
+
+	getTypeInputInfo(target_typid, &typinput, &typioparam);
+	result = OidInputFunctionCall(typinput, str,
+								  typioparam, -1);
+	pfree(str);
+
+	return result;
+}
+
+/*
+ * Casting functions - from variant typedvalue to specific types
+ */
+Datum
+typedvalue_to_numeric(PG_FUNCTION_ARGS)
+{
+	TypedValue tv = (TypedValue) PG_GETARG_POINTER(0);
+
+	PG_RETURN_DATUM(cast_typedvalue_to(tv, NUMERICOID));
+}
+
+Datum
+typedvalue_to_int8(PG_FUNCTION_ARGS)
+{
+	TypedValue tv = (TypedValue) PG_GETARG_POINTER(0);
+
+	PG_RETURN_DATUM(cast_typedvalue_to(tv, INT8OID));
+}
+
+Datum
+typedvalue_to_int4(PG_FUNCTION_ARGS)
+{
+	TypedValue tv = (TypedValue) PG_GETARG_POINTER(0);
+
+	PG_RETURN_DATUM(cast_typedvalue_to(tv, INT4OID));
+}
+
+Datum
+typedvalue_to_float8(PG_FUNCTION_ARGS)
+{
+	TypedValue tv = (TypedValue) PG_GETARG_POINTER(0);
+
+	PG_RETURN_DATUM(cast_typedvalue_to(tv, FLOAT8OID));
+}
+
+Datum
+typedvalue_to_float4(PG_FUNCTION_ARGS)
+{
+	TypedValue tv = (TypedValue) PG_GETARG_POINTER(0);
+
+	PG_RETURN_DATUM(cast_typedvalue_to(tv, FLOAT4OID));
+}
+
+Datum
+typedvalue_to_date(PG_FUNCTION_ARGS)
+{
+	TypedValue tv = (TypedValue) PG_GETARG_POINTER(0);
+
+	PG_RETURN_DATUM(cast_typedvalue_to(tv, DATEOID));
+}
+
+Datum
+typedvalue_to_timestamp(PG_FUNCTION_ARGS)
+{
+	TypedValue tv = (TypedValue) PG_GETARG_POINTER(0);
+
+	PG_RETURN_DATUM(cast_typedvalue_to(tv, TIMESTAMPOID));
+}
+
+Datum
+typedvalue_to_timestamptz(PG_FUNCTION_ARGS)
+{
+	TypedValue tv = (TypedValue) PG_GETARG_POINTER(0);
+
+	PG_RETURN_DATUM(cast_typedvalue_to(tv, TIMESTAMPTZOID));
+}
+
+Datum
+typedvalue_to_interval(PG_FUNCTION_ARGS)
+{
+	TypedValue tv = (TypedValue) PG_GETARG_POINTER(0);
+
+	PG_RETURN_DATUM(cast_typedvalue_to(tv, INTERVALOID));
+}
+
+Datum
+typedvalue_to_text(PG_FUNCTION_ARGS)
+{
+	TypedValue tv = (TypedValue) PG_GETARG_POINTER(0);
+
+	PG_RETURN_DATUM(cast_typedvalue_to(tv, TEXTOID));
+}
+
+Datum
+typedvalue_to_bpchar(PG_FUNCTION_ARGS)
+{
+	TypedValue tv = (TypedValue) PG_GETARG_POINTER(0);
+
+	PG_RETURN_DATUM(cast_typedvalue_to(tv, BPCHAROID));
+}
+
+Datum
+typedvalue_to_bool(PG_FUNCTION_ARGS)
+{
+	TypedValue tv = (TypedValue) PG_GETARG_POINTER(0);
+
+	PG_RETURN_DATUM(cast_typedvalue_to(tv, BOOLOID));
+}
+
+/*
+ * Cast function from specific types to TypedValue
+ */
+Datum
+numeric_to_typedvalue(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_DATUM(makeTypedValue(PG_GETARG_DATUM(0),
+								   NUMERICOID,
+								   -1,
+								   false));
+}
+
+Datum
+int8_to_typedvalue(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_DATUM(makeTypedValue(PG_GETARG_DATUM(0),
+								   INT8OID,
+								   8,
+								   true));
+}
+
+Datum
+int4_to_typedvalue(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_DATUM(makeTypedValue(PG_GETARG_DATUM(0),
+								   INT4OID,
+								   4,
+								   true));
+}
+
+Datum
+float8_to_typedvalue(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_DATUM(makeTypedValue(PG_GETARG_DATUM(0),
+								   FLOAT8OID,
+								   8,
+								   true));
+}
+
+Datum
+float4_to_typedvalue(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_DATUM(makeTypedValue(PG_GETARG_DATUM(0),
+								   FLOAT4OID,
+								   4,
+								   true));
+}
+
+
+Datum
+date_to_typedvalue(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_DATUM(makeTypedValue(PG_GETARG_DATUM(0),
+								   DATEOID,
+								   4,
+								   true));
+}
+
+Datum
+timestamp_to_typedvalue(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_DATUM(makeTypedValue(PG_GETARG_DATUM(0),
+								   TIMESTAMPOID,
+								   8,
+								   true));
+}
+
+Datum
+timestamptz_to_typedvalue(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_DATUM(makeTypedValue(PG_GETARG_DATUM(0),
+								   TIMESTAMPTZOID,
+								   8,
+								   true));
+}
+
+Datum
+interval_to_typedvalue(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_DATUM(makeTypedValue(PG_GETARG_DATUM(0),
+								   INTERVALOID,
+								   16,
+								   false));
+}
+
+Datum
+text_to_typedvalue(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_DATUM(makeTypedValue(PG_GETARG_DATUM(0),
+								   TEXTOID,
+								   -1,
+								   false));
+}
+
+Datum
+bpchar_to_typedvalue(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_DATUM(makeTypedValue(PG_GETARG_DATUM(0),
+								   BPCHAROID,
+								   -1,
+								   false));
+}
+
+Datum
+bool_to_typedvalue(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_DATUM(makeTypedValue(PG_GETARG_DATUM(0),
+								   BOOLOID,
+								   1,
+								   true));
+}
diff --git a/src/backend/utils/adt/windowfuncs.c b/src/backend/utils/adt/windowfuncs.c
index f0c8ae686d..e6bee14899 100644
--- a/src/backend/utils/adt/windowfuncs.c
+++ b/src/backend/utils/adt/windowfuncs.c
@@ -14,6 +14,9 @@
 #include "postgres.h"
 
 #include "utils/builtins.h"
+#include "utils/datum.h"
+#include "utils/lsyscache.h"
+#include "utils/typedvalue.h"
 #include "windowapi.h"
 
 /*
@@ -35,6 +38,20 @@ typedef struct
 	int64		remainder;		/* (total rows) % (bucket num) */
 } ntile_context;
 
+#define PROXY_CONTEXT_MAGIC 19730715
+
+typedef struct
+{
+	int			magic;
+	Oid			typid;
+	int16		typlen;
+	bool		typbyval;
+	int			allocsize;
+	bool		isnull;
+	Datum		value;
+	char		data[FLEXIBLE_ARRAY_MEMBER];
+} proxy_context;
+
 static bool rank_up(WindowObject winobj);
 static Datum leadlag_common(FunctionCallInfo fcinfo,
 							bool forward, bool withoffset, bool withdefault);
@@ -472,3 +489,467 @@ window_nth_value(PG_FUNCTION_ARGS)
 
 	PG_RETURN_DATUM(result);
 }
+
+/*
+ * High level access function. These functions are wrappers for windows API
+ * for PL languages based on usage WindowObjectProxy.
+ */
+Datum
+windowobject_get_current_position(PG_FUNCTION_ARGS)
+{
+	WindowObjectProxy	wop;
+	WindowObject winobj;
+	int64		pos;
+
+	wop = (WindowObjectProxy) DatumGetPointer(PG_GETARG_DATUM(0));
+
+	winobj = wop->winobj;
+	Assert(WindowObjectIsValid(winobj));
+
+	pos = WinGetCurrentPosition(winobj);
+
+	PG_RETURN_INT64(pos);
+}
+
+Datum
+windowobject_set_mark_position(PG_FUNCTION_ARGS)
+{
+	WindowObjectProxy	wop;
+	WindowObject winobj;
+	int64		pos;
+
+	wop = (WindowObjectProxy) DatumGetPointer(PG_GETARG_DATUM(0));
+
+	winobj = wop->winobj;
+	Assert(WindowObjectIsValid(winobj));
+
+	pos = PG_GETARG_INT64(1);
+
+	WinSetMarkPosition(winobj, pos);
+
+	PG_RETURN_VOID();
+}
+
+Datum
+windowobject_get_partition_rowcount(PG_FUNCTION_ARGS)
+{
+	WindowObjectProxy	wop;
+	WindowObject winobj;
+	int64		rc;
+
+	wop = (WindowObjectProxy) DatumGetPointer(PG_GETARG_DATUM(0));
+
+	winobj = wop->winobj;
+	Assert(WindowObjectIsValid(winobj));
+
+	rc = WinGetPartitionRowCount(winobj);
+
+	PG_RETURN_INT64(rc);
+}
+
+Datum
+windowobject_rows_are_peers(PG_FUNCTION_ARGS)
+{
+	WindowObjectProxy	wop;
+	WindowObject winobj;
+	int64		pos1,
+				pos2;
+
+	wop = (WindowObjectProxy) DatumGetPointer(PG_GETARG_DATUM(0));
+
+	winobj = wop->winobj;
+	Assert(WindowObjectIsValid(winobj));
+
+	pos1 = PG_GETARG_INT64(1);
+	pos2 = PG_GETARG_INT64(2);
+
+	PG_RETURN_BOOL(WinRowsArePeers(winobj, pos1, pos2));
+}
+
+#define SEEK_CURRENT_STR		"seek_current"
+#define SEEK_HEAD_STR			"seek_head"
+#define SEEK_TAIL_STR			"seek_tail"
+
+#define STRLEN(s)				(sizeof(s) - 1)
+
+static int
+get_seek_type(text *seektype)
+{
+	char	   *str;
+	int			len;
+	int			result;
+
+	str = VARDATA_ANY(seektype);
+	len = VARSIZE_ANY_EXHDR(seektype);
+
+	if (len == STRLEN(SEEK_CURRENT_STR) && strncmp(str, SEEK_CURRENT_STR, len) == 0)
+		result = WINDOW_SEEK_CURRENT;
+	else if (len == STRLEN(SEEK_HEAD_STR) && strncmp(str, SEEK_HEAD_STR, len) == 0)
+		result = WINDOW_SEEK_HEAD;
+	else if (len == STRLEN(SEEK_TAIL_STR) && strncmp(str, SEEK_TAIL_STR, len) == 0)
+		result = WINDOW_SEEK_TAIL;
+	else
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("seek type value must be \"seek_current\", \"seek_head\" or \"seek_tail\"")));
+
+	return result;
+}
+
+static Oid
+wop_funcarg_info(WindowObjectProxy wop,
+						   int argno,
+						   int16 *typlen,
+						   bool *typbyval)
+{
+	WindowObjectProxyMutable	*mutable_data = wop->mutable_data;
+
+	if (argno != mutable_data->last_argno)
+	{
+		Oid		argtypid = get_fn_expr_argtype(wop->fcinfo->flinfo, argno);
+
+		mutable_data->typid = getBaseType(argtypid);
+		get_typlenbyval(mutable_data->typid,
+						&mutable_data->typlen,
+						&mutable_data->typbyval);
+		mutable_data->last_argno = argno;
+	}
+
+	*typlen = mutable_data->typlen;
+	*typbyval = mutable_data->typbyval;
+
+	return mutable_data->typid;
+}
+
+Datum
+windowobject_get_func_arg_partition(PG_FUNCTION_ARGS)
+{
+	WindowObjectProxy	wop;
+	WindowObject winobj;
+	int			argno;
+	int			relpos;
+	int			seektype;
+	bool		set_mark;
+	Datum		value;
+	bool		isnull;
+	Oid			typid;
+	int16		typlen;
+	bool		typbyval;
+
+	wop = (WindowObjectProxy) DatumGetPointer(PG_GETARG_DATUM(0));
+
+	winobj = wop->winobj;
+	Assert(WindowObjectIsValid(winobj));
+
+	argno = PG_GETARG_INT32(1);
+
+	if (argno < 1)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("arg number less than one")));
+
+	argno -= 1;
+
+	relpos = PG_GETARG_INT32(2);
+	seektype = get_seek_type(PG_GETARG_TEXT_P(3));
+	set_mark = PG_GETARG_BOOL(4);
+
+	value = WinGetFuncArgInPartition(winobj,
+											argno,
+											relpos,
+											seektype,
+											set_mark,
+											&isnull,
+											&wop->mutable_data->isout);
+
+	if (isnull)
+		PG_RETURN_NULL();
+
+	typid = wop_funcarg_info(wop, argno, &typlen, &typbyval);
+
+	PG_RETURN_DATUM(makeTypedValue(value, typid, typlen, typbyval));
+}
+
+Datum
+windowobject_get_func_arg_frame(PG_FUNCTION_ARGS)
+{
+	WindowObjectProxy	wop;
+	WindowObject winobj;
+	int			argno;
+	int			relpos;
+	int			seektype;
+	bool		set_mark;
+	Datum		value;
+	bool		isnull;
+	Oid			typid;
+	int16		typlen;
+	bool		typbyval;
+
+	wop = (WindowObjectProxy) DatumGetPointer(PG_GETARG_DATUM(0));
+
+	winobj = wop->winobj;
+	Assert(WindowObjectIsValid(winobj));
+
+	argno = PG_GETARG_INT32(1);
+	relpos = PG_GETARG_INT32(2);
+	seektype = get_seek_type(PG_GETARG_TEXT_P(3));
+	set_mark = PG_GETARG_BOOL(4);
+
+	if (argno < 1)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("arg number less than one")));
+
+	argno -= 1;
+
+	value = WinGetFuncArgInFrame(winobj,
+											argno,
+											relpos,
+											seektype,
+											set_mark,
+											&isnull,
+											&wop->mutable_data->isout);
+
+	if (isnull)
+		PG_RETURN_NULL();
+
+	typid = wop_funcarg_info(wop, argno, &typlen, &typbyval);
+
+	PG_RETURN_DATUM(makeTypedValue(value, typid, typlen, typbyval));
+}
+
+Datum
+windowobject_get_func_arg_current(PG_FUNCTION_ARGS)
+{
+	WindowObjectProxy	wop;
+	WindowObject winobj;
+	int			argno;
+	Datum		value;
+	bool		isnull;
+	Oid			typid;
+	int16		typlen;
+	bool		typbyval;
+
+	wop = (WindowObjectProxy) DatumGetPointer(PG_GETARG_DATUM(0));
+
+	winobj = wop->winobj;
+
+	Assert(WindowObjectIsValid(winobj));
+
+	argno = PG_GETARG_INT32(1);
+
+	if (argno < 1)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("arg number less than one")));
+
+	argno -= 1;
+
+	value = WinGetFuncArgCurrent(winobj, argno, &isnull);
+
+	wop->mutable_data->isout = false;
+
+	if (isnull)
+		PG_RETURN_NULL();
+
+	typid = wop_funcarg_info(wop, argno, &typlen, &typbyval);
+	PG_RETURN_DATUM(makeTypedValue(value, typid, typlen, typbyval));
+}
+
+static void
+copy_datum_to_partition_context(proxy_context *pcontext,
+								Datum value,
+								bool isnull)
+{
+	if (!isnull)
+	{
+		if (pcontext->typbyval)
+			pcontext->value = value;
+		else if (pcontext->typlen == -1)
+		{
+			struct varlena *s = (struct varlena *) DatumGetPointer(value);
+
+			memcpy(pcontext->data, s, VARSIZE_ANY(s));
+			pcontext->value = PointerGetDatum(pcontext->data);
+		}
+		else
+		{
+			memcpy(pcontext->data, DatumGetPointer(value), pcontext->typlen);
+			pcontext->value = PointerGetDatum(pcontext->data);
+		}
+
+		pcontext->isnull = false;
+	}
+	else
+	{
+		pcontext->value = (Datum) 0;
+		pcontext->isnull = true;
+	}
+}
+
+/*
+ * Returns estimated size of windowobject partition context
+ */
+static int
+estimate_partition_context_size(Datum value,
+								bool isnull,
+								int16 typlen,
+								int16 minsize,
+								int *realsize)
+{
+	if(typlen != -1)
+	{
+		if (typlen < sizeof(Datum))
+		{
+			*realsize = offsetof(proxy_context, data);
+
+			return *realsize;
+		}
+
+		if (typlen > 1024)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("size of value is greather than limit (1024 bytes)")));
+
+		*realsize = offsetof(proxy_context, data) + typlen;
+
+		return *realsize;
+	}
+	else
+	{
+		if (!isnull)
+		{
+			int		size = VARSIZE_ANY_EXHDR(DatumGetPointer(value));
+
+			if (size > 1024)
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						 errmsg("size of value is greather than limit (1024 bytes)")));
+
+			*realsize = size;
+
+			size += size / 3;
+
+			return offsetof(proxy_context, data)
+					+ MAXALIGN(size > minsize ? size : minsize);
+		}
+		else
+		{
+			/* by default we allocate 30 bytes */
+			*realsize = 0;
+
+			return offsetof(proxy_context, data) + MAXALIGN(minsize);
+		}
+	}
+}
+
+#define VARLENA_MINSIZE				32
+
+static proxy_context *
+get_partition_context(FunctionCallInfo fcinfo, bool write_mode)
+{
+	WindowObjectProxy	wop;
+	WindowObject winobj;
+	Oid			typid;
+	int16		typlen;
+	bool		typbyval;
+	Datum		value = (Datum) 0;
+	bool		isnull = true;
+	int			allocsize;
+	int			minsize;
+	int			realsize;
+	proxy_context *pcontext;
+
+	if (PG_ARGISNULL(0))
+		ereport(ERROR,
+				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+				 errmsg("windowobject is NULL")));
+
+	wop = (WindowObjectProxy) DatumGetPointer(PG_GETARG_DATUM(0));
+	winobj = wop->winobj;
+	Assert(WindowObjectIsValid(winobj));
+
+	if (PG_ARGISNULL(2))
+		minsize = VARLENA_MINSIZE;
+	else
+		minsize = PG_GETARG_INT32(2);
+
+	if (!PG_ARGISNULL(1))
+	{
+		value = PG_GETARG_DATUM(1);
+		isnull = false;
+	}
+
+	typid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+	if (!OidIsValid(typid))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot detect type of context value")));
+
+	typid = getBaseType(typid);
+	get_typlenbyval(typid, &typlen, &typbyval);
+
+	Assert(typlen != -2);
+
+	allocsize = estimate_partition_context_size(value,
+												isnull,
+												typlen,
+												minsize,
+												&realsize);
+
+	pcontext = (proxy_context *) WinGetPartitionLocalMemory(winobj, allocsize);
+
+	/* fresh pcontext has zeroed memory */
+	Assert(pcontext->magic == 0 || pcontext->magic == PROXY_CONTEXT_MAGIC);
+
+	if (pcontext->allocsize > 0)
+	{
+		if (realsize > pcontext->allocsize)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("the value cannot be saved to allocated buffer"),
+					 errhint("Try to increase the minsize argument.")));
+
+		if (pcontext->typid != typid)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("partition context was initialized for different type")));
+
+		if (write_mode)
+			copy_datum_to_partition_context(pcontext, value, isnull);
+
+	}
+	else
+	{
+		pcontext->magic = PROXY_CONTEXT_MAGIC;
+		pcontext->typid = typid;
+		pcontext->typlen = typlen;
+		pcontext->typbyval = typbyval;
+		pcontext->allocsize = allocsize;
+
+		copy_datum_to_partition_context(pcontext, value, isnull);
+	}
+
+	return pcontext;
+}
+
+Datum
+windowobject_set_partition_context_value(PG_FUNCTION_ARGS)
+{
+	(void) get_partition_context(fcinfo, true);
+
+	PG_RETURN_VOID();
+}
+
+Datum
+windowobject_get_partition_context_value(PG_FUNCTION_ARGS)
+{
+	proxy_context *pcontext;
+
+	pcontext = get_partition_context(fcinfo, false);
+
+	if (pcontext->isnull)
+		PG_RETURN_NULL();
+
+	PG_RETURN_DATUM(pcontext->value);
+}
diff --git a/src/include/catalog/pg_cast.dat b/src/include/catalog/pg_cast.dat
index d6ca624add..6ad6bb2f5e 100644
--- a/src/include/catalog/pg_cast.dat
+++ b/src/include/catalog/pg_cast.dat
@@ -543,4 +543,54 @@
   castcontext => 'e', castmethod => 'f' },
 { castsource => 'tstzrange', casttarget => 'tstzmultirange', castfunc => 'tstzmultirange(tstzrange)',
   castcontext => 'e', castmethod => 'f' },
+
+# Allow explicit coercions between typedvalue and other types
+{ castsource => 'typedvalue', casttarget => 'numeric', castfunc => 'to_numeric(typedvalue)',
+  castcontext => 'e', castmethod => 'f' },
+{ castsource => 'typedvalue', casttarget => 'int8', castfunc => 'to_int8(typedvalue)',
+  castcontext => 'e', castmethod => 'f' },
+{ castsource => 'typedvalue', casttarget => 'int4', castfunc => 'to_int4(typedvalue)',
+  castcontext => 'e', castmethod => 'f' },
+{ castsource => 'typedvalue', casttarget => 'float8', castfunc => 'to_float8(typedvalue)',
+  castcontext => 'e', castmethod => 'f' },
+{ castsource => 'typedvalue', casttarget => 'float4', castfunc => 'to_float4(typedvalue)',
+  castcontext => 'e', castmethod => 'f' },
+{ castsource => 'typedvalue', casttarget => 'date', castfunc => 'to_date(typedvalue)',
+  castcontext => 'e', castmethod => 'f' },
+{ castsource => 'typedvalue', casttarget => 'timestamp', castfunc => 'to_timestamp(typedvalue)',
+  castcontext => 'e', castmethod => 'f' },
+{ castsource => 'typedvalue', casttarget => 'timestamptz', castfunc => 'to_timestamptz(typedvalue)',
+  castcontext => 'e', castmethod => 'f' },
+{ castsource => 'typedvalue', casttarget => 'interval', castfunc => 'to_interval(typedvalue)',
+  castcontext => 'e', castmethod => 'f' },
+{ castsource => 'typedvalue', casttarget => 'text', castfunc => 'to_text(typedvalue)',
+  castcontext => 'e', castmethod => 'f' },
+{ castsource => 'typedvalue', casttarget => 'varchar', castfunc => 'to_varchar(typedvalue)',
+  castcontext => 'e', castmethod => 'f' },
+{ castsource => 'typedvalue', casttarget => 'bool', castfunc => 'to_bool(typedvalue)',
+  castcontext => 'e', castmethod => 'f' },
+{ castsource => 'numeric', casttarget => 'typedvalue', castfunc => 'to_typedvalue(numeric)',
+  castcontext => 'e', castmethod => 'f' },
+{ castsource => 'int8', casttarget => 'typedvalue', castfunc => 'to_typedvalue(int8)',
+  castcontext => 'e', castmethod => 'f' },
+{ castsource => 'int4', casttarget => 'typedvalue', castfunc => 'to_typedvalue(int4)',
+  castcontext => 'e', castmethod => 'f' },
+{ castsource => 'float8', casttarget => 'typedvalue', castfunc => 'to_typedvalue(float8)',
+  castcontext => 'e', castmethod => 'f' },
+{ castsource => 'float4', casttarget => 'typedvalue', castfunc => 'to_typedvalue(float4)',
+  castcontext => 'e', castmethod => 'f' },
+{ castsource => 'date', casttarget => 'typedvalue', castfunc => 'to_typedvalue(date)',
+  castcontext => 'e', castmethod => 'f' },
+{ castsource => 'timestamp', casttarget => 'typedvalue', castfunc => 'to_typedvalue(timestamp)',
+  castcontext => 'e', castmethod => 'f' },
+{ castsource => 'timestamptz', casttarget => 'typedvalue', castfunc => 'to_typedvalue(timestamptz)',
+  castcontext => 'e', castmethod => 'f' },
+{ castsource => 'interval', casttarget => 'typedvalue', castfunc => 'to_typedvalue(interval)',
+  castcontext => 'e', castmethod => 'f' },
+{ castsource => 'text', casttarget => 'typedvalue', castfunc => 'to_typedvalue(text)',
+  castcontext => 'e', castmethod => 'f' },
+{ castsource => 'varchar', casttarget => 'typedvalue', castfunc => 'to_typedvalue(varchar)',
+  castcontext => 'e', castmethod => 'f' },
+{ castsource => 'bool', casttarget => 'typedvalue', castfunc => 'to_typedvalue(bool)',
+  castcontext => 'e', castmethod => 'f' },
 ]
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 139f4a08bd..0e4ae78310 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -7165,6 +7165,18 @@
 { oid => '2305', descr => 'I/O',
   proname => 'internal_out', prorettype => 'cstring', proargtypes => 'internal',
   prosrc => 'internal_out' },
+{ oid => '9554', descr => 'I/O',
+  proname => 'windowobjectproxy_in', proisstrict => 'f', prorettype => 'windowobjectproxy',
+  proargtypes => 'cstring', prosrc => 'windowobjectproxy_in' },
+{ oid => '9555', descr => 'I/O',
+  proname => 'windowobjectproxy_out', prorettype => 'cstring', proargtypes => 'windowobjectproxy',
+  prosrc => 'windowobjectproxy_out' },
+{ oid => '9556', descr => 'I/O',
+  proname => 'typedvalue_in', proisstrict => 'f', prorettype => 'typedvalue',
+  proargtypes => 'cstring', prosrc => 'typedvalue_in' },
+{ oid => '9557', descr => 'I/O',
+  proname => 'typedvalue_out', prorettype => 'cstring', proargtypes => 'typedvalue',
+  prosrc => 'typedvalue_out' },
 { oid => '2312', descr => 'I/O',
   proname => 'anyelement_in', prorettype => 'anyelement',
   proargtypes => 'cstring', prosrc => 'anyelement_in' },
@@ -7262,6 +7274,80 @@
   prorettype => 'cstring', proargtypes => 'anycompatiblemultirange',
   prosrc => 'anycompatiblemultirange_out' },
 
+# typedvalue cast functions
+{ oid => '9567', descr => 'format typedvalue to numeric',
+  proname => 'to_numeric', provolatile => 's', prorettype => 'numeric',
+  proargtypes => 'typedvalue', prosrc => 'typedvalue_to_numeric' },
+{ oid => '9568', descr => 'format typedvalue to int8',
+  proname => 'to_int8', provolatile => 's', prorettype => 'int8',
+  proargtypes => 'typedvalue', prosrc => 'typedvalue_to_int8' },
+{ oid => '9569', descr => 'format typedvalue to int4',
+  proname => 'to_int4', provolatile => 's', prorettype => 'int4',
+  proargtypes => 'typedvalue', prosrc => 'typedvalue_to_int4' },
+{ oid => '9570', descr => 'format typedvalue to float8',
+  proname => 'to_float8', provolatile => 's', prorettype => 'float8',
+  proargtypes => 'typedvalue', prosrc => 'typedvalue_to_float8' },
+{ oid => '9571', descr => 'format typedvalue to float4',
+  proname => 'to_float4', provolatile => 's', prorettype => 'float4',
+  proargtypes => 'typedvalue', prosrc => 'typedvalue_to_float4' },
+{ oid => '9572', descr => 'format typedvalue to date',
+  proname => 'to_date', provolatile => 's', prorettype => 'date',
+  proargtypes => 'typedvalue', prosrc => 'typedvalue_to_date' },
+{ oid => '9573', descr => 'format typedvalue to timestamp',
+  proname => 'to_timestamp', provolatile => 's', prorettype => 'timestamp',
+  proargtypes => 'typedvalue', prosrc => 'typedvalue_to_timestamp' },
+{ oid => '9574', descr => 'format typedvalue to timestamptz',
+  proname => 'to_timestamptz', provolatile => 's', prorettype => 'timestamptz',
+  proargtypes => 'typedvalue', prosrc => 'typedvalue_to_timestamptz' },
+{ oid => '9575', descr => 'format typedvalue to interval',
+  proname => 'to_interval', provolatile => 's', prorettype => 'interval',
+  proargtypes => 'typedvalue', prosrc => 'typedvalue_to_interval' },
+{ oid => '9576', descr => 'format typedvalue to text',
+  proname => 'to_text', provolatile => 's', prorettype => 'text',
+  proargtypes => 'typedvalue', prosrc => 'typedvalue_to_text' },
+{ oid => '9577', descr => 'format typedvalue to varchar',
+  proname => 'to_varchar', provolatile => 's', prorettype => 'varchar',
+  proargtypes => 'typedvalue', prosrc => 'typedvalue_to_bpchar' },
+{ oid => '9578', descr => 'format numeric to typedvalue',
+  proname => 'to_typedvalue', provolatile => 's', prorettype => 'typedvalue',
+  proargtypes => 'numeric', prosrc => 'numeric_to_typedvalue' },
+{ oid => '9579', descr => 'format int8 to typedvalue',
+  proname => 'to_typedvalue', provolatile => 's', prorettype => 'typedvalue',
+  proargtypes => 'int8', prosrc => 'int8_to_typedvalue' },
+{ oid => '9580', descr => 'format int4 to typedvalue',
+  proname => 'to_typedvalue', provolatile => 's', prorettype => 'typedvalue',
+  proargtypes => 'int4', prosrc => 'int4_to_typedvalue' },
+{ oid => '9581', descr => 'format float8 to typedvalue',
+  proname => 'to_typedvalue', provolatile => 's', prorettype => 'typedvalue',
+  proargtypes => 'float8', prosrc => 'float8_to_typedvalue' },
+{ oid => '9582', descr => 'format float4 to typedvalue',
+  proname => 'to_typedvalue', provolatile => 's', prorettype => 'typedvalue',
+  proargtypes => 'float4', prosrc => 'float4_to_typedvalue' },
+{ oid => '9583', descr => 'format date to typedvalue',
+  proname => 'to_typedvalue', provolatile => 's', prorettype => 'typedvalue',
+  proargtypes => 'date', prosrc => 'date_to_typedvalue' },
+{ oid => '9584', descr => 'format timestamp to typedvalue',
+  proname => 'to_typedvalue', provolatile => 's', prorettype => 'typedvalue',
+  proargtypes => 'timestamp', prosrc => 'timestamp_to_typedvalue' },
+{ oid => '9585', descr => 'format timestamptz to typedvalue',
+  proname => 'to_typedvalue', provolatile => 's', prorettype => 'typedvalue',
+  proargtypes => 'timestamptz', prosrc => 'timestamptz_to_typedvalue' },
+{ oid => '9586', descr => 'format interval to typedvalue',
+  proname => 'to_typedvalue', provolatile => 's', prorettype => 'typedvalue',
+  proargtypes => 'interval', prosrc => 'interval_to_typedvalue' },
+{ oid => '9587', descr => 'format text to typedvalue',
+  proname => 'to_typedvalue', provolatile => 's', prorettype => 'typedvalue',
+  proargtypes => 'text', prosrc => 'text_to_typedvalue' },
+{ oid => '9588', descr => 'format varchar to typedvalue',
+  proname => 'to_typedvalue', provolatile => 's', prorettype => 'typedvalue',
+  proargtypes => 'varchar', prosrc => 'bpchar_to_typedvalue' },
+{ oid => '9589', descr => 'format bool to typedvalue',
+  proname => 'to_typedvalue', provolatile => 's', prorettype => 'typedvalue',
+  proargtypes => 'bool', prosrc => 'bool_to_typedvalue' },
+{ oid => '9590', descr => 'format typedvalue to bool',
+  proname => 'to_bool', provolatile => 's', prorettype => 'bool',
+  proargtypes => 'typedvalue', prosrc => 'typedvalue_to_bool' },
+
 # tablesample method handlers
 { oid => '3313', descr => 'BERNOULLI tablesample method handler',
   proname => 'bernoulli', provolatile => 'v', prorettype => 'tsm_handler',
@@ -9797,6 +9883,35 @@
 { oid => '3114', descr => 'fetch the Nth row value',
   proname => 'nth_value', prokind => 'w', prorettype => 'anyelement',
   proargtypes => 'anyelement int4', prosrc => 'window_nth_value' },
+{ oid => '9558', descr => 'get current position from window object',
+  proname => 'get_current_position', prokind => 'f', prorettype => 'int8',
+  proargtypes => 'windowobjectproxy', prosrc => 'windowobject_get_current_position' },
+{ oid => '9559', descr => 'set current position in window object',
+  proname => 'set_mark_position', prokind => 'f', prorettype => 'void',
+  proargtypes => 'windowobjectproxy int8', prosrc => 'windowobject_set_mark_position' },
+{ oid => '9560', descr => 'get partition row count',
+  proname => 'get_partition_rowcount', prokind => 'f', prorettype => 'int8',
+  proargtypes => 'windowobjectproxy', prosrc => 'windowobject_get_partition_rowcount' },
+{ oid => '9561', descr => 'returns true if two positions are peers',
+  proname => 'rows_are_peers', prokind => 'f', prorettype => 'bool',
+  proargtypes => 'windowobjectproxy int8 int8', prosrc => 'windowobject_rows_are_peers' },
+{ oid => '9562', descr => 'returns argument of window function against to partition',
+  proname => 'get_input_value_in_partition', prokind => 'f', prorettype => 'typedvalue',
+  proargtypes => 'windowobjectproxy int4 int4 text bool', prosrc => 'windowobject_get_func_arg_partition' },
+{ oid => '9563', descr => 'returns argument of window function against to frame',
+  proname => 'get_input_value_in_frame', prokind => 'f', prorettype => 'typedvalue',
+  proargtypes => 'windowobjectproxy int4 int4 text bool', prosrc => 'windowobject_get_func_arg_frame' },
+{ oid => '9564', descr => 'returns argument of window function against to current row',
+  proname => 'get_input_value_for_row', prokind => 'f', prorettype => 'typedvalue',
+  proargtypes => 'windowobjectproxy int4', prosrc => 'windowobject_get_func_arg_current' },
+{ oid => '9565', descr => 'returns a value stored in a partition context',
+  proname => 'get_partition_context_value', prokind => 'f', prorettype => 'anyelement',
+  proargtypes => 'windowobjectproxy anyelement int4',
+  prosrc => 'windowobject_get_partition_context_value', proisstrict => 'f' },
+{ oid => '9566', descr => 'store a value to partition context',
+  proname => 'set_partition_context_value', prokind => 'f', prorettype => 'void',
+  proargtypes => 'windowobjectproxy anyelement int4',
+  prosrc => 'windowobject_set_partition_context_value', proisstrict => 'f' },
 
 # functions for range types
 { oid => '3832', descr => 'I/O',
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index 62018f063a..da4635950d 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -591,6 +591,17 @@
   typtype => 'p', typcategory => 'P', typinput => 'internal_in',
   typoutput => 'internal_out', typreceive => '-', typsend => '-',
   typalign => 'ALIGNOF_POINTER' },
+{ oid => '9552',
+  descr => 'pseudo-type representing an pointer to WindowObjectProxy structure',
+  typname => 'windowobjectproxy', typlen => '-1', typbyval => 'f',
+  typtype => 'p', typcategory => 'P', typinput => 'windowobjectproxy_in',
+  typoutput => 'windowobjectproxy_out', typreceive => '-', typsend => '-',
+  typalign => 'i', typstorage => 'p' },
+{ oid => '9553',
+  descr => 'type that can hold any scalar value with necessary meta',
+  typname => 'typedvalue', typtype => 'b', typlen => '-1', typbyval => 'f', typcategory => 'X',
+  typispreferred => 'f', typinput => 'typedvalue_in', typoutput => 'typedvalue_out',
+  typreceive => '-', typsend => '-', typalign => 'i', typstorage => 'x' },
 { oid => '2283', descr => 'pseudo-type representing a polymorphic base type',
   typname => 'anyelement', typlen => '4', typbyval => 't', typtype => 'p',
   typcategory => 'P', typinput => 'anyelement_in',
diff --git a/src/include/utils/typedvalue.h b/src/include/utils/typedvalue.h
new file mode 100644
index 0000000000..d3827b54a8
--- /dev/null
+++ b/src/include/utils/typedvalue.h
@@ -0,0 +1,28 @@
+/*-------------------------------------------------------------------------
+ *
+ * typedvalue.h
+ *	  Declarations for typedvalue data type support.
+ *
+ * Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ *
+ * src/include/utils/typedvalue.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef __TYPEDVALUE_H__
+#define __TYPEDVALUE_H__
+
+typedef struct
+{
+	int32		vl_len;			/* varlena header */
+	Oid			typid;
+	bool		typbyval;
+	int16		typlen;
+	char		data[FLEXIBLE_ARRAY_MEMBER];
+} TypedValueData;
+
+typedef TypedValueData *TypedValue;
+
+extern Datum makeTypedValue(Datum value, Oid typid, int16 typlen, bool typbyval);
+
+#endif
diff --git a/src/include/windowapi.h b/src/include/windowapi.h
index e8c9fc54d8..a4b8504f78 100644
--- a/src/include/windowapi.h
+++ b/src/include/windowapi.h
@@ -36,6 +36,34 @@
 /* this struct is private in nodeWindowAgg.c */
 typedef struct WindowObjectData *WindowObject;
 
+typedef struct WindowObjectProxyMutable
+{
+	/* true when request on winfuncarg doesn't return data */
+	bool		isout;
+
+	/* cache for type related data of Window arguments */
+	int			last_argno;
+	Oid			typid;
+	int16		typlen;
+	bool		typbyval;
+} WindowObjectProxyMutable;
+
+/*
+ * This type is used as proxy between PL variants of WinFuncArg
+ * functions and PL environment. The variables of windowobjectproxy
+ * type can be copied, so mutable content should be elsewhere.
+ */
+typedef struct WindowObjectProxyData
+{
+	int32		vl_len;			/* varlena header */
+
+	WindowObject winobj;
+	FunctionCallInfo	fcinfo;
+	WindowObjectProxyMutable *mutable_data;
+} WindowObjectProxyData;
+
+typedef WindowObjectProxyData *WindowObjectProxy;
+
 #define PG_WINDOW_OBJECT() ((WindowObject) fcinfo->context)
 
 #define WindowObjectIsValid(winobj) \
diff --git a/src/pl/plpgsql/src/Makefile b/src/pl/plpgsql/src/Makefile
index 193df8a010..ba6edfd42e 100644
--- a/src/pl/plpgsql/src/Makefile
+++ b/src/pl/plpgsql/src/Makefile
@@ -34,7 +34,7 @@ REGRESS_OPTS = --dbname=$(PL_TESTDB)
 
 REGRESS = plpgsql_call plpgsql_control plpgsql_copy plpgsql_domain \
 	plpgsql_record plpgsql_cache plpgsql_simple plpgsql_transaction \
-	plpgsql_trap plpgsql_trigger plpgsql_varprops
+	plpgsql_trap plpgsql_trigger plpgsql_varprops plpgsql_window
 
 # where to find gen_keywordlist.pl and subsidiary files
 TOOLSDIR = $(top_srcdir)/src/tools
diff --git a/src/pl/plpgsql/src/expected/plpgsql_window.out b/src/pl/plpgsql/src/expected/plpgsql_window.out
new file mode 100644
index 0000000000..b9cb015ecc
--- /dev/null
+++ b/src/pl/plpgsql/src/expected/plpgsql_window.out
@@ -0,0 +1,228 @@
+create or replace function pl_row_number()
+returns bigint as $$
+declare pos int8;
+begin
+  pos := get_current_position(windowobject);
+  pos := pos + 1;
+  perform set_mark_position(windowobject, pos);
+  return pos;
+end
+$$
+language plpgsql window;
+select pl_row_number() over (), v from (values(10),(20),(30)) v(v);
+ pl_row_number | v  
+---------------+----
+             1 | 10
+             2 | 20
+             3 | 30
+(3 rows)
+
+create or replace function pl_round_value(numeric)
+returns int as $$
+declare
+  num numeric;
+begin
+  num := get_input_value_for_row(windowobject, 1);
+  return round(num);
+end
+$$ language plpgsql window;
+select pl_round_value(v) over(order by v desc) from generate_series(0.1, 1.0, 0.1) g(v);
+ pl_round_value 
+----------------
+              1
+              1
+              1
+              1
+              1
+              1
+              0
+              0
+              0
+              0
+(10 rows)
+
+select pl_round_value(v + 1) over(order by v desc) from generate_series(0.1, 1.0, 0.1) g(v);
+ pl_round_value 
+----------------
+              2
+              2
+              2
+              2
+              2
+              2
+              1
+              1
+              1
+              1
+(10 rows)
+
+create table test_table(v numeric);
+insert into test_table values(1),(3),(6),(6),(8),(7),(6),(5),(4);
+create or replace function pl_lag(numeric)
+returns numeric as $$
+declare
+  v numeric;
+begin
+  v := get_input_value_in_partition(windowobject, 1, -1, 'seek_current', false);
+  return v;
+end;
+$$ language plpgsql window;
+select pl_lag(v) over (), lag(v) over () from test_table;
+ pl_lag | lag 
+--------+-----
+        |    
+      1 |   1
+      3 |   3
+      6 |   6
+      6 |   6
+      8 |   8
+      7 |   7
+      6 |   6
+      5 |   5
+(9 rows)
+
+drop table test_table;
+create table test_table(v integer);
+insert into test_table values(1),(3),(6),(6),(8),(7),(6),(5),(4);
+select pl_lag(v) over (), lag(v) over () from test_table;
+ pl_lag | lag 
+--------+-----
+        |    
+      1 |   1
+      3 |   3
+      6 |   6
+      6 |   6
+      8 |   8
+      7 |   7
+      6 |   6
+      5 |   5
+(9 rows)
+
+create or replace function pl_moving_avg(numeric)
+returns numeric as $$
+declare
+  s numeric default 0.0;
+  v numeric;
+  c numeric default 0.0;
+begin
+  -- look before
+  v := get_input_value_in_partition(windowobject, 1, -1, 'seek_current', false);
+  if v is not null then
+    s := s + v;
+    c := c + 1.0;
+  end if;
+
+  -- look after
+  v := get_input_value_in_partition(windowobject, 1, 0, 'seek_current', false);
+  if v is not null then
+    s := s + v;
+    c := c + 1.0;
+  end if;
+
+  v := get_input_value_in_partition(windowobject, 1, 1, 'seek_current', false);
+  if v is not null then
+    s := s + v;
+    c := c + 1.0;
+  end if;
+
+  return trim_scale(s / c);
+end
+$$ language plpgsql window;
+select pl_moving_avg(v) over (), v from test_table;
+   pl_moving_avg    | v 
+--------------------+---
+                  2 | 1
+ 3.3333333333333333 | 3
+                  5 | 6
+ 6.6666666666666667 | 6
+                  7 | 8
+                  7 | 7
+                  6 | 6
+                  5 | 5
+                4.5 | 4
+(9 rows)
+
+create or replace function pl_lag_polymorphic(anyelement)
+returns anyelement as $$
+declare
+  v $0%type;
+begin
+  v := get_input_value_in_partition(windowobject, 1, -1, 'seek_current', false);
+  return v;
+end;
+$$ language plpgsql window;
+select pl_lag_polymorphic(v) over (), lag(v) over () from test_table;
+ pl_lag_polymorphic | lag 
+--------------------+-----
+                    |    
+                  1 |   1
+                  3 |   3
+                  6 |   6
+                  6 |   6
+                  8 |   8
+                  7 |   7
+                  6 |   6
+                  5 |   5
+(9 rows)
+
+create or replace function pl_pcontext_test(numeric)
+returns numeric as $$
+declare
+  n numeric;
+  v numeric;
+begin
+  n := get_partition_context_value(windowobject, null::numeric);
+  v := get_input_value_for_row(windowobject, 1);
+  perform set_partition_context_value(windowobject, v);
+
+  return n;
+end
+$$
+language plpgsql window;
+select v, pl_pcontext_test(v) over () from generate_series(0.1, 1.0, 0.1) g(v);
+  v  | pl_pcontext_test 
+-----+------------------
+ 0.1 |                 
+ 0.2 |              0.1
+ 0.3 |              0.2
+ 0.4 |              0.3
+ 0.5 |              0.4
+ 0.6 |              0.5
+ 0.7 |              0.6
+ 0.8 |              0.7
+ 0.9 |              0.8
+ 1.0 |              0.9
+(10 rows)
+
+create table test_missing_values(id int, v integer);
+insert into test_missing_values values(1,10),(2,11),(3,12),(4,null),(5,null),(6,15),(7,16);
+create or replace function pl_pcontext_test(numeric)
+returns numeric as $$
+declare
+  n numeric;
+  v numeric;
+begin
+  v := get_input_value_for_row(windowobject, 1);
+
+  if v is null then
+    v := get_partition_context_value(windowobject, null::numeric);
+  else
+    perform set_partition_context_value(windowobject, v);
+  end if;
+
+  return v;
+end
+$$
+language plpgsql window;
+select id, v, pl_pcontext_test(v) over (order by id) from test_missing_values;
+ id | v  | pl_pcontext_test 
+----+----+------------------
+  1 | 10 |               10
+  2 | 11 |               11
+  3 | 12 |               12
+  4 |    |               12
+  5 |    |               12
+  6 | 15 |               15
+  7 | 16 |               16
+(7 rows)
+
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 042deb2a96..78854d8370 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -586,6 +586,41 @@ do_compile(FunctionCallInfo fcinfo,
 											  true);
 			}
 
+			if (function->fn_prokind == PROKIND_WINDOW)
+			{
+				PLpgSQL_type   *dtype;
+				PLpgSQL_var	   *var;
+
+				/*
+				 * Add the promise variable windowobject with windowobjectproxy type
+				 *
+				 * Pseudotypes are disallowed for custom variables. It is checked
+				 * in plpgsql_build_variable, so instead call this function, build
+				 * promise variable here.
+				 */
+
+				dtype = plpgsql_build_datatype(WINDOWOBJECTPROXYOID,
+											   -1,
+											   function->fn_input_collation,
+											   NULL);
+
+				/* this should be pseudotype */
+				Assert(dtype->ttype == PLPGSQL_TTYPE_PSEUDO);
+
+				var = palloc0(sizeof(PLpgSQL_var));
+
+				var->dtype = PLPGSQL_DTYPE_PROMISE;
+				var->promise = PLPGSQL_PROMISE_WINDOWOBJECT;
+
+				var->refname = pstrdup("windowobject");
+				var->datatype = dtype;
+
+				plpgsql_adddatum((PLpgSQL_datum *) var);
+				plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR,
+								   var->dno,
+								   var->refname);
+			}
+
 			ReleaseSysCache(typeTup);
 			break;
 
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index b9e85a1a0f..ee3a1bbebf 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -593,6 +593,39 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo,
 	 */
 	exec_set_found(&estate, false);
 
+	/*
+	 * Initialize promise winobject
+	 */
+	if (func->fn_prokind == PROKIND_WINDOW)
+	{
+		/* fcinfo is available in this function too */
+		WindowObjectProxy wop;
+		WindowObjectProxyMutable *mutable_data;
+		MemoryContext oldcontext;
+
+		oldcontext = MemoryContextSwitchTo(estate.datum_context);
+
+		wop = palloc(sizeof(WindowObjectProxyData));
+		SET_VARSIZE(wop, sizeof(WindowObjectProxyData));
+
+		wop->winobj = PG_WINDOW_OBJECT();
+
+		Assert(WindowObjectIsValid(wop->winobj));
+
+		mutable_data = palloc0(sizeof(WindowObjectProxyMutable));
+		mutable_data->isout = false;
+		mutable_data->last_argno = -1;
+
+		wop->mutable_data = mutable_data;
+		wop->fcinfo = fcinfo;
+
+		MemoryContextSwitchTo(oldcontext);
+
+		estate.winobjproxy = wop;
+	}
+	else
+		estate.winobjproxy = NULL;
+
 	/*
 	 * Let the instrumentation plugin peek at this function
 	 */
@@ -915,6 +948,11 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
 	plpgsql_estate_setup(&estate, func, NULL, NULL, NULL);
 	estate.trigdata = trigdata;
 
+	/*
+	 * Trigger function cannot be WINDOW function
+	 */
+	estate.winobjproxy = NULL;
+
 	/*
 	 * Setup error traceback support for ereport()
 	 */
@@ -1293,11 +1331,13 @@ copy_plpgsql_datums(PLpgSQL_execstate *estate,
 		PLpgSQL_datum *indatum = indatums[i];
 		PLpgSQL_datum *outdatum;
 
+
 		/* This must agree with plpgsql_finish_datums on what is copiable */
 		switch (indatum->dtype)
 		{
 			case PLPGSQL_DTYPE_VAR:
 			case PLPGSQL_DTYPE_PROMISE:
+
 				outdatum = (PLpgSQL_datum *) ws_next;
 				memcpy(outdatum, indatum, sizeof(PLpgSQL_var));
 				ws_next += MAXALIGN(sizeof(PLpgSQL_var));
@@ -1486,6 +1526,17 @@ plpgsql_fulfill_promise(PLpgSQL_execstate *estate,
 			assign_text_var(estate, var, GetCommandTagName(estate->evtrigdata->tag));
 			break;
 
+		case PLPGSQL_PROMISE_WINDOWOBJECT:
+			if (!estate->winobjproxy)
+				elog(ERROR, "windowobject promise is not in a window function");
+
+			assign_simple_var(estate,
+							  var,
+							  PointerGetDatum(estate->winobjproxy),
+							  false,
+							  false);
+			break;
+
 		default:
 			elog(ERROR, "unrecognized promise type: %d", var->promise);
 	}
@@ -2524,6 +2575,17 @@ exec_stmt_getdiag(PLpgSQL_execstate *estate, PLpgSQL_stmt_getdiag *stmt)
 				}
 				break;
 
+			case PLPGSQL_GETDIAG_VALUE_IS_OUT:
+				{
+					if (!estate->winobjproxy)
+						elog(ERROR, "function is not a window function");
+
+					exec_assign_value(estate, var,
+									  BoolGetDatum(estate->winobjproxy->mutable_data->isout),
+									  false, BOOLOID, -1);
+				}
+				break;
+
 			default:
 				elog(ERROR, "unrecognized diagnostic item kind: %d",
 					 diag_item->kind);
diff --git a/src/pl/plpgsql/src/pl_funcs.c b/src/pl/plpgsql/src/pl_funcs.c
index ee60ced583..992763c735 100644
--- a/src/pl/plpgsql/src/pl_funcs.c
+++ b/src/pl/plpgsql/src/pl_funcs.c
@@ -321,6 +321,8 @@ plpgsql_getdiag_kindname(PLpgSQL_getdiag_kind kind)
 			return "CONSTRAINT_NAME";
 		case PLPGSQL_GETDIAG_DATATYPE_NAME:
 			return "PG_DATATYPE_NAME";
+		case PLPGSQL_GETDIAG_VALUE_IS_OUT:
+			return "PG_VALUE_IS_OUT";
 		case PLPGSQL_GETDIAG_MESSAGE_TEXT:
 			return "MESSAGE_TEXT";
 		case PLPGSQL_GETDIAG_TABLE_NAME:
diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y
index 8227bf0449..12e4735756 100644
--- a/src/pl/plpgsql/src/pl_gram.y
+++ b/src/pl/plpgsql/src/pl_gram.y
@@ -325,6 +325,7 @@ static	void			check_raise_parameters(PLpgSQL_stmt_raise *stmt);
 %token <keyword>	K_PG_EXCEPTION_CONTEXT
 %token <keyword>	K_PG_EXCEPTION_DETAIL
 %token <keyword>	K_PG_EXCEPTION_HINT
+%token <keyword>	K_PG_VALUE_IS_OUT
 %token <keyword>	K_PRINT_STRICT_PARAMS
 %token <keyword>	K_PRIOR
 %token <keyword>	K_QUERY
@@ -1081,6 +1082,9 @@ getdiag_item :
 						else if (tok_is_keyword(tok, &yylval,
 												K_PG_EXCEPTION_CONTEXT, "pg_exception_context"))
 							$$ = PLPGSQL_GETDIAG_ERROR_CONTEXT;
+						else if (tok_is_keyword(tok, &yylval,
+												K_PG_VALUE_IS_OUT, "pg_value_is_out"))
+							$$ = PLPGSQL_GETDIAG_VALUE_IS_OUT;
 						else if (tok_is_keyword(tok, &yylval,
 												K_COLUMN_NAME, "column_name"))
 							$$ = PLPGSQL_GETDIAG_COLUMN_NAME;
diff --git a/src/pl/plpgsql/src/pl_unreserved_kwlist.h b/src/pl/plpgsql/src/pl_unreserved_kwlist.h
index 99b3cf7d8a..d27b7dfb85 100644
--- a/src/pl/plpgsql/src/pl_unreserved_kwlist.h
+++ b/src/pl/plpgsql/src/pl_unreserved_kwlist.h
@@ -84,6 +84,7 @@ PG_KEYWORD("pg_datatype_name", K_PG_DATATYPE_NAME)
 PG_KEYWORD("pg_exception_context", K_PG_EXCEPTION_CONTEXT)
 PG_KEYWORD("pg_exception_detail", K_PG_EXCEPTION_DETAIL)
 PG_KEYWORD("pg_exception_hint", K_PG_EXCEPTION_HINT)
+PG_KEYWORD("pg_value_is_out", K_PG_VALUE_IS_OUT)
 PG_KEYWORD("print_strict_params", K_PRINT_STRICT_PARAMS)
 PG_KEYWORD("prior", K_PRIOR)
 PG_KEYWORD("query", K_QUERY)
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 0c3d30fb13..4c1ed7cbf7 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -23,6 +23,8 @@
 #include "utils/expandedrecord.h"
 #include "utils/typcache.h"
 
+#include "windowapi.h"
+
 
 /**********************************************************************
  * Definitions
@@ -84,7 +86,8 @@ typedef enum PLpgSQL_promise_type
 	PLPGSQL_PROMISE_TG_NARGS,
 	PLPGSQL_PROMISE_TG_ARGV,
 	PLPGSQL_PROMISE_TG_EVENT,
-	PLPGSQL_PROMISE_TG_TAG
+	PLPGSQL_PROMISE_TG_TAG,
+	PLPGSQL_PROMISE_WINDOWOBJECT
 } PLpgSQL_promise_type;
 
 /*
@@ -159,7 +162,8 @@ typedef enum PLpgSQL_getdiag_kind
 	PLPGSQL_GETDIAG_DATATYPE_NAME,
 	PLPGSQL_GETDIAG_MESSAGE_TEXT,
 	PLPGSQL_GETDIAG_TABLE_NAME,
-	PLPGSQL_GETDIAG_SCHEMA_NAME
+	PLPGSQL_GETDIAG_SCHEMA_NAME,
+	PLPGSQL_GETDIAG_VALUE_IS_OUT
 } PLpgSQL_getdiag_kind;
 
 /*
@@ -612,6 +616,17 @@ typedef struct PLpgSQL_stmt_getdiag
 	List	   *diag_items;		/* List of PLpgSQL_diag_item */
 } PLpgSQL_stmt_getdiag;
 
+/*
+ * GET PG_WINDOW_CONTEXT statement
+ */
+typedef struct PLpgSQL_stmt_getwincxt
+{
+	PLpgSQL_stmt_type cmd_type;
+	int			lineno;
+	unsigned int stmtid;
+	List	   *items;
+} PLpgSQL_stmt_getwincxt;
+
 /*
  * IF statement
  */
@@ -1049,6 +1064,8 @@ typedef struct PLpgSQL_execstate
 	TriggerData *trigdata;		/* if regular trigger, data about firing */
 	EventTriggerData *evtrigdata;	/* if event trigger, data about firing */
 
+	WindowObjectProxy winobjproxy;	/* for window function we need proxy
+									 * object between PL and WinFucArg funcions */
 	Datum		retval;
 	bool		retisnull;
 	Oid			rettype;		/* type of current retval */
diff --git a/src/pl/plpgsql/src/sql/plpgsql_window.sql b/src/pl/plpgsql/src/sql/plpgsql_window.sql
new file mode 100644
index 0000000000..6a3cab39a2
--- /dev/null
+++ b/src/pl/plpgsql/src/sql/plpgsql_window.sql
@@ -0,0 +1,135 @@
+create or replace function pl_row_number()
+returns bigint as $$
+declare pos int8;
+begin
+  pos := get_current_position(windowobject);
+  pos := pos + 1;
+  perform set_mark_position(windowobject, pos);
+  return pos;
+end
+$$
+language plpgsql window;
+
+select pl_row_number() over (), v from (values(10),(20),(30)) v(v);
+
+create or replace function pl_round_value(numeric)
+returns int as $$
+declare
+  num numeric;
+begin
+  num := get_input_value_for_row(windowobject, 1);
+  return round(num);
+end
+$$ language plpgsql window;
+
+select pl_round_value(v) over(order by v desc) from generate_series(0.1, 1.0, 0.1) g(v);
+
+select pl_round_value(v + 1) over(order by v desc) from generate_series(0.1, 1.0, 0.1) g(v);
+
+create table test_table(v numeric);
+insert into test_table values(1),(3),(6),(6),(8),(7),(6),(5),(4);
+
+create or replace function pl_lag(numeric)
+returns numeric as $$
+declare
+  v numeric;
+begin
+  v := get_input_value_in_partition(windowobject, 1, -1, 'seek_current', false);
+  return v;
+end;
+$$ language plpgsql window;
+
+select pl_lag(v) over (), lag(v) over () from test_table;
+
+drop table test_table;
+
+create table test_table(v integer);
+insert into test_table values(1),(3),(6),(6),(8),(7),(6),(5),(4);
+
+select pl_lag(v) over (), lag(v) over () from test_table;
+
+create or replace function pl_moving_avg(numeric)
+returns numeric as $$
+declare
+  s numeric default 0.0;
+  v numeric;
+  c numeric default 0.0;
+begin
+  -- look before
+  v := get_input_value_in_partition(windowobject, 1, -1, 'seek_current', false);
+  if v is not null then
+    s := s + v;
+    c := c + 1.0;
+  end if;
+
+  -- look after
+  v := get_input_value_in_partition(windowobject, 1, 0, 'seek_current', false);
+  if v is not null then
+    s := s + v;
+    c := c + 1.0;
+  end if;
+
+  v := get_input_value_in_partition(windowobject, 1, 1, 'seek_current', false);
+  if v is not null then
+    s := s + v;
+    c := c + 1.0;
+  end if;
+
+  return trim_scale(s / c);
+end
+$$ language plpgsql window;
+
+select pl_moving_avg(v) over (), v from test_table;
+
+create or replace function pl_lag_polymorphic(anyelement)
+returns anyelement as $$
+declare
+  v $0%type;
+begin
+  v := get_input_value_in_partition(windowobject, 1, -1, 'seek_current', false);
+  return v;
+end;
+$$ language plpgsql window;
+
+select pl_lag_polymorphic(v) over (), lag(v) over () from test_table;
+
+create or replace function pl_pcontext_test(numeric)
+returns numeric as $$
+declare
+  n numeric;
+  v numeric;
+begin
+  n := get_partition_context_value(windowobject, null::numeric);
+  v := get_input_value_for_row(windowobject, 1);
+  perform set_partition_context_value(windowobject, v);
+
+  return n;
+end
+$$
+language plpgsql window;
+
+select v, pl_pcontext_test(v) over () from generate_series(0.1, 1.0, 0.1) g(v);
+
+create table test_missing_values(id int, v integer);
+insert into test_missing_values values(1,10),(2,11),(3,12),(4,null),(5,null),(6,15),(7,16);
+
+create or replace function pl_pcontext_test(numeric)
+returns numeric as $$
+declare
+  n numeric;
+  v numeric;
+begin
+  v := get_input_value_for_row(windowobject, 1);
+
+  if v is null then
+    v := get_partition_context_value(windowobject, null::numeric);
+  else
+    perform set_partition_context_value(windowobject, v);
+  end if;
+
+  return v;
+end
+$$
+language plpgsql window;
+
+select id, v, pl_pcontext_test(v) over (order by id) from test_missing_values;
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index 0c74dc96a8..8203a7880e 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -73,7 +73,8 @@ ORDER BY p1.oid;
  3361 | pg_ndistinct
  3402 | pg_dependencies
  5017 | pg_mcv_list
-(4 rows)
+ 9553 | typedvalue
+(5 rows)
 
 -- Make sure typarray points to a "true" array type of our own base
 SELECT p1.oid, p1.typname as basetype, p2.typname as arraytype,

Reply via email to