//-------------------------------------------------------
void OdbcPersistor::Remove(const Safir::Dob::Typesystem::EntityId& entityId)
{
    m_debug << "Deleting " << entityId <<std::endl;
    int retries = 0;
    bool errorReported = false;
    bool paramSet = false;
    bool done = false;

    while (!done)
    {
        try
        {
            ConnectIfNeeded(m_odbcConnection, m_isOdbcConnected, retries);

            if (!m_deleteIsValid)
            {
                m_helper.AllocStatement(&m_deleteStatement, m_odbcConnection);
                m_helper.Prepare(m_deleteStatement, "DELETE FROM PersistentEntity WHERE typeId=? AND instance=?");
                m_helper.BindParamInt64(m_deleteStatement, 1, &m_type);
                m_helper.BindParamInt64(m_deleteStatement, 2, &m_instance);
                SetStmtTimeout(m_deleteStatement);
                m_deleteIsValid = true;
                paramSet = false;
            }

            if (!paramSet)
            {
                m_type = entityId.GetTypeId();
                m_instance = entityId.GetInstanceId().GetRawValue();
                paramSet = true;
            }

            m_helper.Execute(m_deleteStatement);
            done = true;
            if (errorReported)
            {
                Safir::Logging::SendSystemLog(Safir::Logging::Informational,
                                                L"Successfully connected to the database");
                errorReported = false;
                retries = 0;
            }
        }
        catch(const OdbcException& e)
        {
            const std::wstring err = Safir::Dob::Typesystem::Utilities::ToWstring(e.what());
            m_debug << "Caught a ReconnectException in Delete:\n" << err << std::endl;
            if (retries > REPORT_AFTER_RECONNECTS && !errorReported)
            {
                Safir::Logging::SendSystemLog(Safir::Logging::Error,
                                              L"Remove: Failed to connect to the database, will keep trying. Exception info: " +
                                              err);
                errorReported = true;
            }
            DisconnectOdbcConnection();
            boost::this_thread::sleep_for(RECONNECT_EXCEPTION_DELAY);
        }
    }
}
//-------------------------------------------------------
void
OdbcPersistor::Insert(const Safir::Dob::Typesystem::EntityId& entityId)
{
    int retries = 0;
    bool errorReported = false;
    m_debug << "Inserting " << entityId <<std::endl;
    bool paramSet = false;
    bool done = false;

    while (!done)
    {
        try
        {
            ConnectIfNeeded(m_odbcConnection, m_isOdbcConnected, retries);

            if (!m_insertIsValid)
            {
                m_helper.AllocStatement(&m_rowExistsStatement, m_odbcConnection);

                m_helper.AllocStatement(&m_insertStatement, m_odbcConnection);
                m_insertIsValid = true;

                m_helper.Prepare
                    (m_rowExistsStatement,
                     "SELECT count(*) as antal from PersistentEntity where typeId=? AND instance=? ");

                m_helper.Prepare
                    (m_insertStatement,
                     "INSERT INTO PersistentEntity (typeid, instance, typename) "
                     "values (?, ?, ?); ");

                m_helper.BindParamInt64(m_rowExistsStatement, 1, &m_type);
                m_helper.BindParamInt64(m_rowExistsStatement, 2, &m_instance);

                m_helper.BindColumnInt64(m_rowExistsStatement,1, &m_rowCount);

                m_helper.BindParamInt64(m_insertStatement, 1, &m_type);
                m_helper.BindParamInt64(m_insertStatement, 2, &m_instance);

                if (Safir::Dob::PersistenceParameters::TextColumnsAreUtf8())
                {
                    m_helper.BindParamString(m_insertStatement,
                                             3,
                                             Safir::Dob::PersistenceParameters::TypeNameColumnSize(),
                                             m_typename.get(),
                                             &m_currentTypenameSize);
                }
                else
                {
                    m_helper.BindParamStringW(m_insertStatement,
                                              3,
                                              Safir::Dob::PersistenceParameters::TypeNameColumnSize() / sizeof(wchar_t),
                                              m_typenameW.get(),
                                              &m_currentTypenameSize);
                }

                SetStmtTimeout(m_insertStatement);
                SetStmtTimeout(m_rowExistsStatement);

                paramSet = false;
            }

            if (!paramSet)
            {
                m_type = entityId.GetTypeId();
                m_instance = entityId.GetInstanceId().GetRawValue();

                const std::wstring typeName = Safir::Dob::Typesystem::Operations::GetName(m_type);
                if (Safir::Dob::PersistenceParameters::TextColumnsAreUtf8())
                {
                    const std::string typeNameUtf8 = Safir::Dob::Typesystem::Utilities::ToUtf8(typeName);

                    const size_t size = (typeNameUtf8.size() + 1)* sizeof (char);
                    if (size > static_cast<size_t>(Safir::Dob::PersistenceParameters::TypeNameColumnSize()))
                    {
                        throw Safir::Dob::Typesystem::SoftwareViolationException
                            (L"The size in bytes of '" + typeName +
                             L"' exceeds Safir.Dob.PersistenceParameters.TypeNameColumnSize",
                             __WFILE__, __LINE__);
                    }
                    memcpy(m_typename.get(), typeNameUtf8.c_str(), size);
                }
                else
                {
                    const size_t size = (typeName.size() + 1)* sizeof (wchar_t);
                    if (size > static_cast<size_t>(Safir::Dob::PersistenceParameters::TypeNameColumnSize()))
                    {
                        throw Safir::Dob::Typesystem::SoftwareViolationException
                            (L"The size in bytes of '" + typeName +
                             L"' exceeds Safir.Dob.PersistenceParameters.TypeNameColumnSize",
                             __WFILE__, __LINE__);
                    }
                    memcpy(m_typenameW.get(), typeName.c_str(), size);
                }
                m_currentTypenameSize = SQL_NTS;

                paramSet = true;
            }

            m_helper.Execute(m_rowExistsStatement);

            bool bExecuteInsert = false;
            if (m_helper.Fetch(m_rowExistsStatement))
            {
                // Insert if no row exist
                if (m_rowCount <= 0)
                {
                    bExecuteInsert = true;
                }
            }

            CloseCursor(m_rowExistsStatement);

            if (bExecuteInsert)
            {
                m_helper.Execute(m_insertStatement);
            }

            done = true;
            if (errorReported)
            {
                Safir::Logging::SendSystemLog(Safir::Logging::Informational,
                                                L"Successfully connected to the database");
                errorReported = false;
                retries = 0;
            }

        }
        catch(const OdbcException& e)
        {
            const std::wstring err = Safir::Dob::Typesystem::Utilities::ToWstring(e.what());
            m_debug << "Caught a ReconnectException in Insert:\n" << err << std::endl;
            if (retries > REPORT_AFTER_RECONNECTS && !errorReported)
            {
                Safir::Logging::SendSystemLog(Safir::Logging::Error,
                                              L"Insert: Failed to connect to the database, will keep trying. Exception info: " +
                                              err);
                errorReported = true;
            }

            DisconnectOdbcConnection();

            boost::this_thread::sleep_for(RECONNECT_EXCEPTION_DELAY);
        }

    }
}
//-------------------------------------------------------
void OdbcPersistor::Store(const Safir::Dob::Typesystem::EntityId& entityId,
                          const Safir::Dob::Typesystem::HandlerId& handlerId,
                          Safir::Dob::Typesystem::BinarySerialization& bin,
                          const bool update)
{
    const bool small = static_cast<int>(bin.size()) < Safir::Dob::PersistenceParameters::BinarySmallDataColumnSize();

    int retries = 0;
    bool errorReported = false;

    bool paramSet = false;
    bool done = false;
    while (!done)
    {
        try
        {
            ConnectIfNeeded(m_odbcConnection, m_isOdbcConnected, retries);

            if (!m_storeIsValid)
            {
                m_helper.AllocStatement(&m_storeStatement, m_odbcConnection);
                m_storeIsValid = true;

                m_helper.Prepare(m_storeStatement,
                                 "UPDATE PersistentEntity "
                                 "SET xmlData=NULL, binarySmallData=?, binaryData=?, handlerid=? "
                                 "WHERE typeId=? AND instance=?");

                BindParamBinary(m_storeStatement, 1, Safir::Dob::PersistenceParameters::BinarySmallDataColumnSize(), m_storeBinarySmallData.get(), &m_currentSmallDataSize);
                BindParamBinary(m_storeStatement, 2, Safir::Dob::PersistenceParameters::BinaryDataColumnSize(), m_storeBinaryLargeData.get(), &m_currentLargeDataSize);
                m_helper.BindParamInt64(m_storeStatement, 3, &m_handler);
                m_helper.BindParamInt64(m_storeStatement, 4, &m_type);
                m_helper.BindParamInt64(m_storeStatement, 5, &m_instance);

                SetStmtTimeout(m_storeStatement);

                paramSet = false;
            }

            if (!paramSet)
            {
                if (small)
                {
                    memcpy(m_storeBinarySmallData.get(),&bin[0], bin.size());
                    m_currentSmallDataSize = bin.size();
                    m_currentLargeDataSize = SQL_NULL_DATA;
                }
                else
                {
                    memcpy(m_storeBinaryLargeData.get(),&bin[0], bin.size());
                    m_currentLargeDataSize = bin.size();
                    m_currentSmallDataSize = SQL_NULL_DATA;
                }

                m_type = entityId.GetTypeId();
                m_instance = entityId.GetInstanceId().GetRawValue();
                m_handler = handlerId.GetRawValue();
                paramSet = true;
            }

            if (!update)
            {
                // After global instanceid's we can no longer assume that all instanceid's are written to the
                // the database at startup. So try to insert them here if the entity already exist
                Insert(entityId);
            }

            m_helper.Execute(m_storeStatement);

            m_debug << "Successfully stored binary entity in database. Size = "<<bin.size() << std::endl;
            done = true;

            if (errorReported)
            {
                Safir::Logging::SendSystemLog(Safir::Logging::Informational,
                                                L"Successfully connected to the database");
                errorReported = false;
                retries = 0;
            }
        }
        catch(const OdbcException& e)
        {
            const std::wstring err = Safir::Dob::Typesystem::Utilities::ToWstring(e.what());
            m_debug << "Caught a ReconnectException in Store:\n" << err << std::endl;
            if (retries > REPORT_AFTER_RECONNECTS && !errorReported)
            {
                Safir::Logging::SendSystemLog(Safir::Logging::Error,
                                              L"Store: Failed to connect to the database, will keep trying. Exception info: " +
                                              err);
                errorReported = true;
            }

            DisconnectOdbcConnection();

            boost::this_thread::sleep_for(RECONNECT_EXCEPTION_DELAY);
        }
    }
}
void ConvertDb()
{
    Safir::Databases::Odbc::Connection  readConnection;
    Safir::Databases::Odbc::Connection  updateConnection;
    Safir::Databases::Odbc::Environment environment;
    environment.Alloc();
    readConnection.Alloc(environment);
    updateConnection.Alloc(environment);

    Safir::Databases::Odbc::Statement getAllStatement;
    Safir::Databases::Odbc::Int64Column typeIdColumn;
    Safir::Databases::Odbc::Int64Column instanceColumn;
    Safir::Databases::Odbc::Int64Column handlerColumn;
    Safir::Databases::Odbc::WideStringColumn xmlDataColumn(Safir::Dob::PersistenceParameters::XmlDataColumnSize());
    Safir::Databases::Odbc::BinaryColumn binaryDataColumn(Safir::Dob::PersistenceParameters::BinaryDataColumnSize());
    Safir::Databases::Odbc::BinaryColumn binarySmallDataColumn(Safir::Dob::PersistenceParameters::BinarySmallDataColumnSize());

    Safir::Databases::Odbc::Statement updateStatement;
    Safir::Databases::Odbc::Int64Parameter updateTypeIdParam;
    Safir::Databases::Odbc::Int64Parameter updateInstanceParam;
    Safir::Databases::Odbc::LongWideStringParameter updateXmlDataParam(Safir::Dob::PersistenceParameters::XmlDataColumnSize());

    readConnection.Connect(Safir::Dob::PersistenceParameters::OdbcStorageConnectString());
    updateConnection.Connect(Safir::Dob::PersistenceParameters::OdbcStorageConnectString());

    updateStatement.Alloc(updateConnection);
    updateStatement.Prepare(L"UPDATE PersistentEntity SET xmlData=?, binarySmallData=NULL, binaryData=NULL WHERE typeId=? AND instance=?");
    updateStatement.BindLongParameter( 1, updateXmlDataParam );
    updateStatement.BindParameter( 2, updateTypeIdParam );
    updateStatement.BindParameter( 3, updateInstanceParam );

    getAllStatement.Alloc(readConnection);
    getAllStatement.Prepare( L"SELECT typeId, instance, binarySmallData, binaryData from PersistentEntity where binaryData is not null or binarySmallData is not null");
    getAllStatement.BindColumn( 1, typeIdColumn );
    getAllStatement.BindColumn( 2, instanceColumn);
    getAllStatement.BindColumn( 3, binarySmallDataColumn);
    getAllStatement.Execute();
    for (;;)
    {
        if (!getAllStatement.Fetch())
        {//we've got all rows!
            break;
        }

        Safir::Dob::Typesystem::EntityId entityId
            (typeIdColumn.GetValue(), Safir::Dob::Typesystem::InstanceId(instanceColumn.GetValue()));

        Safir::Dob::Typesystem::ObjectPtr object;

        if (!binarySmallDataColumn.IsNull())
        {
            const char * const data = reinterpret_cast<const char * const>(binarySmallDataColumn.GetValue());
            object = Safir::Dob::Typesystem::ObjectFactory::Instance().CreateObject(data);
        }
        else
        {
            getAllStatement.GetData(4, binaryDataColumn);
            if (!binaryDataColumn.IsNull())
            { //some binarypersistent data set
                const char * const data = reinterpret_cast<const char * const>(binaryDataColumn.GetValue());
                object = Safir::Dob::Typesystem::ObjectFactory::Instance().CreateObject(data);
            }
        }
        if (object != NULL)
        {
            std::wstring xml = Safir::Dob::Typesystem::Serialization::ToXml(object);
            
            updateTypeIdParam.SetValue(entityId.GetTypeId());
            updateInstanceParam.SetValue(entityId.GetInstanceId().GetRawValue());
            updateXmlDataParam.SetValueAtExecution(static_cast<int>(xml.size() * sizeof (wchar_t)));
            
            updateStatement.Execute();
            unsigned short param = 0;
            if (!updateStatement.ParamData(param))
            {
                throw Safir::Dob::Typesystem::SoftwareViolationException(L"There should be one call to ParamData!",__WFILE__,__LINE__);
            }
            updateXmlDataParam.SetValue(&xml);
            updateStatement.PutData(updateXmlDataParam);
            if (updateStatement.ParamData(param))
            {
                throw Safir::Dob::Typesystem::SoftwareViolationException(L"There should only be one call to ParamData!",__WFILE__,__LINE__);
            }
            updateConnection.Commit();
        }
        else
        {
            //                          m_debug << "No data set for " << objectId <<std::endl;
        }
    }
}