Hi

I simplified access to results of  winfuncargs functions by proxy type
"typedvalue". This type can hold any Datum value, and allows fast cast to
basic buildin types or it can use (slower) generic cast functions. It is
used in cooperation with a plpgsql assign statement that can choose the
correct cast implicitly. When the winfuncarg function returns a value of
the same type, that is expected by the variable on the left side of the
assign statement, then (for basic types), the value is just copied without
casts. With this proxy type is not necessary to have special statement for
assigning returned value from winfuncargs functions, so source code of
window function in plpgsql looks intuitive to me.

Example - implementation of "lag" function in plpgsql

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;

I think this code is usable, and I assign this patch to commitfest.

Regards

Pavel
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index ba5a23ac25..fdc364c05e 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -1418,6 +1418,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 5d2aca8cfe..1974cfd7e6 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -101,6 +101,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 3d6b2f9093..ebb2a16572 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -334,6 +334,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..d2d5dc34c0
--- /dev/null
+++ b/src/backend/utils/adt/typedvalue.c
@@ -0,0 +1,630 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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"
+
+
+static Datum
+TypedValueGetDatum(TypedValue tv)
+{
+	if (tv->typbyval)
+		return *((Datum *) tv->data);
+	else
+		return PointerGetDatum(tv->data);
+}
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
+static Datum
+parse_value(Datum textval, Oid targettid)
+{
+	Oid			typinput,
+				typioparam;
+	char	   *str;
+	Datum		result;
+
+	getTypeInputInfo(targettid, &typinput, &typioparam);
+	str = TextDatumGetCString(textval);
+	result = OidInputFunctionCall(typinput, str,
+								   typioparam, -1);
+	pfree(str);
+	return result;
+}
+
+static Datum
+generic_cast(Datum value, Oid typid, Oid target_typid)
+{
+	return OidFunctionCall1(get_cast_oid(typid,
+										 target_typid,
+										 false),
+							value);
+}
+
+Datum
+typedvalue_to_numeric(PG_FUNCTION_ARGS)
+{
+	TypedValue		tv;
+	Datum			value;
+
+	tv = (TypedValue) PG_GETARG_POINTER(0);
+	value = TypedValueGetDatum(tv);
+
+	switch (tv->typid)
+	{
+		case INT4OID:
+			PG_RETURN_DATUM(DirectFunctionCall1(int4_numeric, value));
+
+		case INT8OID:
+			PG_RETURN_DATUM(DirectFunctionCall1(int8_numeric, value));
+
+		case NUMERICOID:
+			PG_RETURN_DATUM(value);
+
+		case FLOAT4OID:
+			PG_RETURN_DATUM(DirectFunctionCall1(float4_numeric, value));
+
+		case FLOAT8OID:
+			PG_RETURN_DATUM(DirectFunctionCall1(float8_numeric, value));
+
+		case TEXTOID:
+		case BPCHAROID:
+			PG_RETURN_DATUM(parse_value(value, NUMERICOID));
+
+		default:
+			/* slower generic cast */
+			PG_RETURN_DATUM(generic_cast(value, tv->typid, NUMERICOID));
+	}
+}
+
+Datum
+typedvalue_to_int8(PG_FUNCTION_ARGS)
+{
+	TypedValue		tv;
+	Datum			value;
+
+	tv = (TypedValue) PG_GETARG_POINTER(0);
+	value = TypedValueGetDatum(tv);
+
+	switch (tv->typid)
+	{
+		case INT4OID:
+			PG_RETURN_DATUM(DirectFunctionCall1(int48, value));
+
+		case BOOLOID:
+			{
+				int32 i = DatumGetInt32(DirectFunctionCall1(bool_int4, value));
+
+				PG_RETURN_INT64((int64) i);
+			}
+
+		case INT8OID:
+			PG_RETURN_DATUM(value);
+
+		case NUMERICOID:
+			PG_RETURN_DATUM(DirectFunctionCall1(numeric_int8, value));
+
+		case FLOAT4OID:
+			PG_RETURN_DATUM(DirectFunctionCall1(ftoi8, value));
+
+		case FLOAT8OID:
+			PG_RETURN_DATUM(DirectFunctionCall1(dtoi8, value));
+
+		case TEXTOID:
+		case BPCHAROID:
+			PG_RETURN_DATUM(parse_value(value, INT8OID));
+
+		default:
+			/* slower generic cast */
+			PG_RETURN_DATUM(generic_cast(value, tv->typid, INT8OID));
+	}
+}
+
+Datum
+typedvalue_to_int4(PG_FUNCTION_ARGS)
+{
+	TypedValue		tv;
+	Datum			value;
+
+	tv = (TypedValue) PG_GETARG_POINTER(0);
+	value = TypedValueGetDatum(tv);
+
+	switch (tv->typid)
+	{
+		case INT4OID:
+			PG_RETURN_DATUM(value);
+
+		case BOOLOID:
+			PG_RETURN_DATUM(DirectFunctionCall1(bool_int4, value));
+
+		case INT8OID:
+			PG_RETURN_DATUM(DirectFunctionCall1(int84, value));
+
+		case NUMERICOID:
+			PG_RETURN_DATUM(DirectFunctionCall1(numeric_int4, value));
+
+		case FLOAT4OID:
+			PG_RETURN_DATUM(DirectFunctionCall1(ftoi4, value));
+
+		case FLOAT8OID:
+			PG_RETURN_DATUM(DirectFunctionCall1(dtoi4, value));
+
+		case TEXTOID:
+		case BPCHAROID:
+			PG_RETURN_DATUM(parse_value(value, INT4OID));
+
+		default:
+			/* slower generic cast */
+			PG_RETURN_DATUM(generic_cast(value, tv->typid, INT4OID));
+	}
+}
+
+Datum
+typedvalue_to_float8(PG_FUNCTION_ARGS)
+{
+	TypedValue		tv;
+	Datum			value;
+
+	tv = (TypedValue) PG_GETARG_POINTER(0);
+	value = TypedValueGetDatum(tv);
+
+	switch (tv->typid)
+	{
+		case INT4OID:
+			PG_RETURN_DATUM(DirectFunctionCall1(i4tod, value));
+
+		case INT8OID:
+			PG_RETURN_DATUM(DirectFunctionCall1(i8tod, value));
+
+		case NUMERICOID:
+			PG_RETURN_DATUM(DirectFunctionCall1(numeric_float8, value));
+
+		case FLOAT4OID:
+			PG_RETURN_DATUM(DirectFunctionCall1(ftod, value));
+
+		case FLOAT8OID:
+			PG_RETURN_DATUM(value);
+
+		case TEXTOID:
+		case BPCHAROID:
+			PG_RETURN_DATUM(parse_value(value, FLOAT8OID));
+
+		default:
+			/* slower generic cast */
+			PG_RETURN_DATUM(generic_cast(value, tv->typid, FLOAT8OID));
+	}
+
+
+	PG_RETURN_NULL();
+}
+
+Datum
+typedvalue_to_float4(PG_FUNCTION_ARGS)
+{
+	TypedValue		tv;
+	Datum			value;
+
+	tv = (TypedValue) PG_GETARG_POINTER(0);
+	value = TypedValueGetDatum(tv);
+
+	switch (tv->typid)
+	{
+		case INT4OID:
+			PG_RETURN_DATUM(DirectFunctionCall1(i4tof, value));
+
+		case INT8OID:
+			PG_RETURN_DATUM(DirectFunctionCall1(i8tof, value));
+
+		case NUMERICOID:
+			PG_RETURN_DATUM(DirectFunctionCall1(numeric_float4, value));
+
+		case FLOAT4OID:
+			PG_RETURN_DATUM(value);
+
+		case FLOAT8OID:
+			PG_RETURN_DATUM(DirectFunctionCall1(dtof, value));
+
+		case TEXTOID:
+		case BPCHAROID:
+			PG_RETURN_DATUM(parse_value(value, FLOAT4OID));
+
+		default:
+			/* slower generic cast */
+			PG_RETURN_DATUM(generic_cast(value, tv->typid, FLOAT4OID));
+	}
+}
+
+Datum
+typedvalue_to_date(PG_FUNCTION_ARGS)
+{
+	TypedValue		tv;
+	Datum			value;
+
+	tv = (TypedValue) PG_GETARG_POINTER(0);
+	value = TypedValueGetDatum(tv);
+
+	switch (tv->typid)
+	{
+		case DATEOID:
+			PG_RETURN_DATUM(value);
+
+		case TIMESTAMPOID:
+			PG_RETURN_DATUM(DirectFunctionCall1(timestamp_date, value));
+
+		case TIMESTAMPTZOID:
+			PG_RETURN_DATUM(DirectFunctionCall1(timestamptz_date, value));
+
+		case TEXTOID:
+		case BPCHAROID:
+			PG_RETURN_DATUM(parse_value(value, FLOAT4OID));
+
+		default:
+			/* slower generic cast */
+			PG_RETURN_DATUM(generic_cast(value, tv->typid, DATEOID));
+	}
+}
+
+Datum
+typedvalue_to_timestamp(PG_FUNCTION_ARGS)
+{
+	TypedValue		tv;
+	Datum			value;
+
+	tv = (TypedValue) PG_GETARG_POINTER(0);
+	value = TypedValueGetDatum(tv);
+
+	switch (tv->typid)
+	{
+		case DATEOID:
+			PG_RETURN_DATUM(DirectFunctionCall1(date_timestamp, value));
+
+		case TIMESTAMPOID:
+			PG_RETURN_DATUM(value);
+
+		case TIMESTAMPTZOID:
+			PG_RETURN_DATUM(DirectFunctionCall1(timestamptz_timestamp, value));
+
+		case TEXTOID:
+		case BPCHAROID:
+			PG_RETURN_DATUM(parse_value(value, FLOAT4OID));
+
+		default:
+			/* slower generic cast */
+			PG_RETURN_DATUM(generic_cast(value, tv->typid, TIMESTAMPOID));
+	}
+}
+
+Datum
+typedvalue_to_timestamptz(PG_FUNCTION_ARGS)
+{
+	TypedValue		tv;
+	Datum			value;
+
+	tv = (TypedValue) PG_GETARG_POINTER(0);
+	value = TypedValueGetDatum(tv);
+
+	switch (tv->typid)
+	{
+		case DATEOID:
+			PG_RETURN_DATUM(DirectFunctionCall1(date_timestamptz, value));
+
+		case TIMESTAMPOID:
+			PG_RETURN_DATUM(DirectFunctionCall1(timestamp_timestamptz, value));
+
+		case TIMESTAMPTZOID:
+			PG_RETURN_DATUM(value);
+
+		case TEXTOID:
+		case BPCHAROID:
+			PG_RETURN_DATUM(parse_value(value, FLOAT4OID));
+
+		default:
+			/* slower generic cast */
+			PG_RETURN_DATUM(generic_cast(value, tv->typid, TIMESTAMPTZOID));
+	}
+}
+
+Datum
+typedvalue_to_interval(PG_FUNCTION_ARGS)
+{
+	TypedValue		tv;
+	Datum			value;
+
+	tv = (TypedValue) PG_GETARG_POINTER(0);
+	value = TypedValueGetDatum(tv);
+
+	switch (tv->typid)
+	{
+		case INTERVALOID:
+			PG_RETURN_DATUM(value);
+
+		case TEXTOID:
+		case BPCHAROID:
+			PG_RETURN_DATUM(parse_value(value, BOOLOID));
+
+		default:
+			/* slower generic cast */
+			PG_RETURN_DATUM(generic_cast(value, tv->typid, INTERVALOID));
+	}
+}
+
+Datum
+typedvalue_to_text(PG_FUNCTION_ARGS)
+{
+	Oid			typOutput;
+	bool		isVarlena;
+	char	   *str;
+	TypedValue	tv;
+	Datum		value;
+	text	   *txt;
+
+	tv = (TypedValue) PG_GETARG_POINTER(0);
+
+	value = TypedValueGetDatum(tv);
+
+	if (tv->typid == TEXTOID || tv->typid == BPCHAROID)
+	{
+		PG_RETURN_DATUM(datumCopy(value,
+								  tv->typbyval,
+								  tv->typlen));
+	}
+
+	getTypeOutputInfo(tv->typid, &typOutput, &isVarlena);
+
+	value = TypedValueGetDatum(tv);
+	str = OidOutputFunctionCall(typOutput, value);
+	txt = cstring_to_text(str);
+
+	PG_RETURN_TEXT_P(txt);
+}
+
+Datum
+typedvalue_to_bpchar(PG_FUNCTION_ARGS)
+{
+	return typedvalue_to_text(fcinfo);
+}
+
+Datum
+typedvalue_to_bool(PG_FUNCTION_ARGS)
+{
+	TypedValue		tv;
+	Datum			value;
+
+	tv = (TypedValue) PG_GETARG_POINTER(0);
+	value = TypedValueGetDatum(tv);
+
+	switch (tv->typid)
+	{
+		case BOOLOID:
+			PG_RETURN_DATUM(value);
+
+		case INT4OID:
+			PG_RETURN_DATUM(DirectFunctionCall1(int4_bool, value));
+
+		case INT8OID:
+			{
+				int64 i = DatumGetInt64(value);
+
+				PG_RETURN_DATUM(DirectFunctionCall1(int4_bool, Int32GetDatum((int32) i)));
+			}
+
+		case TEXTOID:
+		case BPCHAROID:
+			PG_RETURN_DATUM(parse_value(value, BOOLOID));
+
+		default:
+			/* slower generic cast */
+			PG_RETURN_DATUM(generic_cast(value, tv->typid, BOOLOID));
+	}
+}
+
+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 5a58f50fbb..597de4daa9 100644
--- a/src/include/catalog/pg_cast.dat
+++ b/src/include/catalog/pg_cast.dat
@@ -530,4 +530,56 @@
 { castsource => 'jsonb', casttarget => 'float8', castfunc => 'float8(jsonb)',
   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 27989971db..213d7c5440 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -7130,6 +7130,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' },
@@ -7219,6 +7231,80 @@
   prorettype => 'cstring', proargtypes => 'anycompatiblerange',
   prosrc => 'anycompatiblerange_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',
@@ -9742,6 +9828,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 b2cec07416..5ad4ddfd59 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -560,6 +560,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..ce573b0374
--- /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
\ No newline at end of file
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 e7f4a5f291..ee4bbb560e 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -582,6 +582,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 d4a3d58daa..e2af053fda 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);
 	}
@@ -2475,6 +2526,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 5a7e1a4444..823bbd288b 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;

Reply via email to