mirror of
https://github.com/postgres/postgres.git
synced 2025-07-09 22:41:56 +03:00
We now create pg_constaint rows for NOT NULL constraints with contype='n'. We propagate these constraints during operations such as adding inheritance relationships, creating and attaching partitions, creating tables LIKE other tables. We mostly follow the well-known rules of conislocal and coninhcount that we have for CHECK constraints, with some adaptations; for example, as opposed to CHECK constraints, we don't match NOT NULL ones by name when descending a hierarchy to alter it; instead we match by column number. This means we don't require the constraint names to be identical across a hierarchy. For now, we omit them from system catalogs. Maybe this is worth reconsidering. We don't support NOT VALID nor DEFERRABLE clauses either; these can be added as separate features later (this patch is already large and complicated enough.) This has been very long in the making. The first patch was written by Bernd Helmle in 2010 to add a new pg_constraint.contype value ('n'), which I (Álvaro) then hijacked in 2011 and 2012, until that one was killed by the realization that we ought to use contype='c' instead: manufactured CHECK constraints. However, later SQL standard development, as well as nonobvious emergent properties of that design (mostly, failure to distinguish them from "normal" CHECK constraints as well as the performance implication of having to test the CHECK expression) led us to reconsider this choice, so now the current implementation uses contype='n' again. In 2016 Vitaly Burovoy also worked on this feature[1] but found no consensus for his proposed approach, which was claimed to be closer to the letter of the standard, requiring additional pg_attribute columns to track the OID of the NOT NULL constraint for that column. [1] https://postgr.es/m/CAKOSWNkN6HSyatuys8xZxzRCR-KL1OkHS5-b9qd9bf1Rad3PLA@mail.gmail.com Author: Álvaro Herrera <alvherre@alvh.no-ip.org> Author: Bernd Helmle <mailings@oopsware.de> Reviewed-by: Justin Pryzby <pryzby@telsasoft.com> Reviewed-by: Peter Eisentraut <peter.eisentraut@enterprisedb.com> Discussion: https://postgr.es/m/CACA0E642A0267EDA387AF2B%40%5B172.26.14.62%5D Discussion: https://postgr.es/m/AANLkTinLXMOEMz+0J29tf1POokKi4XDkWJ6-DDR9BKgU@mail.gmail.com Discussion: https://postgr.es/m/20110707213401.GA27098@alvh.no-ip.org Discussion: https://postgr.es/m/1343682669-sup-2532@alvh.no-ip.org Discussion: https://postgr.es/m/CAKOSWNkN6HSyatuys8xZxzRCR-KL1OkHS5-b9qd9bf1Rad3PLA@mail.gmail.com Discussion: https://postgr.es/m/20220817181249.q7qvj3okywctra3c@alvherre.pgsql
836 lines
23 KiB
C
836 lines
23 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* readfuncs.c
|
|
* Reader functions for Postgres tree nodes.
|
|
*
|
|
* Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* src/backend/nodes/readfuncs.c
|
|
*
|
|
* NOTES
|
|
* Parse location fields are written out by outfuncs.c, but only for
|
|
* debugging use. When reading a location field, we normally discard
|
|
* the stored value and set the location field to -1 (ie, "unknown").
|
|
* This is because nodes coming from a stored rule should not be thought
|
|
* to have a known location in the current query's text.
|
|
*
|
|
* However, if restore_location_fields is true, we do restore location
|
|
* fields from the string. This is currently intended only for use by the
|
|
* WRITE_READ_PARSE_PLAN_TREES test code, which doesn't want to cause
|
|
* any change in the node contents.
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include <math.h>
|
|
|
|
#include "miscadmin.h"
|
|
#include "nodes/bitmapset.h"
|
|
#include "nodes/readfuncs.h"
|
|
|
|
|
|
/*
|
|
* Macros to simplify reading of different kinds of fields. Use these
|
|
* wherever possible to reduce the chance for silly typos. Note that these
|
|
* hard-wire conventions about the names of the local variables in a Read
|
|
* routine.
|
|
*/
|
|
|
|
/* Macros for declaring appropriate local variables */
|
|
|
|
/* A few guys need only local_node */
|
|
#define READ_LOCALS_NO_FIELDS(nodeTypeName) \
|
|
nodeTypeName *local_node = makeNode(nodeTypeName)
|
|
|
|
/* And a few guys need only the pg_strtok support fields */
|
|
#define READ_TEMP_LOCALS() \
|
|
const char *token; \
|
|
int length
|
|
|
|
/* ... but most need both */
|
|
#define READ_LOCALS(nodeTypeName) \
|
|
READ_LOCALS_NO_FIELDS(nodeTypeName); \
|
|
READ_TEMP_LOCALS()
|
|
|
|
/* Read an integer field (anything written as ":fldname %d") */
|
|
#define READ_INT_FIELD(fldname) \
|
|
token = pg_strtok(&length); /* skip :fldname */ \
|
|
token = pg_strtok(&length); /* get field value */ \
|
|
local_node->fldname = atoi(token)
|
|
|
|
/* Read an unsigned integer field (anything written as ":fldname %u") */
|
|
#define READ_UINT_FIELD(fldname) \
|
|
token = pg_strtok(&length); /* skip :fldname */ \
|
|
token = pg_strtok(&length); /* get field value */ \
|
|
local_node->fldname = atoui(token)
|
|
|
|
/* Read an unsigned integer field (anything written using UINT64_FORMAT) */
|
|
#define READ_UINT64_FIELD(fldname) \
|
|
token = pg_strtok(&length); /* skip :fldname */ \
|
|
token = pg_strtok(&length); /* get field value */ \
|
|
local_node->fldname = strtou64(token, NULL, 10)
|
|
|
|
/* Read a long integer field (anything written as ":fldname %ld") */
|
|
#define READ_LONG_FIELD(fldname) \
|
|
token = pg_strtok(&length); /* skip :fldname */ \
|
|
token = pg_strtok(&length); /* get field value */ \
|
|
local_node->fldname = atol(token)
|
|
|
|
/* Read an OID field (don't hard-wire assumption that OID is same as uint) */
|
|
#define READ_OID_FIELD(fldname) \
|
|
token = pg_strtok(&length); /* skip :fldname */ \
|
|
token = pg_strtok(&length); /* get field value */ \
|
|
local_node->fldname = atooid(token)
|
|
|
|
/* Read a char field (ie, one ascii character) */
|
|
#define READ_CHAR_FIELD(fldname) \
|
|
token = pg_strtok(&length); /* skip :fldname */ \
|
|
token = pg_strtok(&length); /* get field value */ \
|
|
/* avoid overhead of calling debackslash() for one char */ \
|
|
local_node->fldname = (length == 0) ? '\0' : (token[0] == '\\' ? token[1] : token[0])
|
|
|
|
/* Read an enumerated-type field that was written as an integer code */
|
|
#define READ_ENUM_FIELD(fldname, enumtype) \
|
|
token = pg_strtok(&length); /* skip :fldname */ \
|
|
token = pg_strtok(&length); /* get field value */ \
|
|
local_node->fldname = (enumtype) atoi(token)
|
|
|
|
/* Read a float field */
|
|
#define READ_FLOAT_FIELD(fldname) \
|
|
token = pg_strtok(&length); /* skip :fldname */ \
|
|
token = pg_strtok(&length); /* get field value */ \
|
|
local_node->fldname = atof(token)
|
|
|
|
/* Read a boolean field */
|
|
#define READ_BOOL_FIELD(fldname) \
|
|
token = pg_strtok(&length); /* skip :fldname */ \
|
|
token = pg_strtok(&length); /* get field value */ \
|
|
local_node->fldname = strtobool(token)
|
|
|
|
/* Read a character-string field */
|
|
#define READ_STRING_FIELD(fldname) \
|
|
token = pg_strtok(&length); /* skip :fldname */ \
|
|
token = pg_strtok(&length); /* get field value */ \
|
|
local_node->fldname = nullable_string(token, length)
|
|
|
|
/* Read a parse location field (and possibly throw away the value) */
|
|
#ifdef WRITE_READ_PARSE_PLAN_TREES
|
|
#define READ_LOCATION_FIELD(fldname) \
|
|
token = pg_strtok(&length); /* skip :fldname */ \
|
|
token = pg_strtok(&length); /* get field value */ \
|
|
local_node->fldname = restore_location_fields ? atoi(token) : -1
|
|
#else
|
|
#define READ_LOCATION_FIELD(fldname) \
|
|
token = pg_strtok(&length); /* skip :fldname */ \
|
|
token = pg_strtok(&length); /* get field value */ \
|
|
(void) token; /* in case not used elsewhere */ \
|
|
local_node->fldname = -1 /* set field to "unknown" */
|
|
#endif
|
|
|
|
/* Read a Node field */
|
|
#define READ_NODE_FIELD(fldname) \
|
|
token = pg_strtok(&length); /* skip :fldname */ \
|
|
(void) token; /* in case not used elsewhere */ \
|
|
local_node->fldname = nodeRead(NULL, 0)
|
|
|
|
/* Read a bitmapset field */
|
|
#define READ_BITMAPSET_FIELD(fldname) \
|
|
token = pg_strtok(&length); /* skip :fldname */ \
|
|
(void) token; /* in case not used elsewhere */ \
|
|
local_node->fldname = _readBitmapset()
|
|
|
|
/* Read an attribute number array */
|
|
#define READ_ATTRNUMBER_ARRAY(fldname, len) \
|
|
token = pg_strtok(&length); /* skip :fldname */ \
|
|
local_node->fldname = readAttrNumberCols(len)
|
|
|
|
/* Read an oid array */
|
|
#define READ_OID_ARRAY(fldname, len) \
|
|
token = pg_strtok(&length); /* skip :fldname */ \
|
|
local_node->fldname = readOidCols(len)
|
|
|
|
/* Read an int array */
|
|
#define READ_INT_ARRAY(fldname, len) \
|
|
token = pg_strtok(&length); /* skip :fldname */ \
|
|
local_node->fldname = readIntCols(len)
|
|
|
|
/* Read a bool array */
|
|
#define READ_BOOL_ARRAY(fldname, len) \
|
|
token = pg_strtok(&length); /* skip :fldname */ \
|
|
local_node->fldname = readBoolCols(len)
|
|
|
|
/* Routine exit */
|
|
#define READ_DONE() \
|
|
return local_node
|
|
|
|
|
|
/*
|
|
* NOTE: use atoi() to read values written with %d, or atoui() to read
|
|
* values written with %u in outfuncs.c. An exception is OID values,
|
|
* for which use atooid(). (As of 7.1, outfuncs.c writes OIDs as %u,
|
|
* but this will probably change in the future.)
|
|
*/
|
|
#define atoui(x) ((unsigned int) strtoul((x), NULL, 10))
|
|
|
|
#define strtobool(x) ((*(x) == 't') ? true : false)
|
|
|
|
static char *
|
|
nullable_string(const char *token, int length)
|
|
{
|
|
/* outToken emits <> for NULL, and pg_strtok makes that an empty string */
|
|
if (length == 0)
|
|
return NULL;
|
|
/* outToken emits "" for empty string */
|
|
if (length == 2 && token[0] == '"' && token[1] == '"')
|
|
return pstrdup("");
|
|
/* otherwise, we must remove protective backslashes added by outToken */
|
|
return debackslash(token, length);
|
|
}
|
|
|
|
|
|
/*
|
|
* _readBitmapset
|
|
*
|
|
* Note: this code is used in contexts where we know that a Bitmapset
|
|
* is expected. There is equivalent code in nodeRead() that can read a
|
|
* Bitmapset when we come across one in other contexts.
|
|
*/
|
|
static Bitmapset *
|
|
_readBitmapset(void)
|
|
{
|
|
Bitmapset *result = NULL;
|
|
|
|
READ_TEMP_LOCALS();
|
|
|
|
token = pg_strtok(&length);
|
|
if (token == NULL)
|
|
elog(ERROR, "incomplete Bitmapset structure");
|
|
if (length != 1 || token[0] != '(')
|
|
elog(ERROR, "unrecognized token: \"%.*s\"", length, token);
|
|
|
|
token = pg_strtok(&length);
|
|
if (token == NULL)
|
|
elog(ERROR, "incomplete Bitmapset structure");
|
|
if (length != 1 || token[0] != 'b')
|
|
elog(ERROR, "unrecognized token: \"%.*s\"", length, token);
|
|
|
|
for (;;)
|
|
{
|
|
int val;
|
|
char *endptr;
|
|
|
|
token = pg_strtok(&length);
|
|
if (token == NULL)
|
|
elog(ERROR, "unterminated Bitmapset structure");
|
|
if (length == 1 && token[0] == ')')
|
|
break;
|
|
val = (int) strtol(token, &endptr, 10);
|
|
if (endptr != token + length)
|
|
elog(ERROR, "unrecognized integer: \"%.*s\"", length, token);
|
|
result = bms_add_member(result, val);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* We export this function for use by extensions that define extensible nodes.
|
|
* That's somewhat historical, though, because calling nodeRead() will work.
|
|
*/
|
|
Bitmapset *
|
|
readBitmapset(void)
|
|
{
|
|
return _readBitmapset();
|
|
}
|
|
|
|
#include "readfuncs.funcs.c"
|
|
|
|
|
|
/*
|
|
* Support functions for nodes with custom_read_write attribute or
|
|
* special_read_write attribute
|
|
*/
|
|
|
|
static Const *
|
|
_readConst(void)
|
|
{
|
|
READ_LOCALS(Const);
|
|
|
|
READ_OID_FIELD(consttype);
|
|
READ_INT_FIELD(consttypmod);
|
|
READ_OID_FIELD(constcollid);
|
|
READ_INT_FIELD(constlen);
|
|
READ_BOOL_FIELD(constbyval);
|
|
READ_BOOL_FIELD(constisnull);
|
|
READ_LOCATION_FIELD(location);
|
|
|
|
token = pg_strtok(&length); /* skip :constvalue */
|
|
if (local_node->constisnull)
|
|
token = pg_strtok(&length); /* skip "<>" */
|
|
else
|
|
local_node->constvalue = readDatum(local_node->constbyval);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
static BoolExpr *
|
|
_readBoolExpr(void)
|
|
{
|
|
READ_LOCALS(BoolExpr);
|
|
|
|
/* do-it-yourself enum representation */
|
|
token = pg_strtok(&length); /* skip :boolop */
|
|
token = pg_strtok(&length); /* get field value */
|
|
if (length == 3 && strncmp(token, "and", 3) == 0)
|
|
local_node->boolop = AND_EXPR;
|
|
else if (length == 2 && strncmp(token, "or", 2) == 0)
|
|
local_node->boolop = OR_EXPR;
|
|
else if (length == 3 && strncmp(token, "not", 3) == 0)
|
|
local_node->boolop = NOT_EXPR;
|
|
else
|
|
elog(ERROR, "unrecognized boolop \"%.*s\"", length, token);
|
|
|
|
READ_NODE_FIELD(args);
|
|
READ_LOCATION_FIELD(location);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
static A_Const *
|
|
_readA_Const(void)
|
|
{
|
|
READ_LOCALS(A_Const);
|
|
|
|
/* We expect either NULL or :val here */
|
|
token = pg_strtok(&length);
|
|
if (length == 4 && strncmp(token, "NULL", 4) == 0)
|
|
local_node->isnull = true;
|
|
else
|
|
{
|
|
union ValUnion *tmp = nodeRead(NULL, 0);
|
|
|
|
/* To forestall valgrind complaints, copy only the valid data */
|
|
switch (nodeTag(tmp))
|
|
{
|
|
case T_Integer:
|
|
memcpy(&local_node->val, tmp, sizeof(Integer));
|
|
break;
|
|
case T_Float:
|
|
memcpy(&local_node->val, tmp, sizeof(Float));
|
|
break;
|
|
case T_Boolean:
|
|
memcpy(&local_node->val, tmp, sizeof(Boolean));
|
|
break;
|
|
case T_String:
|
|
memcpy(&local_node->val, tmp, sizeof(String));
|
|
break;
|
|
case T_BitString:
|
|
memcpy(&local_node->val, tmp, sizeof(BitString));
|
|
break;
|
|
default:
|
|
elog(ERROR, "unrecognized node type: %d",
|
|
(int) nodeTag(tmp));
|
|
break;
|
|
}
|
|
}
|
|
|
|
READ_LOCATION_FIELD(location);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readConstraint
|
|
*/
|
|
static Constraint *
|
|
_readConstraint(void)
|
|
{
|
|
READ_LOCALS(Constraint);
|
|
|
|
READ_STRING_FIELD(conname);
|
|
READ_BOOL_FIELD(deferrable);
|
|
READ_BOOL_FIELD(initdeferred);
|
|
READ_LOCATION_FIELD(location);
|
|
|
|
token = pg_strtok(&length); /* skip :contype */
|
|
token = pg_strtok(&length); /* get field value */
|
|
if (length == 4 && strncmp(token, "NULL", 4) == 0)
|
|
local_node->contype = CONSTR_NULL;
|
|
else if (length == 8 && strncmp(token, "NOT_NULL", 8) == 0)
|
|
local_node->contype = CONSTR_NOTNULL;
|
|
else if (length == 7 && strncmp(token, "DEFAULT", 7) == 0)
|
|
local_node->contype = CONSTR_DEFAULT;
|
|
else if (length == 8 && strncmp(token, "IDENTITY", 8) == 0)
|
|
local_node->contype = CONSTR_IDENTITY;
|
|
else if (length == 9 && strncmp(token, "GENERATED", 9) == 0)
|
|
local_node->contype = CONSTR_GENERATED;
|
|
else if (length == 5 && strncmp(token, "CHECK", 5) == 0)
|
|
local_node->contype = CONSTR_CHECK;
|
|
else if (length == 11 && strncmp(token, "PRIMARY_KEY", 11) == 0)
|
|
local_node->contype = CONSTR_PRIMARY;
|
|
else if (length == 6 && strncmp(token, "UNIQUE", 6) == 0)
|
|
local_node->contype = CONSTR_UNIQUE;
|
|
else if (length == 9 && strncmp(token, "EXCLUSION", 9) == 0)
|
|
local_node->contype = CONSTR_EXCLUSION;
|
|
else if (length == 11 && strncmp(token, "FOREIGN_KEY", 11) == 0)
|
|
local_node->contype = CONSTR_FOREIGN;
|
|
else if (length == 15 && strncmp(token, "ATTR_DEFERRABLE", 15) == 0)
|
|
local_node->contype = CONSTR_ATTR_DEFERRABLE;
|
|
else if (length == 19 && strncmp(token, "ATTR_NOT_DEFERRABLE", 19) == 0)
|
|
local_node->contype = CONSTR_ATTR_NOT_DEFERRABLE;
|
|
else if (length == 13 && strncmp(token, "ATTR_DEFERRED", 13) == 0)
|
|
local_node->contype = CONSTR_ATTR_DEFERRED;
|
|
else if (length == 14 && strncmp(token, "ATTR_IMMEDIATE", 14) == 0)
|
|
local_node->contype = CONSTR_ATTR_IMMEDIATE;
|
|
|
|
switch (local_node->contype)
|
|
{
|
|
case CONSTR_NULL:
|
|
/* no extra fields */
|
|
break;
|
|
|
|
case CONSTR_NOTNULL:
|
|
READ_BOOL_FIELD(is_no_inherit);
|
|
READ_STRING_FIELD(colname);
|
|
READ_BOOL_FIELD(skip_validation);
|
|
READ_BOOL_FIELD(initially_valid);
|
|
break;
|
|
|
|
case CONSTR_DEFAULT:
|
|
READ_NODE_FIELD(raw_expr);
|
|
READ_STRING_FIELD(cooked_expr);
|
|
break;
|
|
|
|
case CONSTR_IDENTITY:
|
|
READ_NODE_FIELD(options);
|
|
READ_CHAR_FIELD(generated_when);
|
|
break;
|
|
|
|
case CONSTR_GENERATED:
|
|
READ_NODE_FIELD(raw_expr);
|
|
READ_STRING_FIELD(cooked_expr);
|
|
READ_CHAR_FIELD(generated_when);
|
|
break;
|
|
|
|
case CONSTR_CHECK:
|
|
READ_BOOL_FIELD(is_no_inherit);
|
|
READ_NODE_FIELD(raw_expr);
|
|
READ_STRING_FIELD(cooked_expr);
|
|
READ_BOOL_FIELD(skip_validation);
|
|
READ_BOOL_FIELD(initially_valid);
|
|
break;
|
|
|
|
case CONSTR_PRIMARY:
|
|
READ_NODE_FIELD(keys);
|
|
READ_NODE_FIELD(including);
|
|
READ_NODE_FIELD(options);
|
|
READ_STRING_FIELD(indexname);
|
|
READ_STRING_FIELD(indexspace);
|
|
READ_BOOL_FIELD(reset_default_tblspc);
|
|
/* access_method and where_clause not currently used */
|
|
break;
|
|
|
|
case CONSTR_UNIQUE:
|
|
READ_BOOL_FIELD(nulls_not_distinct);
|
|
READ_NODE_FIELD(keys);
|
|
READ_NODE_FIELD(including);
|
|
READ_NODE_FIELD(options);
|
|
READ_STRING_FIELD(indexname);
|
|
READ_STRING_FIELD(indexspace);
|
|
READ_BOOL_FIELD(reset_default_tblspc);
|
|
/* access_method and where_clause not currently used */
|
|
break;
|
|
|
|
case CONSTR_EXCLUSION:
|
|
READ_NODE_FIELD(exclusions);
|
|
READ_NODE_FIELD(including);
|
|
READ_NODE_FIELD(options);
|
|
READ_STRING_FIELD(indexname);
|
|
READ_STRING_FIELD(indexspace);
|
|
READ_BOOL_FIELD(reset_default_tblspc);
|
|
READ_STRING_FIELD(access_method);
|
|
READ_NODE_FIELD(where_clause);
|
|
break;
|
|
|
|
case CONSTR_FOREIGN:
|
|
READ_NODE_FIELD(pktable);
|
|
READ_NODE_FIELD(fk_attrs);
|
|
READ_NODE_FIELD(pk_attrs);
|
|
READ_CHAR_FIELD(fk_matchtype);
|
|
READ_CHAR_FIELD(fk_upd_action);
|
|
READ_CHAR_FIELD(fk_del_action);
|
|
READ_NODE_FIELD(fk_del_set_cols);
|
|
READ_NODE_FIELD(old_conpfeqop);
|
|
READ_OID_FIELD(old_pktable_oid);
|
|
READ_BOOL_FIELD(skip_validation);
|
|
READ_BOOL_FIELD(initially_valid);
|
|
break;
|
|
|
|
case CONSTR_ATTR_DEFERRABLE:
|
|
case CONSTR_ATTR_NOT_DEFERRABLE:
|
|
case CONSTR_ATTR_DEFERRED:
|
|
case CONSTR_ATTR_IMMEDIATE:
|
|
/* no extra fields */
|
|
break;
|
|
|
|
default:
|
|
elog(ERROR, "unrecognized ConstrType: %d", (int) local_node->contype);
|
|
break;
|
|
}
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
static RangeTblEntry *
|
|
_readRangeTblEntry(void)
|
|
{
|
|
READ_LOCALS(RangeTblEntry);
|
|
|
|
/* put alias + eref first to make dump more legible */
|
|
READ_NODE_FIELD(alias);
|
|
READ_NODE_FIELD(eref);
|
|
READ_ENUM_FIELD(rtekind, RTEKind);
|
|
|
|
switch (local_node->rtekind)
|
|
{
|
|
case RTE_RELATION:
|
|
READ_OID_FIELD(relid);
|
|
READ_CHAR_FIELD(relkind);
|
|
READ_INT_FIELD(rellockmode);
|
|
READ_NODE_FIELD(tablesample);
|
|
READ_UINT_FIELD(perminfoindex);
|
|
break;
|
|
case RTE_SUBQUERY:
|
|
READ_NODE_FIELD(subquery);
|
|
READ_BOOL_FIELD(security_barrier);
|
|
/* we re-use these RELATION fields, too: */
|
|
READ_OID_FIELD(relid);
|
|
READ_INT_FIELD(rellockmode);
|
|
READ_UINT_FIELD(perminfoindex);
|
|
break;
|
|
case RTE_JOIN:
|
|
READ_ENUM_FIELD(jointype, JoinType);
|
|
READ_INT_FIELD(joinmergedcols);
|
|
READ_NODE_FIELD(joinaliasvars);
|
|
READ_NODE_FIELD(joinleftcols);
|
|
READ_NODE_FIELD(joinrightcols);
|
|
READ_NODE_FIELD(join_using_alias);
|
|
break;
|
|
case RTE_FUNCTION:
|
|
READ_NODE_FIELD(functions);
|
|
READ_BOOL_FIELD(funcordinality);
|
|
break;
|
|
case RTE_TABLEFUNC:
|
|
READ_NODE_FIELD(tablefunc);
|
|
/* The RTE must have a copy of the column type info, if any */
|
|
if (local_node->tablefunc)
|
|
{
|
|
TableFunc *tf = local_node->tablefunc;
|
|
|
|
local_node->coltypes = tf->coltypes;
|
|
local_node->coltypmods = tf->coltypmods;
|
|
local_node->colcollations = tf->colcollations;
|
|
}
|
|
break;
|
|
case RTE_VALUES:
|
|
READ_NODE_FIELD(values_lists);
|
|
READ_NODE_FIELD(coltypes);
|
|
READ_NODE_FIELD(coltypmods);
|
|
READ_NODE_FIELD(colcollations);
|
|
break;
|
|
case RTE_CTE:
|
|
READ_STRING_FIELD(ctename);
|
|
READ_UINT_FIELD(ctelevelsup);
|
|
READ_BOOL_FIELD(self_reference);
|
|
READ_NODE_FIELD(coltypes);
|
|
READ_NODE_FIELD(coltypmods);
|
|
READ_NODE_FIELD(colcollations);
|
|
break;
|
|
case RTE_NAMEDTUPLESTORE:
|
|
READ_STRING_FIELD(enrname);
|
|
READ_FLOAT_FIELD(enrtuples);
|
|
READ_NODE_FIELD(coltypes);
|
|
READ_NODE_FIELD(coltypmods);
|
|
READ_NODE_FIELD(colcollations);
|
|
/* we re-use these RELATION fields, too: */
|
|
READ_OID_FIELD(relid);
|
|
break;
|
|
case RTE_RESULT:
|
|
/* no extra fields */
|
|
break;
|
|
default:
|
|
elog(ERROR, "unrecognized RTE kind: %d",
|
|
(int) local_node->rtekind);
|
|
break;
|
|
}
|
|
|
|
READ_BOOL_FIELD(lateral);
|
|
READ_BOOL_FIELD(inh);
|
|
READ_BOOL_FIELD(inFromCl);
|
|
READ_NODE_FIELD(securityQuals);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
static A_Expr *
|
|
_readA_Expr(void)
|
|
{
|
|
READ_LOCALS(A_Expr);
|
|
|
|
token = pg_strtok(&length);
|
|
|
|
if (length == 3 && strncmp(token, "ANY", 3) == 0)
|
|
{
|
|
local_node->kind = AEXPR_OP_ANY;
|
|
READ_NODE_FIELD(name);
|
|
}
|
|
else if (length == 3 && strncmp(token, "ALL", 3) == 0)
|
|
{
|
|
local_node->kind = AEXPR_OP_ALL;
|
|
READ_NODE_FIELD(name);
|
|
}
|
|
else if (length == 8 && strncmp(token, "DISTINCT", 8) == 0)
|
|
{
|
|
local_node->kind = AEXPR_DISTINCT;
|
|
READ_NODE_FIELD(name);
|
|
}
|
|
else if (length == 12 && strncmp(token, "NOT_DISTINCT", 12) == 0)
|
|
{
|
|
local_node->kind = AEXPR_NOT_DISTINCT;
|
|
READ_NODE_FIELD(name);
|
|
}
|
|
else if (length == 6 && strncmp(token, "NULLIF", 6) == 0)
|
|
{
|
|
local_node->kind = AEXPR_NULLIF;
|
|
READ_NODE_FIELD(name);
|
|
}
|
|
else if (length == 2 && strncmp(token, "IN", 2) == 0)
|
|
{
|
|
local_node->kind = AEXPR_IN;
|
|
READ_NODE_FIELD(name);
|
|
}
|
|
else if (length == 4 && strncmp(token, "LIKE", 4) == 0)
|
|
{
|
|
local_node->kind = AEXPR_LIKE;
|
|
READ_NODE_FIELD(name);
|
|
}
|
|
else if (length == 5 && strncmp(token, "ILIKE", 5) == 0)
|
|
{
|
|
local_node->kind = AEXPR_ILIKE;
|
|
READ_NODE_FIELD(name);
|
|
}
|
|
else if (length == 7 && strncmp(token, "SIMILAR", 7) == 0)
|
|
{
|
|
local_node->kind = AEXPR_SIMILAR;
|
|
READ_NODE_FIELD(name);
|
|
}
|
|
else if (length == 7 && strncmp(token, "BETWEEN", 7) == 0)
|
|
{
|
|
local_node->kind = AEXPR_BETWEEN;
|
|
READ_NODE_FIELD(name);
|
|
}
|
|
else if (length == 11 && strncmp(token, "NOT_BETWEEN", 11) == 0)
|
|
{
|
|
local_node->kind = AEXPR_NOT_BETWEEN;
|
|
READ_NODE_FIELD(name);
|
|
}
|
|
else if (length == 11 && strncmp(token, "BETWEEN_SYM", 11) == 0)
|
|
{
|
|
local_node->kind = AEXPR_BETWEEN_SYM;
|
|
READ_NODE_FIELD(name);
|
|
}
|
|
else if (length == 15 && strncmp(token, "NOT_BETWEEN_SYM", 15) == 0)
|
|
{
|
|
local_node->kind = AEXPR_NOT_BETWEEN_SYM;
|
|
READ_NODE_FIELD(name);
|
|
}
|
|
else if (length == 5 && strncmp(token, ":name", 5) == 0)
|
|
{
|
|
local_node->kind = AEXPR_OP;
|
|
local_node->name = nodeRead(NULL, 0);
|
|
}
|
|
else
|
|
elog(ERROR, "unrecognized A_Expr kind: \"%.*s\"", length, token);
|
|
|
|
READ_NODE_FIELD(lexpr);
|
|
READ_NODE_FIELD(rexpr);
|
|
READ_LOCATION_FIELD(location);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
static ExtensibleNode *
|
|
_readExtensibleNode(void)
|
|
{
|
|
const ExtensibleNodeMethods *methods;
|
|
ExtensibleNode *local_node;
|
|
const char *extnodename;
|
|
|
|
READ_TEMP_LOCALS();
|
|
|
|
token = pg_strtok(&length); /* skip :extnodename */
|
|
token = pg_strtok(&length); /* get extnodename */
|
|
|
|
extnodename = nullable_string(token, length);
|
|
if (!extnodename)
|
|
elog(ERROR, "extnodename has to be supplied");
|
|
methods = GetExtensibleNodeMethods(extnodename, false);
|
|
|
|
local_node = (ExtensibleNode *) newNode(methods->node_size,
|
|
T_ExtensibleNode);
|
|
local_node->extnodename = extnodename;
|
|
|
|
/* deserialize the private fields */
|
|
methods->nodeRead(local_node);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
|
|
/*
|
|
* parseNodeString
|
|
*
|
|
* Given a character string representing a node tree, parseNodeString creates
|
|
* the internal node structure.
|
|
*
|
|
* The string to be read must already have been loaded into pg_strtok().
|
|
*/
|
|
Node *
|
|
parseNodeString(void)
|
|
{
|
|
void *return_value;
|
|
|
|
READ_TEMP_LOCALS();
|
|
|
|
/* Guard against stack overflow due to overly complex expressions */
|
|
check_stack_depth();
|
|
|
|
token = pg_strtok(&length);
|
|
|
|
#define MATCH(tokname, namelen) \
|
|
(length == namelen && memcmp(token, tokname, namelen) == 0)
|
|
|
|
if (false)
|
|
;
|
|
#include "readfuncs.switch.c"
|
|
else
|
|
{
|
|
elog(ERROR, "badly formatted node string \"%.32s\"...", token);
|
|
return_value = NULL; /* keep compiler quiet */
|
|
}
|
|
|
|
return (Node *) return_value;
|
|
}
|
|
|
|
|
|
/*
|
|
* readDatum
|
|
*
|
|
* Given a string representation of a constant, recreate the appropriate
|
|
* Datum. The string representation embeds length info, but not byValue,
|
|
* so we must be told that.
|
|
*/
|
|
Datum
|
|
readDatum(bool typbyval)
|
|
{
|
|
Size length,
|
|
i;
|
|
int tokenLength;
|
|
const char *token;
|
|
Datum res;
|
|
char *s;
|
|
|
|
/*
|
|
* read the actual length of the value
|
|
*/
|
|
token = pg_strtok(&tokenLength);
|
|
length = atoui(token);
|
|
|
|
token = pg_strtok(&tokenLength); /* read the '[' */
|
|
if (token == NULL || token[0] != '[')
|
|
elog(ERROR, "expected \"[\" to start datum, but got \"%s\"; length = %zu",
|
|
token ? token : "[NULL]", length);
|
|
|
|
if (typbyval)
|
|
{
|
|
if (length > (Size) sizeof(Datum))
|
|
elog(ERROR, "byval datum but length = %zu", length);
|
|
res = (Datum) 0;
|
|
s = (char *) (&res);
|
|
for (i = 0; i < (Size) sizeof(Datum); i++)
|
|
{
|
|
token = pg_strtok(&tokenLength);
|
|
s[i] = (char) atoi(token);
|
|
}
|
|
}
|
|
else if (length <= 0)
|
|
res = (Datum) NULL;
|
|
else
|
|
{
|
|
s = (char *) palloc(length);
|
|
for (i = 0; i < length; i++)
|
|
{
|
|
token = pg_strtok(&tokenLength);
|
|
s[i] = (char) atoi(token);
|
|
}
|
|
res = PointerGetDatum(s);
|
|
}
|
|
|
|
token = pg_strtok(&tokenLength); /* read the ']' */
|
|
if (token == NULL || token[0] != ']')
|
|
elog(ERROR, "expected \"]\" to end datum, but got \"%s\"; length = %zu",
|
|
token ? token : "[NULL]", length);
|
|
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
* common implementation for scalar-array-reading functions
|
|
*
|
|
* The data format is either "<>" for a NULL pointer (in which case numCols
|
|
* is ignored) or "(item item item)" where the number of items must equal
|
|
* numCols. The convfunc must be okay with stopping at whitespace or a
|
|
* right parenthesis, since pg_strtok won't null-terminate the token.
|
|
*/
|
|
#define READ_SCALAR_ARRAY(fnname, datatype, convfunc) \
|
|
datatype * \
|
|
fnname(int numCols) \
|
|
{ \
|
|
datatype *vals; \
|
|
READ_TEMP_LOCALS(); \
|
|
token = pg_strtok(&length); \
|
|
if (token == NULL) \
|
|
elog(ERROR, "incomplete scalar array"); \
|
|
if (length == 0) \
|
|
return NULL; /* it was "<>", so return NULL pointer */ \
|
|
if (length != 1 || token[0] != '(') \
|
|
elog(ERROR, "unrecognized token: \"%.*s\"", length, token); \
|
|
vals = (datatype *) palloc(numCols * sizeof(datatype)); \
|
|
for (int i = 0; i < numCols; i++) \
|
|
{ \
|
|
token = pg_strtok(&length); \
|
|
if (token == NULL || token[0] == ')') \
|
|
elog(ERROR, "incomplete scalar array"); \
|
|
vals[i] = convfunc(token); \
|
|
} \
|
|
token = pg_strtok(&length); \
|
|
if (token == NULL || length != 1 || token[0] != ')') \
|
|
elog(ERROR, "incomplete scalar array"); \
|
|
return vals; \
|
|
}
|
|
|
|
/*
|
|
* Note: these functions are exported in nodes.h for possible use by
|
|
* extensions, so don't mess too much with their names or API.
|
|
*/
|
|
READ_SCALAR_ARRAY(readAttrNumberCols, int16, atoi)
|
|
READ_SCALAR_ARRAY(readOidCols, Oid, atooid)
|
|
/* outfuncs.c has writeIndexCols, but we don't yet need that here */
|
|
/* READ_SCALAR_ARRAY(readIndexCols, Index, atoui) */
|
|
READ_SCALAR_ARRAY(readIntCols, int, atoi)
|
|
READ_SCALAR_ARRAY(readBoolCols, bool, strtobool)
|