Skip to content

Commit 3f15bfc

Browse files
committed
[yugabyte#2272] YSQL: Creating system views during YSQL cluster upgrade
Summary: Support `CREATE VIEW` during YSQL upgrade imitating the behaviour of `yb_system_views.sql` processing by initdb. This has two key differences with `CREATE TABLE`: * View is defined through a rewrite rule (entry in `pg_rewrite`), which has its own OID allocated in [10000, 16384) range (from `FirstBootstrapObjectId` until `FirstNormalObjectId`). To specify it, new reloption `rewrite_rule_oid` was introduced. * Since it's an SQL script and not BKI definition, reloptions can now be specified and persisted. As such, temporary helper options `table_oid`, `row_type_oid` and `rewrite_rule_oid` are removed before persisting. Additionally, `yb_non_ddl_txn_for_sys_tables_allowed` development flag caused `CREATE VIEW` to fail with a read restart error because cache refresh operation was done in the wrong transaction. Changed it to affect WRITE operations only. Test Plan: ybd --java-test org.yb.pgsql.TestYsqlUpgrade Reviewers: dmitry, mihnea, jason Reviewed By: jason Subscribers: zyu, yql Differential Revision: https://phabricator.dev.yugabyte.com/D12128
1 parent 33ca6e2 commit 3f15bfc

File tree

14 files changed

+308
-31
lines changed

14 files changed

+308
-31
lines changed

java/yb-pgsql/src/test/java/org/yb/pgsql/TestYsqlUpgrade.java

Lines changed: 102 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,97 @@ public void sharedRelsIndexesWork() throws Exception {
438438
}
439439
}
440440

441+
/** Create a system view just like pg_stats and verify they look the same. */
442+
@Test
443+
public void creatingSystemViewsIsLikeInitdb() throws Exception {
444+
TableInfo origTi = new TableInfo("pg_stats", 12081L, 12082L, 12083L,
445+
Arrays.asList());
446+
447+
TableInfo newTi = new TableInfo("pg_stats_2", newSysOid(), newSysOid(), newSysOid(),
448+
Arrays.asList());
449+
450+
try (Connection conn = getConnectionBuilder().withDatabase(customDbName).connect();
451+
Statement stmtA = conn.createStatement();
452+
Statement stmtB = conn.createStatement()) {
453+
enableSystemRelsModification(stmtA);
454+
455+
String createViewSql = "CREATE VIEW pg_catalog." + newTi.name + " WITH ("
456+
+ " table_oid = " + newTi.oid
457+
+ ", row_type_oid = " + newTi.typeOid
458+
+ ", rewrite_rule_oid = " + newTi.ruleOid
459+
+ ", security_barrier = true"
460+
+ ") AS"
461+
+ " SELECT"
462+
+ " nspname AS schemaname,"
463+
+ " relname AS tablename,"
464+
+ " attname AS attname,"
465+
+ " stainherit AS inherited,"
466+
+ " stanullfrac AS null_frac,"
467+
+ " stawidth AS avg_width,"
468+
+ " stadistinct AS n_distinct,"
469+
+ " CASE"
470+
+ " WHEN stakind1 = 1 THEN stavalues1"
471+
+ " WHEN stakind2 = 1 THEN stavalues2"
472+
+ " WHEN stakind3 = 1 THEN stavalues3"
473+
+ " WHEN stakind4 = 1 THEN stavalues4"
474+
+ " WHEN stakind5 = 1 THEN stavalues5"
475+
+ " END AS most_common_vals,"
476+
+ " CASE"
477+
+ " WHEN stakind1 = 1 THEN stanumbers1"
478+
+ " WHEN stakind2 = 1 THEN stanumbers2"
479+
+ " WHEN stakind3 = 1 THEN stanumbers3"
480+
+ " WHEN stakind4 = 1 THEN stanumbers4"
481+
+ " WHEN stakind5 = 1 THEN stanumbers5"
482+
+ " END AS most_common_freqs,"
483+
+ " CASE"
484+
+ " WHEN stakind1 = 2 THEN stavalues1"
485+
+ " WHEN stakind2 = 2 THEN stavalues2"
486+
+ " WHEN stakind3 = 2 THEN stavalues3"
487+
+ " WHEN stakind4 = 2 THEN stavalues4"
488+
+ " WHEN stakind5 = 2 THEN stavalues5"
489+
+ " END AS histogram_bounds,"
490+
+ " CASE"
491+
+ " WHEN stakind1 = 3 THEN stanumbers1[1]"
492+
+ " WHEN stakind2 = 3 THEN stanumbers2[1]"
493+
+ " WHEN stakind3 = 3 THEN stanumbers3[1]"
494+
+ " WHEN stakind4 = 3 THEN stanumbers4[1]"
495+
+ " WHEN stakind5 = 3 THEN stanumbers5[1]"
496+
+ " END AS correlation,"
497+
+ " CASE"
498+
+ " WHEN stakind1 = 4 THEN stavalues1"
499+
+ " WHEN stakind2 = 4 THEN stavalues2"
500+
+ " WHEN stakind3 = 4 THEN stavalues3"
501+
+ " WHEN stakind4 = 4 THEN stavalues4"
502+
+ " WHEN stakind5 = 4 THEN stavalues5"
503+
+ " END AS most_common_elems,"
504+
+ " CASE"
505+
+ " WHEN stakind1 = 4 THEN stanumbers1"
506+
+ " WHEN stakind2 = 4 THEN stanumbers2"
507+
+ " WHEN stakind3 = 4 THEN stanumbers3"
508+
+ " WHEN stakind4 = 4 THEN stanumbers4"
509+
+ " WHEN stakind5 = 4 THEN stanumbers5"
510+
+ " END AS most_common_elem_freqs,"
511+
+ " CASE"
512+
+ " WHEN stakind1 = 5 THEN stanumbers1"
513+
+ " WHEN stakind2 = 5 THEN stanumbers2"
514+
+ " WHEN stakind3 = 5 THEN stanumbers3"
515+
+ " WHEN stakind4 = 5 THEN stanumbers4"
516+
+ " WHEN stakind5 = 5 THEN stanumbers5"
517+
+ " END AS elem_count_histogram"
518+
+ " FROM pg_statistic s JOIN pg_class c ON (c.oid = s.starelid)"
519+
+ " JOIN pg_attribute a ON (c.oid = attrelid AND attnum = s.staattnum)"
520+
+ " LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace)"
521+
+ " WHERE NOT attisdropped"
522+
+ " AND has_column_privilege(c.oid, a.attnum, 'select')"
523+
+ " AND (c.relrowsecurity = false OR NOT row_security_active(c.oid));";
524+
525+
LOG.info("Executing '{}'", createViewSql);
526+
stmtA.execute(createViewSql);
527+
528+
assertTablesAreSimilar(origTi, newTi, stmtA, stmtB);
529+
}
530+
}
531+
441532
//
442533
// Helpers
443534
//
@@ -487,6 +578,7 @@ private void assertTablesAreSimilar(
487578
.map(r -> expectRelfilenodeMismatch ? excluded(r, "relfilenode") : r)
488579
.map(r -> replaced(r, newTi.oid, origTi.oid))
489580
.map(r -> replaced(r, newTi.typeOid, origTi.typeOid))
581+
.map(r -> origTi.ruleOid != 0 ? replaced(r, newTi.ruleOid, origTi.ruleOid) : r)
490582
.map(r -> replacedString(r, newTi.name, origTi.name))
491583
.map(r -> {
492584
for (int i = 0; i < newTi.indexes.size(); ++i) {
@@ -498,8 +590,8 @@ private void assertTablesAreSimilar(
498590
};
499591

500592
{
501-
// pg_class and pg_type (for rel and indexes)
502-
String sql = "SELECT cl.oid, cl.*, tp.oid, tp.* FROM pg_class cl"
593+
// pg_class and pg_type (for rel and indexes), as well as pg_get_viewdef (view query)
594+
String sql = "SELECT cl.oid, cl.*, tp.oid, tp.*, pg_get_viewdef(cl.oid) FROM pg_class cl"
503595
+ " LEFT JOIN pg_type tp ON cl.oid = tp.typrelid"
504596
+ " WHERE cl.oid = %d ORDER By cl.oid, tp.oid";
505597
{
@@ -690,12 +782,20 @@ private class TableInfo {
690782
public final String name;
691783
public final long oid;
692784
public final long typeOid;
785+
/** Only when applicable, set to 0 otherwise. */
786+
public final long ruleOid;
693787
public final List<Pair<String, Long>> indexes;
694788

695789
public TableInfo(String name, long oid, long typeOid, List<Pair<String, Long>> indexes) {
790+
this(name, oid, typeOid, 0L, indexes);
791+
}
792+
793+
public TableInfo(String name, long oid, long typeOid, long ruleOid,
794+
List<Pair<String, Long>> indexes) {
696795
this.name = name;
697796
this.oid = oid;
698797
this.typeOid = typeOid;
798+
this.ruleOid = ruleOid;
699799
this.indexes = Collections.unmodifiableList(new ArrayList<>(indexes));
700800
}
701801
}

src/postgres/src/backend/access/common/reloptions.c

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,17 @@ static relopt_int intRelOpts[] =
392392
1 /* parse_utilcmd takes care of OID >= FirstNormalObjectId for user tables */,
393393
INT_MAX
394394
},
395+
{
396+
{
397+
"rewrite_rule_oid",
398+
"Postgres rewrite rule oid for the new rewrite rule defined for this view.",
399+
RELOPT_KIND_VIEW,
400+
AccessExclusiveLock
401+
},
402+
InvalidOid,
403+
1,
404+
FirstNormalObjectId - 1 /* we don't expect this for non-system views */
405+
},
395406
/* list terminator */
396407
{{NULL}}
397408
};
@@ -827,6 +838,24 @@ add_string_reloption(bits32 kinds, const char *name, const char *desc, const cha
827838
Datum
828839
transformRelOptions(Datum oldOptions, List *defList, const char *namspace,
829840
char *validnsps[], bool ignoreOids, bool isReset)
841+
{
842+
return ybTransformRelOptions(oldOptions, defList, namspace, validnsps,
843+
ignoreOids, isReset,
844+
false /* ybIgnoreYsqlUpgradeViewOptions */);
845+
}
846+
847+
/*
848+
* See above for transformRelOptions description.
849+
*
850+
* If ybIgnoreYsqlUpgradeViewOptions is specified, ignore "table_oid",
851+
* "row_type_oid" and "rewrite_rule_oid" options. This is needed in
852+
* YSQL upgrade mode, where we use them to simulate initdb-like behaviour
853+
* for creating relations but don't want them to be persisted.
854+
*/
855+
Datum
856+
ybTransformRelOptions(Datum oldOptions, List *defList, const char *namspace,
857+
char *validnsps[], bool ignoreOids, bool isReset,
858+
bool ybIgnoreYsqlUpgradeOptions)
830859
{
831860
Datum result;
832861
ArrayBuildState *astate;
@@ -940,6 +969,12 @@ transformRelOptions(Datum oldOptions, List *defList, const char *namspace,
940969
if (ignoreOids && strcmp(def->defname, "oids") == 0)
941970
continue;
942971

972+
if (ybIgnoreYsqlUpgradeOptions &&
973+
(strcmp(def->defname, "table_oid") == 0 ||
974+
strcmp(def->defname, "row_type_oid") == 0 ||
975+
strcmp(def->defname, "rewrite_rule_oid") == 0))
976+
continue;
977+
943978
/* ignore if not in the same namespace */
944979
if (namspace == NULL)
945980
{
@@ -1475,7 +1510,9 @@ view_reloptions(Datum reloptions, bool validate)
14751510
{"security_barrier", RELOPT_TYPE_BOOL,
14761511
offsetof(ViewOptions, security_barrier)},
14771512
{"check_option", RELOPT_TYPE_STRING,
1478-
offsetof(ViewOptions, check_option_offset)}
1513+
offsetof(ViewOptions, check_option_offset)},
1514+
{"rewrite_rule_oid", RELOPT_TYPE_BOOL,
1515+
offsetof(ViewOptions, rewrite_rule_oid)},
14791516
};
14801517

14811518
options = parseRelOptions(reloptions, validate, RELOPT_KIND_VIEW, &numoptions);

src/postgres/src/backend/catalog/catalog.c

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151

5252
/* YB includes. */
5353
#include "access/htup_details.h"
54+
#include "catalog/pg_rewrite.h"
5455
#include "utils/syscache.h"
5556
#include "pg_yb_utils.h"
5657

@@ -693,3 +694,55 @@ GetRowTypeOidFromRelOptions(List *relOptions)
693694

694695
return InvalidOid;
695696
}
697+
698+
/*
699+
* GetRewriteRuleOidFromRelOptions
700+
* Scans through relOptions for any 'rewrite_rule_oid' options, and ensures
701+
* that oid is available. Returns that oid, or InvalidOid if unspecified.
702+
*/
703+
Oid
704+
GetRewriteRuleOidFromRelOptions(List *relOptions)
705+
{
706+
ListCell *opt_cell;
707+
Oid rewrite_rule_oid;
708+
Relation pg_rewrite_desc;
709+
ScanKeyData skey[1];
710+
SysScanDesc rcscan;
711+
HeapTuple tuple;
712+
713+
foreach(opt_cell, relOptions)
714+
{
715+
DefElem *def = (DefElem *) lfirst(opt_cell);
716+
if (strcmp(def->defname, "rewrite_rule_oid") == 0)
717+
{
718+
rewrite_rule_oid = strtol(defGetString(def), NULL, 10);
719+
if (OidIsValid(rewrite_rule_oid))
720+
{
721+
/* Check there's no such rule yet. */
722+
pg_rewrite_desc = heap_open(RewriteRelationId, RowExclusiveLock);
723+
724+
ScanKeyInit(&skey[0],
725+
ObjectIdAttributeNumber,
726+
BTEqualStrategyNumber, F_OIDEQ,
727+
ObjectIdGetDatum(rewrite_rule_oid));
728+
729+
rcscan = systable_beginscan(pg_rewrite_desc, RewriteOidIndexId,
730+
true, NULL, 1, skey);
731+
732+
tuple = systable_getnext(rcscan);
733+
734+
if (HeapTupleIsValid(tuple))
735+
ereport(ERROR,
736+
(errcode(ERRCODE_DUPLICATE_OBJECT),
737+
errmsg("rewrite rule OID %d is in use", rewrite_rule_oid)));
738+
739+
systable_endscan(rcscan);
740+
heap_close(pg_rewrite_desc, RowExclusiveLock);
741+
742+
return rewrite_rule_oid;
743+
}
744+
}
745+
}
746+
747+
return InvalidOid;
748+
}

src/postgres/src/backend/catalog/heap.c

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1206,17 +1206,29 @@ heap_create_with_catalog(const char *relname,
12061206
}
12071207

12081208
/*
1209+
* YSQL upgrade notes:
1210+
* -------------------
12091211
* At this point, reloptions no longer affects the relation
12101212
* creation process, the only remaining use for them is to be
1211-
* stored in pg_class.
1213+
* stored in pg_class. ybTransformRelOptions has already
1214+
* removed all temporary reloptions.
12121215
*
1213-
* For YSQL upgrade, we want system relations to not have
1214-
* reloptions stored to imitate BKI processing, so we can safely
1215-
* remove them now.
1216+
* We want system tables to not have any reloptions stored to
1217+
* imitate BKI processing, so we don't allow any reloption not
1218+
* removed by ybTransformRelOptions.
1219+
*
1220+
* System views, on the other hand, are defined in
1221+
* yb_system_views.sql rather than via BKI, so they are allowed
1222+
* to have non-temporary reloptions.
12161223
*/
1217-
if (is_system && IsYsqlUpgrade)
1224+
if (IsYsqlUpgrade && is_system && relkind != RELKIND_VIEW
1225+
&& reloptions != (Datum) 0)
12181226
{
1219-
reloptions = (Datum) 0;
1227+
ereport(ERROR,
1228+
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
1229+
errmsg("unsuppored reloptions were used for a system table "
1230+
"during YSQL upgrade"),
1231+
errhint("Only a small subset is allowed due to BKI restrictions.")));
12201232
}
12211233

12221234
/*

src/postgres/src/backend/commands/createas.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,8 @@ create_ctas_internal(List *attrList, IntoClause *into)
146146
/* StoreViewQuery scribbles on tree, so make a copy */
147147
Query *query = (Query *) copyObject(into->viewQuery);
148148

149-
StoreViewQuery(intoRelationAddr.objectId, query, false);
149+
StoreViewQuery(intoRelationAddr.objectId, query, false,
150+
InvalidOid /* yb_rule_id */);
150151
CommandCounterIncrement();
151152
}
152153

src/postgres/src/backend/commands/tablecmds.c

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,10 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
658658
ereport(ERROR,
659659
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
660660
errmsg("shared relations can not be partitioned")));
661+
else if (relkind != RELKIND_RELATION)
662+
ereport(ERROR,
663+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
664+
errmsg("only ordinary tables may be shared")));
661665

662666
relisshared = true;
663667

@@ -692,11 +696,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
692696
}
693697
}
694698

695-
if (IsYsqlUpgrade && relkind == RELKIND_VIEW && IsSystemNamespace(namespaceId))
696-
ereport(ERROR,
697-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
698-
errmsg("creating system views during YSQL upgrade is not yet supported")));
699-
700699
/*
701700
* Select tablegroup to use. If not specified, InvalidOid.
702701
* Disallow mixing of COLOCATED=true/false syntax and TABLEGROUP. Cannot use tablegroups
@@ -758,8 +757,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
758757
/*
759758
* Parse and validate reloptions, if any.
760759
*/
761-
reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
762-
true, false);
760+
reloptions = ybTransformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
761+
true, false, IsYsqlUpgrade);
763762

764763
if (relkind == RELKIND_VIEW)
765764
(void) view_reloptions(reloptions, true);

0 commit comments

Comments
 (0)