/*
 * Copyright (C) 2004-2006  Autodesk, Inc.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of version 2.1 of the GNU Lesser
 * General Public License as published by the Free Software Foundation.
 * 
 * This library 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
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#include "Pch.h"
#include "LockTests.h"

CPPUNIT_TEST_SUITE_REGISTRATION (LockTests);
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION (LockTests, "LockTests");

FdoStringP GetClassDescription() { return L"Sample class definition."; }

FdoPtr<FdoIConnection> LockTests::mConnection;

LockTests::LockTests (void)
{
}

LockTests::~LockTests (void)
{
}

void LockTests::setUp ()
{
    static bool bSchemaCreated = false;
    if (CreateSchemaOnly() && !bSchemaCreated)
    {
        mConnection = ArcSDETests::GetConnection ();
        mConnection->SetConnectionString (ArcSDETestConfig::ConnStringMetadcov());
        mConnection->Open ();

        // Create spatial context:
        FdoString* SPATIALCONTEXTNAME = L"TestSC_SampleWithGeom";
        CreateOrUpdateSpatialContext(mConnection, SPATIALCONTEXTNAME, -180.0, -90.0, 249.4967296, 339.4967296, L"4191");


        // Set up a class that supports locking
        //////////////////////////////////////////////////////////////////////

        CleanUpClass(mConnection, ArcSDETestConfig::ClassSchemaSample(), ArcSDETestConfig::ClassNameSample());
        CreateSchema (ArcSDETestConfig::ClassNameSample(), true);


        // Set up a class that does not support locking
        //////////////////////////////////////////////////////////////////////

        CleanUpClass(mConnection, ArcSDETestConfig::ClassSchemaSampleNonLocking(), ArcSDETestConfig::ClassNameSampleNonLocking());
        CreateSchema (ArcSDETestConfig::ClassNameSampleNonLocking(), false);


        // Set up a class that has a geometry property:
        //////////////////////////////////////////////////////////////////////

        // Clean up previous tests:
        CleanUpClass(mConnection, ArcSDETestConfig::ClassSchemaSampleWithGeom(), ArcSDETestConfig::ClassNameSampleWithGeom());

        // establish a class to play with
        FdoPtr<FdoIApplySchema> apply = (FdoIApplySchema*)mConnection->CreateCommand (FdoCommandType_ApplySchema);
        FdoPtr<FdoFeatureSchema> schema = ArcSDETests::GetDefaultSchema(mConnection);
        FdoPtr<FdoClassCollection> classes = schema->GetClasses ();

        // build a simple identity and data property
        FdoPtr<FdoDataPropertyDefinition> identity = FdoDataPropertyDefinition::Create (AdjustRdbmsName(L"Id"), L"Unique identifier");
        identity->SetDataType (FdoDataType_Int32);
        identity->SetNullable (false);
        identity->SetIsAutoGenerated (true);
        identity->SetReadOnly (true);
        FdoPtr<FdoDataPropertyDefinition> definition = FdoDataPropertyDefinition::Create (AdjustRdbmsName(L"LockProperty"), L"simple class for lock testing");
        definition->SetDataType (FdoDataType_String);
        definition->SetLength (256);
        definition->SetPrecision (0);
        definition->SetScale (0);
        definition->SetNullable (false);

        // build the geometry property
        FdoPtr<FdoGeometricPropertyDefinition> geometry = FdoGeometricPropertyDefinition::Create (AdjustRdbmsName(L"MyGeometry"), L"Simple geometric property");
        geometry->SetGeometryTypes (FdoGeometricType_Point | FdoGeometricType_Curve | FdoGeometricType_Surface);
        geometry->SetHasMeasure (true);
        geometry->SetDescription (L"the shape");
        geometry->SetSpatialContextAssociation(SPATIALCONTEXTNAME);

        // assemble the class
        FdoPtr<FdoFeatureClass> cls = FdoFeatureClass::Create (ArcSDETestConfig::ClassNameSampleWithGeom(), (FdoString*)GetClassDescription());
        FdoPtr<FdoPropertyDefinitionCollection> properties = cls->GetProperties ();
        properties->Add (identity);
        properties->Add (definition);
        properties->Add (geometry);
        cls->SetGeometryProperty (geometry);
        FdoPtr<FdoDataPropertyDefinitionCollection> identities = cls->GetIdentityProperties ();
        identities->Add (identity);
        SetClassCapabilities(cls, true, false);

        // submit the new schema
        classes->Add (cls);
        apply->SetFeatureSchema (schema);
        apply->Execute ();


        bSchemaCreated = true;
    }
}

void LockTests::tearDown ()
{
    if ((mConnection != NULL) && (mConnection->GetConnectionState() != FdoConnectionState_Closed))
        mConnection->Close ();
}

void LockTests::CreateSchema (const wchar_t* className, bool lockable)
{
    FdoPtr<FdoIApplySchema> apply = (FdoIApplySchema*)mConnection->CreateCommand (FdoCommandType_ApplySchema);
    FdoPtr<FdoFeatureSchema> schema = ArcSDETests::GetDefaultSchema(mConnection);
    FdoPtr<FdoClassCollection> classes = schema->GetClasses ();

    // build a simple identity and data property
    FdoPtr<FdoDataPropertyDefinition> id = FdoDataPropertyDefinition::Create (AdjustRdbmsName(L"Id"), L"Unique identifier");
    id->SetDataType (FdoDataType_Int32);
    id->SetNullable (false);
    id->SetIsAutoGenerated (true);
    id->SetReadOnly (true);
    FdoPtr<FdoDataPropertyDefinition> property = FdoDataPropertyDefinition::Create (AdjustRdbmsName(L"LockProperty"), L"simple class for lock testing");
    property->SetDataType (FdoDataType_String);
    property->SetLength (256);
    property->SetPrecision (0);
    property->SetScale (0);
    property->SetNullable (false);

    // assemble the class
    FdoPtr<FdoClass> cls = FdoClass::Create (className, (FdoString*)GetClassDescription());
    FdoPtr<FdoPropertyDefinitionCollection> properties = cls->GetProperties ();
    properties->Add (id);
    properties->Add (property);
    FdoPtr<FdoDataPropertyDefinitionCollection> identities = cls->GetIdentityProperties ();
    identities->Add (id);
    SetClassCapabilities(cls, lockable, false);

    // submit the new schema
    classes->Add (cls);
    apply->SetFeatureSchema (schema);
    apply->Execute ();
}

/* Test acquire and release locks. */
void LockTests::acquire_release ()
{
    if (!ArcSDETestConfig::SupportsLocking())   return;
    if (CreateSchemaOnly())  return;

    try
    {
        mConnection = ArcSDETests::GetConnection ();
        mConnection->SetConnectionString (ArcSDETestConfig::ConnStringMetadcov());
        mConnection->Open ();

        CleanUp();

        FdoPtr <FdoITransaction> transaction;

        transaction = mConnection->BeginTransaction ();

        FdoPtr<FdoIInsert> insert = (FdoIInsert*)mConnection->CreateCommand (FdoCommandType_Insert);
        insert->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        FdoPtr<FdoPropertyValueCollection> values = insert->GetPropertyValues ();
        FdoPtr<FdoValueExpression> expression = (FdoValueExpression*)ArcSDETests::ParseByDataType (L"'hello world'", FdoDataType_String);
        FdoPtr<FdoPropertyValue> value = FdoPropertyValue::Create (AdjustRdbmsName(L"LockProperty"), expression);
        values->Add (value);
        FdoPtr<FdoIFeatureReader> reader = insert->Execute ();
        int identity1 = -1;
        while (reader->ReadNext ())
            if (-1 != identity1)
                CPPUNIT_FAIL ("too many features");
            else
                identity1 = reader->GetInt32 (AdjustRdbmsName(L"Id"));

        // lock it
        FdoPtr <FdoIAcquireLock> acquire = (FdoIAcquireLock*)mConnection->CreateCommand (FdoCommandType_AcquireLock);
        acquire->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        acquire->SetLockType (FdoLockType_Exclusive);
        acquire->SetLockStrategy (FdoLockStrategy_All);
        FdoPtr<FdoILockConflictReader> conflicts = acquire->Execute ();
        CPPUNIT_ASSERT_MESSAGE ("conflict", !conflicts->ReadNext ());

        transaction->Commit ();

        // query it
        FdoPtr <FdoIGetLockInfo> get = (FdoIGetLockInfo*)mConnection->CreateCommand (FdoCommandType_GetLockInfo);
        get->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        FdoPtr<FdoILockedObjectReader> locks = get->Execute ();
        int count = 0;
        int id;
        while (locks->ReadNext ())
        {
            {   // use a block to exercise the caching code
                FdoPtr<FdoPropertyValueCollection> ids = locks->GetIdentity ();
                FdoPtr<FdoPropertyValue> property = ids->GetItem (0);
                FdoPtr<FdoInt32Value> value = (FdoInt32Value*)property->GetValue ();
                id = value->GetInt32 ();
            }
            if (identity1 == id)
            {
                CPPUNIT_ASSERT_MESSAGE ("locks read twice", 0 == count);
                const wchar_t* dummy = locks->GetFeatureClassName ();
                CPPUNIT_ASSERT_MESSAGE ("class wrong", 0 == wcscmp (ArcSDETestConfig::QClassNameSample(), dummy));
                CPPUNIT_ASSERT_MESSAGE ("owner wrong", 0 == wcscmp (ArcSDETestConfig::UserNameMetadcov(), locks->GetLockOwner ()));
                CPPUNIT_ASSERT_MESSAGE ("type wrong", FdoLockType_Exclusive == locks->GetLockType ());
                count++;
            }
            else
                CPPUNIT_FAIL ("unknown lock");
        }

        // unlock it
        FdoPtr <FdoIReleaseLock> release = (FdoIReleaseLock*)mConnection->CreateCommand (FdoCommandType_ReleaseLock);
        release->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        conflicts = release->Execute ();
        CPPUNIT_ASSERT_MESSAGE ("conflict", !conflicts->ReadNext ());

        // check
        locks = get->Execute ();
        CPPUNIT_ASSERT_MESSAGE ("lock still present", !locks->ReadNext ());
    }
    catch (FdoException* ge) 
    {
        fail (ge);
    }
}

/* Test acquire and release locks. */
void LockTests::acquire_release_nonlockable ()
{
    if (!ArcSDETestConfig::SupportsLocking())   return;
    if (CreateSchemaOnly())  return;

    try
    {
        mConnection = ArcSDETests::GetConnection ();
        mConnection->SetConnectionString (ArcSDETestConfig::ConnStringMetadcov());
        mConnection->Open ();

        // Clean up previous tests:
        CleanUpClass(mConnection, ArcSDETestConfig::ClassSchemaSampleNonLocking(), ArcSDETestConfig::ClassNameSampleNonLocking(), true);

        FdoPtr <FdoITransaction> transaction;
        transaction = mConnection->BeginTransaction ();

        FdoPtr<FdoIInsert> insert = (FdoIInsert*)mConnection->CreateCommand (FdoCommandType_Insert);
        insert->SetFeatureClassName (ArcSDETestConfig::QClassNameSampleNonLocking());
        FdoPtr<FdoPropertyValueCollection> values = insert->GetPropertyValues ();
        FdoPtr<FdoValueExpression> expression = (FdoValueExpression*)ArcSDETests::ParseByDataType (L"'hello world'", FdoDataType_String);
        FdoPtr<FdoPropertyValue> value = FdoPropertyValue::Create (AdjustRdbmsName(L"LockProperty"), expression);
        values->Add (value);
        FdoPtr<FdoIFeatureReader> reader = insert->Execute ();
        int identity1 = -1;
        while (reader->ReadNext ())
            if (-1 != identity1)
                CPPUNIT_FAIL ("too many features");
            else
                identity1 = reader->GetInt32 (AdjustRdbmsName(L"Id"));

        // lock it
        FdoPtr <FdoIAcquireLock> acquire = (FdoIAcquireLock*)mConnection->CreateCommand (FdoCommandType_AcquireLock);
        acquire->SetFeatureClassName (ArcSDETestConfig::QClassNameSampleNonLocking());
        acquire->SetLockType (FdoLockType_Exclusive);
        acquire->SetLockStrategy (FdoLockStrategy_All);
        try
        {
            FdoPtr<FdoILockConflictReader> conflicts = acquire->Execute ();
            CPPUNIT_FAIL ("locked a non-lockable table");
        }
        catch (FdoException* ge) 
        {
            // expected response
            ge->Release ();
        }
    }
    catch (FdoException* ge) 
    {
        fail (ge);
    }
}

/* Test releasing an object that isn't locked. */
void LockTests::release_only ()
{
    if (!ArcSDETestConfig::SupportsLocking())   return;
    if (CreateSchemaOnly())  return;

    try
    {
        mConnection = ArcSDETests::GetConnection ();
        mConnection->SetConnectionString (ArcSDETestConfig::ConnStringMetadcov());
        mConnection->Open ();

        // Clean up previous tests:
        CleanUp();

        FdoPtr <FdoITransaction> transaction;
        transaction = mConnection->BeginTransaction ();

        FdoPtr<FdoIInsert> insert = (FdoIInsert*)mConnection->CreateCommand (FdoCommandType_Insert);
        insert->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        FdoPtr<FdoPropertyValueCollection> values = insert->GetPropertyValues ();
        FdoPtr<FdoValueExpression> expression = (FdoValueExpression*)ArcSDETests::ParseByDataType (L"'hello world'", FdoDataType_String);
        FdoPtr<FdoPropertyValue> value = FdoPropertyValue::Create (AdjustRdbmsName(L"LockProperty"), expression);
        values->Add (value);
        FdoPtr<FdoIFeatureReader> reader = insert->Execute ();
        int identity1 = -1;
        while (reader->ReadNext ())
            if (-1 != identity1)
                CPPUNIT_FAIL ("too many features");
            else
                identity1 = reader->GetInt32 (AdjustRdbmsName(L"Id"));

        transaction->Commit ();

        // try to unlock it
        FdoPtr <FdoIReleaseLock> release = (FdoIReleaseLock*)mConnection->CreateCommand (FdoCommandType_ReleaseLock);
        release->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        FdoPtr<FdoILockConflictReader> conflicts = release->Execute ();
        CPPUNIT_ASSERT_MESSAGE ("conflict", !conflicts->ReadNext ());
    }
    catch (FdoException* ge) 
    {
        fail (ge);
    }
}

/* Test locking an object as one user and locking it as another user. */
void LockTests::two_user_exclusion ()
{
    FdoStringP val = getenv("rdbms");
    if (val.ICompare(L"oracle") == 0)
    {
        // Disable test since it currently crashes against an ArcSDE 92 server.
        CPPUNIT_FAIL("LockTests::two_user_exclusion disable");
    }

    if (!ArcSDETestConfig::SupportsLocking())   return;
    if (CreateSchemaOnly())  return;

    try
    {
        mConnection = ArcSDETests::GetConnection ();
        mConnection->SetConnectionString (ArcSDETestConfig::ConnStringMetadcov());
        mConnection->Open ();

        // Clean up previous tests:
        CleanUp();

        FdoPtr <FdoITransaction> transaction;
        transaction = mConnection->BeginTransaction ();

        FdoPtr<FdoIInsert> insert = (FdoIInsert*)mConnection->CreateCommand (FdoCommandType_Insert);
        insert->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        FdoPtr<FdoPropertyValueCollection> values = insert->GetPropertyValues ();
        FdoPtr<FdoValueExpression> expression = (FdoValueExpression*)ArcSDETests::ParseByDataType (L"'hello world'", FdoDataType_String);
        FdoPtr<FdoPropertyValue> value = FdoPropertyValue::Create (AdjustRdbmsName(L"LockProperty"), expression);
        values->Add (value);
        FdoPtr<FdoIFeatureReader> reader = insert->Execute ();
        int identity = -1;
        while (reader->ReadNext ())
            if (-1 != identity)
                CPPUNIT_FAIL ("too many features");
            else
                identity = reader->GetInt32 (AdjustRdbmsName(L"Id"));

        // lock it
        FdoPtr <FdoIAcquireLock> acquire = (FdoIAcquireLock*)mConnection->CreateCommand (FdoCommandType_AcquireLock);
        acquire->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        acquire->SetLockType (FdoLockType_Exclusive);
        acquire->SetLockStrategy (FdoLockStrategy_All);
        FdoPtr<FdoILockConflictReader> conflicts = acquire->Execute ();
        CPPUNIT_ASSERT_MESSAGE ("conflict", !conflicts->ReadNext ());

        transaction->Commit ();

        mConnection->Close ();

        // access as another user
        FdoPtr<FdoIConnection> connection;

        connection = ArcSDETests::GetConnection ();
        connection->SetConnectionString (ArcSDETestConfig::ConnStringOzzie());
        connection->Open ();

        FdoPtr <FdoITransaction> transaction2;
        transaction2 = connection->BeginTransaction ();

        // query it
        FdoPtr <FdoIGetLockedObjects> get = (FdoIGetLockedObjects*)connection->CreateCommand (FdoCommandType_GetLockedObjects);
        get->SetLockOwner (ArcSDETestConfig::UserNameMetadcov());
        FdoPtr<FdoILockedObjectReader> locked = get->Execute ();

        bool bFoundLock = false;
        FdoPtr<FdoPropertyValueCollection> ids;
        FdoPtr<FdoPropertyValue> property;
        FdoPtr<FdoInt32Value> value32;
        int id;
        while (locked->ReadNext ())
        {
            CPPUNIT_ASSERT_MESSAGE ("owner wrong", 0 == wcscmp (ArcSDETestConfig::UserNameMetadcov(), locked->GetLockOwner ()));
            if (0 == wcscmp (ArcSDETestConfig::QClassNameSample(), locked->GetFeatureClassName ()))
            {
                ids = locked->GetIdentity ();
                property = ids->GetItem (0);
                value32 = (FdoInt32Value*)property->GetValue ();
                id = value32->GetInt32 ();
           if (identity == id)
           {
                    bFoundLock = true;
           }
           else
               CPPUNIT_FAIL ("unknown lock");
            }
        }
        CPPUNIT_ASSERT_MESSAGE ("lock not found", bFoundLock);

        // try to lock it
        FdoPtr <FdoIAcquireLock> acquire2 = (FdoIAcquireLock*)connection->CreateCommand (FdoCommandType_AcquireLock);
        acquire2->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        acquire2->SetLockType (FdoLockType_Exclusive);
        acquire2->SetLockStrategy (FdoLockStrategy_All);
        FdoPtr<FdoILockConflictReader> conflicts2 = acquire2->Execute ();
        transaction2->Commit ();
        CPPUNIT_ASSERT_MESSAGE ("no conflict", conflicts2->ReadNext ());

        ids = conflicts2->GetIdentity ();
        property = ids->GetItem (0);
        value32 = (FdoInt32Value*)property->GetValue ();
        id = value32->GetInt32 ();
        if (identity == id)
        {
            CPPUNIT_ASSERT_MESSAGE ("class wrong", 0 == wcscmp (ArcSDETestConfig::QClassNameSample(), conflicts2->GetFeatureClassName ()));
            CPPUNIT_ASSERT_MESSAGE ("owner wrong", 0 == wcscmp (ArcSDETestConfig::UserNameMetadcov(), conflicts2->GetLockOwner ()));
        }
        else
            CPPUNIT_FAIL ("unknown lock");

        CPPUNIT_ASSERT_MESSAGE ("too many conflicts", !conflicts2->ReadNext ());

        connection->Close ();

        // clean up (as the original user)
	    mConnection = ArcSDETests::GetConnection ();
        mConnection->SetConnectionString (ArcSDETestConfig::ConnStringMetadcov());
        mConnection->Open ();
    }
    catch (FdoException* ge) 
    {
        fail (ge);
    }
}

/* Test getting the owner list. */
void LockTests::get_owner ()
{

    FdoStringP val = getenv("rdbms");
    if (val.ICompare(L"oracle") == 0)
    {
        // Disable test since it currently crashes against an ArcSDE 92 server.
        CPPUNIT_FAIL("LockTests::get_owner disable");
    }

    if (!ArcSDETestConfig::SupportsLocking())   return;
    if (CreateSchemaOnly())  return;

    try
    {
        mConnection = ArcSDETests::GetConnection ();
        mConnection->SetConnectionString (ArcSDETestConfig::ConnStringMetadcov());
        mConnection->Open ();

        // Clean up previous tests:
        CleanUp();

        FdoPtr <FdoITransaction> transaction;
        transaction = mConnection->BeginTransaction ();

        FdoPtr<FdoIInsert> insert = (FdoIInsert*)mConnection->CreateCommand (FdoCommandType_Insert);
        insert->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        FdoPtr<FdoPropertyValueCollection> values = insert->GetPropertyValues ();
        FdoPtr<FdoValueExpression> expression = (FdoValueExpression*)ArcSDETests::ParseByDataType (L"'first lock'", FdoDataType_String);
        FdoPtr<FdoPropertyValue> value = FdoPropertyValue::Create (AdjustRdbmsName(L"LockProperty"), expression);
        values->Add (value);
        FdoPtr<FdoIFeatureReader> reader = insert->Execute ();

        // lock it
        FdoPtr <FdoIAcquireLock> acquire = (FdoIAcquireLock*)mConnection->CreateCommand (FdoCommandType_AcquireLock);
        acquire->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        acquire->SetLockType (FdoLockType_Exclusive);
        acquire->SetLockStrategy (FdoLockStrategy_All);
        FdoPtr<FdoILockConflictReader> conflicts = acquire->Execute ();
        CPPUNIT_ASSERT_MESSAGE ("conflict", !conflicts->ReadNext ());

        transaction->Commit ();

        mConnection->Close ();

        // insert and lock as another user
        FdoPtr<FdoIConnection> connection;

	    connection = ArcSDETests::GetConnection ();
        connection->SetConnectionString (ArcSDETestConfig::ConnStringOzzie());
        connection->Open ();

        FdoPtr <FdoITransaction> transaction2;
        transaction2 = connection->BeginTransaction ();

        insert = (FdoIInsert*)connection->CreateCommand (FdoCommandType_Insert);
        insert->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        values = insert->GetPropertyValues ();
        expression = (FdoValueExpression*)ArcSDETests::ParseByDataType (L"'second lock'", FdoDataType_String);
        value = FdoPropertyValue::Create (AdjustRdbmsName(L"LockProperty"), expression);
        values->Add (value);
        reader = insert->Execute ();
        reader->ReadNext ();
        int identity = reader->GetInt32 (AdjustRdbmsName(L"Id"));

        // lock it
        acquire = (FdoIAcquireLock*)connection->CreateCommand (FdoCommandType_AcquireLock);
        acquire->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        acquire->SetLockType (FdoLockType_Exclusive);
        acquire->SetLockStrategy (FdoLockStrategy_All);
        wchar_t filter[1024];
        FdoCommonOSUtil::swprintf (filter, ELEMENTS(filter), L"%ls = %d", AdjustRdbmsName(L"Id"), identity);
        acquire->SetFilter (FdoPtr<FdoFilter>(FdoFilter::Parse (filter)));
        conflicts = acquire->Execute ();
        CPPUNIT_ASSERT_MESSAGE ("conflict", !conflicts->ReadNext ());

        // query owners
        FdoPtr<FdoIGetLockOwners> query = (FdoIGetLockOwners*)connection->CreateCommand (FdoCommandType_GetLockOwners);
        FdoPtr<FdoILockOwnersReader> owners = query->Execute ();
        int got = 0;
        while (owners->ReadNext ())
            if (wcscmp (ArcSDETestConfig::UserNameMetadcov(), owners->GetLockOwner ()))
                got |= 1;
            else if (wcscmp (ArcSDETestConfig::UserNameOzzie(), owners->GetLockOwner ()))
                got |= 2;
        CPPUNIT_ASSERT_MESSAGE ("owners wrong", 3 == got);

        transaction2->Commit ();
        connection->Close ();

        // clean up (as the original user)
	    mConnection = ArcSDETests::GetConnection ();
        mConnection->SetConnectionString (ArcSDETestConfig::ConnStringMetadcov());
        mConnection->Open ();
    }
    catch (FdoException* ge) 
    {
        fail (ge);
    }
}

/* Test select for lock. */
void LockTests::select ()
{
    FdoPtr<FdoPropertyValueCollection> ids;
    FdoPtr<FdoPropertyValue> property;
    FdoPtr<FdoInt32Value> value32;
    int id;

    if (!ArcSDETestConfig::SupportsLocking())   return;
    if (CreateSchemaOnly())  return;

    try
    {
        mConnection = ArcSDETests::GetConnection ();
        mConnection->SetConnectionString (ArcSDETestConfig::ConnStringMetadcov());
        mConnection->Open ();

        // Clean up previous tests:
        CleanUp();

        FdoPtr <FdoITransaction> transaction;
        transaction = mConnection->BeginTransaction ();

        FdoPtr<FdoIInsert> insert = (FdoIInsert*)mConnection->CreateCommand (FdoCommandType_Insert);
        insert->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        FdoPtr<FdoPropertyValueCollection> values = insert->GetPropertyValues ();
        FdoPtr<FdoValueExpression> expression = (FdoValueExpression*)ArcSDETests::ParseByDataType (L"'first lock'", FdoDataType_String);
        FdoPtr<FdoPropertyValue> value = FdoPropertyValue::Create (AdjustRdbmsName(L"LockProperty"), expression);
        values->Add (value);
        FdoPtr<FdoIFeatureReader> reader = insert->Execute ();
        reader->ReadNext ();
        int identity1 = reader->GetInt32 (AdjustRdbmsName(L"Id"));

        // don't lock it

        transaction->Commit ();

        mConnection->Close ();

        // insert and lock as another user
        FdoPtr<FdoIConnection> connection;

	    connection = ArcSDETests::GetConnection ();
        connection->SetConnectionString (ArcSDETestConfig::ConnStringOzzie());
        connection->Open ();

        FdoPtr <FdoITransaction> transaction2;
        transaction2 = connection->BeginTransaction ();

        insert = (FdoIInsert*)connection->CreateCommand (FdoCommandType_Insert);
        insert->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        values = insert->GetPropertyValues ();
        expression = (FdoValueExpression*)ArcSDETests::ParseByDataType (L"'second lock'", FdoDataType_String);
        value = FdoPropertyValue::Create (AdjustRdbmsName(L"LockProperty"), expression);
        values->Add (value);
        reader = insert->Execute ();
        reader->ReadNext ();
        int identity2 = reader->GetInt32 (AdjustRdbmsName(L"Id"));

        // lock it
        FdoPtr<FdoIAcquireLock> acquire = (FdoIAcquireLock*)connection->CreateCommand (FdoCommandType_AcquireLock);
        acquire->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        acquire->SetLockType (FdoLockType_Exclusive);
        acquire->SetLockStrategy (FdoLockStrategy_All);
        wchar_t filter[1024];
        FdoCommonOSUtil::swprintf (filter, ELEMENTS(filter), L"%ls = %d", AdjustRdbmsName(L"Id"), identity2);
        acquire->SetFilter (FdoPtr<FdoFilter>(FdoFilter::Parse (filter)));
        FdoPtr<FdoILockConflictReader> conflicts = acquire->Execute ();
        CPPUNIT_ASSERT_MESSAGE ("conflict", !conflicts->ReadNext ());

        transaction2->Commit ();
        connection->Close ();

        // switch back to the original user and select for lock
	    mConnection = ArcSDETests::GetConnection ();
        mConnection->SetConnectionString (ArcSDETestConfig::ConnStringMetadcov());
        mConnection->Open ();

        transaction = mConnection->BeginTransaction ();

        FdoPtr<FdoISelect> select = (FdoISelect*)mConnection->CreateCommand (FdoCommandType_Select);
        select->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        select->SetLockType (FdoLockType_Exclusive);
        select->SetLockStrategy (FdoLockStrategy_All);
        reader = select->ExecuteWithLock ();
        int got = 0;
        while (reader->ReadNext ())
        {
            id = reader->GetInt32 (AdjustRdbmsName(L"Id"));
            if (identity1 == id)
                got |= 1;
            else if (identity2 == id)
                got |= 2;
            else
                CPPUNIT_FAIL ("unknown object");
        }
        CPPUNIT_ASSERT_MESSAGE ("features wrong", 3 == got);

        // check the conflict reader
        conflicts = select->GetLockConflicts ();
        CPPUNIT_ASSERT_MESSAGE ("no conflict", conflicts->ReadNext ());

        ids = conflicts->GetIdentity ();
        property = ids->GetItem (0);
        value32 = (FdoInt32Value*)property->GetValue ();
        id = value32->GetInt32 ();
        if (identity2 != id)
            CPPUNIT_FAIL ("wrong object in conflict reader");

        transaction->Commit ();

        // check it's only object 2 that is locked
        CPPUNIT_ASSERT_MESSAGE("bad lock under this owner", 0==GetLockCount(mConnection, ArcSDETestConfig::UserNameMetadcov(), ArcSDETestConfig::QClassNameSample()));
        got = 0;
        FdoPtr <FdoIGetLockedObjects> get = (FdoIGetLockedObjects*)mConnection->CreateCommand (FdoCommandType_GetLockedObjects);
        FdoPtr<FdoILockedObjectReader> locked;
        get->SetLockOwner (ArcSDETestConfig::UserNameOzzie());
        locked = get->Execute ();
        while (locked->ReadNext ())
        {
            if (0 == wcscmp (ArcSDETestConfig::QClassNameSample(), locked->GetFeatureClassName ()))
            {
                ids = locked->GetIdentity ();
                property = ids->GetItem (0);
                value32 = (FdoInt32Value*)property->GetValue ();
                id = value32->GetInt32 ();
                if (identity1 == id)
                    CPPUNIT_FAIL ("bad lock under other owner");
                else if (identity2 == id)
                    got |= 2;
                else
                    CPPUNIT_FAIL ("unknown lock");
            }
        }
        CPPUNIT_ASSERT_MESSAGE ("wrong conflicts", 2 == got);
    }
    catch (FdoException* ge) 
    {
        fail (ge);
    }
}

/* Test select for lock with strategy 'partial'. */
void LockTests::select_partial ()
{
    FdoPtr<FdoPropertyValueCollection> ids;
    FdoPtr<FdoPropertyValue> property;
    FdoPtr<FdoInt32Value> value32;
    int id;

    if (!ArcSDETestConfig::SupportsLocking())   return;
    if (CreateSchemaOnly())  return;

    try
    {
        mConnection = ArcSDETests::GetConnection ();
        mConnection->SetConnectionString (ArcSDETestConfig::ConnStringMetadcov());
        mConnection->Open ();

        // Clean up previous tests:
        CleanUp();

        FdoPtr <FdoITransaction> transaction;
        transaction = mConnection->BeginTransaction ();

        FdoPtr<FdoIInsert> insert = (FdoIInsert*)mConnection->CreateCommand (FdoCommandType_Insert);
        insert->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        FdoPtr<FdoPropertyValueCollection> values = insert->GetPropertyValues ();
        FdoPtr<FdoValueExpression> expression = (FdoValueExpression*)ArcSDETests::ParseByDataType (L"'first lock'", FdoDataType_String);
        FdoPtr<FdoPropertyValue> value = FdoPropertyValue::Create (AdjustRdbmsName(L"LockProperty"), expression);
        values->Add (value);
        FdoPtr<FdoIFeatureReader> reader = insert->Execute ();
        reader->ReadNext ();
        int identity1 = reader->GetInt32 (AdjustRdbmsName(L"Id"));

        // don't lock it

        transaction->Commit ();

        mConnection->Close ();

        // insert and lock as another user
        FdoPtr<FdoIConnection> connection;

	    connection = ArcSDETests::GetConnection ();
        connection->SetConnectionString (ArcSDETestConfig::ConnStringOzzie());
        connection->Open ();

        FdoPtr <FdoITransaction> transaction2;
        transaction2 = connection->BeginTransaction ();

        insert = (FdoIInsert*)connection->CreateCommand (FdoCommandType_Insert);
        insert->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        values = insert->GetPropertyValues ();
        expression = (FdoValueExpression*)ArcSDETests::ParseByDataType (L"'second lock'", FdoDataType_String);
        value = FdoPropertyValue::Create (AdjustRdbmsName(L"LockProperty"), expression);
        values->Add (value);
        reader = insert->Execute ();
        reader->ReadNext ();
        int identity2 = reader->GetInt32 (AdjustRdbmsName(L"Id"));

        // lock it
        FdoPtr<FdoIAcquireLock> acquire = (FdoIAcquireLock*)connection->CreateCommand (FdoCommandType_AcquireLock);
        acquire->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        acquire->SetLockType (FdoLockType_Exclusive);
        acquire->SetLockStrategy (FdoLockStrategy_All);
        wchar_t filter[1024];
        FdoCommonOSUtil::swprintf (filter, ELEMENTS(filter), L"%ls = %d", AdjustRdbmsName(L"Id"), identity2);
        acquire->SetFilter (FdoPtr<FdoFilter>(FdoFilter::Parse (filter)));
        FdoPtr<FdoILockConflictReader> conflicts = acquire->Execute ();
        CPPUNIT_ASSERT_MESSAGE ("conflict", !conflicts->ReadNext ());

        transaction2->Commit ();
        connection->Close ();

        // switch back to the original user and select for lock
	    mConnection = ArcSDETests::GetConnection ();
        mConnection->SetConnectionString (ArcSDETestConfig::ConnStringMetadcov());
        mConnection->Open ();

        transaction = mConnection->BeginTransaction ();

        FdoPtr<FdoISelect> select = (FdoISelect*)mConnection->CreateCommand (FdoCommandType_Select);
        select->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        select->SetLockType (FdoLockType_Exclusive);
        select->SetLockStrategy (FdoLockStrategy_Partial);
        reader = select->ExecuteWithLock ();
        int got = 0;
        while (reader->ReadNext ())
        {
            id = reader->GetInt32 (AdjustRdbmsName(L"Id"));
            if (identity1 == id)
                got |= 1;
            else if (identity2 == id)
                got |= 2;
            else
                CPPUNIT_FAIL ("unknown object");
        }
        CPPUNIT_ASSERT_MESSAGE ("features wrong", 3 == got);

        // check the conflict reader
        conflicts = select->GetLockConflicts ();
        CPPUNIT_ASSERT_MESSAGE ("no conflict", conflicts->ReadNext ());

        ids = conflicts->GetIdentity ();
        property = ids->GetItem (0);
        value32 = (FdoInt32Value*)property->GetValue ();
        id = value32->GetInt32 ();
        if (identity2 != id)
            CPPUNIT_FAIL ("wrong object in conflict reader");

        transaction->Commit ();

        // check it's both object 1 and 2 that are locked
        got = 0;
        FdoPtr <FdoIGetLockedObjects> get = (FdoIGetLockedObjects*)mConnection->CreateCommand (FdoCommandType_GetLockedObjects);
        FdoPtr<FdoILockedObjectReader> locked = get->Execute ();
        while (locked->ReadNext ())
        {
            if (0==wcscmp(locked->GetFeatureClassName(), ArcSDETestConfig::QClassNameSample()))
            {
                ids = locked->GetIdentity ();
                property = ids->GetItem (0);
                value32 = (FdoInt32Value*)property->GetValue ();
                id = value32->GetInt32 ();
                if (identity1 == id)
                    got |= 1;
                else if (identity2 == id)
                    CPPUNIT_FAIL ("bad lock under this owner");
                else
                    CPPUNIT_FAIL ("unknown lock");
            }
        }
        get->SetLockOwner (ArcSDETestConfig::UserNameOzzie());
        locked = get->Execute ();
        while (locked->ReadNext ())
        {
            if (0==wcscmp(locked->GetFeatureClassName(), ArcSDETestConfig::QClassNameSample()))
            {
                ids = locked->GetIdentity ();
                property = ids->GetItem (0);
                value32 = (FdoInt32Value*)property->GetValue ();
                id = value32->GetInt32 ();
                if (identity1 == id)
                    CPPUNIT_FAIL ("bad lock under other owner");
                else if (identity2 == id)
                    got |= 2;
                else
                    CPPUNIT_FAIL ("unknown lock");
            }
        }
        CPPUNIT_ASSERT_MESSAGE ("wrong conflicts", 3 == got);
    }
    catch (FdoException* ge) 
    {
        fail (ge);
    }
}


/* Test select for lock by getting the conflict list first. */
void LockTests::select_conflicts_first ()
{
    FdoPtr<FdoPropertyValueCollection> ids;
    FdoPtr<FdoPropertyValue> property;
    FdoPtr<FdoInt32Value> value32;
    int id;

    if (!ArcSDETestConfig::SupportsLocking())   return;
    if (CreateSchemaOnly())  return;

    try
    {
        mConnection = ArcSDETests::GetConnection ();
        mConnection->SetConnectionString (ArcSDETestConfig::ConnStringMetadcov());
        mConnection->Open ();

        // Clean up previous tests:
        CleanUp();

        FdoPtr <FdoITransaction> transaction;
        transaction = mConnection->BeginTransaction ();

        FdoPtr<FdoIInsert> insert = (FdoIInsert*)mConnection->CreateCommand (FdoCommandType_Insert);
        insert->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        FdoPtr<FdoPropertyValueCollection> values = insert->GetPropertyValues ();
        FdoPtr<FdoValueExpression> expression = (FdoValueExpression*)ArcSDETests::ParseByDataType (L"'first lock'", FdoDataType_String);
        FdoPtr<FdoPropertyValue> value = FdoPropertyValue::Create (AdjustRdbmsName(L"LockProperty"), expression);
        values->Add (value);
        FdoPtr<FdoIFeatureReader> reader = insert->Execute ();
        reader->ReadNext ();
        int identity1 = reader->GetInt32 (AdjustRdbmsName(L"Id"));

        // don't lock it

        transaction->Commit ();

        mConnection->Close ();

        // insert and lock as another user
        FdoPtr<FdoIConnection> connection;

	    connection = ArcSDETests::GetConnection ();
        connection->SetConnectionString (ArcSDETestConfig::ConnStringOzzie());
        connection->Open ();

        FdoPtr <FdoITransaction> transaction2;
        transaction2 = connection->BeginTransaction ();

        insert = (FdoIInsert*)connection->CreateCommand (FdoCommandType_Insert);
        insert->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        values = insert->GetPropertyValues ();
        expression = (FdoValueExpression*)ArcSDETests::ParseByDataType (L"'second lock'", FdoDataType_String);
        value = FdoPropertyValue::Create (AdjustRdbmsName(L"LockProperty"), expression);
        values->Add (value);
        reader = insert->Execute ();
        reader->ReadNext ();
        int identity2 = reader->GetInt32 (AdjustRdbmsName(L"Id"));

        // lock it
        FdoPtr<FdoIAcquireLock> acquire = (FdoIAcquireLock*)connection->CreateCommand (FdoCommandType_AcquireLock);
        acquire->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        acquire->SetLockType (FdoLockType_Exclusive);
        acquire->SetLockStrategy (FdoLockStrategy_All);
        wchar_t filter[1024];
        FdoCommonOSUtil::swprintf (filter, ELEMENTS(filter), L"%ls = %d", AdjustRdbmsName(L"Id"), identity2);
        acquire->SetFilter (FdoPtr<FdoFilter>(FdoFilter::Parse (filter)));
        FdoPtr<FdoILockConflictReader> conflicts = acquire->Execute ();
        CPPUNIT_ASSERT_MESSAGE ("conflict", !conflicts->ReadNext ());

        transaction2->Commit ();
        connection->Close ();

        // switch back to the original user and select for lock
	    mConnection = ArcSDETests::GetConnection ();
        mConnection->SetConnectionString (ArcSDETestConfig::ConnStringMetadcov());
        mConnection->Open ();

        transaction = mConnection->BeginTransaction ();

        FdoPtr<FdoISelect> select = (FdoISelect*)mConnection->CreateCommand (FdoCommandType_Select);
        select->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        select->SetLockType (FdoLockType_Exclusive);
        select->SetLockStrategy (FdoLockStrategy_All);
        reader = select->ExecuteWithLock ();

        // check the conflict reader
        conflicts = select->GetLockConflicts ();
        CPPUNIT_ASSERT_MESSAGE ("no conflict", conflicts->ReadNext ());

        ids = conflicts->GetIdentity ();
        property = ids->GetItem (0);
        value32 = (FdoInt32Value*)property->GetValue ();
        id = value32->GetInt32 ();
        if (identity2 != id)
            CPPUNIT_FAIL ("wrong object in conflict reader");

        // check the reader
        int got = 0;
        while (reader->ReadNext ())
        {
            id = reader->GetInt32 (AdjustRdbmsName(L"Id"));
            if (identity1 == id)
                got |= 1;
            else if (identity2 == id)
                got |= 2;
            else
                CPPUNIT_FAIL ("unknown object");
        }
        CPPUNIT_ASSERT_MESSAGE ("features wrong", 3 == got);

        transaction->Commit ();

        // check it's only object 2 that is locked
        CPPUNIT_ASSERT_MESSAGE("bad lock under this owner", 0==GetLockCount(mConnection, ArcSDETestConfig::UserNameMetadcov(), ArcSDETestConfig::QClassNameSample()));
        got = 0;
        FdoPtr <FdoIGetLockedObjects> get = (FdoIGetLockedObjects*)mConnection->CreateCommand (FdoCommandType_GetLockedObjects);
        FdoPtr<FdoILockedObjectReader> locked;
        get->SetLockOwner (ArcSDETestConfig::UserNameOzzie());
        locked = get->Execute ();
        while (locked->ReadNext ())
        {
            ids = locked->GetIdentity ();
            property = ids->GetItem (0);
            value32 = (FdoInt32Value*)property->GetValue ();
            id = value32->GetInt32 ();
            if (identity1 == id)
                CPPUNIT_FAIL ("bad lock under other owner");
            else if (identity2 == id)
                got |= 2;
            else
                CPPUNIT_FAIL ("unknown lock");
        }
        CPPUNIT_ASSERT_MESSAGE ("wrong conflicts", 2 == got);
    }
    catch (FdoException* ge) 
    {
        fail (ge);
    }
}

/* Test simple update with a conflict. */
void LockTests::update_where_conflict ()
{
    FdoPtr<FdoPropertyValueCollection> ids;
    FdoPtr<FdoPropertyValue> property;
    FdoPtr<FdoInt32Value> value32;
    int id;
    wchar_t filter[1024];

    if (!ArcSDETestConfig::SupportsLocking())   return;
    if (CreateSchemaOnly())  return;

    try
    {
        mConnection = ArcSDETests::GetConnection ();
        mConnection->SetConnectionString (ArcSDETestConfig::ConnStringMetadcov());
        mConnection->Open ();

        // Clean up previous tests:
        CleanUp();

        FdoPtr <FdoITransaction> transaction;
        transaction = mConnection->BeginTransaction ();

        // insert a couple of objects
        FdoPtr<FdoIInsert> insert = (FdoIInsert*)mConnection->CreateCommand (FdoCommandType_Insert);
        insert->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        FdoPtr<FdoPropertyValueCollection> values = insert->GetPropertyValues ();
        FdoPtr<FdoValueExpression> expression = (FdoValueExpression*)ArcSDETests::ParseByDataType (L"'first lock'", FdoDataType_String);
        FdoPtr<FdoPropertyValue> value = FdoPropertyValue::Create (AdjustRdbmsName(L"LockProperty"), expression);
        values->Add (value);
        FdoPtr<FdoIFeatureReader> reader = insert->Execute ();
        reader->ReadNext ();
        int identity1 = reader->GetInt32 (AdjustRdbmsName(L"Id"));
        reader = insert->Execute ();
        reader->ReadNext ();
        int identity2 = reader->GetInt32 (AdjustRdbmsName(L"Id"));

        // lock one of them
        FdoPtr <FdoIAcquireLock> acquire = (FdoIAcquireLock*)mConnection->CreateCommand (FdoCommandType_AcquireLock);
        acquire->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        acquire->SetLockType (FdoLockType_Exclusive);
        acquire->SetLockStrategy (FdoLockStrategy_All);
        FdoCommonOSUtil::swprintf (filter, ELEMENTS(filter), L"%ls = %d", AdjustRdbmsName(L"Id"), identity1);
        acquire->SetFilter (FdoPtr<FdoFilter>(FdoFilter::Parse (filter)));
        FdoPtr<FdoILockConflictReader> conflicts = acquire->Execute ();
        CPPUNIT_ASSERT_MESSAGE ("conflict", !conflicts->ReadNext ());

        transaction->Commit ();

        mConnection->Close ();

        // try to update both as another user
        FdoPtr<FdoIConnection> connection;

	    connection = ArcSDETests::GetConnection ();
        connection->SetConnectionString (ArcSDETestConfig::ConnStringOzzie());
        connection->Open ();

        FdoPtr <FdoITransaction> transaction2;
        transaction2 = connection->BeginTransaction ();

        FdoPtr<FdoIUpdate> update = (FdoIUpdate*)connection->CreateCommand (FdoCommandType_Update);
        update->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        values = update->GetPropertyValues ();
        expression = (FdoValueExpression*)ArcSDETests::ParseByDataType (L"'second update'", FdoDataType_String);
        value = FdoPropertyValue::Create (AdjustRdbmsName(L"LockProperty"), expression);
        values->Add (value);
        FdoString* adjIdName = AdjustRdbmsName(L"Id");
        FdoCommonOSUtil::swprintf (filter, ELEMENTS(filter), L"%ls = %d OR %ls = %d", adjIdName, identity1, adjIdName, identity2);
        update->SetFilter (FdoPtr<FdoFilter>(FdoFilter::Parse (filter)));
        int count = update->Execute ();
        CPPUNIT_ASSERT_MESSAGE ("update succeeded", 0 == count);

        // get the conflict list and check it's contents
        conflicts = update->GetLockConflicts ();
        CPPUNIT_ASSERT_MESSAGE ("no conflict", conflicts->ReadNext ());
        ids = conflicts->GetIdentity ();
        property = ids->GetItem (0);
        value32 = (FdoInt32Value*)property->GetValue ();
        id = value32->GetInt32 ();
        if (identity1 != id)
            CPPUNIT_FAIL ("wrong object in conflict reader");
        CPPUNIT_ASSERT_MESSAGE ("too many conflicts", !conflicts->ReadNext ());

        transaction2->Commit ();
        connection->Close ();

        // switch back to the original user and check for changes
	    mConnection = ArcSDETests::GetConnection ();
        mConnection->SetConnectionString (ArcSDETestConfig::ConnStringMetadcov());
        mConnection->Open ();

        FdoPtr<FdoISelect> select = (FdoISelect*)mConnection->CreateCommand (FdoCommandType_Select);
        select->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        reader = select->Execute ();
        int got = 0;
        while (reader->ReadNext ())
        {
            id = reader->GetInt32 (AdjustRdbmsName(L"Id"));
            if (identity1 == id)
            {
                got |= 1;
                CPPUNIT_ASSERT_MESSAGE ("data mistakenly updated", 0 == wcscmp (L"first lock", reader->GetString (AdjustRdbmsName(L"LockProperty"))));
            }
            else if (identity2 == id)
            {
                got |= 2;
                CPPUNIT_ASSERT_MESSAGE ("data mistakenly updated", 0 == wcscmp (L"first lock", reader->GetString (AdjustRdbmsName(L"LockProperty"))));
            }
            else
                CPPUNIT_FAIL ("unknown object");
        }
        CPPUNIT_ASSERT_MESSAGE ("features wrong", 3 == got);
    }
    catch (FdoException* ge) 
    {
        fail (ge);
    }
}

/* Test simple update with a spatial select conflict. */
void LockTests::update_spatial_conflict ()
{
    FdoPtr<FdoPropertyValueCollection> ids;
    FdoPtr<FdoPropertyValue> property;
    FdoPtr<FdoInt32Value> value32;
    int id;

    if (!ArcSDETestConfig::SupportsLocking())   return;
    if (CreateSchemaOnly())  return;

    try
    {
        mConnection = ArcSDETests::GetConnection ();
        mConnection->SetConnectionString (ArcSDETestConfig::ConnStringMetadcov());
        mConnection->Open ();

        // Clean up previous tests:
        CleanUpClass(mConnection, ArcSDETestConfig::ClassSchemaSampleWithGeom(), ArcSDETestConfig::ClassNameSampleWithGeom(), true);

        FdoPtr <FdoITransaction> transaction;
        transaction = mConnection->BeginTransaction ();

        // insert a couple of objects
        FdoPtr<FdoIInsert> insert = (FdoIInsert*)mConnection->CreateCommand (FdoCommandType_Insert);
        insert->SetFeatureClassName (ArcSDETestConfig::QClassNameSampleWithGeom());
        FdoPtr<FdoPropertyValueCollection> values = insert->GetPropertyValues ();
        FdoPtr<FdoValueExpression> expression = (FdoValueExpression*)ArcSDETests::ParseByDataType (L"'first lock'", FdoDataType_String);
        FdoPtr<FdoPropertyValue> value = FdoPropertyValue::Create (AdjustRdbmsName(L"LockProperty"), expression);
        values->Add (value);
        expression = (FdoValueExpression*)FdoExpression::Parse (L"GeomFromText('POINT (7 11)')");
        value = FdoPropertyValue::Create (AdjustRdbmsName(L"MyGeometry"), expression);
        values->Add (value);
        FdoPtr<FdoIFeatureReader> reader = insert->Execute ();
        reader->ReadNext ();
        int identity1 = reader->GetInt32 (AdjustRdbmsName(L"Id"));
        value = values->GetItem (AdjustRdbmsName(L"MyGeometry"));
        expression = (FdoValueExpression*)FdoExpression::Parse (L"GeomFromText('POINT (2 13)')");
        value->SetValue (expression);
        reader = insert->Execute ();
        reader->ReadNext ();
        int identity2 = reader->GetInt32 (AdjustRdbmsName(L"Id"));

        // just try and select one of them
        FdoPtr<FdoISelect> select = (FdoISelect*)mConnection->CreateCommand (FdoCommandType_Select);
        select->SetFeatureClassName (ArcSDETestConfig::QClassNameSampleWithGeom());
        wchar_t filter[1024];
        FdoCommonOSUtil::swprintf (filter, ELEMENTS(filter), L"%ls intersects GeomFromText ('POLYGON XY (( 5 5, 16 5, 16 16, 5 16, 5 5 ))')", AdjustRdbmsName(L"MyGeometry"));
        select->SetFilter (FdoPtr<FdoFilter>(FdoFilter::Parse (filter)));
        reader = select->Execute ();
        CPPUNIT_ASSERT_MESSAGE ("no objects selected by spatial query", reader->ReadNext ());

        // lock one of them
        FdoPtr <FdoIAcquireLock> acquire = (FdoIAcquireLock*)mConnection->CreateCommand (FdoCommandType_AcquireLock);
        acquire->SetFeatureClassName (ArcSDETestConfig::QClassNameSampleWithGeom());
        acquire->SetLockType (FdoLockType_Exclusive);
        acquire->SetLockStrategy (FdoLockStrategy_All);
        FdoCommonOSUtil::swprintf (filter, ELEMENTS(filter), L"%ls intersects GeomFromText ('POLYGON XY (( 5 5, 16 5, 16 16, 5 16, 5 5 ))')", AdjustRdbmsName(L"MyGeometry"));
        acquire->SetFilter (FdoPtr<FdoFilter>(FdoFilter::Parse (filter)));
        FdoPtr<FdoILockConflictReader> conflicts = acquire->Execute ();
        CPPUNIT_ASSERT_MESSAGE ("conflict", !conflicts->ReadNext ());

        transaction->Commit ();

        mConnection->Close ();

        // try to update both as another user
        FdoPtr<FdoIConnection> connection;

	    connection = ArcSDETests::GetConnection ();
        connection->SetConnectionString (ArcSDETestConfig::ConnStringOzzie());
        connection->Open ();

        FdoPtr <FdoITransaction> transaction2;
        transaction2 = connection->BeginTransaction ();

        FdoPtr<FdoIUpdate> update = (FdoIUpdate*)connection->CreateCommand (FdoCommandType_Update);
        update->SetFeatureClassName (ArcSDETestConfig::QClassNameSampleWithGeom());
        values = update->GetPropertyValues ();
        expression = (FdoValueExpression*)ArcSDETests::ParseByDataType (L"'second update'", FdoDataType_String);
        value = FdoPropertyValue::Create (AdjustRdbmsName(L"LockProperty"), expression);
        values->Add (value);
        FdoCommonOSUtil::swprintf (filter, ELEMENTS(filter), L"%ls intersects GeomFromText ('POLYGON XY (( 0 0, 20 0, 20 20, 0 20, 0 0 ))')", AdjustRdbmsName(L"MyGeometry"));
        update->SetFilter (FdoPtr<FdoFilter>(FdoFilter::Parse (filter)));
        int count = update->Execute ();
        CPPUNIT_ASSERT_MESSAGE ("update succeeded", 0 == count);

        // get the conflict list and check it's contents
        conflicts = update->GetLockConflicts ();
        CPPUNIT_ASSERT_MESSAGE ("no conflict", conflicts->ReadNext ());
        ids = conflicts->GetIdentity ();
        property = ids->GetItem (0);
        value32 = (FdoInt32Value*)property->GetValue ();
        id = value32->GetInt32 ();
        if (identity1 != id)
            CPPUNIT_FAIL ("wrong object in conflict reader");
        CPPUNIT_ASSERT_MESSAGE ("too many conflicts", !conflicts->ReadNext ());

        transaction2->Commit ();
        connection->Close ();

        // switch back to the original user and check for changes
	    mConnection = ArcSDETests::GetConnection ();
        mConnection->SetConnectionString (ArcSDETestConfig::ConnStringMetadcov());
        mConnection->Open ();

        select = (FdoISelect*)mConnection->CreateCommand (FdoCommandType_Select);
        select->SetFeatureClassName (ArcSDETestConfig::QClassNameSampleWithGeom());
        reader = select->Execute ();
        int got = 0;
        while (reader->ReadNext ())
        {
            id = reader->GetInt32 (AdjustRdbmsName(L"Id"));
            if (identity1 == id)
            {
                got |= 1;
                CPPUNIT_ASSERT_MESSAGE ("data mistakenly updated", 0 == wcscmp (L"first lock", reader->GetString (AdjustRdbmsName(L"LockProperty"))));
            }
            else if (identity2 == id)
            {
                got |= 2;
                CPPUNIT_ASSERT_MESSAGE ("data mistakenly updated", 0 == wcscmp (L"first lock", reader->GetString (AdjustRdbmsName(L"LockProperty"))));
            }
            else
                CPPUNIT_FAIL ("unknown object");
        }
        CPPUNIT_ASSERT_MESSAGE ("features wrong", 3 == got);
    }
    catch (FdoException* ge) 
    {
        fail (ge);
    }
}

/* Test simple delete with a conflict. */
void LockTests::delete_where_conflict ()
{
    FdoPtr<FdoPropertyValueCollection> ids;
    FdoPtr<FdoPropertyValue> property;
    FdoPtr<FdoInt32Value> value32;
    int id;
    wchar_t filter[1024];

    if (!ArcSDETestConfig::SupportsLocking())   return;
    if (CreateSchemaOnly())  return;

    try
    {
        mConnection = ArcSDETests::GetConnection ();
        mConnection->SetConnectionString (ArcSDETestConfig::ConnStringMetadcov());
        mConnection->Open ();

        // Clean up previous tests:
        CleanUp();

        FdoPtr <FdoITransaction> transaction;
        transaction = mConnection->BeginTransaction ();

        // insert a couple of objects
        FdoPtr<FdoIInsert> insert = (FdoIInsert*)mConnection->CreateCommand (FdoCommandType_Insert);
        insert->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        FdoPtr<FdoPropertyValueCollection> values = insert->GetPropertyValues ();
        FdoPtr<FdoValueExpression> expression = (FdoValueExpression*)ArcSDETests::ParseByDataType (L"'first lock'", FdoDataType_String);
        FdoPtr<FdoPropertyValue> value = FdoPropertyValue::Create (AdjustRdbmsName(L"LockProperty"), expression);
        values->Add (value);
        FdoPtr<FdoIFeatureReader> reader = insert->Execute ();
        reader->ReadNext ();
        int identity1 = reader->GetInt32 (AdjustRdbmsName(L"Id"));
        reader = insert->Execute ();
        reader->ReadNext ();
        int identity2 = reader->GetInt32 (AdjustRdbmsName(L"Id"));

        // lock one of them
        FdoPtr <FdoIAcquireLock> acquire = (FdoIAcquireLock*)mConnection->CreateCommand (FdoCommandType_AcquireLock);
        acquire->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        acquire->SetLockType (FdoLockType_Exclusive);
        acquire->SetLockStrategy (FdoLockStrategy_All);
        FdoCommonOSUtil::swprintf (filter, ELEMENTS(filter), L"%ls = %d", AdjustRdbmsName(L"Id"), identity1);
        acquire->SetFilter (FdoPtr<FdoFilter>(FdoFilter::Parse (filter)));
        FdoPtr<FdoILockConflictReader> conflicts = acquire->Execute ();
        CPPUNIT_ASSERT_MESSAGE ("conflict", !conflicts->ReadNext ());

        transaction->Commit ();

        mConnection->Close ();

        // try to delete both as another user
        FdoPtr<FdoIConnection> connection;

	    connection = ArcSDETests::GetConnection ();
        connection->SetConnectionString (ArcSDETestConfig::ConnStringOzzie());
        connection->Open ();

        FdoPtr <FdoITransaction> transaction2;
        transaction2 = connection->BeginTransaction ();

        FdoPtr<FdoIDelete> del = (FdoIDelete*)connection->CreateCommand (FdoCommandType_Delete);
        del->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        FdoString* adjIdName = AdjustRdbmsName(L"Id");
        FdoCommonOSUtil::swprintf (filter, ELEMENTS(filter), L"%ls = %d OR %ls = %d", adjIdName, identity1, adjIdName, identity2);
        del->SetFilter (FdoPtr<FdoFilter>(FdoFilter::Parse (filter)));
        int count = del->Execute ();
        CPPUNIT_ASSERT_MESSAGE ("delete succeeded", 0 == count);

        // get the conflict list and check it's contents
        conflicts = del->GetLockConflicts ();
        CPPUNIT_ASSERT_MESSAGE ("no conflict", conflicts->ReadNext ());
        ids = conflicts->GetIdentity ();
        property = ids->GetItem (0);
        value32 = (FdoInt32Value*)property->GetValue ();
        id = value32->GetInt32 ();
        if (identity1 != id)
            CPPUNIT_FAIL ("wrong object in conflict reader");
        CPPUNIT_ASSERT_MESSAGE ("too many conflicts", !conflicts->ReadNext ());

        transaction2->Commit ();
        connection->Close ();

        // switch back to the original user and check for changes
	    mConnection = ArcSDETests::GetConnection ();
        mConnection->SetConnectionString (ArcSDETestConfig::ConnStringMetadcov());
        mConnection->Open ();

        FdoPtr<FdoISelect> select = (FdoISelect*)mConnection->CreateCommand (FdoCommandType_Select);
        select->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        reader = select->Execute ();
        int got = 0;
        while (reader->ReadNext ())
        {
            id = reader->GetInt32 (AdjustRdbmsName(L"Id"));
            if (identity1 == id)
                got |= 1;
            else if (identity2 == id)
                got |= 2;
            else
                CPPUNIT_FAIL ("unknown object");
        }
        CPPUNIT_ASSERT_MESSAGE ("features wrong", 3 == got);
    }
    catch (FdoException* ge) 
    {
        fail (ge);
    }
}

/* Test simple delete with a spatial select conflict. */
void LockTests::delete_spatial_conflict ()
{
    FdoPtr<FdoPropertyValueCollection> ids;
    FdoPtr<FdoPropertyValue> property;
    FdoPtr<FdoInt32Value> value32;
    int id;

    if (!ArcSDETestConfig::SupportsLocking())   return;
    if (CreateSchemaOnly())  return;

    try
    {
        mConnection = ArcSDETests::GetConnection ();
        mConnection->SetConnectionString (ArcSDETestConfig::ConnStringMetadcov());
        mConnection->Open ();

        // Clean up previous tests:
        CleanUpClass(mConnection, ArcSDETestConfig::ClassSchemaSampleWithGeom(), ArcSDETestConfig::ClassNameSampleWithGeom(), true);

        FdoPtr <FdoITransaction> transaction;
        transaction = mConnection->BeginTransaction ();

        // insert a couple of objects
        FdoPtr<FdoIInsert> insert = (FdoIInsert*)mConnection->CreateCommand (FdoCommandType_Insert);
        insert->SetFeatureClassName (ArcSDETestConfig::QClassNameSampleWithGeom());
        FdoPtr<FdoPropertyValueCollection> values = insert->GetPropertyValues ();
        FdoPtr<FdoValueExpression> expression = (FdoValueExpression*)ArcSDETests::ParseByDataType (L"'first lock'", FdoDataType_String);
        FdoPtr<FdoPropertyValue> value = FdoPropertyValue::Create (AdjustRdbmsName(L"LockProperty"), expression);
        values->Add (value);
        expression = (FdoValueExpression*)FdoExpression::Parse (L"GeomFromText('POINT (7 11)')");
        value = FdoPropertyValue::Create (AdjustRdbmsName(L"MyGeometry"), expression);
        values->Add (value);
        FdoPtr<FdoIFeatureReader> reader = insert->Execute ();
        reader->ReadNext ();
        int identity1 = reader->GetInt32 (AdjustRdbmsName(L"Id"));
        expression = (FdoValueExpression*)FdoExpression::Parse (L"GeomFromText('POINT (2 13)')");
        value->SetValue (expression);
        reader = insert->Execute ();
        reader->ReadNext ();
        int identity2 = reader->GetInt32 (AdjustRdbmsName(L"Id"));

        // just try and select one of them
        FdoPtr<FdoISelect> select = (FdoISelect*)mConnection->CreateCommand (FdoCommandType_Select);
        select->SetFeatureClassName (ArcSDETestConfig::QClassNameSampleWithGeom());
        wchar_t filter[1024];
        FdoCommonOSUtil::swprintf (filter, ELEMENTS(filter), L"%ls intersects GeomFromText ('POLYGON XY (( 5 5, 16 5, 16 16, 5 16, 5 5 ))')", AdjustRdbmsName(L"MyGeometry"));
        select->SetFilter (FdoPtr<FdoFilter>(FdoFilter::Parse (filter)));
        reader = select->Execute ();
        CPPUNIT_ASSERT_MESSAGE ("no objects selected by spatial query", reader->ReadNext ());

        // lock one of them
        FdoPtr <FdoIAcquireLock> acquire = (FdoIAcquireLock*)mConnection->CreateCommand (FdoCommandType_AcquireLock);
        acquire->SetFeatureClassName (ArcSDETestConfig::QClassNameSampleWithGeom());
        acquire->SetLockType (FdoLockType_Exclusive);
        acquire->SetLockStrategy (FdoLockStrategy_All);
        FdoCommonOSUtil::swprintf (filter, ELEMENTS(filter), L"%ls intersects GeomFromText ('POLYGON XY (( 5 5, 16 5, 16 16, 5 16, 5 5 ))')", AdjustRdbmsName(L"MyGeometry"));
        acquire->SetFilter (FdoPtr<FdoFilter>(FdoFilter::Parse (filter)));
        FdoPtr<FdoILockConflictReader> conflicts = acquire->Execute ();
        CPPUNIT_ASSERT_MESSAGE ("conflict", !conflicts->ReadNext ());

        transaction->Commit ();

        mConnection->Close ();

        // try to delete both as another user
        FdoPtr<FdoIConnection> connection;

	    connection = ArcSDETests::GetConnection ();
        connection->SetConnectionString (ArcSDETestConfig::ConnStringOzzie());
        connection->Open ();

        FdoPtr <FdoITransaction> transaction2;
        transaction2 = connection->BeginTransaction ();

        FdoPtr<FdoIDelete> del = (FdoIDelete*)connection->CreateCommand (FdoCommandType_Delete);
        del->SetFeatureClassName (ArcSDETestConfig::QClassNameSampleWithGeom());
        FdoCommonOSUtil::swprintf (filter, ELEMENTS(filter), L"%ls intersects GeomFromText ('POLYGON XY (( 0 0, 20 0, 20 20, 0 20, 0 0 ))')", AdjustRdbmsName(L"MyGeometry"));
        del->SetFilter (FdoPtr<FdoFilter>(FdoFilter::Parse (filter)));
        int count = del->Execute ();
        CPPUNIT_ASSERT_MESSAGE ("del succeeded", 0 == count);

        // get the conflict list and check it's contents
        conflicts = del->GetLockConflicts ();
        CPPUNIT_ASSERT_MESSAGE ("no conflict", conflicts->ReadNext ());
        ids = conflicts->GetIdentity ();
        property = ids->GetItem (0);
        value32 = (FdoInt32Value*)property->GetValue ();
        id = value32->GetInt32 ();
        if (identity1 != id)
            CPPUNIT_FAIL ("wrong object in conflict reader");
        CPPUNIT_ASSERT_MESSAGE ("too many conflicts", !conflicts->ReadNext ());

        transaction2->Commit ();
        connection->Close ();

        // switch back to the original user and check for changes
	    mConnection = ArcSDETests::GetConnection ();
        mConnection->SetConnectionString (ArcSDETestConfig::ConnStringMetadcov());
        mConnection->Open ();

        select = (FdoISelect*)mConnection->CreateCommand (FdoCommandType_Select);
        select->SetFeatureClassName (ArcSDETestConfig::QClassNameSampleWithGeom());
        reader = select->Execute ();
        int got = 0;
        while (reader->ReadNext ())
        {
            id = reader->GetInt32 (AdjustRdbmsName(L"Id"));
            if (identity1 == id)
                got |= 1;
            else if (identity2 == id)
                got |= 2;
            else
                CPPUNIT_FAIL ("unknown object");
        }
        CPPUNIT_ASSERT_MESSAGE ("features wrong", 3 == got);
    }
    catch (FdoException* ge) 
    {
        fail (ge);
    }
}

/* Test select for lock with conflict reader released first. */
void LockTests::select_conflicts_die_first ()
{
    FdoPtr<FdoPropertyValueCollection> ids;
    FdoPtr<FdoPropertyValue> property;
    FdoPtr<FdoInt32Value> value32;
    int id;

    if (!ArcSDETestConfig::SupportsLocking())   return;
    if (CreateSchemaOnly())  return;

    try
    {
        mConnection = ArcSDETests::GetConnection ();
        mConnection->SetConnectionString (ArcSDETestConfig::ConnStringMetadcov());
        mConnection->Open ();

        // Clean up previous tests:
        CleanUp();

        FdoPtr <FdoITransaction> transaction;
        transaction = mConnection->BeginTransaction ();

        FdoPtr<FdoIInsert> insert = (FdoIInsert*)mConnection->CreateCommand (FdoCommandType_Insert);
        insert->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        FdoPtr<FdoPropertyValueCollection> values = insert->GetPropertyValues ();
        FdoPtr<FdoValueExpression> expression = (FdoValueExpression*)ArcSDETests::ParseByDataType (L"'first lock'", FdoDataType_String);
        FdoPtr<FdoPropertyValue> value = FdoPropertyValue::Create (AdjustRdbmsName(L"LockProperty"), expression);
        values->Add (value);
        FdoPtr<FdoIFeatureReader> reader = insert->Execute ();
        reader->ReadNext ();
        int identity1 = reader->GetInt32 (AdjustRdbmsName(L"Id"));

        // don't lock it

        transaction->Commit ();

        mConnection->Close ();

        // insert and lock as another user
        FdoPtr<FdoIConnection> connection;

	    connection = ArcSDETests::GetConnection ();
        connection->SetConnectionString (ArcSDETestConfig::ConnStringOzzie());
        connection->Open ();

        FdoPtr <FdoITransaction> transaction2;
        transaction2 = connection->BeginTransaction ();

        insert = (FdoIInsert*)connection->CreateCommand (FdoCommandType_Insert);
        insert->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        values = insert->GetPropertyValues ();
        expression = (FdoValueExpression*)ArcSDETests::ParseByDataType (L"'second lock'", FdoDataType_String);
        value = FdoPropertyValue::Create (AdjustRdbmsName(L"LockProperty"), expression);
        values->Add (value);
        reader = insert->Execute ();
        reader->ReadNext ();
        int identity2 = reader->GetInt32 (AdjustRdbmsName(L"Id"));

        // lock it
        FdoPtr<FdoIAcquireLock> acquire = (FdoIAcquireLock*)connection->CreateCommand (FdoCommandType_AcquireLock);
        acquire->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        acquire->SetLockType (FdoLockType_Exclusive);
        acquire->SetLockStrategy (FdoLockStrategy_All);
        wchar_t filter[1024];
        FdoCommonOSUtil::swprintf (filter, ELEMENTS(filter), L"%ls = %d", AdjustRdbmsName(L"Id"), identity2);
        acquire->SetFilter (FdoPtr<FdoFilter>(FdoFilter::Parse (filter)));
        FdoPtr<FdoILockConflictReader> conflicts = acquire->Execute ();
        CPPUNIT_ASSERT_MESSAGE ("conflict", !conflicts->ReadNext ());

        transaction2->Commit ();
        connection->Close ();

        // switch back to the original user and select for lock
	    mConnection = ArcSDETests::GetConnection ();
        mConnection->SetConnectionString (ArcSDETestConfig::ConnStringMetadcov());
        mConnection->Open ();

        transaction = mConnection->BeginTransaction ();

        int got = 0;
        FdoPtr<FdoISelect> select;
        {
            select = (FdoISelect*)mConnection->CreateCommand (FdoCommandType_Select);
            select->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
            select->SetLockType (FdoLockType_Exclusive);
            select->SetLockStrategy (FdoLockStrategy_All);
            reader = select->ExecuteWithLock ();
            while (reader->ReadNext ())
            {
                id = reader->GetInt32 (AdjustRdbmsName(L"Id"));
                if (identity1 == id)
                    got |= 1;
                else if (identity2 == id)
                    got |= 2;
                else
                    CPPUNIT_FAIL ("unknown object");
            }
            CPPUNIT_ASSERT_MESSAGE ("features wrong", 3 == got);

            // check the conflict reader
            FdoPtr<FdoILockConflictReader> conflicts = select->GetLockConflicts ();
            CPPUNIT_ASSERT_MESSAGE ("no conflict", conflicts->ReadNext ());

            ids = conflicts->GetIdentity ();
            property = ids->GetItem (0);
            value32 = (FdoInt32Value*)property->GetValue ();
            id = value32->GetInt32 ();
            if (identity2 != id)
                CPPUNIT_FAIL ("wrong object in conflict reader");
        }

        transaction->Commit ();

        // check it's only object 2 that is locked
        CPPUNIT_ASSERT_MESSAGE("bad lock under this owner", 0==GetLockCount(mConnection, ArcSDETestConfig::UserNameMetadcov(), ArcSDETestConfig::QClassNameSample()));
        got = 0;
        FdoPtr <FdoIGetLockedObjects> get = (FdoIGetLockedObjects*)mConnection->CreateCommand (FdoCommandType_GetLockedObjects);
        FdoPtr<FdoILockedObjectReader> locked;
        get->SetLockOwner (ArcSDETestConfig::UserNameOzzie());
        locked = get->Execute ();
        while (locked->ReadNext ())
        {
            ids = locked->GetIdentity ();
            property = ids->GetItem (0);
            value32 = (FdoInt32Value*)property->GetValue ();
            id = value32->GetInt32 ();
            if (identity1 == id)
                CPPUNIT_FAIL ("bad lock under other owner");
            else if (identity2 == id)
                got |= 2;
            else
                CPPUNIT_FAIL ("unknown lock");
        }
        CPPUNIT_ASSERT_MESSAGE ("wrong conflicts", 2 == got);
    }
    catch (FdoException* ge) 
    {
        fail (ge);
    }
}

/* Test select for lock with feature reader released first. */
void LockTests::select_reader_dies_first ()
{
    FdoPtr<FdoPropertyValueCollection> ids;
    FdoPtr<FdoPropertyValue> property;
    FdoPtr<FdoInt32Value> value32;
    int id;

    if (!ArcSDETestConfig::SupportsLocking())   return;
    if (CreateSchemaOnly())  return;

    try
    {
        mConnection = ArcSDETests::GetConnection ();
        mConnection->SetConnectionString (ArcSDETestConfig::ConnStringMetadcov());
        mConnection->Open ();

        // Clean up previous tests:
        CleanUp();

        FdoPtr <FdoITransaction> transaction;
        transaction = mConnection->BeginTransaction ();

        FdoPtr<FdoIInsert> insert = (FdoIInsert*)mConnection->CreateCommand (FdoCommandType_Insert);
        insert->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        FdoPtr<FdoPropertyValueCollection> values = insert->GetPropertyValues ();
        FdoPtr<FdoValueExpression> expression = (FdoValueExpression*)ArcSDETests::ParseByDataType (L"'first lock'", FdoDataType_String);
        FdoPtr<FdoPropertyValue> value = FdoPropertyValue::Create (AdjustRdbmsName(L"LockProperty"), expression);
        values->Add (value);
        FdoPtr<FdoIFeatureReader> reader = insert->Execute ();
        reader->ReadNext ();
        int identity1 = reader->GetInt32 (AdjustRdbmsName(L"Id"));

        // don't lock it

        transaction->Commit ();

        mConnection->Close ();

        // insert and lock as another user
        FdoPtr<FdoIConnection> connection;

	    connection = ArcSDETests::GetConnection ();
        connection->SetConnectionString (ArcSDETestConfig::ConnStringOzzie());
        connection->Open ();

        FdoPtr <FdoITransaction> transaction2;
        transaction2 = connection->BeginTransaction ();

        insert = (FdoIInsert*)connection->CreateCommand (FdoCommandType_Insert);
        insert->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        values = insert->GetPropertyValues ();
        expression = (FdoValueExpression*)ArcSDETests::ParseByDataType (L"'second lock'", FdoDataType_String);
        value = FdoPropertyValue::Create (AdjustRdbmsName(L"LockProperty"), expression);
        values->Add (value);
        reader = insert->Execute ();
        reader->ReadNext ();
        int identity2 = reader->GetInt32 (AdjustRdbmsName(L"Id"));

        // lock it
        FdoPtr<FdoIAcquireLock> acquire = (FdoIAcquireLock*)connection->CreateCommand (FdoCommandType_AcquireLock);
        acquire->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        acquire->SetLockType (FdoLockType_Exclusive);
        acquire->SetLockStrategy (FdoLockStrategy_All);
        wchar_t filter[1024];
        FdoCommonOSUtil::swprintf (filter, ELEMENTS(filter), L"%ls = %d", AdjustRdbmsName(L"Id"), identity2);
        acquire->SetFilter (FdoPtr<FdoFilter>(FdoFilter::Parse (filter)));
        FdoPtr<FdoILockConflictReader> conflicts = acquire->Execute ();
        CPPUNIT_ASSERT_MESSAGE ("conflict", !conflicts->ReadNext ());

        transaction2->Commit ();
        connection->Close ();

        // switch back to the original user and select for lock
	    mConnection = ArcSDETests::GetConnection ();
        mConnection->SetConnectionString (ArcSDETestConfig::ConnStringMetadcov());
        mConnection->Open ();

        transaction = mConnection->BeginTransaction ();

        int got = 0;
        FdoPtr<FdoISelect> select;
        {
            select = (FdoISelect*)mConnection->CreateCommand (FdoCommandType_Select);
            select->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
            select->SetLockType (FdoLockType_Exclusive);
            select->SetLockStrategy (FdoLockStrategy_All);
            FdoPtr<FdoIFeatureReader> reader = select->ExecuteWithLock ();
            while (reader->ReadNext ())
            {
                id = reader->GetInt32 (AdjustRdbmsName(L"Id"));
                if (identity1 == id)
                    got |= 1;
                else if (identity2 == id)
                    got |= 2;
                else
                    CPPUNIT_FAIL ("unknown object");
            }
            CPPUNIT_ASSERT_MESSAGE ("features wrong", 3 == got);

            // check the conflict reader
            conflicts = select->GetLockConflicts ();
            CPPUNIT_ASSERT_MESSAGE ("no conflict", conflicts->ReadNext ());

            ids = conflicts->GetIdentity ();
            property = ids->GetItem (0);
            value32 = (FdoInt32Value*)property->GetValue ();
            id = value32->GetInt32 ();
            if (identity2 != id)
                CPPUNIT_FAIL ("wrong object in conflict reader");
        }

        transaction->Commit ();

        // check it's only object 2 that is locked
        CPPUNIT_ASSERT_MESSAGE("bad lock under this owner", 0==GetLockCount(mConnection, ArcSDETestConfig::UserNameMetadcov(), ArcSDETestConfig::QClassNameSample()));
        got = 0;
        FdoPtr <FdoIGetLockedObjects> get = (FdoIGetLockedObjects*)mConnection->CreateCommand (FdoCommandType_GetLockedObjects);
        FdoPtr<FdoILockedObjectReader> locked;
        get->SetLockOwner (ArcSDETestConfig::UserNameOzzie());
        locked = get->Execute ();
        while (locked->ReadNext ())
        {
            ids = locked->GetIdentity ();
            property = ids->GetItem (0);
            value32 = (FdoInt32Value*)property->GetValue ();
            id = value32->GetInt32 ();
            if (identity1 == id)
                CPPUNIT_FAIL ("bad lock under other owner");
            else if (identity2 == id)
                got |= 2;
            else
                CPPUNIT_FAIL ("unknown lock");
        }
        CPPUNIT_ASSERT_MESSAGE ("wrong conflicts", 2 == got);
    }
    catch (FdoException* ge) 
    {
        fail (ge);
    }
}

/* Test select for lock with select command released first. */
void LockTests::select_select_dies_first ()
{
    FdoPtr<FdoPropertyValueCollection> ids;
    FdoPtr<FdoPropertyValue> property;
    FdoPtr<FdoInt32Value> value32;
    int id;

    if (!ArcSDETestConfig::SupportsLocking())   return;
    if (CreateSchemaOnly())  return;

    try
    {
        mConnection = ArcSDETests::GetConnection ();
        mConnection->SetConnectionString (ArcSDETestConfig::ConnStringMetadcov());
        mConnection->Open ();

        // Clean up previous tests:
        CleanUp();

        FdoPtr <FdoITransaction> transaction;
        transaction = mConnection->BeginTransaction ();

        FdoPtr<FdoIInsert> insert = (FdoIInsert*)mConnection->CreateCommand (FdoCommandType_Insert);
        insert->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        FdoPtr<FdoPropertyValueCollection> values = insert->GetPropertyValues ();
        FdoPtr<FdoValueExpression> expression = (FdoValueExpression*)ArcSDETests::ParseByDataType (L"'first lock'", FdoDataType_String);
        FdoPtr<FdoPropertyValue> value = FdoPropertyValue::Create (AdjustRdbmsName(L"LockProperty"), expression);
        values->Add (value);
        FdoPtr<FdoIFeatureReader> reader = insert->Execute ();
        reader->ReadNext ();
        int identity1 = reader->GetInt32 (AdjustRdbmsName(L"Id"));

        // don't lock it

        transaction->Commit ();

        mConnection->Close ();

        // insert and lock as another user
        FdoPtr<FdoIConnection> connection;

	    connection = ArcSDETests::GetConnection ();
        connection->SetConnectionString (ArcSDETestConfig::ConnStringOzzie());
        connection->Open ();

        FdoPtr <FdoITransaction> transaction2;
        transaction2 = connection->BeginTransaction ();

        insert = (FdoIInsert*)connection->CreateCommand (FdoCommandType_Insert);
        insert->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        values = insert->GetPropertyValues ();
        expression = (FdoValueExpression*)ArcSDETests::ParseByDataType (L"'second lock'", FdoDataType_String);
        value = FdoPropertyValue::Create (AdjustRdbmsName(L"LockProperty"), expression);
        values->Add (value);
        reader = insert->Execute ();
        reader->ReadNext ();
        int identity2 = reader->GetInt32 (AdjustRdbmsName(L"Id"));

        // lock it
        FdoPtr<FdoIAcquireLock> acquire = (FdoIAcquireLock*)connection->CreateCommand (FdoCommandType_AcquireLock);
        acquire->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        acquire->SetLockType (FdoLockType_Exclusive);
        acquire->SetLockStrategy (FdoLockStrategy_All);
        wchar_t filter[1024];
        FdoCommonOSUtil::swprintf (filter, ELEMENTS(filter), L"%ls = %d", AdjustRdbmsName(L"Id"), identity2);
        acquire->SetFilter (FdoPtr<FdoFilter>(FdoFilter::Parse (filter)));
        FdoPtr<FdoILockConflictReader> conflicts = acquire->Execute ();
        CPPUNIT_ASSERT_MESSAGE ("conflict", !conflicts->ReadNext ());

        transaction2->Commit ();
        connection->Close ();

        // switch back to the original user and select for lock
	    mConnection = ArcSDETests::GetConnection ();
        mConnection->SetConnectionString (ArcSDETestConfig::ConnStringMetadcov());
        mConnection->Open ();

        transaction = mConnection->BeginTransaction ();

        int got = 0;
        {
            FdoPtr<FdoISelect> select = (FdoISelect*)mConnection->CreateCommand (FdoCommandType_Select);
            select->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
            select->SetLockType (FdoLockType_Exclusive);
            select->SetLockStrategy (FdoLockStrategy_All);
            reader = select->ExecuteWithLock ();
            while (reader->ReadNext ())
            {
                id = reader->GetInt32 (AdjustRdbmsName(L"Id"));
                if (identity1 == id)
                    got |= 1;
                else if (identity2 == id)
                    got |= 2;
                else
                    CPPUNIT_FAIL ("unknown object");
            }
            CPPUNIT_ASSERT_MESSAGE ("features wrong", 3 == got);

            // check the conflict reader
            conflicts = select->GetLockConflicts ();
            CPPUNIT_ASSERT_MESSAGE ("no conflict", conflicts->ReadNext ());

            ids = conflicts->GetIdentity ();
            property = ids->GetItem (0);
            value32 = (FdoInt32Value*)property->GetValue ();
            id = value32->GetInt32 ();
            if (identity2 != id)
                CPPUNIT_FAIL ("wrong object in conflict reader");
        }

        transaction->Commit ();

        // check it's only object 2 that is locked
        CPPUNIT_ASSERT_MESSAGE("bad lock under this owner", 0==GetLockCount(mConnection, ArcSDETestConfig::UserNameMetadcov(), ArcSDETestConfig::QClassNameSample()));
        got = 0;
        FdoPtr <FdoIGetLockedObjects> get = (FdoIGetLockedObjects*)mConnection->CreateCommand (FdoCommandType_GetLockedObjects);
        FdoPtr<FdoILockedObjectReader> locked;
        get->SetLockOwner (ArcSDETestConfig::UserNameOzzie());
        locked = get->Execute ();
        while (locked->ReadNext ())
        {
            ids = locked->GetIdentity ();
            property = ids->GetItem (0);
            value32 = (FdoInt32Value*)property->GetValue ();
            id = value32->GetInt32 ();
            if (identity1 == id)
                CPPUNIT_FAIL ("bad lock under other owner");
            else if (identity2 == id)
                got |= 2;
            else
                CPPUNIT_FAIL ("unknown lock");
        }
        CPPUNIT_ASSERT_MESSAGE ("wrong conflicts", 2 == got);
    }
    catch (FdoException* ge) 
    {
        fail (ge);
    }
}

/* Test release lock with somebody else's lock present. */
void LockTests::release_partial ()
{
    FdoPtr<FdoPropertyValueCollection> ids;
    FdoPtr<FdoPropertyValue> property;
    FdoPtr<FdoInt32Value> value32;
    int id;

    if (!ArcSDETestConfig::SupportsLocking())   return;
    if (CreateSchemaOnly())  return;

    try
    {
        mConnection = ArcSDETests::GetConnection ();
        mConnection->SetConnectionString (ArcSDETestConfig::ConnStringMetadcov());
        mConnection->Open ();

        // Clean up previous tests:
        CleanUp();

        FdoPtr <FdoITransaction> transaction;
        transaction = mConnection->BeginTransaction ();

        FdoPtr<FdoIInsert> insert = (FdoIInsert*)mConnection->CreateCommand (FdoCommandType_Insert);
        insert->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        FdoPtr<FdoPropertyValueCollection> values = insert->GetPropertyValues ();
        FdoPtr<FdoValueExpression> expression = (FdoValueExpression*)ArcSDETests::ParseByDataType (L"'first lock'", FdoDataType_String);
        FdoPtr<FdoPropertyValue> value = FdoPropertyValue::Create (AdjustRdbmsName(L"LockProperty"), expression);
        values->Add (value);
        FdoPtr<FdoIFeatureReader> reader = insert->Execute ();
        reader->ReadNext ();
        int identity1 = reader->GetInt32 (AdjustRdbmsName(L"Id"));

        // lock it
        FdoPtr <FdoIAcquireLock> acquire = (FdoIAcquireLock*)mConnection->CreateCommand (FdoCommandType_AcquireLock);
        acquire->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        acquire->SetLockType (FdoLockType_Exclusive);
        acquire->SetLockStrategy (FdoLockStrategy_All);
        FdoPtr<FdoILockConflictReader> conflicts = acquire->Execute ();
        CPPUNIT_ASSERT_MESSAGE ("conflict", !conflicts->ReadNext ());

        transaction->Commit ();

        mConnection->Close ();

        // switch to another user insert an object and lock it
        FdoPtr<FdoIConnection> connection;

	    connection = ArcSDETests::GetConnection ();
        connection->SetConnectionString (ArcSDETestConfig::ConnStringOzzie());
        connection->Open ();

        FdoPtr <FdoITransaction> transaction2;
        transaction2 = connection->BeginTransaction ();

        insert = (FdoIInsert*)connection->CreateCommand (FdoCommandType_Insert);
        insert->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        values = insert->GetPropertyValues ();
        expression = (FdoValueExpression*)ArcSDETests::ParseByDataType (L"'second lock'", FdoDataType_String);
        value = FdoPropertyValue::Create (AdjustRdbmsName(L"LockProperty"), expression);
        values->Add (value);
        reader = insert->Execute ();
        reader->ReadNext ();
        int identity2 = reader->GetInt32 (AdjustRdbmsName(L"Id"));

        // lock it
        acquire = (FdoIAcquireLock*)connection->CreateCommand (FdoCommandType_AcquireLock);
        acquire->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        acquire->SetLockType (FdoLockType_Exclusive);
        acquire->SetLockStrategy (FdoLockStrategy_All);
        wchar_t filter[1024];
        FdoCommonOSUtil::swprintf (filter, ELEMENTS(filter), L"%ls = %d", AdjustRdbmsName(L"Id"), identity2);
        acquire->SetFilter (FdoPtr<FdoFilter>(FdoFilter::Parse (filter)));
        conflicts = acquire->Execute ();
        CPPUNIT_ASSERT_MESSAGE ("conflict", !conflicts->ReadNext ());

        transaction2->Commit ();

        transaction2 = connection->BeginTransaction ();

        // now try and unlock everything
        FdoPtr <FdoIReleaseLock> release = (FdoIReleaseLock*)connection->CreateCommand (FdoCommandType_ReleaseLock);
        release->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        conflicts = release->Execute ();
        CPPUNIT_ASSERT_MESSAGE ("no conflict", conflicts->ReadNext ());

        // check it's only object 1 that is in conflict
        ids = conflicts->GetIdentity ();
        property = ids->GetItem (0);
        value32 = (FdoInt32Value*)property->GetValue ();
        id = value32->GetInt32 ();
        if (identity1 != id)
            CPPUNIT_FAIL ("wrong object in conflict reader");

        // check object 2 is unlocked
        FdoPtr <FdoIGetLockedObjects> get = (FdoIGetLockedObjects*)connection->CreateCommand (FdoCommandType_GetLockedObjects);
        FdoPtr<FdoILockedObjectReader> locked = get->Execute ();
        while (locked->ReadNext ())
        {
            if (0 == wcscmp (ArcSDETestConfig::QClassNameSample(), locked->GetFeatureClassName ()))
            {
                ids = locked->GetIdentity ();
                property = ids->GetItem (0);
                value32 = (FdoInt32Value*)property->GetValue ();
                id = value32->GetInt32 ();
                if (identity1 == id)
                    CPPUNIT_FAIL ("bad lock under other owner");
                else if (identity2 == id)
                    CPPUNIT_FAIL ("still locked");
                else
                    CPPUNIT_FAIL ("unknown lock");
            }
        }

        transaction2->Commit ();
        connection->Close ();

	    mConnection = ArcSDETests::GetConnection ();
        mConnection->SetConnectionString (ArcSDETestConfig::ConnStringMetadcov());
        mConnection->Open ();
    }
    catch (FdoException* ge) 
    {
        fail (ge);
    }
}

/* Test releasing somebody else's lock. */
void LockTests::release_anothers ()
{
    FdoPtr<FdoPropertyValueCollection> ids;
    FdoPtr<FdoPropertyValue> property;
    FdoPtr<FdoInt32Value> value32;
    int id;

    if (!ArcSDETestConfig::SupportsLocking())   return;
    if (CreateSchemaOnly())  return;

    try
    {
        mConnection = ArcSDETests::GetConnection ();
        mConnection->SetConnectionString (ArcSDETestConfig::ConnStringMetadcov());
        mConnection->Open ();

        // Clean up previous tests:
        CleanUp();

        FdoPtr <FdoITransaction> transaction;
        transaction = mConnection->BeginTransaction ();

        FdoPtr<FdoIInsert> insert = (FdoIInsert*)mConnection->CreateCommand (FdoCommandType_Insert);
        insert->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        FdoPtr<FdoPropertyValueCollection> values = insert->GetPropertyValues ();
        FdoPtr<FdoValueExpression> expression = (FdoValueExpression*)ArcSDETests::ParseByDataType (L"'first lock'", FdoDataType_String);
        FdoPtr<FdoPropertyValue> value = FdoPropertyValue::Create (AdjustRdbmsName(L"LockProperty"), expression);
        values->Add (value);
        FdoPtr<FdoIFeatureReader> reader = insert->Execute ();
        reader->ReadNext ();
        int identity1 = reader->GetInt32 (AdjustRdbmsName(L"Id"));

        // lock it
        FdoPtr <FdoIAcquireLock> acquire = (FdoIAcquireLock*)mConnection->CreateCommand (FdoCommandType_AcquireLock);
        acquire->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        acquire->SetLockType (FdoLockType_Exclusive);
        acquire->SetLockStrategy (FdoLockStrategy_All);
        FdoPtr<FdoILockConflictReader> conflicts = acquire->Execute ();
        CPPUNIT_ASSERT_MESSAGE ("conflict", !conflicts->ReadNext ());

        transaction->Commit ();

        mConnection->Close ();

        // switch to another and unlock it
        FdoPtr<FdoIConnection> connection;

	    connection = ArcSDETests::GetConnection ();
        connection->SetConnectionString (ArcSDETestConfig::ConnStringOzzie());
        connection->Open ();

        FdoPtr <FdoITransaction> transaction2;
        transaction2 = connection->BeginTransaction ();

        // now try and unlock everything
        FdoPtr <FdoIReleaseLock> release = (FdoIReleaseLock*)connection->CreateCommand (FdoCommandType_ReleaseLock);
        release->SetFeatureClassName (ArcSDETestConfig::QClassNameSample());
        release->SetLockOwner (ArcSDETestConfig::UserNameMetadcov());
        conflicts = release->Execute ();
        CPPUNIT_ASSERT_MESSAGE ("conflict", !conflicts->ReadNext ());

        // check the object is unlocked
        FdoPtr <FdoIGetLockedObjects> get = (FdoIGetLockedObjects*)connection->CreateCommand (FdoCommandType_GetLockedObjects);
        get->SetLockOwner (ArcSDETestConfig::UserNameMetadcov());
        FdoPtr<FdoILockedObjectReader> locked = get->Execute ();
        while (locked->ReadNext ())
        {
            if (0 == wcscmp (ArcSDETestConfig::QClassNameSample(), locked->GetFeatureClassName ()))
            {
                ids = locked->GetIdentity ();
                property = ids->GetItem (0);
                value32 = (FdoInt32Value*)property->GetValue ();
                id = value32->GetInt32 ();
                if (identity1 == id)
                    CPPUNIT_FAIL ("lock still present");
                else
                    CPPUNIT_FAIL ("unknown lock");
            }
        }

        transaction2->Commit ();
        connection->Close ();

	    mConnection = ArcSDETests::GetConnection ();
        mConnection->SetConnectionString (ArcSDETestConfig::ConnStringMetadcov());
        mConnection->Open ();
    }
    catch (FdoException* ge) 
    {
        fail (ge);
    }
}


void LockTests::CleanUp(void)
{
    FdoPtr <FdoITransaction> transaction;
    FdoPtr<FdoIConnection> connection;

    // Delete all rows from ArcSDETestConfig::QClassNameSample():
    {
        // use a local FdoITransaction, to avoid the connection being freed before the transaction in case of error.
        FdoPtr <FdoITransaction> transaction2;  

        // Delete rows owned by secondary user:
        connection = ArcSDETests::GetConnection ();
        connection->SetConnectionString (ArcSDETestConfig::ConnStringOzzie());
        connection->Open ();
        transaction2 = connection->BeginTransaction ();
        FdoPtr<FdoIDelete> del = static_cast<FdoIDelete*>(connection->CreateCommand(FdoCommandType_Delete));
        del->SetFeatureClassName(ArcSDETestConfig::QClassNameSample());
        wchar_t filter[1024];
        FdoCommonOSUtil::swprintf (filter, ELEMENTS(filter), L"%ls like 'second%%'", AdjustRdbmsName(L"LockProperty"));
        del->SetFilter(filter);
        del->Execute();
        transaction2->Commit();
        connection->Close();
        connection = NULL;

        // Delete rows owned by primary user:
        transaction = mConnection->BeginTransaction();
        CleanUpClass(mConnection, ArcSDETestConfig::ClassSchemaSample(), ArcSDETestConfig::ClassNameSample(), true);
        transaction->Commit();
    }

    // Delete all rows from ArcSDETestConfig::QClassNameSampleNonLocking()
    {
        // Delete rows owned by primary user:
        transaction = mConnection->BeginTransaction();
        CleanUpClass(mConnection, ArcSDETestConfig::ClassSchemaSampleNonLocking(), ArcSDETestConfig::QClassNameSampleNonLocking(), true);
        transaction->Commit();
    }

    // Delete all rows from ArcSDETestConfig::QClassNameSampleWithGeom()
    {
        // Delete rows owned by primary user:
        transaction = mConnection->BeginTransaction();
        CleanUpClass(mConnection, ArcSDETestConfig::ClassSchemaSampleWithGeom(), ArcSDETestConfig::QClassNameSampleWithGeom(), true);
        transaction->Commit();
    }
}
