RFC 53:OGR not null约束和默认值

作者:连鲁奥

联系人:spatialys.com上的even dot rouault

状态:采用,在GDAL 2.0中实现

总结

此RFC处理非空约束和OGR字段的默认值。非空约束对于维护基本数据完整性很有用,并且由大多数(所有?)具有SQL功能的驱动程序。默认字段值可以是互补的,也可以独立于非空约束使用,以指定在将要素插入图层时,如果未提供字段值,则必须为其指定值。

非空约束

到目前为止,OGR字段没有空约束,即在创建层/表中的字段时,可能会取消设置某个特征/记录的字段(即具有空值)。这仍然是默认值,即假定字段为空。OGRFieldDefn类扩展了一个布尔属性bNullable,该属性默认为TRUE,可以设置为FALSE来表示NOT NULL约束(bNullable优先于bNotNullable,以避免与双反混淆)。可以在其存储中转换非空约束的驱动程序将使用该属性来确定字段定义是否必须包含非空约束。打开数据源时,将检查它们的元数据以正确设置可为空的属性,以便往返工作。

以下方法被添加到OGRFieldDefn类中

    int                 IsNullable() const { return bNullable; }

/**
 * \brief Return whether this field can receive null values.
 *
 * By default, fields are nullable.
 *
 * Even if this method returns FALSE (i.e not-nullable field), it doesn't mean
 * that OGRFeature::IsFieldSet() will necessary return TRUE, as fields can be
 * temporary unset and null/not-null validation is usually done when
 * OGRLayer::CreateFeature()/SetFeature() is called.
 *
 * This method is the same as the C function OGR_Fld_IsNullable().
 *
 * @return TRUE if the field is authorized to be null.
 * @since GDAL 2.0
 */

    void                SetNullable( int bNullableIn ) { bNullable = bNullableIn; }

/**
 * \brief Set whether this field can receive null values.
 *
 * By default, fields are nullable, so this method is generally called with FALSE
 * to set a not-null constraint.
 *
 * Drivers that support writing not-null constraint will advertise the
 * GDAL_DCAP_NOTNULL_FIELDS driver metadata item.
 *
 * This method is the same as the C function OGR_Fld_SetNullable().
 *
 * @param bNullableIn FALSE if the field must have a not-null constraint.
 * @since GDAL 2.0
 */

由于这对几何字段是正确的,这两个方法也被添加到ogrgometryFieldDefn类中。

请注意,在非空层上添加具有NOTNULL约束的字段通常是不可能的,除非与默认值关联。

将以下方法添加到OGRFeature类:

    int                 Validate( int nValidateFlags, int bEmitError );

/**
 * \brief Validate that a feature meets constraints of its schema.
 *
 * The scope of test is specified with the nValidateFlags parameter.
 *
 * Regarding OGR_F_VAL_WIDTH, the test is done assuming the string width must
 * be interpreted as the number of UTF-8 characters. Some drivers might interpret
 * the width as the number of bytes instead. So this test is rather conservative
 * (if it fails, then it will fail for all interpretations).
 *
 * This method is the same as the C function OGR_F_Validate().
 *
 * @param nValidateFlags OGR_F_VAL_ALL or combination of OGR_F_VAL_NULL,
 *                       OGR_F_VAL_GEOM_TYPE, OGR_F_VAL_WIDTH and OGR_F_VAL_ALLOW_NULL_WHEN_DEFAULT
 *                       with '|' operator
 * @param bEmitError TRUE if a CPLError() must be emitted when a check fails
 * @return TRUE if all enabled validation tests pass.
 * @since GDAL 2.0
 */

其中nValidateFlags是以下各项的组合:

/** Validate that fields respect not-null constraints.
 * Used by OGR_F_Validate().
 * @since GDAL 2.0
 */
#define OGR_F_VAL_NULL           0x00000001

/** Validate that geometries respect geometry column type.
 * Used by OGR_F_Validate().
 * @since GDAL 2.0
 */
#define OGR_F_VAL_GEOM_TYPE      0x00000002

/** Validate that (string) fields respect field width.
 * Used by OGR_F_Validate().
 * @since GDAL 2.0
 */
#define OGR_F_VAL_WIDTH          0x00000004

/** Allow fields that are null when there's an associated default value.
 * This can be used for drivers where the low-level layers will automatically set the
 * field value to the associated default value.
 * This flag only makes sense if OGR_F_VAL_NULL is set too.
 * Used by OGR_F_Validate().
 * @since GDAL 2.0
 */
#define OGR_F_VAL_ALLOW_NULL_WHEN_DEFAULT       0x00000008

/** Enable all validation tests.
 * Used by OGR_F_Validate().
 * @since GDAL 2.0
 */
#define OGR_F_VAL_ALL            0xFFFFFFFF

非空约束的验证通常允许到驱动程序的低级层,因此OGRFeature::Validate()只在少数情况下有用(其中一种情况是GML驱动程序)

添加一个新的标记ALTER_NULLABLE_flag=0x8以传递给OGRLayer::AlterFieldDefn(),以便设置或删除空/非空约束(对于实现它的驱动程序)。

为常规属性字段处理NOT NULL约束的驱动程序应该公布新的GDALu DCAPu NOTNULLu字段和/或GDALu DCAPu NOTNULLu GEOMFIELDS驱动程序元数据项。

不实现OGRLayer::CreateGeomField()接口的驱动程序(即支持单个几何体字段的驱动程序),但可以创建对几何体字段具有非空约束的层,则可以公开几何体可为空的=是/否层创建选项。

注意:由于通常的编写方式,不支持not NULL约束的驱动程序的CreateField()实现通常会复制可空标志的值,如果在添加字段定义之后查询该定义,则可能会有点误导(宽度/精度也是如此)。

所有上述方法都映射到C API中:

  int    CPL_DLL OGR_Fld_IsNullable( OGRFieldDefnH hDefn );
  void   CPL_DLL OGR_Fld_SetNullable( OGRFieldDefnH hDefn, int );

int                  CPL_DLL OGR_GFld_IsNullable( OGRGeomFieldDefnH hDefn );
void                 CPL_DLL OGR_GFld_SetNullable( OGRGeomFieldDefnH hDefn, int );

int    CPL_DLL OGR_F_Validate( OGRFeatureH, int nValidateFlags, int bEmitError );

默认字段值

具有非空约束的字段有时会附带一个默认子句,以便在不填充所有字段的情况下创建新功能,同时保持完整性。也可以在可为空的字段上设置默认值,但出于以后公开的原因,建议避免这样做。

可以转换存储中默认值的驱动程序将使用该属性来确定字段定义是否必须包含默认值。打开数据源时,将检查它们的元数据以正确设置默认值属性,以便往返工作。

在GDAL 1.X中有一个对默认值的初步支持,但是除了OGRFieldDefn上的getter/setter方法之外,它从未实现过。它依赖于一个“OGRField uDefault”成员。OGRField的选择限制了用字段类型表示的默认值,但在某些情况下,我们希望能够为非字符串字段分配表达式或特殊关键字。例如,SQL标准为DateTime字段定义了当前的时间戳。所以一般来说,我们已经删除这个uDefault成员,并用一个“char”替换它 * pszDefault“字符串。

可以设置为默认值的值有:

  • 包含在单引号字符中并正确转义的文本字符串值,如: 'Nice weather. Isn''t it ?'

  • 数值(无引号)

  • 保留关键字(不带引号):当前时间戳、当前日期、当前时间、空

  • 日期时间文本值包含在单引号字符中,定义格式如下:“YYYY/MM/DD HH:MM:SS [.sss] ’

  • 任何其他特定于驱动程序的表达式。e、 g.对于SQLite:(strftime('%Y-%m-%dT%H:%m:%fZ','now'))

以下方法被添加/修改到OGRFieldDefn类

    void                SetDefault( const char* );

/**
 * \brief Set default field value.
 *
 * The default field value is taken into account by drivers (generally those with
 * a SQL interface) that support it at field creation time. OGR will generally not
 * automatically set the default field value to null fields by itself when calling
 * OGRFeature::CreateFeature() / OGRFeature::SetFeature(), but will let the
 * low-level layers to do the job. So retrieving the feature from the layer is
 * recommended.
 *
 * The accepted values are NULL, a numeric value, a literal value enclosed
 * between single quote characters (and inner single quote characters escaped by
 * repetition of the single quote character),
 * CURRENT_TIMESTAMP, CURRENT_TIME, CURRENT_DATE or
 * a driver specific expression (that might be ignored by other drivers).
 * For a datetime literal value, format should be 'YYYY/MM/DD HH:MM:SS[.sss]'
 * (considered as UTC time).
 *
 * Drivers that support writing DEFAULT clauses will advertise the
 * GDAL_DCAP_DEFAULT_FIELDS driver metadata item.
 *
 * This function is the same as the C function OGR_Fld_SetDefault().
 *
 * @param pszDefault new default field value or NULL pointer.
 *
 * @since GDAL 2.0
 */


    const char         *GetDefault() const;

/**
 * \brief Get default field value.
 *
 * This function is the same as the C function OGR_Fld_GetDefault().
 *
 * @return default field value or NULL.
 * @since GDAL 2.0
 */


    int                 IsDefaultDriverSpecific() const;

/**
 * \brief Returns whether the default value is driver specific.
 *
 * Driver specific default values are those that are *not* NULL, a numeric value,
 * a literal value enclosed between single quote characters, CURRENT_TIMESTAMP,
 * CURRENT_TIME, CURRENT_DATE or datetime literal value.
 *
 * This method is the same as the C function OGR_Fld_IsDefaultDriverSpecific().
 *
 * @return TRUE if the default value is driver specific.
 * @since GDAL 2.0
 */

SetDefault() validates that a string literal beginning with ' is properly escaped.

如果值集不属于上述枚举中的四个项目符号之一,IsDefaultDriverSpecific()将返回TRUE。驱动程序使用此选项来确定是否可以处理默认值。

驱动程序应该努力解释并重新格式化上述4种标准格式的默认值,以便能够将默认值从一个驱动程序传播到另一个驱动程序。

将以下方法添加到OGRFeature类:

    void                FillUnsetWithDefault(int bNotNullableOnly,
                                             char** papszOptions );
/**
 * \brief Fill unset fields with default values that might be defined.
 *
 * This method is the same as the C function OGR_F_FillUnsetWithDefault().
 *
 * @param bNotNullableOnly if we should fill only unset fields with a not-null
 *                     constraint.
 * @param papszOptions unused currently. Must be set to NULL.
 * @since GDAL 2.0
 */

它将用一个特性的默认值替换未设置的字段,但是应该很少使用,因为大多数驱动程序将在其低级层中自动进行替换。无法信任CreateFeature()自动修改传递的OGRFeature对象,以将未设置字段设置为其默认值。为此,应该发出显式的GetFeature()调用来检索存储在数据库中的记录。

添加一个新的标志ALTER_DEFAULT_flag=0x8以传递给OGRLayer::AlterFieldDefn(),以便设置、删除或修改默认值(对于实现它的驱动程序)

处理默认值的驱动程序应该公布新的GDALu DCAPu defaultu FIELDS驱动程序元数据项。

注意:由于通常的编写方式,不支持默认值的驱动程序的CreateField()实现通常会复制默认值字符串的值,如果在添加字段定义之后查询字段定义,这可能有点误导。

所有上述方法都映射到C API中:

const char CPL_DLL *OGR_Fld_GetDefault( OGRFieldDefnH hDefn );
void   CPL_DLL OGR_Fld_SetDefault( OGRFieldDefnH hDefn, const char* );
int    CPL_DLL OGR_Fld_IsDefaultDriverSpecific( OGRFieldDefnH hDefn );

void   CPL_DLL OGR_F_FillUnsetWithDefault( OGRFeatureH hFeat,
                                           int bNotNullableOnly,
                                           char** papszOptions );

SWIG绑定(Python/Java/C#/Perl)更改

已完成以下添加:

  • 在FieldDefn类中添加了SetNullable(),IsNullable()

  • 在GeomFieldDefn类中添加了SetNullable(),IsNullable()

  • 在要素类上添加Validate()

  • 在FieldDefn类上提供SetDefault()、GetDefault()、isdefaultdriversspecific()

  • 在要素类上添加FillUnsetWithDefault()

公用事业

ogrinfo已更新为公开非空约束和默认值。例如

Geometry Column 1 NOT NULL = WKT
Geometry Column 2 NOT NULL = geom2
id: Integer (0.0) NOT NULL DEFAULT 1234567
dbl: Real (0.0) NOT NULL DEFAULT 1.456
str: String (0.0) NOT NULL DEFAULT 'a'
d: Date (0.0) NOT NULL DEFAULT CURRENT_DATE
t: Time (0.0) NOT NULL DEFAULT CURRENT_TIME
dt: DateTime (0.0) NOT NULL DEFAULT CURRENT_TIMESTAMP
dt2: DateTime (0.0) NOT NULL DEFAULT '2013/12/11 01:23:45'

在ogr2ogr中添加了两个新闻选项:

  • -forceNullable可移除非空约束(默认情况下,非空约束从源层传播到目标层)

  • -unsetDefault删除默认值(默认情况下,默认值从源层传播到目标层)

除非明确指定,否则如果源层的第一个几何体字段具有NOT NULL约束,ogr2ogr还会自动将GEOMETRY_NULLABLE=NO creation选项设置为支持它的目标层。

文档

新的/修改过的API被记录在案。

文件格式

以下OGR驱动程序已更新以支持新接口。

  • PG:支持不为空(用于属性和多个几何字段)和创建/读取时的默认值。AlterFieldDefn()实现被修改为支持ALTER_NULLABLE_FLAG和ALTER_DEFAULT_FLAG。

  • PGDump:支持不为空(用于属性和多个几何体字段)和创建时的默认值。

  • CartoDB:支持不为空(用于属性和单个几何体字段)和创建时的默认值。也仅支持通过身份验证登录的读取(依赖于对PostgreSQL系统表的查询)

  • GPKG:支持不为空(对于属性及其单个几何体字段)和创建/读取时的默认值。添加了几何图形图层创建。

  • SQLite:不支持空值(对于属性和多个几何体字段)。最近根据5494添加了对多个几何体字段的支持,并在创建/读取时默认。AlterFieldDefn()实现被修改为支持ALTER_NULLABLE_FLAG和ALTER_DEFAULT_FLAG。

  • MySQL:不支持空(仅用于属性字段)和创建/读取时的默认值。

  • OCI:支持不为空(对于属性及其单个几何体字段)和创建/读取时的默认值。添加了几何图形图层创建。

  • VRT:通过新的属性“nullable”和“DEFAULT”(驱动程序文档和数据/ogrvrt.xsd已更新),支持不为空(对于属性和多个几何体字段)和读取时的默认值

  • GML: supports NOT NULL (for attribute and multiple geometry field) on creation/read. DEFAULT not truly supported (no way to express it in .xsd AFAIK), but on creation, unset fields with a NOT NULL constraint and DEFAULT values will be filled by using FillUnsetWithDefault() so as to generate valid XML.

  • WFS:不支持读取时为空(仅适用于属性字段)

  • FileGDB:在读/写时支持notnull(对于属性及其单个几何体字段)。添加了几何图形和可空层创建。创建/读取时默认支持字符串、整数和实数字段(FileGDB SDK和E$RI工具中出现了一些错误/奇怪的行为,在有问题的情况下使用OpenFileGDB驱动程序可以解决……)。读取时默认支持DateTime,但创建时不支持FileGDB SDK中的bug。

  • OpenFileGDB:支持不为空(对于属性及其单个几何字段)和读取时的默认值

mssqlspace可能支持NOT NULL/DEFAULT,但尚未作为此工作的一部分进行更新。

测试套件

测试套件扩展为测试:

  • ogrúu feature.py中OGRFieldDefn、ogrgeomfieldefn和OGRFeature的所有新方法

  • 更新的驱动程序:PG、PGDump、CartoDB、GPKG、SQLite、MySQL、OCI、VRT、GML、FileGDB、OpenFileGDB

  • ogr2ogr的新选项,以及带有notnull/默认传播的默认行为

兼容性问题

这个RFC应该不会引起兼容性问题。

关于API,现有的OGRFieldDefn::SetDefault()已被更改,GetDefaultRef()已被删除。影响应该很小,因为它没有在任何驱动程序中使用,被记录为在将来很容易被删除,所以也不太可能在应用程序中使用(没有C绑定)

当不使用新的API时,在由GDAL创建的层上操作时,行为应该保持不变w.r.t GDAL 1.X。如果正在读取由其他工具创建的层,则可以读取和传播NOT NULL和/或DEFAULT。我们不能排除notnull/DEFAULT的传播在某些情况下会导致问题。在这种情况下,ogr2ogr的新选项将恢复到gdal1.X时代的行为。

实施

实施将由甚至鲁奥完成 (Spatialys _),并由 LINZ (Land Information New Zealand) .

建议的实现位于 https://github.com/rouault/gdal2/tree/rfc53_ogr_notnull_default 储存库。

更改列表: https://github.com/rouault/gdal2/compare/rfc53_ogr_notnull_default

投票历史

+1名来自JukkaR,DanielM和Ever