From 59ec0017594957f42a632d7fe407a8314a00da65 Mon Sep 17 00:00:00 2001
From: erthalion <9erthalion6@gmail.com>
Date: Sun, 29 Oct 2017 22:29:32 +0100
Subject: [PATCH 4/4] Subscripting documentation
---
doc/src/sgml/catalogs.sgml | 7 ++
doc/src/sgml/extend.sgml | 6 ++
doc/src/sgml/filelist.sgml | 1 +
doc/src/sgml/json.sgml | 25 ++++++
doc/src/sgml/ref/create_type.sgml | 30 ++++++-
doc/src/sgml/xsubscripting.sgml | 102 +++++++++++++++++++++++
src/tutorial/Makefile | 4 +-
src/tutorial/subscripting.c | 165 ++++++++++++++++++++++++++++++++++++++
src/tutorial/subscripting.source | 83 +++++++++++++++++++
9 files changed, 420 insertions(+), 3 deletions(-)
create mode 100644 doc/src/sgml/xsubscripting.sgml
create mode 100644 src/tutorial/subscripting.c
create mode 100644 src/tutorial/subscripting.source
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index ef60a58631..787db64be5 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -7838,6 +7838,13 @@ SCRAM-SHA-256$<iteration count>:&l
+ typsubsparse
+ regproc
+ pg_proc.oid
+ Custom subscripting function with type-specific logic, or 0 if this type doesn't support subscripting.
+
+
+ typdefaultbinpg_node_tree
diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml
index e819010875..3c2c11bf8b 100644
--- a/doc/src/sgml/extend.sgml
+++ b/doc/src/sgml/extend.sgml
@@ -35,6 +35,11 @@
+ subscripting procedure (starting in )
+
+
+
+
operator classes for indexes (starting in )
@@ -314,6 +319,7 @@
&xaggr;
&xtypes;
&xoper;
+ &xsubscripting;
&xindex;
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index a72c50eadb..d05f447810 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -69,6 +69,7 @@
+
diff --git a/doc/src/sgml/json.sgml b/doc/src/sgml/json.sgml
index 05ecef2ffc..1154f07ef8 100644
--- a/doc/src/sgml/json.sgml
+++ b/doc/src/sgml/json.sgml
@@ -569,4 +569,29 @@ SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc @> '{"tags": ["qu
compared using the default database collation.
+
+
+ jsonb> Subscripting
+
+ jsonb> data type supports array-style subscripting expressions to extract or update particular element. An example of subscripting syntax:
+
+-- Extract value by key
+SELECT ('{"a": 1}'::jsonb)['a'];
+
+-- Extract nested value by key path
+SELECT ('{"a": {"b": {"c": 1}}}'::jsonb)['a']['b']['c'];
+
+-- Extract element by index
+SELECT ('[1, "2", null]'::jsonb)['1'];
+
+-- Update value by key
+UPDATE table_name set jsonb_field['key'] = 1;
+
+-- Select records using where clause with subscripting
+SELECT * from table_name where jsonb_field['key'] = '"value"';
+
+
+
+
+
diff --git a/doc/src/sgml/ref/create_type.sgml b/doc/src/sgml/ref/create_type.sgml
index 1b409ad22f..ce22a9a9dd 100644
--- a/doc/src/sgml/ref/create_type.sgml
+++ b/doc/src/sgml/ref/create_type.sgml
@@ -54,6 +54,7 @@ CREATE TYPE name (
[ , ELEMENT = element ]
[ , DELIMITER = delimiter ]
[ , COLLATABLE = collatable ]
+ [ , SUBSCRIPTING = subscripting_function ]
)
CREATE TYPE name
@@ -194,7 +195,8 @@ CREATE TYPE namesend_function,
type_modifier_input_function,
type_modifier_output_function and
- analyze_function
+ analyze_function,
+ subscripting_function
are optional. Generally these functions have to be coded in C
or another low-level language.
@@ -451,6 +453,22 @@ CREATE TYPE name
make use of the collation information; this does not happen
automatically merely by marking the type collatable.
+
+
+ The optional
+ subscripting_function
+ contains type-specific logic for subscripting of the data type.
+ By default, there is no such function, which means that the data
+ type doesn't support subscripting. The subscripting function must be
+ declared to take a single argument of type internal>, and return
+ a internal> result. There are two examples of implementation for
+ subscripting function in case of array
+ (array_subscripting)
+ and jsonb
+ (jsonb_subscripting)
+ types in src/backend/utils/adt/arrayfuncs.c> and
+ src/backend/utils/adt/jsonfuncs.c> corresponding.
+
@@ -766,6 +784,16 @@ CREATE TYPE name
+
+
+ subscripting_function
+
+
+ The name of a function that contains type-specific subscripting logic for
+ the data type.
+
+
+
diff --git a/doc/src/sgml/xsubscripting.sgml b/doc/src/sgml/xsubscripting.sgml
new file mode 100644
index 0000000000..b5e909b106
--- /dev/null
+++ b/doc/src/sgml/xsubscripting.sgml
@@ -0,0 +1,102 @@
+
+
+
+ User-defined subscripting procedure
+
+
+ custom subscripting
+
+
+ When you define a new base type, you can also specify a custom procedure
+ to handle subscripting expressions. It should contain logic for verification
+ and decide which function must be used for evaluation of this expression.
+ For instance:
+
+containerSource;
+ ExprEvalStep *step = (ExprEvalStep *) PG_GETARG_POINTER(1);
+
+ // Some assign logic based on sbsdata
+}
+
+Datum
+custom_subscripting_assign(PG_FUNCTION_ARGS)
+{
+ Custom *containerSource = (Custom *) PG_GETARG_DATUM(0);
+ ExprEvalStep *step = (ExprEvalStep *) PG_GETARG_POINTER(1);
+
+ // Some extraction logic based on sbsdata
+}
+
+PG_FUNCTION_INFO_V1(custom_subscripting);
+
+Datum
+custom_subscript_parse(PG_FUNCTION_ARGS)
+{
+ bool isAssignment = PG_GETARG_BOOL(0);
+ SubscriptingRef *sbsref = (SubscriptingRef *) PG_GETARG_POINTER(0);
+ Datum assign_proc = CStringGetTextDatum("custom_subscripting_assign");
+ Datum extract_proc = CStringGetTextDatum("custom_subscripting_extract");
+
+ // Some verifications or type coersion
+
+ if (isAssignment)
+ sbsref->refevalfunc = DirectFunctionCall1(to_regproc, assign_proc);
+ else
+ sbsref->refevalfunc = DirectFunctionCall1(to_regproc, extract_proc);
+
+ PG_RETURN_POINTER(sbsref);
+}]]>
+
+
+
+ Then you can define a subscripting procedure and a custom data type:
+
+
+CREATE FUNCTION custom_subscript_parse(internal)
+ RETURNS internal
+ AS 'filename'
+ LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE custom (
+ internallength = 4,
+ input = custom_in,
+ output = custom_out,
+ subscripting = custom_subscript_parse
+);
+
+
+
+ and use it as usual:
+
+
+CREATE TABLE test_subscripting (
+ data custom
+);
+
+INSERT INTO test_subscripting VALUES ('(1, 2)');
+
+SELECT data[0] from test_subscripting;
+
+UPDATE test_subscripting SET data[1] = 3;
+
+
+
+
+ The examples of custom subscripting implementation can be found in
+ subscripting.sql and subscripting.c
+ in the src/tutorial> directory of the source distribution.
+ See the README> file in that directory for instructions
+ about running the examples.
+
+
+
diff --git a/src/tutorial/Makefile b/src/tutorial/Makefile
index 16dc390f71..0ead60c2d4 100644
--- a/src/tutorial/Makefile
+++ b/src/tutorial/Makefile
@@ -13,8 +13,8 @@
#
#-------------------------------------------------------------------------
-MODULES = complex funcs
-DATA_built = advanced.sql basics.sql complex.sql funcs.sql syscat.sql
+MODULES = complex funcs subscripting
+DATA_built = advanced.sql basics.sql complex.sql funcs.sql syscat.sql subscripting.sql
ifdef NO_PGXS
subdir = src/tutorial
diff --git a/src/tutorial/subscripting.c b/src/tutorial/subscripting.c
new file mode 100644
index 0000000000..4e4e434f25
--- /dev/null
+++ b/src/tutorial/subscripting.c
@@ -0,0 +1,165 @@
+/*
+ * src/tutorial/subscripting.c
+ *
+ ******************************************************************************
+ This file contains routines that can be bound to a Postgres backend and
+ called by the backend in the process of processing queries. The calling
+ format for these routines is dictated by Postgres architecture.
+******************************************************************************/
+
+#include "postgres.h"
+
+#include "catalog/pg_type.h"
+#include "executor/executor.h"
+#include "executor/execExpr.h"
+#include "nodes/nodeFuncs.h"
+#include "parser/parse_coerce.h"
+#include "utils/builtins.h"
+#include "utils/fmgrprotos.h"
+
+PG_MODULE_MAGIC;
+
+typedef struct Custom
+{
+ int first;
+ int second;
+} Custom;
+
+PG_FUNCTION_INFO_V1(custom_in);
+PG_FUNCTION_INFO_V1(custom_subscripting_parse);
+PG_FUNCTION_INFO_V1(custom_subscripting_assign);
+PG_FUNCTION_INFO_V1(custom_subscripting_fetch);
+
+/*****************************************************************************
+ * Input/Output functions
+ *****************************************************************************/
+
+Datum
+custom_in(PG_FUNCTION_ARGS)
+{
+ char *str = PG_GETARG_CSTRING(0);
+ int firstValue,
+ secondValue;
+ Custom *result;
+
+ if (sscanf(str, " ( %d , %d )", &firstValue, &secondValue) != 2)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("invalid input syntax for complex: \"%s\"",
+ str)));
+
+
+ result = (Custom *) palloc(sizeof(Custom));
+ result->first = firstValue;
+ result->second = secondValue;
+ PG_RETURN_POINTER(result);
+}
+
+PG_FUNCTION_INFO_V1(custom_out);
+
+Datum
+custom_out(PG_FUNCTION_ARGS)
+{
+ Custom *custom = (Custom *) PG_GETARG_POINTER(0);
+ char *result;
+
+ result = psprintf("(%d, %d)", custom->first, custom->second);
+ PG_RETURN_CSTRING(result);
+}
+
+/*****************************************************************************
+ * Custom subscripting logic functions
+ *****************************************************************************/
+
+Datum
+custom_subscripting_assign(PG_FUNCTION_ARGS)
+{
+ Custom *containerSource = (Custom *) PG_GETARG_DATUM(0);
+ ExprEvalStep *step = (ExprEvalStep *) PG_GETARG_POINTER(1);
+
+ SubscriptingRefState *sbstate = step->d.sbsref.state;
+ int index;
+
+ if (sbstate->numupper != 1)
+ ereport(ERROR, (errmsg("custom does not support nested subscripting")));
+
+ index = DatumGetInt32(sbstate->upper[0]);
+
+ if (index == 1)
+ containerSource->first = DatumGetInt32(sbstate->replacevalue);
+ else
+ containerSource->second = DatumGetInt32(sbstate->replacevalue);
+
+ PG_RETURN_POINTER(containerSource);
+}
+
+
+Datum
+custom_subscripting_fetch(PG_FUNCTION_ARGS)
+{
+ Custom *containerSource = (Custom *) PG_GETARG_DATUM(0);
+ ExprEvalStep *step = (ExprEvalStep *) PG_GETARG_POINTER(1);
+ SubscriptingRefState *sbstate = step->d.sbsref.state;
+
+ int index;
+
+ if (sbstate->numupper != 1)
+ ereport(ERROR, (errmsg("custom does not support nested subscripting")));
+
+ index = DatumGetInt32(sbstate->upper[0]);
+
+ if (index == 1)
+ PG_RETURN_INT32(containerSource->first);
+ else
+ PG_RETURN_INT32(containerSource->second);
+}
+
+Datum
+custom_subscripting_parse(PG_FUNCTION_ARGS)
+{
+ bool isAssignment = PG_GETARG_BOOL(0);
+ SubscriptingRef *sbsref = (SubscriptingRef *) PG_GETARG_POINTER(1);
+ ParseState *pstate = (ParseState *) PG_GETARG_POINTER(2);
+ List *upperIndexpr = NIL;
+ ListCell *l;
+
+ if (sbsref->reflowerindexpr != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("custom subscript does not support slices"),
+ parser_errposition(pstate, exprLocation(
+ ((Node *)lfirst(sbsref->reflowerindexpr->head))))));
+
+ foreach(l, sbsref->refupperindexpr)
+ {
+ Node *subexpr = (Node *) lfirst(l);
+
+ Assert(subexpr != NULL);
+
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("custom subscript does not support slices"),
+ parser_errposition(pstate, exprLocation(
+ ((Node *) lfirst(sbsref->refupperindexpr->head))))));
+
+ subexpr = coerce_to_target_type(pstate,
+ subexpr, exprType(subexpr),
+ INT4OID, -1,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("custom subscript must have int type"),
+ parser_errposition(pstate, exprLocation(subexpr))));
+
+ upperIndexpr = lappend(upperIndexpr, subexpr);
+ }
+
+ sbsref->refupperindexpr = upperIndexpr;
+ sbsref->refelemtype = INT4OID;
+
+ PG_RETURN_POINTER(sbsref);
+}
diff --git a/src/tutorial/subscripting.source b/src/tutorial/subscripting.source
new file mode 100644
index 0000000000..3417f1ce3d
--- /dev/null
+++ b/src/tutorial/subscripting.source
@@ -0,0 +1,83 @@
+---------------------------------------------------------------------------
+--
+-- subscripting.sql-
+-- This file shows how to create a new subscripting procedure for
+-- user-defined type.
+--
+--
+-- Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+-- Portions Copyright (c) 1994, Regents of the University of California
+--
+-- src/tutorial/subscripting.source
+--
+---------------------------------------------------------------------------
+
+-----------------------------
+-- Creating a new type:
+-- We are going to create a new type called 'complex' which represents
+-- complex numbers.
+-- A user-defined type must have an input and an output function, and
+-- optionally can have binary input and output functions. All of these
+-- are usually user-defined C functions.
+-----------------------------
+
+-- Assume the user defined functions are in /home/erthalion/programms/postgresql-master/src/tutorial/complex$DLSUFFIX
+-- (we do not want to assume this is in the dynamic loader search path).
+-- Look at $PWD/complex.c for the source. Note that we declare all of
+-- them as STRICT, so we do not need to cope with NULL inputs in the
+-- C code. We also mark them IMMUTABLE, since they always return the
+-- same outputs given the same inputs.
+
+-- the input function 'complex_in' takes a null-terminated string (the
+-- textual representation of the type) and turns it into the internal
+-- (in memory) representation. You will get a message telling you 'complex'
+-- does not exist yet but that's okay.
+
+CREATE FUNCTION custom_in(cstring)
+ RETURNS custom
+ AS '_OBJWD_/subscripting'
+ LANGUAGE C IMMUTABLE STRICT;
+
+-- the output function 'complex_out' takes the internal representation and
+-- converts it into the textual representation.
+
+CREATE FUNCTION custom_out(custom)
+ RETURNS cstring
+ AS '_OBJWD_/subscripting'
+ LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION custom_subscripting_parse(internal)
+ RETURNS internal
+ AS '_OBJWD_/subscripting'
+ LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION custom_subscripting_fetch(internal)
+ RETURNS internal
+ AS '_OBJWD_/subscripting'
+ LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION custom_subscripting_assign(internal)
+ RETURNS internal
+ AS '_OBJWD_/subscripting'
+ LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE custom (
+ internallength = 8,
+ input = custom_in,
+ output = custom_out,
+ subscripting_parse = custom_subscripting_parse,
+ subscripting_fetch = custom_subscripting_fetch,
+ subscripting_assign = custom_subscripting_assign
+);
+
+-- we can use it in a table
+
+CREATE TABLE test_subscripting (
+ data custom
+);
+
+INSERT INTO test_subscripting VALUES ('(1, 2)');
+
+SELECT data[0] from test_subscripting;
+
+UPDATE test_subscripting SET data[1] = 3;
--
2.13.0