mirror of
https://github.com/timescale/timescaledb.git
synced 2025-05-16 18:43:18 +08:00
Improve error message when chunk not found
When a chunk is not found, we print a generic error message that does not hint at what we are looking for, which means that it is very hard to locate the problem. This commit adds details to the error message printing out values used for the scan key when searching for the chunk. Related-To: #2344 Related-To: #3400 Related-To: #153
This commit is contained in:
parent
46a584e6b5
commit
5b16ac097e
90
src/chunk.c
90
src/chunk.c
@ -72,6 +72,13 @@ TS_FUNCTION_INFO_V1(ts_chunk_id_from_relid);
|
||||
TS_FUNCTION_INFO_V1(ts_chunk_show);
|
||||
TS_FUNCTION_INFO_V1(ts_chunk_create);
|
||||
|
||||
static const char *
|
||||
DatumGetNameString(Datum datum)
|
||||
{
|
||||
Name name = DatumGetName(datum);
|
||||
return pstrdup(NameStr(*name));
|
||||
}
|
||||
|
||||
/* Used when processing scanned chunks */
|
||||
typedef enum ChunkResult
|
||||
{
|
||||
@ -2454,7 +2461,7 @@ ts_chunk_get_window(int32 dimension_id, int64 point, int count, MemoryContext mc
|
||||
|
||||
static Chunk *
|
||||
chunk_scan_find(int indexid, ScanKeyData scankey[], int nkeys, MemoryContext mctx,
|
||||
bool fail_if_not_found)
|
||||
bool fail_if_not_found, const DisplayKeyData displaykey[])
|
||||
{
|
||||
ChunkStubScanCtx stubctx = { 0 };
|
||||
Chunk *chunk;
|
||||
@ -2478,7 +2485,23 @@ chunk_scan_find(int indexid, ScanKeyData scankey[], int nkeys, MemoryContext mct
|
||||
{
|
||||
case 0:
|
||||
if (fail_if_not_found)
|
||||
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("chunk not found")));
|
||||
{
|
||||
int i = 0;
|
||||
StringInfo info = makeStringInfo();
|
||||
while (i < nkeys)
|
||||
{
|
||||
appendStringInfo(info,
|
||||
"%s: %s",
|
||||
displaykey[i].name,
|
||||
displaykey[i].as_string(scankey[i].sk_argument));
|
||||
if (++i < nkeys)
|
||||
appendStringInfoString(info, ", ");
|
||||
}
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("chunk not found"),
|
||||
errdetail("%s", info->data)));
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
ASSERT_IS_VALID_CHUNK(chunk);
|
||||
@ -2496,12 +2519,21 @@ ts_chunk_get_by_name_with_memory_context(const char *schema_name, const char *ta
|
||||
{
|
||||
NameData schema, table;
|
||||
ScanKeyData scankey[2];
|
||||
static const DisplayKeyData displaykey[2] = {
|
||||
[0] = { .name = "schema_name", .as_string = DatumGetNameString },
|
||||
[1] = { .name = "table_name", .as_string = DatumGetNameString },
|
||||
};
|
||||
|
||||
/* Early check for rogue input */
|
||||
if (schema_name == NULL || table_name == NULL)
|
||||
{
|
||||
if (fail_if_not_found)
|
||||
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("chunk not found")));
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("chunk not found"),
|
||||
errdetail("schema_name: %s, table_name: %s",
|
||||
schema_name ? schema_name : "<null>",
|
||||
table_name ? table_name : "<null>")));
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
@ -2523,7 +2555,12 @@ ts_chunk_get_by_name_with_memory_context(const char *schema_name, const char *ta
|
||||
F_NAMEEQ,
|
||||
NameGetDatum(&table));
|
||||
|
||||
return chunk_scan_find(CHUNK_SCHEMA_NAME_INDEX, scankey, 2, mctx, fail_if_not_found);
|
||||
return chunk_scan_find(CHUNK_SCHEMA_NAME_INDEX,
|
||||
scankey,
|
||||
2,
|
||||
mctx,
|
||||
fail_if_not_found,
|
||||
displaykey);
|
||||
}
|
||||
|
||||
Chunk *
|
||||
@ -2545,17 +2582,33 @@ ts_chunk_get_by_relid(Oid relid, bool fail_if_not_found)
|
||||
return chunk_get_by_name(schema, table, fail_if_not_found);
|
||||
}
|
||||
|
||||
static const char *
|
||||
DatumGetInt32AsString(Datum datum)
|
||||
{
|
||||
char *buf = (char *) palloc(12); /* sign, 10 digits, '\0' */
|
||||
pg_ltoa(DatumGetInt32(datum), buf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
Chunk *
|
||||
ts_chunk_get_by_id(int32 id, bool fail_if_not_found)
|
||||
{
|
||||
ScanKeyData scankey[1];
|
||||
static const DisplayKeyData displaykey[1] = {
|
||||
[0] = { .name = "id", .as_string = DatumGetInt32AsString },
|
||||
};
|
||||
|
||||
/*
|
||||
* Perform an index scan on chunk id.
|
||||
*/
|
||||
ScanKeyInit(&scankey[0], Anum_chunk_idx_id, BTEqualStrategyNumber, F_INT4EQ, Int32GetDatum(id));
|
||||
|
||||
return chunk_scan_find(CHUNK_ID_INDEX, scankey, 1, CurrentMemoryContext, fail_if_not_found);
|
||||
return chunk_scan_find(CHUNK_ID_INDEX,
|
||||
scankey,
|
||||
1,
|
||||
CurrentMemoryContext,
|
||||
fail_if_not_found,
|
||||
displaykey);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2595,7 +2648,8 @@ ts_chunk_num_of_chunks_created_after(const Chunk *chunk)
|
||||
* that, e.g., translates a chunk relid to a chunk_id, or vice versa.
|
||||
*/
|
||||
static bool
|
||||
chunk_simple_scan(ScanIterator *iterator, FormData_chunk *form, bool missing_ok)
|
||||
chunk_simple_scan(ScanIterator *iterator, FormData_chunk *form, bool missing_ok,
|
||||
const DisplayKeyData displaykey[])
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
@ -2611,7 +2665,20 @@ chunk_simple_scan(ScanIterator *iterator, FormData_chunk *form, bool missing_ok)
|
||||
Assert(count == 0 || count == 1);
|
||||
|
||||
if (count == 0 && !missing_ok)
|
||||
{
|
||||
int i = 0;
|
||||
StringInfo info = makeStringInfo();
|
||||
while (i < iterator->ctx.nkeys)
|
||||
{
|
||||
appendStringInfo(info,
|
||||
"%s: %s",
|
||||
displaykey[i].name,
|
||||
displaykey[i].as_string(iterator->ctx.scankey[i].sk_argument));
|
||||
if (++i < iterator->ctx.nkeys)
|
||||
appendStringInfoString(info, ", ");
|
||||
}
|
||||
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("chunk not found")));
|
||||
}
|
||||
|
||||
return count == 1;
|
||||
}
|
||||
@ -2621,6 +2688,10 @@ chunk_simple_scan_by_name(const char *schema, const char *table, FormData_chunk
|
||||
bool missing_ok)
|
||||
{
|
||||
ScanIterator iterator;
|
||||
static const DisplayKeyData displaykey[] = {
|
||||
[0] = { .name = "schema_name", .as_string = DatumGetNameString },
|
||||
[1] = { .name = "table_name", .as_string = DatumGetNameString },
|
||||
};
|
||||
|
||||
if (schema == NULL || table == NULL)
|
||||
return false;
|
||||
@ -2628,7 +2699,7 @@ chunk_simple_scan_by_name(const char *schema, const char *table, FormData_chunk
|
||||
iterator = ts_scan_iterator_create(CHUNK, AccessShareLock, CurrentMemoryContext);
|
||||
init_scan_by_qualified_table_name(&iterator, schema, table);
|
||||
|
||||
return chunk_simple_scan(&iterator, form, missing_ok);
|
||||
return chunk_simple_scan(&iterator, form, missing_ok, displaykey);
|
||||
}
|
||||
|
||||
static bool
|
||||
@ -2661,11 +2732,14 @@ static bool
|
||||
chunk_simple_scan_by_id(int32 chunk_id, FormData_chunk *form, bool missing_ok)
|
||||
{
|
||||
ScanIterator iterator;
|
||||
static const DisplayKeyData displaykey[] = {
|
||||
[0] = { .name = "id", .as_string = DatumGetInt32AsString },
|
||||
};
|
||||
|
||||
iterator = ts_scan_iterator_create(CHUNK, AccessShareLock, CurrentMemoryContext);
|
||||
init_scan_by_chunk_id(&iterator, chunk_id);
|
||||
|
||||
return chunk_simple_scan(&iterator, form, missing_ok);
|
||||
return chunk_simple_scan(&iterator, form, missing_ok, displaykey);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -118,6 +118,15 @@ typedef struct ChunkScanEntry
|
||||
ChunkStub *stub;
|
||||
} ChunkScanEntry;
|
||||
|
||||
/*
|
||||
* Information to be able to display a scan key details for error messages.
|
||||
*/
|
||||
typedef struct DisplayKeyData
|
||||
{
|
||||
const char *name;
|
||||
const char *(*as_string)(Datum);
|
||||
} DisplayKeyData;
|
||||
|
||||
extern Chunk *ts_chunk_create_from_point(const Hypertable *ht, const Point *p, const char *schema,
|
||||
const char *prefix);
|
||||
|
||||
|
@ -856,3 +856,27 @@ NOTICE: chunk "_hyper_13_20_chunk" is already compressed
|
||||
-> Seq Scan on compress_hyper_14_23_chunk
|
||||
(10 rows)
|
||||
|
||||
-- Create a hypertable and add a rogue inherited table to it.
|
||||
CREATE TABLE i165 (time timestamptz PRIMARY KEY);
|
||||
SELECT create_hypertable('i165','time');
|
||||
create_hypertable
|
||||
--------------------
|
||||
(15,public,i165,t)
|
||||
(1 row)
|
||||
|
||||
ALTER TABLE i165 SET (timescaledb.compress);
|
||||
SELECT compress_chunk(show_chunks('i165'));
|
||||
compress_chunk
|
||||
----------------
|
||||
(0 rows)
|
||||
|
||||
CREATE TABLE extras (more_magic bool) INHERITS (i165);
|
||||
INSERT INTO i165 (time) VALUES
|
||||
(generate_series(TIMESTAMP '2019-08-01', TIMESTAMP '2019-08-10', INTERVAL '10 minutes'));
|
||||
\set ON_ERROR_STOP 0
|
||||
\set VERBOSITY default
|
||||
SELECT * FROM i165;
|
||||
ERROR: chunk not found
|
||||
DETAIL: schema_name: public, table_name: extras
|
||||
\set VERBOSITY terse
|
||||
\set ON_ERROR_STOP 1
|
||||
|
@ -856,3 +856,27 @@ NOTICE: chunk "_hyper_13_20_chunk" is already compressed
|
||||
-> Seq Scan on compress_hyper_14_23_chunk
|
||||
(10 rows)
|
||||
|
||||
-- Create a hypertable and add a rogue inherited table to it.
|
||||
CREATE TABLE i165 (time timestamptz PRIMARY KEY);
|
||||
SELECT create_hypertable('i165','time');
|
||||
create_hypertable
|
||||
--------------------
|
||||
(15,public,i165,t)
|
||||
(1 row)
|
||||
|
||||
ALTER TABLE i165 SET (timescaledb.compress);
|
||||
SELECT compress_chunk(show_chunks('i165'));
|
||||
compress_chunk
|
||||
----------------
|
||||
(0 rows)
|
||||
|
||||
CREATE TABLE extras (more_magic bool) INHERITS (i165);
|
||||
INSERT INTO i165 (time) VALUES
|
||||
(generate_series(TIMESTAMP '2019-08-01', TIMESTAMP '2019-08-10', INTERVAL '10 minutes'));
|
||||
\set ON_ERROR_STOP 0
|
||||
\set VERBOSITY default
|
||||
SELECT * FROM i165;
|
||||
ERROR: chunk not found
|
||||
DETAIL: schema_name: public, table_name: extras
|
||||
\set VERBOSITY terse
|
||||
\set ON_ERROR_STOP 1
|
||||
|
@ -550,3 +550,17 @@ SELECT compress_chunk(format('%I.%I',chunk_schema,chunk_name), true) FROM timesc
|
||||
|
||||
-- should be ordered append
|
||||
:PREFIX SELECT * FROM test_ordering ORDER BY 1;
|
||||
|
||||
-- Create a hypertable and add a rogue inherited table to it.
|
||||
CREATE TABLE i165 (time timestamptz PRIMARY KEY);
|
||||
SELECT create_hypertable('i165','time');
|
||||
ALTER TABLE i165 SET (timescaledb.compress);
|
||||
SELECT compress_chunk(show_chunks('i165'));
|
||||
CREATE TABLE extras (more_magic bool) INHERITS (i165);
|
||||
INSERT INTO i165 (time) VALUES
|
||||
(generate_series(TIMESTAMP '2019-08-01', TIMESTAMP '2019-08-10', INTERVAL '10 minutes'));
|
||||
\set ON_ERROR_STOP 0
|
||||
\set VERBOSITY default
|
||||
SELECT * FROM i165;
|
||||
\set VERBOSITY terse
|
||||
\set ON_ERROR_STOP 1
|
||||
|
Loading…
x
Reference in New Issue
Block a user