Skip to content

Commit 137c6a6

Browse files
author
Nikolay Kuznetsov
committed
remove validateOnConflictSetTypes
1 parent 4bb7035 commit 137c6a6

File tree

5 files changed

+15
-158
lines changed

5 files changed

+15
-158
lines changed

internal/compiler/analyze.go

Lines changed: 14 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
package compiler
22

33
import (
4-
"fmt"
54
"sort"
65

76
analyzer "github.com/sqlc-dev/sqlc/internal/analysis"
87
"github.com/sqlc-dev/sqlc/internal/config"
9-
"github.com/sqlc-dev/sqlc/internal/engine/postgresql"
108
"github.com/sqlc-dev/sqlc/internal/source"
119
"github.com/sqlc-dev/sqlc/internal/sql/ast"
1210
"github.com/sqlc-dev/sqlc/internal/sql/named"
@@ -189,7 +187,7 @@ func (c *Compiler) _analyzeQuery(raw *ast.RawStmt, query string, failfast bool)
189187
return nil, err
190188
}
191189
if n, ok := raw.Stmt.(*ast.InsertStmt); ok {
192-
if err := check(c.validateOnConflictClause(n, params)); err != nil {
190+
if err := check(c.validateOnConflictClause(n)); err != nil {
193191
return nil, err
194192
}
195193
}
@@ -227,34 +225,35 @@ func (c *Compiler) _analyzeQuery(raw *ast.RawStmt, query string, failfast bool)
227225
// - ON CONFLICT (col, ...) conflict target columns exist
228226
// - DO UPDATE SET col = ... assignment target columns exist
229227
// - EXCLUDED.col references exist
230-
// - For PostgreSQL: $N param and EXCLUDED.col type compatibility with SET target
231-
func (c *Compiler) validateOnConflictClause(n *ast.InsertStmt, params []Parameter) error {
228+
func (c *Compiler) validateOnConflictClause(n *ast.InsertStmt) error {
232229
if n.OnConflictClause == nil || n.OnConflictClause.Action != ast.OnConflictActionUpdate {
233230
return nil
234231
}
232+
235233
fqn, err := ParseTableName(n.Relation)
236234
if err != nil {
237235
return err
238236
}
237+
239238
table, err := c.catalog.GetTable(fqn)
240239
if err != nil {
241240
return err
242241
}
243242

244-
// Build column name → DataType from catalog for existence and type checks.
245-
colDataTypes := make(map[string]string, len(table.Columns))
243+
// Build set of column names for existence checks.
244+
colNames := make(map[string]struct{}, len(table.Columns))
246245
for _, col := range table.Columns {
247-
colDataTypes[col.Name] = dataType(&col.Type)
246+
colNames[col.Name] = struct{}{}
248247
}
249248

250249
// Validate ON CONFLICT (col, ...) conflict target columns.
251-
if n.OnConflictClause.Infer != nil {
250+
if n.OnConflictClause.Infer != nil && n.OnConflictClause.Infer.IndexElems != nil {
252251
for _, item := range n.OnConflictClause.Infer.IndexElems.Items {
253252
elem, ok := item.(*ast.IndexElem)
254253
if !ok || elem.Name == nil {
255254
continue
256255
}
257-
if _, exists := colDataTypes[*elem.Name]; !exists {
256+
if _, exists := colNames[*elem.Name]; !exists {
258257
e := sqlerr.ColumnNotFound(table.Rel.Name, *elem.Name)
259258
e.Location = n.OnConflictClause.Infer.Location
260259
return e
@@ -263,89 +262,29 @@ func (c *Compiler) validateOnConflictClause(n *ast.InsertStmt, params []Paramete
263262
}
264263

265264
// Validate DO UPDATE SET col = ... assignment target columns and EXCLUDED.col references.
265+
if n.OnConflictClause.TargetList == nil {
266+
return nil
267+
}
266268
for _, item := range n.OnConflictClause.TargetList.Items {
267269
target, ok := item.(*ast.ResTarget)
268270
if !ok || target.Name == nil {
269271
continue
270272
}
271-
if _, exists := colDataTypes[*target.Name]; !exists {
273+
if _, exists := colNames[*target.Name]; !exists {
272274
e := sqlerr.ColumnNotFound(table.Rel.Name, *target.Name)
273275
e.Location = target.Location
274276
return e
275277
}
276278
if ref, ok := target.Val.(*ast.ColumnRef); ok {
277279
if excludedCol, ok := excludedColumn(ref); ok {
278-
if _, exists := colDataTypes[excludedCol]; !exists {
280+
if _, exists := colNames[excludedCol]; !exists {
279281
e := sqlerr.ColumnNotFound(table.Rel.Name, excludedCol)
280282
e.Location = ref.Location
281283
return e
282284
}
283285
}
284286
}
285287
}
286-
287-
// Type compatibility checks (PostgreSQL only).
288-
// To remove type checking: delete this block and validateOnConflictSetTypes.
289-
if c.conf.Engine == config.EnginePostgreSQL {
290-
if err := c.validateOnConflictSetTypes(n, params, colDataTypes); err != nil {
291-
return err
292-
}
293-
}
294-
295-
return nil
296-
}
297-
298-
// validateOnConflictSetTypes checks that values in DO UPDATE SET assignments
299-
// are type-compatible with their target columns (PostgreSQL only).
300-
// It handles $N params (typed from INSERT VALUES) and EXCLUDED.col references.
301-
func (c *Compiler) validateOnConflictSetTypes(n *ast.InsertStmt, params []Parameter, colDataTypes map[string]string) error {
302-
// Build param number → resolved DataType from already-resolved params.
303-
// Skips params with "any" type (unresolved).
304-
paramDataTypes := make(map[int]string, len(params))
305-
for i := range params {
306-
if params[i].Column != nil && params[i].Column.DataType != "any" {
307-
paramDataTypes[params[i].Number] = params[i].Column.DataType
308-
}
309-
}
310-
311-
for _, item := range n.OnConflictClause.TargetList.Items {
312-
target, ok := item.(*ast.ResTarget)
313-
if !ok || target.Name == nil {
314-
continue
315-
}
316-
colDT, ok := colDataTypes[*target.Name]
317-
if !ok {
318-
continue
319-
}
320-
switch val := target.Val.(type) {
321-
case *ast.ParamRef:
322-
paramDT, ok := paramDataTypes[val.Number]
323-
if !ok {
324-
continue
325-
}
326-
if postgresql.TypeFamily(paramDT) != postgresql.TypeFamily(colDT) {
327-
return &sqlerr.Error{
328-
Message: fmt.Sprintf("parameter $%d has type %q but column %q has type %q", val.Number, paramDT, *target.Name, colDT),
329-
Location: val.Location,
330-
}
331-
}
332-
case *ast.ColumnRef:
333-
excludedCol, ok := excludedColumn(val)
334-
if !ok {
335-
continue
336-
}
337-
excludedDT, ok := colDataTypes[excludedCol]
338-
if !ok {
339-
continue
340-
}
341-
if postgresql.TypeFamily(excludedDT) != postgresql.TypeFamily(colDT) {
342-
return &sqlerr.Error{
343-
Message: fmt.Sprintf("EXCLUDED.%s has type %q but column %q has type %q", excludedCol, excludedDT, *target.Name, colDT),
344-
Location: val.Location,
345-
}
346-
}
347-
}
348-
}
349288
return nil
350289
}
351290

internal/endtoend/testdata/update_set_on_conflict/postgresql/pgx/query.sql

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,3 @@ INSERT INTO servers(code, name) VALUES ($1, $2)
1313
ON CONFLICT (code)
1414
DO UPDATE SET name = EXCLUDED.name_typo;
1515

16-
-- name: UpsertServerSetParamTypeMismatch :exec
17-
INSERT INTO servers(code, name) VALUES ($1, $2)
18-
ON CONFLICT (code)
19-
DO UPDATE SET count = $2;
20-
21-
-- name: UpsertServerExcludedTypeMismatch :exec
22-
INSERT INTO servers(code, name, count) VALUES ($1, $2, $3)
23-
ON CONFLICT (code)
24-
DO UPDATE SET count = EXCLUDED.code;
25-
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
CREATE TABLE servers (
22
code varchar PRIMARY KEY,
3-
name text NOT NULL,
4-
count integer NOT NULL DEFAULT 0
3+
name text NOT NULL
54
);

internal/endtoend/testdata/update_set_on_conflict/postgresql/pgx/stderr.txt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,3 @@
22
query.sql:4:15: column "name_typo" of relation "servers" does not exist
33
query.sql:8:13: column "code_typo" of relation "servers" does not exist
44
query.sql:14:22: column "name_typo" of relation "servers" does not exist
5-
query.sql:19:23: parameter $2 has type "text" but column "count" has type "pg_catalog.int4"
6-
query.sql:24:23: EXCLUDED.code has type "pg_catalog.varchar" but column "count" has type "pg_catalog.int4"

internal/engine/postgresql/utils.go

Lines changed: 0 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -39,75 +39,6 @@ func IsNamedParamSign(node *nodes.Node) bool {
3939
return ok && joinNodes(expr.AExpr.Name, ".") == "@"
4040
}
4141

42-
// TypeFamily maps a PostgreSQL DataType string to a canonical type family name,
43-
// grouping compatible type aliases together. This is used for type compatibility
44-
// checks rather than exact string equality, because PostgreSQL considers many
45-
// type aliases assignment-compatible (e.g. text and varchar are both string types).
46-
//
47-
// The groupings are derived from postgresType() in
48-
// internal/codegen/golang/postgresql_type.go, which maps these aliases to the
49-
// same Go type. We cannot call postgresType() directly for type compatibility
50-
// checking because it requires *plugin.GenerateRequest — a protobuf codegen
51-
// struct constructed after compilation — and driver-specific opts.Options.
52-
func TypeFamily(dt string) string {
53-
switch dt {
54-
case "serial", "serial4", "pg_catalog.serial4",
55-
"integer", "int", "int4", "pg_catalog.int4":
56-
return "int32"
57-
case "bigserial", "serial8", "pg_catalog.serial8",
58-
"bigint", "int8", "pg_catalog.int8",
59-
"interval", "pg_catalog.interval":
60-
return "int64"
61-
case "smallserial", "serial2", "pg_catalog.serial2",
62-
"smallint", "int2", "pg_catalog.int2":
63-
return "int16"
64-
case "float", "double precision", "float8", "pg_catalog.float8":
65-
return "float64"
66-
case "real", "float4", "pg_catalog.float4":
67-
return "float32"
68-
case "numeric", "pg_catalog.numeric", "money":
69-
return "numeric"
70-
case "boolean", "bool", "pg_catalog.bool":
71-
return "bool"
72-
case "json", "pg_catalog.json":
73-
return "json"
74-
case "jsonb", "pg_catalog.jsonb":
75-
return "jsonb"
76-
case "bytea", "blob", "pg_catalog.bytea":
77-
return "bytes"
78-
case "date":
79-
return "date"
80-
case "pg_catalog.time":
81-
return "time"
82-
case "pg_catalog.timetz":
83-
return "timetz"
84-
case "pg_catalog.timestamp", "timestamp":
85-
return "timestamp"
86-
case "pg_catalog.timestamptz", "timestamptz":
87-
return "timestamptz"
88-
case "text", "pg_catalog.varchar", "pg_catalog.bpchar",
89-
"string", "citext", "name",
90-
"ltree", "lquery", "ltxtquery":
91-
return "text"
92-
case "uuid":
93-
return "uuid"
94-
case "inet":
95-
return "inet"
96-
case "cidr":
97-
return "cidr"
98-
case "macaddr", "macaddr8":
99-
return "macaddr"
100-
case "bit", "varbit", "pg_catalog.bit", "pg_catalog.varbit":
101-
return "bits"
102-
case "hstore":
103-
return "hstore"
104-
case "vector":
105-
return "vector"
106-
default:
107-
return dt
108-
}
109-
}
110-
11142
func makeByte(s string) byte {
11243
var b byte
11344
if s == "" {

0 commit comments

Comments
 (0)