Skip to content

Commit f5bf7f4

Browse files
Merge pull request #2562 from dolthub/nathan/paramChanges
Support using same parameter multiple times
2 parents 643cb6c + 64684c7 commit f5bf7f4

2 files changed

Lines changed: 71 additions & 9 deletions

File tree

server/connection_data.go

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"github.com/dolthub/doltgresql/core/dataloader"
3131
"github.com/dolthub/doltgresql/core/id"
3232
pgexprs "github.com/dolthub/doltgresql/server/expression"
33+
"github.com/dolthub/doltgresql/server/functions/framework"
3334
"github.com/dolthub/doltgresql/server/node"
3435
pgtypes "github.com/dolthub/doltgresql/server/types"
3536
)
@@ -147,9 +148,8 @@ func extractBindVarTypes(queryPlan sql.Node) ([]uint32, error) {
147148
return false
148149
}
149150
}
150-
if _, ok := types[e.Name]; ok {
151-
// sanity check
152-
err = errors.Errorf("double placeholder given for %s", e.Name)
151+
if existingOid, ok := types[e.Name]; ok {
152+
err = checkCompatibleTypes(existingOid, typOid, e.Name)
153153
}
154154
types[e.Name] = typOid
155155
case *pgexprs.ExplicitCast:
@@ -164,9 +164,8 @@ func extractBindVarTypes(queryPlan sql.Node) ([]uint32, error) {
164164
return false
165165
}
166166
}
167-
if _, ok = types[bindVar.Name]; ok {
168-
// sanity check
169-
err = errors.Errorf("double placeholder given for %s", bindVar.Name)
167+
if existingOid, ok := types[bindVar.Name]; ok {
168+
err = checkCompatibleTypes(existingOid, typOid, bindVar.Name)
170169
}
171170
types[bindVar.Name] = typOid
172171
return false
@@ -180,9 +179,8 @@ func extractBindVarTypes(queryPlan sql.Node) ([]uint32, error) {
180179
err = errors.Errorf("could not determine OID for placeholder %s: %e", bindVar.Name, err)
181180
return false
182181
}
183-
if _, ok = types[bindVar.Name]; ok {
184-
// sanity check
185-
err = errors.Errorf("double placeholder given for %s", bindVar.Name)
182+
if existingOid, ok := types[bindVar.Name]; ok {
183+
err = checkCompatibleTypes(existingOid, typOid, bindVar.Name)
186184
}
187185
types[bindVar.Name] = typOid
188186
return false
@@ -225,3 +223,14 @@ func VitessTypeToObjectID(typ sql.Type) (uint32, error) {
225223
}
226224
return id.Cache().ToOID(doltgresType.ID.AsId()), nil
227225
}
226+
227+
// checkCompatibleTypes checks if multiple types for which a parameter are used are compatible.
228+
func checkCompatibleTypes(existingOid, newOid uint32, newName string) error {
229+
var err error
230+
existing := pgtypes.GetTypeByID(id.Type(id.Cache().ToInternal(existingOid)))
231+
newType := pgtypes.GetTypeByID(id.Type(id.Cache().ToInternal(newOid)))
232+
if _, _, err = framework.FindCommonType([]*pgtypes.DoltgresType{existing, newType}); err != nil {
233+
err = errors.Errorf("parameter %s is used for incompatible types: %s and %s", newName, existing.String(), newType.String())
234+
}
235+
return err
236+
}

testing/go/prepared_statement_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1294,6 +1294,59 @@ var preparedStatementTests = []ScriptTest{
12941294
},
12951295
},
12961296
},
1297+
{
1298+
Name: "Bind parameter to compatible different types",
1299+
SetUpScript: []string{
1300+
"CREATE TABLE text_test (id text, code varchar(10))",
1301+
"CREATE TABLE num_test(small int2, large int8, other float4)",
1302+
"INSERT INTO text_test values ('foo', 'bar'), ('bar', 'foo')",
1303+
"INSERT INTO num_test values (0,0,0), (1, 2, 1.5)",
1304+
},
1305+
Assertions: []ScriptTestAssertion{
1306+
{
1307+
Query: "SELECT * FROM text_test where id = any($1) and code = any($1)",
1308+
BindVars: []any{[]string{"foo", "bar"}},
1309+
Expected: []sql.Row{
1310+
{"foo", "bar"},
1311+
{"bar", "foo"},
1312+
},
1313+
},
1314+
{
1315+
Query: "SELECT * from num_test where small = $1 and large = $1 and other = $1",
1316+
BindVars: []any{0},
1317+
Expected: []sql.Row{
1318+
{0, 0, float64(0)},
1319+
},
1320+
},
1321+
{
1322+
Query: "SELECT * from num_test where small = $1::INTEGER and large = $1::INTEGER and other = $1::INTEGER",
1323+
BindVars: []any{0},
1324+
Expected: []sql.Row{
1325+
{0, 0, float64(0)},
1326+
},
1327+
},
1328+
{
1329+
Query: "SELECT * FROM num_test where small = $1 or other = $1",
1330+
BindVars: []any{1.5},
1331+
Expected: []sql.Row{
1332+
{1, 2, 1.5},
1333+
},
1334+
},
1335+
},
1336+
},
1337+
{
1338+
Name: "Cannot bind parameter to column with incompatible type",
1339+
SetUpScript: []string{
1340+
"CREATE TABLE text_test (fullname text, id int)",
1341+
},
1342+
Assertions: []ScriptTestAssertion{
1343+
{
1344+
Query: "SELECT * FROM text_test where fullname = $1 and id = $1",
1345+
BindVars: []any{1},
1346+
ExpectedErr: "parameter v1 is used for incompatible types: text and integer",
1347+
},
1348+
},
1349+
},
12971350
}
12981351

12991352
var pgCatalogTests = []ScriptTest{

0 commit comments

Comments
 (0)