@@ -8,7 +8,6 @@ package schemachange
88import (
99 "context"
1010 "fmt"
11- "regexp"
1211 "slices"
1312 "strings"
1413
@@ -789,36 +788,6 @@ func (og *operationGenerator) scanStringArrayNullableRows(
789788 return results , nil
790789}
791790
792- func (og * operationGenerator ) scanStringArrayRows (
793- ctx context.Context , tx pgx.Tx , query string , args ... interface {},
794- ) ([][]string , error ) {
795- rows , err := tx .Query (ctx , query , args ... )
796- if err != nil {
797- return nil , errors .Wrapf (err , "scanStringArrayRows: %q %q" , query , args )
798- }
799- defer rows .Close ()
800-
801- var results [][]string
802- for rows .Next () {
803- var columnNames []string
804- err := rows .Scan (& columnNames )
805- if err != nil {
806- return nil , errors .Wrapf (err , "scan: %q, args %q, scanArgs %q" , query , columnNames , args )
807- }
808- results = append (results , columnNames )
809- }
810-
811- if rows .Err () != nil {
812- return nil , rows .Err ()
813- }
814-
815- og .LogQueryResults (
816- query ,
817- results ,
818- args ... )
819- return results , nil
820- }
821-
822791func (og * operationGenerator ) indexExists (
823792 ctx context.Context , tx pgx.Tx , tableName * tree.TableName , indexName string ,
824793) (bool , error ) {
@@ -1051,188 +1020,6 @@ func (og *operationGenerator) constraintExists(
10511020 )` , string (tableName ), string (constraintName ))
10521021}
10531022
1054- var (
1055- // regexpUnknownSchemaErr matches unknown schema errors with
1056- // a descriptor ID, which will have the form: unknown schema "[123]"
1057- regexpUnknownSchemaErr = regexp .MustCompile (`unknown schema "\[\d+]"` )
1058- )
1059-
1060- // checkAndAdjustForUnknownSchemaErrors in certain contexts we will attempt to
1061- // bind descriptors without leasing them, since we are using crdb_internal tables,
1062- // so it's possible for said descriptor to be dropped before we bind it. This
1063- // method will allow for "unknown schema [xx]" in those contexts.
1064- func (og * operationGenerator ) checkAndAdjustForUnknownSchemaErrors (err error ) error {
1065- if pgErr := new (pgconn.PgError ); errors .As (err , & pgErr ) &&
1066- pgcode .MakeCode (pgErr .Code ) == pgcode .InvalidSchemaName {
1067- if regexpUnknownSchemaErr .MatchString (pgErr .Message ) {
1068- og .LogMessage (fmt .Sprintf ("Rolling back due to unknown schema error %v" ,
1069- err ))
1070- // Force a rollback and log inside the operation generator.
1071- return errors .Mark (err , errRunInTxnRbkSentinel )
1072- }
1073- }
1074- return err
1075- }
1076-
1077- // violatesFkConstraints checks if the rows to be inserted will result in a foreign key violation.
1078- func (og * operationGenerator ) violatesFkConstraints (
1079- ctx context.Context ,
1080- tx pgx.Tx ,
1081- tableName * tree.TableName ,
1082- nonGeneratedColNames []tree.Name ,
1083- rows [][]string ,
1084- ) (expectedViolation , potentialViolation bool , err error ) {
1085- // TODO(annie): readd the join on active constraints once #120702 is resolved.
1086- //
1087- // N.B. We add random noise to column names that makes it hard to just directly call on these names. This is
1088- // not the case with table/schema names; thus, only column names are quote_ident'ed to ensure that they get
1089- // referenced properly.
1090- fkConstraints , err := og .scanStringArrayRows (ctx , tx , fmt .Sprintf (`
1091- SELECT array[
1092- parent.table_schema,
1093- parent.table_name,
1094- parent.column_name,
1095- parent.is_generated,
1096- child.column_name,
1097- child.is_generated
1098- ]
1099- FROM (
1100- SELECT conname, conkey, confkey, conrelid, confrelid
1101- FROM pg_constraint
1102- WHERE contype = 'f'
1103- AND conrelid = '%s'::REGCLASS::INT8
1104- ) AS con
1105- JOIN (
1106- SELECT column_name, ordinal_position, column_default, is_generated
1107- FROM information_schema.columns
1108- WHERE table_schema = '%s'
1109- AND table_name = '%s'
1110- ) AS child ON conkey[1] = child.ordinal_position
1111- JOIN (
1112- SELECT pc.oid,
1113- cols.table_schema,
1114- cols.table_name,
1115- cols.column_name,
1116- cols.ordinal_position,
1117- cols.is_generated
1118- FROM pg_class AS pc
1119- JOIN pg_namespace AS pn ON pc.relnamespace = pn.oid
1120- JOIN information_schema.columns AS cols ON (pc.relname = cols.table_name AND pn.nspname = cols.table_schema)
1121- ) AS parent ON (
1122- con.confkey[1] = parent.ordinal_position
1123- AND con.confrelid = parent.oid
1124- )
1125- WHERE child.column_name != 'rowid';
1126- ` , tableName .String (), tableName .Schema (), tableName .Object ()))
1127- if err != nil {
1128- return false , false , og .checkAndAdjustForUnknownSchemaErrors (err )
1129- }
1130-
1131- // Maps a non-generated column name to its index. This way, the value of a
1132- // column in a row can be looked up using row[colToIndexMap["columnName"]] = "valueForColumn"
1133- nonGeneratedColumnNameToIndexMap := map [tree.Name ]int {}
1134-
1135- for i , name := range nonGeneratedColNames {
1136- nonGeneratedColumnNameToIndexMap [name ] = i
1137- }
1138-
1139- for _ , row := range rows {
1140- for _ , constraint := range fkConstraints {
1141- parentTableSchema := tree .Name (constraint [0 ])
1142- parentTableName := tree .Name (constraint [1 ])
1143- parentColumnName := tree .Name (constraint [2 ])
1144- parentIsGenerated := constraint [3 ] == "ALWAYS"
1145- childColumnName := tree .Name (constraint [4 ])
1146- childIsGenerated := constraint [5 ] == "ALWAYS"
1147-
1148- // If self referential, there cannot be a violation.
1149- parentAndChildAreSame := parentTableSchema == tableName .SchemaName && parentTableName == tableName .ObjectName
1150- if parentAndChildAreSame && parentColumnName == childColumnName {
1151- continue
1152- }
1153-
1154- // If the foreign key involves any generated columns, skip detailed FK checking
1155- // and conservatively assume a violation may occur. This avoids the complexity
1156- // of reasoning about values that are automatically computed by the database.
1157- if parentIsGenerated || childIsGenerated {
1158- return false , true , nil
1159- }
1160-
1161- violation , err := og .violatesFkConstraintsHelper (
1162- ctx , tx , nonGeneratedColumnNameToIndexMap , parentTableSchema , parentTableName , parentColumnName , childColumnName , tableName , parentAndChildAreSame , row , rows ,
1163- )
1164- if err != nil {
1165- return false , false , err
1166- }
1167- if violation {
1168- return true , false , nil
1169- }
1170- }
1171- }
1172-
1173- return false , false , nil
1174- }
1175-
1176- // violatesFkConstraintsHelper checks if inserting a single row would violate a
1177- // foreign key constraint between the given child and parent columns.
1178- //
1179- // This function assumes that neither the parent nor child columns are generated.
1180- // The caller must ensure this; generated columns are not supported.
1181- func (og * operationGenerator ) violatesFkConstraintsHelper (
1182- ctx context.Context ,
1183- tx pgx.Tx ,
1184- nonGeneratedColumnNameToIndexMap map [tree.Name ]int ,
1185- parentTableSchema , parentTableName , parentColumn , childColumn tree.Name ,
1186- childTableName * tree.TableName ,
1187- parentAndChildAreSameTable bool ,
1188- rowToInsert []string ,
1189- allRows [][]string ,
1190- ) (bool , error ) {
1191-
1192- childIndex , ok := nonGeneratedColumnNameToIndexMap [childColumn ]
1193- if ! ok {
1194- return false , errors .Newf ("child column %s does not exist in table %s" , childColumn , childTableName )
1195- }
1196- childValue := rowToInsert [childIndex ]
1197- // If the value to insert in the child column is NULL and the column default is NULL, then it is not possible to have a fk violation.
1198- if childValue == "NULL" {
1199- return false , nil
1200- }
1201- // If the parent and child are the same table, then any rows in an existing
1202- // insert may satisfy the same constraint.
1203- var parentAndChildSameQueryColumns []string
1204- if parentAndChildAreSameTable {
1205- for _ , otherRow := range allRows {
1206- parentIdx , ok := nonGeneratedColumnNameToIndexMap [parentColumn ]
1207- if ! ok {
1208- return false , errors .Newf ("parent column %s does not exist in table %s" , parentColumn , childTableName )
1209- }
1210- parentValueInSameInsert := otherRow [parentIdx ]
1211-
1212- // Skip over NULL values.
1213- if parentValueInSameInsert == "NULL" {
1214- continue
1215- }
1216- parentAndChildSameQueryColumns = append (parentAndChildSameQueryColumns ,
1217- fmt .Sprintf ("%s = %s" , parentValueInSameInsert , childValue ))
1218- }
1219- }
1220- checkSharedParentChildRows := ""
1221- if len (parentAndChildSameQueryColumns ) > 0 {
1222- // Check if none of the rows being inserted satisfy the foreign key constraint,
1223- // since the foreign key constraints refers to the same table. So, anything in
1224- // the insert batch can satisfy the constraint.
1225- checkSharedParentChildRows = fmt .Sprintf ("NOT (true = ANY (ARRAY [%s])) AND" ,
1226- strings .Join (parentAndChildSameQueryColumns , "," ))
1227- }
1228- q := fmt .Sprintf (`
1229- SELECT %s count(*) = 0 from %s.%s
1230- WHERE %s = (%s)
1231- ` ,
1232- checkSharedParentChildRows , parentTableSchema .String (), parentTableName .String (), parentColumn .String (), childValue )
1233- return og .scanBool (ctx , tx , q )
1234- }
1235-
12361023func (og * operationGenerator ) columnIsInAddingOrDroppingIndex (
12371024 ctx context.Context , tx pgx.Tx , tableName * tree.TableName , columnName tree.Name ,
12381025) (bool , error ) {
@@ -1766,35 +1553,6 @@ func (og *operationGenerator) tableHasUniqueConstraintMutation(
17661553 );` , tableName )
17671554}
17681555
1769- // tableHasForeignKeyMutation determines if a table has any foreign key constraint
1770- // mutation ongoing. This means either being added or dropped.
1771- func (og * operationGenerator ) tableHasForeignKeyMutation (
1772- ctx context.Context , tx pgx.Tx , tableName * tree.TableName ,
1773- ) (bool , error ) {
1774- return og .scanBool (ctx , tx , `
1775- WITH table_desc AS (
1776- SELECT crdb_internal.pb_to_json(
1777- 'desc',
1778- descriptor,
1779- false
1780- )->'table' as d
1781- FROM system.descriptor
1782- WHERE id = $1::REGCLASS
1783- )
1784- SELECT EXISTS (
1785- SELECT * FROM (
1786- SELECT jsonb_array_elements(
1787- CASE WHEN d->'mutations' IS NULL
1788- THEN '[]'::JSONB
1789- ELSE d->'mutations'
1790- END
1791- ) as m
1792- FROM table_desc)
1793- WHERE (m->>'direction')::STRING IN ('ADD', 'DROP')
1794- AND (m->'constraint'->>'foreign_key') IS NOT NULL
1795- );` , tableName )
1796- }
1797-
17981556// getTableForeignKeyReferences returns a list of tables that reference
17991557// the specified table via foreign key references.
18001558func (og * operationGenerator ) getTableForeignKeyReferences (
0 commit comments