func (c *v3Conn) handleParse(ctx context.Context, buf *readBuffer) error { name, err := buf.getString() if err != nil { return err } // The unnamed prepared statement can be freely overwritten. if name != "" { if c.session.PreparedStatements.Exists(name) { return c.sendInternalError(fmt.Sprintf("prepared statement %q already exists", name)) } } query, err := buf.getString() if err != nil { return err } // The client may provide type information for (some of) the // placeholders. Read this first. numQArgTypes, err := buf.getUint16() if err != nil { return err } inTypeHints := make([]oid.Oid, numQArgTypes) for i := range inTypeHints { typ, err := buf.getUint32() if err != nil { return err } inTypeHints[i] = oid.Oid(typ) } // Prepare the mapping of SQL placeholder names to // types. Pre-populate it with the type hints received from the // client, if any. sqlTypeHints := make(parser.PlaceholderTypes) for i, t := range inTypeHints { if t == 0 { continue } v, ok := oidToDatum[t] if !ok { return c.sendInternalError(fmt.Sprintf("unknown oid type: %v", t)) } sqlTypeHints[fmt.Sprint(i+1)] = v } // Create the new PreparedStatement in the connection's Session. stmt, err := c.session.PreparedStatements.New(ctx, c.executor, name, query, sqlTypeHints) if err != nil { return c.sendError(err) } // Convert the inferred SQL types back to an array of pgwire Oids. inTypes := make([]oid.Oid, 0, len(stmt.SQLTypes)) for k, t := range stmt.SQLTypes { i, err := strconv.Atoi(k) if err != nil || i < 1 { return c.sendInternalError(fmt.Sprintf("invalid placeholder name: $%s", k)) } // Placeholder names are 1-indexed; the arrays in the protocol are // 0-indexed. i-- // Grow inTypes to be at least as large as i. Prepopulate all // slots with the hints provided, if any. for j := len(inTypes); j <= i; j++ { inTypes = append(inTypes, 0) if j < len(inTypeHints) { inTypes[j] = inTypeHints[j] } } // OID to Datum is not a 1-1 mapping (for example, int4 and int8 // both map to TypeInt), so we need to maintain the types sent by // the client. if inTypes[i] != 0 { continue } id, ok := datumToOid[reflect.TypeOf(t)] if !ok { return c.sendInternalError(fmt.Sprintf("unknown datum type: %s", t.Type())) } inTypes[i] = id } for i, t := range inTypes { if t == 0 { return c.sendInternalError( fmt.Sprintf("could not determine data type of placeholder $%d", i+1)) } } // Attach pgwire-specific metadata to the PreparedStatement. stmt.ProtocolMeta = preparedStatementMeta{inTypes: inTypes} c.writeBuf.initMsg(serverMsgParseComplete) return c.writeBuf.finishMsg(c.wr) }
func (c *v3Conn) handleParse(ctx context.Context, buf *readBuffer) error { name, err := buf.getString() if err != nil { return err } // The unnamed prepared statement can be freely overwritten. if name != "" { if _, ok := c.preparedStatements[name]; ok { return c.sendInternalError(fmt.Sprintf("prepared statement %q already exists", name)) } } query, err := buf.getString() if err != nil { return err } numParamTypes, err := buf.getInt16() if err != nil { return err } inTypeHints := make([]oid.Oid, numParamTypes) for i := range inTypeHints { typ, err := buf.getInt32() if err != nil { return err } inTypeHints[i] = oid.Oid(typ) } args := make(parser.MapArgs) for i, t := range inTypeHints { if t == 0 { continue } v, ok := oidToDatum[t] if !ok { return c.sendInternalError(fmt.Sprintf("unknown oid type: %v", t)) } args[fmt.Sprint(i+1)] = v } cols, err := c.executor.Prepare(ctx, query, c.session, args) if err != nil { return c.sendError(err) } pq := preparedStatement{ query: query, inTypes: make([]oid.Oid, 0, len(args)), portalNames: make(map[string]struct{}), columns: cols, } for k, v := range args { i, err := strconv.Atoi(k) if err != nil { return c.sendInternalError(fmt.Sprintf("non-integer parameter: %s", k)) } // ValArgs are 1-indexed, pq.inTypes are 0-indexed. i-- if i < 0 { return c.sendInternalError(fmt.Sprintf("there is no parameter $%s", k)) } // Grow pq.inTypes to be at least as large as i. for j := len(pq.inTypes); j <= i; j++ { pq.inTypes = append(pq.inTypes, 0) if j < len(inTypeHints) { pq.inTypes[j] = inTypeHints[j] } } // OID to Datum is not a 1-1 mapping (for example, int4 and int8 both map // to DummyInt), so we need to maintain the types sent by the client. if pq.inTypes[i] != 0 { continue } id, ok := datumToOid[reflect.TypeOf(v)] if !ok { return c.sendInternalError(fmt.Sprintf("unknown datum type: %s", v.Type())) } pq.inTypes[i] = id } for i := range pq.inTypes { if pq.inTypes[i] == 0 { return c.sendInternalError( fmt.Sprintf("could not determine data type of parameter $%d", i+1)) } } c.preparedStatements[name] = pq c.writeBuf.initMsg(serverMsgParseComplete) return c.writeBuf.finishMsg(c.wr) }
func (c *v3Conn) handleParse(buf *readBuffer) error { name, err := buf.getString() if err != nil { return err } // The unnamed prepared statement can be freely overwritten. if name != "" { if _, ok := c.preparedStatements[name]; ok { return c.sendError(fmt.Sprintf("prepared statement %q already exists", name)) } } query, err := buf.getString() if err != nil { return err } numParamTypes, err := buf.getInt16() if err != nil { return err } inTypeHints := make([]oid.Oid, numParamTypes) for i := range inTypeHints { typ, err := buf.getInt32() if err != nil { return err } inTypeHints[i] = oid.Oid(typ) } args := make(parser.MapArgs) for i, t := range inTypeHints { if t == 0 { continue } v, ok := oidToDatum[t] if !ok { return c.sendError(fmt.Sprintf("unknown oid type: %v", t)) } args[fmt.Sprint(i+1)] = v } cols, pErr := c.executor.Prepare(c.opts.user, query, &c.session, args) if pErr != nil { return c.sendError(pErr.String()) } pq := preparedStatement{ query: query, inTypes: make([]oid.Oid, len(args)), portalNames: make(map[string]struct{}), columns: cols, } copy(pq.inTypes, inTypeHints) for k, v := range args { i, err := strconv.Atoi(k) if err != nil { return c.sendError(fmt.Sprintf("non-integer parameter name: %s", k)) } // ValArgs are 1-indexed, pq.inTypes are 0-indexed. i-- if len(pq.inTypes) <= i { return c.sendError("internal error: leftover valargs") } // OID to Datum is not a 1-1 mapping (for example, int4 and int8 both map // to DummyInt), so we need to maintain the types sent by the client. if pq.inTypes[i] != 0 { continue } id, ok := datumToOid[reflect.TypeOf(v)] if !ok { return c.sendError(fmt.Sprintf("unknown datum type: %s", v.Type())) } pq.inTypes[i] = id } c.preparedStatements[name] = pq c.writeBuf.initMsg(serverMsgParseComplete) return c.writeBuf.finishMsg(c.wr) }