<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to you under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package avatica

import (
	"context"
	"database/sql/driver"
	"errors"
	"math"
	"sync"
	"time"

	"github.com/apache/calcite-avatica-go/v5/message"
)

type stmt struct {
	statementID  uint32
	conn         *conn
	parameters   []*message.AvaticaParameter
	handle       message.StatementHandle
	batchUpdates []*message.UpdateBatch
	sync.Mutex
}

// Close closes a statement
func (s *stmt) Close() error {

	if s.conn.connectionId == "" {
		return driver.ErrBadConn
	}

	if s.conn.config.batching {
		_, err := s.conn.httpClient.post(context.Background(), &message.ExecuteBatchRequest{
			ConnectionId: s.conn.connectionId,
			StatementId:  s.statementID,
			Updates:      s.batchUpdates,
		})
		if err != nil {
			return s.conn.avaticaErrorToResponseErrorOrError(err)
		}
	}

	_, err := s.conn.httpClient.post(context.Background(), &message.CloseStatementRequest{
		ConnectionId: s.conn.connectionId,
		StatementId:  s.statementID,
	})

	if err != nil {
		return s.conn.avaticaErrorToResponseErrorOrError(err)
	}

	return nil
}

// NumInput returns the number of placeholder parameters.
//
// If NumInput returns >= 0, the sql package will sanity check
// argument counts from callers and return errors to the caller
// before the statement's Exec or Query methods are called.
//
// NumInput may also return -1, if the driver doesn't know
// its number of placeholders. In that case, the sql package
// will not sanity check Exec or Query argument counts.
func (s *stmt) NumInput() int {
	return len(s.parameters)
}

// Exec executes a query that doesn't return rows, such
// as an INSERT or UPDATE.
func (s *stmt) Exec(args []driver.Value) (driver.Result, error) {
	list := driverValueToNamedValue(args)
	return s.exec(context.Background(), list)
}

func (s *stmt) exec(ctx context.Context, args []namedValue) (driver.Result, error) {

	if s.conn.connectionId == "" {
		return nil, driver.ErrBadConn
	}

	values := s.parametersToTypedValues(args)

	if s.conn.config.batching {
		s.Lock()
		defer s.Unlock()

		s.batchUpdates = append(s.batchUpdates, &message.UpdateBatch{
			ParameterValues: values,
		})
		return &result{
			affectedRows: -1,
		}, nil
	}

	msg := &message.ExecuteRequest{
		StatementHandle:    &s.handle,
		ParameterValues:    values,
		FirstFrameMaxSize:  s.conn.config.frameMaxSize,
		HasParameterValues: true,
	}

	if s.conn.config.frameMaxSize <= -1 {
		msg.DeprecatedFirstFrameMaxSize = math.MaxInt64
	} else {
		msg.DeprecatedFirstFrameMaxSize = uint64(s.conn.config.frameMaxSize)
	}

	res, err := s.conn.httpClient.post(ctx, msg)

	if err != nil {
		return nil, s.conn.avaticaErrorToResponseErrorOrError(err)
	}

	results := res.(*message.ExecuteResponse).Results

	if len(results) <= 0 {
		return nil, errors.New("empty ResultSet in ExecuteResponse")
	}

	// Currently there is only 1 ResultSet per response
	changed := int64(results[0].UpdateCount)

	return &result{
		affectedRows: changed,
	}, nil
}

// Query executes a query that may return rows, such as a
// SELECT.
func (s *stmt) Query(args []driver.Value) (driver.Rows, error) {
	list := driverValueToNamedValue(args)
	return s.query(context.Background(), list)
}

func (s *stmt) query(ctx context.Context, args []namedValue) (driver.Rows, error) {
	if s.conn.connectionId == "" {
		return nil, driver.ErrBadConn
	}

	msg := &message.ExecuteRequest{
		StatementHandle:    &s.handle,
		ParameterValues:    s.parametersToTypedValues(args),
		FirstFrameMaxSize:  s.conn.config.frameMaxSize,
		HasParameterValues: true,
	}

	if s.conn.config.frameMaxSize <= -1 {
		msg.DeprecatedFirstFrameMaxSize = math.MaxInt64
	} else {
		msg.DeprecatedFirstFrameMaxSize = uint64(s.conn.config.frameMaxSize)
	}

	res, err := s.conn.httpClient.post(ctx, msg)

	if err != nil {
		return nil, s.conn.avaticaErrorToResponseErrorOrError(err)
	}<commit_msg>[CALCITE-5320] Switch from deprecated_first_frame_max_size to first_frame_max_size protobuf member for setting the first frame max size<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to you under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package avatica

import (
	"context"
	"database/sql/driver"
	"errors"
	"math"
	"sync"
	"time"

	"github.com/apache/calcite-avatica-go/v5/message"
)

type stmt struct {
	statementID  uint32
	conn         *conn
	parameters   []*message.AvaticaParameter
	handle       message.StatementHandle
	batchUpdates []*message.UpdateBatch
	sync.Mutex
}

// Close closes a statement
func (s *stmt) Close() error {

	if s.conn.connectionId == "" {
		return driver.ErrBadConn
	}

	if s.conn.config.batching {
		_, err := s.conn.httpClient.post(context.Background(), &message.ExecuteBatchRequest{
			ConnectionId: s.conn.connectionId,
			StatementId:  s.statementID,
			Updates:      s.batchUpdates,
		})
		if err != nil {
			return s.conn.avaticaErrorToResponseErrorOrError(err)
		}
	}

	_, err := s.conn.httpClient.post(context.Background(), &message.CloseStatementRequest{
		ConnectionId: s.conn.connectionId,
		StatementId:  s.statementID,
	})

	if err != nil {
		return s.conn.avaticaErrorToResponseErrorOrError(err)
	}

	return nil
}

// NumInput returns the number of placeholder parameters.
//
// If NumInput returns >= 0, the sql package will sanity check
// argument counts from callers and return errors to the caller
// before the statement's Exec or Query methods are called.
//
// NumInput may also return -1, if the driver doesn't know
// its number of placeholders. In that case, the sql package
// will not sanity check Exec or Query argument counts.
func (s *stmt) NumInput() int {
	return len(s.parameters)
}

// Exec executes a query that doesn't return rows, such
// as an INSERT or UPDATE.
func (s *stmt) Exec(args []driver.Value) (driver.Result, error) {
	list := driverValueToNamedValue(args)
	return s.exec(context.Background(), list)
}

func (s *stmt) exec(ctx context.Context, args []namedValue) (driver.Result, error) {

	if s.conn.connectionId == "" {
		return nil, driver.ErrBadConn
	}

	values := s.parametersToTypedValues(args)

	if s.conn.config.batching {
		s.Lock()
		defer s.Unlock()

		s.batchUpdates = append(s.batchUpdates, &message.UpdateBatch{
			ParameterValues: values,
		})
		return &result{
			affectedRows: -1,
		}, nil
	}

	msg := &message.ExecuteRequest{
		StatementHandle:    &s.handle,
		ParameterValues:    values,
		FirstFrameMaxSize:  s.conn.config.frameMaxSize,
		HasParameterValues: true,
	}

	if s.conn.config.frameMaxSize <= -1 {
		msg.FirstFrameMaxSize = math.MaxInt32
	} else {
		msg.FirstFrameMaxSize = s.conn.config.frameMaxSize
	}

	res, err := s.conn.httpClient.post(ctx, msg)

	if err != nil {
		return nil, s.conn.avaticaErrorToResponseErrorOrError(err)
	}

	results := res.(*message.ExecuteResponse).Results

	if len(results) <= 0 {
		return nil, errors.New("empty ResultSet in ExecuteResponse")
	}

	// Currently there is only 1 ResultSet per response
	changed := int64(results[0].UpdateCount)

	return &result{
		affectedRows: changed,
	}, nil
}

// Query executes a query that may return rows, such as a
// SELECT.
func (s *stmt) Query(args []driver.Value) (driver.Rows, error) {
	list := driverValueToNamedValue(args)
	return s.query(context.Background(), list)
}

func (s *stmt) query(ctx context.Context, args []namedValue) (driver.Rows, error) {
	if s.conn.connectionId == "" {
		return nil, driver.ErrBadConn
	}

	msg := &message.ExecuteRequest{
		StatementHandle:    &s.handle,
		ParameterValues:    s.parametersToTypedValues(args),
		FirstFrameMaxSize:  s.conn.config.frameMaxSize,
		HasParameterValues: true,
	}

	if s.conn.config.frameMaxSize <= -1 {
		msg.FirstFrameMaxSize = math.MaxInt32
	} else {
		msg.FirstFrameMaxSize = s.conn.config.frameMaxSize
	}

	res, err := s.conn.httpClient.post(ctx, msg)

	if err != nil {
		return nil, s.conn.avaticaErrorToResponseErrorOrError(err)
	}
{'fraction_non_alphanumeric': 0.11016949152542373, 'fraction_numerical': 0.005363655867839519, 'mean_word_length': 3.9537725823591923, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1245958', 'n_tokens_mistral': 3112, 'n_tokens_neox': 2916, 'n_words': 872}