From 2ddf2651130328e52d7243934ca2ee7d2665c94a Mon Sep 17 00:00:00 2001 From: Roman Eskin Date: Wed, 7 Aug 2024 18:08:42 +1000 Subject: [PATCH] Improve statistics backup queries --- backup/statistics.go | 8 ++++---- backup/statistics_test.go | 26 ++++++++++++-------------- restore/restore.go | 10 ++++------ 3 files changed, 20 insertions(+), 24 deletions(-) diff --git a/backup/statistics.go b/backup/statistics.go index 8dd7b0d8a..24079eb36 100644 --- a/backup/statistics.go +++ b/backup/statistics.go @@ -69,16 +69,16 @@ func GenerateAttributeStatisticsQueries(table Table, attStat AttributeStatistic) attributeSlotsQueryStr = generateAttributeSlotsQuery4(attStat) } - attributeQueries = append(attributeQueries, fmt.Sprintf(`DELETE FROM pg_statistic -WHERE starelid = %s AND staattnum = (SELECT attnum FROM pg_attribute WHERE attrelid = %s AND attname = '%s');`, starelidStr, starelidStr, utils.EscapeSingleQuotes(attStat.AttName))) + attributeQueries = append(attributeQueries, fmt.Sprintf(`WITH attr_info AS (SELECT attrelid, attnum FROM pg_attribute WHERE attrelid = %s AND attname = '%s') +DELETE FROM pg_statistic WHERE starelid = (SELECT attrelid from attr_info) AND staattnum = (SELECT attnum from attr_info);`, starelidStr, utils.EscapeSingleQuotes(attStat.AttName))) attributeQueries = append(attributeQueries, fmt.Sprintf(`INSERT INTO pg_statistic SELECT - %s, + attrelid, attnum,%s %f::real, %d::integer, %f::real, %s -FROM pg_attribute WHERE attrelid = %s AND attname = '%s';`, starelidStr, inheritStr, attStat.NullFraction, attStat.Width, attStat.Distinct, attributeSlotsQueryStr, starelidStr, utils.EscapeSingleQuotes(attStat.AttName))) +FROM pg_attribute WHERE attrelid = %s AND attname = '%s';`, inheritStr, attStat.NullFraction, attStat.Width, attStat.Distinct, attributeSlotsQueryStr, starelidStr, utils.EscapeSingleQuotes(attStat.AttName))) /* * If a type name starts with exactly one underscore, it describes an array diff --git a/backup/statistics_test.go b/backup/statistics_test.go index 08ac8ab78..7f6366b32 100644 --- a/backup/statistics_test.go +++ b/backup/statistics_test.go @@ -100,11 +100,10 @@ SET reltuples = 0.000000::real WHERE oid = 'testschema.testtable2'::regclass::oid;`, - `DELETE FROM pg_statistic -WHERE starelid = 'testschema.testtable2'::regclass::oid AND staattnum = (SELECT attnum FROM pg_attribute WHERE attrelid = 'testschema.testtable2'::regclass::oid AND attname = 'testattWithArray');`, - + `WITH attr_info AS (SELECT attrelid, attnum FROM pg_attribute WHERE attrelid = 'testschema.testtable2'::regclass::oid AND attname = 'testattWithArray') +DELETE FROM pg_statistic WHERE starelid = (SELECT attrelid from attr_info) AND staattnum = (SELECT attnum from attr_info);`, fmt.Sprintf(`INSERT INTO pg_statistic SELECT - 'testschema.testtable2'::regclass::oid, + attrelid, attnum,%[1]s 0.000000::real, 0::integer, @@ -127,11 +126,10 @@ WHERE starelid = 'testschema.testtable2'::regclass::oid AND staattnum = (SELECT NULL FROM pg_attribute WHERE attrelid = 'testschema.testtable2'::regclass::oid AND attname = 'testattWithArray';`, insertReplace1, insertReplace2, insertReplace3, insertReplace4, insertReplace5), - `DELETE FROM pg_statistic -WHERE starelid = 'testschema.testtable2'::regclass::oid AND staattnum = (SELECT attnum FROM pg_attribute WHERE attrelid = 'testschema.testtable2'::regclass::oid AND attname = 'testatt');`, - + `WITH attr_info AS (SELECT attrelid, attnum FROM pg_attribute WHERE attrelid = 'testschema.testtable2'::regclass::oid AND attname = 'testatt') +DELETE FROM pg_statistic WHERE starelid = (SELECT attrelid from attr_info) AND staattnum = (SELECT attnum from attr_info);`, fmt.Sprintf(`INSERT INTO pg_statistic SELECT - 'testschema.testtable2'::regclass::oid, + attrelid, attnum,%[1]s 0.400000::real, 10::integer, @@ -183,12 +181,12 @@ WHERE oid = '"""test''schema"""."""test''table"""'::regclass::oid;`)) } attStatsQueries := backup.GenerateAttributeStatisticsQueries(tableTestTable, attStats) - Expect(attStatsQueries[0]).To(Equal(fmt.Sprintf(`DELETE FROM pg_statistic -WHERE starelid = 'testschema."test''table"'::regclass::oid AND staattnum = (SELECT attnum FROM pg_attribute WHERE attrelid = 'testschema."test''table"'::regclass::oid AND attname = 'testatt');`))) + Expect(attStatsQueries[0]).To(Equal(fmt.Sprintf(`WITH attr_info AS (SELECT attrelid, attnum FROM pg_attribute WHERE attrelid = 'testschema."test''table"'::regclass::oid AND attname = 'testatt') +DELETE FROM pg_statistic WHERE starelid = (SELECT attrelid from attr_info) AND staattnum = (SELECT attnum from attr_info);`))) insertReplace1, insertReplace2, insertReplace3, insertReplace4, insertReplace5 := getStatInsertReplace(0, 0) Expect(attStatsQueries[1]).To(Equal(fmt.Sprintf(`INSERT INTO pg_statistic SELECT - 'testschema."test''table"'::regclass::oid, + attrelid, attnum,%s 0.400000::real, 10::integer, @@ -222,12 +220,12 @@ FROM pg_attribute WHERE attrelid = 'testschema."test''table"'::regclass::oid AND attStatsQueries := backup.GenerateAttributeStatisticsQueries(tableTestTable, attStats) - Expect(attStatsQueries[0]).To(Equal(fmt.Sprintf(`DELETE FROM pg_statistic -WHERE starelid = 'testschema."test''table"'::regclass::oid AND staattnum = (SELECT attnum FROM pg_attribute WHERE attrelid = 'testschema."test''table"'::regclass::oid AND attname = 'testatt');`))) + Expect(attStatsQueries[0]).To(Equal(fmt.Sprintf(`WITH attr_info AS (SELECT attrelid, attnum FROM pg_attribute WHERE attrelid = 'testschema."test''table"'::regclass::oid AND attname = 'testatt') +DELETE FROM pg_statistic WHERE starelid = (SELECT attrelid from attr_info) AND staattnum = (SELECT attnum from attr_info);`))) insertReplace1, insertReplace2, insertReplace3, insertReplace4, insertReplace5 := getStatInsertReplace(10, 12) Expect(attStatsQueries[1]).To(Equal(fmt.Sprintf(`INSERT INTO pg_statistic SELECT - 'testschema."test''table"'::regclass::oid, + attrelid, attnum,%s 0.400000::real, 10::integer, diff --git a/restore/restore.go b/restore/restore.go index 39fc039a3..5dfc5e24d 100644 --- a/restore/restore.go +++ b/restore/restore.go @@ -410,15 +410,14 @@ func editStatementsRedirectSchema(statements []toc.StatementWithType, redirectSc return } - schemaMatch := `(?:".+?"|[^."']+?)` // matches either an unquoted schema with no dots and no quotes or a quoted schema + schemaMatch := `(?:".+?"|[^.]+?)` // matches either an unquoted schema with no dots or a quoted schema containing dots // This expression matches a GRANT or REVOKE statement on any object and captures the old schema name permissionsRE := regexp.MustCompile(fmt.Sprintf(`(?m)(^(?:REVOKE|GRANT) .+ ON .+?) (%s)((\..+)? (?:FROM|TO) .+)`, schemaMatch)) // This expression matches an ATTACH PARTITION statement and captures both the parent and child schema names attachRE := regexp.MustCompile(fmt.Sprintf(`(ALTER TABLE(?: ONLY)?) (%[1]s)(\..+ ATTACH PARTITION) (%[1]s)(\..+)`, schemaMatch)) // This expression matches a '.'::regclass::oid expression regclassOidRE := regexp.MustCompile(fmt.Sprintf(`'(%s)((\.[^']+)'\:\:regclass\:\:oid)`, schemaMatch)) - // These expressions match the first and the last occurences of the '.
'::regclass::oid expression - firstRegclassOidRE := regexp.MustCompile(fmt.Sprintf(`(?s)^(.*?)(%s)(.*)$`, regclassOidRE)) + // These expression matches the last occurence of the '.
'::regclass::oid expression lastRegclassOidRE := regexp.MustCompile(fmt.Sprintf(`(?s)^(.*)(%s)(.*?)$`, regclassOidRE)) for i := range statements { oldSchema := fmt.Sprintf("%s.", statements[i].Schema) @@ -445,10 +444,9 @@ func editStatementsRedirectSchema(statements []toc.StatementWithType, redirectSc replaced = true } - // Statistic statements need two schema replacements. We replace the first and the last occurences, - // to avoid a very small chance that schema name was in the statistic data itself, that we do not want to alter. + // Statistic statements needs one schema replacements. We replace the last occurence to avoid a very small + // chance that schema name was in the statistic data itself, that we do not want to alter. if strings.Contains(statement, "pg_statistic") { - statement = firstRegclassOidRE.ReplaceAllString(statement, fmt.Sprintf("${1}'%s${4}$6", redirectSchema)) statement = lastRegclassOidRE.ReplaceAllString(statement, fmt.Sprintf("${1}'%s${4}$6", redirectSchema)) replaced = true }