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

net.sf.mongodb_jdbc_driver.MongoDbStatement Maven / Gradle / Ivy

The newest version!
/*
 *  Copyright (C) 2012 Rob Manning
 *  [email protected]
 *  
 *  This file is part of MongoDB JDBC Driver.
 *
 *  MongoDB JDBC Driver is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  MongoDB JDBC Driver is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with MongoDB JDBC Driver.  If not, see .
 */

package net.sf.mongodb_jdbc_driver;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.SQLWarning;
import java.sql.Statement;
import java.util.List;

import net.sf.mongodb_jdbc_driver.zql.ParseException;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;

public class MongoDbStatement implements Statement
{

	private final DB db;

	private final MongoDbConnection con;

	private final ParserHelper parserHelper = new ParserHelper();

	private MongoDbResultSet lastResultSet;

	private boolean isClosed = false;

	private int maxRows = -1;

	/**
	 * @param con
	 */
	public MongoDbStatement(final MongoDbConnection con)
	{
		this.db = con.getDb();
		this.con = con;
	}

	@Override
	public  T unwrap(final Class iface) throws SQLException
	{
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public boolean isWrapperFor(final Class iface) throws SQLException
	{
		// TODO Auto-generated method stub
		return false;
	}

	private void checkForSupportedColumn(final String selectColumn, String sql) throws SQLException
	{
		if (!selectColumn.equals("*") && !selectColumn.equals("\"_id\"") && !selectColumn.equals("document"))
		{
			throw new SQLException("Unsupported select item (" + selectColumn + ") in select column list ("
				+ sql + ")");
		}
	}
	
	private int getSelectColumnCount(final List columns) {
		if (columns.contains("*")) {
			return 2;
		} 
		return columns.size();
	}

	@Override
	public ResultSet executeQuery(final String sql) throws SQLException
	{

		lastResultSet = new MongoDbResultSet();
		lastResultSet.setStatement(this);

		try
		{
			if (!parserHelper.isSelectQuery(sql))
			{
				throw new SQLException("Statement must be a select query");
			}

			final String collectionName = parserHelper.getSelectTable(sql);
			final DBCollection collection = db.getCollection(collectionName);

			final List selectColumns = parserHelper.getSelectColumns(sql);

			if (selectColumns.size() == 1 && "count(*)".equals(selectColumns.get(0)))
			{
				lastResultSet.setColumnNames(new String[] { "count(*)" });
				lastResultSet.addRow(new String[] { "" + collection.find().count() });
			}
			else
			{
				boolean projectId = false;
				boolean projectDocument = false;
				String[] selectColumnNamesArr = new String[getSelectColumnCount(selectColumns)];
				int selectColumnNamesArrIdx = 0;
				/*
				 * Here we have a select list, which only makes sense if it contains: _id and/or document or "*" 
				 */
				for (final String selectColumn : selectColumns)
				{
					checkForSupportedColumn(selectColumn, sql);
					
					if (selectColumn.equals("*"))
					{
						projectId = true;
						projectDocument = true;
						selectColumnNamesArr[0] = "\"_id\"";
						selectColumnNamesArr[1] = "document";
						break;
					}
					else
					{
						if (selectColumn.equals("\"_id\""))
						{
							selectColumnNamesArr[selectColumnNamesArrIdx++] = "\"_id\"";
							projectId = true;
						}
						if (selectColumn.equals("document"))
						{
							selectColumnNamesArr[selectColumnNamesArrIdx++] = "document";
							projectDocument = true;
						}
					}
				}

				lastResultSet.setColumnNames(selectColumnNamesArr);

				final DBCursor cursor = collection.find();
				final Gson gson = new GsonBuilder().setPrettyPrinting().create();
				final JsonParser jp = new JsonParser();

				try
				{
					// TODO: Fix this greedy implementation
					//
					// This is not a great implementation here. For instance, if there are many items in the
					// cursor, then merely executing the statement causes them all to be read into memory at the
					// same time. Instead, the cursor should be moved into the MongoDbResultSet, where next()
					// can retrieve the next record from the cursor, just-in-time, rather than this greedy
					// approach.
					while (cursor.hasNext() && (maxRows == -1 || lastResultSet.getRowCount() < maxRows))
					{
						final DBObject object = cursor.next();
						final String id = object.get("_id").toString();
						String document = object.toString();
						if (projectDocument)
						{
							final JsonElement je = jp.parse(document);
							document = gson.toJson(je);
						}
						if (projectId && projectDocument)
						{
							if (selectColumnNamesArr[0].equals("document")) {
								lastResultSet.addRow(new String[] { document, id });
							} else {
								lastResultSet.addRow(new String[] { id, document });
							}
						}
						else if (projectId)
						{
							lastResultSet.addRow(new String[] { id });
						}
						else
						{
							lastResultSet.addRow(new String[] { document });
						}
					}
				}
				finally
				{
					cursor.close();
				}
			}
		}
		catch (final ParseException e)
		{
			throw new SQLException("Unable to parse query (" + sql + "): " + e.getMessage(), e);
		}
		return lastResultSet;
	}

	@Override
	public int executeUpdate(final String sql) throws SQLException
	{
		throw new SQLException("Update support has not yet been added to this driver");
	}

	/**
	 * @see java.sql.Statement#close()
	 */
	@Override
	public void close() throws SQLException
	{
		if (lastResultSet != null)
		{
			lastResultSet.close();
		}
		this.isClosed = true;
	}

	@Override
	public int getMaxFieldSize() throws SQLException
	{
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public void setMaxFieldSize(final int max) throws SQLException
	{
		// TODO Auto-generated method stub

	}

	/**
	 * @see java.sql.Statement#getMaxRows()
	 */
	@Override
	public int getMaxRows() throws SQLException
	{
		return maxRows;
	}

	/**
	 * @see java.sql.Statement#setMaxRows(int)
	 */
	@Override
	public void setMaxRows(final int max) throws SQLException
	{
		this.maxRows = max;

	}

	@Override
	public void setEscapeProcessing(final boolean enable) throws SQLException
	{
		// TODO Auto-generated method stub

	}

	/**
	 * @see java.sql.Statement#getQueryTimeout()
	 */
	@Override
	public int getQueryTimeout() throws SQLException
	{
		checkClosed();
		throw new SQLFeatureNotSupportedException("MongoDB provides no support for query timeouts.");
	}

	/**
	 * @see java.sql.Statement#setQueryTimeout(int)
	 */
	@Override
	public void setQueryTimeout(final int seconds) throws SQLException
	{
		checkClosed();
		throw new SQLFeatureNotSupportedException("MongoDB provides no support for query timeouts.");

	}

	/**
	 * @see java.sql.Statement#cancel()
	 */
	@Override
	public void cancel() throws SQLException
	{
		checkClosed();
		throw new SQLFeatureNotSupportedException("MongoDB provides no support for interrupting an operation.");

	}

	/**
	 * @see java.sql.Statement#getWarnings()
	 */
	@Override
	public SQLWarning getWarnings() throws SQLException
	{
		checkClosed();

		// Until update support is added, there are no warnings.
		return null;
	}

	/**
	 * @see java.sql.Statement#clearWarnings()
	 */
	@Override
	public void clearWarnings() throws SQLException
	{
		checkClosed();
		// Until update support is added, there are no warnings.

	}

	/**
	 * @see java.sql.Statement#setCursorName(java.lang.String)
	 */
	@Override
	public void setCursorName(final String name) throws SQLException
	{
		checkClosed();
		// Driver doesn't support positioned updates for now, so no-op.
	}

	/**
	 * @see java.sql.Statement#execute(java.lang.String)
	 */
	@Override
	public boolean execute(final String sql) throws SQLException
	{
		checkClosed();
		boolean result = false;
		try
		{
			if (parserHelper.isSelectQuery(sql))
			{
				lastResultSet = (MongoDbResultSet) executeQuery(sql);
				result = true;
			}
			else
			{

				// TODO: handle as a non-select statement. Not sure what that would be in the content of MongoDB

			}
		}
		catch (final ParseException e)
		{
			throw new SQLException("Unable to parse query (" + sql + ") : " + e.getMessage(), e);
		}
		return result;
	}

	/**
	 * @see java.sql.Statement#getResultSet()
	 */
	@Override
	public ResultSet getResultSet() throws SQLException
	{
		checkClosed();
		return lastResultSet;
	}

	/**
	 * @see java.sql.Statement#getUpdateCount()
	 */
	@Override
	public int getUpdateCount() throws SQLException
	{
		checkClosed();
		return 0;
	}

	@Override
	public boolean getMoreResults() throws SQLException
	{
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public void setFetchDirection(final int direction) throws SQLException
	{
		// TODO Auto-generated method stub

	}

	@Override
	public int getFetchDirection() throws SQLException
	{
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public void setFetchSize(final int rows) throws SQLException
	{
		// TODO Auto-generated method stub

	}

	@Override
	public int getFetchSize() throws SQLException
	{
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public int getResultSetConcurrency() throws SQLException
	{
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public int getResultSetType() throws SQLException
	{
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public void addBatch(final String sql) throws SQLException
	{
		// TODO Auto-generated method stub

	}

	@Override
	public void clearBatch() throws SQLException
	{
		// TODO Auto-generated method stub

	}

	@Override
	public int[] executeBatch() throws SQLException
	{
		checkClosed();
		// TODO Auto-generated method stub
		return null;
	}

	/**
	 * @see java.sql.Statement#getConnection()
	 */
	@Override
	public Connection getConnection() throws SQLException
	{
		checkClosed();
		return this.con;
	}

	@Override
	public boolean getMoreResults(final int current) throws SQLException
	{
		checkClosed();
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public ResultSet getGeneratedKeys() throws SQLException
	{
		checkClosed();
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public int executeUpdate(final String sql, final int autoGeneratedKeys) throws SQLException
	{
		checkClosed();
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public int executeUpdate(final String sql, final int[] columnIndexes) throws SQLException
	{
		checkClosed();
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public int executeUpdate(final String sql, final String[] columnNames) throws SQLException
	{
		checkClosed();
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public boolean execute(final String sql, final int autoGeneratedKeys) throws SQLException
	{
		checkClosed();
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean execute(final String sql, final int[] columnIndexes) throws SQLException
	{
		checkClosed();
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean execute(final String sql, final String[] columnNames) throws SQLException
	{
		checkClosed();
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public int getResultSetHoldability() throws SQLException
	{
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public boolean isClosed() throws SQLException
	{
		return isClosed;
	}

	@Override
	public void setPoolable(final boolean poolable) throws SQLException
	{
		// TODO Auto-generated method stub

	}

	@Override
	public boolean isPoolable() throws SQLException
	{
		// TODO Auto-generated method stub
		return false;
	}

	private void checkClosed() throws SQLException
	{
		if (isClosed)
		{
			throw new SQLException("Statement was previously closed.");
		}
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy