public inbox for [email protected]
help / color / mirror / Atom feedFrom: jian he <[email protected]>
To: Paul Jungwirth <[email protected]>
Cc: Peter Eisentraut <[email protected]>
Cc: PostgreSQL Hackers <[email protected]>
Subject: Re: SQL:2011 application time
Date: Wed, 25 Oct 2023 14:14:34 +0800
Message-ID: <CACJufxF9jJJqsVnmcDfVTMgn-hj15mJDvfByNOWZ9x0Wib3hSw@mail.gmail.com> (raw)
In-Reply-To: <[email protected]>
References: <CA+renyUApHgSZF9-nd-a0+OPGharLQLO=mDHcY4_qQ0+noCUVg@mail.gmail.com>
<[email protected]>
<[email protected]>
<[email protected]>
<CACJufxHD2kv6hCy8yjVk486+d5rcuJS2XxkNH80g9Ui6Rz81Cg@mail.gmail.com>
<CACJufxFTqiag-4fDPWJLxwwX+2gcZZWJPM9GyhPYD0tH=uM+jQ@mail.gmail.com>
<CA+renyV4RxkWx57Vi5k_k0d7mkT7+F2kHYuKkwNPBaSOCaotFA@mail.gmail.com>
<CACJufxG7DKLhyJgBaFttdXZe-4dOVu3NVED+4sdegdGs6R7JdQ@mail.gmail.com>
<CACJufxFxQH5NxD9UDvbcRCPtaSga+y0+Wwt54PA4KahLwhahOA@mail.gmail.com>
<[email protected]>
<CACJufxGCWDSK2pYMwNdsyDGk6skbP_L5yvSBK67L6pxbGUF8OQ@mail.gmail.com>
<[email protected]>
<CACJufxG_iZc8hbdcfjkU6q81kLf4R2Fp=VyDZbyhocuTbMNVEw@mail.gmail.com>
<[email protected]>
<[email protected]>
<[email protected]>
On Wed, Oct 11, 2023 at 12:47 PM Paul Jungwirth
<[email protected]> wrote:
>
> On 9/25/23 14:00, Peter Eisentraut wrote:
> > Looking through the tests in v16-0001:
> >
> > +-- PK with no columns just WITHOUT OVERLAPS:
> > +CREATE TABLE temporal_rng (
> > + valid_at tsrange,
> > + CONSTRAINT temporal_rng_pk PRIMARY KEY (valid_at WITHOUT OVERLAPS)
> > +);
> > +ERROR: syntax error at or near "WITHOUT"
> > +LINE 3: CONSTRAINT temporal_rng_pk PRIMARY KEY (valid_at WITHOUT OV...
> > + ^
> >
> > I think this error is confusing. The SQL standard requires at least one
> > non-period column in a PK. I don't know why that is or why we should
> > implement it. But if we want to implement it, maybe we should enforce
> > that in parse analysis rather than directly in the parser, to be able to
> > produce a more friendly error message.
>
> Okay.
>
> (I think the reason the standard requires one non-period column is to
> identify the "entity". If philosophically the row is an Aristotelian
> proposition about that thing, the period qualifies it as true just
> during some time span. So the scalar part is doing the work that a PK
> conventionally does, and the period part does something else. Perhaps a
> PK/UNIQUE constraint with no scalar part would still be useful, but not
> very often I think, and I'm not sure it makes sense to call it PK/UNIQUE.)
>
> > +-- PK with a range column/PERIOD that isn't there:
> > +CREATE TABLE temporal_rng (
> > + id INTEGER,
> > + CONSTRAINT temporal_rng_pk PRIMARY KEY (id, valid_at WITHOUT
> > OVERLAPS)
> > +);
> > +ERROR: range or PERIOD "valid_at" in WITHOUT OVERLAPS does not exist
> >
> > I think here we should just produce a "column doesn't exist" error
> > message, the same as if the "id" column was invalid. We don't need to
> > get into the details of what kind of column it should be. That is done
> > in the next test
>
> I'll change it. The reason for the different wording is that it might
> not be a column at all. It might be a PERIOD. So what about just "column
> or PERIOD doesn't exist"? (Your suggestion is fine too though.)
>
> > +ERROR: column "valid_at" in WITHOUT OVERLAPS is not a range type
> >
> > Also, in any case it would be nice to have a location pointer here (for
> > both cases).
>
> Agreed.
>
I refactored findNeworOldColumn to better handle error reports.
please check the attached.
Attachments:
[text/x-patch] v1-0001-refactor-findNewOrOldColumn.patch (7.6K, ../CACJufxF9jJJqsVnmcDfVTMgn-hj15mJDvfByNOWZ9x0Wib3hSw@mail.gmail.com/2-v1-0001-refactor-findNewOrOldColumn.patch)
download | inline diff:
From 81a5eab2596c0e322dee8165ad73343328f496ed Mon Sep 17 00:00:00 2001
From: pgaddict <[email protected]>
Date: Wed, 25 Oct 2023 13:50:38 +0800
Subject: [PATCH v1 1/1] refactor findNewOrOldColumn.
refator findNeworOldColumn to validate WITHOUT OVERLAP constraint
associated key column exists and it's a range data type.
create/alter table add without overlap constraint:
if the key column not exists,
it will report the same as no "without overlap" normal case.
create table add without overlap constraint:
if overlap constraint associated column's data type is not range,
it will report it's not a range error, also print out the
parser position.
alter table add without overlap constraint:
if overlap constraint associated column's data type is not range,
it will report not a range data type error.
it will not print out parser position (I don't know how to do that).
---
src/backend/parser/parse_utilcmd.c | 81 ++++++++++---------
.../regress/expected/without_overlaps.out | 8 +-
2 files changed, 47 insertions(+), 42 deletions(-)
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index e195410c..f44de591 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -124,8 +124,7 @@ static List *get_opclass(Oid opclass, Oid actual_datatype);
static void transformIndexConstraints(CreateStmtContext *cxt);
static IndexStmt *transformIndexConstraint(Constraint *constraint,
CreateStmtContext *cxt);
-static bool findNewOrOldColumn(CreateStmtContext *cxt, char *colname, char **typname,
- Oid *typid);
+static void validate_without_overlaps(CreateStmtContext *cxt, char *colname);
static void transformExtendedStatistics(CreateStmtContext *cxt);
static void transformFKConstraints(CreateStmtContext *cxt,
bool skipValidation,
@@ -2698,34 +2697,19 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
* Anything in without_overlaps should be included,
* but with the overlaps operator (&&) instead of equality.
*/
- if (constraint->without_overlaps != NULL) {
+ if (constraint->without_overlaps != NULL)
+ {
char *without_overlaps_str = strVal(constraint->without_overlaps);
IndexElem *iparam = makeNode(IndexElem);
- char *typname;
- Oid typid;
/*
* Iterate through the table's columns
* (like just a little bit above).
* If we find one whose name is the same as without_overlaps,
* validate that it's a range type.
- *
- * Otherwise report an error.
+ * if it cannot found, raise an error.
*/
-
- if (findNewOrOldColumn(cxt, without_overlaps_str, &typname, &typid))
- {
- if (!type_is_range(typid))
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("column \"%s\" in WITHOUT OVERLAPS is not a range type",
- without_overlaps_str)));
- }
- else
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_COLUMN),
- errmsg("range or PERIOD \"%s\" in WITHOUT OVERLAPS does not exist",
- without_overlaps_str)));
+ validate_without_overlaps(cxt, without_overlaps_str);
iparam->name = pstrdup(without_overlaps_str);
iparam->expr = NULL;
@@ -2744,6 +2728,9 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
{
/*
* Force the column to NOT NULL since it is part of the primary key.
+ * prior loop foreach(lc, constraint->keys) does not loop over
+ * without_overlap keys. So we need manually add here.
+ *
*/
AlterTableCmd *notnullcmd = makeNode(AlterTableCmd);
@@ -2872,31 +2859,37 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
}
/*
- * Tries to find a column by name among the existing ones (if it's an ALTER TABLE)
- * and the new ones. Sets typname and typid if one is found. Returns false if we
- * couldn't find a match.
+ * Tries to find and validate the typid associated
+ * with a without_overlaps column name, even in ALTER TABLE case.
+ * After check, without_overlaps column should exist, and it's a range type.
+ *
*/
-static bool
-findNewOrOldColumn(CreateStmtContext *cxt, char *colname, char **typname, Oid *typid)
+static void
+validate_without_overlaps(CreateStmtContext *cxt, char *colname)
{
/* Check the new columns first in case their type is changing. */
-
ColumnDef *column = NULL;
ListCell *columns;
+ Oid typid;
+ bool found = false;
foreach(columns, cxt->columns)
{
column = lfirst_node(ColumnDef, columns);
if (strcmp(column->colname, colname) == 0)
{
- *typid = typenameTypeId(NULL, column->typeName);
- *typname = TypeNameToString(column->typeName);
- return true;
+ typid = typenameTypeId(NULL, column->typeName);
+ if (!type_is_range(typid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("column \"%s\" in WITHOUT OVERLAPS is not a range type",colname),
+ parser_errposition(cxt->pstate, column->location)));
+ found = true;
+ break;
}
}
/* Look up columns on existing table. */
-
if (cxt->isalter)
{
Relation rel = cxt->rel;
@@ -2905,22 +2898,30 @@ findNewOrOldColumn(CreateStmtContext *cxt, char *colname, char **typname, Oid *t
Form_pg_attribute attr = TupleDescAttr(rel->rd_att, i);
const char *attname;
- if (attr->attisdropped)
- continue;
-
attname = NameStr(attr->attname);
if (strcmp(attname, colname) == 0)
{
- Type type = typeidType(attr->atttypid);
- *typid = attr->atttypid;
- *typname = pstrdup(typeTypeName(type));
- ReleaseSysCache(type);
- return true;
+ typid = attr->atttypid;
+
+ if (attr->attisdropped)
+ {
+ found = false;
+ break;
+ }
+ if (!type_is_range(typid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("column \"%s\" in WITHOUT OVERLAPS is not a range type", colname)));
+ found = true;
+ break;
}
}
}
- return false;
+ if (!found)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("column \"%s\" named in key does not exist",colname)));
}
/*
diff --git a/src/test/regress/expected/without_overlaps.out b/src/test/regress/expected/without_overlaps.out
index 4b8abb08..a916e56a 100644
--- a/src/test/regress/expected/without_overlaps.out
+++ b/src/test/regress/expected/without_overlaps.out
@@ -19,7 +19,7 @@ CREATE TABLE temporal_rng (
id INTEGER,
CONSTRAINT temporal_rng_pk PRIMARY KEY (id, valid_at WITHOUT OVERLAPS)
);
-ERROR: range or PERIOD "valid_at" in WITHOUT OVERLAPS does not exist
+ERROR: column "valid_at" named in key does not exist
-- PK with a non-range column:
CREATE TABLE temporal_rng (
id INTEGER,
@@ -27,6 +27,8 @@ CREATE TABLE temporal_rng (
CONSTRAINT temporal_rng_pk PRIMARY KEY (id, valid_at WITHOUT OVERLAPS)
);
ERROR: column "valid_at" in WITHOUT OVERLAPS is not a range type
+LINE 3: valid_at TEXT,
+ ^
-- PK with one column plus a range:
CREATE TABLE temporal_rng (
-- Since we can't depend on having btree_gist here,
@@ -110,7 +112,7 @@ CREATE TABLE temporal_rng2 (
id INTEGER,
CONSTRAINT temporal_rng2_uq UNIQUE (id, valid_at WITHOUT OVERLAPS)
);
-ERROR: range or PERIOD "valid_at" in WITHOUT OVERLAPS does not exist
+ERROR: column "valid_at" named in key does not exist
-- UNIQUE with a non-range column:
CREATE TABLE temporal_rng2 (
id INTEGER,
@@ -118,6 +120,8 @@ CREATE TABLE temporal_rng2 (
CONSTRAINT temporal_rng2_uq UNIQUE (id, valid_at WITHOUT OVERLAPS)
);
ERROR: column "valid_at" in WITHOUT OVERLAPS is not a range type
+LINE 3: valid_at TEXT,
+ ^
-- UNIQUE with one column plus a range:
CREATE TABLE temporal_rng2 (
-- Since we can't depend on having btree_gist here,
--
2.34.1
view thread (66+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected], [email protected], [email protected], [email protected]
Subject: Re: SQL:2011 application time
In-Reply-To: <CACJufxF9jJJqsVnmcDfVTMgn-hj15mJDvfByNOWZ9x0Wib3hSw@mail.gmail.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox