All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.starschema.clouddb.jdbc.BQStatementRoot Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (c) 2015, STARSCHEMA LTD. All rights reserved.
 *
 * 

Redistribution and use in source and binary forms, with or without modification, are permitted * provided that the following conditions are met: * *

1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the * above copyright notice, this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * *

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *

This class is the parent of BQStatement and BQPreparedStatement */ package net.starschema.clouddb.jdbc; import com.google.api.services.bigquery.model.*; import java.io.IOException; import java.math.BigInteger; import java.sql.*; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class partially implements java.sql.Statement, and java.sql.PreparedStatement * * @author Horváth Attila * @author Balazs Gunics */ public abstract class BQStatementRoot { /** Reference to store the ran Query run by Executequery or Execute */ ResultSet resset = null; /** String containing the context of the Project */ String projectId = null; Logger logger = LoggerFactory.getLogger(BQStatementRoot.class); /** Variable that stores the closed state of the statement */ boolean closed = false; /** Reference for the Connection that created this Statement object */ BQConnection connection; /** Variable that stores the set query timeout */ int querytimeout = Integer.MAX_VALUE / 1000 - 1; /** Instance of log4j.Logger */ /** Variable stores the time an execute is made */ long starttime = 0; /** Variable that stores the max row number which can be stored in the resultset */ int resultMaxRowCount = Integer.MAX_VALUE - 1; /** Variable to Store EscapeProc state */ boolean EscapeProc = false; /** These Variables contain information about the type of resultset this statement creates */ int resultSetType; int resultSetConcurrency; /** to be used with setMaxFieldSize */ private int maxFieldSize = 0; protected AtomicReference mostRecentJobReference = new AtomicReference<>(); /** * * *

Implementation Details:

* *
* Not implemented yet. * * @throws BQSQLException */ public void addBatch(String arg0) throws SQLException { throw new BQSQLException("Not implemented." + "addBatch(string)"); } /** * * *

Implementation Details:

* *
* Not implemented yet. * * @throws BQSQLFeatureNotSupportedException */ public void cancel() throws SQLException { throw new BQSQLFeatureNotSupportedException("cancel()"); } /** * * *

Implementation Details:

* *
* Not implemented yet. * * @throws BQSQLException */ public void clearBatch() throws SQLException { throw new BQSQLException("Not implemented." + "clearBatch()"); } /** * * *

Implementation Details:

* *
* Not implemented yet. * * @throws BQSQLException */ public void clearWarnings() throws SQLException { throw new BQSQLException("Not implemented." + "clearWarnings()"); } /** * * *

Implementation Details:

* *
* Only sets closed boolean to true */ public void close() throws SQLException { this.closed = true; if (this.resset != null) { this.resset.close(); } } /** * * *

Implementation Details:

* *
* Wrapper for execute; calls execute(arg0, false). Does NOT bypass maxBillingBytes. */ public boolean execute(String sql) throws SQLException { return this.execute(sql, false); } /** * * *

Implementation Details:

* *
* Executes the given SQL statement on BigQuery (note: it returns only 1 resultset). This function * directly uses executeQuery function. It also allows bypassing maxBillingBytes for PDTs. */ public boolean execute(String sql, boolean unlimitedBillingBytes) throws SQLException { if (this.isClosed()) { throw new BQSQLException("This Statement is Closed"); } this.resset = this.executeQuery(sql, unlimitedBillingBytes); this.logger.info("Executing Query: " + sql); if (this.resset != null) { return true; } else { return false; } } /** * * *

Implementation Details:

* *
* Not implemented yet. * * @throws BQSQLFeatureNotSupportedException */ public boolean execute(String sql, int autoGeneratedKeys) throws SQLException { throw new BQSQLFeatureNotSupportedException("execute(String, int)"); } /** * * *

Implementation Details:

* *
* Not implemented yet. * * @throws BQSQLFeatureNotSupportedException */ public boolean execute(String sql, int[] columnIndexes) throws SQLException { throw new BQSQLFeatureNotSupportedException("execute(string,int[])"); } /** * * *

Implementation Details:

* *
* Not implemented yet. * * @throws BQSQLFeatureNotSupportedException */ public boolean execute(String sql, String[] columnNames) throws SQLException { throw new BQSQLFeatureNotSupportedException("execute(string,string[])"); } /** * * *

Implementation Details:

* *
* Not implemented yet. * * @throws BQSQLException */ public int[] executeBatch() throws SQLException { throw new BQSQLException("Not implemented." + "executeBatch()"); } /** * Execute DML (DELETE, INSERT, UPDATE). If you want to perform a SELECT or other DDL look at * {@link #execute(String)} */ private int executeDML(String sql) throws SQLException { if (this.isClosed()) { throw new BQSQLException("This Statement is Closed"); } if (connection.getUseLegacySql()) { throw new BQSQLFeatureNotSupportedException("Legacy SQL does not support DML"); } this.starttime = System.currentTimeMillis(); final Job referencedJob; try { QueryResponse qr = BQSupportFuncts.runSyncQuery( this.connection.getBigquery(), projectId, sql, connection.getDataSet(), connection.getDataSetProjectId(), this.connection.getUseLegacySql(), this.connection.getMaxBillingBytes(), (long) querytimeout * 1000, (long) getMaxRows(), this.getAllLabels(), this.connection.getUseQueryCache(), this.connection.getJobCreationMode()); this.mostRecentJobReference.set(qr.getJobReference()); if (defaultValueIfNull(qr.getJobComplete(), false)) { // I hope they don't insert more than 2^32-1 :) return Math.toIntExact(defaultValueIfNull(qr.getNumDmlAffectedRows(), 0L)); } referencedJob = this.connection .getBigquery() .jobs() .get(projectId, qr.getJobReference().getJobId()) .execute(); } catch (IOException e) { throw new BQSQLException("Something went wrong with the query: " + sql, e); } try { do { if (BQSupportFuncts.getQueryState(referencedJob, this.connection.getBigquery(), projectId) .equals("DONE")) { return Math.toIntExact( BQSupportFuncts.getQueryResults( this.connection.getBigquery(), projectId, referencedJob) .getNumDmlAffectedRows()); } Thread.sleep(500); this.logger.debug("slept for 500" + "ms, querytimeout is: " + this.querytimeout + "s"); } while (System.currentTimeMillis() - this.starttime <= (long) this.querytimeout * 1000); } catch (IOException | InterruptedException e) { throw new BQSQLException("Something went wrong with the query: " + sql, e); } // here we should kill/stop the running job, but bigquery doesn't // support that :( throw new BQSQLException("Query run took more than the specified timeout"); } public ResultSet executeQuery(String querySql, boolean unlimitedBillingBytes) throws SQLException { if (this.isClosed()) { throw new BQSQLException("This Statement is Closed"); } this.starttime = System.currentTimeMillis(); Job referencedJob; Long billingBytes = !unlimitedBillingBytes ? this.connection.getMaxBillingBytes() : null; boolean jobAlreadyCompleted = false; try { QueryResponse qr = BQSupportFuncts.runSyncQuery( this.connection.getBigquery(), projectId, querySql, connection.getDataSet(), connection.getDataSetProjectId(), this.connection.getUseLegacySql(), billingBytes, (long) querytimeout * 1000, (long) getMaxRows(), this.getAllLabels(), this.connection.getUseQueryCache(), this.connection.getJobCreationMode()); this.mostRecentJobReference.set(qr.getJobReference()); referencedJob = this.connection .getBigquery() .jobs() .get(projectId, qr.getJobReference().getJobId()) .execute(); if (defaultValueIfNull(qr.getJobComplete(), false)) { List rows = defaultValueIfNull(qr.getRows(), new ArrayList()); if (BigInteger.valueOf(rows.size()).equals(qr.getTotalRows())) { TableSchema schema = defaultValueIfNull(qr.getSchema(), new TableSchema()); String biEngineMode = null; List biEngineReasons = null; Optional biEngineStatisticsOptional = Optional.ofNullable(referencedJob) .map(Job::getStatistics) .map(JobStatistics::getQuery) .map(JobStatistics2::getBiEngineStatistics); if (biEngineStatisticsOptional.isPresent()) { BiEngineStatistics biEngineStatistics = biEngineStatisticsOptional.get(); biEngineMode = biEngineStatistics.getBiEngineMode(); biEngineReasons = biEngineStatistics.getBiEngineReasons(); } return new BQScrollableResultSet( rows, this, schema, qr.getTotalBytesProcessed(), qr.getCacheHit(), biEngineMode, biEngineReasons, referencedJob.getJobReference(), qr.getQueryId()); } jobAlreadyCompleted = true; } this.logger.info("Executing Query: " + querySql); } catch (IOException e) { throw new BQSQLException("Something went wrong with the query: " + querySql, e); } try { do { if (jobAlreadyCompleted || BQSupportFuncts.getQueryState( referencedJob, this.connection.getBigquery(), projectId) .equals("DONE")) { if (resultSetType == ResultSet.TYPE_SCROLL_INSENSITIVE) { return new BQScrollableResultSet( BQSupportFuncts.getQueryResults( this.connection.getBigquery(), projectId, referencedJob), this); } else { return new BQForwardOnlyResultSet( this.connection.getBigquery(), projectId, referencedJob, null, this); } } // Pause execution for half second before polling job status // again, to // reduce unnecessary calls to the BigQUery API and lower // overall // application bandwidth. Thread.sleep(500); this.logger.debug("slept for 500" + "ms, querytimeout is: " + this.querytimeout + "s"); } while (System.currentTimeMillis() - this.starttime <= (long) this.querytimeout * 1000); // it runs for a minimum of 1 time } catch (IOException e) { throw new BQSQLException("Something went wrong with the query: " + querySql, e); } catch (InterruptedException e) { e.printStackTrace(); } // here we should kill/stop the running job, but bigquery doesn't // support that :( throw new BQSQLException("Query run took more than the specified timeout"); } protected Map getAllLabels() { return this.connection.getLabels(); } private static T defaultValueIfNull(T value, T defaultValue) { return value == null ? defaultValue : value; } /** * Only supported for standard SQL (non-legacy) * * @throws BQSQLException */ public int executeUpdate(String sql) throws SQLException { return executeDML(sql); } /** * * *

Implementation Details:

* *
* Not implemented yet. * * @throws BQSQLFeatureNotSupportedException */ public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { throw new BQSQLFeatureNotSupportedException("executeUpdate(String,int)"); } /** * * *

Implementation Details:

* *
* Not implemented yet. * * @throws BQSQLFeatureNotSupportedException */ public int executeUpdate(String sql, int[] columnIndexes) throws SQLException { throw new BQSQLFeatureNotSupportedException("executeUpdate(string,int[])"); } /** * * *

Implementation Details:

* *
* Not implemented yet. * * @throws BQSQLFeatureNotSupportedException */ public int executeUpdate(String sql, String[] columnNames) throws SQLException { throw new BQSQLFeatureNotSupportedException("execute(update(string,string[])"); } /** * * *

Implementation Details:

* *
* Returns the connection that made the object. * * @returns connection */ public Connection getConnection() throws SQLException { return this.connection; } /** * * *

Implementation Details:

* *
* Fetch direction is unknown. * * @return FETCH_UNKNOWN */ public int getFetchDirection() throws SQLException { return ResultSet.FETCH_UNKNOWN; } /** * * *

Implementation Details:

* *
* Not implemented yet. * * @throws BQSQLException */ public int getFetchSize() throws SQLException { throw new BQSQLException("Not implemented." + "getFetchSize()"); } /** * * *

Implementation Details:

* *
* Not implemented yet. * * @throws BQSQLException */ public ResultSet getGeneratedKeys() throws SQLException { throw new BQSQLFeatureNotSupportedException("getGeneratedKeys()"); } /** * * *

Implementation Details:

* *
* Not implemented yet. * * @throws BQSQLException */ public int getMaxFieldSize() throws SQLException { return maxFieldSize; } /** * * *

Implementation Details:

* *
* We store it in an array which indexed through, int We could return Integer.MAX_VALUE too, but i * don't think we could get that much row. * * @return 0 - */ public int getMaxRows() throws SQLException { return this.resultMaxRowCount; } /** * * *

Implementation Details:

* *
* Not implemented yet, since Bigquery Does not support precompiled sql * * @throws BQSQLException */ public ResultSetMetaData getMetaData() throws SQLException { throw new BQSQLException(new SQLFeatureNotSupportedException("getMetaData()")); } /** * * *

Implementation Details:

* *
* Multiple result sets are not supported currently. * * @return false; */ public boolean getMoreResults() throws SQLException { return false; } /** * * *

Implementation Details:

* *
* Multiple result sets are not supported currently. we check that the result set is open, the * parameter is acceptable, and close our current resultset or throw a * FeatureNotSupportedException * * @param current - one of the following Statement constants indicating what should happen to * current ResultSet objects obtained using the method getResultSet: * Statement.CLOSE_CURRENT_RESULT, Statement.KEEP_CURRENT_RESULT, or * Statement.CLOSE_ALL_RESULTS * @throws BQSQLException */ public boolean getMoreResults(int current) throws SQLException { if (this.closed) { throw new BQSQLException("Statement is closed."); } if (current == Statement.CLOSE_CURRENT_RESULT || current == Statement.KEEP_CURRENT_RESULT || current == Statement.CLOSE_ALL_RESULTS) { if (BQDatabaseMetadata.multipleOpenResultsSupported && (current == Statement.KEEP_CURRENT_RESULT || current == Statement.CLOSE_ALL_RESULTS)) { throw new BQSQLFeatureNotSupportedException(); } // Statement.CLOSE_CURRENT_RESULT this.close(); return false; } else { throw new BQSQLException("Wrong parameter."); } } public int getQueryTimeout() throws SQLException { if (this.isClosed()) { throw new BQSQLException("This Statement is Closed"); } if (this.starttime == 0) { return 0; } if (this.querytimeout == Integer.MAX_VALUE) { return 0; } else { if (System.currentTimeMillis() - this.starttime > this.querytimeout) { throw new BQSQLException("Time is over"); } return this.querytimeout; } } /** * * *

Implementation Details:

* *
* Gives back reultset stored in resset */ public ResultSet getResultSet() throws SQLException { if (this.isClosed()) { throw new BQSQLException("This Statement is Closed"); } if (this.resset != null) { return this.resset; } else { return null; } } /** * * *

Implementation Details:

* *
* The driver is read only currently. * * @return CONCUR_READ_ONLY */ public int getResultSetConcurrency() throws SQLException { return ResultSet.CONCUR_READ_ONLY; } /** * * *

Implementation Details:

* *
* Read only mode, no commit. * * @return CLOSE_CURSORS_AT_COMMIT */ public int getResultSetHoldability() throws SQLException { return ResultSet.CLOSE_CURSORS_AT_COMMIT; // TODO } /** * * *

Implementation Details:

* *
* Updates and deletes not supported. * * @return TYPE_SCROLL_INSENSITIVE */ public int getResultSetType() throws SQLException { return ResultSet.TYPE_SCROLL_INSENSITIVE; } /** * * *

Implementation Details:

* *
* Result will be a ResultSet object. * * @return -1 */ public int getUpdateCount() throws SQLException { return -1; } /** * * *

Implementation Details:

* *
* returns null * * @return null */ public SQLWarning getWarnings() throws SQLException { return null; // TODO Implement Warning Handling } /** * * *

Implementation Details:

* *
* Returns the value of boolean closed */ public boolean isClosed() throws SQLException { return this.closed; } /** * * *

Implementation Details:

* *
* Not implemented yet. * * @throws BQSQLException */ public boolean isPoolable() throws SQLException { throw new BQSQLException("Not implemented." + "isPoolable()"); } /** * * *

Implementation Details:

* *
* Not implemented yet. * * @return false */ public boolean isWrapperFor(Class iface) throws SQLException { return false; } /** * * *

Implementation Details:

* *
* FeatureNotSupportedExceptionjpg * * @throws BQSQLFeatureNotSupportedException */ public void setCursorName(String arg0) throws SQLException { throw new BQSQLFeatureNotSupportedException("setCursorName(string)"); } /** * * *

Implementation Details:

* *
* Now Only setf this.EscapeProc to arg0 */ public void setEscapeProcessing(boolean arg0) throws SQLException { if (this.isClosed()) { throw new BQSQLException("This Statement is Closed"); } this.EscapeProc = arg0; } /** * * *

Implementation Details:

* *
* Not implemented yet. * * @throws BQSQLException */ public void setFetchDirection(int arg0) throws SQLException { throw new BQSQLException("Not implemented." + "setFetchDirection(int)"); } /** * * *

Implementation Details:

* *
* Does nothing * * @throws BQSQLException */ public void setFetchSize(int arg0) throws SQLException { if (this.isClosed()) { throw new BQSQLException("Statement closed"); } } /** * Sets the limit for the maximum number of bytes in a ResultSet column storing character or * binary values to the given number of bytes. This limit applies only to BINARY, VARBINARY, * LONGVARBINARY, CHAR, VARCHAR, and LONGVARCHAR fields. If the limit is exceeded, the excess data * is silently discarded. For maximum portability, use values greater than 256. * * @throws BQSQLException */ public void setMaxFieldSize(int arg0) throws SQLException { this.maxFieldSize = arg0; } /** NOTE: can pass 0 or negative to set to unlimited */ public void setMaxRows(int newMax) { this.resultMaxRowCount = newMax <= 0 ? Integer.MAX_VALUE - 1 : newMax; } /** * * *

Implementation Details:

* *
* Not implemented yet. * * @throws BQSQLException */ public void setPoolable(boolean arg0) throws SQLException { throw new BQSQLException("Not implemented." + "setPoolable(bool)"); } public void setQueryTimeout(int seconds) throws SQLException { if (this.isClosed()) { throw new BQSQLException("This Statement is Closed"); } if (seconds == 0) { this.querytimeout = Integer.MAX_VALUE; } else { this.querytimeout = seconds; } } /** * * *

Implementation Details:

* *
* Always throws SQLException * * @throws SQLException */ public T unwrap(Class iface) throws SQLException { throw new BQSQLException("not found"); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy