diff --git a/src/bin/psql/describe.cpp b/src/bin/psql/describe.cpp index 1cea973cf..ab382d77d 100644 --- a/src/bin/psql/describe.cpp +++ b/src/bin/psql/describe.cpp @@ -13,7 +13,6 @@ */ #include "settings.h" #include "postgres_fe.h" - #include #include "catalog/pg_class.h" @@ -1264,6 +1263,134 @@ static void PrintTableSliceInfo(char relKind, const char* distType, const char* return; } +uint32 GetVersionNum() +{ + PQExpBufferData query; + initPQExpBuffer(&query); + PGresult* result = NULL; + int tuples = 0; + uint32 versionNum = 0; + + appendPQExpBuffer(&query, "select pg_catalog.working_version_num()"); + result = PSQLexec(query.data, false); + tuples = PQntuples(result); + if (tuples > 0) + versionNum = atooid(PQgetvalue(result, 0, 0)); + + PQclear(result); + termPQExpBuffer(&query); + + return versionNum; +} + +bool PartkeyexprIsNull(const char* relationname, bool isSubPart) +{ + PQExpBufferData partkeyexpr; + PGresult* partkeyexpr_res = NULL; + int tuples = 0; + bool partkeyexprIsNull = true; + uint32 versionNum = GetVersionNum(); + if (versionNum < 92836) + return partkeyexprIsNull; + + initPQExpBuffer(&partkeyexpr); + if (!isSubPart) + appendPQExpBuffer(&partkeyexpr, + "select partkeyexpr from pg_partition where (parttype = 'r') and (parentid in (select oid from pg_class where relname = \'%s\'));", + relationname); + else + appendPQExpBuffer(&partkeyexpr, + "select distinct partkeyexpr from pg_partition where (parttype = 'p') and (parentid in (select oid from pg_class where relname = \'%s\'))", + relationname); + partkeyexpr_res = PSQLexec(partkeyexpr.data, false); + tuples = PQntuples(partkeyexpr_res); + char* partkeyexpr_buf = NULL; + if (tuples > 0) + partkeyexpr_buf = PQgetvalue(partkeyexpr_res, 0, 0); + if (partkeyexpr_buf && strcmp(partkeyexpr_buf, "") != 0) + partkeyexprIsNull = false; + PQclear(partkeyexpr_res); + termPQExpBuffer(&partkeyexpr); + return partkeyexprIsNull; +} + +bool GetPartkeyexprSrc(bool isSubPart, const char* schemaname, const char* relationname, PQExpBuffer result_buf) +{ + PQExpBufferData buf; + initPQExpBuffer(&buf); + PGresult* result = NULL; + int tuples = 0; + printfPQExpBuffer(&buf, "select pg_get_tabledef(\'\"%s\".\"%s\"\');", schemaname, relationname); + result = PSQLexec(buf.data, false); + tuples = PQntuples(result); + + char* tabledef = NULL; + if (tuples > 0) + tabledef = PQgetvalue(result, 0, 0); + if (!tabledef) { + PQclear(result); + termPQExpBuffer(&buf); + return false; + } + + char* start = NULL; + bool success = true; + if (isSubPart) { + start = strstr(tabledef, "SUBPARTITION BY"); + if (!start) { + psql_error("Wrong table description: %s. The result is from SQL %s.", tabledef, buf.data); + success = false; + } + start += 16; + } else { + start = strstr(tabledef, "PARTITION BY"); + if (!start) { + psql_error("Wrong table description: %s. The result is from SQL %s.", tabledef, buf.data); + success = false; + } + start += 13; + } + + if (!success) { + PQclear(result); + termPQExpBuffer(&buf); + return false; + } + + char* pos = strchr(start, '('); + if (!pos) { + psql_error("Wrong table description: %s. The result is from SQL %s.", tabledef, buf.data); + PQclear(result); + termPQExpBuffer(&buf); + return false; + } + + char* i = strchr(start, ')'); + char* j = strchr(pos + 1, '('); + size_t icount = 0; + size_t jcount = 0; + while (j && j < i) { + j++; + j = strchr(j, '('); + jcount++; + } + while (i && icount < jcount) { + i++; + i = strchr(i, ')'); + icount++; + } + if (!i || (i < pos + 1)) { + psql_error("Wrong table description: %s. The result is from SQL %s.", tabledef, buf.data); + PQclear(result); + termPQExpBuffer(&buf); + return false; + } + appendBinaryPQExpBuffer(result_buf, pos + 1, i - pos - 1); + + PQclear(result); + termPQExpBuffer(&buf); + return true; +} /* * describeOneTableDetails (for \d) @@ -2712,30 +2839,38 @@ static bool describeOneTableDetails(const char* schemaname, const char* relation else if (strcmp(partition_type, "h") == 0) printfPQExpBuffer(&tmp_part_buf, "Partition By HASH("); /* 3. Get partition key name through partition key postition and pg_attribute. */ - printfPQExpBuffer(&buf, - "SELECT attname\n" - "FROM pg_attribute\n" - "WHERE attrelid = '%s' AND attnum > 0 order by attnum", - oid); + bool partkeyexprIsNull = PartkeyexprIsNull(relationname, false); + if (!partkeyexprIsNull) { + bool success = GetPartkeyexprSrc(false, schemaname, relationname, &tmp_part_buf); + if (!success) + goto error_return; + } else { + printfPQExpBuffer(&buf, + "SELECT attname\n" + "FROM pg_attribute\n" + "WHERE attrelid = '%s' AND attnum > 0 order by attnum", + oid); - tmp_result = PSQLexec(buf.data, false); - if (tmp_result == NULL) - goto error_return; + tmp_result = PSQLexec(buf.data, false); + if (tmp_result == NULL) + goto error_return; - key_position = strtok_s(partition_key, separator_symbol, &next_key); - while (key_position != NULL) { - /* - * When there are multiple partition key, we use comma to separate them - * and show. - */ - if (!first_flag) { - appendPQExpBuffer(&tmp_part_buf, "%s", PQgetvalue(tmp_result, atoi(key_position) - 1, 0)); - first_flag = true; - } else { - appendPQExpBuffer(&tmp_part_buf, ", %s", PQgetvalue(tmp_result, atoi(key_position) - 1, 0)); + key_position = strtok_s(partition_key, separator_symbol, &next_key); + while (key_position != NULL) { + /* + * When there are multiple partition key, we use comma to separate them + * and show. + */ + if (!first_flag) { + appendPQExpBuffer(&tmp_part_buf, "%s", PQgetvalue(tmp_result, atoi(key_position) - 1, 0)); + first_flag = true; + } else { + appendPQExpBuffer(&tmp_part_buf, ", %s", PQgetvalue(tmp_result, atoi(key_position) - 1, 0)); + } + key_position = strtok_s(NULL, separator_symbol, &next_key); } - key_position = strtok_s(NULL, separator_symbol, &next_key); } + appendPQExpBuffer(&tmp_part_buf, ")"); if (strcmp(partition_type, "i") == 0) { @@ -2770,20 +2905,27 @@ static bool describeOneTableDetails(const char* schemaname, const char* relation } else { goto error_return; } - - char* subpartition_key = PQgetvalue(partresult, 0, 0); - char* next_subkey = NULL; - char* subkey_position = strtok_s(subpartition_key, separator_symbol, &next_subkey); - first_flag = false; - while (subkey_position != NULL) { - if (!first_flag) { - appendPQExpBuffer(&tmp_part_buf, "%s", PQgetvalue(tmp_result, atoi(subkey_position) - 1, 0)); - first_flag = true; - } else { - appendPQExpBuffer(&tmp_part_buf, ", %s", PQgetvalue(tmp_result, atoi(subkey_position) - 1, 0)); + bool subpartkeyexprIsNull = PartkeyexprIsNull(relationname, true); + if (!subpartkeyexprIsNull) { + bool success = GetPartkeyexprSrc(true, schemaname, relationname, &tmp_part_buf); + if (!success) + goto error_return; + } else { + char* subpartition_key = PQgetvalue(partresult, 0, 0); + char* next_subkey = NULL; + char* subkey_position = strtok_s(subpartition_key, separator_symbol, &next_subkey); + first_flag = false; + while (subkey_position != NULL) { + if (!first_flag) { + appendPQExpBuffer(&tmp_part_buf, "%s", PQgetvalue(tmp_result, atoi(subkey_position) - 1, 0)); + first_flag = true; + } else { + appendPQExpBuffer(&tmp_part_buf, ", %s", PQgetvalue(tmp_result, atoi(subkey_position) - 1, 0)); + } + subkey_position = strtok_s(NULL, separator_symbol, &next_subkey); } - subkey_position = strtok_s(NULL, separator_symbol, &next_subkey); } + appendPQExpBuffer(&tmp_part_buf, ")"); } diff --git a/src/test/regress/input/partition_expr_key.source b/src/test/regress/input/partition_expr_key.source index 5d081155a..f0f6401ea 100644 --- a/src/test/regress/input/partition_expr_key.source +++ b/src/test/regress/input/partition_expr_key.source @@ -329,6 +329,19 @@ select pg_get_tabledef('testrangesubpart'); select pg_get_tabledef('testlistsubpart'); select pg_get_tabledef('testhashsubpart'); select pg_get_tabledef('testnormalsubpart'); +\d testrangepart; +\d testlistpart; +\d testhashpart; +\d testrangesubpart; +\d testlistsubpart; +\d testhashsubpart; +\d testnormalsubpart; +create table "testrangesubPART"(a int, b int) partition by range(a-b) subpartition by range(a+b) +( + partition p0 values less than(1000)(subpartition p00 values less than(100)), + partition p1 values less than(2000)(subpartition p10 values less than(200)) +); +\d "testrangesubPART" create database part_expr_key_db1 dbcompatibility 'B'; \c part_expr_key_db1; create table t1(c1 int not null, c2 int) diff --git a/src/test/regress/output/partition_expr_key.source b/src/test/regress/output/partition_expr_key.source index 8e1bf9536..cb4c0ff02 100644 --- a/src/test/regress/output/partition_expr_key.source +++ b/src/test/regress/output/partition_expr_key.source @@ -1109,6 +1109,88 @@ select pg_get_tabledef('testnormalsubpart'); ENABLE ROW MOVEMENT; (1 row) +\d testrangepart; + Table "public.testrangepart" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition By RANGE(abs((a * 2))) +Number of partitions: 2 (View pg_partition to check each partition range.) + +\d testlistpart; + Table "public.testlistpart" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition By LIST(abs((a * 2))) +Number of partitions: 2 (View pg_partition to check each partition range.) + +\d testhashpart; + Table "public.testhashpart" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition By HASH(abs((a * 2))) +Number of partitions: 2 (View pg_partition to check each partition range.) + +\d testrangesubpart; +Table "public.testrangesubpart" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition By RANGE((a + b)) Subpartition By RANGE((a - b)) +Number of partitions: 2 (View pg_partition to check each partition range.) +Number of subpartitions: 2 (View pg_partition to check each subpartition range.) + +\d testlistsubpart; +Table "public.testlistsubpart" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition By RANGE(abs((a * 2))) Subpartition By LIST(abs((b * 2))) +Number of partitions: 2 (View pg_partition to check each partition range.) +Number of subpartitions: 2 (View pg_partition to check each subpartition range.) + +\d testhashsubpart; +Table "public.testhashsubpart" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition By RANGE(a) Subpartition By HASH(abs((b * 2))) +Number of partitions: 2 (View pg_partition to check each partition range.) +Number of subpartitions: 4 (View pg_partition to check each subpartition range.) + +\d testnormalsubpart; +Table "public.testnormalsubpart" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition By RANGE(a) Subpartition By RANGE(b) +Number of partitions: 2 (View pg_partition to check each partition range.) +Number of subpartitions: 2 (View pg_partition to check each subpartition range.) + +create table "testrangesubPART"(a int, b int) partition by range(a-b) subpartition by range(a+b) +( + partition p0 values less than(1000)(subpartition p00 values less than(100)), + partition p1 values less than(2000)(subpartition p10 values less than(200)) +); +\d "testrangesubPART" +Table "public.testrangesubPART" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition By RANGE((a - b)) Subpartition By RANGE((a + b)) +Number of partitions: 2 (View pg_partition to check each partition range.) +Number of subpartitions: 2 (View pg_partition to check each subpartition range.) + create database part_expr_key_db1 dbcompatibility 'B'; \c part_expr_key_db1; create table t1(c1 int not null, c2 int)