// Npgsql.NpgsqlCopyOut.cs
//
// Author:
// Kalle Hallivuori
//
// Copyright (C) 2007 The Npgsql Development Team
// npgsql-general@gborg.postgresql.org
// http://gborg.postgresql.org/project/npgsql/projdisplay.php
//
// Permission to use, copy, modify, and distribute this software and its
// documentation for any purpose, without fee, and without a written
// agreement is hereby granted, provided that the above copyright notice
// and this paragraph and the following two paragraphs appear in all copies.
//
// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY
// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.
//
// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS
// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
using System;
using System.IO;
namespace Npgsql
{
///
/// Represents a PostgreSQL COPY TO STDOUT operation with a corresponding SQL statement
/// to execute against a PostgreSQL database
/// and an associated stream used to write results to (if provided by user)
/// or for reading the results (when generated by driver).
/// Eg. new NpgsqlCopyOut("COPY (SELECT * FROM mytable) TO STDOUT", connection, streamToWrite).Start();
///
public class NpgsqlCopyOut
{
private readonly NpgsqlConnector _context;
private readonly NpgsqlCommand _cmd;
private Stream _copyStream;
private bool _disposeCopyStream; // user did not provide stream, so reset it after use
///
/// Creates NpgsqlCommand to run given query upon Start(), after which CopyStream provides data from database as requested in the query.
///
public NpgsqlCopyOut(string copyOutQuery, NpgsqlConnection conn)
: this(new NpgsqlCommand(copyOutQuery, conn), conn)
{
}
///
/// Given command is run upon Start(), after which CopyStream provides data from database as requested in the query.
///
public NpgsqlCopyOut(NpgsqlCommand cmd, NpgsqlConnection conn)
: this(cmd, conn, null)
{
}
///
/// Given command is executed upon Start() and all requested copy data is written to toStream immediately.
///
public NpgsqlCopyOut(NpgsqlCommand cmd, NpgsqlConnection conn, Stream toStream)
{
_context = conn.Connector;
_cmd = cmd;
_copyStream = toStream;
}
///
/// Returns true if the connection is currently reserved for this operation.
///
public bool IsActive
{
get
{
return
_context != null && _context.State == ConnectorState.CopyOut && _context.Mediator.CopyStream == _copyStream;
}
}
///
/// The stream provided by user or generated upon Start()
///
public Stream CopyStream
{
get { return _copyStream; }
}
///
/// The Command used to execute this copy operation.
///
public NpgsqlCommand NpgsqlCommand
{
get { return _cmd; }
}
///
/// Returns true if this operation is currently active and in binary format.
///
public bool IsBinary
{
get { return IsActive && _context.CopyFormat.IsBinary; }
}
///
/// Returns true if this operation is currently active and field at given location is in binary format.
///
public bool FieldIsBinary(int fieldNumber)
{
return IsActive && _context.CopyFormat.FieldIsBinary(fieldNumber);
}
///
/// Returns number of fields if this operation is currently active, otherwise -1
///
public int FieldCount
{
get { return IsActive ? _context.CopyFormat.FieldCount : -1; }
}
///
/// Command specified upon creation is executed as a non-query.
/// If CopyStream is set upon creation, all copy data from server will be written to it, and operation will be finished immediately.
/// Otherwise the CopyStream member can be used for reading copy data from server until no more data is available.
///
public void Start()
{
if (_context.State == ConnectorState.Ready)
{
_context.Mediator.CopyStream = _copyStream;
_cmd.ExecuteNonQuery();
_disposeCopyStream = _copyStream == null;
_copyStream = _context.Mediator.CopyStream;
if (_copyStream == null && _context.State != ConnectorState.Ready)
{
throw new InvalidOperationException("Not a COPY OUT query: " + _cmd.CommandText);
}
}
else
{
throw new InvalidOperationException("Copy can only start in Ready state, not in " + _context.State);
}
}
///
/// Faster alternative to using the generated CopyStream.
///
public byte[] Read
{
get { return IsActive ? ((NpgsqlCopyOutStream) _copyStream).Read() : null; }
}
///
/// Flush generated CopyStream at once. Effectively reads and discard all the rest of copy data from server.
///
public void End()
{
if (_context != null)
{
bool wasActive = IsActive;
if (wasActive)
{
if (_copyStream is NpgsqlCopyOutStream)
{
_copyStream.Close();
}
else
{
while (_context.GetCopyOutData() != null)
{
; // flush rest
}
}
}
if (_context.Mediator.CopyStream == _copyStream)
{
_context.Mediator.CopyStream = null;
if (_disposeCopyStream)
{
_copyStream = null;
}
}
}
}
}
}