<commit_before>package org.basex.query.func.sql;

import static org.basex.query.QueryError.*;
import static org.basex.query.QueryText.*;
import static org.basex.util.Token.*;

import java.sql.*;

import org.basex.query.*;
import org.basex.query.iter.*;
import org.basex.query.value.item.*;
import org.basex.query.value.node.*;
import org.basex.query.value.type.*;
import org.basex.util.*;

/**
 * Functions on relational databases.
 *
 * @author BaseX Team 2005-18, BSD License
 * @author <NAME>
 */
public final class SqlExecutePrepared extends SqlExecute {
  /** QName. */
  private static final QNm Q_PARAMETERS = new QNm(SQL_PREFIX, "parameters", SQL_URI);
  /** QName. */
  private static final QNm Q_PARAMETER = new QNm(SQL_PREFIX, "parameter", SQL_URI);

  /** Type int. */
  private static final byte[] INT = AtomType.INT.string();
  /** Type string. */
  private static final byte[] STRING = AtomType.STR.string();
  /** Type boolean. */
  private static final byte[] BOOL = AtomType.BLN.string();
  /** Type date. */
  private static final byte[] DATE = AtomType.DAT.string();
  /** Type double. */
  private static final byte[] DOUBLE = AtomType.DBL.string();
  /** Type float. */
  private static final byte[] FLOAT = AtomType.FLT.string();
  /** Type short. */
  private static final byte[] SHORT = AtomType.SHR.string();
  /** Type time. */
  private static final byte[] TIME = AtomType.TIM.string();
  /** Type timestamp. */
  private static final byte[] TIMESTAMP = token("timestamp");
  /** Type xml. */
  private static final byte[] SQLXML = token("sqlxml");

  /** Attribute "type" of <sql:parameter/>. */
  private static final byte[] TYPE = token("type");
  /** Attribute "null" of <sql:parameter/>. */
  private static final byte[] NULL = token("null");

  @Override
  public Iter iter(final QueryContext qc) throws QueryException {
    checkCreate(qc);

    final PreparedStatement stmt = prepared(qc);
    long n = 0;
    ANode params = null;
    if(exprs.length > 1) {
      params = toElem(exprs[1], qc);
      if(!params.qname().eq(Q_PARAMETERS)) throw INVALIDOPTION_X.get(info, params.qname().local());
      n = countParams(params);
    }
    final StatementOptions options = toOptions(2, new StatementOptions(), qc);

    try {
      stmt.setQueryTimeout(options.get(StatementOptions.TIMEOUT));
      try {
        // Check if number of parameters equals number of place holders
        final int ph = stmt.getParameterMetaData().getParameterCount();
        if(n != ph) throw SQL_PARAMETERS_X_X.get(info, n, ph);
      } catch(final SQLSyntaxErrorException ex) {
        // Oracle will fail receiving ParameterMetaData on update statement ()
        // stackoverflow.com/questions/30666622/apache-dbutils-changing-column-name-in-update-sql
        // disable all oracle exceptions (found beside ORA-00904 ORA-00936 also)
        if(!ex.getMessage().startsWith("ORA-")) throw ex;
      }
      if(params != null) setParameters(params.children(), stmt);
      // If execute returns false, statement was updating: return number of updated rows
      return iter(stmt, false, stmt.execute());
    } catch(final SQLException ex) {
      throw SQL_ERROR_X.get(info, ex);
    }
  }

  /**
   * Counts the numbers of <sql:parameter/> elements.
   * @param params element <sql:parameter/>
   * @return number of parameters
   */
  private static long countParams(final ANode params) {
    final BasicNodeIter iter = params.children();
    long size = iter.size();
    if(size == -1) do ++size;
    while(iter.next() != null);
    return size;
  }

  /**
   * Sets the parameters of a prepared statement.
   * @param params parameters
   * @param stmt prepared statement
   * @throws QueryException query exception
   */
  private void setParameters(final BasicNodeIter params, final PreparedStatement stmt)
      throws QueryException {

    int i = 0;
    for(ANode next; (next = params.next()) != null;) {
      // Check name
      if(!next.qname().eq(Q_PARAMETER)) throw INVALIDOPTION_X.get(info, next.qname().local());
      final BasicNodeIter attrs = next.attributes();
      byte[] paramType = null;
      boolean isNull = false;
      for(ANode attr; (attr = attrs.next()) != null;) {
        // attribute "type"
        if(eq(attr.name(), TYPE)) paramType = attr.string();
        // attribute "null"<commit_msg>[MOD] removed parameter count-check on sql:execute-prepared (#1537)<commit_after>package org.basex.query.func.sql;

import static org.basex.query.QueryError.*;
import static org.basex.query.QueryText.*;
import static org.basex.util.Token.*;

import java.sql.*;

import org.basex.query.*;
import org.basex.query.iter.*;
import org.basex.query.value.item.*;
import org.basex.query.value.node.*;
import org.basex.query.value.type.*;
import org.basex.util.*;

/**
 * Functions on relational databases.
 *
 * @author BaseX Team 2005-18, BSD License
 * @author <NAME>
 */
public final class SqlExecutePrepared extends SqlExecute {
  /** QName. */
  private static final QNm Q_PARAMETERS = new QNm(SQL_PREFIX, "parameters", SQL_URI);
  /** QName. */
  private static final QNm Q_PARAMETER = new QNm(SQL_PREFIX, "parameter", SQL_URI);

  /** Type int. */
  private static final byte[] INT = AtomType.INT.string();
  /** Type string. */
  private static final byte[] STRING = AtomType.STR.string();
  /** Type boolean. */
  private static final byte[] BOOL = AtomType.BLN.string();
  /** Type date. */
  private static final byte[] DATE = AtomType.DAT.string();
  /** Type double. */
  private static final byte[] DOUBLE = AtomType.DBL.string();
  /** Type float. */
  private static final byte[] FLOAT = AtomType.FLT.string();
  /** Type short. */
  private static final byte[] SHORT = AtomType.SHR.string();
  /** Type time. */
  private static final byte[] TIME = AtomType.TIM.string();
  /** Type timestamp. */
  private static final byte[] TIMESTAMP = token("timestamp");
  /** Type xml. */
  private static final byte[] SQLXML = token("sqlxml");

  /** Attribute "type" of <sql:parameter/>. */
  private static final byte[] TYPE = token("type");
  /** Attribute "null" of <sql:parameter/>. */
  private static final byte[] NULL = token("null");

  @Override
  public Iter iter(final QueryContext qc) throws QueryException {
    checkCreate(qc);

    final PreparedStatement stmt = prepared(qc);
    ANode params = null;
    if(exprs.length > 1) {
      params = toElem(exprs[1], qc);
      if(!params.qname().eq(Q_PARAMETERS)) throw INVALIDOPTION_X.get(info, params.qname().local());
    }
    final StatementOptions options = toOptions(2, new StatementOptions(), qc);

    try {
      stmt.setQueryTimeout(options.get(StatementOptions.TIMEOUT));
      if(params != null) setParameters(params.children(), stmt);
      // If execute returns false, statement was updating: return number of updated rows
      return iter(stmt, false, stmt.execute());
    } catch(final QueryException ex) {
      // already handled
      throw ex;
    } catch(final Exception ex) {
      // assume other then SQLException related to SQL Processing also
      // Eg. java.lang.ArrayIndexOutOfBoundsException in case of SQLite
      throw SQL_ERROR_X.get(info, ex);
    }
  }

  /**
   * Sets the parameters of a prepared statement.
   * @param params parameters
   * @param stmt prepared statement
   * @throws QueryException query exception
   */
  private void setParameters(final BasicNodeIter params, final PreparedStatement stmt)
      throws QueryException {

    int i = 0;
    for(ANode next; (next = params.next()) != null;) {
      // Check name
      if(!next.qname().eq(Q_PARAMETER)) throw INVALIDOPTION_X.get(info, next.qname().local());
      final BasicNodeIter attrs = next.attributes();
      byte[] paramType = null;
      boolean isNull = false;
      for(ANode attr; (attr = attrs.next()) != null;) {
        // attribute "type"
        if(eq(attr.name(), TYPE)) paramType = attr.string();
        // attribute "null"
{'fraction_non_alphanumeric': 0.1402538645218047, 'fraction_numerical': 0.005529722257132085, 'mean_word_length': 3.271604938271605, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 4}, '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': '7489417', 'n_tokens_mistral': 2421, 'n_tokens_neox': 2376, 'n_words': 688}