Erstellen eines aktualisierbaren Anbieters
Visual C++ unterstützt aktualisierbare Anbieter oder Anbieter, die den Datenspeicher aktualisieren (schreiben in) können. In diesem Thema wird erläutert, wie Aktualisierbare Anbieter mithilfe von OLE DB-Vorlagen erstellt werden.
In diesem Thema wird davon ausgegangen, dass Sie mit einem funktionsfähigen Anbieter beginnen. Es gibt zwei Schritte zum Erstellen eines aktualisierbaren Anbieters. Sie müssen zuerst entscheiden, wie der Anbieter Änderungen am Datenspeicher vornehmen wird; insbesondere, ob Änderungen sofort oder zurückgestellt werden sollen, bis ein Updatebefehl ausgegeben wird. Im Abschnitt "Erstellen von Anbietern aktualisierbar" werden die Änderungen und Einstellungen beschrieben, die Sie im Anbietercode ausführen müssen.
Als Nächstes müssen Sie sicherstellen, dass Ihr Anbieter alle Funktionen enthält, um alles zu unterstützen, was der Verbraucher möglicherweise anfordert. Wenn der Verbraucher den Datenspeicher aktualisieren möchte, muss der Anbieter Code enthalten, der Daten im Datenspeicher speichert. Sie können z. B. die C-Laufzeitbibliothek oder MFC verwenden, um solche Vorgänge für Ihre Datenquelle auszuführen. Im Abschnitt "Schreiben in die Datenquelle" wird beschrieben, wie Sie in die Datenquelle schreiben, mit NULL- und Standardwerten umgehen und Spaltenkennzeichnungen festlegen.
Hinweis
UpdatePV ist ein Beispiel für einen aktualisierbaren Anbieter. UpdatePV ist identisch mit MyProv, aber mit aktualisierbarer Unterstützung.
Anbieter aktualisierbar machen
Der Schlüssel, einen Anbieter aktualisierbar zu machen, besteht darin, zu verstehen, welche Vorgänge Ihr Anbieter für den Datenspeicher ausführen soll und wie der Anbieter diese Vorgänge ausführen soll. Insbesondere besteht das Hauptproblem darin, ob Aktualisierungen des Datenspeichers sofort oder verzögert (batched) ausgeführt werden sollen, bis ein Updatebefehl ausgegeben wird.
Sie müssen zuerst entscheiden, ob sie von IRowsetChangeImpl
oder IRowsetUpdateImpl
in Der Rowset-Klasse erben sollen. Je nachdem, welche dieser Methoden Implementiert werden sollen, sind die Funktionen von drei Methoden betroffen: SetData
, , InsertRows
und DeleteRows
.
Wenn Sie von IRowsetChangeImpl erben, ändert das Aufrufen dieser drei Methoden sofort den Datenspeicher.
Wenn Sie von IRowsetUpdateImpl erben, verzögern die Methoden Änderungen am Datenspeicher, bis Sie aufrufen
Update
,GetOriginalData
oderUndo
. Wenn das Update mehrere Änderungen umfasst, werden sie im Batchmodus ausgeführt (beachten Sie, dass Batchänderungen erheblichen Arbeitsspeicheraufwand verursachen können).
Beachten Sie, dass IRowsetUpdateImpl
von IRowsetChangeImpl
. IRowsetUpdateImpl
So erhalten Sie eine Änderungsfunktion plus Batchfunktion.
So unterstützen Sie die Updatability in Ihrem Anbieter
Erben Sie in Der Rowset-Klasse von
IRowsetChangeImpl
oderIRowsetUpdateImpl
. Diese Klassen stellen geeignete Schnittstellen zum Ändern des Datenspeichers bereit:Hinzufügen von IRowsetChange
Fügen Sie ihre Vererbungskette mithilfe dieses Formulars hinzu
IRowsetChangeImpl
:IRowsetChangeImpl< rowset-name, storage-name >
Fügen Sie außerdem dem
BEGIN_COM_MAP
Abschnitt in der Rowsetklasse hinzuCOM_INTERFACE_ENTRY(IRowsetChange)
.Hinzufügen von IRowsetUpdate
Fügen Sie ihre Vererbungskette mithilfe dieses Formulars hinzu
IRowsetUpdate
:IRowsetUpdateImpl< rowset-name, storage>
Hinweis
Sie sollten die
IRowsetChangeImpl
Linie aus Ihrer Vererbungskette entfernen. Diese ausnahme der zuvor erwähnten Direktive muss den Code enthalten fürIRowsetChangeImpl
.Fügen Sie Folgendes zu Ihrer COM-Karte hinzu (
BEGIN_COM_MAP ... END_COM_MAP
):Wenn Sie implementieren Zu COM-Zuordnung hinzufügen IRowsetChangeImpl
COM_INTERFACE_ENTRY(IRowsetChange)
IRowsetUpdateImpl
COM_INTERFACE_ENTRY(IRowsetUpdate)
Wenn Sie implementieren Zur Eigenschaftensatzzuordnung hinzufügen IRowsetChangeImpl
PROPERTY_INFO_ENTRY_VALUE(IRowsetChange, VARIANT_FALSE)
IRowsetUpdateImpl
PROPERTY_INFO_ENTRY_VALUE(IRowsetUpdate, VARIANT_FALSE)
Fügen Sie in Ihrem Befehl folgendes zu Ihrer Eigenschaftensatzzuordnung hinzu (
BEGIN_PROPSET_MAP ... END_PROPSET_MAP
):Wenn Sie implementieren Zur Eigenschaftensatzzuordnung hinzufügen IRowsetChangeImpl
PROPERTY_INFO_ENTRY_VALUE(IRowsetChange, VARIANT_FALSE)
IRowsetUpdateImpl
PROPERTY_INFO_ENTRY_VALUE(IRowsetChange, VARIANT_FALSE)PROPERTY_INFO_ENTRY_VALUE(IRowsetUpdate, VARIANT_FALSE)
In Der Eigenschaftensatzzuordnung sollten Sie auch alle folgenden Einstellungen enthalten, wie sie unten angezeigt werden:
PROPERTY_INFO_ENTRY_VALUE(UPDATABILITY, DBPROPVAL_UP_CHANGE | DBPROPVAL_UP_INSERT | DBPROPVAL_UP_DELETE) PROPERTY_INFO_ENTRY_VALUE(CHANGEINSERTEDROWS, VARIANT_TRUE) PROPERTY_INFO_ENTRY_VALUE(IMMOBILEROWS, VARIANT_TRUE) PROPERTY_INFO_ENTRY_EX(OWNINSERT, VT_BOOL, DBPROPFLAGS_ROWSET | DBPROPFLAGS_READ, VARIANT_TRUE, 0) PROPERTY_INFO_ENTRY_EX(OWNUPDATEDELETE, VT_BOOL, DBPROPFLAGS_ROWSET | DBPROPFLAGS_READ, VARIANT_TRUE, 0) PROPERTY_INFO_ENTRY_EX(OTHERINSERT, VT_BOOL, DBPROPFLAGS_ROWSET | DBPROPFLAGS_READ, VARIANT_TRUE, 0) PROPERTY_INFO_ENTRY_EX(OTHERUPDATEDELETE, VT_BOOL, DBPROPFLAGS_ROWSET | DBPROPFLAGS_READ, VARIANT_TRUE, 0) PROPERTY_INFO_ENTRY_EX(REMOVEDELETED, VT_BOOL, DBPROPFLAGS_ROWSET | DBPROPFLAGS_READ, VARIANT_FALSE, 0)
Sie finden die in diesen Makroaufrufen verwendeten Werte, indem Sie in Atldb.h nach den Eigenschaften-IDs und -Werten suchen (wenn Atldb.h sich von der Onlinedokumentation unterscheidet, ersetzt Atldb.h die Dokumentation).
Hinweis
Viele der
VARIANT_FALSE
Einstellungen sindVARIANT_TRUE
von den OLE DB-Vorlagen erforderlich. Die OLE DB-Spezifikation gibt an, dass sie lese-/schreibgeschützt sein können, aber die OLE DB-Vorlagen können nur einen Wert unterstützen.Wenn Sie IRowsetChangeImpl implementieren
Wenn Sie implementieren
IRowsetChangeImpl
, müssen Sie die folgenden Eigenschaften für Ihren Anbieter festlegen. Diese Eigenschaften werden in erster Linie verwendet, um Schnittstellen überICommandProperties::SetProperties
.DBPROP_IRowsetChange
: Legt diese Einstellung automatisch festDBPROP_IRowsetChange
.DBPROP_UPDATABILITY
: Eine Bitmaske, die die unterstützten Methoden fürIRowsetChange
:SetData
, ,DeleteRows
oderInsertRow
.DBPROP_CHANGEINSERTEDROWS
: Verbraucher können zeilenweise anrufenIRowsetChange::DeleteRows
oderSetData
neu eingefügte Zeilen verwenden.DBPROP_IMMOBILEROWS
: Das Rowset ordnet eingefügte oder aktualisierte Zeilen nicht neu an.
Wenn Sie IRowsetUpdateImpl implementieren
Wenn Sie implementieren
IRowsetUpdateImpl
, müssen Sie die folgenden Eigenschaften für Ihren Anbieter festlegen, zusätzlich zum Festlegen aller Eigenschaften fürIRowsetChangeImpl
zuvor aufgeführte:DBPROP_IRowsetUpdate
.DBPROP_OWNINSERT
: Muss READ_ONLY UND VARIANT_TRUE sein.DBPROP_OWNUPDATEDELETE
: Muss READ_ONLY UND VARIANT_TRUE sein.DBPROP_OTHERINSERT
: Muss READ_ONLY UND VARIANT_TRUE sein.DBPROP_OTHERUPDATEDELETE
: Muss READ_ONLY UND VARIANT_TRUE sein.DBPROP_REMOVEDELETED
: Muss READ_ONLY UND VARIANT_TRUE sein.DBPROP_MAXPENDINGROWS
.
Hinweis
Wenn Sie Benachrichtigungen unterstützen, verfügen Sie möglicherweise auch über einige andere Eigenschaften. weitere Informationen finden Sie im Abschnitt
IRowsetNotifyCP
zu dieser Liste.
Schreiben in die Datenquelle
Rufen Sie die Execute
Funktion auf, um aus der Datenquelle zu lesen. Rufen Sie die FlushData
Funktion auf, um in die Datenquelle zu schreiben. (Im Allgemeinen bedeutet "Leeren" das Speichern von Änderungen, die Sie an einer Tabelle oder einem Index auf einem Datenträger vornehmen.)
FlushData(HROW, HACCESSOR);
Mit den Argumenten HROW (Row Handle) und Accessor handle (HACCESSOR) können Sie den zu schreibenden Bereich angeben. In der Regel schreiben Sie jeweils ein einzelnes Datenfeld.
Die FlushData
Methode schreibt Daten in das Format, in dem sie ursprünglich gespeichert wurde. Wenn Sie diese Funktion nicht außer Kraft setzen, funktioniert ihr Anbieter ordnungsgemäß, änderungen werden jedoch nicht in den Datenspeicher geleert.
Wann soll geleert werden?
Die Anbietervorlagen rufen FlushData auf, wenn Daten in den Datenspeicher geschrieben werden müssen; dies tritt in der Regel (aber nicht immer) als Ergebnis von Aufrufen der folgenden Funktionen auf:
IRowsetChange::DeleteRows
IRowsetChange::SetData
IRowsetChange::InsertRows
(wenn neue Daten in die Zeile eingefügt werden sollen)IRowsetUpdate::Update
Ein Blick hinter die Kulissen
Der Verbraucher führt einen Anruf aus, der eine Spülung erfordert (z. B. Update), und dieser Aufruf wird an den Anbieter übergeben, der immer Folgendes ausführt:
Aufrufe
SetDBStatus
, wenn ein Statuswert gebunden ist.Überprüft Spaltenkennzeichnungen.
Ruft
IsUpdateAllowed
.
Diese drei Schritte tragen zur Sicherheit bei. Dann ruft der Anbieter auf FlushData
.
Implementieren von FlushData
Zur Implementierung FlushData
müssen Sie mehrere Probleme berücksichtigen:
Stellen Sie sicher, dass der Datenspeicher Änderungen verarbeiten kann.
Behandeln von NULL-Werten.
Behandeln von Standardwerten.
Um Ihre eigene FlushData
Methode zu implementieren, müssen Sie:
Wechseln Sie zu Ihrer Rowset-Klasse.
Fügen Sie in der Rowset-Klasse die Deklaration von:
HRESULT FlushData(HROW, HACCESSOR) { // Insert your implementation here and return an HRESULT. }
Bereitstellen einer Implementierung von
FlushData
.
Eine gute Implementierung von FlushData
Speichern nur der Zeilen und Spalten, die tatsächlich aktualisiert werden. Sie können die Parameter HROW und HACCESSOR verwenden, um die aktuelle Zeile und Spalte zu bestimmen, die zur Optimierung gespeichert wird.
In der Regel besteht die größte Herausforderung darin, mit Ihrem eigenen nativen Datenspeicher zu arbeiten. Versuchen Sie nach Möglichkeit folgendes:
Behalten Sie die Methode zum Schreiben in Ihren Datenspeicher so einfach wie möglich bei.
Behandeln Sie NULL-Werte (optional, aber empfohlen).
Behandeln von Standardwerten (optional, aber empfohlen).
Es empfiehlt sich, die tatsächlich angegebenen Werte im Datenspeicher für NULL und Standardwerte zu haben. Es ist am besten, wenn Sie diese Daten extrapolieren können. Andernfalls wird empfohlen, NULL- und Standardwerte nicht zuzulassen.
Das folgende Beispiel zeigt, wie FlushData
in der RUpdateRowset
Klasse im UpdatePV
Beispiel implementiert wird (siehe Rowset.h im Beispielcode):
///////////////////////////////////////////////////////////////////////////
// class RUpdateRowset (in rowset.h)
...
HRESULT FlushData(HROW, HACCESSOR)
{
ATLTRACE2(atlTraceDBProvider, 0, "RUpdateRowset::FlushData\n");
USES_CONVERSION;
enum {
sizeOfString = 256,
sizeOfFileName = MAX_PATH
};
FILE* pFile = NULL;
TCHAR szString[sizeOfString];
TCHAR szFile[sizeOfFileName];
errcode err = 0;
ObjectLock lock(this);
// From a filename, passed in as a command text,
// scan the file placing data in the data array.
if (m_strCommandText == (BSTR)NULL)
{
ATLTRACE( "RRowsetUpdate::FlushData -- "
"No filename specified\n");
return E_FAIL;
}
// Open the file
_tcscpy_s(szFile, sizeOfFileName, OLE2T(m_strCommandText));
if ((szFile[0] == _T('\0')) ||
((err = _tfopen_s(&pFile, &szFile[0], _T("w"))) != 0))
{
ATLTRACE("RUpdateRowset::FlushData -- Could not open file\n");
return DB_E_NOTABLE;
}
// Iterate through the row data and store it.
for (long l=0; l<m_rgRowData.GetSize(); l++)
{
CAgentMan am = m_rgRowData[l];
_putw((int)am.dwFixed, pFile);
if (_tcscmp(&am.szCommand[0], _T("")) != 0)
_stprintf_s(&szString[0], _T("%s\n"), am.szCommand);
else
_stprintf_s(&szString[0], _T("%s\n"), _T("NULL"));
_fputts(szString, pFile);
if (_tcscmp(&am.szText[0], _T("")) != 0)
_stprintf_s(&szString[0], _T("%s\n"), am.szText);
else
_stprintf_s(&szString[0], _T("%s\n"), _T("NULL"));
_fputts(szString, pFile);
if (_tcscmp(&am.szCommand2[0], _T("")) != 0)
_stprintf_s(&szString[0], _T("%s\n"), am.szCommand2);
else
_stprintf_s(&szString[0], _T("%s\n"), _T("NULL"));
_fputts(szString, pFile);
if (_tcscmp(&am.szText2[0], _T("")) != 0)
_stprintf_s(&szString[0], _T("%s\n"), am.szText2);
else
_stprintf_s(&szString[0], _T("%s\n"), _T("NULL"));
_fputts(szString, pFile);
}
if (fflush(pFile) == EOF || fclose(pFile) == EOF)
{
ATLTRACE("RRowsetUpdate::FlushData -- "
"Couldn't flush or close file\n");
}
return S_OK;
}
Behandeln von Änderungen
Damit Ihr Anbieter Änderungen verarbeiten kann, müssen Sie zuerst sicherstellen, dass Ihr Datenspeicher (z. B. eine Textdatei oder Videodatei) über Möglichkeiten verfügt, mit denen Sie Änderungen daran vornehmen können. Wenn dies nicht der Fall ist, sollten Sie diesen Code separat vom Anbieterprojekt erstellen.
Behandeln von NULL-Daten
Es ist möglich, dass ein Endbenutzer NULL-Daten sendet. Wenn Sie NULL-Werte in Felder in der Datenquelle schreiben, können potenzielle Probleme auftreten. Stellen Sie sich eine auftragsbezogene Anwendung vor, die Werte für Ort und Postleitzahl akzeptiert; sie kann entweder oder beide Werte akzeptieren, aber keines, weil in diesem Fall die Übermittlung unmöglich wäre. Sie müssen daher bestimmte Kombinationen von NULL-Werten in Feldern einschränken, die für Ihre Anwendung sinnvoll sind.
Als Anbieterentwickler müssen Sie überlegen, wie Sie diese Daten speichern, wie Sie diese Daten aus dem Datenspeicher lesen und wie Sie dies für den Benutzer angeben. Insbesondere müssen Sie überlegen, wie Sie den Datenstatus von Rowsetdaten in der Datenquelle ändern (z. B. DataStatus = NULL). Sie entscheiden, welcher Wert zurückgegeben werden soll, wenn ein Verbraucher auf ein Feld zugreift, das einen NULL-Wert enthält.
Sehen Sie sich den Code im UpdatePV-Beispiel an; es veranschaulicht, wie ein Anbieter NULL-Daten verarbeiten kann. In UpdatePV speichert der Anbieter NULL-Daten, indem die Zeichenfolge "NULL" im Datenspeicher geschrieben wird. Wenn sie NULL-Daten aus dem Datenspeicher liest, wird diese Zeichenfolge angezeigt und dann der Puffer geleert, wodurch eine NULL-Zeichenfolge erstellt wird. Außerdem enthält sie eine Außerkraftsetzung, in der IRowsetImpl::GetDBStatus
sie DBSTATUS_S_ISNULL zurückgibt, wenn dieser Datenwert leer ist.
Markieren nullabler Spalten
Wenn Sie auch Schema-Rowsets implementieren (siehe IDBSchemaRowsetImpl
), sollte Ihre Implementierung im DBSCHEMA_COLUMNS-Rowset (in der Regel in Ihrem Anbieter durch CxxxSchemaColSchemaRowset gekennzeichnet) angeben, dass die Spalte nullfähig ist.
Sie müssen auch angeben, dass alle nullfähigen Spalten den DBCOLUMNFLAGS_ISNULLABLE Wert in Ihrer Version der GetColumnInfo
.
Wenn Sie in der OLE DB-Vorlagenimplementierung Spalten nicht als Nullwerte markieren, geht der Anbieter davon aus, dass er einen Wert enthalten muss und dem Consumer nicht das Senden von NULL-Werten gestattet.
Das folgende Beispiel zeigt, wie die CommonGetColInfo
Funktion in CUpdateCommand implementiert wird (siehe UpProvRS.cpp) in UpdatePV. Beachten Sie, wie die Spalten diese DBCOLUMNFLAGS_ISNULLABLE für nullwerte Spalten aufweisen.
/////////////////////////////////////////////////////////////////////////////
// CUpdateCommand (in UpProvRS.cpp)
ATLCOLUMNINFO* CommonGetColInfo(IUnknown* pPropsUnk, ULONG* pcCols, bool bBookmark)
{
static ATLCOLUMNINFO _rgColumns[6];
ULONG ulCols = 0;
if (bBookmark)
{
ADD_COLUMN_ENTRY_EX(ulCols, OLESTR("Bookmark"), 0,
sizeof(DWORD), DBTYPE_BYTES,
0, 0, GUID_NULL, CAgentMan, dwBookmark,
DBCOLUMNFLAGS_ISBOOKMARK)
ulCols++;
}
// Next set the other columns up.
// Add a fixed length entry for OLE DB conformance testing purposes
ADD_COLUMN_ENTRY_EX(ulCols, OLESTR("Fixed"), 1, 4, DBTYPE_UI4,
10, 255, GUID_NULL, CAgentMan, dwFixed,
DBCOLUMNFLAGS_WRITE |
DBCOLUMNFLAGS_ISFIXEDLENGTH)
ulCols++;
ADD_COLUMN_ENTRY_EX(ulCols, OLESTR("Command"), 2, 16, DBTYPE_STR,
255, 255, GUID_NULL, CAgentMan, szCommand,
DBCOLUMNFLAGS_WRITE | DBCOLUMNFLAGS_ISNULLABLE)
ulCols++;
ADD_COLUMN_ENTRY_EX(ulCols, OLESTR("Text"), 3, 16, DBTYPE_STR,
255, 255, GUID_NULL, CAgentMan, szText,
DBCOLUMNFLAGS_WRITE | DBCOLUMNFLAGS_ISNULLABLE)
ulCols++;
ADD_COLUMN_ENTRY_EX(ulCols, OLESTR("Command2"), 4, 16, DBTYPE_STR,
255, 255, GUID_NULL, CAgentMan, szCommand2,
DBCOLUMNFLAGS_WRITE | DBCOLUMNFLAGS_ISNULLABLE)
ulCols++;
ADD_COLUMN_ENTRY_EX(ulCols, OLESTR("Text2"), 5, 16, DBTYPE_STR,
255, 255, GUID_NULL, CAgentMan, szText2,
DBCOLUMNFLAGS_WRITE | DBCOLUMNFLAGS_ISNULLABLE)
ulCols++;
if (pcCols != NULL)
{
*pcCols = ulCols;
}
return _rgColumns;
}
Standardwerte
Wie bei NULL-Daten haben Sie die Verantwortung, sich mit dem Ändern von Standardwerten zu befassen.
Der Standardwert von FlushData
und Execute
ist, S_OK zurückzugeben. Wenn Sie diese Funktion daher nicht überschreiben, werden die Änderungen erfolgreich angezeigt (S_OK werden zurückgegeben), aber sie werden nicht an den Datenspeicher übertragen.
UpdatePV
Im Beispiel (in Rowset.h) behandelt die SetDBStatus
Methode Standardwerte wie folgt:
virtual HRESULT SetDBStatus(DBSTATUS* pdbStatus, CSimpleRow* pRow,
ATLCOLUMNINFO* pColInfo)
{
ATLASSERT(pRow != NULL && pColInfo != NULL && pdbStatus != NULL);
void* pData = NULL;
char* pDefaultData = NULL;
DWORD* pFixedData = NULL;
switch (*pdbStatus)
{
case DBSTATUS_S_DEFAULT:
pData = (void*)&m_rgRowData[pRow->m_iRowset];
if (pColInfo->wType == DBTYPE_STR)
{
pDefaultData = (char*)pData + pColInfo->cbOffset;
strcpy_s(pDefaultData, "Default");
}
else
{
pFixedData = (DWORD*)((BYTE*)pData +
pColInfo->cbOffset);
*pFixedData = 0;
return S_OK;
}
break;
case DBSTATUS_S_ISNULL:
default:
break;
}
return S_OK;
}
Spaltenkennzeichnungen
Wenn Sie Standardwerte für Ihre Spalten unterstützen, müssen Sie sie mithilfe von Metadaten in der SchemaRowset-Klasse>des <Anbieters festlegen. Legen Sie m_bColumnHasDefault = VARIANT_TRUE
fest.
Sie haben auch die Verantwortung, die Spaltenkennzeichnungen festzulegen, die mit dem aufgezählten TYP DBCOLUMNFLAGS angegeben werden. Die Spaltenkennzeichen beschreiben Die Spaltenmerkmale.
In der CUpdateSessionColSchemaRowset
Klasse in UpdatePV
(in Session.h) wird beispielsweise die erste Spalte auf diese Weise eingerichtet:
// Set up column 1
trData[0].m_ulOrdinalPosition = 1;
trData[0].m_bIsNullable = VARIANT_FALSE;
trData[0].m_bColumnHasDefault = VARIANT_TRUE;
trData[0].m_nDataType = DBTYPE_UI4;
trData[0].m_nNumericPrecision = 10;
trData[0].m_ulColumnFlags = DBCOLUMNFLAGS_WRITE |
DBCOLUMNFLAGS_ISFIXEDLENGTH;
lstrcpyW(trData[0].m_szColumnDefault, OLESTR("0"));
m_rgRowData.Add(trData[0]);
Dieser Code gibt unter anderem an, dass die Spalte einen Standardwert von 0 unterstützt, schreibbar ist und dass alle Daten in der Spalte dieselbe Länge aufweisen. Wenn die Daten in einer Spalte variable Länge aufweisen sollen, würden Sie dieses Flag nicht festlegen.