func (c *v3Conn) handleParse(buf *readBuffer) error { name, err := buf.getString() if err != nil { return err } if _, ok := c.parsed[name]; ok && name != "" { return util.Errorf("prepared statement %q already exists", name) } query, err := buf.getString() if err != nil { return err } pq := parsedQuery{query: query} numTypes, err := buf.getInt16() if err != nil { return err } pq.types = make([]oid.Oid, numTypes) for i := int16(0); i < numTypes; i++ { typ, err := buf.getInt32() if err != nil { return err } pq.types[i] = oid.Oid(typ) } c.parsed[name] = pq c.writeBuf.initMsg(serverMsgParseComplete) return c.writeBuf.finishMsg(c.wr) }
func (b *readBuf) oid() (n oid.Oid) { n = oid.Oid(binary.BigEndian.Uint32(*b)) *b = (*b)[4:] return }
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) } stmt, err := parser.ParseOneTraditional(query) if err != nil { return c.sendError(err.Error()) } 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 } if err := parser.InferArgs(stmt, args); err != nil { return c.sendError(err.Error()) } pq := preparedStatement{ query: query, inTypes: make([]oid.Oid, len(args)), portalNames: make(map[string]struct{}), } 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)) } // 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-1] != 0 { continue } id, ok := datumToOid[v] if !ok { return c.sendError(fmt.Sprintf("unknown datum type: %s", v.Type())) } pq.inTypes[i-1] = id } cols, pErr := c.executor.StatementResult(c.opts.user, stmt, args) if pErr != nil { return c.sendError(pErr.GoError().Error()) } pq.columns = cols c.preparedStatements[name] = pq 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 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 := sql.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 := sql.DatumToOid(t) if !ok { return c.sendInternalError(fmt.Sprintf("unknown datum type: %s", t)) } 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) }