Exemplo n.º 1
0
FdoSchemaManagerP OdbcStaticConnection::CreateSchemaManager()
{
    FdoSchemaManagerP schMgr = new FdoOdbcSchemaManager(m_gdbi_conn, (FdoString*) FdoStringP(mDatastore));
    FdoSmPhOdbcMgrP physMgr = schMgr->GetPhysicalSchema()->SmartCast<FdoSmPhOdbcMgr>();

    // Pass down the location of the COM directory for MetaSchema creation scripts.
    physMgr->SetHomeDir( FdoStringP(L"Dbg/com") );
    physMgr->SetRdbiContext( m_rdbi_context );

    return schMgr;
}
void MySqlFdoInsertTest::characterSets()
{
    StaticConnection* conn = new MySqlStaticConnection();

    try {

        UnitTestUtil::SetProvider( conn->GetServiceName() ); 

        conn->connect();

        FdoSchemaManagerP mgr = conn->CreateSchemaManager();

        FdoSmPhMgrP phMgr = mgr->GetPhysicalSchema();

        FdoStringP datastore = phMgr->GetDcOwnerName(
            UnitTestUtil::GetEnviron("datastore", CHARSET_SUFFIX)
        );

        FdoSmPhDatabaseP database = phMgr->GetDatabase();

        FdoSmPhOwnerP owner = phMgr->FindOwner( datastore, L"", false );
        if ( owner ) {
            owner->SetElementState( FdoSchemaElementState_Deleted );
            owner->Commit();
        }

        owner = database->CreateOwner(
            datastore, 
            false
        );
        owner->SetPassword( L"test" );
        owner->Commit();
        owner->SetCurrent();

        FdoSmPhGrdMgrP grdMgr = phMgr->SmartCast<FdoSmPhGrdMgr>();
        GdbiConnection* gdbiConnection = grdMgr->GetGdbiConnection();

        // Create tables for various combinations of character set and types of characters
        // that will be stored in the table. 
        charSetCreateTable( gdbiConnection, L"latin1", L"ascii7" );
        charSetCreateTable( gdbiConnection, L"latin1", L"8bit" );
        charSetCreateTable( gdbiConnection, L"utf8", L"ascii7" );
        charSetCreateTable( gdbiConnection, L"utf8", L"8bit" );
        charSetCreateTable( gdbiConnection, L"utf8", L"japan" );
        charSetCreateTable( gdbiConnection, L"ucs2", L"ascii7" );
        charSetCreateTable( gdbiConnection, L"ucs2", L"8bit" );
        charSetCreateTable( gdbiConnection, L"ucs2", L"japan" );
        charSetCreateTable( gdbiConnection, L"cp932", L"ascii7" );
        charSetCreateTable( gdbiConnection, L"cp932", L"japan" );

        phMgr = NULL;
        mgr = NULL;
        conn->disconnect();
        delete conn;
        conn = NULL;
        
        // A couple of combinations are not tried:
        //  - latin11 character set, Japanese characters
        //  = cp932 character set, 8 bit characters
        // since these are expected to fail (characters not in character set).

        FdoPtr<FdoIConnection> connection = UnitTestUtil::GetConnection(CHARSET_SUFFIX, false);

        // Round-trip insert/select test each table.
        charSetTestTable( connection, L"latin1", L"ascii7", L"abc", L"a" );
        charSetTestTable( connection, L"latin1", L"8bit", L"\x00e4\x00e5", L"\x00e4" );
        charSetTestTable( connection, L"utf8", L"ascii7", L"abc", L"a" );
        charSetTestTable( connection, L"utf8", L"8bit", L"\x00e4\x00e5", L"\x00e4" );
        charSetTestTable( connection, L"utf8", L"japan", L"\x30b0\x30b1", L"\x30b0" );
        charSetTestTable( connection, L"ucs2", L"ascii7", L"abc", L"a" );
        charSetTestTable( connection, L"ucs2", L"8bit", L"\x00e4\x00e5", L"\x00e4" );
        charSetTestTable( connection, L"ucs2", L"japan", L"\x30b0\x30b1", L"\x30b0" );
        charSetTestTable( connection, L"cp932", L"ascii7", L"abc", L"a" );
        charSetTestTable( connection, L"cp932", L"japan", L"\x30b0\x30b1", L"\x30b0" );
    }
    catch (FdoCommandException *ex)
    {
        if (conn)
        {
            conn->disconnect();
            delete conn;
        }
        UnitTestUtil::FailOnException(ex);
    }
    catch (FdoException *ex)
    {
        if (conn)
        {
            conn->disconnect();
            delete conn;
        }
        UnitTestUtil::FailOnException(ex);
    }
}
void MySqlFdoInsertTest::insertBoundaryUnsigned()
{
    StaticConnection* conn = new MySqlStaticConnection();

    try {

        UnitTestUtil::SetProvider( conn->GetServiceName() ); 

        conn->connect();

        FdoSchemaManagerP mgr = conn->CreateSchemaManager();

        FdoSmPhMgrP phMgr = mgr->GetPhysicalSchema();

        FdoStringP datastore = phMgr->GetDcOwnerName(
            UnitTestUtil::GetEnviron("datastore", UNSIGNED_SUFFIX)
        );

        FdoSmPhDatabaseP database = phMgr->GetDatabase();

        FdoSmPhOwnerP owner = phMgr->FindOwner( datastore, L"", false );
        if ( owner ) {
            owner->SetElementState( FdoSchemaElementState_Deleted );
            owner->Commit();
        }

        owner = database->CreateOwner(
            datastore, 
            false
        );
        owner->SetPassword( L"test" );

        FdoStringP tableName = L"unsigned_test";
            
        FdoSmPhTableP table = owner->CreateTable( tableName );
        table->SetPkeyName( tableName + L"_key" );
        FdoSmPhColumnP column = table->CreateColumnInt32(
            L"id",
            false
        );
        table->AddPkeyCol( column->GetName() );
        column = table->CreateColumnUnknown(
            L"uint_column",
            L"int unsigned",
            false,
            0,
            0
        );
        owner->Commit();
        
        phMgr = NULL;
        mgr = NULL;
        conn->disconnect();
        delete conn;
        conn = NULL;

        FdoPtr<FdoIConnection> connection = UnitTestUtil::GetConnection(UNSIGNED_SUFFIX, false);
        FdoPtr<FdoITransaction> featureTransaction = connection->BeginTransaction();
        FdoPtr<FdoIInsert> insertCommand = (FdoIInsert *) connection->CreateCommand(FdoCommandType_Insert);
        insertCommand->SetFeatureClassName(tableName);
        FdoPtr<FdoPropertyValueCollection> propertyValues = insertCommand->GetPropertyValues();

        FdoPtr<FdoDataValue> dataValue;
        dataValue = FdoDataValue::Create(L"1");
        FdoPtr<FdoPropertyValue> propertyValue = AddNewProperty( propertyValues, L"id");
        propertyValue->SetValue(dataValue);

        dataValue = FdoDataValue::Create(L"0");
        propertyValue = AddNewProperty( propertyValues, L"uint_column");
        propertyValue->SetValue(dataValue);

        FdoPtr<FdoIFeatureReader> reader = insertCommand->Execute();

        dataValue = FdoDataValue::Create(L"2");
        propertyValue = AddNewProperty( propertyValues, L"id");
        propertyValue->SetValue(dataValue);

        dataValue = FdoDataValue::Create(L"4294967295");
        propertyValue = AddNewProperty( propertyValues, L"uint_column");
        propertyValue->SetValue(dataValue);

        reader = insertCommand->Execute();

        featureTransaction->Commit();

        // check 
    	FdoPtr<FdoISelect> selectCmd = (FdoISelect *) connection->CreateCommand(FdoCommandType_Select);
	    selectCmd->SetFeatureClassName(tableName);

    	FdoPtr<FdoIFeatureReader> featureReader = selectCmd->Execute();
        FdoInt32 rowCount = 0;

        while ( featureReader->ReadNext() ) {
            rowCount++;

            switch ( featureReader->GetInt32(L"id") ) {
            case 1:
                CPPUNIT_ASSERT ( featureReader->GetInt64(L"uint_column") == 0 );
                break;

            case 2:
                CPPUNIT_ASSERT ( featureReader->GetInt64(L"uint_column") == 4294967295LL);
                break;
            }
        }
        CPPUNIT_ASSERT( rowCount == 2 );    
    }
    catch (FdoCommandException *ex)
    {
        if (conn)
        {
            conn->disconnect();
            delete conn;
        }
        UnitTestUtil::FailOnException(ex);
    }
    catch (FdoException *ex)
    {
        if (conn)
        {
            conn->disconnect();
            delete conn;
        }
        UnitTestUtil::FailOnException(ex);
    }
}
// Tests the creating of unique constraints that are too large for 
// MySQL. Verifies that the MySQL provider properly truncates
// constraint columns to get the total size within the maximum allowed.
void MySqlSchemaMgrTests::testWideConstraint ()
{
    StaticConnection* conn = CreateStaticConnection();
    FdoPtr<FdoIConnection> fdoConn;
    FdoInt32 ix;

    try
    {
        char prvenv[100];
        FdoStringP providerName = conn->GetServiceName();
        sprintf( prvenv, "provider=%ls", (FdoString*) providerName );
#ifdef _WIN32
        _putenv( prvenv );
#else
        putenv( prvenv );
#endif

        // Sets the other env.
        UnitTestUtil::SetProvider( conn->GetServiceName() ); 

        printf( "\nOpening Connection ...\n" );

        conn->connect();

        FdoSchemaManagerP mgr = conn->CreateSchemaManager();

        FdoSmPhGrdMgrP phMgr = mgr->GetPhysicalSchema()->SmartCast<FdoSmPhGrdMgr>();

        FdoSmPhDatabaseP database = phMgr->GetDatabase();

        printf( "Predeleting schema ...\n" );

        FdoStringP datastore = phMgr->GetDcOwnerName(
            UnitTestUtil::GetEnviron("datastore", DB_NAME_SUFFIX)
        );

        FdoSmPhOwnerP owner = phMgr->FindOwner( datastore, L"", false );
        if ( owner ) {
            owner->SetElementState( FdoSchemaElementState_Deleted );
            owner->Commit();
        }

        printf( "Creating schema ...\n" );

        owner = database->CreateOwner(
            datastore, 
            false
        );
        owner->SetPassword( L"test" );

        owner->Commit();
        FdoInt32 charLen = (FdoInt32)(owner->SmartCast<FdoSmPhMySqlOwner>()->GetCharacterSet()->SmartCast<FdoSmPhMySqlCharacterSet>()->GetCharLen());

        // Table1 tests various column types and constraints 
        // with various numbers and sizes of columns.
        // Primary Keys, indexes and unique constraints are tested.

        FdoSmPhTableP table = owner->CreateTable( phMgr->GetDcDbObjectName(L"TABLE1" ));
        FdoSmPhColumnP column = table->CreateColumnInt32( L"ID", false );
        table->AddPkeyCol( column->GetName() );

        // Columns of various types (tested by adding to primary key) 
        column = table->CreateColumnBLOB( L"BLOB_COLUMN", true );
        table->AddPkeyCol( column->GetName() );
        column = table->CreateColumnChar( L"STRING_COLUMN", false, 50 );
        table->AddPkeyCol( column->GetName() );
        column = table->CreateColumnSingle( L"SINGLE_COLUMN", true );
        table->AddPkeyCol( column->GetName() );
        column = table->CreateColumnDouble( L"DOUBLE_COLUMN", true );
        table->AddPkeyCol( column->GetName() );
        column = table->CreateColumnDecimal( L"DECIMAL_COLUMN", true, 65, 0 );
        table->AddPkeyCol( column->GetName() );
        column = table->CreateColumnBool( L"BOOL_COLUMN", true );
        table->AddPkeyCol( column->GetName() );
        column = table->CreateColumnByte( L"BYTE_COLUMN", true );
        table->AddPkeyCol( column->GetName() );
        column = table->CreateColumnInt16( L"INT16_COLUMN", true );
        table->AddPkeyCol( column->GetName() );
        column = table->CreateColumnInt32( L"INT32_COLUMN", true );
        table->AddPkeyCol( column->GetName() );
        column = table->CreateColumnInt64( L"INT64_COLUMN", true );
        table->AddPkeyCol( column->GetName() );
        column = table->CreateColumnDate( L"DATE_COLUMN", true );
        table->AddPkeyCol( column->GetName() );
        AddProviderColumns( table );
        column = table->GetColumns()->GetItem( L"BINARY_COLUMN" );
        table->AddPkeyCol( column->GetName() );
        column = table->GetColumns()->GetItem( L"MEDIUMBLOB_COLUMN" );
        column = table->GetColumns()->GetItem( L"TINYBLOB_COLUMN" );
        column = table->GetColumns()->GetItem( L"VARBINARY_COLUMN" );
        table->AddPkeyCol( column->GetName() );
        column = table->GetColumns()->GetItem( L"DATE2_COLUMN" );
        table->AddPkeyCol( column->GetName() );
        column = table->GetColumns()->GetItem( L"TIMESTAMP_COLUMN" );
        table->AddPkeyCol( column->GetName() );

        // Columns of various length.

        column = table->CreateColumnChar( L"STRING100000", false, 100000 );
        column = table->CreateColumnChar( L"STRING2000", false, 2000 );
        column = table->CreateColumnChar( L"STRING700_1", false, 700 );
        column = table->CreateColumnChar( L"STRING700_2", false, 700 );

        for ( ix = 0; ix < 10; ix++ ) {
            column = table->CreateColumnChar( 
                FdoStringP::Format(L"STRING300_%d", ix + 1),
                false,
                300 
            );
        }

        for ( ix = 0; ix < 10; ix++ ) {
            column = table->CreateColumnChar( 
                FdoStringP::Format(L"STRING100_%d", ix + 1),
                false,
                100 
            );
        }

        for ( ix = 0; ix < 10; ix++ ) {
            column = table->CreateColumnChar( 
                FdoStringP::Format(L"STRING40_%d", ix + 1),
                false,
                40 
            );
        }

        // Various unique constraints

        // One large (will be truncated) column, one small (not truncated)
        FdoSmPhColumnsP	ukeyColumns = new FdoSmPhColumnCollection();
	    table->GetUkeyColumns()->Add( ukeyColumns );
        table->AddUkeyCol( table->GetUkeyColumns()->GetCount() - 1, L"STRING100000" );
        table->AddUkeyCol( table->GetUkeyColumns()->GetCount() - 1, L"STRING100_1" );

        // Two large, one small. 
        ukeyColumns = new FdoSmPhColumnCollection();
	    table->GetUkeyColumns()->Add( ukeyColumns );
        table->AddUkeyCol( table->GetUkeyColumns()->GetCount() - 1, L"STRING2000" );
        table->AddUkeyCol( table->GetUkeyColumns()->GetCount() - 1, L"STRING700_1" );
        table->AddUkeyCol( table->GetUkeyColumns()->GetCount() - 1, L"STRING300_1" );

        table->Commit();

        // Three large, two small
        ukeyColumns = new FdoSmPhColumnCollection();
	    table->GetUkeyColumns()->Add( ukeyColumns );
        table->AddUkeyCol( table->GetUkeyColumns()->GetCount() - 1, L"STRING100000" );
        table->AddUkeyCol( table->GetUkeyColumns()->GetCount() - 1, L"STRING2000" );
        table->AddUkeyCol( table->GetUkeyColumns()->GetCount() - 1, L"STRING700_1" );
        table->AddUkeyCol( table->GetUkeyColumns()->GetCount() - 1, L"STRING100_3" );
        table->AddUkeyCol( table->GetUkeyColumns()->GetCount() - 1, L"STRING100_4" );

        // Constraint will maximum number of columns (16): 11 large, 5 small.
        ukeyColumns = new FdoSmPhColumnCollection();
	    table->GetUkeyColumns()->Add( ukeyColumns );
        table->AddUkeyCol( table->GetUkeyColumns()->GetCount() - 1, L"STRING2000" );

        for ( ix = 0; ix < 10; ix++ ) {
            table->AddUkeyCol( 
                table->GetUkeyColumns()->GetCount() - 1, 
                FdoStringP::Format(L"STRING300_%d", ix + 1)
            );
        }

        for ( ix = 0; ix < 4; ix++ ) {
            table->AddUkeyCol( 
                table->GetUkeyColumns()->GetCount() - 1, 
                FdoStringP::Format(L"STRING100_%d", ix + 1)
            );
        }

        for ( ix = 0; ix < 1; ix++ ) {
            table->AddUkeyCol( 
                table->GetUkeyColumns()->GetCount() - 1, 
                FdoStringP::Format(L"STRING40_%d", ix + 1)
            );
        }

        // No large columns, no truncation.
        // Tests columns whose size is over the initial truncation size but
        // under final truncation size.
        ukeyColumns = new FdoSmPhColumnCollection();
	    table->GetUkeyColumns()->Add( ukeyColumns );
        table->AddUkeyCol( table->GetUkeyColumns()->GetCount() - 1, L"STRING100_3" );
        table->AddUkeyCol( table->GetUkeyColumns()->GetCount() - 1, L"STRING100_4" );

        // No large columns, no truncation.
        // Tests columns whose size is under the initial truncation size.
        ukeyColumns = new FdoSmPhColumnCollection();
	    table->GetUkeyColumns()->Add( ukeyColumns );
        table->AddUkeyCol( table->GetUkeyColumns()->GetCount() - 1, L"STRING40_3" );
        table->AddUkeyCol( table->GetUkeyColumns()->GetCount() - 1, L"STRING40_4" );

        // Test an index.
        FdoSmPhIndexP index = table->CreateIndex(L"table1_ix1", true );
        index->GetColumns()->Add( FdoSmPhColumnP(table->GetColumns()->GetItem(L"STRING2000")) );
        index->GetColumns()->Add( FdoSmPhColumnP(table->GetColumns()->GetItem(L"STRING700_1")) );

        FdoStringP pkeyClause = table->GetAddPkeySql();

        FdoStringP expectedClause = FdoStringP::Format(
            L"constraint \"pk_table1\" primary key ( \"ID\", \"BLOB_COLUMN\"(%d), \"STRING_COLUMN\", \"SINGLE_COLUMN\", \"DOUBLE_COLUMN\", \"DECIMAL_COLUMN\", \"BOOL_COLUMN\", \"BYTE_COLUMN\", \"INT16_COLUMN\", \"INT32_COLUMN\", \"INT64_COLUMN\", \"DATE_COLUMN\", \"BINARY_COLUMN\", \"VARBINARY_COLUMN\", \"DATE2_COLUMN\", \"TIMESTAMP_COLUMN\" )",
            ( 680 - (charLen * 50))
        );

        // Verify that blob column got truncated to expected amount
        // (1000 - total size of other columns).
        CPPUNIT_ASSERT( pkeyClause ==  expectedClause );

        FdoStringP ukeyClause = table->GetAddUkeysSql();

        // Verify that all unique key columns got truncate to expected amounts.
        if ( charLen == 1 ) 
            CPPUNIT_ASSERT( ukeyClause == L"UNIQUE (\"AUTOINCREMENT_COLUMN\"), UNIQUE (\"STRING100000\"(760), \"STRING100_1\"), UNIQUE (\"STRING2000\"(350), \"STRING700_1\"(350), \"STRING300_1\"), UNIQUE (\"STRING100000\"(266), \"STRING2000\"(266), \"STRING700_1\"(266), \"STRING100_3\", \"STRING100_4\"), UNIQUE (\"STRING2000\"(64), \"STRING300_1\"(64), \"STRING300_2\"(64), \"STRING300_3\"(64), \"STRING300_4\"(64), \"STRING300_5\"(64), \"STRING300_6\"(64), \"STRING300_7\"(64), \"STRING300_8\"(64), \"STRING300_9\"(64), \"STRING300_10\"(64), \"STRING100_1\"(64), \"STRING100_2\"(64), \"STRING100_3\"(64), \"STRING100_4\"(64), \"STRING40_1\"), UNIQUE (\"STRING100_3\", \"STRING100_4\"), UNIQUE (\"STRING40_3\", \"STRING40_4\")" );

        // Table 2 tests a case where 3 constraint columns are 
        // over initial truncation limit, 2 are over the next limit
        // and only 1 over the final limit.
        // Later on we verify truncation by trying some data inserts.
        table = owner->CreateTable( phMgr->GetDcDbObjectName(L"TABLE2" ));
        column = table->CreateColumnChar( L"STRING2000", false, 2000 );
        table->AddPkeyCol( column->GetName() );
        column = table->CreateColumnChar( L"STRING400", false, 400 );
        table->AddPkeyCol( column->GetName() );
        column = table->CreateColumnChar( L"STRING100", false, 100 );
        table->AddPkeyCol( column->GetName() );

        pkeyClause = table->GetAddPkeySql();

        if ( charLen == 1 ) 
            CPPUNIT_ASSERT( pkeyClause == L"constraint \"pk_table2\" primary key ( \"STRING2000\"(500), \"STRING400\"(400), \"STRING100\" )" );

        // Calculating the size of MySql decimal columns is 
        // complicated. Table3 tests creating constraints
        // with decimal columns of various sizes
        table = owner->CreateTable( phMgr->GetDcDbObjectName(L"TABLE3" ));
        FdoSmPhMySqlTableP mysqlTable = table->SmartCast<FdoSmPhMySqlTable>();
        mysqlTable->SetStorageEngine( MySQLOvStorageEngineType_MyISAM );
        
        // Add a couple of large strings to verify that they
        // are truncated to the expected amount. This verifies
        // that the decimal column size calculations are 
        // correct (i.e. enough space was reserved for the 
        // decimal columns).
        column = table->CreateColumnChar( L"STRING1", false, 2000 );
        table->AddPkeyCol( column->GetName() );
        column = table->CreateColumnChar( L"STRING2", false, 2000 );
        table->AddPkeyCol( column->GetName() );
        column = table->CreateColumnDecimal( L"DECIMAL1", true, 65, 0 );
        table->AddPkeyCol( column->GetName() );
        column = table->CreateColumnDecimal( L"DECIMAL2", true, 65, 10 );
        table->AddPkeyCol( column->GetName() );
        column = table->CreateColumnDecimal( L"DECIMAL3", true, 45, 0 );
        table->AddPkeyCol( column->GetName() );
        column = table->CreateColumnDecimal( L"DECIMAL4", true, 44, 0 );
        table->AddPkeyCol( column->GetName() );
        column = table->CreateColumnDecimal( L"DECIMAL5", true, 21, 12 );
        table->AddPkeyCol( column->GetName() );
        column = table->CreateColumnDecimal( L"DECIMAL6", true, 10, 5 );
        table->AddPkeyCol( column->GetName() );
        column = table->CreateColumnDecimal( L"DECIMAL7", true, 5, 0 );
        table->AddPkeyCol( column->GetName() );
        column = table->CreateColumnDecimal( L"DECIMAL8", true, 1, 0 );
        table->AddPkeyCol( column->GetName() );

        pkeyClause = table->GetAddPkeySql();
    
        if ( charLen == 1 ) 
            CPPUNIT_ASSERT( pkeyClause == L"constraint \"pk_table3\" primary key ( \"STRING1\"(440), \"STRING2\"(440), \"DECIMAL1\", \"DECIMAL2\", \"DECIMAL3\", \"DECIMAL4\", \"DECIMAL5\", \"DECIMAL6\", \"DECIMAL7\", \"DECIMAL8\" )" );
        
        database->Commit();

        if ( charLen == 1 ) {
            // Test column truncation by inserting some data values.
            wchar_t prefix500[500];
            wchar_t prefix400[400];
            wchar_t prefix100[100];

            // Fill in every character except the last 
            // non-truncated one for each constraint column.
            for ( ix = 0; ix < 499; ix++ ) 
                prefix500[ix] = 'a';
            for ( ix = 0; ix < 399; ix++ ) 
                prefix400[ix] = 'a';
            for ( ix = 0; ix < 99; ix++ ) 
                prefix100[ix] = 'a';

            prefix500[499] = 0;
            prefix400[399] = 0;
            prefix100[99] = 0;

            GdbiConnection* gdbiConn = phMgr->GetGdbiConnection();

            // Insert first object 
            gdbiConn->ExecuteNonQuery(
                (FdoString*) FdoStringP::Format( 
                    L"insert into table2 ( STRING2000, STRING400, STRING100 ) values ( '%ls', '%ls', '%ls' )",
                    (FdoString*) (FdoStringP(prefix500) + L"a"),
                    (FdoString*) (FdoStringP(prefix400) + L"a"),
                    (FdoString*) (FdoStringP(prefix100) + L"a")
                )
            );

            // Insert three more objects. In each case the truncated
            // key column values are identical exception for the 
            // last character of one of the columns.
            gdbiConn->ExecuteNonQuery(
                (FdoString*) FdoStringP::Format( 
                    L"insert into table2 ( STRING2000, STRING400, STRING100 ) values ( '%ls', '%ls', '%ls' )",
                    (FdoString*) (FdoStringP(prefix500) + L"b"),
                    (FdoString*) (FdoStringP(prefix400) + L"a"),
                    (FdoString*) (FdoStringP(prefix100) + L"a")
                )
            );

            gdbiConn->ExecuteNonQuery(
                (FdoString*) FdoStringP::Format( 
                    L"insert into table2 ( STRING2000, STRING400, STRING100 ) values ( '%ls', '%ls', '%ls' )",
                    (FdoString*) (FdoStringP(prefix500) + L"a"),
                    (FdoString*) (FdoStringP(prefix400) + L"b"),
                    (FdoString*) (FdoStringP(prefix100) + L"a")
                )
            );

            gdbiConn->ExecuteNonQuery(
                (FdoString*) FdoStringP::Format( 
                    L"insert into table2 ( STRING2000, STRING400, STRING100 ) values ( '%ls', '%ls', '%ls' )",
                    (FdoString*) (FdoStringP(prefix500) + L"a"),
                    (FdoString*) (FdoStringP(prefix400) + L"a"),
                    (FdoString*) (FdoStringP(prefix100) + L"b")
                )
            );

            bool duplicate = true;

            try {
                // try inserting a duplicate. The key column values are not duplicate
                // but the truncated values are.
                gdbiConn->ExecuteNonQuery(
                    (FdoString*) FdoStringP::Format( 
                        L"insert into table2 ( STRING2000, STRING400, STRING100 ) values ( '%ls', '%ls', '%ls' )",
                        (FdoString*) (FdoStringP(prefix500) + L"ab"),
                        (FdoString*) (FdoStringP(prefix400) + L"ab"),
                        (FdoString*) (FdoStringP(prefix100) + L"ab")
                    )
                );
            }
            catch ( FdoException* exc ) 
            {
                exc->Release();
                duplicate = false;
            }
            catch ( ... ) 
            {
                duplicate = false;
            }

            CPPUNIT_ASSERT( !duplicate );
        }

        conn->disconnect();

        delete conn;

        if ( charLen != 1 ) 
            CPPUNIT_FAIL ("Test appears to be Ok; However database character set is not Latin1 so truncation validation was skipped");

        printf( "Done\n" );
    }
    catch (FdoException* e ) 
    {
        UnitTestUtil::FailOnException(e);
    }
    catch (CppUnit::Exception exception)
    {
        throw exception;
    }
    catch (...)
    {
        CPPUNIT_FAIL ("unexpected exception encountered");
    }
}
// Tests character set and collation retrieval
void MySqlSchemaMgrTests::testCharacterSets ()
{
    StaticConnection* conn = CreateStaticConnection();
    FdoInt32 idx;

    try
    {
        char prvenv[100];
        FdoStringP providerName = conn->GetServiceName();
        sprintf( prvenv, "provider=%ls", (FdoString*) providerName );
#ifdef _WIN32
        _putenv( prvenv );
#else
        putenv( prvenv );
#endif

        // Sets the other env.
        UnitTestUtil::SetProvider( conn->GetServiceName() ); 

        printf( "\nOpening Connection ...\n" );

        conn->connect();

        FdoSchemaManagerP mgr = conn->CreateSchemaManager();

        FdoSmPhGrdMgrP phMgr = mgr->GetPhysicalSchema()->SmartCast<FdoSmPhGrdMgr>();

        FdoSmPhDatabaseP database = phMgr->GetDatabase();

        for ( idx = 0; idx < 1; idx++ ) {
            FdoSmPhCharacterSetP charSet = database->FindCharacterSet( L"latin1" );
            CPPUNIT_ASSERT( charSet != NULL );
            FdoSmPhMySqlCharacterSetP mySqlCharSet = charSet->SmartCast<FdoSmPhMySqlCharacterSet>();
            CPPUNIT_ASSERT( mySqlCharSet->GetCharLen() == 1 );

            charSet = database->GetCharacterSet( L"utf8" );
            mySqlCharSet = charSet->SmartCast<FdoSmPhMySqlCharacterSet>();
            CPPUNIT_ASSERT( mySqlCharSet->GetCharLen() == 3 );

            FdoSmPhCollationP collation = database->FindCollation( L"latin1_bin" );
            CPPUNIT_ASSERT( collation != NULL );
            charSet = collation->GetCharacterSet();
            CPPUNIT_ASSERT( wcscmp(charSet->GetName(), L"latin1") == 0 );

            collation = database->GetCollation( L"utf8_bin" );
            charSet = collation->GetCharacterSet();
            CPPUNIT_ASSERT( wcscmp(charSet->GetName(), L"utf8") == 0 );
        }


        CPPUNIT_ASSERT( database->FindCharacterSet(L"noexist") == NULL );
        CPPUNIT_ASSERT( database->FindCollation(L"noexist") == NULL );

        bool succeeded = true;
        try {
            FdoSmPhCharacterSetP charSet = database->GetCharacterSet( L"noexist" );
        }
        catch ( FdoException* exc ) 
        {
            exc->Release();
            succeeded = false;
        }
        CPPUNIT_ASSERT( !succeeded );

        succeeded = true;
        try {
            FdoSmPhCollationP collation = database->GetCollation( L"noexist" );
        }
        catch ( FdoException* exc ) 
        {
            exc->Release();
            succeeded = false;
        }
        CPPUNIT_ASSERT( !succeeded );

        conn->disconnect();

        delete conn;

        printf( "Done\n" );
    }
    catch (FdoException* e ) 
    {
        UnitTestUtil::FailOnException(e);
    }
    catch (CppUnit::Exception exception)
    {
        throw exception;
    }
    catch (...)
    {
        CPPUNIT_FAIL ("unexpected exception encountered");
    }
}
FdoIFeatureReader *FdoRdbmsSelectCommand::Execute( bool distinct, FdoInt16 callerId  )
{
    if (!mConnection || !mFdoConnection || mFdoConnection->GetConnectionState() != FdoConnectionState_Open)
        throw FdoCommandException::Create(NlsMsgGet(FDORDBMS_13, "Connection not established"));
    // Flush out any outstanding modifications before selecting; so that the select
    // sees a current picture of the RDBMS.
    mIConnection->Flush();
    int                 qid = -1;
    bool                res = false;
    bool delStatement = true;
    GdbiStatement* statement = NULL;

    const FdoSmLpClassDefinition *classDefinition = mConnection->GetSchemaUtil()->GetClass(this->GetClassNameRef()->GetText());

    bool isFeatureClass = ( classDefinition != NULL &&  classDefinition->GetClassType() == FdoClassType_FeatureClass );
    bool isForUpdate = HasLobProperty( classDefinition );

    bool doNotUseSimpleSelect = mHasObjectProps && (mIdentifiers == NULL || mIdentifiers->GetCount() == 0);

    try
    {
        // for now we support only select with joins
        if (callerId == (FdoInt16)FdoCommandType_Select && !doNotUseSimpleSelect && !mHasObjectProps)
        {
            FdoPtr<FdoRdbmsSqlBuilder> sqlBuilder = mFdoConnection->GetSqlBuilder();
            if (sqlBuilder)
            {
                std::vector<NameOrderingPair> ordering;
                FdoPtr<FdoParameterValueCollection> params = GetParameterValues();
                FdoPtr<FdoJoinCriteriaCollection> jcColl = GetJoinCriteria();
                sqlBuilder->SetParameterValues(params);
                if (mOrderingIdentifiers && mOrderingIdentifiers->GetCount())
                {
                    for (int i=0; i<mOrderingIdentifiers->GetCount(); i++)
                    {
                        FdoPtr<FdoIdentifier> id = mOrderingIdentifiers->GetItem(i);
                        ordering.push_back(NameOrderingPair(id.p, ((int)mOrderingOptions.size() != mOrderingIdentifiers->GetCount()) ? mOrderingOption : mOrderingOptions[id->GetName()])); 
                    }
                }

                FdoString* sqlString = sqlBuilder->ToSelectSqlString(GetClassNameRef(), mAliasName, GetFilterRef(), mIdentifiers, ordering, jcColl);
                if (sqlString != NULL && *sqlString != '\0')
                {
                    statement = mConnection->GetGdbiConnection()->Prepare( sqlString );

                    std::vector< std::pair< FdoLiteralValue*, FdoInt64 > >* paramsUsed = sqlBuilder->GetUsedParameterValues();

                    if (paramsUsed != NULL && paramsUsed->size())
                    {
                        if (mBindParamsHelper == NULL)
                            mBindParamsHelper = new FdoRdbmsPropBindHelper(mConn);

                        mBindParamsHelper->BindParameters(statement, paramsUsed);
                    }

                    GdbiQueryResult *queryRslt = statement->ExecuteQuery();

                    delete statement;

                    if (mBindParamsHelper != NULL)
                        mBindParamsHelper->Clear();

                    // statement will be deleted in the reader.
                    delStatement = false;

                    return FdoRdbmsSimpleFeatureReader::Create(mFdoConnection, queryRslt, isFeatureClass, classDefinition, NULL, mIdentifiers);
                }
            }
        }

        FdoPtr<FdoRdbmsFilterProcessor>flterProcessor = mFdoConnection->GetFilterProcessor();
        FdoPtr<FdoParameterValueCollection> params = GetParameterValues();
        flterProcessor->SetParameterValues(params);

        FdoRdbmsFilterUtilConstrainDef filterConstrain;
        filterConstrain.distinct = distinct;
        filterConstrain.orderingOption = mOrderingOption;
        filterConstrain.selectedProperties = mIdentifiers;
        filterConstrain.groupByProperties = mGroupingCol;
        filterConstrain.orderByProperties = mOrderingIdentifiers;

   		// Verify if this is a special case we can optimize (no filter, no grouping filter,
		// and only aggregate functions Count() and/or SpatialExtents())
        FdoRdbmsFeatureReader *reader = GetOptimizedFeatureReader( classDefinition );
        if ( reader )
            return reader;

        // FDO supports expression functions that may not have native support in the
        // underlying system. If this is the case then the request has to be handled
        // by the Expression Engine.

        bool isValidFilter = true,
             isValidSelectList = true;

        if ( this->GetFilterRef() != NULL )
            isValidFilter = flterProcessor->IsValidExpression( this->GetFilterRef() );
        isValidSelectList = flterProcessor->IsValidExpression( mIdentifiers );

        if ( ( !isValidFilter ) || ( !isValidSelectList ) )
        {
            // Either the selected property list of the the filter is invalid. In any case
            // create a SQL statement that selects all properties for the current class.
            // If the filter is valid it is used to narrow the selected amount of data.

            FdoString *sqlStatement =
                    flterProcessor->FilterToSql( ((isValidFilter)
                                                        ? this->GetFilterRef()
                                                        : NULL),
                                                 this->GetClassNameRef()->GetText() );

            GdbiQueryResult *queryRslt = mConnection->GetGdbiConnection()->ExecuteQuery( sqlStatement );
            FdoPtr<FdoIFeatureReader> featureReader = new FdoRdbmsFeatureReader( mFdoConnection,
                                                                                 queryRslt,
                                                                                 isFeatureClass,
                                                                                 classDefinition,
                                                                                 NULL,
                                                                                 NULL,
                                                                                 0,
                                                                                 NULL );

            // The Expression Engine cannot work with the class definition of type
            // "FdoSmLpClassDefinition". Instead it is necessary to get the corresponding
            // definition of type "FdoClassDefinition". This is done next.

            const FdoSmLpSchema* schema = mConnection->GetSchema( this->GetClassNameRef()->GetText() );
            FdoFeatureSchemasP fdoFeatureSchemas = mFdoConnection->GetSchemaManager()->GetFdoSchemas( schema->GetName() );
            FdoClassesP classCol = (FdoClassCollection *)fdoFeatureSchemas->FindClass( this->GetClassNameRef()->GetText() );
            FdoClassDefinitionP classDef = classCol->GetItem(0);

			// Create the collection of custom functions.
			FdoSmLpSchemasP	schemas = ((FdoSmLpSchema *) schema)->GetSchemas();

			FdoExpressionEngineFunctionCollection *userDefinedFunctions = GetUserDefinedFunctions( schemas->GetSpatialContextMgr()->GetSpatialContexts(), classDef );

			return FdoExpressionEngineUtilFeatureReader::Create(
                                                        classDef,
                                                        featureReader, 
                                                        this->GetFilterRef(),
                                                        mIdentifiers,
                                                        userDefinedFunctions);
		}

        // The selected properties and the filter are both valid. Do the normal processing.

		// Validate the filter
		if ( this->GetFilterRef() != NULL )
		{
			FdoPtr<FdoIFilterCapabilities> filterCaps = mFdoConnection->GetFilterCapabilities();
	
			FdoExpressionEngine::ValidateFilter( NULL, this->GetFilterRef(), NULL, filterCaps);
		}

        // Call FilterToSql just to populate the filter's list of geometric conditions;
        FdoString * sqlString = flterProcessor->FilterToSql( this->GetFilterRef(),
                                                             this->GetClassNameRef()->GetText(),
                                                             SqlCommandType_Select,
                                                             FdoCommandType_Select,
                                                             &filterConstrain,
                                                             isForUpdate,
                                                             callerId );

        FdoPtr<FdoRdbmsFilterProcessor::BoundGeometryCollection> boundGeometries = flterProcessor->GetBoundGeometryValues();
        FdoPtr<FdoRdbmsSecondarySpatialFilterCollection> geometricConditions = flterProcessor->GetGeometricConditions();
   		vector<int> * logicalOps = flterProcessor->GetFilterLogicalOps();

        FdoPtr<FdoIdentifierCollection> idsWithGeoms = FdoIdentifierCollection::Create();

        if (( mIdentifiers && mIdentifiers->GetCount() > 0) )
        {
            // Make sure that the properties are listed for any geometric conditions that require secondary filtering.
            if (geometricConditions != NULL)
            {
                for (FdoInt32 i=0;  i < geometricConditions->GetCount();  i++)
                {
                    FdoPtr<FdoRdbmsSpatialSecondaryFilter> ssf = geometricConditions->GetItem(i);
                    FdoString * ssfPropertyName = ssf->GetPropertyName();
                    FdoPtr<FdoIdentifier> ssfId = mIdentifiers->FindItem(ssfPropertyName);
                    if (ssfId == NULL)
                    {
                        // Property wasn't selected by the caller.
                        ssfId = FdoIdentifier::Create(ssfPropertyName);
                        idsWithGeoms->Add(ssfId);
                    }
                }
                // Convert to SQL again if we added geometric properties.
                if (idsWithGeoms->GetCount() > 0)
                {
                    // Make our own copy of the original list, to avoid a side effect of changing
                    // mIdentifiers, which the user can effectively see.  Perserve original order,
                    // effectively creating a new list that appends the geometry names.
                    for (FdoInt32 i = mIdentifiers->GetCount()-1;  i >= 0;  i--)
                    {
                        FdoPtr<FdoIdentifier> id = mIdentifiers->GetItem(i);
                        idsWithGeoms->Insert(0, id);
                    }
                    filterConstrain.selectedProperties = idsWithGeoms;
                    sqlString = flterProcessor->FilterToSql( this->GetFilterRef(),
                                                             this->GetClassNameRef()->GetText(),
                                                             SqlCommandType_Select,
                                                             FdoCommandType_Select,
                                                             &filterConstrain,
                                                             isForUpdate,
                                                             callerId );
                }
            }
            if (callerId == FdoCommandType_SelectAggregates)
            {
                FdoSchemaManagerP pschemaManager = mConnection->GetSchemaUtil()->GetSchemaManager();
                FdoPtr<FdoFeatureSchemaCollection> schemas = pschemaManager->GetFdoSchemas(L"");
                FdoPtr<FdoCommonExpressionExecutor> expVer = FdoCommonExpressionExecutor::Create(schemas, GetClassNameRef());
                FdoPtr<FdoIExpressionCapabilities> expCap = mFdoConnection->GetExpressionCapabilities();
                expVer->ValidateIdentifiers(mIdentifiers, expCap);
            }
        }

        statement = mConnection->GetGdbiConnection()->Prepare( sqlString );

        std::vector< std::pair< FdoLiteralValue*, FdoInt64 > >* paramsUsed = flterProcessor->GetUsedParameterValues();

        if (paramsUsed != NULL && paramsUsed->size())
        {
            if (mBindParamsHelper == NULL)
                mBindParamsHelper = new FdoRdbmsPropBindHelper(mConn);

            mBindParamsHelper->BindParameters(statement, paramsUsed);
        }

        GdbiQueryResult *queryRslt = statement->ExecuteQuery();

        delete statement;

        if (mBindParamsHelper != NULL)
            mBindParamsHelper->Clear();

        // statement will be deleted in the reader.
        delStatement = false;

        // For now only SQL Spatial Server supports SupportsSimpleReader, later (after we add some unit tests) we can extend it to other providers
        if (!flterProcessor->ContainsCustomObjects() && flterProcessor->SupportsSimpleReader() && geometricConditions == NULL && callerId == (FdoInt16)FdoCommandType_Select && !doNotUseSimpleSelect)
        {
            return FdoRdbmsSimpleFeatureReader::Create(mFdoConnection, queryRslt, isFeatureClass, classDefinition, NULL, mIdentifiers);
        }
        else
        {
            if (( mIdentifiers && mIdentifiers->GetCount() > 0))
                return new FdoRdbmsFeatureSubsetReader (mFdoConnection, queryRslt, isFeatureClass, classDefinition, NULL, mIdentifiers, geometricConditions, logicalOps );
            else
                return new FdoRdbmsFeatureReader (mFdoConnection, queryRslt, isFeatureClass, classDefinition, NULL, NULL, 0, geometricConditions, logicalOps ); // The feature reader should free the queryRslt
        }
    }

    catch (FdoCommandException *ex)
    {
        if (delStatement)
            delete statement;
        ex;
        SELECT_CLEANUP;
        throw;
    }
    catch (FdoException *ex)
    {
        if (delStatement)
            delete statement;
        SELECT_CLEANUP;
        // Wrap in FdoPtr to remove original reference to original exception
        throw FdoCommandException::Create(ex->GetExceptionMessage(), FdoPtr<FdoException>(ex), ex->GetNativeErrorCode());
    }

    catch ( ... )
    {
        if (delStatement)
            delete statement;
        SELECT_CLEANUP;
        throw;
    }
}
void FdoSchemaRollbackTest::DoTest ( bool bPreClear, const wchar_t* schemaName, bool bRollbackOnly, bool bCommit )
{
	FdoPtr<FdoIConnection> connection;
    StaticConnection* staticConn = NULL;
    FdoSchemaManagerP mgr;
    const FdoSmLpSchemaCollection* lp = NULL;

    // All of these tests exercise the Schema Manager but only one test
    // exercises sometime that can currently happen in FDO. Therefore,
    // to save time ...
    // mode. 
    if ( bPreClear || (wcslen(schemaName) > 0) || (!bRollbackOnly) || bCommit ) {
        // these other tests are skipped by default.
        if ( !UnitTestUtil::DoThisTest( "rollback" ) )
            return;
    }

    try {
		// delete, re-create and open the datastore
		printf( "Initializing Connection ... \n" );
		connection = UnitTestUtil::CreateConnection(
			true,
			true,
            DB_NAME_SUFFIX,
            NULL,
            NULL,
            0
		);

        staticConn = UnitTestUtil::NewStaticConnection();
        staticConn->connect();
        staticConn->SetSchema( DB_NAME_SUFFIX );

	    // The following tests must be run in the following order to get the expected results.

	    printf( "Creating Acad Schema ... \n" );
	    CreateAcadSchema( connection );

	    printf( "Creating Electric Schema ... \n" );
	    CreateElectricSchema( connection );

        // At this point all schema elements are in the rollback cache. The following 
        // conditionally does a rollback, which clears the rollback cache. This means
        // that only subsequently modified elements will be later rolled back  when 
        // bRollbackOnly is true.
        if ( bPreClear ) {
            FdoPtr<FdoITransaction> clearTrans = connection->BeginTransaction();
		    clearTrans->Rollback();
		}

        // Grab snapshot of original MetaSchema ( for debugging ).
		printf( "Writing Original Schema ... \n" );
        FdoSchemaManagerP mgr = staticConn->CreateSchemaManager();
        const FdoSmLpSchemaCollection* lp = mgr->RefLogicalPhysicalSchemas();
        lp->XMLSerialize( UnitTestUtil::GetOutputFileName( L"schema_rollback_orig.xml" ) );

		printf( "Deleting some tables ... \n" );
        // Do some deletes outside of the schema manager. These tables will not be re-created
        // bPreClear and bRollbackOnly are true, since these tables won't be in the 
        // rollback cache.
        FdoSmPhMgrP phMgr = mgr->GetPhysicalSchema();
        FdoSmPhDbObjectP pObject = phMgr->FindDbObject( phMgr->GetDcDbObjectName(L"Conductor"), L"", L"", false );
        pObject->SetElementState( FdoSchemaElementState_Deleted );
        pObject->Commit();
        pObject = phMgr->FindDbObject( phMgr->GetDcDbObjectName(L"AcDbEntity_AcXData"), L"", L"", false );
        pObject->SetElementState( FdoSchemaElementState_Deleted );
        pObject->Commit();
    	
		FdoPtr<FdoITransaction> trans1 = connection->BeginTransaction();

		printf( "Modifying Acad Schema ... \n" );
		ModAcadSchema( connection );
		printf( "Modifying Electric Schema ... \n" );
        ModElectricSchema( connection );

		printf( "Performing rollback ... \n" );
 		if ( bCommit ) {
            // Commit before rollback if asked to. In this case, rollback will not 
            // have any physical schema synchronizations to perform.
		    trans1->Commit();
		    trans1 = connection->BeginTransaction();
		}

		if ( (wcslen(schemaName) == 0) && bRollbackOnly ) {
            // Transaction rollback invokes SynchPhysical on all schemas ( synchronize 
            // rollbacks only ) so exercise it in this case.
		    trans1->Rollback();
		}
		else {
            // For other cases, call SynchPhysical directly, after low-level rollback.
		    UnitTestUtil::Sql2Db( L"rollback", connection );
            mgr = staticConn->CreateSchemaManager();
		    mgr->SynchPhysical( schemaName, bRollbackOnly );
		}

        // Write out the resulting metaschema
		printf( "Writing Schema after Rollback ... \n" );
        mgr = staticConn->CreateSchemaManager();
        lp = mgr->RefLogicalPhysicalSchemas();
        lp->XMLSerialize( OutFileName(bPreClear, schemaName, bRollbackOnly, bCommit) );

		try {
		    trans1 = NULL;
		}
		catch ( ... ) {
		}

		printf( "Closing Connection ... \n" );
		UnitTestUtil::CloseConnection( connection, false, DB_NAME_SUFFIX );

		// Compare results with expected results.
        // Todo: check SqlServer and MySql results.

#ifdef RDBI_DEF_ORA
        UnitTestUtil::CheckOutput( 
	        MasterFileName(bPreClear, schemaName, bRollbackOnly, bCommit), 
	        OutFileName(bPreClear, schemaName, bRollbackOnly, bCommit)
        );
#endif
        phMgr = NULL;
        mgr = NULL;

        delete staticConn;
	}
	catch ( FdoException* e ) 
	{
        if ( staticConn ) 
            delete staticConn;
		try {
            if ( connection ) 
    			connection->Close(); 
		}
		catch ( ... ) 
		{
		}
		UnitTestUtil::FailOnException( e );
	}
	catch ( CppUnit::Exception e ) 
	{
        if ( staticConn ) 
            delete staticConn;
		if ( connection )
            connection->Close(); 
		throw;
	}
   	catch (...)
   	{
        if ( staticConn ) 
            delete staticConn;
		if ( connection )
            connection->Close(); 
   		CPPUNIT_FAIL ("caught unexpected exception");
   	}
		
	printf( "Done\n" );

}