!2902 【bugfixed】解决net、range、hash等类型ignore_error不生效的问题

Merge pull request !2902 from laishenghao/ignore
This commit is contained in:
opengauss-bot
2023-02-17 02:47:59 +00:00
committed by Gitee
6 changed files with 397 additions and 35 deletions

View File

@ -34,6 +34,8 @@ Datum macaddr_in(PG_FUNCTION_ARGS)
char junk[JUNK_LEN];
int count;
int level = fcinfo->can_ignore ? WARNING : ERROR;
/* %1s matches iff there is trailing non-whitespace garbage */
count = sscanf_s(str, "%x:%x:%x:%x:%x:%x%1s", &a, &b, &c, &d, &e, &f, junk, JUNK_LEN);
@ -47,15 +49,23 @@ Datum macaddr_in(PG_FUNCTION_ARGS)
count = sscanf_s(str, "%2x%2x.%2x%2x.%2x%2x%1s", &a, &b, &c, &d, &e, &f, junk, JUNK_LEN);
if (count != EXPECT_PARA_NUMS)
count = sscanf_s(str, "%2x%2x%2x%2x%2x%2x%1s", &a, &b, &c, &d, &e, &f, junk, JUNK_LEN);
if (count != EXPECT_PARA_NUMS)
ereport(ERROR,
if (count != EXPECT_PARA_NUMS) {
ereport(level,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type macaddr: \"%s\"", str)));
/* ignore error case: reset to base value */
a = b = c = d = e = f = 0;
}
if ((a > 255) || (b > 255) || (c > 255) || (d > 255) || (e > 255) || (f > 255))
ereport(ERROR,
if ((a > 255) || (b > 255) || (c > 255) || (d > 255) || (e > 255) || (f > 255)) {
ereport(level,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("invalid octet value in \"macaddr\" value: \"%s\"", str)));
/* ignore error case: reset to base value */
a = b = c = d = e = f = 0;
}
result = (macaddr*)palloc(sizeof(macaddr));

View File

@ -70,12 +70,12 @@ static int ip_addrsize(inet* inetptr)
/*
* Common INET/CIDR input routine
*/
static inet* network_in(char* src, bool is_cidr)
static inet* network_in(char* src, bool is_cidr, bool can_ignore = false)
{
int bits;
inet* dst = NULL;
dst = (inet*)palloc0(sizeof(inet));
inet* dst = (inet*)palloc0(sizeof(inet));;
bool should_reset_base = false;
int level = can_ignore ? WARNING : ERROR;
/*
* First, check to see if this is an IPv6 or IPv4 address. IPv6 addresses
@ -89,21 +89,34 @@ static inet* network_in(char* src, bool is_cidr)
ip_family(dst) = PGSQL_AF_INET;
bits = inet_net_pton(ip_family(dst), src, ip_addr(dst), is_cidr ? ip_addrsize(dst) : -1);
if ((bits < 0) || (bits > ip_maxbits(dst)))
ereport(ERROR,
if ((bits < 0) || (bits > ip_maxbits(dst))) {
ereport(level,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
/* translator: first %s is inet or cidr */
errmsg("invalid input syntax for type %s: \"%s\"", is_cidr ? "cidr" : "inet", src)));
should_reset_base = true;
}
/*
* Error check: CIDR values must not have any bits set beyond the masklen.
*/
if (is_cidr) {
if (!addressOK(ip_addr(dst), bits, ip_family(dst)))
ereport(ERROR,
if (is_cidr && !should_reset_base) {
if (!addressOK(ip_addr(dst), bits, ip_family(dst))) {
ereport(level,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid cidr value: \"%s\"", src),
errdetail("Value has bits set to right of mask.")));
should_reset_base = true;
}
}
if (should_reset_base) {
ip_family(dst) = PGSQL_AF_INET;
src = "0.0.0.0";
bits = inet_net_pton(ip_family(dst), src, ip_addr(dst), is_cidr ? ip_addrsize(dst) : -1);
}
ip_bits(dst) = bits;
@ -116,14 +129,14 @@ Datum inet_in(PG_FUNCTION_ARGS)
{
char* src = PG_GETARG_CSTRING(0);
PG_RETURN_INET_P(network_in(src, false));
PG_RETURN_INET_P(network_in(src, false, fcinfo->can_ignore));
}
Datum cidr_in(PG_FUNCTION_ARGS)
{
char* src = PG_GETARG_CSTRING(0);
PG_RETURN_INET_P(network_in(src, true));
PG_RETURN_INET_P(network_in(src, true, fcinfo->can_ignore));
}
/*

View File

@ -34,6 +34,7 @@
#include "access/hash.h"
#include "lib/stringinfo.h"
#include "libpq/pqformat.h"
#include "parser/parse_type.h"
#include "utils/builtins.h"
#include "utils/date.h"
#include "utils/int8.h"
@ -53,7 +54,8 @@ typedef struct RangeIOData {
static RangeIOData* get_range_io_data(FunctionCallInfo fcinfo, Oid rngtypid, IOFuncSelector func);
static char range_parse_flags(const char* flags_str);
static void range_parse(const char* input_str, char* flags, char** lbound_str, char** ubound_str);
static void range_parse(const char* input_str, bool can_ignore, char* flags, char** lbound_str,
char** ubound_str, bool* should_reset_base);
static const char* range_parse_bound(const char* string, const char* ptr, char** bound_str, bool* infinite);
static char* range_deparse(char flags, const char* lbound_str, const char* ubound_str);
static char* range_bound_escape(const char* value);
@ -62,6 +64,8 @@ static Pointer datum_write(Pointer ptr, Datum datum, bool typbyval, char typalig
void CheckRangeTypeMatch(RangeType* r1, RangeType* r2);
static Datum GetRangeTypeBaseValue(Oid rangeoid, Oid typmod, char* string);
/*
* ----------------------------------------------------------
* I/O FUNCTIONS
@ -80,11 +84,16 @@ Datum range_in(PG_FUNCTION_ARGS)
char* ubound_str = NULL;
RangeBound lower;
RangeBound upper;
bool should_reset_base = false;
cache = get_range_io_data(fcinfo, rngtypoid, IOFunc_input);
/* parse */
range_parse(input_str, &flags, &lbound_str, &ubound_str);
range_parse(input_str, fcinfo->can_ignore, &flags, &lbound_str, &ubound_str, &should_reset_base);
if (should_reset_base) {
return GetRangeTypeBaseValue(rngtypoid, typmod, input_str);
}
/* call element type's input function */
if (RANGE_HAS_LBOUND((unsigned char)flags))
@ -1740,10 +1749,12 @@ static char range_parse_flags(const char* flags_str)
*
* Input parameters:
* string: input string to be parsed
* can_ignore: whether ignore errors
* Output parameters:
* *flags: receives flags bitmask
* *lbound_str: receives palloc'd lower bound string, or NULL if none
* *ubound_str: receives palloc'd upper bound string, or NULL if none
* *should_reset_base: whether have to reset to base value by type
*
* This is modeled somewhat after record_in in rowtypes.c.
* The input syntax is:
@ -1763,10 +1774,12 @@ static char range_parse_flags(const char* flags_str)
* brackets) can be enclosed in double-quotes or escaped with backslash. Within
* double-quotes, a double-quote can be escaped with double-quote or backslash.
*/
static void range_parse(const char* string, char* flags, char** lbound_str, char** ubound_str)
static void range_parse(const char* string, bool can_ignore, char* flags, char** lbound_str,
char** ubound_str, bool* should_reset_base)
{
const char* ptr = string;
bool infinite = false;
int level = can_ignore ? WARNING : ERROR;
*flags = 0;
@ -1787,11 +1800,14 @@ static void range_parse(const char* string, char* flags, char** lbound_str, char
ptr++;
/* should have consumed everything */
if (*ptr != '\0')
ereport(ERROR,
if (*ptr != '\0') {
ereport(level,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed range literal: \"%s\"", string),
errdetail("Junk after \"empty\" keyword.")));
*should_reset_base = true;
return;
}
return;
}
@ -1799,25 +1815,33 @@ static void range_parse(const char* string, char* flags, char** lbound_str, char
if (*ptr == '[') {
*flags = (unsigned char)(*flags) | RANGE_LB_INC;
ptr++;
} else if (*ptr == '(')
} else if (*ptr == '(') {
ptr++;
else
ereport(ERROR,
} else {
ereport(level,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed range literal: \"%s\"", string),
errdetail("Missing left parenthesis or bracket.")));
*should_reset_base = true;
return;
}
ptr = range_parse_bound(string, ptr, lbound_str, &infinite);
if (infinite)
*flags = (unsigned char)(*flags) | RANGE_LB_INF;
if (*ptr == ',')
if (*ptr == ',') {
ptr++;
else
ereport(ERROR,
} else {
ereport(level,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed range literal: \"%s\"", string),
errdetail("Missing comma after lower bound.")));
*should_reset_base = true;
return;
}
ptr = range_parse_bound(string, ptr, ubound_str, &infinite);
if (infinite)
@ -1826,23 +1850,66 @@ static void range_parse(const char* string, char* flags, char** lbound_str, char
if (*ptr == ']') {
*flags = (unsigned int)*flags | RANGE_UB_INC;
ptr++;
} else if (*ptr == ')')
} else if (*ptr == ')') {
ptr++;
else /* must be a comma */
ereport(ERROR,
} else { /* must be a comma */
ereport(level,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed range literal: \"%s\"", string),
errdetail("Too many commas.")));
*should_reset_base = true;
return;
}
/* consume whitespace */
while (*ptr != '\0' && isspace((unsigned char)*ptr))
ptr++;
if (*ptr != '\0')
ereport(ERROR,
if (*ptr != '\0') {
ereport(level,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed range literal: \"%s\"", string),
errdetail("Junk after right parenthesis or bracket.")));
*should_reset_base = true;
return;
}
}
static Datum GetRangeTypeBaseValue(Oid rangeoid, Oid typmod, char* string)
{
Datum datum;
switch (rangeoid) {
case NUMRANGEOID:
case INT8RANGEOID:
case INT4RANGEOID: {
Type targetType = typeidType(rangeoid);
datum = stringTypeDatum(targetType, "(0,0)", typmod, true);
ReleaseSysCache(targetType);
break;
}
case TSRANGEOID:
case TSTZRANGEOID: {
Type targetType = typeidType(rangeoid);
datum = stringTypeDatum(targetType, "(1970-01-01 00:00:00,1970-01-01 00:00:00)", typmod, true);
ReleaseSysCache(targetType);
break;
}
case DATERANGEOID: {
Type targetType = typeidType(rangeoid);
datum = stringTypeDatum(targetType, "(1970-01-01,1970-01-01)", typmod, true);
ReleaseSysCache(targetType);
break;
}
default: {
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed range literal: \"%s\"", string),
errdetail("the feature \"ignore_error\" not supports \"%u\" type yet.", rangeoid)));
break;
}
}
return datum;
}
/*

View File

@ -275,13 +275,16 @@ Datum hash16out(PG_FUNCTION_ARGS)
PG_RETURN_CSTRING(res.data);
}
static void string_to_hash32(const char* source, hash32_t* hash32)
static void string_to_hash32(char* source, hash32_t* hash32, bool can_ignore)
{
const char* src = source;
char* src = source;
int len = strlen(src);
int level = can_ignore ? WARNING : ERROR;
if (len != HASH32_LEN * 2) {
ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
ereport(level, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for hash32: \"%s\"", source)));
src = "00000000000000000000000000000000";
}
for (int i = 0; i < HASH32_LEN; ++i) {
@ -295,7 +298,7 @@ Datum hash32in(PG_FUNCTION_ARGS)
hash32_t *hash32 = NULL;
hash32 = (hash32_t*)palloc(sizeof(hash32_t));
string_to_hash32(hash32_str, hash32);
string_to_hash32(hash32_str, hash32, fcinfo->can_ignore);
PG_RETURN_HASH32_P(hash32);
}

View File

@ -983,6 +983,211 @@ select * from t_bit;
0
(2 rows)
-- test net types
reset sql_ignore_strategy;
show sql_ignore_strategy;
sql_ignore_strategy
---------------------
ignore_null
(1 row)
create table net(
c1 cidr not null,
c2 inet not null,
c3 macaddr not null
);
insert /*+ ignore_error */ into net values('', '', '');
WARNING: invalid input syntax for type cidr: ""
LINE 1: insert /*+ ignore_error */ into net values('', '', '');
^
CONTEXT: referenced column: c1
WARNING: invalid input syntax for type inet: ""
LINE 1: insert /*+ ignore_error */ into net values('', '', '');
^
CONTEXT: referenced column: c2
WARNING: invalid input syntax for type macaddr: ""
LINE 1: insert /*+ ignore_error */ into net values('', '', '');
^
CONTEXT: referenced column: c3
insert /*+ ignore_error */ into net values(null, null, null);
WARNING: null value in column "c1" violates not-null constraint
DETAIL: Failing row contains (null, null, null).
insert /*+ ignore_error */ into net values(1::int, 1.1::float, 'sdfdf'::time);
WARNING: invalid input syntax for type time: "sdfdf"
LINE 1: ...nore_error */ into net values(1::int, 1.1::float, 'sdfdf'::t...
^
WARNING: column "c1" is of type cidr but expression is of type integer. Data truncated automatically.
CONTEXT: referenced column: c1
WARNING: column "c2" is of type inet but expression is of type double precision. Data truncated automatically.
CONTEXT: referenced column: c2
WARNING: column "c3" is of type macaddr but expression is of type time without time zone. Data truncated automatically.
CONTEXT: referenced column: c3
set sql_ignore_strategy='overwrite_null';
insert /*+ ignore_error */ into net values(null, null, null);
WARNING: null value in column "c1" violates not-null constraint
DETAIL: Failing row contains (null, null, null).
insert /*+ ignore_error */ into net values(1::int, 1.1::float, 'sdfdf'::time);
WARNING: invalid input syntax for type time: "sdfdf"
LINE 1: ...nore_error */ into net values(1::int, 1.1::float, 'sdfdf'::t...
^
WARNING: column "c1" is of type cidr but expression is of type integer. Data truncated automatically.
CONTEXT: referenced column: c1
WARNING: column "c2" is of type inet but expression is of type double precision. Data truncated automatically.
CONTEXT: referenced column: c2
WARNING: column "c3" is of type macaddr but expression is of type time without time zone. Data truncated automatically.
CONTEXT: referenced column: c3
select * from net;
c1 | c2 | c3
------------+---------+-------------------
0.0.0.0/32 | 0.0.0.0 | 00:00:00:00:00:00
0.0.0.0/32 | 0.0.0.0 | 00:00:00:00:00:00
0.0.0.0/32 | 0.0.0.0 | 00:00:00:00:00:00
0.0.0.0/32 | 0.0.0.0 | 00:00:00:00:00:00
(4 rows)
reset sql_ignore_strategy;
-- test range types
create table ran(
c1 numrange not null,
c2 int8range not null,
c3 int4range not null,
c4 tsrange not null,
c5 tstzrange not null,
c6 daterange not null
);
insert /*+ ignore_error */ into ran values('', '', '', '', '', '');
WARNING: malformed range literal: ""
LINE 1: insert /*+ ignore_error */ into ran values('', '', '', '', '...
^
DETAIL: Missing left parenthesis or bracket.
CONTEXT: referenced column: c1
WARNING: malformed range literal: ""
LINE 1: insert /*+ ignore_error */ into ran values('', '', '', '', '...
^
DETAIL: Missing left parenthesis or bracket.
CONTEXT: referenced column: c2
WARNING: malformed range literal: ""
LINE 1: ...nsert /*+ ignore_error */ into ran values('', '', '', '', ''...
^
DETAIL: Missing left parenthesis or bracket.
CONTEXT: referenced column: c3
WARNING: malformed range literal: ""
LINE 1: ...t /*+ ignore_error */ into ran values('', '', '', '', '', ''...
^
DETAIL: Missing left parenthesis or bracket.
CONTEXT: referenced column: c4
WARNING: malformed range literal: ""
LINE 1: .../*+ ignore_error */ into ran values('', '', '', '', '', '');
^
DETAIL: Missing left parenthesis or bracket.
CONTEXT: referenced column: c5
WARNING: malformed range literal: ""
LINE 1: .../*+ ignore_error */ into ran values('', '', '', '', '', '');
^
DETAIL: Missing left parenthesis or bracket.
CONTEXT: referenced column: c6
insert /*+ ignore_error */ into ran values(null, null, null, null, null, null);
WARNING: null value in column "c1" violates not-null constraint
DETAIL: Failing row contains (null, null, null, null, null, null).
set sql_ignore_strategy='overwrite_null';
insert /*+ ignore_error */ into ran values(null, null, null, null, null, null);
WARNING: null value in column "c1" violates not-null constraint
DETAIL: Failing row contains (null, null, null, null, null, null).
select * from ran;
c1 | c2 | c3 | c4 | c5 | c6
-------+-------+-------+-------+-------+-------
empty | empty | empty | empty | empty | empty
empty | empty | empty | empty | empty | empty
(2 rows)
reset sql_ignore_strategy;
-- test hash & tsvector types
create table hashvec(
c1 hash16 not null,
c2 hash32 not null,
c3 tsvector not null
);
insert /*+ ignore_error */ into hashvec values('', '', '');
WARNING: invalid input syntax for hash32: ""
LINE 1: insert /*+ ignore_error */ into hashvec values('', '', '');
^
CONTEXT: referenced column: c2
insert /*+ ignore_error */ into hashvec values(null, null, null);
WARNING: null value in column "c1" violates not-null constraint
DETAIL: Failing row contains (null, null, null).
insert /*+ ignore_error */ into hashvec values(1::int, 1.1::float, 'sdfdf'::time);
WARNING: invalid input syntax for type time: "sdfdf"
LINE 1: ..._error */ into hashvec values(1::int, 1.1::float, 'sdfdf'::t...
^
WARNING: column "c1" is of type hash16 but expression is of type integer. Data truncated automatically.
CONTEXT: referenced column: c1
WARNING: column "c2" is of type hash32 but expression is of type double precision. Data truncated automatically.
CONTEXT: referenced column: c2
WARNING: column "c3" is of type tsvector but expression is of type time without time zone. Data truncated automatically.
CONTEXT: referenced column: c3
set sql_ignore_strategy='overwrite_null';
insert /*+ ignore_error */ into hashvec values(null, null, null);
WARNING: null value in column "c1" violates not-null constraint
DETAIL: Failing row contains (null, null, null).
insert /*+ ignore_error */ into hashvec values(1::int, 1.1::float, 'sdfdf'::time);
WARNING: invalid input syntax for type time: "sdfdf"
LINE 1: ..._error */ into hashvec values(1::int, 1.1::float, 'sdfdf'::t...
^
WARNING: column "c1" is of type hash16 but expression is of type integer. Data truncated automatically.
CONTEXT: referenced column: c1
WARNING: column "c2" is of type hash32 but expression is of type double precision. Data truncated automatically.
CONTEXT: referenced column: c2
WARNING: column "c3" is of type tsvector but expression is of type time without time zone. Data truncated automatically.
CONTEXT: referenced column: c3
select * from hashvec;
c1 | c2 | c3
------------------+----------------------------------+----
0000000000000000 | 00000000000000000000000000000000 |
0000000000000000 | 00000000000000000000000000000000 |
0000000000000000 | 00000000000000000000000000000000 |
0000000000000000 | 00000000000000000000000000000000 |
(4 rows)
reset sql_ignore_strategy;
-- test hash & tsvector types
create table varbit(
c1 BIT VARYING(5) not null
);
insert /*+ ignore_error */ into varbit values('');
insert /*+ ignore_error */ into varbit values(null);
WARNING: null value in column "c1" violates not-null constraint
DETAIL: Failing row contains (null).
insert /*+ ignore_error */ into varbit values('sdfdf'::time);
WARNING: invalid input syntax for type time: "sdfdf"
LINE 1: insert /*+ ignore_error */ into varbit values('sdfdf'::time)...
^
WARNING: column "c1" is of type bit varying but expression is of type time without time zone. Data truncated automatically.
CONTEXT: referenced column: c1
set sql_ignore_strategy='overwrite_null';
insert /*+ ignore_error */ into varbit values(null);
WARNING: null value in column "c1" violates not-null constraint
DETAIL: Failing row contains (null).
insert /*+ ignore_error */ into varbit values(1::int);
WARNING: column "c1" is of type bit varying but expression is of type integer. Data truncated automatically.
CONTEXT: referenced column: c1
insert /*+ ignore_error */ into varbit values('sdfdf'::time);
WARNING: invalid input syntax for type time: "sdfdf"
LINE 1: insert /*+ ignore_error */ into varbit values('sdfdf'::time)...
^
WARNING: column "c1" is of type bit varying but expression is of type time without time zone. Data truncated automatically.
CONTEXT: referenced column: c1
select * from varbit;
c1
----
(5 rows)
reset sql_ignore_strategy;
drop table net, ran, hashvec, varbit;
-- restore context
reset timezone;
show timezone;

View File

@ -253,6 +253,70 @@ select * from t_bit;
update /*+ ignore_error */ t_bit set c = '12a34';
select * from t_bit;
-- test net types
reset sql_ignore_strategy;
show sql_ignore_strategy;
create table net(
c1 cidr not null,
c2 inet not null,
c3 macaddr not null
);
insert /*+ ignore_error */ into net values('', '', '');
insert /*+ ignore_error */ into net values(null, null, null);
insert /*+ ignore_error */ into net values(1::int, 1.1::float, 'sdfdf'::time);
set sql_ignore_strategy='overwrite_null';
insert /*+ ignore_error */ into net values(null, null, null);
insert /*+ ignore_error */ into net values(1::int, 1.1::float, 'sdfdf'::time);
select * from net;
reset sql_ignore_strategy;
-- test range types
create table ran(
c1 numrange not null,
c2 int8range not null,
c3 int4range not null,
c4 tsrange not null,
c5 tstzrange not null,
c6 daterange not null
);
insert /*+ ignore_error */ into ran values('', '', '', '', '', '');
insert /*+ ignore_error */ into ran values(null, null, null, null, null, null);
set sql_ignore_strategy='overwrite_null';
insert /*+ ignore_error */ into ran values(null, null, null, null, null, null);
select * from ran;
reset sql_ignore_strategy;
-- test hash & tsvector types
create table hashvec(
c1 hash16 not null,
c2 hash32 not null,
c3 tsvector not null
);
insert /*+ ignore_error */ into hashvec values('', '', '');
insert /*+ ignore_error */ into hashvec values(null, null, null);
insert /*+ ignore_error */ into hashvec values(1::int, 1.1::float, 'sdfdf'::time);
set sql_ignore_strategy='overwrite_null';
insert /*+ ignore_error */ into hashvec values(null, null, null);
insert /*+ ignore_error */ into hashvec values(1::int, 1.1::float, 'sdfdf'::time);
select * from hashvec;
reset sql_ignore_strategy;
-- test hash & tsvector types
create table varbit(
c1 BIT VARYING(5) not null
);
insert /*+ ignore_error */ into varbit values('');
insert /*+ ignore_error */ into varbit values(null);
insert /*+ ignore_error */ into varbit values('sdfdf'::time);
set sql_ignore_strategy='overwrite_null';
insert /*+ ignore_error */ into varbit values(null);
insert /*+ ignore_error */ into varbit values(1::int);
insert /*+ ignore_error */ into varbit values('sdfdf'::time);
select * from varbit;
reset sql_ignore_strategy;
drop table net, ran, hashvec, varbit;
-- restore context
reset timezone;
show timezone;