I would like to propose extending composite types with constraints.
Currently there is a preliminar patch for not null constraints.

===
Changes :
  -Use the parser from create table to get the constraints
'OptTableElementList' instead of 'OptTableFuncElementList'.
  - Add a new transformation transformCompositeTypeStmt similar to
transformCreateStmt to add constraint informations to stmt.
   - Implement a recursive notnull check  when a type is composite
==
Features enabled :
 - Enable not null in parser for
create type tyname AS (  a  type NOT NULL )
 - Check for null in rowtypes

===



Wesley S. Massuda
From 5ef7ec081528f8405d34a4fb2155e74fdcd95094 Mon Sep 17 00:00:00 2001
From: Wesley Sales Massuda <wesley.mass...@gmail.com>
Date: Thu, 15 Dec 2016 12:29:42 -0200
Subject: [PATCH] add support for not null constraints for composite types add
 not null insert check for row_types

---
 src/backend/executor/execMain.c           |  78 +++++++++++++++++++++++
 src/backend/parser/gram.y                 |   2 +-
 src/backend/parser/parse_utilcmd.c        | 101 ++++++++++++++++++++++++++++++
 src/backend/tcop/utility.c                |   2 +-
 src/test/regress/expected/create_type.out |   2 +-
 src/test/regress/sql/create_type.sql      |   2 +-
 6 files changed, 183 insertions(+), 4 deletions(-)

diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index d43a204..b452fd6 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1756,6 +1756,7 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
 	Assert(constr || resultRelInfo->ri_PartitionCheck);
 
 	if (constr && constr->has_not_null)
+
 	{
 		int			natts = tupdesc->natts;
 		int			attrChk;
@@ -1783,6 +1784,13 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
 						 val_desc ? errdetail("Failing row contains %s.", val_desc) : 0,
 						 errtablecol(rel, attrChk)));
 			}
+      Oid typoid = tupdesc->attrs[attrChk -1]->atttypid;
+      bool isrow = type_is_rowtype(typoid);
+      if(isrow){
+        bool isnull;
+        Datum dat = slot_getattr(slot,attrChk ,&isnull);
+        composite_isnull(tupdesc,resultRelInfo ,slot,attrChk -1,dat,estate);
+      }
 		}
 	}
 
@@ -1831,7 +1839,77 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
 		  val_desc ? errdetail("Failing row contains %s.", val_desc) : 0));
 	}
 }
+void
+composite_isnull(TupleDesc topdesc,ResultRelInfo *resultRelInfo,TupleTableSlot *slot,int attnum,Datum composite, EState *estate)
+{
+	Relation	rel = resultRelInfo->ri_RelationDesc;
+	HeapTupleHeader td;
+	Oid			tupType;
+	int32		tupTypmod;
+	TupleDesc	tupdesc;
+	HeapTupleData tmptup,
+			   *tuple;
+	int			i;
+
+	Bitmapset  *modifiedCols;
+	Bitmapset  *insertedCols;
+	Bitmapset  *updatedCols;
+
+	td = DatumGetHeapTupleHeader(composite);
+
+	/* Extract rowtype info and find a tupdesc */
+	tupType = HeapTupleHeaderGetTypeId(td);
+	tupTypmod = HeapTupleHeaderGetTypMod(td);
+	tupdesc = lookup_rowtype_tupdesc_copy(tupType, tupTypmod);
+
+	/* Build a temporary HeapTuple control structure */
+	tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
+	tmptup.t_data = td;
+	tuple = &tmptup;
+
+
+	for (i = 0; i < tupdesc->natts; i++)
+	{
+		Datum		val;
+		bool		isnull;
+		char	   *attname;
+		Oid			outfuncoid;
+
+	if (tupdesc->attrs[i]->attisdropped)
+			continue;
+  if(tupdesc->attrs[i]->attnotnull ){
+  Datum att = heap_getattr(tuple,i+1,tupdesc,&isnull);
+  if( isnull){
+				char	   *val_desc;
+        insertedCols = GetInsertedColumns(resultRelInfo, estate);
+				updatedCols = GetUpdatedColumns(resultRelInfo, estate);
+				modifiedCols = bms_union(insertedCols, updatedCols);
+				val_desc = ExecBuildSlotValueDescription(RelationGetRelid(rel),
+														 slot,
+														 topdesc,
+														 modifiedCols,
+														 64);
 
+				ereport(ERROR,
+						(errcode(ERRCODE_NOT_NULL_VIOLATION),
+						 errmsg("null value in column \"%s\" violates not-null constraint",
+							  NameStr(tupdesc->attrs[i]->attname)),
+						 val_desc ? errdetail("Failing row contains %s.", val_desc) : 0,
+						 errtablecol(rel, attnum +1 )));
+
+    }
+  
+    Oid typoid = tupdesc->attrs[i]->atttypid;
+    bool isrow = type_is_rowtype(typoid);
+    if(isrow){
+        composite_isnull(topdesc,resultRelInfo ,slot,attnum,att,estate);
+    }
+  }
+
+	}
+
+	ReleaseTupleDesc(tupdesc);
+}
 /*
  * ExecWithCheckOptions -- check that tuple satisfies any WITH CHECK OPTIONs
  * of the specified kind.
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 2ed7b52..2e873bb 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -5504,7 +5504,7 @@ DefineStmt:
 					n->definition = NIL;
 					$$ = (Node *)n;
 				}
-			| CREATE TYPE_P any_name AS '(' OptTableFuncElementList ')'
+			| CREATE TYPE_P any_name AS '(' OptTableElementList ')'
 				{
 					CompositeTypeStmt *n = makeNode(CompositeTypeStmt);
 
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index cc6a961..d5624f0 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -373,6 +373,107 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
 }
 
 /*
+ * transformCompositeTypeStmt -
+ *	  parse analysis for CREATE TYPE  AS
+ *
+ * Returns a List of utility commands to be done in sequence.  One of these
+ * will be the transformed CreateStmt, but there may be additional actions
+ * to be done before and after the actual DefineRelation() call.
+ *
+ * SQL allows constraints to be scattered all over, so thumb through
+ * the columns and collect all constraints into one place.
+ * If there are any implied indices (e.g. UNIQUE or PRIMARY KEY)
+ * then expand those into multiple IndexStmt blocks.
+ *	  - thomas 1997-12-02
+ */
+CompositeTypeStmt *
+transformCompositeTypeStmt(CompositeTypeStmt *stmt, const char *queryString)
+{
+	ParseState *pstate;
+	CreateStmtContext cxt;
+	List	   *result;
+	List	   *save_alist;
+	ListCell   *elements;
+	Oid			namespaceid;
+	Oid			existing_relid;
+	ParseCallbackState pcbstate;
+	bool		like_found = false;
+
+	/*
+	 * We must not scribble on the passed-in CreateStmt, so copy it.  (This is
+	 * overkill, but easy.)
+	 */
+	stmt = (CompositeTypeStmt *) copyObject(stmt);
+
+	/* Set up pstate */
+	pstate = make_parsestate(NULL);
+	pstate->p_sourcetext = queryString;
+
+	/*
+	 * Look up the creation namespace.  This also checks permissions on the
+	 * target namespace, locks it against concurrent drops, checks for a
+	 * preexisting relation in that namespace with the same name, and updates
+	 * stmt->relation->relpersistence if the selected namespace is temporary.
+	 */
+	setup_parser_errposition_callback(&pcbstate, pstate,
+									  stmt->typevar->location);
+	namespaceid =
+		RangeVarGetAndCheckCreationNamespace(stmt->typevar, NoLock,
+											 &existing_relid);
+	cancel_parser_errposition_callback(&pcbstate);
+	cxt.pstate = pstate;
+	cxt.relation = stmt->typevar;
+	cxt.rel = NULL;
+	cxt.isalter = false;
+	cxt.columns = NIL;
+	cxt.ckconstraints = NIL;
+	cxt.fkconstraints = NIL;
+	cxt.ixconstraints = NIL;
+	cxt.inh_indexes = NIL;
+	cxt.blist = NIL;
+	cxt.alist = NIL;
+	cxt.pkey = NULL;
+ foreach(elements, stmt->coldeflist)
+	{
+		Node	   *element = lfirst(elements);
+
+		switch (nodeTag(element))
+		{
+			case T_ColumnDef:
+				transformColumnDefinition(&cxt, (ColumnDef *) element);
+				break;
+
+			case T_TableLikeClause:
+				if (!like_found)
+				{
+					cxt.hasoids = false;
+					like_found = true;
+				}
+				transformTableLikeClause(&cxt, (TableLikeClause *) element);
+				break;
+
+			case T_Constraint:
+				/* process later */
+				break;
+
+			default:
+				elog(ERROR, "unrecognized node type: %d",
+					 (int) nodeTag(element));
+				break;
+		}
+	}
+
+
+	/*
+	 * Output results.
+	 */
+	stmt->coldeflist= cxt.columns;
+
+
+	return stmt;
+}
+
+/*
  * transformColumnDefinition -
  *		transform a single ColumnDef within CREATE TABLE
  *		Also used in ALTER TABLE ADD COLUMN
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index fd4eff4..0cc66bf 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1355,7 +1355,7 @@ ProcessUtilitySlow(ParseState *pstate,
 
 			case T_CompositeTypeStmt:	/* CREATE TYPE (composite) */
 				{
-					CompositeTypeStmt *stmt = (CompositeTypeStmt *) parsetree;
+          CompositeTypeStmt *stmt = transformCompositeTypeStmt((CompositeTypeStmt *)parsetree,queryString);
 
 					address = DefineCompositeType(stmt->typevar,
 												  stmt->coldeflist);
diff --git a/src/test/regress/expected/create_type.out b/src/test/regress/expected/create_type.out
index 7bdad4e..796b4c0 100644
--- a/src/test/regress/expected/create_type.out
+++ b/src/test/regress/expected/create_type.out
@@ -84,7 +84,7 @@ SELECT * FROM default_test;
 (1 row)
 
 -- Test stand-alone composite type
-CREATE TYPE default_test_row AS (f1 text_w_default, f2 int42);
+CREATE TYPE default_test_row AS (f1 text_w_default not null, f2 int42);
 CREATE FUNCTION get_default_test() RETURNS SETOF default_test_row AS '
   SELECT * FROM default_test;
 ' LANGUAGE SQL;
diff --git a/src/test/regress/sql/create_type.sql b/src/test/regress/sql/create_type.sql
index a1839ef..fa89059 100644
--- a/src/test/regress/sql/create_type.sql
+++ b/src/test/regress/sql/create_type.sql
@@ -86,7 +86,7 @@ SELECT * FROM default_test;
 
 -- Test stand-alone composite type
 
-CREATE TYPE default_test_row AS (f1 text_w_default, f2 int42);
+CREATE TYPE default_test_row AS (f1 text_w_default not null, f2 int42);
 
 CREATE FUNCTION get_default_test() RETURNS SETOF default_test_row AS '
   SELECT * FROM default_test;
-- 
2.10.2

-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to