Example #1
0
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)
}
Example #2
0
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)
}
Example #3
0
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)
}