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