mirror of
https://github.com/postgres/postgres.git
synced 2025-09-02 04:21:28 +03:00
Fix the raw-parsetree representation of star (as in SELECT * FROM or
SELECT foo.*) so that it cannot be confused with a quoted identifier "*". Instead create a separate node type A_Star to represent this notation. Per pgsql-hackers discussion of 2007-Sep-27.
This commit is contained in:
@@ -11,7 +11,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.619 2008/08/28 23:09:47 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.620 2008/08/30 01:39:14 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@@ -89,7 +89,7 @@ static bool QueryIsRule = FALSE;
|
||||
*/
|
||||
/*#define __YYSCLASS*/
|
||||
|
||||
static Node *makeColumnRef(char *relname, List *indirection, int location);
|
||||
static Node *makeColumnRef(char *colname, List *indirection, int location);
|
||||
static Node *makeTypeCast(Node *arg, TypeName *typename, int location);
|
||||
static Node *makeStringConst(char *str, int location);
|
||||
static Node *makeStringConstCast(char *str, int location, TypeName *typename);
|
||||
@@ -102,6 +102,7 @@ static Node *makeBoolAConst(bool state, int location);
|
||||
static FuncCall *makeOverlaps(List *largs, List *rargs, int location);
|
||||
static void check_qualified_name(List *names);
|
||||
static List *check_func_name(List *names);
|
||||
static List *check_indirection(List *indirection);
|
||||
static List *extractArgTypes(List *parameters);
|
||||
static SelectStmt *findLeftmostSelect(SelectStmt *node);
|
||||
static void insertSelectOptions(SelectStmt *stmt,
|
||||
@@ -5144,9 +5145,7 @@ UnlistenStmt:
|
||||
| UNLISTEN '*'
|
||||
{
|
||||
UnlistenStmt *n = makeNode(UnlistenStmt);
|
||||
n->relation = makeNode(RangeVar);
|
||||
n->relation->relname = "*";
|
||||
n->relation->schemaname = NULL;
|
||||
n->relation = NULL;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
;
|
||||
@@ -5999,7 +5998,7 @@ insert_column_item:
|
||||
{
|
||||
$$ = makeNode(ResTarget);
|
||||
$$->name = $1;
|
||||
$$->indirection = $2;
|
||||
$$->indirection = check_indirection($2);
|
||||
$$->val = NULL;
|
||||
$$->location = @1;
|
||||
}
|
||||
@@ -6138,7 +6137,7 @@ set_target:
|
||||
{
|
||||
$$ = makeNode(ResTarget);
|
||||
$$->name = $1;
|
||||
$$->indirection = $2;
|
||||
$$->indirection = check_indirection($2);
|
||||
$$->val = NULL; /* upper production sets this */
|
||||
$$->location = @1;
|
||||
}
|
||||
@@ -7842,7 +7841,7 @@ c_expr: columnref { $$ = $1; }
|
||||
{
|
||||
A_Indirection *n = makeNode(A_Indirection);
|
||||
n->arg = (Node *) p;
|
||||
n->indirection = $2;
|
||||
n->indirection = check_indirection($2);
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
else
|
||||
@@ -7854,7 +7853,7 @@ c_expr: columnref { $$ = $1; }
|
||||
{
|
||||
A_Indirection *n = makeNode(A_Indirection);
|
||||
n->arg = $2;
|
||||
n->indirection = $4;
|
||||
n->indirection = check_indirection($4);
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
else
|
||||
@@ -8409,7 +8408,7 @@ xml_attribute_el: a_expr AS ColLabel
|
||||
{
|
||||
$$ = makeNode(ResTarget);
|
||||
$$->name = $3;
|
||||
$$->indirection = NULL;
|
||||
$$->indirection = NIL;
|
||||
$$->val = (Node *) $1;
|
||||
$$->location = @1;
|
||||
}
|
||||
@@ -8417,7 +8416,7 @@ xml_attribute_el: a_expr AS ColLabel
|
||||
{
|
||||
$$ = makeNode(ResTarget);
|
||||
$$->name = NULL;
|
||||
$$->indirection = NULL;
|
||||
$$->indirection = NIL;
|
||||
$$->val = (Node *) $1;
|
||||
$$->location = @1;
|
||||
}
|
||||
@@ -8724,7 +8723,7 @@ indirection_el:
|
||||
}
|
||||
| '.' '*'
|
||||
{
|
||||
$$ = (Node *) makeString("*");
|
||||
$$ = (Node *) makeNode(A_Star);
|
||||
}
|
||||
| '[' a_expr ']'
|
||||
{
|
||||
@@ -8833,7 +8832,7 @@ target_el: a_expr AS ColLabel
|
||||
| '*'
|
||||
{
|
||||
ColumnRef *n = makeNode(ColumnRef);
|
||||
n->fields = list_make1(makeString("*"));
|
||||
n->fields = list_make1(makeNode(A_Star));
|
||||
n->location = @1;
|
||||
|
||||
$$ = makeNode(ResTarget);
|
||||
@@ -9511,7 +9510,7 @@ SpecialRuleRelation:
|
||||
%%
|
||||
|
||||
static Node *
|
||||
makeColumnRef(char *relname, List *indirection, int location)
|
||||
makeColumnRef(char *colname, List *indirection, int location)
|
||||
{
|
||||
/*
|
||||
* Generate a ColumnRef node, with an A_Indirection node added if there
|
||||
@@ -9533,23 +9532,30 @@ makeColumnRef(char *relname, List *indirection, int location)
|
||||
if (nfields == 0)
|
||||
{
|
||||
/* easy case - all indirection goes to A_Indirection */
|
||||
c->fields = list_make1(makeString(relname));
|
||||
i->indirection = indirection;
|
||||
c->fields = list_make1(makeString(colname));
|
||||
i->indirection = check_indirection(indirection);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* got to split the list in two */
|
||||
i->indirection = list_copy_tail(indirection, nfields);
|
||||
i->indirection = check_indirection(list_copy_tail(indirection,
|
||||
nfields));
|
||||
indirection = list_truncate(indirection, nfields);
|
||||
c->fields = lcons(makeString(relname), indirection);
|
||||
c->fields = lcons(makeString(colname), indirection);
|
||||
}
|
||||
i->arg = (Node *) c;
|
||||
return (Node *) i;
|
||||
}
|
||||
else if (IsA(lfirst(l), A_Star))
|
||||
{
|
||||
/* We only allow '*' at the end of a ColumnRef */
|
||||
if (lnext(l) != NULL)
|
||||
yyerror("improper use of \"*\"");
|
||||
}
|
||||
nfields++;
|
||||
}
|
||||
/* No subscripting, so all indirection gets added to field list */
|
||||
c->fields = lcons(makeString(relname), indirection);
|
||||
c->fields = lcons(makeString(colname), indirection);
|
||||
return (Node *) c;
|
||||
}
|
||||
|
||||
@@ -9712,8 +9718,6 @@ check_qualified_name(List *names)
|
||||
{
|
||||
if (!IsA(lfirst(i), String))
|
||||
yyerror("syntax error");
|
||||
else if (strcmp(strVal(lfirst(i)), "*") == 0)
|
||||
yyerror("syntax error");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9731,12 +9735,31 @@ check_func_name(List *names)
|
||||
{
|
||||
if (!IsA(lfirst(i), String))
|
||||
yyerror("syntax error");
|
||||
else if (strcmp(strVal(lfirst(i)), "*") == 0)
|
||||
yyerror("syntax error");
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
/* check_indirection --- check the result of indirection production
|
||||
*
|
||||
* We only allow '*' at the end of the list, but it's hard to enforce that
|
||||
* in the grammar, so do it here.
|
||||
*/
|
||||
static List *
|
||||
check_indirection(List *indirection)
|
||||
{
|
||||
ListCell *l;
|
||||
|
||||
foreach(l, indirection)
|
||||
{
|
||||
if (IsA(lfirst(l), A_Star))
|
||||
{
|
||||
if (lnext(l) != NULL)
|
||||
yyerror("improper use of \"*\"");
|
||||
}
|
||||
}
|
||||
return indirection;
|
||||
}
|
||||
|
||||
/* extractArgTypes()
|
||||
* Given a list of FunctionParameter nodes, extract a list of just the
|
||||
* argument types (TypeNames) for input parameters only. This is what
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.177 2008/08/28 23:09:47 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.178 2008/08/30 01:39:14 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -1181,7 +1181,8 @@ findTargetlistEntry(ParseState *pstate, Node *node, List **tlist, int clause)
|
||||
*----------
|
||||
*/
|
||||
if (IsA(node, ColumnRef) &&
|
||||
list_length(((ColumnRef *) node)->fields) == 1)
|
||||
list_length(((ColumnRef *) node)->fields) == 1 &&
|
||||
IsA(linitial(((ColumnRef *) node)->fields), String))
|
||||
{
|
||||
char *name = strVal(linitial(((ColumnRef *) node)->fields));
|
||||
int location = ((ColumnRef *) node)->location;
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.232 2008/08/28 23:09:47 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.233 2008/08/30 01:39:14 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -334,6 +334,13 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
|
||||
|
||||
if (IsA(n, A_Indices))
|
||||
subscripts = lappend(subscripts, n);
|
||||
else if (IsA(n, A_Star))
|
||||
{
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("row expansion via \"*\" is not supported here"),
|
||||
parser_errposition(pstate, exprLocation(basenode))));
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert(IsA(n, String));
|
||||
@@ -403,10 +410,14 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
char *name = strVal(linitial(cref->fields));
|
||||
Node *field1 = (Node *) linitial(cref->fields);
|
||||
char *name1;
|
||||
|
||||
Assert(IsA(field1, String));
|
||||
name1 = strVal(field1);
|
||||
|
||||
/* Try to identify as an unqualified column */
|
||||
node = colNameToVar(pstate, name, false, cref->location);
|
||||
node = colNameToVar(pstate, name1, false, cref->location);
|
||||
|
||||
if (node == NULL)
|
||||
{
|
||||
@@ -419,7 +430,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
|
||||
* have used VALUE as a column name in the past.)
|
||||
*/
|
||||
if (pstate->p_value_substitute != NULL &&
|
||||
strcmp(name, "value") == 0)
|
||||
strcmp(name1, "value") == 0)
|
||||
{
|
||||
node = (Node *) copyObject(pstate->p_value_substitute);
|
||||
|
||||
@@ -442,32 +453,40 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
|
||||
* PostQUEL-inspired syntax. The preferred form now is
|
||||
* "rel.*".
|
||||
*/
|
||||
if (refnameRangeTblEntry(pstate, NULL, name,
|
||||
if (refnameRangeTblEntry(pstate, NULL, name1,
|
||||
&levels_up) != NULL)
|
||||
node = transformWholeRowRef(pstate, NULL, name,
|
||||
node = transformWholeRowRef(pstate, NULL, name1,
|
||||
cref->location);
|
||||
else
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_COLUMN),
|
||||
errmsg("column \"%s\" does not exist",
|
||||
name),
|
||||
name1),
|
||||
parser_errposition(pstate, cref->location)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
char *name1 = strVal(linitial(cref->fields));
|
||||
char *name2 = strVal(lsecond(cref->fields));
|
||||
Node *field1 = (Node *) linitial(cref->fields);
|
||||
Node *field2 = (Node *) lsecond(cref->fields);
|
||||
char *name1;
|
||||
char *name2;
|
||||
|
||||
Assert(IsA(field1, String));
|
||||
name1 = strVal(field1);
|
||||
|
||||
/* Whole-row reference? */
|
||||
if (strcmp(name2, "*") == 0)
|
||||
if (IsA(field2, A_Star))
|
||||
{
|
||||
node = transformWholeRowRef(pstate, NULL, name1,
|
||||
cref->location);
|
||||
break;
|
||||
}
|
||||
|
||||
Assert(IsA(field2, String));
|
||||
name2 = strVal(field2);
|
||||
|
||||
/* Try to identify as a once-qualified column */
|
||||
node = qualifiedNameToVar(pstate, NULL, name1, name2, true,
|
||||
cref->location);
|
||||
@@ -490,18 +509,29 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
char *name1 = strVal(linitial(cref->fields));
|
||||
char *name2 = strVal(lsecond(cref->fields));
|
||||
char *name3 = strVal(lthird(cref->fields));
|
||||
Node *field1 = (Node *) linitial(cref->fields);
|
||||
Node *field2 = (Node *) lsecond(cref->fields);
|
||||
Node *field3 = (Node *) lthird(cref->fields);
|
||||
char *name1;
|
||||
char *name2;
|
||||
char *name3;
|
||||
|
||||
Assert(IsA(field1, String));
|
||||
name1 = strVal(field1);
|
||||
Assert(IsA(field2, String));
|
||||
name2 = strVal(field2);
|
||||
|
||||
/* Whole-row reference? */
|
||||
if (strcmp(name3, "*") == 0)
|
||||
if (IsA(field3, A_Star))
|
||||
{
|
||||
node = transformWholeRowRef(pstate, name1, name2,
|
||||
cref->location);
|
||||
break;
|
||||
}
|
||||
|
||||
Assert(IsA(field3, String));
|
||||
name3 = strVal(field3);
|
||||
|
||||
/* Try to identify as a twice-qualified column */
|
||||
node = qualifiedNameToVar(pstate, name1, name2, name3, true,
|
||||
cref->location);
|
||||
@@ -520,10 +550,21 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
char *name1 = strVal(linitial(cref->fields));
|
||||
char *name2 = strVal(lsecond(cref->fields));
|
||||
char *name3 = strVal(lthird(cref->fields));
|
||||
char *name4 = strVal(lfourth(cref->fields));
|
||||
Node *field1 = (Node *) linitial(cref->fields);
|
||||
Node *field2 = (Node *) lsecond(cref->fields);
|
||||
Node *field3 = (Node *) lthird(cref->fields);
|
||||
Node *field4 = (Node *) lfourth(cref->fields);
|
||||
char *name1;
|
||||
char *name2;
|
||||
char *name3;
|
||||
char *name4;
|
||||
|
||||
Assert(IsA(field1, String));
|
||||
name1 = strVal(field1);
|
||||
Assert(IsA(field2, String));
|
||||
name2 = strVal(field2);
|
||||
Assert(IsA(field3, String));
|
||||
name3 = strVal(field3);
|
||||
|
||||
/*
|
||||
* We check the catalog name and then ignore it.
|
||||
@@ -536,13 +577,16 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
|
||||
parser_errposition(pstate, cref->location)));
|
||||
|
||||
/* Whole-row reference? */
|
||||
if (strcmp(name4, "*") == 0)
|
||||
if (IsA(field4, A_Star))
|
||||
{
|
||||
node = transformWholeRowRef(pstate, name2, name3,
|
||||
cref->location);
|
||||
break;
|
||||
}
|
||||
|
||||
Assert(IsA(field4, String));
|
||||
name4 = strVal(field4);
|
||||
|
||||
/* Try to identify as a twice-qualified column */
|
||||
node = qualifiedNameToVar(pstate, name2, name3, name4, true,
|
||||
cref->location);
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.162 2008/08/28 23:09:48 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.163 2008/08/30 01:39:14 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -109,14 +109,14 @@ transformTargetList(ParseState *pstate, List *targetlist)
|
||||
|
||||
/*
|
||||
* Check for "something.*". Depending on the complexity of the
|
||||
* "something", the star could appear as the last name in ColumnRef,
|
||||
* "something", the star could appear as the last field in ColumnRef,
|
||||
* or as the last indirection item in A_Indirection.
|
||||
*/
|
||||
if (IsA(res->val, ColumnRef))
|
||||
{
|
||||
ColumnRef *cref = (ColumnRef *) res->val;
|
||||
|
||||
if (strcmp(strVal(llast(cref->fields)), "*") == 0)
|
||||
if (IsA(llast(cref->fields), A_Star))
|
||||
{
|
||||
/* It is something.*, expand into multiple items */
|
||||
p_target = list_concat(p_target,
|
||||
@@ -128,10 +128,8 @@ transformTargetList(ParseState *pstate, List *targetlist)
|
||||
else if (IsA(res->val, A_Indirection))
|
||||
{
|
||||
A_Indirection *ind = (A_Indirection *) res->val;
|
||||
Node *lastitem = llast(ind->indirection);
|
||||
|
||||
if (IsA(lastitem, String) &&
|
||||
strcmp(strVal(lastitem), "*") == 0)
|
||||
if (IsA(llast(ind->indirection), A_Star))
|
||||
{
|
||||
/* It is something.*, expand into multiple items */
|
||||
p_target = list_concat(p_target,
|
||||
@@ -176,14 +174,14 @@ transformExpressionList(ParseState *pstate, List *exprlist)
|
||||
|
||||
/*
|
||||
* Check for "something.*". Depending on the complexity of the
|
||||
* "something", the star could appear as the last name in ColumnRef,
|
||||
* "something", the star could appear as the last field in ColumnRef,
|
||||
* or as the last indirection item in A_Indirection.
|
||||
*/
|
||||
if (IsA(e, ColumnRef))
|
||||
{
|
||||
ColumnRef *cref = (ColumnRef *) e;
|
||||
|
||||
if (strcmp(strVal(llast(cref->fields)), "*") == 0)
|
||||
if (IsA(llast(cref->fields), A_Star))
|
||||
{
|
||||
/* It is something.*, expand into multiple items */
|
||||
result = list_concat(result,
|
||||
@@ -195,10 +193,8 @@ transformExpressionList(ParseState *pstate, List *exprlist)
|
||||
else if (IsA(e, A_Indirection))
|
||||
{
|
||||
A_Indirection *ind = (A_Indirection *) e;
|
||||
Node *lastitem = llast(ind->indirection);
|
||||
|
||||
if (IsA(lastitem, String) &&
|
||||
strcmp(strVal(lastitem), "*") == 0)
|
||||
if (IsA(llast(ind->indirection), A_Star))
|
||||
{
|
||||
/* It is something.*, expand into multiple items */
|
||||
result = list_concat(result,
|
||||
@@ -560,6 +556,13 @@ transformAssignmentIndirection(ParseState *pstate,
|
||||
if (((A_Indices *) n)->lidx != NULL)
|
||||
isSlice = true;
|
||||
}
|
||||
else if (IsA(n, A_Star))
|
||||
{
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("row expansion via \"*\" is not supported here"),
|
||||
parser_errposition(pstate, location)));
|
||||
}
|
||||
else
|
||||
{
|
||||
FieldStore *fstore;
|
||||
@@ -809,7 +812,7 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
|
||||
* ExpandColumnRefStar()
|
||||
* Transforms foo.* into a list of expressions or targetlist entries.
|
||||
*
|
||||
* This handles the case where '*' appears as the last or only name in a
|
||||
* This handles the case where '*' appears as the last or only item in a
|
||||
* ColumnRef. The code is shared between the case of foo.* at the top level
|
||||
* in a SELECT target list (where we want TargetEntry nodes in the result)
|
||||
* and foo.* in a ROW() or VALUES() construct (where we want just bare
|
||||
@@ -830,13 +833,9 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref,
|
||||
* (e.g., SELECT * FROM emp, dept)
|
||||
*
|
||||
* Since the grammar only accepts bare '*' at top level of SELECT, we
|
||||
* need not handle the targetlist==false case here. However, we must
|
||||
* test for it because the grammar currently fails to distinguish a
|
||||
* quoted name "*" from a real asterisk.
|
||||
* need not handle the targetlist==false case here.
|
||||
*/
|
||||
if (!targetlist)
|
||||
elog(ERROR, "invalid use of *");
|
||||
|
||||
Assert(targetlist);
|
||||
return ExpandAllTables(pstate);
|
||||
}
|
||||
else
|
||||
@@ -1226,7 +1225,7 @@ FigureColnameInternal(Node *node, char **name)
|
||||
{
|
||||
Node *i = lfirst(l);
|
||||
|
||||
if (strcmp(strVal(i), "*") != 0)
|
||||
if (IsA(i, String))
|
||||
fname = strVal(i);
|
||||
}
|
||||
if (fname)
|
||||
@@ -1242,13 +1241,12 @@ FigureColnameInternal(Node *node, char **name)
|
||||
char *fname = NULL;
|
||||
ListCell *l;
|
||||
|
||||
/* find last field name, if any, ignoring "*" */
|
||||
/* find last field name, if any, ignoring "*" and subscripts */
|
||||
foreach(l, ind->indirection)
|
||||
{
|
||||
Node *i = lfirst(l);
|
||||
|
||||
if (IsA(i, String) &&
|
||||
strcmp(strVal(i), "*") != 0)
|
||||
if (IsA(i, String))
|
||||
fname = strVal(i);
|
||||
}
|
||||
if (fname)
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.97 2008/04/29 20:44:49 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.98 2008/08/30 01:39:14 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -305,7 +305,8 @@ typenameTypeMod(ParseState *pstate, const TypeName *typename, Type typ)
|
||||
{
|
||||
ColumnRef *cr = (ColumnRef *) tm;
|
||||
|
||||
if (list_length(cr->fields) == 1)
|
||||
if (list_length(cr->fields) == 1 &&
|
||||
IsA(linitial(cr->fields), String))
|
||||
cstr = strVal(linitial(cr->fields));
|
||||
}
|
||||
if (!cstr)
|
||||
|
Reference in New Issue
Block a user