<?/*
Copyright (C) 2003 Media Net Link, Inc. Danville, CA 94526 USA

This file is part of AthenaRMS.

AthenaRMS 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 2 of the License, or
(at your option) any later version.

AthenaRMS 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/?>
<?php

require_once 'db.php';

class Blob {

    function Blob() {
	## Name of the blobtable for MySQL databases (see below)
	$this->blobtable = 'blobtable';
    }

    ##
    ## The only functions that should be called are:
    ##
    ##	    Blob::save($object)
    ##		- Saves $object into a blob.  Returns an oid.
    ##	    Blob::get($oid)
    ##		- Fetches and returns the object with given oid.
    ##	    Blob::delete($oid)
    ##		- Deletes the object with given oid.
    ##

    ## Returns an OID.
    function save($object) {
	global $db_config;

	switch ($db_config['driver']) {
	    case 'mysql':
		return $this->mysql_save($object);
		break;
	    case 'pgsql':
		return $this->pgsql_save($object);
		break;
	    case 'oci8':
		return $this->oci8_save($object);
		break;
	    default:
		break;
	}
    }

    function get($oid) {
	global $db_config;

	switch ($db_config['driver']) {
	    case 'mysql':
		return $this->mysql_get($oid);
		break;
	    case 'pgsql':
		return $this->pgsql_get($oid);
		break;
	    case 'oci8':
		return $this->oracle_get($oid);
		break;
	    default:
		break;
	}
    }

    function delete($oid) {
	global $db_config;

	switch ($db_config['driver']) {
	    case 'mysql':
	    case 'oci8':
		return $this->mysql_delete($oid);
		break;
	    case 'pgsql':
		return $this->pgsql_delete($oid);
		break;
	    default:
		break;
	}
    }

    #####################################################################
    ##
    ## PRIVATE FUNCTIONS FROM HERE ON OUT
    ##
    #####################################################################

    #####################################################################
    ##
    ## Postgres stuff starts here
    ##
    #####################################################################

    function pgsql_save($object) {
	global $dbi;

	$this->pgsql_start_transactions();

	## Steal the connection from PearDB.
	$conn = $dbi->connection;

	$oid = pg_lo_create ($conn);
	if (!$oid) {
	    $this->error = "Blob::pgsql_save(): pg_lo_create() failed";
	    $this->pgsql_rollback();
	    return;
	}

	$h = pg_lo_open ($conn, $oid, 'w');
	if (!$h) {
	    $this->error = "Blob::pgsql_save(): pg_lo_open() failed";
	    $this->pgsql_rollback();
	    return;
	}

	$size = pg_lo_write ($h, $object);
	if (!$size) {
	    $this->error = "Blob::pgsql_save(): pg_lo_write() failed";
	    $this->pgsql_rollback();
	    return;
	}

	if (!pg_lo_close ($h)) {
	    $this->error = "Blob::pgsql_save(): pg_lo_close() failed";
	    $this->pgsql_rollback();
	    return;
	}

	$this->pgsql_commit();
	return $oid;
    }

    function pgsql_get($oid) {
	global $dbi;
	$this->pgsql_start_transactions();
	$conn = $dbi->connection;
	$h = pg_lo_open ($conn, $oid, 'r');
	$object = pg_lo_read ($h, 9999999);
	pg_lo_close ($h);

	$this->pgsql_rollback(); ## we didn't actually write anything
	return $object;
    }

    /*
	PHP's pgsql large object implementation requires that the pg_lo_* 
	functions be called within a transaction.  So pgsql_start_transactions()
	must be called before any pg_lo_* functions, and then once we're
	finished we call either pgsql_commit() or pgsql_rollback() to end
	the current transaction.
    */
    function pgsql_start_transactions() {
	global $dbi;
	pg_query($dbi->connection, "begin");
    }

    function pgsql_commit() {
	global $dbi;
	pg_query($dbi->connection, "commit");
    }

    function pgsql_rollback() {
	global $dbi;
	pg_query($dbi->connection, "rollback");
    }

    function pgsql_delete($oid) {
	global $dbi;
print "pgsql_delete($oid)\n";
	$this->pgsql_start_transactions();
	$conn = $dbi->connection;
	$ret = pg_lo_unlink ($conn, $oid);
	$this->pgsql_commit();
	return $ret;
    }

    #####################################################################
    ##
    ## MySQL stuff starts here
    ##
    #####################################################################

    /*

	So here's how we're doing MySQL stuff.

	The Athena code expects an oid returned whenever a large object is
	saved.  In Postgres, blobs are stored in a separate magical blob
	table, and the oid is an index into that table.  However, in MySQL, 
	blobs are just another datatype.  So we'll fake that functionality
	by creating a separate blobtable in MySQL and inserting blobs
	there, returning the IDs into that table.

	At least, that's my intention. -geordan

    */

    function mysql_save($object) {
	global $dbi;
	$bt = $this->blobtable; ## never gonna come back down

	$id = $dbi->nextId('blobtable_id');
	$sql = "
	    insert into $bt (blobtable_id, object) values (?,?)
	";
	$res = $dbi->query($sql, array($id, $object));

	if (DB::isError($res)) {
	    $this->error = "Blob::mysql_save(): ".$res->getMessage().$res->getUserInfo();
	    return null;
	}

	return $id;
    }

    function mysql_get($oid) {
	global $dbi;
	$bt = $this->blobtable; ## flaming june

	$sql = "
	    select  object
	    from    $bt
	    where   blobtable_id = ?
	";
	$res = $dbi->getOne($sql, array($oid));
	if (DB::isError($res)) {
	    $this->error = "Blob::mysql_get(): ".$res->getMessage();
	    return null;
	}

	return $res;
    }

    function mysql_delete($oid) {
	global $dbi;
	$bt = $this->blobtable; ## embracing the future

	$sql = "
	    delete from $bt
	    where blobtable_id = ?
	";
	$res = $dbi->query($sql, array($oid));
	if (DB::isError($res)) {
	    $this->error = "Blob::mysql_delete(): ".$res->getMessage();
	    return null;
	}

	return 1;
    }

    function oci8_save($object) {
	global $dbi;

	## Steal the connection from PearDB.
	$conn = $dbi->connection;

	$bt = $this->blobtable; ## flaming june

	$id = $dbi->nextId('blobtable_id');

	$sql = "
	    insert into $bt (blobtable_id, object)
                     values (:blobid, empty_blob())
            returning object into :obj_lob
	";

	$stmt = OCIParse($conn, $sql);

        $clob1 = OCINewDescriptor($conn, OCI_D_LOB);

        OCIBindByName($stmt, ':blobid', &$id, 32);
        OCIBindByName($stmt, ':obj_lob', &$clob1, -1, OCI_B_BLOB);

        OCIExecute($stmt, OCI_DEFAULT);

        @$clob1->save($object);

        OCICommit($conn);

	return $id;
    }

    function oracle_get($oid) {
	global $dbi;

	## Steal the connection from PearDB.
	$conn = $dbi->connection;

	$bt = $this->blobtable; ## flaming june

	$sql = "
	    select  object
	    from    $bt
	    where   blobtable_id = :blobid
	";

        $stmt = OCIParse($conn, $sql);

        $res = OCINewDescriptor($conn, OCI_D_LOB);

        OCIBindByName($stmt, ':blobid', &$oid, 32);
        OCIDefineByName($stmt, 'OBJECT', &$res);
        OCIExecute($stmt);
        OCIFetch($stmt);
        OCIFreeStatement($stmt);

	return $res->load();
    }
  }

?>
