Add Boolean node

Started by Peter Eisentrautabout 4 years ago27 messages
#1Peter Eisentraut
peter.eisentraut@enterprisedb.com
1 attachment(s)

This patch adds a new node type Boolean, to go alongside the "value"
nodes Integer, Float, String, etc. This seems appropriate given that
Boolean values are a fundamental part of the system and are used a lot.

Before, SQL-level Boolean constants were represented by a string with
a cast, and internal Boolean values in DDL commands were usually
represented by Integer nodes. This takes the place of both of these
uses, making the intent clearer and having some amount of type safety.

Attachments:

v1-0001-Add-Boolean-node.patchtext/plain; charset=UTF-8; name=v1-0001-Add-Boolean-node.patchDownload
From 4e1ef56b5443fa11d981eb6e407dfc7c244dc60e Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 27 Dec 2021 09:52:05 +0100
Subject: [PATCH v1] Add Boolean node

Before, SQL-level boolean constants were represented by a string with
a cast, and internal Boolean values in DDL commands were usually
represented by Integer nodes.  This takes the place of both of these
uses, making the intent clearer and having some amount of type safety.
---
 contrib/postgres_fdw/postgres_fdw.c           | 32 +++---
 src/backend/commands/define.c                 |  4 +
 src/backend/commands/functioncmds.c           | 14 +--
 src/backend/commands/sequence.c               |  4 +-
 src/backend/commands/tsearchcmds.c            |  9 ++
 src/backend/commands/user.c                   | 28 +++---
 src/backend/nodes/copyfuncs.c                 | 16 +++
 src/backend/nodes/equalfuncs.c                | 11 +++
 src/backend/nodes/nodeFuncs.c                 |  1 +
 src/backend/nodes/outfuncs.c                  |  8 ++
 src/backend/nodes/read.c                      |  5 +
 src/backend/nodes/value.c                     | 12 +++
 src/backend/parser/gram.y                     | 98 +++++++++----------
 src/backend/parser/parse_node.c               |  8 ++
 src/backend/replication/repl_gram.y           | 14 +--
 src/include/nodes/nodes.h                     |  1 +
 src/include/nodes/parsenodes.h                |  1 +
 src/include/nodes/value.h                     |  8 ++
 src/test/isolation/expected/ri-trigger.out    | 60 ++++++------
 .../regress/expected/create_function_3.out    |  2 +-
 20 files changed, 210 insertions(+), 126 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index fa9a099f13..4d61d88d7b 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -103,7 +103,7 @@ enum FdwModifyPrivateIndex
 	FdwModifyPrivateTargetAttnums,
 	/* Length till the end of VALUES clause (as an Integer node) */
 	FdwModifyPrivateLen,
-	/* has-returning flag (as an Integer node) */
+	/* has-returning flag (as a Boolean node) */
 	FdwModifyPrivateHasReturning,
 	/* Integer list of attribute numbers retrieved by RETURNING */
 	FdwModifyPrivateRetrievedAttrs
@@ -122,11 +122,11 @@ enum FdwDirectModifyPrivateIndex
 {
 	/* SQL statement to execute remotely (as a String node) */
 	FdwDirectModifyPrivateUpdateSql,
-	/* has-returning flag (as an Integer node) */
+	/* has-returning flag (as a Boolean node) */
 	FdwDirectModifyPrivateHasReturning,
 	/* Integer list of attribute numbers retrieved by RETURNING */
 	FdwDirectModifyPrivateRetrievedAttrs,
-	/* set-processed flag (as an Integer node) */
+	/* set-processed flag (as a Boolean node) */
 	FdwDirectModifyPrivateSetProcessed
 };
 
@@ -280,9 +280,9 @@ typedef struct PgFdwAnalyzeState
  */
 enum FdwPathPrivateIndex
 {
-	/* has-final-sort flag (as an Integer node) */
+	/* has-final-sort flag (as a Boolean node) */
 	FdwPathPrivateHasFinalSort,
-	/* has-limit flag (as an Integer node) */
+	/* has-limit flag (as a Boolean node) */
 	FdwPathPrivateHasLimit
 };
 
@@ -1245,9 +1245,9 @@ postgresGetForeignPlan(PlannerInfo *root,
 	 */
 	if (best_path->fdw_private)
 	{
-		has_final_sort = intVal(list_nth(best_path->fdw_private,
+		has_final_sort = boolVal(list_nth(best_path->fdw_private,
 										 FdwPathPrivateHasFinalSort));
-		has_limit = intVal(list_nth(best_path->fdw_private,
+		has_limit = boolVal(list_nth(best_path->fdw_private,
 									FdwPathPrivateHasLimit));
 	}
 
@@ -1879,7 +1879,7 @@ postgresPlanForeignModify(PlannerInfo *root,
 	return list_make5(makeString(sql.data),
 					  targetAttrs,
 					  makeInteger(values_end_len),
-					  makeInteger((retrieved_attrs != NIL)),
+					  makeBoolean((retrieved_attrs != NIL)),
 					  retrieved_attrs);
 }
 
@@ -1916,7 +1916,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate,
 									 FdwModifyPrivateTargetAttnums);
 	values_end_len = intVal(list_nth(fdw_private,
 									 FdwModifyPrivateLen));
-	has_returning = intVal(list_nth(fdw_private,
+	has_returning = boolVal(list_nth(fdw_private,
 									FdwModifyPrivateHasReturning));
 	retrieved_attrs = (List *) list_nth(fdw_private,
 										FdwModifyPrivateRetrievedAttrs);
@@ -2567,9 +2567,9 @@ postgresPlanDirectModify(PlannerInfo *root,
 	 * Items in the list must match enum FdwDirectModifyPrivateIndex, above.
 	 */
 	fscan->fdw_private = list_make4(makeString(sql.data),
-									makeInteger((retrieved_attrs != NIL)),
+									makeBoolean((retrieved_attrs != NIL)),
 									retrieved_attrs,
-									makeInteger(plan->canSetTag));
+									makeBoolean(plan->canSetTag));
 
 	/*
 	 * Update the foreign-join-related fields.
@@ -2667,11 +2667,11 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags)
 	/* Get private info created by planner functions. */
 	dmstate->query = strVal(list_nth(fsplan->fdw_private,
 									 FdwDirectModifyPrivateUpdateSql));
-	dmstate->has_returning = intVal(list_nth(fsplan->fdw_private,
+	dmstate->has_returning = boolVal(list_nth(fsplan->fdw_private,
 											 FdwDirectModifyPrivateHasReturning));
 	dmstate->retrieved_attrs = (List *) list_nth(fsplan->fdw_private,
 												 FdwDirectModifyPrivateRetrievedAttrs);
-	dmstate->set_processed = intVal(list_nth(fsplan->fdw_private,
+	dmstate->set_processed = boolVal(list_nth(fsplan->fdw_private,
 											 FdwDirectModifyPrivateSetProcessed));
 
 	/* Create context for per-tuple temp workspace. */
@@ -6566,7 +6566,7 @@ add_foreign_ordered_paths(PlannerInfo *root, RelOptInfo *input_rel,
 	 * Build the fdw_private list that will be used by postgresGetForeignPlan.
 	 * Items in the list must match order in enum FdwPathPrivateIndex.
 	 */
-	fdw_private = list_make2(makeInteger(true), makeInteger(false));
+	fdw_private = list_make2(makeBoolean(true), makeBoolean(false));
 
 	/* Create foreign ordering path */
 	ordered_path = create_foreign_upper_path(root,
@@ -6797,8 +6797,8 @@ add_foreign_final_paths(PlannerInfo *root, RelOptInfo *input_rel,
 	 * Build the fdw_private list that will be used by postgresGetForeignPlan.
 	 * Items in the list must match order in enum FdwPathPrivateIndex.
 	 */
-	fdw_private = list_make2(makeInteger(has_final_sort),
-							 makeInteger(extra->limit_needed));
+	fdw_private = list_make2(makeBoolean(has_final_sort),
+							 makeBoolean(extra->limit_needed));
 
 	/*
 	 * Create foreign final path; this gets rid of a no-longer-needed outer
diff --git a/src/backend/commands/define.c b/src/backend/commands/define.c
index 19c317a472..fbf5168649 100644
--- a/src/backend/commands/define.c
+++ b/src/backend/commands/define.c
@@ -59,6 +59,8 @@ defGetString(DefElem *def)
 			return psprintf("%ld", (long) intVal(def->arg));
 		case T_Float:
 			return castNode(Float, def->arg)->val;
+		case T_Boolean:
+			return boolVal(def->arg) ? "true" : "false";
 		case T_String:
 			return strVal(def->arg);
 		case T_TypeName:
@@ -116,6 +118,8 @@ defGetBoolean(DefElem *def)
 	 */
 	switch (nodeTag(def->arg))
 	{
+		case T_Boolean:
+			return boolVal(def->arg);
 		case T_Integer:
 			switch (intVal(def->arg))
 			{
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 38e78f7102..ba5b23fb11 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -813,15 +813,15 @@ compute_function_attributes(ParseState *pstate,
 	if (transform_item)
 		*transform = transform_item->arg;
 	if (windowfunc_item)
-		*windowfunc_p = intVal(windowfunc_item->arg);
+		*windowfunc_p = boolVal(windowfunc_item->arg);
 	if (volatility_item)
 		*volatility_p = interpret_func_volatility(volatility_item);
 	if (strict_item)
-		*strict_p = intVal(strict_item->arg);
+		*strict_p = boolVal(strict_item->arg);
 	if (security_item)
-		*security_definer = intVal(security_item->arg);
+		*security_definer = boolVal(security_item->arg);
 	if (leakproof_item)
-		*leakproof_p = intVal(leakproof_item->arg);
+		*leakproof_p = boolVal(leakproof_item->arg);
 	if (set_items)
 		*proconfig = update_proconfig_value(NULL, set_items);
 	if (cost_item)
@@ -1417,12 +1417,12 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
 	if (volatility_item)
 		procForm->provolatile = interpret_func_volatility(volatility_item);
 	if (strict_item)
-		procForm->proisstrict = intVal(strict_item->arg);
+		procForm->proisstrict = boolVal(strict_item->arg);
 	if (security_def_item)
-		procForm->prosecdef = intVal(security_def_item->arg);
+		procForm->prosecdef = boolVal(security_def_item->arg);
 	if (leakproof_item)
 	{
-		procForm->proleakproof = intVal(leakproof_item->arg);
+		procForm->proleakproof = boolVal(leakproof_item->arg);
 		if (procForm->proleakproof && !superuser())
 			ereport(ERROR,
 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 72bfdc07a4..81368b8b2f 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -1401,7 +1401,7 @@ init_params(ParseState *pstate, List *options, bool for_identity,
 	/* CYCLE */
 	if (is_cycled != NULL)
 	{
-		seqform->seqcycle = intVal(is_cycled->arg);
+		seqform->seqcycle = boolVal(is_cycled->arg);
 		Assert(BoolIsValid(seqform->seqcycle));
 		seqdataform->log_cnt = 0;
 	}
@@ -1739,7 +1739,7 @@ sequence_options(Oid relid)
 	options = lappend(options,
 					  makeDefElem("cache", (Node *) makeFloat(psprintf(INT64_FORMAT, pgsform->seqcache)), -1));
 	options = lappend(options,
-					  makeDefElem("cycle", (Node *) makeInteger(pgsform->seqcycle), -1));
+					  makeDefElem("cycle", (Node *) makeBoolean(pgsform->seqcycle), -1));
 	options = lappend(options,
 					  makeDefElem("increment", (Node *) makeFloat(psprintf(INT64_FORMAT, pgsform->seqincrement)), -1));
 	options = lappend(options,
diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c
index c47a05d10d..b7261a88d4 100644
--- a/src/backend/commands/tsearchcmds.c
+++ b/src/backend/commands/tsearchcmds.c
@@ -1742,6 +1742,15 @@ buildDefItem(const char *name, const char *val, bool was_quoted)
 			return makeDefElem(pstrdup(name),
 							   (Node *) makeFloat(pstrdup(val)),
 							   -1);
+
+		if (strcmp(val, "true") == 0)
+			return makeDefElem(pstrdup(name),
+							   (Node *) makeBoolean(true),
+							   -1);
+		if (strcmp(val, "false") == 0)
+			return makeDefElem(pstrdup(name),
+							   (Node *) makeBoolean(false),
+							   -1);
 	}
 	/* Just make it a string */
 	return makeDefElem(pstrdup(name),
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index c8c0dd0dd5..59ee671e8d 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -217,17 +217,17 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
 	if (dpassword && dpassword->arg)
 		password = strVal(dpassword->arg);
 	if (dissuper)
-		issuper = intVal(dissuper->arg) != 0;
+		issuper = boolVal(dissuper->arg) != 0;
 	if (dinherit)
-		inherit = intVal(dinherit->arg) != 0;
+		inherit = boolVal(dinherit->arg) != 0;
 	if (dcreaterole)
-		createrole = intVal(dcreaterole->arg) != 0;
+		createrole = boolVal(dcreaterole->arg) != 0;
 	if (dcreatedb)
-		createdb = intVal(dcreatedb->arg) != 0;
+		createdb = boolVal(dcreatedb->arg) != 0;
 	if (dcanlogin)
-		canlogin = intVal(dcanlogin->arg) != 0;
+		canlogin = boolVal(dcanlogin->arg) != 0;
 	if (disreplication)
-		isreplication = intVal(disreplication->arg) != 0;
+		isreplication = boolVal(disreplication->arg) != 0;
 	if (dconnlimit)
 	{
 		connlimit = intVal(dconnlimit->arg);
@@ -245,7 +245,7 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
 	if (dvalidUntil)
 		validUntil = strVal(dvalidUntil->arg);
 	if (dbypassRLS)
-		bypassrls = intVal(dbypassRLS->arg) != 0;
+		bypassrls = boolVal(dbypassRLS->arg) != 0;
 
 	/* Check some permissions first */
 	if (issuper)
@@ -611,17 +611,17 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
 	if (dpassword && dpassword->arg)
 		password = strVal(dpassword->arg);
 	if (dissuper)
-		issuper = intVal(dissuper->arg);
+		issuper = boolVal(dissuper->arg);
 	if (dinherit)
-		inherit = intVal(dinherit->arg);
+		inherit = boolVal(dinherit->arg);
 	if (dcreaterole)
-		createrole = intVal(dcreaterole->arg);
+		createrole = boolVal(dcreaterole->arg);
 	if (dcreatedb)
-		createdb = intVal(dcreatedb->arg);
+		createdb = boolVal(dcreatedb->arg);
 	if (dcanlogin)
-		canlogin = intVal(dcanlogin->arg);
+		canlogin = boolVal(dcanlogin->arg);
 	if (disreplication)
-		isreplication = intVal(disreplication->arg);
+		isreplication = boolVal(disreplication->arg);
 	if (dconnlimit)
 	{
 		connlimit = intVal(dconnlimit->arg);
@@ -635,7 +635,7 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
 	if (dvalidUntil)
 		validUntil = strVal(dvalidUntil->arg);
 	if (dbypassRLS)
-		bypassrls = intVal(dbypassRLS->arg);
+		bypassrls = boolVal(dbypassRLS->arg);
 
 	/*
 	 * Scan the pg_authid relation to be certain the user exists.
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index df0b747883..2924a6c81b 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2749,6 +2749,9 @@ _copyA_Const(const A_Const *from)
 			case T_Float:
 				COPY_STRING_FIELD(val.fval.val);
 				break;
+			case T_Boolean:
+				COPY_SCALAR_FIELD(val.boolval.val);
+				break;
 			case T_String:
 				COPY_STRING_FIELD(val.sval.val);
 				break;
@@ -4948,6 +4951,16 @@ _copyFloat(const Float *from)
 	return newnode;
 }
 
+static Boolean *
+_copyBoolean(const Boolean *from)
+{
+	Boolean	   *newnode = makeNode(Boolean);
+
+	COPY_SCALAR_FIELD(val);
+
+	return newnode;
+}
+
 static String *
 _copyString(const String *from)
 {
@@ -5355,6 +5368,9 @@ copyObjectImpl(const void *from)
 		case T_Float:
 			retval = _copyFloat(from);
 			break;
+		case T_Boolean:
+			retval = _copyBoolean(from);
+			break;
 		case T_String:
 			retval = _copyString(from);
 			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index cb7ddd463c..36a003bed0 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -3138,6 +3138,14 @@ _equalFloat(const Float *a, const Float *b)
 	return true;
 }
 
+static bool
+_equalBoolean(const Boolean *a, const Boolean *b)
+{
+	COMPARE_SCALAR_FIELD(val);
+
+	return true;
+}
+
 static bool
 _equalString(const String *a, const String *b)
 {
@@ -3374,6 +3382,9 @@ equal(const void *a, const void *b)
 		case T_Float:
 			retval = _equalFloat(a, b);
 			break;
+		case T_Boolean:
+			retval = _equalBoolean(a, b);
+			break;
 		case T_String:
 			retval = _equalString(a, b);
 			break;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index e276264882..397aa6de3c 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -3535,6 +3535,7 @@ raw_expression_tree_walker(Node *node,
 		case T_SQLValueFunction:
 		case T_Integer:
 		case T_Float:
+		case T_Boolean:
 		case T_String:
 		case T_BitString:
 		case T_ParamRef:
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 91a89b6d51..c100215f40 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -3433,6 +3433,12 @@ _outFloat(StringInfo str, const Float *node)
 	appendStringInfoString(str, node->val);
 }
 
+static void
+_outBoolean(StringInfo str, const Boolean *node)
+{
+	appendStringInfoString(str, node->val ? "true" : "false");
+}
+
 static void
 _outString(StringInfo str, const String *node)
 {
@@ -3845,6 +3851,8 @@ outNode(StringInfo str, const void *obj)
 		_outInteger(str, (Integer *) obj);
 	else if (IsA(obj, Float))
 		_outFloat(str, (Float *) obj);
+	else if (IsA(obj, Boolean))
+		_outBoolean(str, (Boolean *) obj);
 	else if (IsA(obj, String))
 		_outString(str, (String *) obj);
 	else if (IsA(obj, BitString))
diff --git a/src/backend/nodes/read.c b/src/backend/nodes/read.c
index d281f7db6c..6a82197687 100644
--- a/src/backend/nodes/read.c
+++ b/src/backend/nodes/read.c
@@ -283,6 +283,8 @@ nodeTokenType(const char *token, int length)
 		retval = RIGHT_PAREN;
 	else if (*token == '{')
 		retval = LEFT_BRACE;
+	else if (strcmp(token, "true") == 0 || strcmp(token, "false") == 0)
+		retval = T_Boolean;
 	else if (*token == '"' && length > 1 && token[length - 1] == '"')
 		retval = T_String;
 	else if (*token == 'b')
@@ -438,6 +440,9 @@ nodeRead(const char *token, int tok_len)
 				result = (Node *) makeFloat(fval);
 			}
 			break;
+		case T_Boolean:
+			result = (Node *) makeBoolean(token[0] == 't');
+			break;
 		case T_String:
 			/* need to remove leading and trailing quotes, and backslashes */
 			result = (Node *) makeString(debackslash(token + 1, tok_len - 2));
diff --git a/src/backend/nodes/value.c b/src/backend/nodes/value.c
index 515f93c223..42c2f7199c 100644
--- a/src/backend/nodes/value.c
+++ b/src/backend/nodes/value.c
@@ -42,6 +42,18 @@ makeFloat(char *numericStr)
 	return v;
 }
 
+/*
+ *	makeBoolean
+ */
+Boolean *
+makeBoolean(bool val)
+{
+	Boolean	   *v = makeNode(Boolean);
+
+	v->val = val;
+	return v;
+}
+
 /*
  *	makeString
  *
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 3d4dd43e47..ce06100c63 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -177,10 +177,10 @@ static Node *makeStringConst(char *str, int location);
 static Node *makeStringConstCast(char *str, int location, TypeName *typename);
 static Node *makeIntConst(int val, int location);
 static Node *makeFloatConst(char *str, int location);
+static Node *makeBoolAConst(bool state, int location);
 static Node *makeBitStringConst(char *str, int location);
 static Node *makeNullAConst(int location);
 static Node *makeAConst(Node *v, int location);
-static Node *makeBoolAConst(bool state, int location);
 static RoleSpec *makeRoleSpec(RoleSpecType type, int location);
 static void check_qualified_name(List *names, core_yyscan_t yyscanner);
 static List *check_func_name(List *names, core_yyscan_t yyscanner);
@@ -1133,7 +1133,7 @@ AlterOptRoleElem:
 				}
 			| INHERIT
 				{
-					$$ = makeDefElem("inherit", (Node *)makeInteger(true), @1);
+					$$ = makeDefElem("inherit", (Node *)makeBoolean(true), @1);
 				}
 			| CONNECTION LIMIT SignedIconst
 				{
@@ -1156,36 +1156,36 @@ AlterOptRoleElem:
 					 * size of the main parser.
 					 */
 					if (strcmp($1, "superuser") == 0)
-						$$ = makeDefElem("superuser", (Node *)makeInteger(true), @1);
+						$$ = makeDefElem("superuser", (Node *)makeBoolean(true), @1);
 					else if (strcmp($1, "nosuperuser") == 0)
-						$$ = makeDefElem("superuser", (Node *)makeInteger(false), @1);
+						$$ = makeDefElem("superuser", (Node *)makeBoolean(false), @1);
 					else if (strcmp($1, "createrole") == 0)
-						$$ = makeDefElem("createrole", (Node *)makeInteger(true), @1);
+						$$ = makeDefElem("createrole", (Node *)makeBoolean(true), @1);
 					else if (strcmp($1, "nocreaterole") == 0)
-						$$ = makeDefElem("createrole", (Node *)makeInteger(false), @1);
+						$$ = makeDefElem("createrole", (Node *)makeBoolean(false), @1);
 					else if (strcmp($1, "replication") == 0)
-						$$ = makeDefElem("isreplication", (Node *)makeInteger(true), @1);
+						$$ = makeDefElem("isreplication", (Node *)makeBoolean(true), @1);
 					else if (strcmp($1, "noreplication") == 0)
-						$$ = makeDefElem("isreplication", (Node *)makeInteger(false), @1);
+						$$ = makeDefElem("isreplication", (Node *)makeBoolean(false), @1);
 					else if (strcmp($1, "createdb") == 0)
-						$$ = makeDefElem("createdb", (Node *)makeInteger(true), @1);
+						$$ = makeDefElem("createdb", (Node *)makeBoolean(true), @1);
 					else if (strcmp($1, "nocreatedb") == 0)
-						$$ = makeDefElem("createdb", (Node *)makeInteger(false), @1);
+						$$ = makeDefElem("createdb", (Node *)makeBoolean(false), @1);
 					else if (strcmp($1, "login") == 0)
-						$$ = makeDefElem("canlogin", (Node *)makeInteger(true), @1);
+						$$ = makeDefElem("canlogin", (Node *)makeBoolean(true), @1);
 					else if (strcmp($1, "nologin") == 0)
-						$$ = makeDefElem("canlogin", (Node *)makeInteger(false), @1);
+						$$ = makeDefElem("canlogin", (Node *)makeBoolean(false), @1);
 					else if (strcmp($1, "bypassrls") == 0)
-						$$ = makeDefElem("bypassrls", (Node *)makeInteger(true), @1);
+						$$ = makeDefElem("bypassrls", (Node *)makeBoolean(true), @1);
 					else if (strcmp($1, "nobypassrls") == 0)
-						$$ = makeDefElem("bypassrls", (Node *)makeInteger(false), @1);
+						$$ = makeDefElem("bypassrls", (Node *)makeBoolean(false), @1);
 					else if (strcmp($1, "noinherit") == 0)
 					{
 						/*
 						 * Note that INHERIT is a keyword, so it's handled by main parser, but
 						 * NOINHERIT is handled here.
 						 */
-						$$ = makeDefElem("inherit", (Node *)makeInteger(false), @1);
+						$$ = makeDefElem("inherit", (Node *)makeBoolean(false), @1);
 					}
 					else
 						ereport(ERROR,
@@ -3175,7 +3175,7 @@ copy_opt_item:
 				}
 			| FREEZE
 				{
-					$$ = makeDefElem("freeze", (Node *)makeInteger(true), @1);
+					$$ = makeDefElem("freeze", (Node *)makeBoolean(true), @1);
 				}
 			| DELIMITER opt_as Sconst
 				{
@@ -3191,7 +3191,7 @@ copy_opt_item:
 				}
 			| HEADER_P
 				{
-					$$ = makeDefElem("header", (Node *)makeInteger(true), @1);
+					$$ = makeDefElem("header", (Node *)makeBoolean(true), @1);
 				}
 			| QUOTE opt_as Sconst
 				{
@@ -4499,11 +4499,11 @@ SeqOptElem: AS SimpleTypename
 				}
 			| CYCLE
 				{
-					$$ = makeDefElem("cycle", (Node *)makeInteger(true), @1);
+					$$ = makeDefElem("cycle", (Node *)makeBoolean(true), @1);
 				}
 			| NO CYCLE
 				{
-					$$ = makeDefElem("cycle", (Node *)makeInteger(false), @1);
+					$$ = makeDefElem("cycle", (Node *)makeBoolean(false), @1);
 				}
 			| INCREMENT opt_by NumericOnly
 				{
@@ -4739,7 +4739,7 @@ create_extension_opt_item:
 				}
 			| CASCADE
 				{
-					$$ = makeDefElem("cascade", (Node *)makeInteger(true), @1);
+					$$ = makeDefElem("cascade", (Node *)makeBoolean(true), @1);
 				}
 		;
 
@@ -7936,15 +7936,15 @@ createfunc_opt_list:
 common_func_opt_item:
 			CALLED ON NULL_P INPUT_P
 				{
-					$$ = makeDefElem("strict", (Node *)makeInteger(false), @1);
+					$$ = makeDefElem("strict", (Node *)makeBoolean(false), @1);
 				}
 			| RETURNS NULL_P ON NULL_P INPUT_P
 				{
-					$$ = makeDefElem("strict", (Node *)makeInteger(true), @1);
+					$$ = makeDefElem("strict", (Node *)makeBoolean(true), @1);
 				}
 			| STRICT_P
 				{
-					$$ = makeDefElem("strict", (Node *)makeInteger(true), @1);
+					$$ = makeDefElem("strict", (Node *)makeBoolean(true), @1);
 				}
 			| IMMUTABLE
 				{
@@ -7960,27 +7960,27 @@ common_func_opt_item:
 				}
 			| EXTERNAL SECURITY DEFINER
 				{
-					$$ = makeDefElem("security", (Node *)makeInteger(true), @1);
+					$$ = makeDefElem("security", (Node *)makeBoolean(true), @1);
 				}
 			| EXTERNAL SECURITY INVOKER
 				{
-					$$ = makeDefElem("security", (Node *)makeInteger(false), @1);
+					$$ = makeDefElem("security", (Node *)makeBoolean(false), @1);
 				}
 			| SECURITY DEFINER
 				{
-					$$ = makeDefElem("security", (Node *)makeInteger(true), @1);
+					$$ = makeDefElem("security", (Node *)makeBoolean(true), @1);
 				}
 			| SECURITY INVOKER
 				{
-					$$ = makeDefElem("security", (Node *)makeInteger(false), @1);
+					$$ = makeDefElem("security", (Node *)makeBoolean(false), @1);
 				}
 			| LEAKPROOF
 				{
-					$$ = makeDefElem("leakproof", (Node *)makeInteger(true), @1);
+					$$ = makeDefElem("leakproof", (Node *)makeBoolean(true), @1);
 				}
 			| NOT LEAKPROOF
 				{
-					$$ = makeDefElem("leakproof", (Node *)makeInteger(false), @1);
+					$$ = makeDefElem("leakproof", (Node *)makeBoolean(false), @1);
 				}
 			| COST NumericOnly
 				{
@@ -8020,7 +8020,7 @@ createfunc_opt_item:
 				}
 			| WINDOW
 				{
-					$$ = makeDefElem("window", (Node *)makeInteger(true), @1);
+					$$ = makeDefElem("window", (Node *)makeBoolean(true), @1);
 				}
 			| common_func_opt_item
 				{
@@ -9943,7 +9943,7 @@ AlterSubscriptionStmt:
 					n->kind = ALTER_SUBSCRIPTION_ENABLED;
 					n->subname = $3;
 					n->options = list_make1(makeDefElem("enabled",
-											(Node *)makeInteger(true), @1));
+											(Node *)makeBoolean(true), @1));
 					$$ = (Node *)n;
 				}
 			| ALTER SUBSCRIPTION name DISABLE_P
@@ -9953,7 +9953,7 @@ AlterSubscriptionStmt:
 					n->kind = ALTER_SUBSCRIPTION_ENABLED;
 					n->subname = $3;
 					n->options = list_make1(makeDefElem("enabled",
-											(Node *)makeInteger(false), @1));
+											(Node *)makeBoolean(false), @1));
 					$$ = (Node *)n;
 				}
 		;
@@ -12876,7 +12876,7 @@ xmltable_column_el:
 										(errcode(ERRCODE_SYNTAX_ERROR),
 										 errmsg("conflicting or redundant NULL / NOT NULL declarations for column \"%s\"", fc->colname),
 										 parser_errposition(defel->location)));
-							fc->is_not_null = intVal(defel->arg);
+							fc->is_not_null = boolVal(defel->arg);
 							nullability_seen = true;
 						}
 						else
@@ -12916,9 +12916,9 @@ xmltable_column_option_el:
 			| DEFAULT b_expr
 				{ $$ = makeDefElem("default", $2, @1); }
 			| NOT NULL_P
-				{ $$ = makeDefElem("is_not_null", (Node *) makeInteger(true), @1); }
+				{ $$ = makeDefElem("is_not_null", (Node *) makeBoolean(true), @1); }
 			| NULL_P
-				{ $$ = makeDefElem("is_not_null", (Node *) makeInteger(false), @1); }
+				{ $$ = makeDefElem("is_not_null", (Node *) makeBoolean(false), @1); }
 		;
 
 xml_namespace_list:
@@ -16707,6 +16707,18 @@ makeFloatConst(char *str, int location)
    return (Node *)n;
 }
 
+static Node *
+makeBoolAConst(bool state, int location)
+{
+	A_Const *n = makeNode(A_Const);
+
+	n->val.boolval.type = T_Boolean;
+	n->val.boolval.val = state;
+	n->location = location;
+
+   return (Node *)n;
+}
+
 static Node *
 makeBitStringConst(char *str, int location)
 {
@@ -16745,26 +16757,14 @@ makeAConst(Node *v, int location)
 			n = makeIntConst(castNode(Integer, v)->val, location);
 			break;
 
-		case T_String:
 		default:
-			n = makeStringConst(castNode(String, v)->val, location);
-			break;
+			/* currently not used */
+			Assert(false);
 	}
 
 	return n;
 }
 
-/* makeBoolAConst()
- * Create an A_Const string node and put it inside a boolean cast.
- */
-static Node *
-makeBoolAConst(bool state, int location)
-{
-	return makeStringConstCast((state ? "t" : "f"),
-							   location,
-							   SystemTypeName("bool"));
-}
-
 /* makeRoleSpec
  * Create a RoleSpec with the given type
  */
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c
index 8cfe6f67c0..f016c61b49 100644
--- a/src/backend/parser/parse_node.c
+++ b/src/backend/parser/parse_node.c
@@ -426,6 +426,14 @@ make_const(ParseState *pstate, A_Const *aconst)
 			}
 			break;
 
+		case T_Boolean:
+			val = BoolGetDatum(aconst->val.boolval.val);
+
+			typeid = BOOLOID;
+			typelen = 1;
+			typebyval = true;
+			break;
+
 		case T_String:
 
 			/*
diff --git a/src/backend/replication/repl_gram.y b/src/backend/replication/repl_gram.y
index dcb1108579..dd23f98b44 100644
--- a/src/backend/replication/repl_gram.y
+++ b/src/backend/replication/repl_gram.y
@@ -212,7 +212,7 @@ base_backup_legacy_opt:
 			| K_PROGRESS
 				{
 				  $$ = makeDefElem("progress",
-								   (Node *)makeInteger(true), -1);
+								   (Node *)makeBoolean(true), -1);
 				}
 			| K_FAST
 				{
@@ -222,12 +222,12 @@ base_backup_legacy_opt:
 			| K_WAL
 				{
 				  $$ = makeDefElem("wal",
-								   (Node *)makeInteger(true), -1);
+								   (Node *)makeBoolean(true), -1);
 				}
 			| K_NOWAIT
 				{
 				  $$ = makeDefElem("wait",
-								   (Node *)makeInteger(false), -1);
+								   (Node *)makeBoolean(false), -1);
 				}
 			| K_MAX_RATE UCONST
 				{
@@ -237,12 +237,12 @@ base_backup_legacy_opt:
 			| K_TABLESPACE_MAP
 				{
 				  $$ = makeDefElem("tablespace_map",
-								   (Node *)makeInteger(true), -1);
+								   (Node *)makeBoolean(true), -1);
 				}
 			| K_NOVERIFY_CHECKSUMS
 				{
 				  $$ = makeDefElem("verify_checksums",
-								   (Node *)makeInteger(false), -1);
+								   (Node *)makeBoolean(false), -1);
 				}
 			| K_MANIFEST SCONST
 				{
@@ -313,12 +313,12 @@ create_slot_legacy_opt:
 			| K_RESERVE_WAL
 				{
 				  $$ = makeDefElem("reserve_wal",
-								   (Node *)makeInteger(true), -1);
+								   (Node *)makeBoolean(true), -1);
 				}
 			| K_TWO_PHASE
 				{
 				  $$ = makeDefElem("two_phase",
-								   (Node *)makeInteger(true), -1);
+								   (Node *)makeBoolean(true), -1);
 				}
 			;
 
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 7c657c1241..15444c60b7 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -292,6 +292,7 @@ typedef enum NodeTag
 	 */
 	T_Integer,
 	T_Float,
+	T_Boolean,
 	T_String,
 	T_BitString,
 
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 4c5a8a39bf..9f3fd13371 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -304,6 +304,7 @@ typedef struct A_Const
 		Node		node;
 		Integer		ival;
 		Float		fval;
+		Boolean		boolval;
 		String		sval;
 		BitString	bsval;
 	}			val;
diff --git a/src/include/nodes/value.h b/src/include/nodes/value.h
index 8b71b510eb..6630518c54 100644
--- a/src/include/nodes/value.h
+++ b/src/include/nodes/value.h
@@ -48,6 +48,12 @@ typedef struct Float
 	char	   *val;
 } Float;
 
+typedef struct Boolean
+{
+	NodeTag		type;
+	bool		val;
+} Boolean;
+
 typedef struct String
 {
 	NodeTag		type;
@@ -62,10 +68,12 @@ typedef struct BitString
 
 #define intVal(v)		(castNode(Integer, v)->val)
 #define floatVal(v)		atof(castNode(Float, v)->val)
+#define boolVal(v)		(castNode(Boolean, v)->val)
 #define strVal(v)		(castNode(String, v)->val)
 
 extern Integer *makeInteger(int i);
 extern Float *makeFloat(char *numericStr);
+extern Boolean *makeBoolean(bool var);
 extern String *makeString(char *str);
 extern BitString *makeBitString(char *str);
 
diff --git a/src/test/isolation/expected/ri-trigger.out b/src/test/isolation/expected/ri-trigger.out
index 842df80a90..db85618bef 100644
--- a/src/test/isolation/expected/ri-trigger.out
+++ b/src/test/isolation/expected/ri-trigger.out
@@ -4,9 +4,9 @@ starting permutation: wxry1 c1 r2 wyrx2 c2
 step wxry1: INSERT INTO child (parent_id) VALUES (0);
 step c1: COMMIT;
 step r2: SELECT TRUE;
-bool
-----
-t   
+?column?
+--------
+t       
 (1 row)
 
 step wyrx2: DELETE FROM parent WHERE parent_id = 0;
@@ -16,9 +16,9 @@ step c2: COMMIT;
 starting permutation: wxry1 r2 c1 wyrx2 c2
 step wxry1: INSERT INTO child (parent_id) VALUES (0);
 step r2: SELECT TRUE;
-bool
-----
-t   
+?column?
+--------
+t       
 (1 row)
 
 step c1: COMMIT;
@@ -29,9 +29,9 @@ step c2: COMMIT;
 starting permutation: wxry1 r2 wyrx2 c1 c2
 step wxry1: INSERT INTO child (parent_id) VALUES (0);
 step r2: SELECT TRUE;
-bool
-----
-t   
+?column?
+--------
+t       
 (1 row)
 
 step wyrx2: DELETE FROM parent WHERE parent_id = 0;
@@ -42,9 +42,9 @@ ERROR:  could not serialize access due to read/write dependencies among transact
 starting permutation: wxry1 r2 wyrx2 c2 c1
 step wxry1: INSERT INTO child (parent_id) VALUES (0);
 step r2: SELECT TRUE;
-bool
-----
-t   
+?column?
+--------
+t       
 (1 row)
 
 step wyrx2: DELETE FROM parent WHERE parent_id = 0;
@@ -54,9 +54,9 @@ ERROR:  could not serialize access due to read/write dependencies among transact
 
 starting permutation: r2 wxry1 c1 wyrx2 c2
 step r2: SELECT TRUE;
-bool
-----
-t   
+?column?
+--------
+t       
 (1 row)
 
 step wxry1: INSERT INTO child (parent_id) VALUES (0);
@@ -67,9 +67,9 @@ step c2: COMMIT;
 
 starting permutation: r2 wxry1 wyrx2 c1 c2
 step r2: SELECT TRUE;
-bool
-----
-t   
+?column?
+--------
+t       
 (1 row)
 
 step wxry1: INSERT INTO child (parent_id) VALUES (0);
@@ -80,9 +80,9 @@ ERROR:  could not serialize access due to read/write dependencies among transact
 
 starting permutation: r2 wxry1 wyrx2 c2 c1
 step r2: SELECT TRUE;
-bool
-----
-t   
+?column?
+--------
+t       
 (1 row)
 
 step wxry1: INSERT INTO child (parent_id) VALUES (0);
@@ -93,9 +93,9 @@ ERROR:  could not serialize access due to read/write dependencies among transact
 
 starting permutation: r2 wyrx2 wxry1 c1 c2
 step r2: SELECT TRUE;
-bool
-----
-t   
+?column?
+--------
+t       
 (1 row)
 
 step wyrx2: DELETE FROM parent WHERE parent_id = 0;
@@ -106,9 +106,9 @@ ERROR:  could not serialize access due to read/write dependencies among transact
 
 starting permutation: r2 wyrx2 wxry1 c2 c1
 step r2: SELECT TRUE;
-bool
-----
-t   
+?column?
+--------
+t       
 (1 row)
 
 step wyrx2: DELETE FROM parent WHERE parent_id = 0;
@@ -119,9 +119,9 @@ ERROR:  could not serialize access due to read/write dependencies among transact
 
 starting permutation: r2 wyrx2 c2 wxry1 c1
 step r2: SELECT TRUE;
-bool
-----
-t   
+?column?
+--------
+t       
 (1 row)
 
 step wyrx2: DELETE FROM parent WHERE parent_id = 0;
diff --git a/src/test/regress/expected/create_function_3.out b/src/test/regress/expected/create_function_3.out
index 3a4fd45147..e0c4bee893 100644
--- a/src/test/regress/expected/create_function_3.out
+++ b/src/test/regress/expected/create_function_3.out
@@ -403,7 +403,7 @@ SELECT pg_get_functiondef('functest_S_13'::regproc);
   LANGUAGE sql                                            +
  BEGIN ATOMIC                                             +
   SELECT 1;                                               +
-  SELECT false AS bool;                                   +
+  SELECT false;                                           +
  END                                                      +
  
 (1 row)

base-commit: 86d9888d2ead04a1a139bbaef9d7f4648022fe4b
-- 
2.34.1

#2Pavel Stehule
pavel.stehule@gmail.com
In reply to: Peter Eisentraut (#1)
Re: Add Boolean node

po 27. 12. 2021 v 10:02 odesílatel Peter Eisentraut <
peter.eisentraut@enterprisedb.com> napsal:

This patch adds a new node type Boolean, to go alongside the "value"
nodes Integer, Float, String, etc. This seems appropriate given that
Boolean values are a fundamental part of the system and are used a lot.

Before, SQL-level Boolean constants were represented by a string with
a cast, and internal Boolean values in DDL commands were usually
represented by Integer nodes. This takes the place of both of these
uses, making the intent clearer and having some amount of type safety.

+1

Regards

Pavel

#3Sascha Kuhl
yogidabanli@gmail.com
In reply to: Pavel Stehule (#2)
Re: Add Boolean node

Can that boolean node be cultural dependent validation for the value? By
the developer? By all?

Pavel Stehule <pavel.stehule@gmail.com> schrieb am Mo., 27. Dez. 2021,
10:09:

Show quoted text

po 27. 12. 2021 v 10:02 odesílatel Peter Eisentraut <
peter.eisentraut@enterprisedb.com> napsal:

This patch adds a new node type Boolean, to go alongside the "value"
nodes Integer, Float, String, etc. This seems appropriate given that
Boolean values are a fundamental part of the system and are used a lot.

Before, SQL-level Boolean constants were represented by a string with
a cast, and internal Boolean values in DDL commands were usually
represented by Integer nodes. This takes the place of both of these
uses, making the intent clearer and having some amount of type safety.

+1

Regards

Pavel

#4Pavel Stehule
pavel.stehule@gmail.com
In reply to: Sascha Kuhl (#3)
Re: Add Boolean node

po 27. 12. 2021 v 11:08 odesílatel Sascha Kuhl <yogidabanli@gmail.com>
napsal:

Can that boolean node be cultural dependent validation for the value? By
the developer? By all?

why?

The boolean node is not a boolean type.

This is an internal feature. There should not be any cultural dependency

Regards

Pavel

Show quoted text

Pavel Stehule <pavel.stehule@gmail.com> schrieb am Mo., 27. Dez. 2021,
10:09:

po 27. 12. 2021 v 10:02 odesílatel Peter Eisentraut <
peter.eisentraut@enterprisedb.com> napsal:

This patch adds a new node type Boolean, to go alongside the "value"
nodes Integer, Float, String, etc. This seems appropriate given that
Boolean values are a fundamental part of the system and are used a lot.

Before, SQL-level Boolean constants were represented by a string with
a cast, and internal Boolean values in DDL commands were usually
represented by Integer nodes. This takes the place of both of these
uses, making the intent clearer and having some amount of type safety.

+1

Regards

Pavel

#5Julien Rouhaud
rjuju123@gmail.com
In reply to: Pavel Stehule (#2)
Re: Add Boolean node

On Mon, Dec 27, 2021 at 5:09 PM Pavel Stehule <pavel.stehule@gmail.com> wrote:

po 27. 12. 2021 v 10:02 odesílatel Peter Eisentraut <peter.eisentraut@enterprisedb.com> napsal:

This patch adds a new node type Boolean, to go alongside the "value"
nodes Integer, Float, String, etc. This seems appropriate given that
Boolean values are a fundamental part of the system and are used a lot.

Before, SQL-level Boolean constants were represented by a string with
a cast, and internal Boolean values in DDL commands were usually
represented by Integer nodes. This takes the place of both of these
uses, making the intent clearer and having some amount of type safety.

+1

+1 too, looks like a good improvement. The patch looks good to me,
although it's missing comment updates for at least nodeTokenType() and
nodeRead().

#6Sascha Kuhl
yogidabanli@gmail.com
In reply to: Pavel Stehule (#4)
Re: Add Boolean node

You think, all values are valid. Is a higher german order valid for Turkey,
that only know baskets, as a Form of order. For me not all forms of all are
valid for all. You cannot Export or Import food that You dislike, because
it would hurt you. Do you have dishes that you dislike? Is all valid for
you and your culture.

It is ok that this is an internal feature, that is not cultural dependent.
Iwanted to give you my Interpretation of this Feature. It is ok It doesn't
fit 😉

Pavel Stehule <pavel.stehule@gmail.com> schrieb am Mo., 27. Dez. 2021,
11:15:

Show quoted text

po 27. 12. 2021 v 11:08 odesílatel Sascha Kuhl <yogidabanli@gmail.com>
napsal:

Can that boolean node be cultural dependent validation for the value? By
the developer? By all?

why?

The boolean node is not a boolean type.

This is an internal feature. There should not be any cultural dependency

Regards

Pavel

Pavel Stehule <pavel.stehule@gmail.com> schrieb am Mo., 27. Dez. 2021,
10:09:

po 27. 12. 2021 v 10:02 odesílatel Peter Eisentraut <
peter.eisentraut@enterprisedb.com> napsal:

This patch adds a new node type Boolean, to go alongside the "value"
nodes Integer, Float, String, etc. This seems appropriate given that
Boolean values are a fundamental part of the system and are used a lot.

Before, SQL-level Boolean constants were represented by a string with
a cast, and internal Boolean values in DDL commands were usually
represented by Integer nodes. This takes the place of both of these
uses, making the intent clearer and having some amount of type safety.

+1

Regards

Pavel

#7Pavel Stehule
pavel.stehule@gmail.com
In reply to: Sascha Kuhl (#6)
Re: Add Boolean node

Hi

po 27. 12. 2021 v 11:24 odesílatel Sascha Kuhl <yogidabanli@gmail.com>
napsal:

You think, all values are valid. Is a higher german order valid for
Turkey, that only know baskets, as a Form of order. For me not all forms of
all are valid for all. You cannot Export or Import food that You dislike,
because it would hurt you. Do you have dishes that you dislike? Is all
valid for you and your culture.

It is ok that this is an internal feature, that is not cultural dependent.
Iwanted to give you my Interpretation of this Feature. It is ok It doesn't
fit 😉

Please, don't use top posting mode in this mailing list
https://en.wikipedia.org/wiki/Posting_style#Top-posting

This is an internal feature - Node structures are not visible from SQL
level. And internal features will be faster and less complex, if we don't
need to implement cultural dependency there. So False is just only false,
and not "false" or "lez" or "nepravda" or "Marchen" any other.

On a custom level it is a different situation. Although I am not sure if it
is a good idea to implement local dependency for boolean type. In Czech
language we have two related words for "false" - "lez" and "nepravda". And
nothing is used in IT. But we use Czech (German) format date (and
everywhere in code ISO format should be preferred), and we use czech
sorting. In internal things less complexity is better (higher complexity
means lower safety) . On a custom level, anybody can do what they like.

Regards

Pavel

Show quoted text

Pavel Stehule <pavel.stehule@gmail.com> schrieb am Mo., 27. Dez. 2021,
11:15:

po 27. 12. 2021 v 11:08 odesílatel Sascha Kuhl <yogidabanli@gmail.com>
napsal:

Can that boolean node be cultural dependent validation for the value? By
the developer? By all?

why?

The boolean node is not a boolean type.

This is an internal feature. There should not be any cultural dependency

Regards

Pavel

Pavel Stehule <pavel.stehule@gmail.com> schrieb am Mo., 27. Dez. 2021,
10:09:

po 27. 12. 2021 v 10:02 odesílatel Peter Eisentraut <
peter.eisentraut@enterprisedb.com> napsal:

This patch adds a new node type Boolean, to go alongside the "value"
nodes Integer, Float, String, etc. This seems appropriate given that
Boolean values are a fundamental part of the system and are used a lot.

Before, SQL-level Boolean constants were represented by a string with
a cast, and internal Boolean values in DDL commands were usually
represented by Integer nodes. This takes the place of both of these
uses, making the intent clearer and having some amount of type safety.

+1

Regards

Pavel

#8Sascha Kuhl
yogidabanli@gmail.com
In reply to: Pavel Stehule (#7)
Re: Add Boolean node

Pavel Stehule <pavel.stehule@gmail.com> schrieb am Mo., 27. Dez. 2021,
11:49:

Hi

po 27. 12. 2021 v 11:24 odesílatel Sascha Kuhl <yogidabanli@gmail.com>
napsal:

You think, all values are valid. Is a higher german order valid for
Turkey, that only know baskets, as a Form of order. For me not all forms of
all are valid for all. You cannot Export or Import food that You dislike,
because it would hurt you. Do you have dishes that you dislike? Is all
valid for you and your culture.

It is ok that this is an internal feature, that is not cultural
dependent. Iwanted to give you my Interpretation of this Feature. It is ok
It doesn't fit 😉

Please, don't use top posting mode in this mailing list
https://en.wikipedia.org/wiki/Posting_style#Top-posting

I will read and learn on that. Thanks for the hint.

This is an internal feature - Node structures are not visible from SQL
level. And internal features will be faster and less complex, if we don't
need to implement cultural dependency there. So False is just only false,
and not "false" or "lez" or "nepravda" or "Marchen" any other.

On a custom level it is a different situation. Although I am not sure if
it is a good idea to implement local dependency for boolean type. In Czech
language we have two related words for "false" - "lez" and "nepravda". And
nothing is used in IT. But we use Czech (German) format date (and
everywhere in code ISO format should be preferred), and we use czech
sorting. In internal things less complexity is better (higher complexity
means lower safety) . On a custom level, anybody can do what they like.

I agree on that from a german point of view. This is great structure on a
first guess.

Show quoted text

Regards

Pavel

Pavel Stehule <pavel.stehule@gmail.com> schrieb am Mo., 27. Dez. 2021,
11:15:

po 27. 12. 2021 v 11:08 odesílatel Sascha Kuhl <yogidabanli@gmail.com>
napsal:

Can that boolean node be cultural dependent validation for the value?
By the developer? By all?

why?

The boolean node is not a boolean type.

This is an internal feature. There should not be any cultural dependency

Regards

Pavel

Pavel Stehule <pavel.stehule@gmail.com> schrieb am Mo., 27. Dez. 2021,
10:09:

po 27. 12. 2021 v 10:02 odesílatel Peter Eisentraut <
peter.eisentraut@enterprisedb.com> napsal:

This patch adds a new node type Boolean, to go alongside the "value"
nodes Integer, Float, String, etc. This seems appropriate given that
Boolean values are a fundamental part of the system and are used a
lot.

Before, SQL-level Boolean constants were represented by a string with
a cast, and internal Boolean values in DDL commands were usually
represented by Integer nodes. This takes the place of both of these
uses, making the intent clearer and having some amount of type safety.

+1

Regards

Pavel

#9Sascha Kuhl
yogidabanli@gmail.com
In reply to: Sascha Kuhl (#8)
Re: Add Boolean node

Sascha Kuhl <yogidabanli@gmail.com> schrieb am Mo., 27. Dez. 2021, 12:13:

Pavel Stehule <pavel.stehule@gmail.com> schrieb am Mo., 27. Dez. 2021,
11:49:

Hi

po 27. 12. 2021 v 11:24 odesílatel Sascha Kuhl <yogidabanli@gmail.com>
napsal:

You think, all values are valid. Is a higher german order valid for
Turkey, that only know baskets, as a Form of order. For me not all forms of
all are valid for all. You cannot Export or Import food that You dislike,
because it would hurt you. Do you have dishes that you dislike? Is all
valid for you and your culture.

It is ok that this is an internal feature, that is not cultural
dependent. Iwanted to give you my Interpretation of this Feature. It is ok
It doesn't fit 😉

Please, don't use top posting mode in this mailing list
https://en.wikipedia.org/wiki/Posting_style#Top-posting

I will read and learn on that. Thanks for the hint.

This is an internal feature - Node structures are not visible from SQL
level. And internal features will be faster and less complex, if we don't
need to implement cultural dependency there. So False is just only false,
and not "false" or "lez" or "nepravda" or "Marchen" any other.

On a custom level it is a different situation. Although I am not sure if
it is a good idea to implement local dependency for boolean type. In Czech
language we have two related words for "false" - "lez" and "nepravda". And
nothing is used in IT. But we use Czech (German) format date (and
everywhere in code ISO format shou lld be preferred), and we use czech
sorting. In internal things less complexity is better (higher complexity
means lower safety) . On a custom level, anybody can do what they like.

If you See databases as a tree, buche like books, the stem is internal,
less complexity, strong and safe. The custom level are the bows and leafs.
Ever leaf gets the ingredients it likes, but all are of the same type.

Show quoted text

I agree on that from a german point of view. This is great structure on a
first guess.

Regards

Pavel

Pavel Stehule <pavel.stehule@gmail.com> schrieb am Mo., 27. Dez. 2021,
11:15:

po 27. 12. 2021 v 11:08 odesílatel Sascha Kuhl <yogidabanli@gmail.com>
napsal:

Can that boolean node be cultural dependent validation for the value?
By the developer? By all?

why?

The boolean node is not a boolean type.

This is an internal feature. There should not be any cultural dependency

Regards

Pavel

Pavel Stehule <pavel.stehule@gmail.com> schrieb am Mo., 27. Dez.
2021, 10:09:

po 27. 12. 2021 v 10:02 odesílatel Peter Eisentraut <
peter.eisentraut@enterprisedb.com> napsal:

This patch adds a new node type Boolean, to go alongside the "value"
nodes Integer, Float, String, etc. This seems appropriate given
that
Boolean values are a fundamental part of the system and are used a
lot.

Before, SQL-level Boolean constants were represented by a string with
a cast, and internal Boolean values in DDL commands were usually
represented by Integer nodes. This takes the place of both of these
uses, making the intent clearer and having some amount of type
safety.

+1

Regards

Pavel

#10Pavel Stehule
pavel.stehule@gmail.com
In reply to: Sascha Kuhl (#9)
Re: Add Boolean node

po 27. 12. 2021 v 12:23 odesílatel Sascha Kuhl <yogidabanli@gmail.com>
napsal:

Sascha Kuhl <yogidabanli@gmail.com> schrieb am Mo., 27. Dez. 2021, 12:13:

Pavel Stehule <pavel.stehule@gmail.com> schrieb am Mo., 27. Dez. 2021,
11:49:

Hi

po 27. 12. 2021 v 11:24 odesílatel Sascha Kuhl <yogidabanli@gmail.com>
napsal:

You think, all values are valid. Is a higher german order valid for
Turkey, that only know baskets, as a Form of order. For me not all forms of
all are valid for all. You cannot Export or Import food that You dislike,
because it would hurt you. Do you have dishes that you dislike? Is all
valid for you and your culture.

It is ok that this is an internal feature, that is not cultural
dependent. Iwanted to give you my Interpretation of this Feature. It is ok
It doesn't fit 😉

Please, don't use top posting mode in this mailing list
https://en.wikipedia.org/wiki/Posting_style#Top-posting

I will read and learn on that. Thanks for the hint.

This is an internal feature - Node structures are not visible from SQL
level. And internal features will be faster and less complex, if we don't
need to implement cultural dependency there. So False is just only false,
and not "false" or "lez" or "nepravda" or "Marchen" any other.

On a custom level it is a different situation. Although I am not sure if
it is a good idea to implement local dependency for boolean type. In Czech
language we have two related words for "false" - "lez" and "nepravda". And
nothing is used in IT. But we use Czech (German) format date (and
everywhere in code ISO format shou lld be preferred), and we use czech
sorting. In internal things less complexity is better (higher complexity
means lower safety) . On a custom level, anybody can do what they like.

If you See databases as a tree, buche like books, the stem is internal,
less complexity, strong and safe. The custom level are the bows and leafs.
Ever leaf gets the ingredients it likes, but all are of the same type.

again - Node type is not equal to data type.

Regards

Pavel

#11Sascha Kuhl
yogidabanli@gmail.com
In reply to: Pavel Stehule (#10)
Re: Add Boolean node

Pavel Stehule <pavel.stehule@gmail.com> schrieb am Mo., 27. Dez. 2021,
12:28:

po 27. 12. 2021 v 12:23 odesílatel Sascha Kuhl <yogidabanli@gmail.com>
napsal:

Sascha Kuhl <yogidabanli@gmail.com> schrieb am Mo., 27. Dez. 2021, 12:13:

Pavel Stehule <pavel.stehule@gmail.com> schrieb am Mo., 27. Dez. 2021,
11:49:

Hi

po 27. 12. 2021 v 11:24 odesílatel Sascha Kuhl <yogidabanli@gmail.com>
napsal:

You think, all values are valid. Is a higher german order valid for
Turkey, that only know baskets, as a Form of order. For me not all forms of
all are valid for all. You cannot Export or Import food that You dislike,
because it would hurt you. Do you have dishes that you dislike? Is all
valid for you and your culture.

It is ok that this is an internal feature, that is not cultural
dependent. Iwanted to give you my Interpretation of this Feature. It is ok
It doesn't fit 😉

Please, don't use top posting mode in this mailing list
https://en.wikipedia.org/wiki/Posting_style#Top-posting

I will read and learn on that. Thanks for the hint.

This is an internal feature - Node structures are not visible from SQL
level. And internal features will be faster and less complex, if we don't
need to implement cultural dependency there. So False is just only false,
and not "false" or "lez" or "nepravda" or "Marchen" any other.

On a custom level it is a different situation. Although I am not sure
if it is a good idea to implement local dependency for boolean type. In
Czech language we have two related words for "false" - "lez" and
"nepravda". And nothing is used in IT. But we use Czech (German) format
date (and everywhere in code ISO format shou lld be preferred), and we use
czech sorting. In internal things less complexity is better (higher
complexity means lower safety) . On a custom level, anybody can do what
they like.

If you See databases as a tree, buche like books, the stem is internal,
less complexity, strong and safe. The custom level are the bows and leafs.
Ever leaf gets the ingredients it likes, but all are of the same type.

again - Node type is not equal to data type.

Did you know that different culture have different trees. You read that.
The Chinese Bonsai reflects Chinese Société, as well as the german buche
reflects Verwaltung

Thanks for the separation of node and data. If you consider keys, ie.
Indexes trees, keys and nodes can be easily the same, in a simulation.
Thanks for your view.

Show quoted text

Regards

Pavel

#12Pavel Stehule
pavel.stehule@gmail.com
In reply to: Sascha Kuhl (#11)
Re: Add Boolean node

po 27. 12. 2021 v 13:05 odesílatel Sascha Kuhl <yogidabanli@gmail.com>
napsal:

Pavel Stehule <pavel.stehule@gmail.com> schrieb am Mo., 27. Dez. 2021,
12:28:

po 27. 12. 2021 v 12:23 odesílatel Sascha Kuhl <yogidabanli@gmail.com>
napsal:

Sascha Kuhl <yogidabanli@gmail.com> schrieb am Mo., 27. Dez. 2021,
12:13:

Pavel Stehule <pavel.stehule@gmail.com> schrieb am Mo., 27. Dez. 2021,
11:49:

Hi

po 27. 12. 2021 v 11:24 odesílatel Sascha Kuhl <yogidabanli@gmail.com>
napsal:

You think, all values are valid. Is a higher german order valid for
Turkey, that only know baskets, as a Form of order. For me not all forms of
all are valid for all. You cannot Export or Import food that You dislike,
because it would hurt you. Do you have dishes that you dislike? Is all
valid for you and your culture.

It is ok that this is an internal feature, that is not cultural
dependent. Iwanted to give you my Interpretation of this Feature. It is ok
It doesn't fit 😉

Please, don't use top posting mode in this mailing list
https://en.wikipedia.org/wiki/Posting_style#Top-posting

I will read and learn on that. Thanks for the hint.

This is an internal feature - Node structures are not visible from SQL
level. And internal features will be faster and less complex, if we don't
need to implement cultural dependency there. So False is just only false,
and not "false" or "lez" or "nepravda" or "Marchen" any other.

On a custom level it is a different situation. Although I am not sure
if it is a good idea to implement local dependency for boolean type. In
Czech language we have two related words for "false" - "lez" and
"nepravda". And nothing is used in IT. But we use Czech (German) format
date (and everywhere in code ISO format shou lld be preferred), and we use
czech sorting. In internal things less complexity is better (higher
complexity means lower safety) . On a custom level, anybody can do what
they like.

If you See databases as a tree, buche like books, the stem is internal,
less complexity, strong and safe. The custom level are the bows and leafs.
Ever leaf gets the ingredients it likes, but all are of the same type.

again - Node type is not equal to data type.

Did you know that different culture have different trees. You read that.
The Chinese Bonsai reflects Chinese Société, as well as the german buche
reflects Verwaltung

Thanks for the separation of node and data. If you consider keys, ie.
Indexes trees, keys and nodes can be easily the same, in a simulation.
Thanks for your view.

look at Postgres source code , please.
https://github.com/postgres/postgres/tree/master/src/backend/nodes. In this
case nodes have no relation to the index's tree.

Regards

Pavel

Show quoted text

Regards

Pavel

#13Ashutosh Bapat
ashutosh.bapat.oss@gmail.com
In reply to: Peter Eisentraut (#1)
Re: Add Boolean node

That looks like a good change. I wonder what motivates that now? Why
wasn't it added when the usages grew? Are there more Boolean usages
planned?

I ask because this code change will affect ability to automatically
cherry-pick some of the patches.

defGetBoolean() - please update the comment in the default to case to
mention defGetString along with opt_boolean_or_string production.
Reading the existing code in that function, one would wonder why to
use true and false over say on and off. But true/false seems a natural
choice. So that's fine.

defGetBoolean() and nodeRead() could use a common function to parse a
boolean string. The code in nodeRead() seems to assume that any string
starting with "t" will represent value true. Is that right?

We are using literal constants "true"/"false" at many places. This
patch adds another one. I am wondering whether it makes sense to add
#define TRUE_STR, FALSE_STR and use it everywhere for consistency and
correctness.

For the sake of consistency (again :)), we should have a function to
return string representation of a Boolean node and use it in both
defGetString and _outBoolean().

Are the expected output changes like below necessary? Might affect
backward compatibility for applications.
-bool
-----
-t
+?column?
+--------
+t

On Mon, Dec 27, 2021 at 2:32 PM Peter Eisentraut
<peter.eisentraut@enterprisedb.com> wrote:

This patch adds a new node type Boolean, to go alongside the "value"
nodes Integer, Float, String, etc. This seems appropriate given that
Boolean values are a fundamental part of the system and are used a lot.

Before, SQL-level Boolean constants were represented by a string with
a cast, and internal Boolean values in DDL commands were usually
represented by Integer nodes. This takes the place of both of these
uses, making the intent clearer and having some amount of type safety.

--
Best Wishes,
Ashutosh Bapat

#14Tom Lane
tgl@sss.pgh.pa.us
In reply to: Ashutosh Bapat (#13)
Re: Add Boolean node

Ashutosh Bapat <ashutosh.bapat.oss@gmail.com> writes:

That looks like a good change. I wonder what motivates that now? Why
wasn't it added when the usages grew?

You'd have to find some of the original Berkeley people to get an
answer for that. Possibly it's got something to do with the fact
that C didn't have a separate bool type back then ... or, going
even further back, that LISP didn't either. In any case, it seems
like a plausible improvement now.

Didn't really read the patch in any detail, but I did have one idea:
I think that the different things-that-used-to-be-Value-nodes ought to
use different field names, say ival, rval, bval, sval not just "val".
That makes it more likely that you'd catch any code that is doing the
wrong thing and not going through one of the access macros.

regards, tom lane

#15Alvaro Herrera
alvherre@alvh.no-ip.org
In reply to: Peter Eisentraut (#1)
Re: Add Boolean node

On 2021-Dec-27, Peter Eisentraut wrote:

This patch adds a new node type Boolean, to go alongside the "value" nodes
Integer, Float, String, etc. This seems appropriate given that Boolean
values are a fundamental part of the system and are used a lot.

I like the idea. I'm surprised that there is no notational savings in
the patch, however.

diff --git a/src/test/regress/expected/create_function_3.out b/src/test/regress/expected/create_function_3.out
index 3a4fd45147..e0c4bee893 100644
--- a/src/test/regress/expected/create_function_3.out
+++ b/src/test/regress/expected/create_function_3.out
@@ -403,7 +403,7 @@ SELECT pg_get_functiondef('functest_S_13'::regproc);
LANGUAGE sql                                            +
BEGIN ATOMIC                                             +
SELECT 1;                                               +
-  SELECT false AS bool;                                   +
+  SELECT false;                                           +
END                                                      +

Hmm, interesting side-effect: we no longer assign a column name in this
case so it remains "?column?", just like it happens for other datatypes.
This seems okay to me. (This is also what causes the changes in the
isolationtester expected output.)

--
Álvaro Herrera 39°49'30"S 73°17'W — https://www.EnterpriseDB.com/
"Ni aún el genio muy grande llegaría muy lejos
si tuviera que sacarlo todo de su propio interior" (Goethe)

#16Zhihong Yu
zyu@yugabyte.com
In reply to: Alvaro Herrera (#15)
Re: Add Boolean node

Hi,
For buildDefItem():

+       if (strcmp(val, "true") == 0)
+           return makeDefElem(pstrdup(name),
+                              (Node *) makeBoolean(true),
+                              -1);
+       if (strcmp(val, "false") == 0)

Should 'TRUE' / 'FALSE' be considered above ?

-       issuper = intVal(dissuper->arg) != 0;
+       issuper = boolVal(dissuper->arg) != 0;

Can the above be written as (since issuper is a bool):

+ issuper = boolVal(dissuper->arg);

Cheers

#17Peter Eisentraut
peter.eisentraut@enterprisedb.com
In reply to: Ashutosh Bapat (#13)
Re: Add Boolean node

On 27.12.21 14:15, Ashutosh Bapat wrote:

That looks like a good change. I wonder what motivates that now? Why
wasn't it added when the usages grew? Are there more Boolean usages
planned?

Mainly, I was looking at Integer/makeInteger() and noticed that most
uses of those weren't actually integers but booleans. This change makes
it clearer which is which.

#18Josef Šimánek
josef.simanek@gmail.com
In reply to: Alvaro Herrera (#15)
Re: Add Boolean node

po 27. 12. 2021 v 16:10 odesílatel Alvaro Herrera
<alvherre@alvh.no-ip.org> napsal:

On 2021-Dec-27, Peter Eisentraut wrote:

This patch adds a new node type Boolean, to go alongside the "value" nodes
Integer, Float, String, etc. This seems appropriate given that Boolean
values are a fundamental part of the system and are used a lot.

I like the idea. I'm surprised that there is no notational savings in
the patch, however.

diff --git a/src/test/regress/expected/create_function_3.out b/src/test/regress/expected/create_function_3.out
index 3a4fd45147..e0c4bee893 100644
--- a/src/test/regress/expected/create_function_3.out
+++ b/src/test/regress/expected/create_function_3.out
@@ -403,7 +403,7 @@ SELECT pg_get_functiondef('functest_S_13'::regproc);
LANGUAGE sql                                            +
BEGIN ATOMIC                                             +
SELECT 1;                                               +
-  SELECT false AS bool;                                   +
+  SELECT false;                                           +
END                                                      +

Hmm, interesting side-effect: we no longer assign a column name in this
case so it remains "?column?", just like it happens for other datatypes.
This seems okay to me. (This is also what causes the changes in the
isolationtester expected output.)

This seems to be caused by a change of makeBoolAConst function. I was
thinking for a while about the potential backward compatibility
problems, but I wasn't able to find any.

Show quoted text

--
Álvaro Herrera 39°49'30"S 73°17'W — https://www.EnterpriseDB.com/
"Ni aún el genio muy grande llegaría muy lejos
si tuviera que sacarlo todo de su propio interior" (Goethe)

#19Tom Lane
tgl@sss.pgh.pa.us
In reply to: Josef Šimánek (#18)
Re: Add Boolean node

=?UTF-8?B?Sm9zZWYgxaBpbcOhbmVr?= <josef.simanek@gmail.com> writes:

po 27. 12. 2021 v 16:10 odesílatel Alvaro Herrera
<alvherre@alvh.no-ip.org> napsal:

Hmm, interesting side-effect: we no longer assign a column name in this
case so it remains "?column?", just like it happens for other datatypes.
This seems okay to me. (This is also what causes the changes in the
isolationtester expected output.)

This seems to be caused by a change of makeBoolAConst function. I was
thinking for a while about the potential backward compatibility
problems, but I wasn't able to find any.

In theory this could break some application that's expecting
"SELECT ..., true, ..." to return a column name of "bool"
rather than "?column?". The risk of that being a problem in
practice seems rather low, though. It certainly seems like a
wart that you get a type name for that but not for other sorts
of literals such as 1 or 2.4, so I'm okay with the change.

regards, tom lane

#20Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#14)
Re: Add Boolean node

On 2021-12-27 09:53:32 -0500, Tom Lane wrote:

Didn't really read the patch in any detail, but I did have one idea:
I think that the different things-that-used-to-be-Value-nodes ought to
use different field names, say ival, rval, bval, sval not just "val".
That makes it more likely that you'd catch any code that is doing the
wrong thing and not going through one of the access macros.

If we go around changing all these places, it might be worth to also change
Integer to be a int64 instead of an int.

#21Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#20)
Re: Add Boolean node

Andres Freund <andres@anarazel.de> writes:

If we go around changing all these places, it might be worth to also change
Integer to be a int64 instead of an int.

Meh ... that would have some non-obvious consequences, I think,
at least if you tried to make the grammar make use of the extra
width (it'd change the type resolution behavior for integer-ish
literals). I think it's better to keep it as plain int.

regards, tom lane

#22Andres Freund
andres@anarazel.de
In reply to: Peter Eisentraut (#1)
Re: Add Boolean node

Hi,

On 2021-12-27 10:02:14 +0100, Peter Eisentraut wrote:

This patch adds a new node type Boolean, to go alongside the "value" nodes
Integer, Float, String, etc. This seems appropriate given that Boolean
values are a fundamental part of the system and are used a lot.

Before, SQL-level Boolean constants were represented by a string with
a cast, and internal Boolean values in DDL commands were usually represented
by Integer nodes. This takes the place of both of these uses, making the
intent clearer and having some amount of type safety.

This annoyed me plenty of times before, plus many.

From 4e1ef56b5443fa11d981eb6e407dfc7c244dc60e Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 27 Dec 2021 09:52:05 +0100
Subject: [PATCH v1] Add Boolean node

Before, SQL-level boolean constants were represented by a string with
a cast, and internal Boolean values in DDL commands were usually
represented by Integer nodes. This takes the place of both of these
uses, making the intent clearer and having some amount of type safety.
---
...
20 files changed, 210 insertions(+), 126 deletions(-)

This might be easier to review if there were one patch adding the Boolean
type, and then a separate one converting users?

diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c
index c47a05d10d..b7261a88d4 100644
--- a/src/backend/commands/tsearchcmds.c
+++ b/src/backend/commands/tsearchcmds.c
@@ -1742,6 +1742,15 @@ buildDefItem(const char *name, const char *val, bool was_quoted)
return makeDefElem(pstrdup(name),
(Node *) makeFloat(pstrdup(val)),
-1);
+
+		if (strcmp(val, "true") == 0)
+			return makeDefElem(pstrdup(name),
+							   (Node *) makeBoolean(true),
+							   -1);
+		if (strcmp(val, "false") == 0)
+			return makeDefElem(pstrdup(name),
+							   (Node *) makeBoolean(false),
+							   -1);
}
/* Just make it a string */
return makeDefElem(pstrdup(name),

Hm. defGetBoolean() interprets "true", "false", "on", "off" as booleans. ISTM
we shouldn't invent different behaviours for individual subsystems?

--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -3433,6 +3433,12 @@ _outFloat(StringInfo str, const Float *node)
appendStringInfoString(str, node->val);
}
+static void
+_outBoolean(StringInfo str, const Boolean *node)
+{
+	appendStringInfoString(str, node->val ? "true" : "false");
+}

Any reason not to use 't' and 'f' instead? It seems unnecessary to bloat the
node output by the longer strings, and it makes parsing more expensive
too:

--- a/src/backend/nodes/read.c
+++ b/src/backend/nodes/read.c
@@ -283,6 +283,8 @@ nodeTokenType(const char *token, int length)
retval = RIGHT_PAREN;
else if (*token == '{')
retval = LEFT_BRACE;
+	else if (strcmp(token, "true") == 0 || strcmp(token, "false") == 0)
+		retval = T_Boolean;
else if (*token == '"' && length > 1 && token[length - 1] == '"')
retval = T_String;
else if (*token == 'b')

Before this could be implemented as a jump table, not now it can't easily be
anymore.

diff --git a/src/test/isolation/expected/ri-trigger.out b/src/test/isolation/expected/ri-trigger.out
index 842df80a90..db85618bef 100644
--- a/src/test/isolation/expected/ri-trigger.out
+++ b/src/test/isolation/expected/ri-trigger.out
@@ -4,9 +4,9 @@ starting permutation: wxry1 c1 r2 wyrx2 c2
step wxry1: INSERT INTO child (parent_id) VALUES (0);
step c1: COMMIT;
step r2: SELECT TRUE;
-bool
-----
-t   
+?column?
+--------
+t       
(1 row)

This doesn't seem great. It might be more consistent ("SELECT 1" doesn't end
up with 'integer' as column name), but this still seems like an unnecessarily
large user-visible change for an internal data-representation change?

Greetings,

Andres Freund

#23Peter Eisentraut
peter.eisentraut@enterprisedb.com
In reply to: Andres Freund (#20)
Re: Add Boolean node

On 29.12.21 21:32, Andres Freund wrote:

On 2021-12-27 09:53:32 -0500, Tom Lane wrote:

Didn't really read the patch in any detail, but I did have one idea:
I think that the different things-that-used-to-be-Value-nodes ought to
use different field names, say ival, rval, bval, sval not just "val".
That makes it more likely that you'd catch any code that is doing the
wrong thing and not going through one of the access macros.

If we go around changing all these places, it might be worth to also change
Integer to be a int64 instead of an int.

I was actually looking into that, when I realized that most uses of
Integer were actually Booleans. Hence the current patch to clear those
fake Integers out of the way. I haven't gotten to analyze the int64
question any further, but it should be easier hereafter.

#24Peter Eisentraut
peter.eisentraut@enterprisedb.com
In reply to: Peter Eisentraut (#1)
3 attachment(s)
Re: Add Boolean node

On 27.12.21 10:02, Peter Eisentraut wrote:

This patch adds a new node type Boolean, to go alongside the "value"
nodes Integer, Float, String, etc.  This seems appropriate given that
Boolean values are a fundamental part of the system and are used a lot.

Before, SQL-level Boolean constants were represented by a string with
a cast, and internal Boolean values in DDL commands were usually
represented by Integer nodes.  This takes the place of both of these
uses, making the intent clearer and having some amount of type safety.

Here is an update of this patch set based on the feedback. First, I
added a patch that makes some changes in AlterRole() that my original
patch might have broken or at least made more confusing. Unlike in
CreateRole(), we use three-valued logic here, so that a variable like
issuper would have 0 = no, 1 = yes, -1 = not specified, keep previous
value. I'm simplifying this, by instead using the dissuper etc.
variables to track whether a setting was specified. This makes
everything a bit simpler and makes the subsequent patch easier.

Second, I added the suggest by Tom Lane to rename to fields in the
used-to-be-Value nodes to be different in each node type (ival, fval,
etc.). I agree that this makes things a bit cleaner and reduces the
changes of mixups.

And third, the original patch that introduces the Boolean node with some
small changes based on the feedback.

Attachments:

v2-0001-Refactor-AlterRole.patchtext/plain; charset=UTF-8; name=v2-0001-Refactor-AlterRole.patchDownload
From 8d5f7f330c0824c862a3c0d8ce5cd3d578e22d82 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 3 Jan 2022 09:37:12 +0100
Subject: [PATCH v2 1/3] Refactor AlterRole()

Get rid of the three-valued logic for the Boolean variables to track
whether the value was been specified and what the new value should be.
Instead, we can use the "dfoo" variables to determine whether the
value was specified and should be applied.  This was already done in
some cases, so this makes this more uniform and removes one layer of
indirection.
---
 src/backend/commands/user.c | 97 +++++++++++++------------------------
 1 file changed, 35 insertions(+), 62 deletions(-)

diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index aa69821be4..0aae87ff4a 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -501,20 +501,12 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
 				new_tuple;
 	Form_pg_authid authform;
 	ListCell   *option;
-	char	   *rolename = NULL;
+	char	   *rolename;
 	char	   *password = NULL;	/* user password */
-	int			issuper = -1;	/* Make the user a superuser? */
-	int			inherit = -1;	/* Auto inherit privileges? */
-	int			createrole = -1;	/* Can this user create roles? */
-	int			createdb = -1;	/* Can the user create databases? */
-	int			canlogin = -1;	/* Can this user login? */
-	int			isreplication = -1; /* Is this a replication role? */
 	int			connlimit = -1; /* maximum connections allowed */
-	List	   *rolemembers = NIL;	/* roles to be added/removed */
 	char	   *validUntil = NULL;	/* time the login is valid until */
 	Datum		validUntil_datum;	/* same, as timestamptz Datum */
 	bool		validUntil_null;
-	int			bypassrls = -1;
 	DefElem    *dpassword = NULL;
 	DefElem    *dissuper = NULL;
 	DefElem    *dinherit = NULL;
@@ -610,18 +602,6 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
 
 	if (dpassword && dpassword->arg)
 		password = strVal(dpassword->arg);
-	if (dissuper)
-		issuper = intVal(dissuper->arg);
-	if (dinherit)
-		inherit = intVal(dinherit->arg);
-	if (dcreaterole)
-		createrole = intVal(dcreaterole->arg);
-	if (dcreatedb)
-		createdb = intVal(dcreatedb->arg);
-	if (dcanlogin)
-		canlogin = intVal(dcanlogin->arg);
-	if (disreplication)
-		isreplication = intVal(disreplication->arg);
 	if (dconnlimit)
 	{
 		connlimit = intVal(dconnlimit->arg);
@@ -630,12 +610,8 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 					 errmsg("invalid connection limit: %d", connlimit)));
 	}
-	if (drolemembers)
-		rolemembers = (List *) drolemembers->arg;
 	if (dvalidUntil)
 		validUntil = strVal(dvalidUntil->arg);
-	if (dbypassRLS)
-		bypassrls = intVal(dbypassRLS->arg);
 
 	/*
 	 * Scan the pg_authid relation to be certain the user exists.
@@ -654,21 +630,21 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
 	 * property.  Otherwise, if you don't have createrole, you're only allowed
 	 * to change your own password.
 	 */
-	if (authform->rolsuper || issuper >= 0)
+	if (authform->rolsuper || dissuper)
 	{
 		if (!superuser())
 			ereport(ERROR,
 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 					 errmsg("must be superuser to alter superuser roles or change superuser attribute")));
 	}
-	else if (authform->rolreplication || isreplication >= 0)
+	else if (authform->rolreplication || disreplication)
 	{
 		if (!superuser())
 			ereport(ERROR,
 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 					 errmsg("must be superuser to alter replication roles or change replication attribute")));
 	}
-	else if (bypassrls >= 0)
+	else if (dbypassRLS)
 	{
 		if (!superuser())
 			ereport(ERROR,
@@ -677,23 +653,16 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
 	}
 	else if (!have_createrole_privilege())
 	{
-		/* We already checked issuper, isreplication, and bypassrls */
-		if (!(inherit < 0 &&
-			  createrole < 0 &&
-			  createdb < 0 &&
-			  canlogin < 0 &&
-			  !dconnlimit &&
-			  !rolemembers &&
-			  !validUntil &&
-			  dpassword &&
-			  roleid == GetUserId()))
+		/* check the rest */
+		if (dinherit || dcreaterole || dcreatedb || dcanlogin || dconnlimit ||
+			drolemembers || dvalidUntil || !dpassword || roleid != GetUserId())
 			ereport(ERROR,
 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 					 errmsg("permission denied")));
 	}
 
 	/* Convert validuntil to internal form */
-	if (validUntil)
+	if (dvalidUntil)
 	{
 		validUntil_datum = DirectFunctionCall3(timestamptz_in,
 											   CStringGetDatum(validUntil),
@@ -729,39 +698,39 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
 	/*
 	 * issuper/createrole/etc
 	 */
-	if (issuper >= 0)
+	if (dissuper)
 	{
-		new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper > 0);
+		new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(intVal(dissuper->arg));
 		new_record_repl[Anum_pg_authid_rolsuper - 1] = true;
 	}
 
-	if (inherit >= 0)
+	if (dinherit)
 	{
-		new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit > 0);
+		new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(intVal(dinherit->arg));
 		new_record_repl[Anum_pg_authid_rolinherit - 1] = true;
 	}
 
-	if (createrole >= 0)
+	if (dcreaterole)
 	{
-		new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole > 0);
+		new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(intVal(dcreaterole->arg));
 		new_record_repl[Anum_pg_authid_rolcreaterole - 1] = true;
 	}
 
-	if (createdb >= 0)
+	if (dcreatedb)
 	{
-		new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb > 0);
+		new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(intVal(dcreatedb->arg));
 		new_record_repl[Anum_pg_authid_rolcreatedb - 1] = true;
 	}
 
-	if (canlogin >= 0)
+	if (dcanlogin)
 	{
-		new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin > 0);
+		new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(intVal(dcanlogin->arg));
 		new_record_repl[Anum_pg_authid_rolcanlogin - 1] = true;
 	}
 
-	if (isreplication >= 0)
+	if (disreplication)
 	{
-		new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication > 0);
+		new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(intVal(disreplication->arg));
 		new_record_repl[Anum_pg_authid_rolreplication - 1] = true;
 	}
 
@@ -808,9 +777,9 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
 	new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = validUntil_null;
 	new_record_repl[Anum_pg_authid_rolvaliduntil - 1] = true;
 
-	if (bypassrls >= 0)
+	if (dbypassRLS)
 	{
-		new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(bypassrls > 0);
+		new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(intVal(dbypassRLS->arg));
 		new_record_repl[Anum_pg_authid_rolbypassrls - 1] = true;
 	}
 
@@ -827,17 +796,21 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
 	 * Advance command counter so we can see new record; else tests in
 	 * AddRoleMems may fail.
 	 */
-	if (rolemembers)
+	if (drolemembers)
+	{
+		List	   *rolemembers	= (List *) drolemembers->arg;
+
 		CommandCounterIncrement();
 
-	if (stmt->action == +1)		/* add members to role */
-		AddRoleMems(rolename, roleid,
-					rolemembers, roleSpecsToIds(rolemembers),
-					GetUserId(), false);
-	else if (stmt->action == -1)	/* drop members from role */
-		DelRoleMems(rolename, roleid,
-					rolemembers, roleSpecsToIds(rolemembers),
-					false);
+		if (stmt->action == +1)		/* add members to role */
+			AddRoleMems(rolename, roleid,
+						rolemembers, roleSpecsToIds(rolemembers),
+						GetUserId(), false);
+		else if (stmt->action == -1)	/* drop members from role */
+			DelRoleMems(rolename, roleid,
+						rolemembers, roleSpecsToIds(rolemembers),
+						false);
+	}
 
 	/*
 	 * Close pg_authid, but keep lock till commit.

base-commit: 69872d0bbe64fcd67c4fb4c61e5c7bf6a3443a47
-- 
2.34.1

v2-0002-Rename-value-node-fields.patchtext/plain; charset=UTF-8; name=v2-0002-Rename-value-node-fields.patchDownload
From 08010ba3345fb8020587326304d6a6dafe0f098c Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 3 Jan 2022 09:59:44 +0100
Subject: [PATCH v2 2/3] Rename value node fields

For the formerly-Value node types, rename the "val" field to a name
specific to the node type, namely "ival", "fval", "sval", and "bsval".
This makes some code clearer and catches mixups better.
---
 src/backend/commands/define.c      |  4 ++--
 src/backend/nodes/copyfuncs.c      | 16 ++++++++--------
 src/backend/nodes/equalfuncs.c     |  8 ++++----
 src/backend/nodes/outfuncs.c       | 10 +++++-----
 src/backend/nodes/value.c          |  8 ++++----
 src/backend/parser/gram.y          | 24 ++++++++++++------------
 src/backend/parser/parse_node.c    | 10 +++++-----
 src/backend/parser/parse_type.c    |  6 +++---
 src/backend/parser/parse_utilcmd.c |  2 +-
 src/backend/utils/adt/oid.c        |  2 +-
 src/backend/utils/misc/guc.c       |  2 +-
 src/include/nodes/value.h          | 14 +++++++-------
 12 files changed, 53 insertions(+), 53 deletions(-)

diff --git a/src/backend/commands/define.c b/src/backend/commands/define.c
index 19c317a472..0fe7e471d9 100644
--- a/src/backend/commands/define.c
+++ b/src/backend/commands/define.c
@@ -58,7 +58,7 @@ defGetString(DefElem *def)
 		case T_Integer:
 			return psprintf("%ld", (long) intVal(def->arg));
 		case T_Float:
-			return castNode(Float, def->arg)->val;
+			return castNode(Float, def->arg)->fval;
 		case T_String:
 			return strVal(def->arg);
 		case T_TypeName:
@@ -201,7 +201,7 @@ defGetInt64(DefElem *def)
 			 * strings.
 			 */
 			return DatumGetInt64(DirectFunctionCall1(int8in,
-													 CStringGetDatum(castNode(Float, def->arg)->val)));
+													 CStringGetDatum(castNode(Float, def->arg)->fval)));
 		default:
 			ereport(ERROR,
 					(errcode(ERRCODE_SYNTAX_ERROR),
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index df0b747883..439b903eb8 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2744,16 +2744,16 @@ _copyA_Const(const A_Const *from)
 		switch (nodeTag(&from->val))
 		{
 			case T_Integer:
-				COPY_SCALAR_FIELD(val.ival.val);
+				COPY_SCALAR_FIELD(val.ival.ival);
 				break;
 			case T_Float:
-				COPY_STRING_FIELD(val.fval.val);
+				COPY_STRING_FIELD(val.fval.fval);
 				break;
 			case T_String:
-				COPY_STRING_FIELD(val.sval.val);
+				COPY_STRING_FIELD(val.sval.sval);
 				break;
 			case T_BitString:
-				COPY_STRING_FIELD(val.bsval.val);
+				COPY_STRING_FIELD(val.bsval.bsval);
 				break;
 			default:
 				elog(ERROR, "unrecognized node type: %d",
@@ -4933,7 +4933,7 @@ _copyInteger(const Integer *from)
 {
 	Integer	   *newnode = makeNode(Integer);
 
-	COPY_SCALAR_FIELD(val);
+	COPY_SCALAR_FIELD(ival);
 
 	return newnode;
 }
@@ -4943,7 +4943,7 @@ _copyFloat(const Float *from)
 {
 	Float	   *newnode = makeNode(Float);
 
-	COPY_STRING_FIELD(val);
+	COPY_STRING_FIELD(fval);
 
 	return newnode;
 }
@@ -4953,7 +4953,7 @@ _copyString(const String *from)
 {
 	String	   *newnode = makeNode(String);
 
-	COPY_STRING_FIELD(val);
+	COPY_STRING_FIELD(sval);
 
 	return newnode;
 }
@@ -4963,7 +4963,7 @@ _copyBitString(const BitString *from)
 {
 	BitString   *newnode = makeNode(BitString);
 
-	COPY_STRING_FIELD(val);
+	COPY_STRING_FIELD(bsval);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index cb7ddd463c..be09f91ee2 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -3125,7 +3125,7 @@ _equalList(const List *a, const List *b)
 static bool
 _equalInteger(const Integer *a, const Integer *b)
 {
-	COMPARE_SCALAR_FIELD(val);
+	COMPARE_SCALAR_FIELD(ival);
 
 	return true;
 }
@@ -3133,7 +3133,7 @@ _equalInteger(const Integer *a, const Integer *b)
 static bool
 _equalFloat(const Float *a, const Float *b)
 {
-	COMPARE_STRING_FIELD(val);
+	COMPARE_STRING_FIELD(fval);
 
 	return true;
 }
@@ -3141,7 +3141,7 @@ _equalFloat(const Float *a, const Float *b)
 static bool
 _equalString(const String *a, const String *b)
 {
-	COMPARE_STRING_FIELD(val);
+	COMPARE_STRING_FIELD(sval);
 
 	return true;
 }
@@ -3149,7 +3149,7 @@ _equalString(const String *a, const String *b)
 static bool
 _equalBitString(const BitString *a, const BitString *b)
 {
-	COMPARE_STRING_FIELD(val);
+	COMPARE_STRING_FIELD(bsval);
 
 	return true;
 }
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 91a89b6d51..1accbc1e78 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -3420,7 +3420,7 @@ _outA_Expr(StringInfo str, const A_Expr *node)
 static void
 _outInteger(StringInfo str, const Integer *node)
 {
-	appendStringInfo(str, "%d", node->val);
+	appendStringInfo(str, "%d", node->ival);
 }
 
 static void
@@ -3430,7 +3430,7 @@ _outFloat(StringInfo str, const Float *node)
 	 * We assume the value is a valid numeric literal and so does not
 	 * need quoting.
 	 */
-	appendStringInfoString(str, node->val);
+	appendStringInfoString(str, node->fval);
 }
 
 static void
@@ -3441,8 +3441,8 @@ _outString(StringInfo str, const String *node)
 	 * but we don't want it to do anything with an empty string.
 	 */
 	appendStringInfoChar(str, '"');
-	if (node->val[0] != '\0')
-		outToken(str, node->val);
+	if (node->sval[0] != '\0')
+		outToken(str, node->sval);
 	appendStringInfoChar(str, '"');
 }
 
@@ -3450,7 +3450,7 @@ static void
 _outBitString(StringInfo str, const BitString *node)
 {
 	/* internal representation already has leading 'b' */
-	appendStringInfoString(str, node->val);
+	appendStringInfoString(str, node->bsval);
 }
 
 static void
diff --git a/src/backend/nodes/value.c b/src/backend/nodes/value.c
index 515f93c223..ab2d6f74d3 100644
--- a/src/backend/nodes/value.c
+++ b/src/backend/nodes/value.c
@@ -24,7 +24,7 @@ makeInteger(int i)
 {
 	Integer	   *v = makeNode(Integer);
 
-	v->val = i;
+	v->ival = i;
 	return v;
 }
 
@@ -38,7 +38,7 @@ makeFloat(char *numericStr)
 {
 	Float	   *v = makeNode(Float);
 
-	v->val = numericStr;
+	v->fval = numericStr;
 	return v;
 }
 
@@ -52,7 +52,7 @@ makeString(char *str)
 {
 	String	   *v = makeNode(String);
 
-	v->val = str;
+	v->sval = str;
 	return v;
 }
 
@@ -66,6 +66,6 @@ makeBitString(char *str)
 {
 	BitString  *v = makeNode(BitString);
 
-	v->val = str;
+	v->bsval = str;
 	return v;
 }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index f3c232842d..adce748a69 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -1719,7 +1719,7 @@ zone_value:
 					if ($3 != NIL)
 					{
 						A_Const *n = (A_Const *) linitial($3);
-						if ((n->val.ival.val & ~(INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE))) != 0)
+						if ((n->val.ival.ival & ~(INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE))) != 0)
 							ereport(ERROR,
 									(errcode(ERRCODE_SYNTAX_ERROR),
 									 errmsg("time zone interval must be HOUR or HOUR TO MINUTE"),
@@ -16667,7 +16667,7 @@ makeStringConst(char *str, int location)
 	A_Const *n = makeNode(A_Const);
 
 	n->val.sval.type = T_String;
-	n->val.sval.val = str;
+	n->val.sval.sval = str;
 	n->location = location;
 
    return (Node *)n;
@@ -16687,7 +16687,7 @@ makeIntConst(int val, int location)
 	A_Const *n = makeNode(A_Const);
 
 	n->val.ival.type = T_Integer;
-	n->val.ival.val = val;
+	n->val.ival.ival = val;
 	n->location = location;
 
    return (Node *)n;
@@ -16699,7 +16699,7 @@ makeFloatConst(char *str, int location)
 	A_Const *n = makeNode(A_Const);
 
 	n->val.fval.type = T_Float;
-	n->val.fval.val = str;
+	n->val.fval.fval = str;
 	n->location = location;
 
    return (Node *)n;
@@ -16711,7 +16711,7 @@ makeBitStringConst(char *str, int location)
 	A_Const *n = makeNode(A_Const);
 
 	n->val.bsval.type = T_BitString;
-	n->val.bsval.val = str;
+	n->val.bsval.bsval = str;
 	n->location = location;
 
    return (Node *)n;
@@ -16736,16 +16736,16 @@ makeAConst(Node *v, int location)
 	switch (v->type)
 	{
 		case T_Float:
-			n = makeFloatConst(castNode(Float, v)->val, location);
+			n = makeFloatConst(castNode(Float, v)->fval, location);
 			break;
 
 		case T_Integer:
-			n = makeIntConst(castNode(Integer, v)->val, location);
+			n = makeIntConst(castNode(Integer, v)->ival, location);
 			break;
 
 		case T_String:
 		default:
-			n = makeStringConst(castNode(String, v)->val, location);
+			n = makeStringConst(castNode(String, v)->sval, location);
 			break;
 	}
 
@@ -17049,7 +17049,7 @@ doNegate(Node *n, int location)
 
 		if (IsA(&con->val, Integer))
 		{
-			con->val.ival.val = -con->val.ival.val;
+			con->val.ival.ival = -con->val.ival.ival;
 			return n;
 		}
 		if (IsA(&con->val, Float))
@@ -17065,14 +17065,14 @@ doNegate(Node *n, int location)
 static void
 doNegateFloat(Float *v)
 {
-	char   *oldval = v->val;
+	char   *oldval = v->fval;
 
 	if (*oldval == '+')
 		oldval++;
 	if (*oldval == '-')
-		v->val = oldval+1;	/* just strip the '-' */
+		v->fval = oldval+1;	/* just strip the '-' */
 	else
-		v->val = psprintf("-%s", oldval);
+		v->fval = psprintf("-%s", oldval);
 }
 
 static Node *
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c
index 8cfe6f67c0..bd92133b21 100644
--- a/src/backend/parser/parse_node.c
+++ b/src/backend/parser/parse_node.c
@@ -376,7 +376,7 @@ make_const(ParseState *pstate, A_Const *aconst)
 	switch (nodeTag(&aconst->val))
 	{
 		case T_Integer:
-			val = Int32GetDatum(aconst->val.ival.val);
+			val = Int32GetDatum(intVal(&aconst->val));
 
 			typeid = INT4OID;
 			typelen = sizeof(int32);
@@ -385,7 +385,7 @@ make_const(ParseState *pstate, A_Const *aconst)
 
 		case T_Float:
 			/* could be an oversize integer as well as a float ... */
-			if (scanint8(aconst->val.fval.val, true, &val64))
+			if (scanint8(aconst->val.fval.fval, true, &val64))
 			{
 				/*
 				 * It might actually fit in int32. Probably only INT_MIN can
@@ -415,7 +415,7 @@ make_const(ParseState *pstate, A_Const *aconst)
 				/* arrange to report location if numeric_in() fails */
 				setup_parser_errposition_callback(&pcbstate, pstate, aconst->location);
 				val = DirectFunctionCall3(numeric_in,
-										  CStringGetDatum(aconst->val.fval.val),
+										  CStringGetDatum(aconst->val.fval.fval),
 										  ObjectIdGetDatum(InvalidOid),
 										  Int32GetDatum(-1));
 				cancel_parser_errposition_callback(&pcbstate);
@@ -432,7 +432,7 @@ make_const(ParseState *pstate, A_Const *aconst)
 			 * We assume here that UNKNOWN's internal representation is the
 			 * same as CSTRING
 			 */
-			val = CStringGetDatum(aconst->val.sval.val);
+			val = CStringGetDatum(strVal(&aconst->val));
 
 			typeid = UNKNOWNOID;	/* will be coerced later */
 			typelen = -2;		/* cstring-style varwidth type */
@@ -443,7 +443,7 @@ make_const(ParseState *pstate, A_Const *aconst)
 			/* arrange to report location if bit_in() fails */
 			setup_parser_errposition_callback(&pcbstate, pstate, aconst->location);
 			val = DirectFunctionCall3(bit_in,
-									  CStringGetDatum(aconst->val.bsval.val),
+									  CStringGetDatum(aconst->val.bsval.bsval),
 									  ObjectIdGetDatum(InvalidOid),
 									  Int32GetDatum(-1));
 			cancel_parser_errposition_callback(&pcbstate);
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index 31b07ad5ae..f2d5e98127 100644
--- a/src/backend/parser/parse_type.c
+++ b/src/backend/parser/parse_type.c
@@ -382,17 +382,17 @@ typenameTypeMod(ParseState *pstate, const TypeName *typeName, Type typ)
 
 			if (IsA(&ac->val, Integer))
 			{
-				cstr = psprintf("%ld", (long) ac->val.ival.val);
+				cstr = psprintf("%ld", (long) intVal(&ac->val));
 			}
 			else if (IsA(&ac->val, Float))
 			{
 				/* we can just use the string representation directly. */
-				cstr = ac->val.fval.val;
+				cstr = ac->val.fval.fval;
 			}
 			else if (IsA(&ac->val, String))
 			{
 				/* we can just use the string representation directly. */
-				cstr = ac->val.sval.val;
+				cstr = strVal(&ac->val);
 			}
 		}
 		else if (IsA(tm, ColumnRef))
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 2d857a301b..2c64f12a78 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -603,7 +603,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 		qstring = quote_qualified_identifier(snamespace, sname);
 		snamenode = makeNode(A_Const);
 		snamenode->val.node.type = T_String;
-		snamenode->val.sval.val = qstring;
+		snamenode->val.sval.sval = qstring;
 		snamenode->location = -1;
 		castnode = makeNode(TypeCast);
 		castnode->typeName = SystemTypeName("regclass");
diff --git a/src/backend/utils/adt/oid.c b/src/backend/utils/adt/oid.c
index 7be260663e..4e684fe91c 100644
--- a/src/backend/utils/adt/oid.c
+++ b/src/backend/utils/adt/oid.c
@@ -324,7 +324,7 @@ oidparse(Node *node)
 			 * constants by the lexer.  Accept these if they are valid OID
 			 * strings.
 			 */
-			return oidin_subr(castNode(Float, node)->val, NULL);
+			return oidin_subr(castNode(Float, node)->fval, NULL);
 		default:
 			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
 	}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index f9504d3aec..ff95c3631c 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -8332,7 +8332,7 @@ flatten_set_variable_args(const char *name, List *args)
 				break;
 			case T_Float:
 				/* represented as a string, so just copy it */
-				appendStringInfoString(&buf, castNode(Float, &con->val)->val);
+				appendStringInfoString(&buf, castNode(Float, &con->val)->fval);
 				break;
 			case T_String:
 				val = strVal(&con->val);
diff --git a/src/include/nodes/value.h b/src/include/nodes/value.h
index 8b71b510eb..cc3a4a639c 100644
--- a/src/include/nodes/value.h
+++ b/src/include/nodes/value.h
@@ -28,7 +28,7 @@
 typedef struct Integer
 {
 	NodeTag		type;
-	int			val;
+	int			ival;
 } Integer;
 
 /*
@@ -45,24 +45,24 @@ typedef struct Integer
 typedef struct Float
 {
 	NodeTag		type;
-	char	   *val;
+	char	   *fval;
 } Float;
 
 typedef struct String
 {
 	NodeTag		type;
-	char	   *val;
+	char	   *sval;
 } String;
 
 typedef struct BitString
 {
 	NodeTag		type;
-	char	   *val;
+	char	   *bsval;
 } BitString;
 
-#define intVal(v)		(castNode(Integer, v)->val)
-#define floatVal(v)		atof(castNode(Float, v)->val)
-#define strVal(v)		(castNode(String, v)->val)
+#define intVal(v)		(castNode(Integer, v)->ival)
+#define floatVal(v)		atof(castNode(Float, v)->fval)
+#define strVal(v)		(castNode(String, v)->sval)
 
 extern Integer *makeInteger(int i);
 extern Float *makeFloat(char *numericStr);
-- 
2.34.1

v2-0003-Add-Boolean-node.patchtext/plain; charset=UTF-8; name=v2-0003-Add-Boolean-node.patchDownload
From eb4bdfc511f1c0f2e8f3971699c33fcfb8cf6105 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 3 Jan 2022 11:19:52 +0100
Subject: [PATCH v2 3/3] Add Boolean node

Before, SQL-level boolean constants were represented by a string with
a cast, and internal Boolean values in DDL commands were usually
represented by Integer nodes.  This takes the place of both of these
uses, making the intent clearer and having some amount of type safety.
---
 contrib/postgres_fdw/postgres_fdw.c           | 32 +++---
 src/backend/commands/define.c                 |  2 +
 src/backend/commands/functioncmds.c           | 14 +--
 src/backend/commands/sequence.c               |  4 +-
 src/backend/commands/tsearchcmds.c            |  9 ++
 src/backend/commands/user.c                   | 28 +++---
 src/backend/nodes/copyfuncs.c                 | 16 +++
 src/backend/nodes/equalfuncs.c                | 11 +++
 src/backend/nodes/nodeFuncs.c                 |  1 +
 src/backend/nodes/outfuncs.c                  |  8 ++
 src/backend/nodes/read.c                      |  9 +-
 src/backend/nodes/value.c                     | 12 +++
 src/backend/parser/gram.y                     | 98 +++++++++----------
 src/backend/parser/parse_node.c               |  8 ++
 src/backend/replication/repl_gram.y           | 14 +--
 src/include/nodes/nodes.h                     |  1 +
 src/include/nodes/parsenodes.h                |  1 +
 src/include/nodes/value.h                     |  8 ++
 src/test/isolation/expected/ri-trigger.out    | 60 ++++++------
 .../regress/expected/create_function_3.out    |  2 +-
 20 files changed, 210 insertions(+), 128 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index fa9a099f13..4d61d88d7b 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -103,7 +103,7 @@ enum FdwModifyPrivateIndex
 	FdwModifyPrivateTargetAttnums,
 	/* Length till the end of VALUES clause (as an Integer node) */
 	FdwModifyPrivateLen,
-	/* has-returning flag (as an Integer node) */
+	/* has-returning flag (as a Boolean node) */
 	FdwModifyPrivateHasReturning,
 	/* Integer list of attribute numbers retrieved by RETURNING */
 	FdwModifyPrivateRetrievedAttrs
@@ -122,11 +122,11 @@ enum FdwDirectModifyPrivateIndex
 {
 	/* SQL statement to execute remotely (as a String node) */
 	FdwDirectModifyPrivateUpdateSql,
-	/* has-returning flag (as an Integer node) */
+	/* has-returning flag (as a Boolean node) */
 	FdwDirectModifyPrivateHasReturning,
 	/* Integer list of attribute numbers retrieved by RETURNING */
 	FdwDirectModifyPrivateRetrievedAttrs,
-	/* set-processed flag (as an Integer node) */
+	/* set-processed flag (as a Boolean node) */
 	FdwDirectModifyPrivateSetProcessed
 };
 
@@ -280,9 +280,9 @@ typedef struct PgFdwAnalyzeState
  */
 enum FdwPathPrivateIndex
 {
-	/* has-final-sort flag (as an Integer node) */
+	/* has-final-sort flag (as a Boolean node) */
 	FdwPathPrivateHasFinalSort,
-	/* has-limit flag (as an Integer node) */
+	/* has-limit flag (as a Boolean node) */
 	FdwPathPrivateHasLimit
 };
 
@@ -1245,9 +1245,9 @@ postgresGetForeignPlan(PlannerInfo *root,
 	 */
 	if (best_path->fdw_private)
 	{
-		has_final_sort = intVal(list_nth(best_path->fdw_private,
+		has_final_sort = boolVal(list_nth(best_path->fdw_private,
 										 FdwPathPrivateHasFinalSort));
-		has_limit = intVal(list_nth(best_path->fdw_private,
+		has_limit = boolVal(list_nth(best_path->fdw_private,
 									FdwPathPrivateHasLimit));
 	}
 
@@ -1879,7 +1879,7 @@ postgresPlanForeignModify(PlannerInfo *root,
 	return list_make5(makeString(sql.data),
 					  targetAttrs,
 					  makeInteger(values_end_len),
-					  makeInteger((retrieved_attrs != NIL)),
+					  makeBoolean((retrieved_attrs != NIL)),
 					  retrieved_attrs);
 }
 
@@ -1916,7 +1916,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate,
 									 FdwModifyPrivateTargetAttnums);
 	values_end_len = intVal(list_nth(fdw_private,
 									 FdwModifyPrivateLen));
-	has_returning = intVal(list_nth(fdw_private,
+	has_returning = boolVal(list_nth(fdw_private,
 									FdwModifyPrivateHasReturning));
 	retrieved_attrs = (List *) list_nth(fdw_private,
 										FdwModifyPrivateRetrievedAttrs);
@@ -2567,9 +2567,9 @@ postgresPlanDirectModify(PlannerInfo *root,
 	 * Items in the list must match enum FdwDirectModifyPrivateIndex, above.
 	 */
 	fscan->fdw_private = list_make4(makeString(sql.data),
-									makeInteger((retrieved_attrs != NIL)),
+									makeBoolean((retrieved_attrs != NIL)),
 									retrieved_attrs,
-									makeInteger(plan->canSetTag));
+									makeBoolean(plan->canSetTag));
 
 	/*
 	 * Update the foreign-join-related fields.
@@ -2667,11 +2667,11 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags)
 	/* Get private info created by planner functions. */
 	dmstate->query = strVal(list_nth(fsplan->fdw_private,
 									 FdwDirectModifyPrivateUpdateSql));
-	dmstate->has_returning = intVal(list_nth(fsplan->fdw_private,
+	dmstate->has_returning = boolVal(list_nth(fsplan->fdw_private,
 											 FdwDirectModifyPrivateHasReturning));
 	dmstate->retrieved_attrs = (List *) list_nth(fsplan->fdw_private,
 												 FdwDirectModifyPrivateRetrievedAttrs);
-	dmstate->set_processed = intVal(list_nth(fsplan->fdw_private,
+	dmstate->set_processed = boolVal(list_nth(fsplan->fdw_private,
 											 FdwDirectModifyPrivateSetProcessed));
 
 	/* Create context for per-tuple temp workspace. */
@@ -6566,7 +6566,7 @@ add_foreign_ordered_paths(PlannerInfo *root, RelOptInfo *input_rel,
 	 * Build the fdw_private list that will be used by postgresGetForeignPlan.
 	 * Items in the list must match order in enum FdwPathPrivateIndex.
 	 */
-	fdw_private = list_make2(makeInteger(true), makeInteger(false));
+	fdw_private = list_make2(makeBoolean(true), makeBoolean(false));
 
 	/* Create foreign ordering path */
 	ordered_path = create_foreign_upper_path(root,
@@ -6797,8 +6797,8 @@ add_foreign_final_paths(PlannerInfo *root, RelOptInfo *input_rel,
 	 * Build the fdw_private list that will be used by postgresGetForeignPlan.
 	 * Items in the list must match order in enum FdwPathPrivateIndex.
 	 */
-	fdw_private = list_make2(makeInteger(has_final_sort),
-							 makeInteger(extra->limit_needed));
+	fdw_private = list_make2(makeBoolean(has_final_sort),
+							 makeBoolean(extra->limit_needed));
 
 	/*
 	 * Create foreign final path; this gets rid of a no-longer-needed outer
diff --git a/src/backend/commands/define.c b/src/backend/commands/define.c
index 0fe7e471d9..f17cfb5ade 100644
--- a/src/backend/commands/define.c
+++ b/src/backend/commands/define.c
@@ -59,6 +59,8 @@ defGetString(DefElem *def)
 			return psprintf("%ld", (long) intVal(def->arg));
 		case T_Float:
 			return castNode(Float, def->arg)->fval;
+		case T_Boolean:
+			return boolVal(def->arg) ? "true" : "false";
 		case T_String:
 			return strVal(def->arg);
 		case T_TypeName:
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 38e78f7102..ba5b23fb11 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -813,15 +813,15 @@ compute_function_attributes(ParseState *pstate,
 	if (transform_item)
 		*transform = transform_item->arg;
 	if (windowfunc_item)
-		*windowfunc_p = intVal(windowfunc_item->arg);
+		*windowfunc_p = boolVal(windowfunc_item->arg);
 	if (volatility_item)
 		*volatility_p = interpret_func_volatility(volatility_item);
 	if (strict_item)
-		*strict_p = intVal(strict_item->arg);
+		*strict_p = boolVal(strict_item->arg);
 	if (security_item)
-		*security_definer = intVal(security_item->arg);
+		*security_definer = boolVal(security_item->arg);
 	if (leakproof_item)
-		*leakproof_p = intVal(leakproof_item->arg);
+		*leakproof_p = boolVal(leakproof_item->arg);
 	if (set_items)
 		*proconfig = update_proconfig_value(NULL, set_items);
 	if (cost_item)
@@ -1417,12 +1417,12 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
 	if (volatility_item)
 		procForm->provolatile = interpret_func_volatility(volatility_item);
 	if (strict_item)
-		procForm->proisstrict = intVal(strict_item->arg);
+		procForm->proisstrict = boolVal(strict_item->arg);
 	if (security_def_item)
-		procForm->prosecdef = intVal(security_def_item->arg);
+		procForm->prosecdef = boolVal(security_def_item->arg);
 	if (leakproof_item)
 	{
-		procForm->proleakproof = intVal(leakproof_item->arg);
+		procForm->proleakproof = boolVal(leakproof_item->arg);
 		if (procForm->proleakproof && !superuser())
 			ereport(ERROR,
 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 72bfdc07a4..81368b8b2f 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -1401,7 +1401,7 @@ init_params(ParseState *pstate, List *options, bool for_identity,
 	/* CYCLE */
 	if (is_cycled != NULL)
 	{
-		seqform->seqcycle = intVal(is_cycled->arg);
+		seqform->seqcycle = boolVal(is_cycled->arg);
 		Assert(BoolIsValid(seqform->seqcycle));
 		seqdataform->log_cnt = 0;
 	}
@@ -1739,7 +1739,7 @@ sequence_options(Oid relid)
 	options = lappend(options,
 					  makeDefElem("cache", (Node *) makeFloat(psprintf(INT64_FORMAT, pgsform->seqcache)), -1));
 	options = lappend(options,
-					  makeDefElem("cycle", (Node *) makeInteger(pgsform->seqcycle), -1));
+					  makeDefElem("cycle", (Node *) makeBoolean(pgsform->seqcycle), -1));
 	options = lappend(options,
 					  makeDefElem("increment", (Node *) makeFloat(psprintf(INT64_FORMAT, pgsform->seqincrement)), -1));
 	options = lappend(options,
diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c
index c47a05d10d..b7261a88d4 100644
--- a/src/backend/commands/tsearchcmds.c
+++ b/src/backend/commands/tsearchcmds.c
@@ -1742,6 +1742,15 @@ buildDefItem(const char *name, const char *val, bool was_quoted)
 			return makeDefElem(pstrdup(name),
 							   (Node *) makeFloat(pstrdup(val)),
 							   -1);
+
+		if (strcmp(val, "true") == 0)
+			return makeDefElem(pstrdup(name),
+							   (Node *) makeBoolean(true),
+							   -1);
+		if (strcmp(val, "false") == 0)
+			return makeDefElem(pstrdup(name),
+							   (Node *) makeBoolean(false),
+							   -1);
 	}
 	/* Just make it a string */
 	return makeDefElem(pstrdup(name),
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 0aae87ff4a..288805ba78 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -217,17 +217,17 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
 	if (dpassword && dpassword->arg)
 		password = strVal(dpassword->arg);
 	if (dissuper)
-		issuper = intVal(dissuper->arg) != 0;
+		issuper = boolVal(dissuper->arg);
 	if (dinherit)
-		inherit = intVal(dinherit->arg) != 0;
+		inherit = boolVal(dinherit->arg);
 	if (dcreaterole)
-		createrole = intVal(dcreaterole->arg) != 0;
+		createrole = boolVal(dcreaterole->arg);
 	if (dcreatedb)
-		createdb = intVal(dcreatedb->arg) != 0;
+		createdb = boolVal(dcreatedb->arg);
 	if (dcanlogin)
-		canlogin = intVal(dcanlogin->arg) != 0;
+		canlogin = boolVal(dcanlogin->arg);
 	if (disreplication)
-		isreplication = intVal(disreplication->arg) != 0;
+		isreplication = boolVal(disreplication->arg);
 	if (dconnlimit)
 	{
 		connlimit = intVal(dconnlimit->arg);
@@ -245,7 +245,7 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
 	if (dvalidUntil)
 		validUntil = strVal(dvalidUntil->arg);
 	if (dbypassRLS)
-		bypassrls = intVal(dbypassRLS->arg) != 0;
+		bypassrls = boolVal(dbypassRLS->arg);
 
 	/* Check some permissions first */
 	if (issuper)
@@ -700,37 +700,37 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
 	 */
 	if (dissuper)
 	{
-		new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(intVal(dissuper->arg));
+		new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(boolVal(dissuper->arg));
 		new_record_repl[Anum_pg_authid_rolsuper - 1] = true;
 	}
 
 	if (dinherit)
 	{
-		new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(intVal(dinherit->arg));
+		new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(boolVal(dinherit->arg));
 		new_record_repl[Anum_pg_authid_rolinherit - 1] = true;
 	}
 
 	if (dcreaterole)
 	{
-		new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(intVal(dcreaterole->arg));
+		new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(boolVal(dcreaterole->arg));
 		new_record_repl[Anum_pg_authid_rolcreaterole - 1] = true;
 	}
 
 	if (dcreatedb)
 	{
-		new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(intVal(dcreatedb->arg));
+		new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(boolVal(dcreatedb->arg));
 		new_record_repl[Anum_pg_authid_rolcreatedb - 1] = true;
 	}
 
 	if (dcanlogin)
 	{
-		new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(intVal(dcanlogin->arg));
+		new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(boolVal(dcanlogin->arg));
 		new_record_repl[Anum_pg_authid_rolcanlogin - 1] = true;
 	}
 
 	if (disreplication)
 	{
-		new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(intVal(disreplication->arg));
+		new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(boolVal(disreplication->arg));
 		new_record_repl[Anum_pg_authid_rolreplication - 1] = true;
 	}
 
@@ -779,7 +779,7 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
 
 	if (dbypassRLS)
 	{
-		new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(intVal(dbypassRLS->arg));
+		new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(boolVal(dbypassRLS->arg));
 		new_record_repl[Anum_pg_authid_rolbypassrls - 1] = true;
 	}
 
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 439b903eb8..e0b440b9f6 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2749,6 +2749,9 @@ _copyA_Const(const A_Const *from)
 			case T_Float:
 				COPY_STRING_FIELD(val.fval.fval);
 				break;
+			case T_Boolean:
+				COPY_SCALAR_FIELD(val.boolval.boolval);
+				break;
 			case T_String:
 				COPY_STRING_FIELD(val.sval.sval);
 				break;
@@ -4948,6 +4951,16 @@ _copyFloat(const Float *from)
 	return newnode;
 }
 
+static Boolean *
+_copyBoolean(const Boolean *from)
+{
+	Boolean	   *newnode = makeNode(Boolean);
+
+	COPY_SCALAR_FIELD(boolval);
+
+	return newnode;
+}
+
 static String *
 _copyString(const String *from)
 {
@@ -5355,6 +5368,9 @@ copyObjectImpl(const void *from)
 		case T_Float:
 			retval = _copyFloat(from);
 			break;
+		case T_Boolean:
+			retval = _copyBoolean(from);
+			break;
 		case T_String:
 			retval = _copyString(from);
 			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index be09f91ee2..64522e8db0 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -3138,6 +3138,14 @@ _equalFloat(const Float *a, const Float *b)
 	return true;
 }
 
+static bool
+_equalBoolean(const Boolean *a, const Boolean *b)
+{
+	COMPARE_SCALAR_FIELD(boolval);
+
+	return true;
+}
+
 static bool
 _equalString(const String *a, const String *b)
 {
@@ -3374,6 +3382,9 @@ equal(const void *a, const void *b)
 		case T_Float:
 			retval = _equalFloat(a, b);
 			break;
+		case T_Boolean:
+			retval = _equalBoolean(a, b);
+			break;
 		case T_String:
 			retval = _equalString(a, b);
 			break;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index e276264882..397aa6de3c 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -3535,6 +3535,7 @@ raw_expression_tree_walker(Node *node,
 		case T_SQLValueFunction:
 		case T_Integer:
 		case T_Float:
+		case T_Boolean:
 		case T_String:
 		case T_BitString:
 		case T_ParamRef:
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 1accbc1e78..598adcd844 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -3433,6 +3433,12 @@ _outFloat(StringInfo str, const Float *node)
 	appendStringInfoString(str, node->fval);
 }
 
+static void
+_outBoolean(StringInfo str, const Boolean *node)
+{
+	appendStringInfoString(str, node->boolval ? "true" : "false");
+}
+
 static void
 _outString(StringInfo str, const String *node)
 {
@@ -3845,6 +3851,8 @@ outNode(StringInfo str, const void *obj)
 		_outInteger(str, (Integer *) obj);
 	else if (IsA(obj, Float))
 		_outFloat(str, (Float *) obj);
+	else if (IsA(obj, Boolean))
+		_outBoolean(str, (Boolean *) obj);
 	else if (IsA(obj, String))
 		_outString(str, (String *) obj);
 	else if (IsA(obj, BitString))
diff --git a/src/backend/nodes/read.c b/src/backend/nodes/read.c
index d281f7db6c..b2330f2002 100644
--- a/src/backend/nodes/read.c
+++ b/src/backend/nodes/read.c
@@ -235,7 +235,7 @@ debackslash(const char *token, int length)
  * nodeTokenType -
  *	  returns the type of the node token contained in token.
  *	  It returns one of the following valid NodeTags:
- *		T_Integer, T_Float, T_String, T_BitString
+ *		T_Integer, T_Float, T_Boolean, T_String, T_BitString
  *	  and some of its own:
  *		RIGHT_PAREN, LEFT_PAREN, LEFT_BRACE, OTHER_TOKEN
  *
@@ -283,6 +283,8 @@ nodeTokenType(const char *token, int length)
 		retval = RIGHT_PAREN;
 	else if (*token == '{')
 		retval = LEFT_BRACE;
+	else if (strcmp(token, "true") == 0 || strcmp(token, "false") == 0)
+		retval = T_Boolean;
 	else if (*token == '"' && length > 1 && token[length - 1] == '"')
 		retval = T_String;
 	else if (*token == 'b')
@@ -298,7 +300,7 @@ nodeTokenType(const char *token, int length)
  *
  * This routine applies some semantic knowledge on top of the purely
  * lexical tokenizer pg_strtok().   It can read
- *	* Value token nodes (integers, floats, or strings);
+ *	* Value token nodes (integers, floats, booleans, or strings);
  *	* General nodes (via parseNodeString() from readfuncs.c);
  *	* Lists of the above;
  *	* Lists of integers or OIDs.
@@ -438,6 +440,9 @@ nodeRead(const char *token, int tok_len)
 				result = (Node *) makeFloat(fval);
 			}
 			break;
+		case T_Boolean:
+			result = (Node *) makeBoolean(token[0] == 't');
+			break;
 		case T_String:
 			/* need to remove leading and trailing quotes, and backslashes */
 			result = (Node *) makeString(debackslash(token + 1, tok_len - 2));
diff --git a/src/backend/nodes/value.c b/src/backend/nodes/value.c
index ab2d6f74d3..937bf90e83 100644
--- a/src/backend/nodes/value.c
+++ b/src/backend/nodes/value.c
@@ -42,6 +42,18 @@ makeFloat(char *numericStr)
 	return v;
 }
 
+/*
+ *	makeBoolean
+ */
+Boolean *
+makeBoolean(bool val)
+{
+	Boolean	   *v = makeNode(Boolean);
+
+	v->boolval = val;
+	return v;
+}
+
 /*
  *	makeString
  *
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index adce748a69..1f6498f0a9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -177,10 +177,10 @@ static Node *makeStringConst(char *str, int location);
 static Node *makeStringConstCast(char *str, int location, TypeName *typename);
 static Node *makeIntConst(int val, int location);
 static Node *makeFloatConst(char *str, int location);
+static Node *makeBoolAConst(bool state, int location);
 static Node *makeBitStringConst(char *str, int location);
 static Node *makeNullAConst(int location);
 static Node *makeAConst(Node *v, int location);
-static Node *makeBoolAConst(bool state, int location);
 static RoleSpec *makeRoleSpec(RoleSpecType type, int location);
 static void check_qualified_name(List *names, core_yyscan_t yyscanner);
 static List *check_func_name(List *names, core_yyscan_t yyscanner);
@@ -1133,7 +1133,7 @@ AlterOptRoleElem:
 				}
 			| INHERIT
 				{
-					$$ = makeDefElem("inherit", (Node *)makeInteger(true), @1);
+					$$ = makeDefElem("inherit", (Node *)makeBoolean(true), @1);
 				}
 			| CONNECTION LIMIT SignedIconst
 				{
@@ -1156,36 +1156,36 @@ AlterOptRoleElem:
 					 * size of the main parser.
 					 */
 					if (strcmp($1, "superuser") == 0)
-						$$ = makeDefElem("superuser", (Node *)makeInteger(true), @1);
+						$$ = makeDefElem("superuser", (Node *)makeBoolean(true), @1);
 					else if (strcmp($1, "nosuperuser") == 0)
-						$$ = makeDefElem("superuser", (Node *)makeInteger(false), @1);
+						$$ = makeDefElem("superuser", (Node *)makeBoolean(false), @1);
 					else if (strcmp($1, "createrole") == 0)
-						$$ = makeDefElem("createrole", (Node *)makeInteger(true), @1);
+						$$ = makeDefElem("createrole", (Node *)makeBoolean(true), @1);
 					else if (strcmp($1, "nocreaterole") == 0)
-						$$ = makeDefElem("createrole", (Node *)makeInteger(false), @1);
+						$$ = makeDefElem("createrole", (Node *)makeBoolean(false), @1);
 					else if (strcmp($1, "replication") == 0)
-						$$ = makeDefElem("isreplication", (Node *)makeInteger(true), @1);
+						$$ = makeDefElem("isreplication", (Node *)makeBoolean(true), @1);
 					else if (strcmp($1, "noreplication") == 0)
-						$$ = makeDefElem("isreplication", (Node *)makeInteger(false), @1);
+						$$ = makeDefElem("isreplication", (Node *)makeBoolean(false), @1);
 					else if (strcmp($1, "createdb") == 0)
-						$$ = makeDefElem("createdb", (Node *)makeInteger(true), @1);
+						$$ = makeDefElem("createdb", (Node *)makeBoolean(true), @1);
 					else if (strcmp($1, "nocreatedb") == 0)
-						$$ = makeDefElem("createdb", (Node *)makeInteger(false), @1);
+						$$ = makeDefElem("createdb", (Node *)makeBoolean(false), @1);
 					else if (strcmp($1, "login") == 0)
-						$$ = makeDefElem("canlogin", (Node *)makeInteger(true), @1);
+						$$ = makeDefElem("canlogin", (Node *)makeBoolean(true), @1);
 					else if (strcmp($1, "nologin") == 0)
-						$$ = makeDefElem("canlogin", (Node *)makeInteger(false), @1);
+						$$ = makeDefElem("canlogin", (Node *)makeBoolean(false), @1);
 					else if (strcmp($1, "bypassrls") == 0)
-						$$ = makeDefElem("bypassrls", (Node *)makeInteger(true), @1);
+						$$ = makeDefElem("bypassrls", (Node *)makeBoolean(true), @1);
 					else if (strcmp($1, "nobypassrls") == 0)
-						$$ = makeDefElem("bypassrls", (Node *)makeInteger(false), @1);
+						$$ = makeDefElem("bypassrls", (Node *)makeBoolean(false), @1);
 					else if (strcmp($1, "noinherit") == 0)
 					{
 						/*
 						 * Note that INHERIT is a keyword, so it's handled by main parser, but
 						 * NOINHERIT is handled here.
 						 */
-						$$ = makeDefElem("inherit", (Node *)makeInteger(false), @1);
+						$$ = makeDefElem("inherit", (Node *)makeBoolean(false), @1);
 					}
 					else
 						ereport(ERROR,
@@ -3175,7 +3175,7 @@ copy_opt_item:
 				}
 			| FREEZE
 				{
-					$$ = makeDefElem("freeze", (Node *)makeInteger(true), @1);
+					$$ = makeDefElem("freeze", (Node *)makeBoolean(true), @1);
 				}
 			| DELIMITER opt_as Sconst
 				{
@@ -3191,7 +3191,7 @@ copy_opt_item:
 				}
 			| HEADER_P
 				{
-					$$ = makeDefElem("header", (Node *)makeInteger(true), @1);
+					$$ = makeDefElem("header", (Node *)makeBoolean(true), @1);
 				}
 			| QUOTE opt_as Sconst
 				{
@@ -4499,11 +4499,11 @@ SeqOptElem: AS SimpleTypename
 				}
 			| CYCLE
 				{
-					$$ = makeDefElem("cycle", (Node *)makeInteger(true), @1);
+					$$ = makeDefElem("cycle", (Node *)makeBoolean(true), @1);
 				}
 			| NO CYCLE
 				{
-					$$ = makeDefElem("cycle", (Node *)makeInteger(false), @1);
+					$$ = makeDefElem("cycle", (Node *)makeBoolean(false), @1);
 				}
 			| INCREMENT opt_by NumericOnly
 				{
@@ -4739,7 +4739,7 @@ create_extension_opt_item:
 				}
 			| CASCADE
 				{
-					$$ = makeDefElem("cascade", (Node *)makeInteger(true), @1);
+					$$ = makeDefElem("cascade", (Node *)makeBoolean(true), @1);
 				}
 		;
 
@@ -7934,15 +7934,15 @@ createfunc_opt_list:
 common_func_opt_item:
 			CALLED ON NULL_P INPUT_P
 				{
-					$$ = makeDefElem("strict", (Node *)makeInteger(false), @1);
+					$$ = makeDefElem("strict", (Node *)makeBoolean(false), @1);
 				}
 			| RETURNS NULL_P ON NULL_P INPUT_P
 				{
-					$$ = makeDefElem("strict", (Node *)makeInteger(true), @1);
+					$$ = makeDefElem("strict", (Node *)makeBoolean(true), @1);
 				}
 			| STRICT_P
 				{
-					$$ = makeDefElem("strict", (Node *)makeInteger(true), @1);
+					$$ = makeDefElem("strict", (Node *)makeBoolean(true), @1);
 				}
 			| IMMUTABLE
 				{
@@ -7958,27 +7958,27 @@ common_func_opt_item:
 				}
 			| EXTERNAL SECURITY DEFINER
 				{
-					$$ = makeDefElem("security", (Node *)makeInteger(true), @1);
+					$$ = makeDefElem("security", (Node *)makeBoolean(true), @1);
 				}
 			| EXTERNAL SECURITY INVOKER
 				{
-					$$ = makeDefElem("security", (Node *)makeInteger(false), @1);
+					$$ = makeDefElem("security", (Node *)makeBoolean(false), @1);
 				}
 			| SECURITY DEFINER
 				{
-					$$ = makeDefElem("security", (Node *)makeInteger(true), @1);
+					$$ = makeDefElem("security", (Node *)makeBoolean(true), @1);
 				}
 			| SECURITY INVOKER
 				{
-					$$ = makeDefElem("security", (Node *)makeInteger(false), @1);
+					$$ = makeDefElem("security", (Node *)makeBoolean(false), @1);
 				}
 			| LEAKPROOF
 				{
-					$$ = makeDefElem("leakproof", (Node *)makeInteger(true), @1);
+					$$ = makeDefElem("leakproof", (Node *)makeBoolean(true), @1);
 				}
 			| NOT LEAKPROOF
 				{
-					$$ = makeDefElem("leakproof", (Node *)makeInteger(false), @1);
+					$$ = makeDefElem("leakproof", (Node *)makeBoolean(false), @1);
 				}
 			| COST NumericOnly
 				{
@@ -8018,7 +8018,7 @@ createfunc_opt_item:
 				}
 			| WINDOW
 				{
-					$$ = makeDefElem("window", (Node *)makeInteger(true), @1);
+					$$ = makeDefElem("window", (Node *)makeBoolean(true), @1);
 				}
 			| common_func_opt_item
 				{
@@ -9941,7 +9941,7 @@ AlterSubscriptionStmt:
 					n->kind = ALTER_SUBSCRIPTION_ENABLED;
 					n->subname = $3;
 					n->options = list_make1(makeDefElem("enabled",
-											(Node *)makeInteger(true), @1));
+											(Node *)makeBoolean(true), @1));
 					$$ = (Node *)n;
 				}
 			| ALTER SUBSCRIPTION name DISABLE_P
@@ -9951,7 +9951,7 @@ AlterSubscriptionStmt:
 					n->kind = ALTER_SUBSCRIPTION_ENABLED;
 					n->subname = $3;
 					n->options = list_make1(makeDefElem("enabled",
-											(Node *)makeInteger(false), @1));
+											(Node *)makeBoolean(false), @1));
 					$$ = (Node *)n;
 				}
 		;
@@ -12874,7 +12874,7 @@ xmltable_column_el:
 										(errcode(ERRCODE_SYNTAX_ERROR),
 										 errmsg("conflicting or redundant NULL / NOT NULL declarations for column \"%s\"", fc->colname),
 										 parser_errposition(defel->location)));
-							fc->is_not_null = intVal(defel->arg);
+							fc->is_not_null = boolVal(defel->arg);
 							nullability_seen = true;
 						}
 						else
@@ -12914,9 +12914,9 @@ xmltable_column_option_el:
 			| DEFAULT b_expr
 				{ $$ = makeDefElem("default", $2, @1); }
 			| NOT NULL_P
-				{ $$ = makeDefElem("is_not_null", (Node *) makeInteger(true), @1); }
+				{ $$ = makeDefElem("is_not_null", (Node *) makeBoolean(true), @1); }
 			| NULL_P
-				{ $$ = makeDefElem("is_not_null", (Node *) makeInteger(false), @1); }
+				{ $$ = makeDefElem("is_not_null", (Node *) makeBoolean(false), @1); }
 		;
 
 xml_namespace_list:
@@ -16705,6 +16705,18 @@ makeFloatConst(char *str, int location)
    return (Node *)n;
 }
 
+static Node *
+makeBoolAConst(bool state, int location)
+{
+	A_Const *n = makeNode(A_Const);
+
+	n->val.boolval.type = T_Boolean;
+	n->val.boolval.boolval = state;
+	n->location = location;
+
+   return (Node *)n;
+}
+
 static Node *
 makeBitStringConst(char *str, int location)
 {
@@ -16743,26 +16755,14 @@ makeAConst(Node *v, int location)
 			n = makeIntConst(castNode(Integer, v)->ival, location);
 			break;
 
-		case T_String:
 		default:
-			n = makeStringConst(castNode(String, v)->sval, location);
-			break;
+			/* currently not used */
+			Assert(false);
 	}
 
 	return n;
 }
 
-/* makeBoolAConst()
- * Create an A_Const string node and put it inside a boolean cast.
- */
-static Node *
-makeBoolAConst(bool state, int location)
-{
-	return makeStringConstCast((state ? "t" : "f"),
-							   location,
-							   SystemTypeName("bool"));
-}
-
 /* makeRoleSpec
  * Create a RoleSpec with the given type
  */
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c
index bd92133b21..533669e37e 100644
--- a/src/backend/parser/parse_node.c
+++ b/src/backend/parser/parse_node.c
@@ -426,6 +426,14 @@ make_const(ParseState *pstate, A_Const *aconst)
 			}
 			break;
 
+		case T_Boolean:
+			val = BoolGetDatum(boolVal(&aconst->val));
+
+			typeid = BOOLOID;
+			typelen = 1;
+			typebyval = true;
+			break;
+
 		case T_String:
 
 			/*
diff --git a/src/backend/replication/repl_gram.y b/src/backend/replication/repl_gram.y
index dcb1108579..dd23f98b44 100644
--- a/src/backend/replication/repl_gram.y
+++ b/src/backend/replication/repl_gram.y
@@ -212,7 +212,7 @@ base_backup_legacy_opt:
 			| K_PROGRESS
 				{
 				  $$ = makeDefElem("progress",
-								   (Node *)makeInteger(true), -1);
+								   (Node *)makeBoolean(true), -1);
 				}
 			| K_FAST
 				{
@@ -222,12 +222,12 @@ base_backup_legacy_opt:
 			| K_WAL
 				{
 				  $$ = makeDefElem("wal",
-								   (Node *)makeInteger(true), -1);
+								   (Node *)makeBoolean(true), -1);
 				}
 			| K_NOWAIT
 				{
 				  $$ = makeDefElem("wait",
-								   (Node *)makeInteger(false), -1);
+								   (Node *)makeBoolean(false), -1);
 				}
 			| K_MAX_RATE UCONST
 				{
@@ -237,12 +237,12 @@ base_backup_legacy_opt:
 			| K_TABLESPACE_MAP
 				{
 				  $$ = makeDefElem("tablespace_map",
-								   (Node *)makeInteger(true), -1);
+								   (Node *)makeBoolean(true), -1);
 				}
 			| K_NOVERIFY_CHECKSUMS
 				{
 				  $$ = makeDefElem("verify_checksums",
-								   (Node *)makeInteger(false), -1);
+								   (Node *)makeBoolean(false), -1);
 				}
 			| K_MANIFEST SCONST
 				{
@@ -313,12 +313,12 @@ create_slot_legacy_opt:
 			| K_RESERVE_WAL
 				{
 				  $$ = makeDefElem("reserve_wal",
-								   (Node *)makeInteger(true), -1);
+								   (Node *)makeBoolean(true), -1);
 				}
 			| K_TWO_PHASE
 				{
 				  $$ = makeDefElem("two_phase",
-								   (Node *)makeInteger(true), -1);
+								   (Node *)makeBoolean(true), -1);
 				}
 			;
 
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 7c657c1241..15444c60b7 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -292,6 +292,7 @@ typedef enum NodeTag
 	 */
 	T_Integer,
 	T_Float,
+	T_Boolean,
 	T_String,
 	T_BitString,
 
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 784164b32a..ef12148b1f 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -304,6 +304,7 @@ typedef struct A_Const
 		Node		node;
 		Integer		ival;
 		Float		fval;
+		Boolean		boolval;
 		String		sval;
 		BitString	bsval;
 	}			val;
diff --git a/src/include/nodes/value.h b/src/include/nodes/value.h
index cc3a4a639c..5f877ea9ea 100644
--- a/src/include/nodes/value.h
+++ b/src/include/nodes/value.h
@@ -48,6 +48,12 @@ typedef struct Float
 	char	   *fval;
 } Float;
 
+typedef struct Boolean
+{
+	NodeTag		type;
+	bool		boolval;
+} Boolean;
+
 typedef struct String
 {
 	NodeTag		type;
@@ -62,10 +68,12 @@ typedef struct BitString
 
 #define intVal(v)		(castNode(Integer, v)->ival)
 #define floatVal(v)		atof(castNode(Float, v)->fval)
+#define boolVal(v)		(castNode(Boolean, v)->boolval)
 #define strVal(v)		(castNode(String, v)->sval)
 
 extern Integer *makeInteger(int i);
 extern Float *makeFloat(char *numericStr);
+extern Boolean *makeBoolean(bool var);
 extern String *makeString(char *str);
 extern BitString *makeBitString(char *str);
 
diff --git a/src/test/isolation/expected/ri-trigger.out b/src/test/isolation/expected/ri-trigger.out
index 842df80a90..db85618bef 100644
--- a/src/test/isolation/expected/ri-trigger.out
+++ b/src/test/isolation/expected/ri-trigger.out
@@ -4,9 +4,9 @@ starting permutation: wxry1 c1 r2 wyrx2 c2
 step wxry1: INSERT INTO child (parent_id) VALUES (0);
 step c1: COMMIT;
 step r2: SELECT TRUE;
-bool
-----
-t   
+?column?
+--------
+t       
 (1 row)
 
 step wyrx2: DELETE FROM parent WHERE parent_id = 0;
@@ -16,9 +16,9 @@ step c2: COMMIT;
 starting permutation: wxry1 r2 c1 wyrx2 c2
 step wxry1: INSERT INTO child (parent_id) VALUES (0);
 step r2: SELECT TRUE;
-bool
-----
-t   
+?column?
+--------
+t       
 (1 row)
 
 step c1: COMMIT;
@@ -29,9 +29,9 @@ step c2: COMMIT;
 starting permutation: wxry1 r2 wyrx2 c1 c2
 step wxry1: INSERT INTO child (parent_id) VALUES (0);
 step r2: SELECT TRUE;
-bool
-----
-t   
+?column?
+--------
+t       
 (1 row)
 
 step wyrx2: DELETE FROM parent WHERE parent_id = 0;
@@ -42,9 +42,9 @@ ERROR:  could not serialize access due to read/write dependencies among transact
 starting permutation: wxry1 r2 wyrx2 c2 c1
 step wxry1: INSERT INTO child (parent_id) VALUES (0);
 step r2: SELECT TRUE;
-bool
-----
-t   
+?column?
+--------
+t       
 (1 row)
 
 step wyrx2: DELETE FROM parent WHERE parent_id = 0;
@@ -54,9 +54,9 @@ ERROR:  could not serialize access due to read/write dependencies among transact
 
 starting permutation: r2 wxry1 c1 wyrx2 c2
 step r2: SELECT TRUE;
-bool
-----
-t   
+?column?
+--------
+t       
 (1 row)
 
 step wxry1: INSERT INTO child (parent_id) VALUES (0);
@@ -67,9 +67,9 @@ step c2: COMMIT;
 
 starting permutation: r2 wxry1 wyrx2 c1 c2
 step r2: SELECT TRUE;
-bool
-----
-t   
+?column?
+--------
+t       
 (1 row)
 
 step wxry1: INSERT INTO child (parent_id) VALUES (0);
@@ -80,9 +80,9 @@ ERROR:  could not serialize access due to read/write dependencies among transact
 
 starting permutation: r2 wxry1 wyrx2 c2 c1
 step r2: SELECT TRUE;
-bool
-----
-t   
+?column?
+--------
+t       
 (1 row)
 
 step wxry1: INSERT INTO child (parent_id) VALUES (0);
@@ -93,9 +93,9 @@ ERROR:  could not serialize access due to read/write dependencies among transact
 
 starting permutation: r2 wyrx2 wxry1 c1 c2
 step r2: SELECT TRUE;
-bool
-----
-t   
+?column?
+--------
+t       
 (1 row)
 
 step wyrx2: DELETE FROM parent WHERE parent_id = 0;
@@ -106,9 +106,9 @@ ERROR:  could not serialize access due to read/write dependencies among transact
 
 starting permutation: r2 wyrx2 wxry1 c2 c1
 step r2: SELECT TRUE;
-bool
-----
-t   
+?column?
+--------
+t       
 (1 row)
 
 step wyrx2: DELETE FROM parent WHERE parent_id = 0;
@@ -119,9 +119,9 @@ ERROR:  could not serialize access due to read/write dependencies among transact
 
 starting permutation: r2 wyrx2 c2 wxry1 c1
 step r2: SELECT TRUE;
-bool
-----
-t   
+?column?
+--------
+t       
 (1 row)
 
 step wyrx2: DELETE FROM parent WHERE parent_id = 0;
diff --git a/src/test/regress/expected/create_function_3.out b/src/test/regress/expected/create_function_3.out
index 3a4fd45147..e0c4bee893 100644
--- a/src/test/regress/expected/create_function_3.out
+++ b/src/test/regress/expected/create_function_3.out
@@ -403,7 +403,7 @@ SELECT pg_get_functiondef('functest_S_13'::regproc);
   LANGUAGE sql                                            +
  BEGIN ATOMIC                                             +
   SELECT 1;                                               +
-  SELECT false AS bool;                                   +
+  SELECT false;                                           +
  END                                                      +
  
 (1 row)
-- 
2.34.1

#25Peter Eisentraut
peter.eisentraut@enterprisedb.com
In reply to: Peter Eisentraut (#24)
3 attachment(s)
Re: Add Boolean node

On 03.01.22 12:04, Peter Eisentraut wrote:

On 27.12.21 10:02, Peter Eisentraut wrote:

This patch adds a new node type Boolean, to go alongside the "value"
nodes Integer, Float, String, etc.  This seems appropriate given that
Boolean values are a fundamental part of the system and are used a lot.

Before, SQL-level Boolean constants were represented by a string with
a cast, and internal Boolean values in DDL commands were usually
represented by Integer nodes.  This takes the place of both of these
uses, making the intent clearer and having some amount of type safety.

Here is an update of this patch set based on the feedback.  First, I
added a patch that makes some changes in AlterRole() that my original
patch might have broken or at least made more confusing.  Unlike in
CreateRole(), we use three-valued logic here, so that a variable like
issuper would have 0 = no, 1 = yes, -1 = not specified, keep previous
value.  I'm simplifying this, by instead using the dissuper etc.
variables to track whether a setting was specified.  This makes
everything a bit simpler and makes the subsequent patch easier.

Second, I added the suggest by Tom Lane to rename to fields in the
used-to-be-Value nodes to be different in each node type (ival, fval,
etc.).  I agree that this makes things a bit cleaner and reduces the
changes of mixups.

And third, the original patch that introduces the Boolean node with some
small changes based on the feedback.

Another very small update, attempting to appease the cfbot.

Attachments:

v3-0001-Refactor-AlterRole.patchtext/plain; charset=UTF-8; name=v3-0001-Refactor-AlterRole.patchDownload
From 8d5f7f330c0824c862a3c0d8ce5cd3d578e22d82 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 3 Jan 2022 09:37:12 +0100
Subject: [PATCH v3 1/3] Refactor AlterRole()

Get rid of the three-valued logic for the Boolean variables to track
whether the value was been specified and what the new value should be.
Instead, we can use the "dfoo" variables to determine whether the
value was specified and should be applied.  This was already done in
some cases, so this makes this more uniform and removes one layer of
indirection.
---
 src/backend/commands/user.c | 97 +++++++++++++------------------------
 1 file changed, 35 insertions(+), 62 deletions(-)

diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index aa69821be4..0aae87ff4a 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -501,20 +501,12 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
 				new_tuple;
 	Form_pg_authid authform;
 	ListCell   *option;
-	char	   *rolename = NULL;
+	char	   *rolename;
 	char	   *password = NULL;	/* user password */
-	int			issuper = -1;	/* Make the user a superuser? */
-	int			inherit = -1;	/* Auto inherit privileges? */
-	int			createrole = -1;	/* Can this user create roles? */
-	int			createdb = -1;	/* Can the user create databases? */
-	int			canlogin = -1;	/* Can this user login? */
-	int			isreplication = -1; /* Is this a replication role? */
 	int			connlimit = -1; /* maximum connections allowed */
-	List	   *rolemembers = NIL;	/* roles to be added/removed */
 	char	   *validUntil = NULL;	/* time the login is valid until */
 	Datum		validUntil_datum;	/* same, as timestamptz Datum */
 	bool		validUntil_null;
-	int			bypassrls = -1;
 	DefElem    *dpassword = NULL;
 	DefElem    *dissuper = NULL;
 	DefElem    *dinherit = NULL;
@@ -610,18 +602,6 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
 
 	if (dpassword && dpassword->arg)
 		password = strVal(dpassword->arg);
-	if (dissuper)
-		issuper = intVal(dissuper->arg);
-	if (dinherit)
-		inherit = intVal(dinherit->arg);
-	if (dcreaterole)
-		createrole = intVal(dcreaterole->arg);
-	if (dcreatedb)
-		createdb = intVal(dcreatedb->arg);
-	if (dcanlogin)
-		canlogin = intVal(dcanlogin->arg);
-	if (disreplication)
-		isreplication = intVal(disreplication->arg);
 	if (dconnlimit)
 	{
 		connlimit = intVal(dconnlimit->arg);
@@ -630,12 +610,8 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 					 errmsg("invalid connection limit: %d", connlimit)));
 	}
-	if (drolemembers)
-		rolemembers = (List *) drolemembers->arg;
 	if (dvalidUntil)
 		validUntil = strVal(dvalidUntil->arg);
-	if (dbypassRLS)
-		bypassrls = intVal(dbypassRLS->arg);
 
 	/*
 	 * Scan the pg_authid relation to be certain the user exists.
@@ -654,21 +630,21 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
 	 * property.  Otherwise, if you don't have createrole, you're only allowed
 	 * to change your own password.
 	 */
-	if (authform->rolsuper || issuper >= 0)
+	if (authform->rolsuper || dissuper)
 	{
 		if (!superuser())
 			ereport(ERROR,
 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 					 errmsg("must be superuser to alter superuser roles or change superuser attribute")));
 	}
-	else if (authform->rolreplication || isreplication >= 0)
+	else if (authform->rolreplication || disreplication)
 	{
 		if (!superuser())
 			ereport(ERROR,
 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 					 errmsg("must be superuser to alter replication roles or change replication attribute")));
 	}
-	else if (bypassrls >= 0)
+	else if (dbypassRLS)
 	{
 		if (!superuser())
 			ereport(ERROR,
@@ -677,23 +653,16 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
 	}
 	else if (!have_createrole_privilege())
 	{
-		/* We already checked issuper, isreplication, and bypassrls */
-		if (!(inherit < 0 &&
-			  createrole < 0 &&
-			  createdb < 0 &&
-			  canlogin < 0 &&
-			  !dconnlimit &&
-			  !rolemembers &&
-			  !validUntil &&
-			  dpassword &&
-			  roleid == GetUserId()))
+		/* check the rest */
+		if (dinherit || dcreaterole || dcreatedb || dcanlogin || dconnlimit ||
+			drolemembers || dvalidUntil || !dpassword || roleid != GetUserId())
 			ereport(ERROR,
 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 					 errmsg("permission denied")));
 	}
 
 	/* Convert validuntil to internal form */
-	if (validUntil)
+	if (dvalidUntil)
 	{
 		validUntil_datum = DirectFunctionCall3(timestamptz_in,
 											   CStringGetDatum(validUntil),
@@ -729,39 +698,39 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
 	/*
 	 * issuper/createrole/etc
 	 */
-	if (issuper >= 0)
+	if (dissuper)
 	{
-		new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper > 0);
+		new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(intVal(dissuper->arg));
 		new_record_repl[Anum_pg_authid_rolsuper - 1] = true;
 	}
 
-	if (inherit >= 0)
+	if (dinherit)
 	{
-		new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit > 0);
+		new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(intVal(dinherit->arg));
 		new_record_repl[Anum_pg_authid_rolinherit - 1] = true;
 	}
 
-	if (createrole >= 0)
+	if (dcreaterole)
 	{
-		new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole > 0);
+		new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(intVal(dcreaterole->arg));
 		new_record_repl[Anum_pg_authid_rolcreaterole - 1] = true;
 	}
 
-	if (createdb >= 0)
+	if (dcreatedb)
 	{
-		new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb > 0);
+		new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(intVal(dcreatedb->arg));
 		new_record_repl[Anum_pg_authid_rolcreatedb - 1] = true;
 	}
 
-	if (canlogin >= 0)
+	if (dcanlogin)
 	{
-		new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin > 0);
+		new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(intVal(dcanlogin->arg));
 		new_record_repl[Anum_pg_authid_rolcanlogin - 1] = true;
 	}
 
-	if (isreplication >= 0)
+	if (disreplication)
 	{
-		new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication > 0);
+		new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(intVal(disreplication->arg));
 		new_record_repl[Anum_pg_authid_rolreplication - 1] = true;
 	}
 
@@ -808,9 +777,9 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
 	new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = validUntil_null;
 	new_record_repl[Anum_pg_authid_rolvaliduntil - 1] = true;
 
-	if (bypassrls >= 0)
+	if (dbypassRLS)
 	{
-		new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(bypassrls > 0);
+		new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(intVal(dbypassRLS->arg));
 		new_record_repl[Anum_pg_authid_rolbypassrls - 1] = true;
 	}
 
@@ -827,17 +796,21 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
 	 * Advance command counter so we can see new record; else tests in
 	 * AddRoleMems may fail.
 	 */
-	if (rolemembers)
+	if (drolemembers)
+	{
+		List	   *rolemembers	= (List *) drolemembers->arg;
+
 		CommandCounterIncrement();
 
-	if (stmt->action == +1)		/* add members to role */
-		AddRoleMems(rolename, roleid,
-					rolemembers, roleSpecsToIds(rolemembers),
-					GetUserId(), false);
-	else if (stmt->action == -1)	/* drop members from role */
-		DelRoleMems(rolename, roleid,
-					rolemembers, roleSpecsToIds(rolemembers),
-					false);
+		if (stmt->action == +1)		/* add members to role */
+			AddRoleMems(rolename, roleid,
+						rolemembers, roleSpecsToIds(rolemembers),
+						GetUserId(), false);
+		else if (stmt->action == -1)	/* drop members from role */
+			DelRoleMems(rolename, roleid,
+						rolemembers, roleSpecsToIds(rolemembers),
+						false);
+	}
 
 	/*
 	 * Close pg_authid, but keep lock till commit.

base-commit: 69872d0bbe64fcd67c4fb4c61e5c7bf6a3443a47
-- 
2.34.1

v3-0002-Rename-value-node-fields.patchtext/plain; charset=UTF-8; name=v3-0002-Rename-value-node-fields.patchDownload
From 08010ba3345fb8020587326304d6a6dafe0f098c Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 3 Jan 2022 09:59:44 +0100
Subject: [PATCH v3 2/3] Rename value node fields

For the formerly-Value node types, rename the "val" field to a name
specific to the node type, namely "ival", "fval", "sval", and "bsval".
This makes some code clearer and catches mixups better.
---
 src/backend/commands/define.c      |  4 ++--
 src/backend/nodes/copyfuncs.c      | 16 ++++++++--------
 src/backend/nodes/equalfuncs.c     |  8 ++++----
 src/backend/nodes/outfuncs.c       | 10 +++++-----
 src/backend/nodes/value.c          |  8 ++++----
 src/backend/parser/gram.y          | 24 ++++++++++++------------
 src/backend/parser/parse_node.c    | 10 +++++-----
 src/backend/parser/parse_type.c    |  6 +++---
 src/backend/parser/parse_utilcmd.c |  2 +-
 src/backend/utils/adt/oid.c        |  2 +-
 src/backend/utils/misc/guc.c       |  2 +-
 src/include/nodes/value.h          | 14 +++++++-------
 12 files changed, 53 insertions(+), 53 deletions(-)

diff --git a/src/backend/commands/define.c b/src/backend/commands/define.c
index 19c317a472..0fe7e471d9 100644
--- a/src/backend/commands/define.c
+++ b/src/backend/commands/define.c
@@ -58,7 +58,7 @@ defGetString(DefElem *def)
 		case T_Integer:
 			return psprintf("%ld", (long) intVal(def->arg));
 		case T_Float:
-			return castNode(Float, def->arg)->val;
+			return castNode(Float, def->arg)->fval;
 		case T_String:
 			return strVal(def->arg);
 		case T_TypeName:
@@ -201,7 +201,7 @@ defGetInt64(DefElem *def)
 			 * strings.
 			 */
 			return DatumGetInt64(DirectFunctionCall1(int8in,
-													 CStringGetDatum(castNode(Float, def->arg)->val)));
+													 CStringGetDatum(castNode(Float, def->arg)->fval)));
 		default:
 			ereport(ERROR,
 					(errcode(ERRCODE_SYNTAX_ERROR),
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index df0b747883..439b903eb8 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2744,16 +2744,16 @@ _copyA_Const(const A_Const *from)
 		switch (nodeTag(&from->val))
 		{
 			case T_Integer:
-				COPY_SCALAR_FIELD(val.ival.val);
+				COPY_SCALAR_FIELD(val.ival.ival);
 				break;
 			case T_Float:
-				COPY_STRING_FIELD(val.fval.val);
+				COPY_STRING_FIELD(val.fval.fval);
 				break;
 			case T_String:
-				COPY_STRING_FIELD(val.sval.val);
+				COPY_STRING_FIELD(val.sval.sval);
 				break;
 			case T_BitString:
-				COPY_STRING_FIELD(val.bsval.val);
+				COPY_STRING_FIELD(val.bsval.bsval);
 				break;
 			default:
 				elog(ERROR, "unrecognized node type: %d",
@@ -4933,7 +4933,7 @@ _copyInteger(const Integer *from)
 {
 	Integer	   *newnode = makeNode(Integer);
 
-	COPY_SCALAR_FIELD(val);
+	COPY_SCALAR_FIELD(ival);
 
 	return newnode;
 }
@@ -4943,7 +4943,7 @@ _copyFloat(const Float *from)
 {
 	Float	   *newnode = makeNode(Float);
 
-	COPY_STRING_FIELD(val);
+	COPY_STRING_FIELD(fval);
 
 	return newnode;
 }
@@ -4953,7 +4953,7 @@ _copyString(const String *from)
 {
 	String	   *newnode = makeNode(String);
 
-	COPY_STRING_FIELD(val);
+	COPY_STRING_FIELD(sval);
 
 	return newnode;
 }
@@ -4963,7 +4963,7 @@ _copyBitString(const BitString *from)
 {
 	BitString   *newnode = makeNode(BitString);
 
-	COPY_STRING_FIELD(val);
+	COPY_STRING_FIELD(bsval);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index cb7ddd463c..be09f91ee2 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -3125,7 +3125,7 @@ _equalList(const List *a, const List *b)
 static bool
 _equalInteger(const Integer *a, const Integer *b)
 {
-	COMPARE_SCALAR_FIELD(val);
+	COMPARE_SCALAR_FIELD(ival);
 
 	return true;
 }
@@ -3133,7 +3133,7 @@ _equalInteger(const Integer *a, const Integer *b)
 static bool
 _equalFloat(const Float *a, const Float *b)
 {
-	COMPARE_STRING_FIELD(val);
+	COMPARE_STRING_FIELD(fval);
 
 	return true;
 }
@@ -3141,7 +3141,7 @@ _equalFloat(const Float *a, const Float *b)
 static bool
 _equalString(const String *a, const String *b)
 {
-	COMPARE_STRING_FIELD(val);
+	COMPARE_STRING_FIELD(sval);
 
 	return true;
 }
@@ -3149,7 +3149,7 @@ _equalString(const String *a, const String *b)
 static bool
 _equalBitString(const BitString *a, const BitString *b)
 {
-	COMPARE_STRING_FIELD(val);
+	COMPARE_STRING_FIELD(bsval);
 
 	return true;
 }
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 91a89b6d51..1accbc1e78 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -3420,7 +3420,7 @@ _outA_Expr(StringInfo str, const A_Expr *node)
 static void
 _outInteger(StringInfo str, const Integer *node)
 {
-	appendStringInfo(str, "%d", node->val);
+	appendStringInfo(str, "%d", node->ival);
 }
 
 static void
@@ -3430,7 +3430,7 @@ _outFloat(StringInfo str, const Float *node)
 	 * We assume the value is a valid numeric literal and so does not
 	 * need quoting.
 	 */
-	appendStringInfoString(str, node->val);
+	appendStringInfoString(str, node->fval);
 }
 
 static void
@@ -3441,8 +3441,8 @@ _outString(StringInfo str, const String *node)
 	 * but we don't want it to do anything with an empty string.
 	 */
 	appendStringInfoChar(str, '"');
-	if (node->val[0] != '\0')
-		outToken(str, node->val);
+	if (node->sval[0] != '\0')
+		outToken(str, node->sval);
 	appendStringInfoChar(str, '"');
 }
 
@@ -3450,7 +3450,7 @@ static void
 _outBitString(StringInfo str, const BitString *node)
 {
 	/* internal representation already has leading 'b' */
-	appendStringInfoString(str, node->val);
+	appendStringInfoString(str, node->bsval);
 }
 
 static void
diff --git a/src/backend/nodes/value.c b/src/backend/nodes/value.c
index 515f93c223..ab2d6f74d3 100644
--- a/src/backend/nodes/value.c
+++ b/src/backend/nodes/value.c
@@ -24,7 +24,7 @@ makeInteger(int i)
 {
 	Integer	   *v = makeNode(Integer);
 
-	v->val = i;
+	v->ival = i;
 	return v;
 }
 
@@ -38,7 +38,7 @@ makeFloat(char *numericStr)
 {
 	Float	   *v = makeNode(Float);
 
-	v->val = numericStr;
+	v->fval = numericStr;
 	return v;
 }
 
@@ -52,7 +52,7 @@ makeString(char *str)
 {
 	String	   *v = makeNode(String);
 
-	v->val = str;
+	v->sval = str;
 	return v;
 }
 
@@ -66,6 +66,6 @@ makeBitString(char *str)
 {
 	BitString  *v = makeNode(BitString);
 
-	v->val = str;
+	v->bsval = str;
 	return v;
 }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index f3c232842d..adce748a69 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -1719,7 +1719,7 @@ zone_value:
 					if ($3 != NIL)
 					{
 						A_Const *n = (A_Const *) linitial($3);
-						if ((n->val.ival.val & ~(INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE))) != 0)
+						if ((n->val.ival.ival & ~(INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE))) != 0)
 							ereport(ERROR,
 									(errcode(ERRCODE_SYNTAX_ERROR),
 									 errmsg("time zone interval must be HOUR or HOUR TO MINUTE"),
@@ -16667,7 +16667,7 @@ makeStringConst(char *str, int location)
 	A_Const *n = makeNode(A_Const);
 
 	n->val.sval.type = T_String;
-	n->val.sval.val = str;
+	n->val.sval.sval = str;
 	n->location = location;
 
    return (Node *)n;
@@ -16687,7 +16687,7 @@ makeIntConst(int val, int location)
 	A_Const *n = makeNode(A_Const);
 
 	n->val.ival.type = T_Integer;
-	n->val.ival.val = val;
+	n->val.ival.ival = val;
 	n->location = location;
 
    return (Node *)n;
@@ -16699,7 +16699,7 @@ makeFloatConst(char *str, int location)
 	A_Const *n = makeNode(A_Const);
 
 	n->val.fval.type = T_Float;
-	n->val.fval.val = str;
+	n->val.fval.fval = str;
 	n->location = location;
 
    return (Node *)n;
@@ -16711,7 +16711,7 @@ makeBitStringConst(char *str, int location)
 	A_Const *n = makeNode(A_Const);
 
 	n->val.bsval.type = T_BitString;
-	n->val.bsval.val = str;
+	n->val.bsval.bsval = str;
 	n->location = location;
 
    return (Node *)n;
@@ -16736,16 +16736,16 @@ makeAConst(Node *v, int location)
 	switch (v->type)
 	{
 		case T_Float:
-			n = makeFloatConst(castNode(Float, v)->val, location);
+			n = makeFloatConst(castNode(Float, v)->fval, location);
 			break;
 
 		case T_Integer:
-			n = makeIntConst(castNode(Integer, v)->val, location);
+			n = makeIntConst(castNode(Integer, v)->ival, location);
 			break;
 
 		case T_String:
 		default:
-			n = makeStringConst(castNode(String, v)->val, location);
+			n = makeStringConst(castNode(String, v)->sval, location);
 			break;
 	}
 
@@ -17049,7 +17049,7 @@ doNegate(Node *n, int location)
 
 		if (IsA(&con->val, Integer))
 		{
-			con->val.ival.val = -con->val.ival.val;
+			con->val.ival.ival = -con->val.ival.ival;
 			return n;
 		}
 		if (IsA(&con->val, Float))
@@ -17065,14 +17065,14 @@ doNegate(Node *n, int location)
 static void
 doNegateFloat(Float *v)
 {
-	char   *oldval = v->val;
+	char   *oldval = v->fval;
 
 	if (*oldval == '+')
 		oldval++;
 	if (*oldval == '-')
-		v->val = oldval+1;	/* just strip the '-' */
+		v->fval = oldval+1;	/* just strip the '-' */
 	else
-		v->val = psprintf("-%s", oldval);
+		v->fval = psprintf("-%s", oldval);
 }
 
 static Node *
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c
index 8cfe6f67c0..bd92133b21 100644
--- a/src/backend/parser/parse_node.c
+++ b/src/backend/parser/parse_node.c
@@ -376,7 +376,7 @@ make_const(ParseState *pstate, A_Const *aconst)
 	switch (nodeTag(&aconst->val))
 	{
 		case T_Integer:
-			val = Int32GetDatum(aconst->val.ival.val);
+			val = Int32GetDatum(intVal(&aconst->val));
 
 			typeid = INT4OID;
 			typelen = sizeof(int32);
@@ -385,7 +385,7 @@ make_const(ParseState *pstate, A_Const *aconst)
 
 		case T_Float:
 			/* could be an oversize integer as well as a float ... */
-			if (scanint8(aconst->val.fval.val, true, &val64))
+			if (scanint8(aconst->val.fval.fval, true, &val64))
 			{
 				/*
 				 * It might actually fit in int32. Probably only INT_MIN can
@@ -415,7 +415,7 @@ make_const(ParseState *pstate, A_Const *aconst)
 				/* arrange to report location if numeric_in() fails */
 				setup_parser_errposition_callback(&pcbstate, pstate, aconst->location);
 				val = DirectFunctionCall3(numeric_in,
-										  CStringGetDatum(aconst->val.fval.val),
+										  CStringGetDatum(aconst->val.fval.fval),
 										  ObjectIdGetDatum(InvalidOid),
 										  Int32GetDatum(-1));
 				cancel_parser_errposition_callback(&pcbstate);
@@ -432,7 +432,7 @@ make_const(ParseState *pstate, A_Const *aconst)
 			 * We assume here that UNKNOWN's internal representation is the
 			 * same as CSTRING
 			 */
-			val = CStringGetDatum(aconst->val.sval.val);
+			val = CStringGetDatum(strVal(&aconst->val));
 
 			typeid = UNKNOWNOID;	/* will be coerced later */
 			typelen = -2;		/* cstring-style varwidth type */
@@ -443,7 +443,7 @@ make_const(ParseState *pstate, A_Const *aconst)
 			/* arrange to report location if bit_in() fails */
 			setup_parser_errposition_callback(&pcbstate, pstate, aconst->location);
 			val = DirectFunctionCall3(bit_in,
-									  CStringGetDatum(aconst->val.bsval.val),
+									  CStringGetDatum(aconst->val.bsval.bsval),
 									  ObjectIdGetDatum(InvalidOid),
 									  Int32GetDatum(-1));
 			cancel_parser_errposition_callback(&pcbstate);
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index 31b07ad5ae..f2d5e98127 100644
--- a/src/backend/parser/parse_type.c
+++ b/src/backend/parser/parse_type.c
@@ -382,17 +382,17 @@ typenameTypeMod(ParseState *pstate, const TypeName *typeName, Type typ)
 
 			if (IsA(&ac->val, Integer))
 			{
-				cstr = psprintf("%ld", (long) ac->val.ival.val);
+				cstr = psprintf("%ld", (long) intVal(&ac->val));
 			}
 			else if (IsA(&ac->val, Float))
 			{
 				/* we can just use the string representation directly. */
-				cstr = ac->val.fval.val;
+				cstr = ac->val.fval.fval;
 			}
 			else if (IsA(&ac->val, String))
 			{
 				/* we can just use the string representation directly. */
-				cstr = ac->val.sval.val;
+				cstr = strVal(&ac->val);
 			}
 		}
 		else if (IsA(tm, ColumnRef))
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 2d857a301b..2c64f12a78 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -603,7 +603,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 		qstring = quote_qualified_identifier(snamespace, sname);
 		snamenode = makeNode(A_Const);
 		snamenode->val.node.type = T_String;
-		snamenode->val.sval.val = qstring;
+		snamenode->val.sval.sval = qstring;
 		snamenode->location = -1;
 		castnode = makeNode(TypeCast);
 		castnode->typeName = SystemTypeName("regclass");
diff --git a/src/backend/utils/adt/oid.c b/src/backend/utils/adt/oid.c
index 7be260663e..4e684fe91c 100644
--- a/src/backend/utils/adt/oid.c
+++ b/src/backend/utils/adt/oid.c
@@ -324,7 +324,7 @@ oidparse(Node *node)
 			 * constants by the lexer.  Accept these if they are valid OID
 			 * strings.
 			 */
-			return oidin_subr(castNode(Float, node)->val, NULL);
+			return oidin_subr(castNode(Float, node)->fval, NULL);
 		default:
 			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
 	}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index f9504d3aec..ff95c3631c 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -8332,7 +8332,7 @@ flatten_set_variable_args(const char *name, List *args)
 				break;
 			case T_Float:
 				/* represented as a string, so just copy it */
-				appendStringInfoString(&buf, castNode(Float, &con->val)->val);
+				appendStringInfoString(&buf, castNode(Float, &con->val)->fval);
 				break;
 			case T_String:
 				val = strVal(&con->val);
diff --git a/src/include/nodes/value.h b/src/include/nodes/value.h
index 8b71b510eb..cc3a4a639c 100644
--- a/src/include/nodes/value.h
+++ b/src/include/nodes/value.h
@@ -28,7 +28,7 @@
 typedef struct Integer
 {
 	NodeTag		type;
-	int			val;
+	int			ival;
 } Integer;
 
 /*
@@ -45,24 +45,24 @@ typedef struct Integer
 typedef struct Float
 {
 	NodeTag		type;
-	char	   *val;
+	char	   *fval;
 } Float;
 
 typedef struct String
 {
 	NodeTag		type;
-	char	   *val;
+	char	   *sval;
 } String;
 
 typedef struct BitString
 {
 	NodeTag		type;
-	char	   *val;
+	char	   *bsval;
 } BitString;
 
-#define intVal(v)		(castNode(Integer, v)->val)
-#define floatVal(v)		atof(castNode(Float, v)->val)
-#define strVal(v)		(castNode(String, v)->val)
+#define intVal(v)		(castNode(Integer, v)->ival)
+#define floatVal(v)		atof(castNode(Float, v)->fval)
+#define strVal(v)		(castNode(String, v)->sval)
 
 extern Integer *makeInteger(int i);
 extern Float *makeFloat(char *numericStr);
-- 
2.34.1

v3-0003-Add-Boolean-node.patchtext/plain; charset=UTF-8; name=v3-0003-Add-Boolean-node.patchDownload
From e4278f65fe7c9d9c88af3abd26604d27f124c752 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 3 Jan 2022 14:16:04 +0100
Subject: [PATCH v3 3/3] Add Boolean node

Before, SQL-level boolean constants were represented by a string with
a cast, and internal Boolean values in DDL commands were usually
represented by Integer nodes.  This takes the place of both of these
uses, making the intent clearer and having some amount of type safety.
---
 contrib/postgres_fdw/postgres_fdw.c           | 32 +++---
 src/backend/commands/define.c                 |  2 +
 src/backend/commands/functioncmds.c           | 14 +--
 src/backend/commands/sequence.c               |  4 +-
 src/backend/commands/tsearchcmds.c            |  9 ++
 src/backend/commands/user.c                   | 28 +++---
 src/backend/nodes/copyfuncs.c                 | 16 +++
 src/backend/nodes/equalfuncs.c                | 11 +++
 src/backend/nodes/nodeFuncs.c                 |  1 +
 src/backend/nodes/outfuncs.c                  |  8 ++
 src/backend/nodes/read.c                      |  9 +-
 src/backend/nodes/value.c                     | 12 +++
 src/backend/parser/gram.y                     | 99 ++++++++++---------
 src/backend/parser/parse_node.c               |  8 ++
 src/backend/replication/repl_gram.y           | 14 +--
 src/include/nodes/nodes.h                     |  1 +
 src/include/nodes/parsenodes.h                |  1 +
 src/include/nodes/value.h                     |  8 ++
 src/test/isolation/expected/ri-trigger.out    | 60 +++++------
 .../regress/expected/create_function_3.out    |  2 +-
 20 files changed, 211 insertions(+), 128 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index fa9a099f13..4d61d88d7b 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -103,7 +103,7 @@ enum FdwModifyPrivateIndex
 	FdwModifyPrivateTargetAttnums,
 	/* Length till the end of VALUES clause (as an Integer node) */
 	FdwModifyPrivateLen,
-	/* has-returning flag (as an Integer node) */
+	/* has-returning flag (as a Boolean node) */
 	FdwModifyPrivateHasReturning,
 	/* Integer list of attribute numbers retrieved by RETURNING */
 	FdwModifyPrivateRetrievedAttrs
@@ -122,11 +122,11 @@ enum FdwDirectModifyPrivateIndex
 {
 	/* SQL statement to execute remotely (as a String node) */
 	FdwDirectModifyPrivateUpdateSql,
-	/* has-returning flag (as an Integer node) */
+	/* has-returning flag (as a Boolean node) */
 	FdwDirectModifyPrivateHasReturning,
 	/* Integer list of attribute numbers retrieved by RETURNING */
 	FdwDirectModifyPrivateRetrievedAttrs,
-	/* set-processed flag (as an Integer node) */
+	/* set-processed flag (as a Boolean node) */
 	FdwDirectModifyPrivateSetProcessed
 };
 
@@ -280,9 +280,9 @@ typedef struct PgFdwAnalyzeState
  */
 enum FdwPathPrivateIndex
 {
-	/* has-final-sort flag (as an Integer node) */
+	/* has-final-sort flag (as a Boolean node) */
 	FdwPathPrivateHasFinalSort,
-	/* has-limit flag (as an Integer node) */
+	/* has-limit flag (as a Boolean node) */
 	FdwPathPrivateHasLimit
 };
 
@@ -1245,9 +1245,9 @@ postgresGetForeignPlan(PlannerInfo *root,
 	 */
 	if (best_path->fdw_private)
 	{
-		has_final_sort = intVal(list_nth(best_path->fdw_private,
+		has_final_sort = boolVal(list_nth(best_path->fdw_private,
 										 FdwPathPrivateHasFinalSort));
-		has_limit = intVal(list_nth(best_path->fdw_private,
+		has_limit = boolVal(list_nth(best_path->fdw_private,
 									FdwPathPrivateHasLimit));
 	}
 
@@ -1879,7 +1879,7 @@ postgresPlanForeignModify(PlannerInfo *root,
 	return list_make5(makeString(sql.data),
 					  targetAttrs,
 					  makeInteger(values_end_len),
-					  makeInteger((retrieved_attrs != NIL)),
+					  makeBoolean((retrieved_attrs != NIL)),
 					  retrieved_attrs);
 }
 
@@ -1916,7 +1916,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate,
 									 FdwModifyPrivateTargetAttnums);
 	values_end_len = intVal(list_nth(fdw_private,
 									 FdwModifyPrivateLen));
-	has_returning = intVal(list_nth(fdw_private,
+	has_returning = boolVal(list_nth(fdw_private,
 									FdwModifyPrivateHasReturning));
 	retrieved_attrs = (List *) list_nth(fdw_private,
 										FdwModifyPrivateRetrievedAttrs);
@@ -2567,9 +2567,9 @@ postgresPlanDirectModify(PlannerInfo *root,
 	 * Items in the list must match enum FdwDirectModifyPrivateIndex, above.
 	 */
 	fscan->fdw_private = list_make4(makeString(sql.data),
-									makeInteger((retrieved_attrs != NIL)),
+									makeBoolean((retrieved_attrs != NIL)),
 									retrieved_attrs,
-									makeInteger(plan->canSetTag));
+									makeBoolean(plan->canSetTag));
 
 	/*
 	 * Update the foreign-join-related fields.
@@ -2667,11 +2667,11 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags)
 	/* Get private info created by planner functions. */
 	dmstate->query = strVal(list_nth(fsplan->fdw_private,
 									 FdwDirectModifyPrivateUpdateSql));
-	dmstate->has_returning = intVal(list_nth(fsplan->fdw_private,
+	dmstate->has_returning = boolVal(list_nth(fsplan->fdw_private,
 											 FdwDirectModifyPrivateHasReturning));
 	dmstate->retrieved_attrs = (List *) list_nth(fsplan->fdw_private,
 												 FdwDirectModifyPrivateRetrievedAttrs);
-	dmstate->set_processed = intVal(list_nth(fsplan->fdw_private,
+	dmstate->set_processed = boolVal(list_nth(fsplan->fdw_private,
 											 FdwDirectModifyPrivateSetProcessed));
 
 	/* Create context for per-tuple temp workspace. */
@@ -6566,7 +6566,7 @@ add_foreign_ordered_paths(PlannerInfo *root, RelOptInfo *input_rel,
 	 * Build the fdw_private list that will be used by postgresGetForeignPlan.
 	 * Items in the list must match order in enum FdwPathPrivateIndex.
 	 */
-	fdw_private = list_make2(makeInteger(true), makeInteger(false));
+	fdw_private = list_make2(makeBoolean(true), makeBoolean(false));
 
 	/* Create foreign ordering path */
 	ordered_path = create_foreign_upper_path(root,
@@ -6797,8 +6797,8 @@ add_foreign_final_paths(PlannerInfo *root, RelOptInfo *input_rel,
 	 * Build the fdw_private list that will be used by postgresGetForeignPlan.
 	 * Items in the list must match order in enum FdwPathPrivateIndex.
 	 */
-	fdw_private = list_make2(makeInteger(has_final_sort),
-							 makeInteger(extra->limit_needed));
+	fdw_private = list_make2(makeBoolean(has_final_sort),
+							 makeBoolean(extra->limit_needed));
 
 	/*
 	 * Create foreign final path; this gets rid of a no-longer-needed outer
diff --git a/src/backend/commands/define.c b/src/backend/commands/define.c
index 0fe7e471d9..f17cfb5ade 100644
--- a/src/backend/commands/define.c
+++ b/src/backend/commands/define.c
@@ -59,6 +59,8 @@ defGetString(DefElem *def)
 			return psprintf("%ld", (long) intVal(def->arg));
 		case T_Float:
 			return castNode(Float, def->arg)->fval;
+		case T_Boolean:
+			return boolVal(def->arg) ? "true" : "false";
 		case T_String:
 			return strVal(def->arg);
 		case T_TypeName:
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 38e78f7102..ba5b23fb11 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -813,15 +813,15 @@ compute_function_attributes(ParseState *pstate,
 	if (transform_item)
 		*transform = transform_item->arg;
 	if (windowfunc_item)
-		*windowfunc_p = intVal(windowfunc_item->arg);
+		*windowfunc_p = boolVal(windowfunc_item->arg);
 	if (volatility_item)
 		*volatility_p = interpret_func_volatility(volatility_item);
 	if (strict_item)
-		*strict_p = intVal(strict_item->arg);
+		*strict_p = boolVal(strict_item->arg);
 	if (security_item)
-		*security_definer = intVal(security_item->arg);
+		*security_definer = boolVal(security_item->arg);
 	if (leakproof_item)
-		*leakproof_p = intVal(leakproof_item->arg);
+		*leakproof_p = boolVal(leakproof_item->arg);
 	if (set_items)
 		*proconfig = update_proconfig_value(NULL, set_items);
 	if (cost_item)
@@ -1417,12 +1417,12 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
 	if (volatility_item)
 		procForm->provolatile = interpret_func_volatility(volatility_item);
 	if (strict_item)
-		procForm->proisstrict = intVal(strict_item->arg);
+		procForm->proisstrict = boolVal(strict_item->arg);
 	if (security_def_item)
-		procForm->prosecdef = intVal(security_def_item->arg);
+		procForm->prosecdef = boolVal(security_def_item->arg);
 	if (leakproof_item)
 	{
-		procForm->proleakproof = intVal(leakproof_item->arg);
+		procForm->proleakproof = boolVal(leakproof_item->arg);
 		if (procForm->proleakproof && !superuser())
 			ereport(ERROR,
 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 72bfdc07a4..81368b8b2f 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -1401,7 +1401,7 @@ init_params(ParseState *pstate, List *options, bool for_identity,
 	/* CYCLE */
 	if (is_cycled != NULL)
 	{
-		seqform->seqcycle = intVal(is_cycled->arg);
+		seqform->seqcycle = boolVal(is_cycled->arg);
 		Assert(BoolIsValid(seqform->seqcycle));
 		seqdataform->log_cnt = 0;
 	}
@@ -1739,7 +1739,7 @@ sequence_options(Oid relid)
 	options = lappend(options,
 					  makeDefElem("cache", (Node *) makeFloat(psprintf(INT64_FORMAT, pgsform->seqcache)), -1));
 	options = lappend(options,
-					  makeDefElem("cycle", (Node *) makeInteger(pgsform->seqcycle), -1));
+					  makeDefElem("cycle", (Node *) makeBoolean(pgsform->seqcycle), -1));
 	options = lappend(options,
 					  makeDefElem("increment", (Node *) makeFloat(psprintf(INT64_FORMAT, pgsform->seqincrement)), -1));
 	options = lappend(options,
diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c
index c47a05d10d..b7261a88d4 100644
--- a/src/backend/commands/tsearchcmds.c
+++ b/src/backend/commands/tsearchcmds.c
@@ -1742,6 +1742,15 @@ buildDefItem(const char *name, const char *val, bool was_quoted)
 			return makeDefElem(pstrdup(name),
 							   (Node *) makeFloat(pstrdup(val)),
 							   -1);
+
+		if (strcmp(val, "true") == 0)
+			return makeDefElem(pstrdup(name),
+							   (Node *) makeBoolean(true),
+							   -1);
+		if (strcmp(val, "false") == 0)
+			return makeDefElem(pstrdup(name),
+							   (Node *) makeBoolean(false),
+							   -1);
 	}
 	/* Just make it a string */
 	return makeDefElem(pstrdup(name),
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 0aae87ff4a..288805ba78 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -217,17 +217,17 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
 	if (dpassword && dpassword->arg)
 		password = strVal(dpassword->arg);
 	if (dissuper)
-		issuper = intVal(dissuper->arg) != 0;
+		issuper = boolVal(dissuper->arg);
 	if (dinherit)
-		inherit = intVal(dinherit->arg) != 0;
+		inherit = boolVal(dinherit->arg);
 	if (dcreaterole)
-		createrole = intVal(dcreaterole->arg) != 0;
+		createrole = boolVal(dcreaterole->arg);
 	if (dcreatedb)
-		createdb = intVal(dcreatedb->arg) != 0;
+		createdb = boolVal(dcreatedb->arg);
 	if (dcanlogin)
-		canlogin = intVal(dcanlogin->arg) != 0;
+		canlogin = boolVal(dcanlogin->arg);
 	if (disreplication)
-		isreplication = intVal(disreplication->arg) != 0;
+		isreplication = boolVal(disreplication->arg);
 	if (dconnlimit)
 	{
 		connlimit = intVal(dconnlimit->arg);
@@ -245,7 +245,7 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
 	if (dvalidUntil)
 		validUntil = strVal(dvalidUntil->arg);
 	if (dbypassRLS)
-		bypassrls = intVal(dbypassRLS->arg) != 0;
+		bypassrls = boolVal(dbypassRLS->arg);
 
 	/* Check some permissions first */
 	if (issuper)
@@ -700,37 +700,37 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
 	 */
 	if (dissuper)
 	{
-		new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(intVal(dissuper->arg));
+		new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(boolVal(dissuper->arg));
 		new_record_repl[Anum_pg_authid_rolsuper - 1] = true;
 	}
 
 	if (dinherit)
 	{
-		new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(intVal(dinherit->arg));
+		new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(boolVal(dinherit->arg));
 		new_record_repl[Anum_pg_authid_rolinherit - 1] = true;
 	}
 
 	if (dcreaterole)
 	{
-		new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(intVal(dcreaterole->arg));
+		new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(boolVal(dcreaterole->arg));
 		new_record_repl[Anum_pg_authid_rolcreaterole - 1] = true;
 	}
 
 	if (dcreatedb)
 	{
-		new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(intVal(dcreatedb->arg));
+		new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(boolVal(dcreatedb->arg));
 		new_record_repl[Anum_pg_authid_rolcreatedb - 1] = true;
 	}
 
 	if (dcanlogin)
 	{
-		new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(intVal(dcanlogin->arg));
+		new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(boolVal(dcanlogin->arg));
 		new_record_repl[Anum_pg_authid_rolcanlogin - 1] = true;
 	}
 
 	if (disreplication)
 	{
-		new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(intVal(disreplication->arg));
+		new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(boolVal(disreplication->arg));
 		new_record_repl[Anum_pg_authid_rolreplication - 1] = true;
 	}
 
@@ -779,7 +779,7 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
 
 	if (dbypassRLS)
 	{
-		new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(intVal(dbypassRLS->arg));
+		new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(boolVal(dbypassRLS->arg));
 		new_record_repl[Anum_pg_authid_rolbypassrls - 1] = true;
 	}
 
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 439b903eb8..e0b440b9f6 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2749,6 +2749,9 @@ _copyA_Const(const A_Const *from)
 			case T_Float:
 				COPY_STRING_FIELD(val.fval.fval);
 				break;
+			case T_Boolean:
+				COPY_SCALAR_FIELD(val.boolval.boolval);
+				break;
 			case T_String:
 				COPY_STRING_FIELD(val.sval.sval);
 				break;
@@ -4948,6 +4951,16 @@ _copyFloat(const Float *from)
 	return newnode;
 }
 
+static Boolean *
+_copyBoolean(const Boolean *from)
+{
+	Boolean	   *newnode = makeNode(Boolean);
+
+	COPY_SCALAR_FIELD(boolval);
+
+	return newnode;
+}
+
 static String *
 _copyString(const String *from)
 {
@@ -5355,6 +5368,9 @@ copyObjectImpl(const void *from)
 		case T_Float:
 			retval = _copyFloat(from);
 			break;
+		case T_Boolean:
+			retval = _copyBoolean(from);
+			break;
 		case T_String:
 			retval = _copyString(from);
 			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index be09f91ee2..64522e8db0 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -3138,6 +3138,14 @@ _equalFloat(const Float *a, const Float *b)
 	return true;
 }
 
+static bool
+_equalBoolean(const Boolean *a, const Boolean *b)
+{
+	COMPARE_SCALAR_FIELD(boolval);
+
+	return true;
+}
+
 static bool
 _equalString(const String *a, const String *b)
 {
@@ -3374,6 +3382,9 @@ equal(const void *a, const void *b)
 		case T_Float:
 			retval = _equalFloat(a, b);
 			break;
+		case T_Boolean:
+			retval = _equalBoolean(a, b);
+			break;
 		case T_String:
 			retval = _equalString(a, b);
 			break;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index e276264882..397aa6de3c 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -3535,6 +3535,7 @@ raw_expression_tree_walker(Node *node,
 		case T_SQLValueFunction:
 		case T_Integer:
 		case T_Float:
+		case T_Boolean:
 		case T_String:
 		case T_BitString:
 		case T_ParamRef:
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 1accbc1e78..598adcd844 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -3433,6 +3433,12 @@ _outFloat(StringInfo str, const Float *node)
 	appendStringInfoString(str, node->fval);
 }
 
+static void
+_outBoolean(StringInfo str, const Boolean *node)
+{
+	appendStringInfoString(str, node->boolval ? "true" : "false");
+}
+
 static void
 _outString(StringInfo str, const String *node)
 {
@@ -3845,6 +3851,8 @@ outNode(StringInfo str, const void *obj)
 		_outInteger(str, (Integer *) obj);
 	else if (IsA(obj, Float))
 		_outFloat(str, (Float *) obj);
+	else if (IsA(obj, Boolean))
+		_outBoolean(str, (Boolean *) obj);
 	else if (IsA(obj, String))
 		_outString(str, (String *) obj);
 	else if (IsA(obj, BitString))
diff --git a/src/backend/nodes/read.c b/src/backend/nodes/read.c
index d281f7db6c..b2330f2002 100644
--- a/src/backend/nodes/read.c
+++ b/src/backend/nodes/read.c
@@ -235,7 +235,7 @@ debackslash(const char *token, int length)
  * nodeTokenType -
  *	  returns the type of the node token contained in token.
  *	  It returns one of the following valid NodeTags:
- *		T_Integer, T_Float, T_String, T_BitString
+ *		T_Integer, T_Float, T_Boolean, T_String, T_BitString
  *	  and some of its own:
  *		RIGHT_PAREN, LEFT_PAREN, LEFT_BRACE, OTHER_TOKEN
  *
@@ -283,6 +283,8 @@ nodeTokenType(const char *token, int length)
 		retval = RIGHT_PAREN;
 	else if (*token == '{')
 		retval = LEFT_BRACE;
+	else if (strcmp(token, "true") == 0 || strcmp(token, "false") == 0)
+		retval = T_Boolean;
 	else if (*token == '"' && length > 1 && token[length - 1] == '"')
 		retval = T_String;
 	else if (*token == 'b')
@@ -298,7 +300,7 @@ nodeTokenType(const char *token, int length)
  *
  * This routine applies some semantic knowledge on top of the purely
  * lexical tokenizer pg_strtok().   It can read
- *	* Value token nodes (integers, floats, or strings);
+ *	* Value token nodes (integers, floats, booleans, or strings);
  *	* General nodes (via parseNodeString() from readfuncs.c);
  *	* Lists of the above;
  *	* Lists of integers or OIDs.
@@ -438,6 +440,9 @@ nodeRead(const char *token, int tok_len)
 				result = (Node *) makeFloat(fval);
 			}
 			break;
+		case T_Boolean:
+			result = (Node *) makeBoolean(token[0] == 't');
+			break;
 		case T_String:
 			/* need to remove leading and trailing quotes, and backslashes */
 			result = (Node *) makeString(debackslash(token + 1, tok_len - 2));
diff --git a/src/backend/nodes/value.c b/src/backend/nodes/value.c
index ab2d6f74d3..937bf90e83 100644
--- a/src/backend/nodes/value.c
+++ b/src/backend/nodes/value.c
@@ -42,6 +42,18 @@ makeFloat(char *numericStr)
 	return v;
 }
 
+/*
+ *	makeBoolean
+ */
+Boolean *
+makeBoolean(bool val)
+{
+	Boolean	   *v = makeNode(Boolean);
+
+	v->boolval = val;
+	return v;
+}
+
 /*
  *	makeString
  *
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index adce748a69..505b7dfe07 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -177,10 +177,10 @@ static Node *makeStringConst(char *str, int location);
 static Node *makeStringConstCast(char *str, int location, TypeName *typename);
 static Node *makeIntConst(int val, int location);
 static Node *makeFloatConst(char *str, int location);
+static Node *makeBoolAConst(bool state, int location);
 static Node *makeBitStringConst(char *str, int location);
 static Node *makeNullAConst(int location);
 static Node *makeAConst(Node *v, int location);
-static Node *makeBoolAConst(bool state, int location);
 static RoleSpec *makeRoleSpec(RoleSpecType type, int location);
 static void check_qualified_name(List *names, core_yyscan_t yyscanner);
 static List *check_func_name(List *names, core_yyscan_t yyscanner);
@@ -1133,7 +1133,7 @@ AlterOptRoleElem:
 				}
 			| INHERIT
 				{
-					$$ = makeDefElem("inherit", (Node *)makeInteger(true), @1);
+					$$ = makeDefElem("inherit", (Node *)makeBoolean(true), @1);
 				}
 			| CONNECTION LIMIT SignedIconst
 				{
@@ -1156,36 +1156,36 @@ AlterOptRoleElem:
 					 * size of the main parser.
 					 */
 					if (strcmp($1, "superuser") == 0)
-						$$ = makeDefElem("superuser", (Node *)makeInteger(true), @1);
+						$$ = makeDefElem("superuser", (Node *)makeBoolean(true), @1);
 					else if (strcmp($1, "nosuperuser") == 0)
-						$$ = makeDefElem("superuser", (Node *)makeInteger(false), @1);
+						$$ = makeDefElem("superuser", (Node *)makeBoolean(false), @1);
 					else if (strcmp($1, "createrole") == 0)
-						$$ = makeDefElem("createrole", (Node *)makeInteger(true), @1);
+						$$ = makeDefElem("createrole", (Node *)makeBoolean(true), @1);
 					else if (strcmp($1, "nocreaterole") == 0)
-						$$ = makeDefElem("createrole", (Node *)makeInteger(false), @1);
+						$$ = makeDefElem("createrole", (Node *)makeBoolean(false), @1);
 					else if (strcmp($1, "replication") == 0)
-						$$ = makeDefElem("isreplication", (Node *)makeInteger(true), @1);
+						$$ = makeDefElem("isreplication", (Node *)makeBoolean(true), @1);
 					else if (strcmp($1, "noreplication") == 0)
-						$$ = makeDefElem("isreplication", (Node *)makeInteger(false), @1);
+						$$ = makeDefElem("isreplication", (Node *)makeBoolean(false), @1);
 					else if (strcmp($1, "createdb") == 0)
-						$$ = makeDefElem("createdb", (Node *)makeInteger(true), @1);
+						$$ = makeDefElem("createdb", (Node *)makeBoolean(true), @1);
 					else if (strcmp($1, "nocreatedb") == 0)
-						$$ = makeDefElem("createdb", (Node *)makeInteger(false), @1);
+						$$ = makeDefElem("createdb", (Node *)makeBoolean(false), @1);
 					else if (strcmp($1, "login") == 0)
-						$$ = makeDefElem("canlogin", (Node *)makeInteger(true), @1);
+						$$ = makeDefElem("canlogin", (Node *)makeBoolean(true), @1);
 					else if (strcmp($1, "nologin") == 0)
-						$$ = makeDefElem("canlogin", (Node *)makeInteger(false), @1);
+						$$ = makeDefElem("canlogin", (Node *)makeBoolean(false), @1);
 					else if (strcmp($1, "bypassrls") == 0)
-						$$ = makeDefElem("bypassrls", (Node *)makeInteger(true), @1);
+						$$ = makeDefElem("bypassrls", (Node *)makeBoolean(true), @1);
 					else if (strcmp($1, "nobypassrls") == 0)
-						$$ = makeDefElem("bypassrls", (Node *)makeInteger(false), @1);
+						$$ = makeDefElem("bypassrls", (Node *)makeBoolean(false), @1);
 					else if (strcmp($1, "noinherit") == 0)
 					{
 						/*
 						 * Note that INHERIT is a keyword, so it's handled by main parser, but
 						 * NOINHERIT is handled here.
 						 */
-						$$ = makeDefElem("inherit", (Node *)makeInteger(false), @1);
+						$$ = makeDefElem("inherit", (Node *)makeBoolean(false), @1);
 					}
 					else
 						ereport(ERROR,
@@ -3175,7 +3175,7 @@ copy_opt_item:
 				}
 			| FREEZE
 				{
-					$$ = makeDefElem("freeze", (Node *)makeInteger(true), @1);
+					$$ = makeDefElem("freeze", (Node *)makeBoolean(true), @1);
 				}
 			| DELIMITER opt_as Sconst
 				{
@@ -3191,7 +3191,7 @@ copy_opt_item:
 				}
 			| HEADER_P
 				{
-					$$ = makeDefElem("header", (Node *)makeInteger(true), @1);
+					$$ = makeDefElem("header", (Node *)makeBoolean(true), @1);
 				}
 			| QUOTE opt_as Sconst
 				{
@@ -4499,11 +4499,11 @@ SeqOptElem: AS SimpleTypename
 				}
 			| CYCLE
 				{
-					$$ = makeDefElem("cycle", (Node *)makeInteger(true), @1);
+					$$ = makeDefElem("cycle", (Node *)makeBoolean(true), @1);
 				}
 			| NO CYCLE
 				{
-					$$ = makeDefElem("cycle", (Node *)makeInteger(false), @1);
+					$$ = makeDefElem("cycle", (Node *)makeBoolean(false), @1);
 				}
 			| INCREMENT opt_by NumericOnly
 				{
@@ -4739,7 +4739,7 @@ create_extension_opt_item:
 				}
 			| CASCADE
 				{
-					$$ = makeDefElem("cascade", (Node *)makeInteger(true), @1);
+					$$ = makeDefElem("cascade", (Node *)makeBoolean(true), @1);
 				}
 		;
 
@@ -7934,15 +7934,15 @@ createfunc_opt_list:
 common_func_opt_item:
 			CALLED ON NULL_P INPUT_P
 				{
-					$$ = makeDefElem("strict", (Node *)makeInteger(false), @1);
+					$$ = makeDefElem("strict", (Node *)makeBoolean(false), @1);
 				}
 			| RETURNS NULL_P ON NULL_P INPUT_P
 				{
-					$$ = makeDefElem("strict", (Node *)makeInteger(true), @1);
+					$$ = makeDefElem("strict", (Node *)makeBoolean(true), @1);
 				}
 			| STRICT_P
 				{
-					$$ = makeDefElem("strict", (Node *)makeInteger(true), @1);
+					$$ = makeDefElem("strict", (Node *)makeBoolean(true), @1);
 				}
 			| IMMUTABLE
 				{
@@ -7958,27 +7958,27 @@ common_func_opt_item:
 				}
 			| EXTERNAL SECURITY DEFINER
 				{
-					$$ = makeDefElem("security", (Node *)makeInteger(true), @1);
+					$$ = makeDefElem("security", (Node *)makeBoolean(true), @1);
 				}
 			| EXTERNAL SECURITY INVOKER
 				{
-					$$ = makeDefElem("security", (Node *)makeInteger(false), @1);
+					$$ = makeDefElem("security", (Node *)makeBoolean(false), @1);
 				}
 			| SECURITY DEFINER
 				{
-					$$ = makeDefElem("security", (Node *)makeInteger(true), @1);
+					$$ = makeDefElem("security", (Node *)makeBoolean(true), @1);
 				}
 			| SECURITY INVOKER
 				{
-					$$ = makeDefElem("security", (Node *)makeInteger(false), @1);
+					$$ = makeDefElem("security", (Node *)makeBoolean(false), @1);
 				}
 			| LEAKPROOF
 				{
-					$$ = makeDefElem("leakproof", (Node *)makeInteger(true), @1);
+					$$ = makeDefElem("leakproof", (Node *)makeBoolean(true), @1);
 				}
 			| NOT LEAKPROOF
 				{
-					$$ = makeDefElem("leakproof", (Node *)makeInteger(false), @1);
+					$$ = makeDefElem("leakproof", (Node *)makeBoolean(false), @1);
 				}
 			| COST NumericOnly
 				{
@@ -8018,7 +8018,7 @@ createfunc_opt_item:
 				}
 			| WINDOW
 				{
-					$$ = makeDefElem("window", (Node *)makeInteger(true), @1);
+					$$ = makeDefElem("window", (Node *)makeBoolean(true), @1);
 				}
 			| common_func_opt_item
 				{
@@ -9941,7 +9941,7 @@ AlterSubscriptionStmt:
 					n->kind = ALTER_SUBSCRIPTION_ENABLED;
 					n->subname = $3;
 					n->options = list_make1(makeDefElem("enabled",
-											(Node *)makeInteger(true), @1));
+											(Node *)makeBoolean(true), @1));
 					$$ = (Node *)n;
 				}
 			| ALTER SUBSCRIPTION name DISABLE_P
@@ -9951,7 +9951,7 @@ AlterSubscriptionStmt:
 					n->kind = ALTER_SUBSCRIPTION_ENABLED;
 					n->subname = $3;
 					n->options = list_make1(makeDefElem("enabled",
-											(Node *)makeInteger(false), @1));
+											(Node *)makeBoolean(false), @1));
 					$$ = (Node *)n;
 				}
 		;
@@ -12874,7 +12874,7 @@ xmltable_column_el:
 										(errcode(ERRCODE_SYNTAX_ERROR),
 										 errmsg("conflicting or redundant NULL / NOT NULL declarations for column \"%s\"", fc->colname),
 										 parser_errposition(defel->location)));
-							fc->is_not_null = intVal(defel->arg);
+							fc->is_not_null = boolVal(defel->arg);
 							nullability_seen = true;
 						}
 						else
@@ -12914,9 +12914,9 @@ xmltable_column_option_el:
 			| DEFAULT b_expr
 				{ $$ = makeDefElem("default", $2, @1); }
 			| NOT NULL_P
-				{ $$ = makeDefElem("is_not_null", (Node *) makeInteger(true), @1); }
+				{ $$ = makeDefElem("is_not_null", (Node *) makeBoolean(true), @1); }
 			| NULL_P
-				{ $$ = makeDefElem("is_not_null", (Node *) makeInteger(false), @1); }
+				{ $$ = makeDefElem("is_not_null", (Node *) makeBoolean(false), @1); }
 		;
 
 xml_namespace_list:
@@ -16705,6 +16705,18 @@ makeFloatConst(char *str, int location)
    return (Node *)n;
 }
 
+static Node *
+makeBoolAConst(bool state, int location)
+{
+	A_Const *n = makeNode(A_Const);
+
+	n->val.boolval.type = T_Boolean;
+	n->val.boolval.boolval = state;
+	n->location = location;
+
+   return (Node *)n;
+}
+
 static Node *
 makeBitStringConst(char *str, int location)
 {
@@ -16743,26 +16755,15 @@ makeAConst(Node *v, int location)
 			n = makeIntConst(castNode(Integer, v)->ival, location);
 			break;
 
-		case T_String:
 		default:
-			n = makeStringConst(castNode(String, v)->sval, location);
-			break;
+			/* currently not used */
+			Assert(false);
+			n = NULL;
 	}
 
 	return n;
 }
 
-/* makeBoolAConst()
- * Create an A_Const string node and put it inside a boolean cast.
- */
-static Node *
-makeBoolAConst(bool state, int location)
-{
-	return makeStringConstCast((state ? "t" : "f"),
-							   location,
-							   SystemTypeName("bool"));
-}
-
 /* makeRoleSpec
  * Create a RoleSpec with the given type
  */
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c
index bd92133b21..533669e37e 100644
--- a/src/backend/parser/parse_node.c
+++ b/src/backend/parser/parse_node.c
@@ -426,6 +426,14 @@ make_const(ParseState *pstate, A_Const *aconst)
 			}
 			break;
 
+		case T_Boolean:
+			val = BoolGetDatum(boolVal(&aconst->val));
+
+			typeid = BOOLOID;
+			typelen = 1;
+			typebyval = true;
+			break;
+
 		case T_String:
 
 			/*
diff --git a/src/backend/replication/repl_gram.y b/src/backend/replication/repl_gram.y
index dcb1108579..dd23f98b44 100644
--- a/src/backend/replication/repl_gram.y
+++ b/src/backend/replication/repl_gram.y
@@ -212,7 +212,7 @@ base_backup_legacy_opt:
 			| K_PROGRESS
 				{
 				  $$ = makeDefElem("progress",
-								   (Node *)makeInteger(true), -1);
+								   (Node *)makeBoolean(true), -1);
 				}
 			| K_FAST
 				{
@@ -222,12 +222,12 @@ base_backup_legacy_opt:
 			| K_WAL
 				{
 				  $$ = makeDefElem("wal",
-								   (Node *)makeInteger(true), -1);
+								   (Node *)makeBoolean(true), -1);
 				}
 			| K_NOWAIT
 				{
 				  $$ = makeDefElem("wait",
-								   (Node *)makeInteger(false), -1);
+								   (Node *)makeBoolean(false), -1);
 				}
 			| K_MAX_RATE UCONST
 				{
@@ -237,12 +237,12 @@ base_backup_legacy_opt:
 			| K_TABLESPACE_MAP
 				{
 				  $$ = makeDefElem("tablespace_map",
-								   (Node *)makeInteger(true), -1);
+								   (Node *)makeBoolean(true), -1);
 				}
 			| K_NOVERIFY_CHECKSUMS
 				{
 				  $$ = makeDefElem("verify_checksums",
-								   (Node *)makeInteger(false), -1);
+								   (Node *)makeBoolean(false), -1);
 				}
 			| K_MANIFEST SCONST
 				{
@@ -313,12 +313,12 @@ create_slot_legacy_opt:
 			| K_RESERVE_WAL
 				{
 				  $$ = makeDefElem("reserve_wal",
-								   (Node *)makeInteger(true), -1);
+								   (Node *)makeBoolean(true), -1);
 				}
 			| K_TWO_PHASE
 				{
 				  $$ = makeDefElem("two_phase",
-								   (Node *)makeInteger(true), -1);
+								   (Node *)makeBoolean(true), -1);
 				}
 			;
 
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 7c657c1241..15444c60b7 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -292,6 +292,7 @@ typedef enum NodeTag
 	 */
 	T_Integer,
 	T_Float,
+	T_Boolean,
 	T_String,
 	T_BitString,
 
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 784164b32a..ef12148b1f 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -304,6 +304,7 @@ typedef struct A_Const
 		Node		node;
 		Integer		ival;
 		Float		fval;
+		Boolean		boolval;
 		String		sval;
 		BitString	bsval;
 	}			val;
diff --git a/src/include/nodes/value.h b/src/include/nodes/value.h
index cc3a4a639c..5f877ea9ea 100644
--- a/src/include/nodes/value.h
+++ b/src/include/nodes/value.h
@@ -48,6 +48,12 @@ typedef struct Float
 	char	   *fval;
 } Float;
 
+typedef struct Boolean
+{
+	NodeTag		type;
+	bool		boolval;
+} Boolean;
+
 typedef struct String
 {
 	NodeTag		type;
@@ -62,10 +68,12 @@ typedef struct BitString
 
 #define intVal(v)		(castNode(Integer, v)->ival)
 #define floatVal(v)		atof(castNode(Float, v)->fval)
+#define boolVal(v)		(castNode(Boolean, v)->boolval)
 #define strVal(v)		(castNode(String, v)->sval)
 
 extern Integer *makeInteger(int i);
 extern Float *makeFloat(char *numericStr);
+extern Boolean *makeBoolean(bool var);
 extern String *makeString(char *str);
 extern BitString *makeBitString(char *str);
 
diff --git a/src/test/isolation/expected/ri-trigger.out b/src/test/isolation/expected/ri-trigger.out
index 842df80a90..db85618bef 100644
--- a/src/test/isolation/expected/ri-trigger.out
+++ b/src/test/isolation/expected/ri-trigger.out
@@ -4,9 +4,9 @@ starting permutation: wxry1 c1 r2 wyrx2 c2
 step wxry1: INSERT INTO child (parent_id) VALUES (0);
 step c1: COMMIT;
 step r2: SELECT TRUE;
-bool
-----
-t   
+?column?
+--------
+t       
 (1 row)
 
 step wyrx2: DELETE FROM parent WHERE parent_id = 0;
@@ -16,9 +16,9 @@ step c2: COMMIT;
 starting permutation: wxry1 r2 c1 wyrx2 c2
 step wxry1: INSERT INTO child (parent_id) VALUES (0);
 step r2: SELECT TRUE;
-bool
-----
-t   
+?column?
+--------
+t       
 (1 row)
 
 step c1: COMMIT;
@@ -29,9 +29,9 @@ step c2: COMMIT;
 starting permutation: wxry1 r2 wyrx2 c1 c2
 step wxry1: INSERT INTO child (parent_id) VALUES (0);
 step r2: SELECT TRUE;
-bool
-----
-t   
+?column?
+--------
+t       
 (1 row)
 
 step wyrx2: DELETE FROM parent WHERE parent_id = 0;
@@ -42,9 +42,9 @@ ERROR:  could not serialize access due to read/write dependencies among transact
 starting permutation: wxry1 r2 wyrx2 c2 c1
 step wxry1: INSERT INTO child (parent_id) VALUES (0);
 step r2: SELECT TRUE;
-bool
-----
-t   
+?column?
+--------
+t       
 (1 row)
 
 step wyrx2: DELETE FROM parent WHERE parent_id = 0;
@@ -54,9 +54,9 @@ ERROR:  could not serialize access due to read/write dependencies among transact
 
 starting permutation: r2 wxry1 c1 wyrx2 c2
 step r2: SELECT TRUE;
-bool
-----
-t   
+?column?
+--------
+t       
 (1 row)
 
 step wxry1: INSERT INTO child (parent_id) VALUES (0);
@@ -67,9 +67,9 @@ step c2: COMMIT;
 
 starting permutation: r2 wxry1 wyrx2 c1 c2
 step r2: SELECT TRUE;
-bool
-----
-t   
+?column?
+--------
+t       
 (1 row)
 
 step wxry1: INSERT INTO child (parent_id) VALUES (0);
@@ -80,9 +80,9 @@ ERROR:  could not serialize access due to read/write dependencies among transact
 
 starting permutation: r2 wxry1 wyrx2 c2 c1
 step r2: SELECT TRUE;
-bool
-----
-t   
+?column?
+--------
+t       
 (1 row)
 
 step wxry1: INSERT INTO child (parent_id) VALUES (0);
@@ -93,9 +93,9 @@ ERROR:  could not serialize access due to read/write dependencies among transact
 
 starting permutation: r2 wyrx2 wxry1 c1 c2
 step r2: SELECT TRUE;
-bool
-----
-t   
+?column?
+--------
+t       
 (1 row)
 
 step wyrx2: DELETE FROM parent WHERE parent_id = 0;
@@ -106,9 +106,9 @@ ERROR:  could not serialize access due to read/write dependencies among transact
 
 starting permutation: r2 wyrx2 wxry1 c2 c1
 step r2: SELECT TRUE;
-bool
-----
-t   
+?column?
+--------
+t       
 (1 row)
 
 step wyrx2: DELETE FROM parent WHERE parent_id = 0;
@@ -119,9 +119,9 @@ ERROR:  could not serialize access due to read/write dependencies among transact
 
 starting permutation: r2 wyrx2 c2 wxry1 c1
 step r2: SELECT TRUE;
-bool
-----
-t   
+?column?
+--------
+t       
 (1 row)
 
 step wyrx2: DELETE FROM parent WHERE parent_id = 0;
diff --git a/src/test/regress/expected/create_function_3.out b/src/test/regress/expected/create_function_3.out
index 3a4fd45147..e0c4bee893 100644
--- a/src/test/regress/expected/create_function_3.out
+++ b/src/test/regress/expected/create_function_3.out
@@ -403,7 +403,7 @@ SELECT pg_get_functiondef('functest_S_13'::regproc);
   LANGUAGE sql                                            +
  BEGIN ATOMIC                                             +
   SELECT 1;                                               +
-  SELECT false AS bool;                                   +
+  SELECT false;                                           +
  END                                                      +
  
 (1 row)
-- 
2.34.1

#26Pavel Stehule
pavel.stehule@gmail.com
In reply to: Peter Eisentraut (#25)
Re: Add Boolean node

Hi

po 3. 1. 2022 v 14:18 odesílatel Peter Eisentraut <
peter.eisentraut@enterprisedb.com> napsal:

On 03.01.22 12:04, Peter Eisentraut wrote:

On 27.12.21 10:02, Peter Eisentraut wrote:

This patch adds a new node type Boolean, to go alongside the "value"
nodes Integer, Float, String, etc. This seems appropriate given that
Boolean values are a fundamental part of the system and are used a lot.

Before, SQL-level Boolean constants were represented by a string with
a cast, and internal Boolean values in DDL commands were usually
represented by Integer nodes. This takes the place of both of these
uses, making the intent clearer and having some amount of type safety.

Here is an update of this patch set based on the feedback. First, I
added a patch that makes some changes in AlterRole() that my original
patch might have broken or at least made more confusing. Unlike in
CreateRole(), we use three-valued logic here, so that a variable like
issuper would have 0 = no, 1 = yes, -1 = not specified, keep previous
value. I'm simplifying this, by instead using the dissuper etc.
variables to track whether a setting was specified. This makes
everything a bit simpler and makes the subsequent patch easier.

Second, I added the suggest by Tom Lane to rename to fields in the
used-to-be-Value nodes to be different in each node type (ival, fval,
etc.). I agree that this makes things a bit cleaner and reduces the
changes of mixups.

And third, the original patch that introduces the Boolean node with some
small changes based on the feedback.

Another very small update, attempting to appease the cfbot.

This is almost trivial patch

There are not problems with patching, compilation and tests

make check-world passed

There are not objection from me or from community

I'll mark this patch as ready for committer

Regards

Pavel

#27Peter Eisentraut
peter.eisentraut@enterprisedb.com
In reply to: Pavel Stehule (#26)
Re: Add Boolean node

On 13.01.22 10:48, Pavel Stehule wrote:

There are not objection from me or from community

I'll mark this patch as ready for committer

This patch set has been committed.