SQL2003 GENERATED ... AS ... syntax

Started by Nonameover 22 years ago7 messages
#1Noname
des@des.no
1 attachment(s)

As previously mentioned, I'm working on implementing [subject]. I
think I've mostly worked it out, but I'm having trouble with the
GENERATED ALWAYS case.

My changes (see attached patch) can be summarized as follows:

- in backend/utils/adt/misc.c:

- added a force_default_value() function which takes a string
argument (the name of the column to force to default) and
currently does nothing.

- in backend/parser/gram.y:

- when GENERATED ... AS ... is encountered in a column definition,
it adds a node of the new T_Generated type to the constraint
list. This node contains a bool that differentiates between BY
DEFAULT and ALWAYS, and a pointer to a CreateSeqStmt (for
IDENTITY '(' OptSeqList ')') or a List constructed by the a_expr
production (for '(' a_expr ')')

- in backend/parser/analyze.c:

- factored out the code from transformColumnDefinition() that
creates the sequence and the DEFAULT constraint into a separate
CreateSerialColumn() function which takes as one of its arguments
is a List of sequence options. The SERIAL code passes in a NIL
list, while the GENERATED AS IDENTITY code passes in the option
list from the CreateSeqStmt.

- added a CreateAlwaysDefaultColumn() function which synthesizes a
CreateTrigStmt equivalent to CREATE TRIGGER foo BEFORE INSERT ON
bar FOR EACH ROW EXECUTE PROCEDURE force_default_value ('baz')
and adds it to the work list. This function is called by
transformColumnDefinition() if a Generated node with always set
to true is encountered.

Now I must be doing something wrong in CreateAlwaysDefaultColumn(),
because the CreateTrigStmt fails to execute:

| des=# create table test ( id int generated always as identity ( minvalue 1000 ), word text );
| NOTICE: CREATE TABLE will create implicit sequence "test_id_seq" for SERIAL column "test.id"
| NOTICE: CREATE TABLE will create implicit trigger "test_id_always_default" for column "test.id"
| ERROR: relation "public.test" does not exist

GENERATED BY DEFAULT AS IDENTITY works fine though, so I must have
done *something* right:

| des=# create table test ( id int generated by default as identity ( minvalue 1000 ), word text );
| NOTICE: CREATE TABLE will create implicit sequence "test_id_seq" for SERIAL column "test.id"
| CREATE TABLE
| des=# select sequence_name, last_value, min_value, max_value from test_id_seq;
| sequence_name | last_value | min_value | max_value
| ---------------+------------+-----------+---------------------
| test_id_seq | 1000 | 1000 | 9223372036854775807
| (1 row)
|

On the other hand, I seem to have botched the definition of
force_default_value() in include/catalog/pg_proc.h, because adding the
trigger manually doesn't seem to work either:

| des=# \df force_default_value
| List of functions
| Result data type | Schema | Name | Argument data types
| ------------------+------------+---------------------+---------------------
| "trigger" | pg_catalog | force_default_value | text
| (1 row)
|
| des=# create trigger test_id_always_default before insert on test for each row execute procedure force_default_value ('id');
| ERROR: function force_default_value() does not exist

Any suggestions?

DES
--
Dag-Erling Smørgrav - des@des.no

Attachments:

sql2003_generated_as.difftext/x-patchDownload
Index: src/backend/parser/analyze.c
===================================================================
RCS file: /home/pqcvs/pgsql-server/src/backend/parser/analyze.c,v
retrieving revision 1.283
diff -u -u -r1.283 analyze.c
--- src/backend/parser/analyze.c	1 Aug 2003 00:15:22 -0000	1.283
+++ src/backend/parser/analyze.c	3 Aug 2003 11:39:12 -0000
@@ -917,13 +917,116 @@
 }
 
 static void
+CreateSerialColumn(ParseState *pstate, CreateStmtContext *cxt,
+				   ColumnDef *column, List *seqopts)
+{
+	Constraint *constraint;
+	char	   *sname;
+	char	   *snamespace;
+	char	   *qstring;
+	A_Const    *snamenode;
+	FuncCall   *funccallnode;
+	CreateSeqStmt *seqstmt;
+
+	/*
+	 * Determine name and namespace to use for the sequence.
+	 */
+	sname = makeObjectName(cxt->relation->relname, column->colname, "seq");
+	snamespace = get_namespace_name(RangeVarGetCreationNamespace(cxt->relation));
+
+	ereport(NOTICE,
+			(errmsg("%s will create implicit sequence \"%s\" for SERIAL column \"%s.%s\"",
+					cxt->stmtType, sname,
+					cxt->relation->relname, column->colname)));
+
+	/*
+	 * Build a CREATE SEQUENCE command to create the sequence object,
+	 * and add it to the list of things to be done before this
+	 * CREATE/ALTER TABLE.
+	 */
+	seqstmt = makeNode(CreateSeqStmt);
+	seqstmt->sequence = makeRangeVar(snamespace, sname);
+	seqstmt->options = seqopts;
+
+	cxt->blist = lappend(cxt->blist, seqstmt);
+
+	/*
+	 * Mark the ColumnDef so that during execution, an appropriate
+	 * dependency will be added from the sequence to the column.
+	 */
+	column->support = makeRangeVar(snamespace, sname);
+
+	/*
+	 * Create appropriate constraints for SERIAL.  We do this in full,
+	 * rather than shortcutting, so that we will detect any
+	 * conflicting constraints the user wrote (like a different
+	 * DEFAULT).
+	 *
+	 * Create an expression tree representing the function call
+	 * nextval('"sequencename"')
+	 */
+	qstring = quote_qualified_identifier(snamespace, sname);
+	snamenode = makeNode(A_Const);
+	snamenode->val.type = T_String;
+	snamenode->val.val.str = qstring;
+	funccallnode = makeNode(FuncCall);
+	funccallnode->funcname = SystemFuncName("nextval");
+	funccallnode->args = makeList1(snamenode);
+	funccallnode->agg_star = false;
+	funccallnode->agg_distinct = false;
+
+	constraint = makeNode(Constraint);
+	constraint->contype = CONSTR_DEFAULT;
+	constraint->name = sname;
+	constraint->raw_expr = (Node *) funccallnode;
+	constraint->cooked_expr = NULL;
+	constraint->keys = NIL;
+	column->constraints = lappend(column->constraints, constraint);
+
+	constraint = makeNode(Constraint);
+	constraint->contype = CONSTR_NOTNULL;
+	column->constraints = lappend(column->constraints, constraint);
+}
+
+static void
+CreateAlwaysDefaultColumn(ParseState *pstate, CreateStmtContext *cxt,
+						  ColumnDef *column)
+{
+	char		*tname;
+	char		*tnamespace;
+	CreateTrigStmt *trigstmt;
+
+	tname = makeObjectName(cxt->relation->relname,
+						   column->colname, "always_default");
+	tnamespace = get_namespace_name(RangeVarGetCreationNamespace(cxt->relation));
+
+	ereport(NOTICE,
+			(errmsg("%s will create implicit trigger \"%s\" for column \"%s.%s\"",
+					cxt->stmtType, tname,
+					cxt->relation->relname, column->colname)));
+
+	trigstmt = makeNode(CreateTrigStmt);
+	trigstmt->trigname = tname;
+	trigstmt->relation = makeRangeVar(tnamespace, cxt->relation->relname);
+	trigstmt->funcname = SystemFuncName("force_default_value");
+	trigstmt->args = makeList1(makeString(column->colname));
+	trigstmt->before = true;
+	trigstmt->row = true;
+	trigstmt->actions[0] = 'i';
+
+	cxt->blist = lappend(cxt->blist, trigstmt);
+}
+
+static void
 transformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt,
 						  ColumnDef *column)
 {
 	bool		is_serial;
+	bool		is_always_default;
 	bool		saw_nullable;
 	Constraint *constraint;
 	List	   *clist;
+	List	   *seqopts;
 
 	cxt->columns = lappend(cxt->columns, column);
 
@@ -937,6 +1040,7 @@
 			strcmp(typname, "serial4") == 0)
 		{
 			is_serial = true;
+			seqopts = NIL;
 			column->typename->names = NIL;
 			column->typename->typeid = INT4OID;
 		}
@@ -944,6 +1048,7 @@
 				 strcmp(typname, "serial8") == 0)
 		{
 			is_serial = true;
+			seqopts = NIL;
 			column->typename->names = NIL;
 			column->typename->typeid = INT8OID;
 		}
@@ -952,76 +1057,62 @@
 	/* Do necessary work on the column type declaration */
 	transformColumnType(pstate, column);
 
-	/* Special actions for SERIAL pseudo-types */
-	if (is_serial)
+	/* Check for GENERATED ... AS ... */
+	is_always_default = false;
+	foreach(clist, column->constraints)
 	{
-		char	   *sname;
-		char	   *snamespace;
-		char	   *qstring;
-		A_Const    *snamenode;
-		FuncCall   *funccallnode;
-		CreateSeqStmt *seqstmt;
+		Generated		*generated;
 
-		/*
-		 * Determine name and namespace to use for the sequence.
-		 */
-		sname = makeObjectName(cxt->relation->relname, column->colname, "seq");
-		snamespace = get_namespace_name(RangeVarGetCreationNamespace(cxt->relation));
-
-		ereport(NOTICE,
-				(errmsg("%s will create implicit sequence \"%s\" for SERIAL column \"%s.%s\"",
-						cxt->stmtType, sname,
-						cxt->relation->relname, column->colname)));
+		generated = lfirst(clist);
+		if (!IsA(generated, Generated))
+			continue;
 
-		/*
-		 * Build a CREATE SEQUENCE command to create the sequence object,
-		 * and add it to the list of things to be done before this
-		 * CREATE/ALTER TABLE.
-		 */
-		seqstmt = makeNode(CreateSeqStmt);
-		seqstmt->sequence = makeRangeVar(snamespace, sname);
-		seqstmt->options = NIL;
+		if (generated->always)
+			is_always_default = true;
 
-		cxt->blist = lappend(cxt->blist, seqstmt);
+		if (IsA(generated->expr, CreateSeqStmt)) {			/* AS IDENTITY */
+			CreateSeqStmt *stmt = (CreateSeqStmt *)generated->expr;
 
-		/*
-		 * Mark the ColumnDef so that during execution, an appropriate
-		 * dependency will be added from the sequence to the column.
-		 */
-		column->support = makeRangeVar(snamespace, sname);
+			/* is this column already serial? */
+			if (is_serial) {
+				ereport(WARNING,
+						(errmsg("multiple SERIAL / GENERATED AS IDENTITY on column \"%s.%s\"",
+								cxt->relation->relname, column->colname)));
+				continue;
+			}
+
+			/* check the type */
+			column->typename->typeid = LookupTypeName(column->typename);
+			if (column->typename->typeid != InvalidOid)
+				column->typename->names = NIL;
+			if (column->typename->typeid != INT4OID)
+				ereport(ERROR,
+						(errcode(ERRCODE_DATATYPE_MISMATCH),
+						 errmsg("GENERATED AS IDENTITY columns must be of integer type")));
 
-		/*
-		 * Create appropriate constraints for SERIAL.  We do this in full,
-		 * rather than shortcutting, so that we will detect any
-		 * conflicting constraints the user wrote (like a different
-		 * DEFAULT).
-		 *
-		 * Create an expression tree representing the function call
-		 * nextval('"sequencename"')
-		 */
-		qstring = quote_qualified_identifier(snamespace, sname);
-		snamenode = makeNode(A_Const);
-		snamenode->val.type = T_String;
-		snamenode->val.val.str = qstring;
-		funccallnode = makeNode(FuncCall);
-		funccallnode->funcname = SystemFuncName("nextval");
-		funccallnode->args = makeList1(snamenode);
-		funccallnode->agg_star = false;
-		funccallnode->agg_distinct = false;
-
-		constraint = makeNode(Constraint);
-		constraint->contype = CONSTR_DEFAULT;
-		constraint->name = sname;
-		constraint->raw_expr = (Node *) funccallnode;
-		constraint->cooked_expr = NULL;
-		constraint->keys = NIL;
-		column->constraints = lappend(column->constraints, constraint);
-
-		constraint = makeNode(Constraint);
-		constraint->contype = CONSTR_NOTNULL;
-		column->constraints = lappend(column->constraints, constraint);
+			is_serial = true;
+			seqopts = stmt->options;
+			stmt->options = NIL;
+		} else {											/* AS ( <expr> ) */
+			/*
+			 * "GENERATED BY DEFAULT AS ( <expr> )" is not valid SQL,
+			 * though it might make sense to accept it as an alternate
+			 * syntax for "DEFAULT expr".
+			 */
+			Assert(is_always_default);
+
+			ereport(ERROR,
+					(errcode(ERRCODE_DATATYPE_MISMATCH),
+					 errmsg("GENERATED AS ( <expr> ) is not yet supported")));
+		}
 	}
 
+	if (is_serial)
+		CreateSerialColumn(pstate, cxt, column, seqopts);
+
+	if (is_always_default)
+		CreateAlwaysDefaultColumn(pstate, cxt, column);
+
 	/* Process column constraints, if any... */
 	transformConstraintAttrs(column->constraints);
 
@@ -1045,6 +1136,12 @@
 			continue;
 		}
 
+		/*
+		 * GENERATE ... AS ... was handled earlier
+		 */
+		if (IsA(constraint, Generated))
+			continue;
+
 		Assert(IsA(constraint, Constraint));
 
 		switch (constraint->contype)
@@ -1188,7 +1285,7 @@
 						inhRelation->relation->relname)));
 
 	/*
-	 * Check for SELECT privilages 
+	 * Check for SELECT privilages
 	 */
 	aclresult = pg_class_aclcheck(RelationGetRelid(relation), GetUserId(),
 								  ACL_SELECT);
Index: src/backend/parser/gram.y
===================================================================
RCS file: /home/pqcvs/pgsql-server/src/backend/parser/gram.y,v
retrieving revision 2.427
diff -u -u -r2.427 gram.y
--- src/backend/parser/gram.y	19 Jul 2003 20:20:52 -0000	2.427
+++ src/backend/parser/gram.y	3 Aug 2003 10:55:19 -0000
@@ -317,6 +317,7 @@
 %type <list>	constraints_set_list
 %type <boolean> constraints_set_mode
 
+%type <node>	GeneratedAs
 
 /*
  * If you make any token changes, update the keyword table in
@@ -326,7 +327,7 @@
 
 /* ordinary key words in alphabetical order */
 %token <keyword> ABORT_P ABSOLUTE_P ACCESS ACTION ADD AFTER
-	AGGREGATE ALL ALTER ANALYSE ANALYZE AND ANY ARRAY AS ASC
+	AGGREGATE ALL ALTER ALWAYS ANALYSE ANALYZE AND ANY ARRAY AS ASC
 	ASSERTION ASSIGNMENT AT AUTHORIZATION
 
 	BACKWARD BEFORE BEGIN_P BETWEEN BIGINT BINARY BIT
@@ -349,11 +350,11 @@
 	FALSE_P FETCH FIRST_P FLOAT_P FOR FORCE FOREIGN FORWARD
 	FREEZE FROM FULL FUNCTION
 
-	GLOBAL GRANT GROUP_P
+	GENERATED GLOBAL GRANT GROUP_P
 
 	HANDLER HAVING HOLD HOUR_P
 
-	ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IN_P INCLUDING INCREMENT
+	IDENTITY ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IN_P INCLUDING INCREMENT
 	INDEX INHERITS INITIALLY INNER_P INOUT INPUT_P
 	INSENSITIVE INSERT INSTEAD INT_P INTEGER INTERSECT
 	INTERVAL INTO INVOKER IS ISNULL ISOLATION
@@ -1604,6 +1605,39 @@
 					n->fk_del_action	= (char) ($5 & 0xFF);
 					n->deferrable		= FALSE;
 					n->initdeferred		= FALSE;
+					$$ = (Node *)n;
+				}
+			| GENERATED ALWAYS GeneratedAs
+				{
+					Generated *n = makeNode(Generated);
+					n->always = true;
+					n->expr = $3;
+					$$ = (Node *)n;
+				}
+			| GENERATED BY DEFAULT GeneratedAs
+				{
+					Generated *n = makeNode(Generated);
+					n->always = false;
+					n->expr = $4;
+					$$ = (Node *)n;
+				}
+		;
+
+GeneratedAs:
+			AS '(' a_expr ')'
+				{
+					$$ = $3;
+				}
+			| AS IDENTITY
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					n->options = NIL;
+					$$ = (Node *)n;
+				}
+			| AS IDENTITY '(' OptSeqList ')'
+				{
+					CreateSeqStmt *n = makeNode(CreateSeqStmt);
+					n->options = $4;
 					$$ = (Node *)n;
 				}
 		;
Index: src/backend/parser/keywords.c
===================================================================
RCS file: /home/pqcvs/pgsql-server/src/backend/parser/keywords.c,v
retrieving revision 1.140
diff -u -u -r1.140 keywords.c
--- src/backend/parser/keywords.c	25 Jun 2003 03:40:18 -0000	1.140
+++ src/backend/parser/keywords.c	2 Aug 2003 22:46:40 -0000
@@ -39,6 +39,7 @@
 	{"aggregate", AGGREGATE},
 	{"all", ALL},
 	{"alter", ALTER},
+	{"always", ALWAYS},
 	{"analyse", ANALYSE},		/* British spelling */
 	{"analyze", ANALYZE},
 	{"and", AND},
@@ -141,6 +142,7 @@
 	{"from", FROM},
 	{"full", FULL},
 	{"function", FUNCTION},
+	{"generated", GENERATED},
 	{"global", GLOBAL},
 	{"grant", GRANT},
 	{"group", GROUP_P},
@@ -148,6 +150,7 @@
 	{"having", HAVING},
 	{"hold", HOLD},
 	{"hour", HOUR_P},
+	{"identity", IDENTITY},
 	{"ilike", ILIKE},
 	{"immediate", IMMEDIATE},
 	{"immutable", IMMUTABLE},
Index: src/backend/utils/adt/misc.c
===================================================================
RCS file: /home/pqcvs/pgsql-server/src/backend/utils/adt/misc.c,v
retrieving revision 1.30
diff -u -u -r1.30 misc.c
--- src/backend/utils/adt/misc.c	27 Jun 2003 17:07:03 -0000	1.30
+++ src/backend/utils/adt/misc.c	3 Aug 2003 11:29:05 -0000
@@ -18,6 +18,7 @@
 #include <time.h>
 
 #include "commands/dbcommands.h"
+#include "commands/trigger.h"
 #include "miscadmin.h"
 #include "utils/builtins.h"
 
@@ -57,4 +58,30 @@
 
 	namestrcpy(db, get_database_name(MyDatabaseId));
 	PG_RETURN_NAME(db);
+}
+
+/*
+ * force_default_value()
+ *  Force a column to its default value
+ */
+Datum
+force_default_value(PG_FUNCTION_ARGS)
+{
+	TriggerData *trigdata;
+	int			tgnargs;
+	char	  **tgargs;
+
+	if (!CALLED_AS_TRIGGER(fcinfo))
+		ereport(ERROR,
+				(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
+				 errmsg("%s() was not fired by trigger manager",
+						PG_FUNCNAME_MACRO)));
+
+	trigdata = (TriggerData *)fcinfo->context;
+	tgnargs = trigdata->tg_trigger->tgnargs;
+	tgargs = trigdata->tg_trigger->tgargs;
+
+	ereport(NOTICE, (errmsg("%s() was fired!", PG_FUNCNAME_MACRO)));
+
+	return PointerGetDatum(NULL);
 }
Index: src/include/catalog/pg_proc.h
===================================================================
RCS file: /home/pqcvs/pgsql-server/src/include/catalog/pg_proc.h,v
retrieving revision 1.310
diff -u -u -r1.310 pg_proc.h
--- src/include/catalog/pg_proc.h	30 Jul 2003 22:56:24 -0000	1.310
+++ src/include/catalog/pg_proc.h	3 Aug 2003 11:18:51 -0000
@@ -3419,6 +3419,9 @@
 DATA(insert OID = 2509 (  pg_get_expr		   PGNSP PGUID 12 f f t f s 3 25 "25 26 16"	pg_get_expr_ext - _null_ ));
 DESCR("deparse an encoded expression with pretty-print option");
 
+DATA(insert OID = 2510 (  force_default_value  PGNSP PGUID 12 f f t f v 1 2279 "25"	force_default_value - _null_ ));
+DESCR("force default value for GENERATED ALWAYS columns");
+
 
 /*
  * Symbolic values for provolatile column: these indicate whether the result
Index: src/include/nodes/nodes.h
===================================================================
RCS file: /home/pqcvs/pgsql-server/src/include/nodes/nodes.h,v
retrieving revision 1.144
diff -u -u -r1.144 nodes.h
--- src/include/nodes/nodes.h	3 Jul 2003 16:34:25 -0000	1.144
+++ src/include/nodes/nodes.h	2 Aug 2003 23:11:36 -0000
@@ -283,6 +283,7 @@
 	T_CreateOpClassItem,
 	T_CompositeTypeStmt,
 	T_InhRelation,
+	T_Generated,
 
 	/*
 	 * TAGS FOR FUNCTION-CALL CONTEXT AND RESULTINFO NODES (see fmgr.h)
Index: src/include/nodes/parsenodes.h
===================================================================
RCS file: /home/pqcvs/pgsql-server/src/include/nodes/parsenodes.h,v
retrieving revision 1.243
diff -u -u -r1.243 parsenodes.h
--- src/include/nodes/parsenodes.h	3 Jul 2003 16:34:25 -0000	1.243
+++ src/include/nodes/parsenodes.h	3 Aug 2003 10:21:44 -0000
@@ -975,6 +975,16 @@
 	bool		skip_validation;	/* skip validation of existing rows? */
 } FkConstraint;
 
+/*
+ * Definitions for GENERATED ... AS ... in CreateStmt
+ */
+typedef struct Generated
+{
+	NodeTag		type;
+	bool		always;			/* true if ALWAYS, false if BY DEFAULT */
+	Node	   *expr;			/* Expression or CreateSeqStmt */
+} Generated;
+
 /* ----------------------
  *		Create/Drop TRIGGER Statements
  * ----------------------
Index: src/include/utils/builtins.h
===================================================================
RCS file: /home/pqcvs/pgsql-server/src/include/utils/builtins.h,v
retrieving revision 1.224
diff -u -u -r1.224 builtins.h
--- src/include/utils/builtins.h	30 Jul 2003 22:56:24 -0000	1.224
+++ src/include/utils/builtins.h	3 Aug 2003 11:13:49 -0000
@@ -337,6 +337,7 @@
 extern Datum nullvalue(PG_FUNCTION_ARGS);
 extern Datum nonnullvalue(PG_FUNCTION_ARGS);
 extern Datum current_database(PG_FUNCTION_ARGS);
+extern Datum force_default_value(PG_FUNCTION_ARGS);
 
 /* not_in.c */
 extern Datum int4notin(PG_FUNCTION_ARGS);
#2Rod Taylor
rbt@rbt.ca
In reply to: Noname (#1)
Re: SQL2003 GENERATED ... AS ... syntax

On the other hand, I seem to have botched the definition of
force_default_value() in include/catalog/pg_proc.h, because adding the
trigger manually doesn't seem to work either:

| des=# \df force_default_value
| List of functions
| Result data type | Schema | Name | Argument data types
| ------------------+------------+---------------------+---------------------
| "trigger" | pg_catalog | force_default_value | text
| (1 row)
|
| des=# create trigger test_id_always_default before insert on test for each row execute procedure force_default_value ('id');
| ERROR: function force_default_value() does not exist

Any suggestions?

Triggers are strange this way. You need to create the function without
any arguments. The procedure is expected to find (and enforce) the
arguments through use of the TriggerData struct.

ttdummy() in src/test/regress/regress.c is an example of a Trigger
taking 2 arguments.

Using triggers could cause some interesting side effects when they're
system controlled. Triggers are fired by naming convention
(alphabetical order). If the user creates two triggers, firing first
and third (force_default_value() being second) they will see two
different values in that field.

It would be interesting to see what other DBs do with ALWAYS and BEFORE
triggers (whether they see the user supplied or generated value).

I think a longer term solution would be to add a type to pg_attrdef and
a bool for ALWAYS. (Tom?)
- default, identity, and generator being the types.

- SERIAL would be converted into an identity (not the other way around).

- psql would not show defaults in the case of an identity.

- pg_dump could be trained to use the identity syntax on the table which
has the added benefit of not relying on sequence naming convention.

- rewriteTargetList() can be used to enforce the default value from the
client perspective only (triggers, user rules, etc. could all override
ALWAYS default but the spec does not mention otherwise).

#3Noname
des@des.no
In reply to: Rod Taylor (#2)
Re: SQL2003 GENERATED ... AS ... syntax

Rod Taylor <rbt@rbt.ca> writes:

I think a longer term solution would be to add a type to pg_attrdef and
a bool for ALWAYS. (Tom?)

I thought about it, but won't that change the on-disk format? Since
minor version upgrades aren't supposed to require a dump / restore,
and I understand 7.4 is already in feature freeze, the earliest
opportunity for something like this would be 7.5.

DES
--
Dag-Erling Smørgrav - des@des.no

#4Rod Taylor
rbt@rbt.ca
In reply to: Noname (#3)
Re: SQL2003 GENERATED ... AS ... syntax

On Sun, 2003-08-03 at 10:31, Dag-Erling Smørgrav wrote:

Rod Taylor <rbt@rbt.ca> writes:

I think a longer term solution would be to add a type to pg_attrdef and
a bool for ALWAYS. (Tom?)

I thought about it, but won't that change the on-disk format? Since
minor version upgrades aren't supposed to require a dump / restore,
and I understand 7.4 is already in feature freeze, the earliest
opportunity for something like this would be 7.5.

Yes it would. The solution you have already requires an initdb (changed
pg_proc.h), as such will probably need to wait until 7.5 for
integration. You might be able to squeeze it in as a contrib module for
7.4 though.

#5Tom Lane
tgl@sss.pgh.pa.us
In reply to: Noname (#3)
Re: SQL2003 GENERATED ... AS ... syntax

des@des.no (Dag-Erling =?iso-8859-1?q?Sm=F8rgrav?=) writes:

I thought about it, but won't that change the on-disk format? Since
minor version upgrades aren't supposed to require a dump / restore,
and I understand 7.4 is already in feature freeze, the earliest
opportunity for something like this would be 7.5.

The earliest opportunity is 7.5 anyway. We don't do feature additions
in dot-releases, only bug fixes.

I don't much care for a trigger-based implementation of the
functionality; it seems like a crude way to do it. Seems like an easier
answer would involve rejecting attempts to set the column explicitly
during the rewriter stage that inserts default values.

I haven't looked at the SQL200x spec so I'm not sure exactly how they
define GENERATED ALWAYS --- is it an error to try to override the
default, or is your attempt silently ignored? We could do either.

regards, tom lane

#6Tom Lane
tgl@sss.pgh.pa.us
In reply to: Rod Taylor (#4)
Re: SQL2003 GENERATED ... AS ... syntax

Rod Taylor <rbt@rbt.ca> writes:

Yes it would. The solution you have already requires an initdb (changed
pg_proc.h), as such will probably need to wait until 7.5 for
integration. You might be able to squeeze it in as a contrib module for
7.4 though.

Given that it also requires grammar and parser changes, I don't see how
it could exist as contrib at all.

I recommend dropping any thought of this appearing in any form in any
7.4.* release; it just ain't gonna happen that way. Design a clean
solution for 7.5, instead.

regards, tom lane

#7Rod Taylor
rbt@rbt.ca
In reply to: Tom Lane (#6)
Re: SQL2003 GENERATED ... AS ... syntax

On Sun, 2003-08-03 at 14:35, Tom Lane wrote:

Rod Taylor <rbt@rbt.ca> writes:

Yes it would. The solution you have already requires an initdb (changed
pg_proc.h), as such will probably need to wait until 7.5 for
integration. You might be able to squeeze it in as a contrib module for
7.4 though.

Given that it also requires grammar and parser changes, I don't see how
it could exist as contrib at all.

I recommend dropping any thought of this appearing in any form in any
7.4.* release; it just ain't gonna happen that way. Design a clean
solution for 7.5, instead.

I see.. I was thinking of the userlocks module which has a partial
implementation in the backend, with the 'work' units in contrib.