8913 строки
296 KiB
Python
Исполняемый файл
8913 строки
296 KiB
Python
Исполняемый файл
#!/usr/bin/env pytest
|
|
# -*- coding: utf-8 -*-
|
|
###############################################################################
|
|
# $Id$
|
|
#
|
|
# Project: GDAL/OGR Test Suite
|
|
# Purpose: Test GeoPackage driver functionality.
|
|
# Author: Paul Ramsey <pramsey@boundlessgeom.com>
|
|
#
|
|
###############################################################################
|
|
# Copyright (c) 2004, Paul Ramsey <pramsey@boundlessgeom.com>
|
|
# Copyright (c) 2014, Even Rouault <even dot rouault at spatialys.com>
|
|
#
|
|
# Permission is hereby granted, free of charge, to any person obtaining a
|
|
# copy of this software and associated documentation files (the "Software"),
|
|
# to deal in the Software without restriction, including without limitation
|
|
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
# and/or sell copies of the Software, and to permit persons to whom the
|
|
# Software is furnished to do so, subject to the following conditions:
|
|
#
|
|
# The above copyright notice and this permission notice shall be included
|
|
# in all copies or substantial portions of the Software.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
# DEALINGS IN THE SOFTWARE.
|
|
###############################################################################
|
|
|
|
import math
|
|
import os
|
|
import struct
|
|
import sys
|
|
import threading
|
|
import time
|
|
|
|
import gdaltest
|
|
import pytest
|
|
from test_py_scripts import samples_path
|
|
|
|
from osgeo import gdal, ogr, osr
|
|
|
|
pytestmark = pytest.mark.require_driver("GPKG")
|
|
|
|
###############################################################################
|
|
@pytest.fixture(autouse=True, scope="module")
|
|
def module_disable_exceptions():
|
|
with gdaltest.disable_exceptions():
|
|
yield
|
|
|
|
|
|
###############################################################################
|
|
@pytest.fixture(autouse=True, scope="module")
|
|
def startup_and_cleanup():
|
|
|
|
gdaltest.gpkg_dr = ogr.GetDriverByName("GPKG")
|
|
|
|
try:
|
|
os.remove("tmp/gpkg_test.gpkg")
|
|
except OSError:
|
|
pass
|
|
|
|
# This is to speed-up the runtime of tests on EXT4 filesystems
|
|
# Do not use this for production environment if you care about data safety
|
|
# w.r.t system/OS crashes, unless you know what you are doing.
|
|
with gdal.config_option("OGR_SQLITE_SYNCHRONOUS", "OFF"):
|
|
|
|
yield
|
|
|
|
if gdal.ReadDir("/vsimem") is not None:
|
|
print(gdal.ReadDir("/vsimem"))
|
|
for f in gdal.ReadDir("/vsimem"):
|
|
gdal.Unlink("/vsimem/" + f)
|
|
|
|
try:
|
|
os.remove("tmp/gpkg_test.gpkg")
|
|
except OSError:
|
|
pass
|
|
|
|
|
|
###############################################################################
|
|
|
|
|
|
def get_sqlite_version():
|
|
with gdaltest.disable_exceptions():
|
|
ds = ogr.Open(":memory:")
|
|
if ds is None:
|
|
return (0, 0, 0)
|
|
sql_lyr = ds.ExecuteSQL("SELECT sqlite_version()")
|
|
f = sql_lyr.GetNextFeature()
|
|
version = f.GetField(0)
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
return tuple([int(x) for x in version.split(".")[0:3]])
|
|
|
|
|
|
###############################################################################
|
|
# Validate a geopackage
|
|
|
|
|
|
def has_validate():
|
|
path = samples_path
|
|
if path not in sys.path:
|
|
sys.path.append(path)
|
|
try:
|
|
import validate_gpkg
|
|
|
|
validate_gpkg.check
|
|
except ImportError:
|
|
print("Cannot import validate_gpkg")
|
|
return False
|
|
return True
|
|
|
|
|
|
def _validate_check(filename):
|
|
if not has_validate():
|
|
return
|
|
import validate_gpkg
|
|
|
|
validate_gpkg.check(filename, extra_checks=True, warning_as_error=True)
|
|
|
|
|
|
def validate(filename, quiet=False):
|
|
my_filename = filename
|
|
if my_filename.startswith("/vsimem/"):
|
|
my_filename = "tmp/validate.gpkg"
|
|
f = gdal.VSIFOpenL(filename, "rb")
|
|
if f is None:
|
|
print("Cannot open %s" % filename)
|
|
return False
|
|
content = gdal.VSIFReadL(1, 10000000, f)
|
|
gdal.VSIFCloseL(f)
|
|
open(my_filename, "wb").write(content)
|
|
try:
|
|
_validate_check(my_filename)
|
|
except Exception as e:
|
|
if not quiet:
|
|
print(e)
|
|
return False
|
|
finally:
|
|
if my_filename != filename:
|
|
os.unlink(my_filename)
|
|
return True
|
|
|
|
|
|
###############################################################################
|
|
# Create a fresh database.
|
|
|
|
|
|
def test_ogr_gpkg_1():
|
|
|
|
gpkg_ds = gdaltest.gpkg_dr.CreateDataSource("tmp/gpkg_test.gpkg")
|
|
|
|
assert gpkg_ds is not None
|
|
|
|
gpkg_ds = None
|
|
|
|
assert validate("tmp/gpkg_test.gpkg"), "validation failed"
|
|
|
|
|
|
###############################################################################
|
|
# Re-open database to test validity
|
|
|
|
|
|
def test_ogr_gpkg_2():
|
|
|
|
gpkg_ds = ogr.Open("tmp/gpkg_test.gpkg", update=1)
|
|
|
|
# Should default to GPKG 1.2
|
|
sql_lyr = gpkg_ds.ExecuteSQL("PRAGMA application_id")
|
|
f = sql_lyr.GetNextFeature()
|
|
if f["application_id"] != 1196444487:
|
|
f.DumpReadable()
|
|
pytest.fail()
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
sql_lyr = gpkg_ds.ExecuteSQL("PRAGMA user_version")
|
|
f = sql_lyr.GetNextFeature()
|
|
if f["user_version"] != 10200:
|
|
f.DumpReadable()
|
|
pytest.fail()
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
|
|
###############################################################################
|
|
# Create a layer
|
|
|
|
|
|
def test_ogr_gpkg_3():
|
|
|
|
gpkg_ds = ogr.Open("tmp/gpkg_test.gpkg", update=1)
|
|
|
|
srs4326 = osr.SpatialReference()
|
|
srs4326.ImportFromEPSG(4326)
|
|
lyr = gpkg_ds.CreateLayer(
|
|
"first_layer",
|
|
geom_type=ogr.wkbPoint,
|
|
srs=srs4326,
|
|
options=["GEOMETRY_NAME=gpkg_geometry", "SPATIAL_INDEX=NO"],
|
|
)
|
|
assert lyr is not None
|
|
|
|
# Test creating a layer with an existing name
|
|
lyr = gpkg_ds.CreateLayer("a_layer", options=["SPATIAL_INDEX=NO"])
|
|
assert lyr is not None
|
|
with gdaltest.error_handler():
|
|
lyr = gpkg_ds.CreateLayer("a_layer", options=["SPATIAL_INDEX=NO"])
|
|
assert lyr is None, "layer creation should have failed"
|
|
|
|
|
|
###############################################################################
|
|
# Close and re-open to test the layer registration
|
|
|
|
|
|
def test_ogr_gpkg_4():
|
|
|
|
assert validate("tmp/gpkg_test.gpkg"), "validation failed"
|
|
|
|
with gdaltest.error_handler():
|
|
gpkg_ds = ogr.Open("tmp/gpkg_test.gpkg", update=1)
|
|
|
|
assert gpkg_ds is not None
|
|
|
|
assert gpkg_ds.GetLayerCount() == 2, "unexpected number of layers"
|
|
|
|
lyr0 = gpkg_ds.GetLayer(0)
|
|
|
|
assert lyr0.GetFIDColumn() == "fid", "unexpected FID name for layer 0"
|
|
|
|
gpkg_ds = None
|
|
gpkg_ds = ogr.Open("tmp/gpkg_test.gpkg", update=1)
|
|
|
|
lyr0 = gpkg_ds.GetLayer(0)
|
|
|
|
assert lyr0.GetName() == "first_layer", "unexpected layer name for layer 0"
|
|
|
|
gpkg_ds = None
|
|
gpkg_ds = ogr.Open("tmp/gpkg_test.gpkg", update=1)
|
|
|
|
lyr0 = gpkg_ds.GetLayer(0)
|
|
lyr1 = gpkg_ds.GetLayer(1)
|
|
|
|
assert (
|
|
lyr0.GetLayerDefn().GetGeomFieldDefn(0).GetName() == "gpkg_geometry"
|
|
), "unexpected geometry field name for layer 0"
|
|
|
|
assert lyr1.GetName() == "a_layer", "unexpected layer name for layer 1"
|
|
|
|
sql_lyr = gpkg_ds.ExecuteSQL(
|
|
"SELECT * FROM sqlite_master WHERE name = 'gpkg_extensions'"
|
|
)
|
|
assert sql_lyr.GetFeatureCount() == 0
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
|
|
###############################################################################
|
|
# Delete a layer
|
|
|
|
|
|
def test_ogr_gpkg_5():
|
|
|
|
gpkg_ds = ogr.Open("tmp/gpkg_test.gpkg", update=1)
|
|
|
|
assert gpkg_ds.GetLayerCount() == 2, "unexpected number of layers"
|
|
|
|
with gdaltest.error_handler():
|
|
ret = gpkg_ds.DeleteLayer(-1)
|
|
assert ret != 0, "expected error"
|
|
|
|
with gdaltest.error_handler():
|
|
ret = gpkg_ds.DeleteLayer(gpkg_ds.GetLayerCount())
|
|
assert ret != 0, "expected error"
|
|
|
|
assert gpkg_ds.DeleteLayer(1) == 0, "got error code from DeleteLayer(1)"
|
|
|
|
assert gpkg_ds.DeleteLayer(0) == 0, "got error code from DeleteLayer(0)"
|
|
|
|
assert gpkg_ds.GetLayerCount() == 0, "unexpected number of layers (not 0)"
|
|
|
|
|
|
###############################################################################
|
|
# Add fields
|
|
|
|
|
|
def test_ogr_gpkg_6():
|
|
|
|
gpkg_ds = ogr.Open("tmp/gpkg_test.gpkg", update=1)
|
|
|
|
srs4326 = osr.SpatialReference()
|
|
srs4326.ImportFromEPSG(4326)
|
|
lyr = gpkg_ds.CreateLayer("field_test_layer", geom_type=ogr.wkbPoint, srs=srs4326)
|
|
assert lyr is not None
|
|
|
|
field_defn = ogr.FieldDefn("dummy", ogr.OFTString)
|
|
lyr.CreateField(field_defn)
|
|
|
|
assert (
|
|
lyr.GetLayerDefn().GetFieldDefn(0).GetType() == ogr.OFTString
|
|
), "wrong field type"
|
|
|
|
gpkg_ds = None
|
|
|
|
assert validate("tmp/gpkg_test.gpkg"), "validation failed"
|
|
|
|
with gdaltest.error_handler():
|
|
gpkg_ds = ogr.Open("tmp/gpkg_test.gpkg", update=1)
|
|
|
|
assert gpkg_ds is not None
|
|
|
|
assert gpkg_ds.GetLayerCount() == 1
|
|
|
|
lyr = gpkg_ds.GetLayer(0)
|
|
assert lyr.GetName() == "field_test_layer"
|
|
|
|
field_defn_out = lyr.GetLayerDefn().GetFieldDefn(0)
|
|
assert field_defn_out.GetType() == ogr.OFTString, "wrong field type after reopen"
|
|
|
|
assert field_defn_out.GetName() == "dummy", "wrong field name after reopen"
|
|
|
|
|
|
###############################################################################
|
|
# Add a feature / read a feature / set a feature / upsert a feature / delete a feature
|
|
|
|
|
|
def test_ogr_gpkg_7():
|
|
|
|
gpkg_ds = ogr.Open("tmp/gpkg_test.gpkg", update=1)
|
|
|
|
lyr = gpkg_ds.GetLayerByName("field_test_layer")
|
|
geom = ogr.CreateGeometryFromWkt("POINT(10 10)")
|
|
feat = ogr.Feature(lyr.GetLayerDefn())
|
|
feat.SetGeometry(geom)
|
|
feat.SetField("dummy", "a dummy value")
|
|
|
|
assert (
|
|
lyr.TestCapability(ogr.OLCSequentialWrite) == 1
|
|
), "lyr.TestCapability(ogr.OLCSequentialWrite) != 1"
|
|
|
|
assert lyr.CreateFeature(feat) == 0, "cannot create feature"
|
|
|
|
# Read back what we just inserted
|
|
lyr.ResetReading()
|
|
feat_read = lyr.GetNextFeature()
|
|
assert feat_read.GetField("dummy") == "a dummy value", "output does not match input"
|
|
|
|
# Only inserted one thing, so second feature should return NULL
|
|
feat_read = lyr.GetNextFeature()
|
|
assert feat_read is None, "last call should return NULL"
|
|
|
|
# Check that calling again GetNextFeature() does not reset the iterator
|
|
feat_read = lyr.GetNextFeature()
|
|
assert feat_read is None, "last call should still return NULL"
|
|
|
|
# Add another feature
|
|
geom = ogr.CreateGeometryFromWkt("POINT(100 100)")
|
|
feat = ogr.Feature(lyr.GetLayerDefn())
|
|
feat.SetGeometry(geom)
|
|
feat.SetField("dummy", "who you calling a dummy?")
|
|
assert lyr.CreateFeature(feat) == 0, "cannot create feature"
|
|
|
|
assert (
|
|
lyr.TestCapability(ogr.OLCRandomRead) == 1
|
|
), "lyr.TestCapability(ogr.OLCRandomRead) != 1"
|
|
|
|
# Random read a feature
|
|
feat_read_random = lyr.GetFeature(feat.GetFID())
|
|
assert (
|
|
feat_read_random.GetField("dummy") == "who you calling a dummy?"
|
|
), "random read output does not match input"
|
|
|
|
assert (
|
|
lyr.TestCapability(ogr.OLCRandomWrite) == 1
|
|
), "lyr.TestCapability(ogr.OLCRandomWrite) != 1"
|
|
|
|
# Random write a feature
|
|
feat.SetField("dummy", "i am no dummy")
|
|
lyr.SetFeature(feat)
|
|
feat_read_random = lyr.GetFeature(feat.GetFID())
|
|
assert (
|
|
feat_read_random.GetField("dummy") == "i am no dummy"
|
|
), "random read output does not match random write input"
|
|
|
|
assert (
|
|
lyr.TestCapability(ogr.OLCDeleteFeature) == 1
|
|
), "lyr.TestCapability(ogr.OLCDeleteFeature) != 1"
|
|
|
|
assert lyr.GetFeatureCount() == 2
|
|
|
|
def get_feature_count_from_gpkg_contents():
|
|
sql_lyr = gpkg_ds.ExecuteSQL(
|
|
'SELECT feature_count FROM gpkg_ogr_contents WHERE table_name = "field_test_layer"',
|
|
dialect="DEBUG",
|
|
)
|
|
f = sql_lyr.GetNextFeature()
|
|
ret = f.GetField(0)
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
return ret
|
|
|
|
assert get_feature_count_from_gpkg_contents() == 2
|
|
|
|
assert lyr.CreateFeature(ogr.Feature(lyr.GetLayerDefn())) == ogr.OGRERR_NONE
|
|
|
|
assert lyr.GetFeatureCount() == 3
|
|
|
|
# 2 is expected here since CreateFeature() has temporarily disable triggers
|
|
assert get_feature_count_from_gpkg_contents() == 2
|
|
|
|
# Test upserting an existing feature
|
|
feat.SetField("dummy", "updated")
|
|
fid = feat.GetFID()
|
|
assert lyr.UpsertFeature(feat) == ogr.OGRERR_NONE, "cannot upsert existing feature"
|
|
|
|
assert feat.GetFID() == fid
|
|
|
|
# UpsertFeature() has serialized value 3 and re-enables triggers
|
|
assert get_feature_count_from_gpkg_contents() == 3
|
|
|
|
upserted_feat = lyr.GetFeature(feat.GetFID())
|
|
assert (
|
|
upserted_feat.GetField("dummy") == "updated"
|
|
), "upsert failed to update existing feature"
|
|
|
|
# Delete a feature
|
|
lyr.DeleteFeature(feat.GetFID())
|
|
|
|
assert get_feature_count_from_gpkg_contents() is None
|
|
|
|
lyr.SyncToDisk()
|
|
|
|
assert get_feature_count_from_gpkg_contents() is None
|
|
|
|
assert lyr.GetFeatureCount() == 2, "delete feature did not delete"
|
|
|
|
assert get_feature_count_from_gpkg_contents() == 2
|
|
|
|
# Test upserting a non-existing feature
|
|
assert (
|
|
lyr.UpsertFeature(feat) == ogr.OGRERR_NONE
|
|
), "cannot upsert non-existing feature"
|
|
assert feat.GetFID() == fid
|
|
|
|
assert get_feature_count_from_gpkg_contents() == 3
|
|
|
|
assert lyr.GetFeatureCount() == 3, "upsert failed to add non-existing feature"
|
|
|
|
lyr.SyncToDisk()
|
|
|
|
assert get_feature_count_from_gpkg_contents() == 3
|
|
|
|
# Test updating non-existing feature
|
|
feat.SetFID(-10)
|
|
assert (
|
|
lyr.SetFeature(feat) == ogr.OGRERR_NON_EXISTING_FEATURE
|
|
), "Expected failure of SetFeature()."
|
|
|
|
# Test deleting non-existing feature
|
|
assert (
|
|
lyr.DeleteFeature(-10) == ogr.OGRERR_NON_EXISTING_FEATURE
|
|
), "Expected failure of DeleteFeature()."
|
|
|
|
# Delete the layer
|
|
if gpkg_ds.DeleteLayer("field_test_layer") != 0:
|
|
gdaltest.post_reason("got error code from DeleteLayer(field_test_layer)")
|
|
|
|
|
|
###############################################################################
|
|
# Test a variety of geometry feature types and attribute types
|
|
|
|
|
|
def test_ogr_gpkg_8():
|
|
|
|
gpkg_ds = ogr.Open("tmp/gpkg_test.gpkg", update=1)
|
|
|
|
srs = osr.SpatialReference()
|
|
# Test a non-default SRS
|
|
srs.ImportFromEPSG(32631)
|
|
|
|
lyr = gpkg_ds.CreateLayer("tbl_linestring", geom_type=ogr.wkbLineString, srs=srs)
|
|
assert lyr is not None
|
|
|
|
lyr.StartTransaction()
|
|
lyr.CreateField(ogr.FieldDefn("fld_integer", ogr.OFTInteger))
|
|
lyr.CreateField(ogr.FieldDefn("fld_string", ogr.OFTString))
|
|
lyr.CreateField(ogr.FieldDefn("fld_real", ogr.OFTReal))
|
|
lyr.CreateField(ogr.FieldDefn("fld_date", ogr.OFTDate))
|
|
lyr.CreateField(ogr.FieldDefn("fld_datetime", ogr.OFTDateTime))
|
|
lyr.CreateField(ogr.FieldDefn("fld_binary", ogr.OFTBinary))
|
|
fld_defn = ogr.FieldDefn("fld_boolean", ogr.OFTInteger)
|
|
fld_defn.SetSubType(ogr.OFSTBoolean)
|
|
lyr.CreateField(fld_defn)
|
|
fld_defn = ogr.FieldDefn("fld_smallint", ogr.OFTInteger)
|
|
fld_defn.SetSubType(ogr.OFSTInt16)
|
|
lyr.CreateField(fld_defn)
|
|
fld_defn = ogr.FieldDefn("fld_float", ogr.OFTReal)
|
|
fld_defn.SetSubType(ogr.OFSTFloat32)
|
|
lyr.CreateField(fld_defn)
|
|
lyr.CreateField(ogr.FieldDefn("fld_integer64", ogr.OFTInteger64))
|
|
|
|
geom = ogr.CreateGeometryFromWkt("LINESTRING(5 5,10 5,10 10,5 10)")
|
|
feat = ogr.Feature(lyr.GetLayerDefn())
|
|
feat.SetGeometry(geom)
|
|
|
|
for i in range(10):
|
|
feat.SetFID(-1)
|
|
feat.SetField("fld_integer", 10 + i)
|
|
feat.SetField("fld_real", 3.14159 / (i + 1))
|
|
feat.SetField("fld_string", "test string %d test" % i)
|
|
feat.SetField("fld_date", "2014/05/17 ")
|
|
feat.SetField("fld_datetime", "2014/12/31 23:59:59.999Z")
|
|
feat.SetFieldBinaryFromHexString("fld_binary", "fffe")
|
|
feat.SetField("fld_boolean", 1)
|
|
feat.SetField("fld_smallint", -32768)
|
|
feat.SetField("fld_float", 1.23)
|
|
feat.SetField("fld_integer64", 1000000000000 + i)
|
|
|
|
assert lyr.CreateFeature(feat) == 0, "cannot create feature %d" % i
|
|
lyr.CommitTransaction()
|
|
|
|
feat = ogr.Feature(lyr.GetLayerDefn())
|
|
assert lyr.CreateFeature(feat) == 0, "cannot insert empty"
|
|
|
|
feat.SetFID(6)
|
|
assert lyr.SetFeature(feat) == 0, "cannot update with empty"
|
|
|
|
gpkg_ds = None
|
|
|
|
assert validate("tmp/gpkg_test.gpkg"), "validation failed"
|
|
|
|
gpkg_ds = ogr.Open("tmp/gpkg_test.gpkg", update=1)
|
|
lyr = gpkg_ds.GetLayerByName("tbl_linestring")
|
|
assert lyr.GetLayerDefn().GetFieldDefn(6).GetSubType() == ogr.OFSTBoolean
|
|
assert lyr.GetLayerDefn().GetFieldDefn(7).GetSubType() == ogr.OFSTInt16
|
|
assert lyr.GetLayerDefn().GetFieldDefn(8).GetSubType() == ogr.OFSTFloat32
|
|
feat = lyr.GetNextFeature()
|
|
if (
|
|
feat.GetField(0) != 10
|
|
or feat.GetField(1) != "test string 0 test"
|
|
or feat.GetField(2) != 3.14159
|
|
or feat.GetField(3) != "2014/05/17"
|
|
or feat.GetField(4) != "2014/12/31 23:59:59.999+00"
|
|
or feat.GetField(5) != "FFFE"
|
|
or feat.GetField(6) != 1
|
|
or feat.GetField(7) != -32768
|
|
or feat.GetField(8) != 1.23
|
|
or feat.GetField(9) != 1000000000000
|
|
):
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
|
|
lyr = gpkg_ds.CreateLayer("tbl_polygon", geom_type=ogr.wkbPolygon, srs=srs)
|
|
assert lyr is not None
|
|
|
|
lyr.StartTransaction()
|
|
lyr.CreateField(ogr.FieldDefn("fld_datetime", ogr.OFTDateTime))
|
|
lyr.CreateField(ogr.FieldDefn("fld_string", ogr.OFTString))
|
|
|
|
geom = ogr.CreateGeometryFromWkt(
|
|
"POLYGON((5 5, 10 5, 10 10, 5 10, 5 5),(6 6, 6 7, 7 7, 7 6, 6 6))"
|
|
)
|
|
feat = ogr.Feature(lyr.GetLayerDefn())
|
|
feat.SetGeometry(geom)
|
|
|
|
for i in range(10):
|
|
feat.SetFID(-1)
|
|
feat.SetField("fld_string", "my super string %d" % i)
|
|
feat.SetField("fld_datetime", "2010-01-01")
|
|
|
|
assert lyr.CreateFeature(feat) == 0, "cannot create polygon feature %d" % i
|
|
lyr.CommitTransaction()
|
|
|
|
feat = lyr.GetFeature(3)
|
|
geom_read = feat.GetGeometryRef()
|
|
assert (
|
|
geom.ExportToWkt() == geom_read.ExportToWkt()
|
|
), "geom output not equal to geom input"
|
|
|
|
# Test out the 3D support...
|
|
lyr = gpkg_ds.CreateLayer("tbl_polygon25d", geom_type=ogr.wkbPolygon25D, srs=srs)
|
|
assert lyr is not None
|
|
|
|
lyr.CreateField(ogr.FieldDefn("fld_string", ogr.OFTString))
|
|
geom = ogr.CreateGeometryFromWkt(
|
|
"POLYGON((5 5 1, 10 5 2, 10 10 3, 5 104 , 5 5 1),(6 6 4, 6 7 5, 7 7 6, 7 6 7, 6 6 4))"
|
|
)
|
|
feat = ogr.Feature(lyr.GetLayerDefn())
|
|
feat.SetGeometry(geom)
|
|
lyr.CreateFeature(feat)
|
|
|
|
lyr.ResetReading()
|
|
feat = lyr.GetNextFeature()
|
|
geom_read = feat.GetGeometryRef()
|
|
assert (
|
|
geom.ExportToWkt() == geom_read.ExportToWkt()
|
|
), "3d geom output not equal to geom input"
|
|
|
|
|
|
###############################################################################
|
|
# Test support for extents and counts
|
|
|
|
|
|
def test_ogr_gpkg_9():
|
|
|
|
gpkg_ds = ogr.Open("tmp/gpkg_test.gpkg", update=1)
|
|
lyr = gpkg_ds.GetLayerByName("tbl_linestring")
|
|
extent = lyr.GetExtent()
|
|
assert extent == (5.0, 10.0, 5.0, 10.0), "got bad extent"
|
|
|
|
fcount = lyr.GetFeatureCount()
|
|
assert fcount == 11, "got bad featurecount"
|
|
|
|
|
|
###############################################################################
|
|
# Test non-SELECT SQL commands
|
|
|
|
|
|
def test_ogr_gpkg_11():
|
|
|
|
gpkg_ds = ogr.Open("tmp/gpkg_test.gpkg", update=1)
|
|
gpkg_ds.ExecuteSQL(
|
|
"CREATE INDEX tbl_linestring_fld_integer_idx ON tbl_linestring(fld_integer)"
|
|
)
|
|
gpkg_ds.ExecuteSQL("ALTER TABLE tbl_linestring RENAME TO tbl_linestring_renamed;")
|
|
gpkg_ds.ExecuteSQL("VACUUM")
|
|
gpkg_ds = None
|
|
|
|
gpkg_ds = ogr.Open("tmp/gpkg_test.gpkg", update=1)
|
|
lyr = gpkg_ds.GetLayerByName("tbl_linestring_renamed")
|
|
assert lyr is not None
|
|
lyr.SetAttributeFilter("fld_integer = 10")
|
|
assert lyr.GetFeatureCount() == 1
|
|
|
|
|
|
###############################################################################
|
|
# Test SELECT SQL commands
|
|
|
|
|
|
def test_ogr_gpkg_12():
|
|
|
|
gpkg_ds = ogr.Open("tmp/gpkg_test.gpkg", update=1)
|
|
sql_lyr = gpkg_ds.ExecuteSQL("SELECT * FROM tbl_linestring_renamed")
|
|
assert sql_lyr.GetFIDColumn() == "fid"
|
|
assert sql_lyr.GetGeomType() == ogr.wkbLineString
|
|
assert sql_lyr.GetGeometryColumn() == "geom"
|
|
assert sql_lyr.GetSpatialRef().ExportToWkt().find("32631") >= 0
|
|
feat = sql_lyr.GetNextFeature()
|
|
assert feat.GetFID() == 1
|
|
assert sql_lyr.GetFeatureCount() == 11
|
|
assert sql_lyr.GetLayerDefn().GetFieldCount() == 10
|
|
assert sql_lyr.GetLayerDefn().GetFieldDefn(6).GetSubType() == ogr.OFSTBoolean
|
|
assert sql_lyr.GetLayerDefn().GetFieldDefn(7).GetSubType() == ogr.OFSTInt16
|
|
assert sql_lyr.GetLayerDefn().GetFieldDefn(8).GetSubType() == ogr.OFSTFloat32
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
sql_lyr = gpkg_ds.ExecuteSQL(
|
|
"SELECT "
|
|
"CAST(fid AS INTEGER) AS FID, "
|
|
"CAST(fid AS INTEGER) AS FID, "
|
|
"_rowid_ ,"
|
|
"CAST(geom AS BLOB) AS GEOM, "
|
|
"CAST(geom AS BLOB) AS GEOM, "
|
|
"CAST(fld_integer AS INTEGER) AS FLD_INTEGER, "
|
|
"CAST(fld_integer AS INTEGER) AS FLD_INTEGER, "
|
|
"CAST(fld_string AS TEXT) AS FLD_STRING, "
|
|
"CAST(fld_real AS REAL) AS FLD_REAL, "
|
|
"CAST(fld_binary as BLOB) as FLD_BINARY, "
|
|
"CAST(fld_integer64 AS INTEGER) AS FLD_INTEGER64 "
|
|
"FROM tbl_linestring_renamed"
|
|
)
|
|
assert sql_lyr.GetFIDColumn() == "FID"
|
|
assert sql_lyr.GetGeometryColumn() == "GEOM"
|
|
assert sql_lyr.GetLayerDefn().GetFieldCount() == 5
|
|
assert sql_lyr.GetLayerDefn().GetFieldDefn(0).GetName() == "FLD_INTEGER"
|
|
assert sql_lyr.GetLayerDefn().GetFieldDefn(0).GetType() == ogr.OFTInteger
|
|
assert sql_lyr.GetLayerDefn().GetFieldDefn(1).GetName() == "FLD_STRING"
|
|
assert sql_lyr.GetLayerDefn().GetFieldDefn(1).GetType() == ogr.OFTString
|
|
assert sql_lyr.GetLayerDefn().GetFieldDefn(2).GetName() == "FLD_REAL"
|
|
assert sql_lyr.GetLayerDefn().GetFieldDefn(2).GetType() == ogr.OFTReal
|
|
assert sql_lyr.GetLayerDefn().GetFieldDefn(3).GetName() == "FLD_BINARY"
|
|
assert sql_lyr.GetLayerDefn().GetFieldDefn(3).GetType() == ogr.OFTBinary
|
|
assert sql_lyr.GetLayerDefn().GetFieldDefn(4).GetName() == "FLD_INTEGER64"
|
|
assert sql_lyr.GetLayerDefn().GetFieldDefn(4).GetType() == ogr.OFTInteger64
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
sql_lyr = gpkg_ds.ExecuteSQL("SELECT * FROM tbl_linestring_renamed WHERE 0=1")
|
|
feat = sql_lyr.GetNextFeature()
|
|
assert feat is None
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
for sql in [
|
|
"SELECT * FROM tbl_linestring_renamed LIMIT 1",
|
|
"SELECT * FROM tbl_linestring_renamed ORDER BY fld_integer LIMIT 1",
|
|
"SELECT * FROM tbl_linestring_renamed UNION ALL SELECT * FROM tbl_linestring_renamed ORDER BY fld_integer LIMIT 1",
|
|
]:
|
|
sql_lyr = gpkg_ds.ExecuteSQL(sql)
|
|
feat = sql_lyr.GetNextFeature()
|
|
assert feat is not None
|
|
feat = sql_lyr.GetNextFeature()
|
|
assert feat is None
|
|
assert sql_lyr.GetFeatureCount() == 1
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
sql_lyr = gpkg_ds.ExecuteSQL("SELECT sqlite_version()")
|
|
feat = sql_lyr.GetNextFeature()
|
|
assert feat is not None
|
|
assert sql_lyr.GetLayerDefn().GetFieldCount() == 1
|
|
assert sql_lyr.GetLayerDefn().GetGeomFieldCount() == 0
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
|
|
###############################################################################
|
|
# Test non-spatial tables
|
|
|
|
|
|
def test_ogr_gpkg_13():
|
|
|
|
gpkg_ds = ogr.Open("tmp/gpkg_test.gpkg", update=1)
|
|
lyr = gpkg_ds.CreateLayer("non_spatial", geom_type=ogr.wkbNone)
|
|
feat = ogr.Feature(lyr.GetLayerDefn())
|
|
lyr.CreateFeature(feat)
|
|
feat = None
|
|
lyr.CreateField(ogr.FieldDefn("fld_integer", ogr.OFTInteger))
|
|
feat = ogr.Feature(lyr.GetLayerDefn())
|
|
feat.SetField("fld_integer", 1)
|
|
lyr.CreateFeature(feat)
|
|
feat = None
|
|
lyr.ResetReading()
|
|
feat = lyr.GetNextFeature()
|
|
if not feat.IsFieldNull("fld_integer"):
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
feat = lyr.GetNextFeature()
|
|
if feat.GetField("fld_integer") != 1:
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
|
|
# Test second aspatial layer
|
|
lyr = gpkg_ds.CreateLayer("non_spatial2", geom_type=ogr.wkbNone)
|
|
|
|
gpkg_ds = None
|
|
gdal.ErrorReset()
|
|
gpkg_ds = ogr.Open("tmp/gpkg_test.gpkg", update=1)
|
|
assert gdal.GetLastErrorMsg() == "", "fail : warning NOT expected"
|
|
assert gpkg_ds.GetLayerCount() == 5
|
|
lyr = gpkg_ds.GetLayer("non_spatial")
|
|
assert lyr.GetGeomType() == ogr.wkbNone
|
|
feat = lyr.GetNextFeature()
|
|
assert feat.IsFieldNull("fld_integer")
|
|
feat = lyr.GetNextFeature()
|
|
if feat.GetField("fld_integer") != 1:
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
|
|
|
|
###############################################################################
|
|
# Add various geometries to test spatial filtering
|
|
|
|
|
|
def test_ogr_gpkg_14():
|
|
|
|
sr = osr.SpatialReference()
|
|
sr.ImportFromEPSG(32631)
|
|
|
|
gpkg_ds = ogr.Open("tmp/gpkg_test.gpkg", update=1)
|
|
lyr = gpkg_ds.CreateLayer(
|
|
"point_no_spi-but-with-dashes",
|
|
geom_type=ogr.wkbPoint,
|
|
options=["SPATIAL_INDEX=NO"],
|
|
srs=sr,
|
|
)
|
|
assert lyr.TestCapability(ogr.OLCFastSpatialFilter) == 0
|
|
feat = ogr.Feature(lyr.GetLayerDefn())
|
|
feat.SetGeometry(ogr.CreateGeometryFromWkt("POINT(1000 30000000)"))
|
|
lyr.CreateFeature(feat)
|
|
feat = ogr.Feature(lyr.GetLayerDefn())
|
|
feat.SetGeometry(ogr.CreateGeometryFromWkt("POINT(-1000 30000000)"))
|
|
lyr.CreateFeature(feat)
|
|
feat = ogr.Feature(lyr.GetLayerDefn())
|
|
feat.SetGeometry(ogr.CreateGeometryFromWkt("POINT(1000 -30000000)"))
|
|
lyr.CreateFeature(feat)
|
|
feat = ogr.Feature(lyr.GetLayerDefn())
|
|
feat.SetGeometry(ogr.CreateGeometryFromWkt("POINT(-1000 -30000000)"))
|
|
lyr.CreateFeature(feat)
|
|
# Test null geometry
|
|
feat = ogr.Feature(lyr.GetLayerDefn())
|
|
lyr.CreateFeature(feat)
|
|
# Test empty geometry
|
|
feat = ogr.Feature(lyr.GetLayerDefn())
|
|
feat.SetGeometry(ogr.CreateGeometryFromWkt("POINT EMPTY"))
|
|
lyr.CreateFeature(feat)
|
|
|
|
f = lyr.GetFeature(5)
|
|
if f.GetGeometryRef() is not None:
|
|
f.DumpReadable()
|
|
pytest.fail()
|
|
f = lyr.GetFeature(6)
|
|
if f.GetGeometryRef().ExportToWkt() != "POINT EMPTY":
|
|
f.DumpReadable()
|
|
pytest.fail()
|
|
f = None
|
|
|
|
sql_lyr = gpkg_ds.ExecuteSQL('SELECT * FROM "point_no_spi-but-with-dashes"')
|
|
res = sql_lyr.TestCapability(ogr.OLCFastSpatialFilter)
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
assert res == 0
|
|
|
|
lyr = gpkg_ds.CreateLayer("point-with-spi-and-dashes", geom_type=ogr.wkbPoint)
|
|
assert lyr.TestCapability(ogr.OLCFastSpatialFilter) == 1
|
|
feat = ogr.Feature(lyr.GetLayerDefn())
|
|
feat.SetGeometry(ogr.CreateGeometryFromWkt("POINT(1000 30000000)"))
|
|
lyr.CreateFeature(feat)
|
|
feat = ogr.Feature(lyr.GetLayerDefn())
|
|
feat.SetGeometry(ogr.CreateGeometryFromWkt("POINT(-1000 30000000)"))
|
|
lyr.CreateFeature(feat)
|
|
feat = ogr.Feature(lyr.GetLayerDefn())
|
|
feat.SetGeometry(ogr.CreateGeometryFromWkt("POINT(1000 -30000000)"))
|
|
lyr.CreateFeature(feat)
|
|
feat = ogr.Feature(lyr.GetLayerDefn())
|
|
feat.SetGeometry(ogr.CreateGeometryFromWkt("POINT(-1000 -30000000)"))
|
|
lyr.CreateFeature(feat)
|
|
# Test null geometry
|
|
feat = ogr.Feature(lyr.GetLayerDefn())
|
|
lyr.CreateFeature(feat)
|
|
# Test empty geometry
|
|
feat = ogr.Feature(lyr.GetLayerDefn())
|
|
feat.SetGeometry(ogr.CreateGeometryFromWkt("POINT EMPTY"))
|
|
lyr.CreateFeature(feat)
|
|
|
|
sql_lyr = gpkg_ds.ExecuteSQL('SELECT * FROM "point-with-spi-and-dashes"')
|
|
res = sql_lyr.TestCapability(ogr.OLCFastSpatialFilter)
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
assert res == 1
|
|
|
|
# Test spatial filer right away
|
|
lyr.SetSpatialFilterRect(1000, 30000000, 1000, 30000000)
|
|
lyr.ResetReading()
|
|
f = lyr.GetNextFeature()
|
|
assert f is not None
|
|
f = lyr.GetNextFeature()
|
|
assert f is None
|
|
|
|
|
|
###############################################################################
|
|
def _has_spatialite_4_3_or_later(ds):
|
|
has_spatialite_4_3_or_later = False
|
|
with gdaltest.error_handler():
|
|
sql_lyr = ds.ExecuteSQL("SELECT spatialite_version()")
|
|
if sql_lyr:
|
|
f = sql_lyr.GetNextFeature()
|
|
version = f.GetField(0)
|
|
version = ".".join(version.split(".")[0:2])
|
|
version = float(version)
|
|
if version >= 4.3:
|
|
has_spatialite_4_3_or_later = True
|
|
# print('Spatialite 4.3 or later found')
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
return has_spatialite_4_3_or_later
|
|
|
|
|
|
###############################################################################
|
|
# Test SQL functions
|
|
|
|
|
|
def test_ogr_gpkg_15():
|
|
|
|
gpkg_ds = ogr.Open("tmp/gpkg_test.gpkg", update=1)
|
|
sql_lyr = gpkg_ds.ExecuteSQL(
|
|
"SELECT ST_IsEmpty(geom), ST_SRID(geom), ST_GeometryType(geom), "
|
|
+ 'ST_MinX(geom), ST_MinY(geom), ST_MaxX(geom), ST_MaxY(geom) FROM "point_no_spi-but-with-dashes" WHERE fid = 1'
|
|
)
|
|
feat = sql_lyr.GetNextFeature()
|
|
if (
|
|
feat.GetField(0) != 0
|
|
or feat.GetField(1) != 32631
|
|
or feat.GetField(2) != "POINT"
|
|
or feat.GetField(3) != 1000
|
|
or feat.GetField(4) != 30000000
|
|
or feat.GetField(5) != 1000
|
|
or feat.GetField(6) != 30000000
|
|
):
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
sql_lyr = gpkg_ds.ExecuteSQL(
|
|
"SELECT ST_IsEmpty(geom), ST_SRID(geom), ST_GeometryType(geom), "
|
|
+ "ST_MinX(geom), ST_MinY(geom), ST_MaxX(geom), ST_MaxY(geom) FROM tbl_linestring_renamed WHERE geom IS NULL"
|
|
)
|
|
feat = sql_lyr.GetNextFeature()
|
|
if (
|
|
not feat.IsFieldNull(0)
|
|
or not feat.IsFieldNull(1)
|
|
or not feat.IsFieldNull(2)
|
|
or not feat.IsFieldNull(3)
|
|
or not feat.IsFieldNull(4)
|
|
or not feat.IsFieldNull(5)
|
|
or not feat.IsFieldNull(6)
|
|
):
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
for (expected_type, actual_type, expected_result) in [
|
|
("POINT", "POINT", 1),
|
|
("LINESTRING", "POINT", 0),
|
|
("GEOMETRY", "POINT", 1),
|
|
("POINT", "GEOMETRY", 0),
|
|
("GEOMETRYCOLLECTION", "MULTIPOINT", 1),
|
|
("GEOMETRYCOLLECTION", "POINT", 0),
|
|
]:
|
|
sql_lyr = gpkg_ds.ExecuteSQL(
|
|
"SELECT GPKG_IsAssignable('%s', '%s')" % (expected_type, actual_type)
|
|
)
|
|
feat = sql_lyr.GetNextFeature()
|
|
got_result = feat.GetField(0)
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
assert (
|
|
got_result == expected_result
|
|
), "expected_type=%s actual_type=%s expected_result=%d got_result=%d" % (
|
|
expected_type,
|
|
actual_type,
|
|
expected_result,
|
|
got_result,
|
|
)
|
|
|
|
for (sql, expected_result) in [
|
|
("SELECT HasSpatialIndex('point-with-spi-and-dashes', 'geom')", 1),
|
|
("SELECT DisableSpatialIndex('point-with-spi-and-dashes', 'geom')", 1),
|
|
("SELECT HasSpatialIndex('point-with-spi-and-dashes', 'geom')", 0),
|
|
("SELECT DisableSpatialIndex('point-with-spi-and-dashes', 'geom')", 0),
|
|
("SELECT CreateSpatialIndex('point-with-spi-and-dashes', 'geom')", 1),
|
|
("SELECT HasSpatialIndex('point-with-spi-and-dashes', 'geom')", 1),
|
|
("SELECT CreateSpatialIndex('point-with-spi-and-dashes', 'geom')", 0),
|
|
("SELECT CreateSpatialIndex('point-with-spi-and-dashes', NULL)", 0),
|
|
("SELECT HasSpatialIndex('point-with-spi-and-dashes', NULL)", 0),
|
|
("SELECT CreateSpatialIndex(NULL, 'geom')", 0),
|
|
("SELECT CreateSpatialIndex('bla', 'geom')", 0),
|
|
("SELECT CreateSpatialIndex('point-with-spi-and-dashes', 'bla')", 0),
|
|
("SELECT DisableSpatialIndex('point-with-spi-and-dashes', NULL)", 0),
|
|
("SELECT DisableSpatialIndex(NULL, 'geom')", 0),
|
|
("SELECT DisableSpatialIndex('bla', 'geom')", 0),
|
|
("SELECT DisableSpatialIndex('point-with-spi-and-dashes', 'bla')", 0),
|
|
("SELECT HasSpatialIndex(NULL, 'geom')", 0),
|
|
("SELECT HasSpatialIndex('bla', 'geom')", 0),
|
|
("SELECT HasSpatialIndex('point-with-spi-and-dashes', 'bla')", 0),
|
|
("SELECT CreateSpatialIndex('non_spatial', '')", 0),
|
|
("SELECT CreateSpatialIndex('point_no_spi-but-with-dashes', 'geom')", 1),
|
|
# Final DisableSpatialIndex: will be effectively deleted at dataset closing
|
|
("SELECT DisableSpatialIndex('point_no_spi-but-with-dashes', 'geom')", 1),
|
|
]:
|
|
if expected_result == 0:
|
|
gdal.PushErrorHandler("CPLQuietErrorHandler")
|
|
sql_lyr = gpkg_ds.ExecuteSQL(sql)
|
|
if expected_result == 0:
|
|
gdal.PopErrorHandler()
|
|
feat = sql_lyr.GetNextFeature()
|
|
got_result = feat.GetField(0)
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
assert got_result == expected_result, sql
|
|
|
|
# NULL argument
|
|
sql_lyr = gpkg_ds.ExecuteSQL("SELECT SridFromAuthCRS(NULL, 4326)")
|
|
feat = sql_lyr.GetNextFeature()
|
|
if feat.GetField(0) != -1:
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
# NULL argument
|
|
sql_lyr = gpkg_ds.ExecuteSQL("SELECT SridFromAuthCRS('epsg', NULL)")
|
|
feat = sql_lyr.GetNextFeature()
|
|
if feat.GetField(0) != -1:
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
# Existing entry
|
|
sql_lyr = gpkg_ds.ExecuteSQL("SELECT SridFromAuthCRS('epsg', 4326)")
|
|
feat = sql_lyr.GetNextFeature()
|
|
if feat.GetField(0) != 4326:
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
# Non existing entry
|
|
sql_lyr = gpkg_ds.ExecuteSQL("SELECT SridFromAuthCRS('epsg', 1234)")
|
|
feat = sql_lyr.GetNextFeature()
|
|
if feat.GetField(0) != -1:
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
# NULL argument
|
|
sql_lyr = gpkg_ds.ExecuteSQL("SELECT ImportFromEPSG(NULL)")
|
|
feat = sql_lyr.GetNextFeature()
|
|
if feat.GetField(0) != -1:
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
# Existing entry in gpkg_spatial_ref_sys
|
|
sql_lyr = gpkg_ds.ExecuteSQL("SELECT ImportFromEPSG(4326)")
|
|
feat = sql_lyr.GetNextFeature()
|
|
if feat.GetField(0) != 4326:
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
# New entry in gpkg_spatial_ref_sys
|
|
sql_lyr = gpkg_ds.ExecuteSQL("SELECT ImportFromEPSG(32633)")
|
|
feat = sql_lyr.GetNextFeature()
|
|
if feat.GetField(0) != 32633:
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
# Invalid code
|
|
with gdaltest.error_handler():
|
|
sql_lyr = gpkg_ds.ExecuteSQL("SELECT ImportFromEPSG(0)")
|
|
feat = sql_lyr.GetNextFeature()
|
|
if feat.GetField(0) != -1:
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
# NULL argument
|
|
sql_lyr = gpkg_ds.ExecuteSQL("SELECT ST_Transform(NULL, 4326)")
|
|
feat = sql_lyr.GetNextFeature()
|
|
if feat.GetGeometryRef() is not None:
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
# Invalid geometry
|
|
with gdaltest.error_handler():
|
|
sql_lyr = gpkg_ds.ExecuteSQL("SELECT ST_Transform(x'00', 4326)")
|
|
feat = sql_lyr.GetNextFeature()
|
|
if feat.GetGeometryRef() is not None:
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
# NULL argument
|
|
sql_lyr = gpkg_ds.ExecuteSQL(
|
|
"SELECT ST_Transform(geom, NULL) FROM tbl_linestring_renamed"
|
|
)
|
|
feat = sql_lyr.GetNextFeature()
|
|
if feat.GetGeometryRef() is not None:
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
# Invalid target SRID=0
|
|
# GeoPackage: The record with an srs_id of 0 SHALL be used for undefined geographic coordinate reference systems.
|
|
with gdaltest.error_handler():
|
|
sql_lyr = gpkg_ds.ExecuteSQL(
|
|
"SELECT ST_Transform(geom, 0), ST_SRID(ST_Transform(geom, 0)) FROM tbl_linestring_renamed"
|
|
)
|
|
assert sql_lyr.GetSpatialRef().ExportToWkt().find("Undefined geographic SRS") >= 0
|
|
feat = sql_lyr.GetNextFeature()
|
|
if feat.GetGeometryRef() is None or feat.GetField(0) != 0:
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
# Invalid source SRID=0
|
|
# GeoPackage: The record with an srs_id of 0 SHALL be used for undefined geographic coordinate reference systems.
|
|
# The source is undefined geographic coordinate reference systems (based on WGS84) and the target is WGS84,
|
|
# and the result is an identity transformation that leaves geometry unchanged.
|
|
src_lyr = gpkg_ds.GetLayerByName("point-with-spi-and-dashes")
|
|
assert src_lyr.GetSpatialRef().ExportToWkt().find("Undefined geographic SRS") >= 0
|
|
with gdaltest.error_handler():
|
|
sql_lyr = gpkg_ds.ExecuteSQL(
|
|
'SELECT ST_Transform(geom, 4326), ST_SRID(ST_Transform(geom, 4326)) FROM "point-with-spi-and-dashes"'
|
|
)
|
|
assert sql_lyr.GetSpatialRef().ExportToWkt().find("WGS_1984") >= 0
|
|
feat = sql_lyr.GetNextFeature()
|
|
if feat.GetGeometryRef() is None or feat.GetField(0) != 4326:
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
# Invalid spatialite geometry: SRID=4326,MULTIPOINT EMPTY truncated
|
|
with gdaltest.error_handler():
|
|
sql_lyr = gpkg_ds.ExecuteSQL(
|
|
"SELECT ST_Transform(x'0001E610000000000000000000000000000000000000000000000000000000000000000000007C04000000000000FE', 4326) FROM tbl_linestring_renamed"
|
|
)
|
|
feat = sql_lyr.GetNextFeature()
|
|
if feat.GetGeometryRef() is not None:
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
sql_lyr = gpkg_ds.ExecuteSQL(
|
|
"SELECT ST_Transform(geom, ST_SRID(geom)) FROM tbl_linestring_renamed"
|
|
)
|
|
feat = sql_lyr.GetNextFeature()
|
|
if feat.GetGeometryRef().ExportToWkt() != "LINESTRING (5 5,10 5,10 10,5 10)":
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
sql_lyr = gpkg_ds.ExecuteSQL(
|
|
"SELECT ST_SRID(ST_Transform(geom, 4326)) FROM tbl_linestring_renamed"
|
|
)
|
|
feat = sql_lyr.GetNextFeature()
|
|
if feat.GetField(0) != 4326:
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
# Spatialite geometry: SRID=4326,MULTIPOINT EMPTY
|
|
sql_lyr = gpkg_ds.ExecuteSQL(
|
|
"SELECT ST_SRID(ST_Transform(x'0001E610000000000000000000000000000000000000000000000000000000000000000000007C0400000000000000FE', 4326)) FROM tbl_linestring_renamed"
|
|
)
|
|
feat = sql_lyr.GetNextFeature()
|
|
if feat.GetField(0) != 4326:
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
# Error case: less than 8 bytes
|
|
sql_lyr = gpkg_ds.ExecuteSQL("SELECT ST_MinX(x'00')")
|
|
feat = sql_lyr.GetNextFeature()
|
|
if feat.IsFieldSetAndNotNull(0):
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
feat = None
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
# Error case: 8 wrong bytes
|
|
sql_lyr = gpkg_ds.ExecuteSQL("SELECT ST_MinX(x'0001020304050607')")
|
|
feat = sql_lyr.GetNextFeature()
|
|
if feat.IsFieldSetAndNotNull(0):
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
feat = None
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
# Error case: too short blob
|
|
sql_lyr = gpkg_ds.ExecuteSQL("SELECT ST_GeometryType(x'4750001100000000')")
|
|
feat = sql_lyr.GetNextFeature()
|
|
if feat.IsFieldSetAndNotNull(0):
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
feat = None
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
# Error case: too short blob
|
|
sql_lyr = gpkg_ds.ExecuteSQL("SELECT ST_GeometryType(x'475000110000000001040000')")
|
|
feat = sql_lyr.GetNextFeature()
|
|
if feat.IsFieldSetAndNotNull(0):
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
feat = None
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
# Invalid geometry, but long enough for our purpose...
|
|
sql_lyr = gpkg_ds.ExecuteSQL(
|
|
"SELECT ST_GeometryType(x'47500011000000000104000000')"
|
|
)
|
|
feat = sql_lyr.GetNextFeature()
|
|
if feat.GetField(0) != "MULTIPOINT":
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
feat = None
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
# Spatialite geometry (MULTIPOINT EMPTY)
|
|
sql_lyr = gpkg_ds.ExecuteSQL(
|
|
"SELECT ST_GeometryType(x'00010000000000000000000000000000000000000000000000000000000000000000000000007C0400000000000000FE')"
|
|
)
|
|
feat = sql_lyr.GetNextFeature()
|
|
if feat.GetField(0) != "MULTIPOINT":
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
feat = None
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
# Spatialite geometry (MULTIPOINT EMPTY)
|
|
sql_lyr = gpkg_ds.ExecuteSQL(
|
|
"SELECT ST_IsEmpty(x'00010000000000000000000000000000000000000000000000000000000000000000000000007C0400000000000000FE')"
|
|
)
|
|
feat = sql_lyr.GetNextFeature()
|
|
if feat.GetField(0) != 1:
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
feat = None
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
# Error case: invalid geometry
|
|
with gdaltest.error_handler():
|
|
sql_lyr = gpkg_ds.ExecuteSQL(
|
|
"SELECT ST_GeometryType(x'475000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000')"
|
|
)
|
|
feat = sql_lyr.GetNextFeature()
|
|
if feat.IsFieldSetAndNotNull(0):
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
feat = None
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
# Error case: invalid type
|
|
sql_lyr = gpkg_ds.ExecuteSQL("SELECT GPKG_IsAssignable('POINT', NULL)")
|
|
feat = sql_lyr.GetNextFeature()
|
|
feat = None
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
# Error case: invalid type
|
|
sql_lyr = gpkg_ds.ExecuteSQL("SELECT GPKG_IsAssignable(NULL, 'POINT')")
|
|
feat = sql_lyr.GetNextFeature()
|
|
feat = None
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
# Test hstore_get_value
|
|
sql_lyr = gpkg_ds.ExecuteSQL("SELECT hstore_get_value('a=>b', 'a')")
|
|
feat = sql_lyr.GetNextFeature()
|
|
if feat.GetField(0) != "b":
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
feat = None
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
# Test hstore_get_value
|
|
sql_lyr = gpkg_ds.ExecuteSQL("SELECT hstore_get_value('a=>b', 'x')")
|
|
feat = sql_lyr.GetNextFeature()
|
|
if feat.GetField(0) is not None:
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
feat = None
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
# Error case: invalid type
|
|
sql_lyr = gpkg_ds.ExecuteSQL("SELECT hstore_get_value('a=>b', NULL)")
|
|
feat = sql_lyr.GetNextFeature()
|
|
if feat.GetField(0) is not None:
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
feat = None
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
# Error case: invalid type
|
|
sql_lyr = gpkg_ds.ExecuteSQL("SELECT hstore_get_value(NULL, 'a')")
|
|
feat = sql_lyr.GetNextFeature()
|
|
if feat.GetField(0) is not None:
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
feat = None
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
if (
|
|
ogr.GetGEOSVersionMajor() * 10000
|
|
+ ogr.GetGEOSVersionMinor() * 100
|
|
+ ogr.GetGEOSVersionMicro()
|
|
>= 30800
|
|
):
|
|
sql_lyr = gpkg_ds.ExecuteSQL("SELECT ST_MakeValid(NULL)")
|
|
feat = sql_lyr.GetNextFeature()
|
|
assert feat.GetGeometryRef() is None
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
sql_lyr = gpkg_ds.ExecuteSQL("SELECT ST_MakeValid('invalid')")
|
|
feat = sql_lyr.GetNextFeature()
|
|
assert feat.GetGeometryRef() is None
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
sql_lyr = gpkg_ds.ExecuteSQL(
|
|
"SELECT ST_MakeValid(geom) FROM tbl_linestring_renamed"
|
|
)
|
|
feat = sql_lyr.GetNextFeature()
|
|
if feat.GetGeometryRef().ExportToWkt() != "LINESTRING (5 5,10 5,10 10,5 10)":
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
if _has_spatialite_4_3_or_later(gpkg_ds):
|
|
sql_lyr = gpkg_ds.ExecuteSQL(
|
|
"SELECT ST_Buffer(geom, 1e-10) FROM tbl_linestring_renamed"
|
|
)
|
|
assert sql_lyr.GetGeomType() == ogr.wkbPolygon
|
|
assert sql_lyr.GetSpatialRef().ExportToWkt().find("32631") >= 0
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
|
|
###############################################################################
|
|
# Test SetSRID() function
|
|
|
|
|
|
def test_ogr_gpkg_SetSRID():
|
|
|
|
filename = "/vsimem/test_ogr_gpkg_SetSRID.gpkg"
|
|
ds = ogr.GetDriverByName("GPKG").CreateDataSource(filename)
|
|
lyr = ds.CreateLayer("foo")
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT(0 0)"))
|
|
lyr.CreateFeature(f)
|
|
|
|
for srid in (32631, 0, -1, 12345678):
|
|
sql_lyr = ds.ExecuteSQL("SELECT ST_SRID(SetSRID(geom, %d)) FROM foo" % srid)
|
|
f = sql_lyr.GetNextFeature()
|
|
try:
|
|
assert f.GetField(0) == srid, srid
|
|
finally:
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
|
|
sql_lyr = ds.ExecuteSQL("SELECT ST_SRID(SetSRID(NULL, 32631)) FROM foo")
|
|
f = sql_lyr.GetNextFeature()
|
|
try:
|
|
assert f.GetField(0) is None
|
|
finally:
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
|
|
ds = None
|
|
gdal.Unlink("/vsimem/test_ogr_gpkg_SetSRID.gpkg")
|
|
|
|
|
|
###############################################################################
|
|
# Test ST_EnvIntersects() function
|
|
|
|
|
|
def test_ogr_gpkg_ST_EnvIntersects():
|
|
|
|
filename = "/vsimem/test_ogr_gpkg_ST_EnvIntersects.gpkg"
|
|
ds = ogr.GetDriverByName("GPKG").CreateDataSource(filename)
|
|
lyr = ds.CreateLayer("foo")
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometry(ogr.CreateGeometryFromWkt("LINESTRING(1 2,3 4)"))
|
|
lyr.CreateFeature(f)
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometry(ogr.CreateGeometryFromWkt("LINESTRING(5 6,7 8)"))
|
|
lyr.CreateFeature(f)
|
|
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT ST_EnvIntersects(geom, 0, 0, 0.99, 100),"
|
|
+ " ST_EnvIntersects(geom, 0, 4.01, 100, 100),"
|
|
+ " ST_EnvIntersects(geom, 3.01, 0, 100, 100),"
|
|
+ " ST_EnvIntersects(geom, 0, 0, 100, 1.99),"
|
|
+ " ST_EnvIntersects(geom, 0.99, 1.99, 1.01, 2.01),"
|
|
+ " ST_EnvIntersects(geom, 0.99, 3.99, 1.01, 4.01),"
|
|
+ " ST_EnvIntersects(geom, 2.99, 3.99, 3.01, 4.01),"
|
|
+ " ST_EnvIntersects(geom, 2.99, 1.99, 3.01, 2.01)"
|
|
+ " FROM foo WHERE fid = 1"
|
|
)
|
|
f = sql_lyr.GetNextFeature()
|
|
try:
|
|
assert f.GetField(0) == 0
|
|
assert f.GetField(1) == 0
|
|
assert f.GetField(2) == 0
|
|
assert f.GetField(3) == 0
|
|
assert f.GetField(4) == 1
|
|
assert f.GetField(5) == 1
|
|
assert f.GetField(6) == 1
|
|
assert f.GetField(7) == 1
|
|
finally:
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT ST_EnvIntersects(a.geom, b.geom) FROM foo a, foo b WHERE a.fid = 1 AND b.fid = 1"
|
|
)
|
|
f = sql_lyr.GetNextFeature()
|
|
try:
|
|
assert f.GetField(0) == 1
|
|
finally:
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT ST_EnvIntersects(a.geom, b.geom) FROM foo a, foo b WHERE a.fid = 1 AND b.fid = 2"
|
|
)
|
|
f = sql_lyr.GetNextFeature()
|
|
try:
|
|
assert f.GetField(0) == 0
|
|
finally:
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT ST_EnvIntersects(a.geom, b.geom) FROM foo a, foo b WHERE a.fid = 2 AND b.fid = 1"
|
|
)
|
|
f = sql_lyr.GetNextFeature()
|
|
try:
|
|
assert f.GetField(0) == 0
|
|
finally:
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
|
|
ds = None
|
|
gdal.Unlink("/vsimem/test_ogr_gpkg_ST_EnvIntersects.gpkg")
|
|
|
|
|
|
###############################################################################
|
|
# Test unknown extensions
|
|
|
|
|
|
def test_ogr_gpkg_16():
|
|
|
|
ds = gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpk_16.gpkg")
|
|
ds.CreateLayer("foo")
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_extensions ( table_name, column_name, "
|
|
+ "extension_name, definition, scope ) VALUES ( 'foo', 'geom', 'myext', 'some ext', 'write-only' ) "
|
|
)
|
|
ds = None
|
|
|
|
# No warning since we open as read-only
|
|
ds = ogr.Open("/vsimem/ogr_gpk_16.gpkg")
|
|
lyr = ds.GetLayer(0)
|
|
lyr.GetLayerDefn()
|
|
gdal.ErrorReset()
|
|
ds = None
|
|
assert gdal.GetLastErrorMsg() == "", "fail : warning NOT expected"
|
|
|
|
# Warning since we open as read-write
|
|
ds = ogr.Open("/vsimem/ogr_gpk_16.gpkg", update=1)
|
|
lyr = ds.GetLayer(0)
|
|
gdal.ErrorReset()
|
|
with gdaltest.error_handler():
|
|
lyr.GetLayerDefn()
|
|
assert gdal.GetLastErrorMsg() != "", "fail : warning expected"
|
|
|
|
ds.ExecuteSQL(
|
|
"UPDATE gpkg_extensions SET scope = 'read-write' WHERE extension_name = 'myext'"
|
|
)
|
|
ds = None
|
|
|
|
# Warning since we open as read-only
|
|
ds = ogr.Open("/vsimem/ogr_gpk_16.gpkg")
|
|
lyr = ds.GetLayer(0)
|
|
gdal.ErrorReset()
|
|
with gdaltest.error_handler():
|
|
lyr.GetLayerDefn()
|
|
assert gdal.GetLastErrorMsg() != "", "fail : warning expected"
|
|
|
|
# and also as read-write
|
|
ds = ogr.Open("/vsimem/ogr_gpk_16.gpkg", update=1)
|
|
lyr = ds.GetLayer(0)
|
|
gdal.ErrorReset()
|
|
with gdaltest.error_handler():
|
|
lyr.GetLayerDefn()
|
|
assert gdal.GetLastErrorMsg() != "", "fail : warning expected"
|
|
ds = None
|
|
|
|
gdal.Unlink("/vsimem/ogr_gpk_16.gpkg")
|
|
|
|
# Test with unsupported geometry type
|
|
ds = gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpk_16.gpkg")
|
|
ds.CreateLayer("foo")
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_extensions ( table_name, column_name, "
|
|
+ "extension_name, definition, scope ) VALUES ( 'foo', 'geom', 'gpkg_geom_XXXX', 'some ext', 'read-write' ) "
|
|
)
|
|
ds = None
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpk_16.gpkg")
|
|
lyr = ds.GetLayer(0)
|
|
gdal.ErrorReset()
|
|
with gdaltest.error_handler():
|
|
lyr.GetLayerDefn()
|
|
assert gdal.GetLastErrorMsg() != "", "fail : warning expected"
|
|
|
|
gdal.Unlink("/vsimem/ogr_gpk_16.gpkg")
|
|
|
|
# Test with database wide unknown extension
|
|
ds = gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpk_16.gpkg")
|
|
ds.CreateLayer("foo")
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_extensions ( "
|
|
+ "extension_name, definition, scope ) VALUES ( 'myext', 'some ext', 'write-only' ) "
|
|
)
|
|
ds = None
|
|
|
|
# No warning since we open as read-only
|
|
ds = ogr.Open("/vsimem/ogr_gpk_16.gpkg")
|
|
lyr = ds.GetLayer(0)
|
|
gdal.ErrorReset()
|
|
lyr.GetLayerDefn()
|
|
assert gdal.GetLastErrorMsg() == "", "fail : warning NOT expected"
|
|
|
|
# Warning since we open as read-write
|
|
gdal.ErrorReset()
|
|
with gdaltest.error_handler():
|
|
ds = ogr.Open("/vsimem/ogr_gpk_16.gpkg", update=1)
|
|
assert gdal.GetLastErrorMsg() != "", "fail : warning expected"
|
|
|
|
ds.ExecuteSQL(
|
|
"UPDATE gpkg_extensions SET scope = 'read-write' WHERE extension_name = 'myext'"
|
|
)
|
|
ds = None
|
|
|
|
# Warning since we open as read-only
|
|
gdal.ErrorReset()
|
|
with gdaltest.error_handler():
|
|
ds = ogr.Open("/vsimem/ogr_gpk_16.gpkg")
|
|
assert gdal.GetLastErrorMsg() != "", "fail : warning expected"
|
|
|
|
# and also as read-write
|
|
gdal.ErrorReset()
|
|
with gdaltest.error_handler():
|
|
ds = ogr.Open("/vsimem/ogr_gpk_16.gpkg", update=1)
|
|
assert gdal.GetLastErrorMsg() != "", "fail : warning expected"
|
|
ds = None
|
|
|
|
gdal.Unlink("/vsimem/ogr_gpk_16.gpkg")
|
|
|
|
|
|
###############################################################################
|
|
# Run INDIRECT_SQLITE dialect
|
|
|
|
|
|
def test_ogr_gpkg_17():
|
|
|
|
ds = gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpkg_17.gpkg")
|
|
sql_lyr = ds.ExecuteSQL("SELECT ogr_version()", dialect="INDIRECT_SQLITE")
|
|
f = sql_lyr.GetNextFeature()
|
|
assert f is not None
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
ds = None
|
|
|
|
gdal.Unlink("/vsimem/ogr_gpkg_17.gpkg")
|
|
|
|
|
|
###############################################################################
|
|
# Test geometry type extension
|
|
|
|
|
|
def test_ogr_gpkg_18():
|
|
|
|
ds = gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpkg_18.gpkg")
|
|
lyr = ds.CreateLayer("wkbCircularString", geom_type=ogr.wkbCircularString)
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometry(ogr.CreateGeometryFromWkt("CIRCULARSTRING(0 0,1 0,0 0)"))
|
|
lyr.CreateFeature(f)
|
|
f = None
|
|
ds = None
|
|
|
|
assert validate("/vsimem/ogr_gpkg_18.gpkg"), "validation failed"
|
|
|
|
gdal.ErrorReset()
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_18.gpkg")
|
|
assert gdal.GetLastErrorMsg() == "", "fail : warning NOT expected"
|
|
|
|
lyr = ds.GetLayer(0)
|
|
assert lyr.GetGeomType() == ogr.wkbCircularString
|
|
f = lyr.GetNextFeature()
|
|
g = f.GetGeometryRef()
|
|
assert g.GetGeometryType() == ogr.wkbCircularString
|
|
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT * FROM gpkg_extensions WHERE table_name = 'wkbCircularString' AND extension_name = 'gpkg_geom_CIRCULARSTRING'"
|
|
)
|
|
assert sql_lyr.GetFeatureCount() == 1
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
ds = None
|
|
|
|
gdal.Unlink("/vsimem/ogr_gpkg_18.gpkg")
|
|
|
|
# Also test with a wkbUnknown layer and add curve geometries afterwards
|
|
ds = gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpkg_18.gpkg")
|
|
lyr = ds.CreateLayer("test")
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometry(ogr.CreateGeometryFromWkt("CIRCULARSTRING(0 0,1 0,0 0)"))
|
|
lyr.CreateFeature(f)
|
|
f = None
|
|
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT * FROM gpkg_extensions WHERE table_name = 'test' AND extension_name = 'gpkg_geom_CIRCULARSTRING'"
|
|
)
|
|
assert sql_lyr.GetFeatureCount() == 1
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
|
|
ds = None
|
|
|
|
gdal.ErrorReset()
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_18.gpkg")
|
|
assert gdal.GetLastErrorMsg() == "", "fail : warning NOT expected"
|
|
|
|
lyr = ds.GetLayer(0)
|
|
f = lyr.GetNextFeature()
|
|
g = f.GetGeometryRef()
|
|
assert g.GetGeometryType() == ogr.wkbCircularString
|
|
|
|
ds = None
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_18.gpkg", update=1)
|
|
lyr = ds.GetLayer(0)
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometry(ogr.CreateGeometryFromWkt("CIRCULARSTRING(0 0,1 0,0 0)"))
|
|
gdal.ErrorReset()
|
|
ret = lyr.CreateFeature(f)
|
|
assert ret == 0 and gdal.GetLastErrorMsg() == ""
|
|
f = None
|
|
ds = None
|
|
|
|
ds = gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpkg_18.gpkg")
|
|
lyr = ds.CreateLayer("test", geom_type=ogr.wkbTriangle)
|
|
with gdaltest.error_handler():
|
|
# Warning 1: Registering non-standard gpkg_geom_TRIANGLE extension
|
|
ds.FlushCache()
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT * FROM gpkg_extensions WHERE table_name = 'test' AND extension_name = 'gpkg_geom_TRIANGLE'"
|
|
)
|
|
assert sql_lyr.GetFeatureCount() == 1
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
ds = None
|
|
|
|
if has_validate():
|
|
ret = validate("/vsimem/ogr_gpkg_18.gpkg", quiet=True)
|
|
assert not ret, "validation unexpectedly succeeded"
|
|
|
|
# Test non-linear geometry in GeometryCollection
|
|
ds = gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpkg_18.gpkg")
|
|
lyr = ds.CreateLayer("test")
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometry(
|
|
ogr.CreateGeometryFromWkt("GEOMETRYCOLLECTION(CIRCULARSTRING(0 0,1 0,0 0))")
|
|
)
|
|
lyr.CreateFeature(f)
|
|
f = None
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT * FROM gpkg_extensions WHERE table_name = 'test' AND extension_name LIKE 'gpkg_geom_%'"
|
|
)
|
|
assert sql_lyr.GetFeatureCount() == 1
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
ds = None
|
|
|
|
gdal.Unlink("/vsimem/ogr_gpkg_18.gpkg")
|
|
|
|
|
|
###############################################################################
|
|
# Test metadata
|
|
|
|
|
|
def test_ogr_gpkg_19():
|
|
|
|
ds = gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpkg_19.gpkg")
|
|
assert not ds.GetMetadata()
|
|
lyr = ds.CreateLayer("test_without_md")
|
|
assert not lyr.GetMetadata()
|
|
|
|
ds.SetMetadataItem("foo", "bar")
|
|
|
|
# GEOPACKAGE metadata domain is not allowed in a non-raster context
|
|
with gdaltest.error_handler():
|
|
ds.SetMetadata(ds.GetMetadata("GEOPACKAGE"), "GEOPACKAGE")
|
|
ds.SetMetadataItem("foo", ds.GetMetadataItem("foo", "GEOPACKAGE"), "GEOPACKAGE")
|
|
|
|
ds = None
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_19.gpkg")
|
|
assert ds.GetMetadataDomainList() == [""]
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_19.gpkg")
|
|
assert len(ds.GetMetadata()) == 1
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_19.gpkg")
|
|
assert ds.GetMetadataItem("foo") == "bar", ds.GetMetadata()
|
|
ds = None
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_19.gpkg", update=1)
|
|
lyr = ds.CreateLayer(
|
|
"test_with_md", options=["IDENTIFIER=ident", "DESCRIPTION=desc"]
|
|
)
|
|
lyr.SetMetadataItem("IDENTIFIER", "ignored_because_of_lco")
|
|
lyr.SetMetadataItem("DESCRIPTION", "ignored_because_of_lco")
|
|
lyr.SetMetadata(
|
|
{
|
|
"IDENTIFIER": "ignored_because_of_lco",
|
|
"DESCRIPTION": "ignored_because_of_lco",
|
|
}
|
|
)
|
|
ds = None
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_19.gpkg")
|
|
|
|
# Check that we don't create triggers
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT * FROM sqlite_master WHERE type = 'trigger' AND tbl_name IN ('gpkg_metadata', 'gpkg_metadata_reference')"
|
|
)
|
|
assert sql_lyr.GetFeatureCount() == 0
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
|
|
lyr = ds.GetLayer("test_with_md")
|
|
assert lyr.GetMetadataItem("IDENTIFIER") == "ident"
|
|
assert lyr.GetMetadataItem("DESCRIPTION") == "desc"
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_19.gpkg", update=1)
|
|
lyr = ds.GetLayer("test_with_md")
|
|
assert lyr.GetMetadata() == {"IDENTIFIER": "ident", "DESCRIPTION": "desc"}
|
|
lyr.SetMetadataItem("IDENTIFIER", "another_ident")
|
|
lyr.SetMetadataItem("DESCRIPTION", "another_desc")
|
|
ds = None
|
|
|
|
# FIXME? Is it expected to have a .aux.xml here ?
|
|
gdal.Unlink("/vsimem/ogr_gpkg_19.gpkg.aux.xml")
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_19.gpkg", update=1)
|
|
lyr = ds.GetLayer("test_with_md")
|
|
assert lyr.GetMetadata() == {
|
|
"IDENTIFIER": "another_ident",
|
|
"DESCRIPTION": "another_desc",
|
|
}
|
|
lyr.SetMetadataItem("foo", "bar")
|
|
lyr.SetMetadataItem("bar", "baz", "another_domain")
|
|
ds = None
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_19.gpkg", update=1)
|
|
lyr = ds.GetLayer("test_with_md")
|
|
assert lyr.GetMetadataDomainList() == ["", "another_domain"]
|
|
ds = None
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_19.gpkg", update=1)
|
|
lyr = ds.GetLayer("test_with_md")
|
|
assert lyr.GetMetadata() == {
|
|
"IDENTIFIER": "another_ident",
|
|
"foo": "bar",
|
|
"DESCRIPTION": "another_desc",
|
|
}
|
|
assert lyr.GetMetadata("another_domain") == {"bar": "baz"}
|
|
lyr.SetMetadata(None)
|
|
lyr.SetMetadata(None, "another_domain")
|
|
ds = None
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_19.gpkg", update=1)
|
|
lyr = ds.GetLayer("test_with_md")
|
|
assert lyr.GetMetadata() == {
|
|
"IDENTIFIER": "another_ident",
|
|
"DESCRIPTION": "another_desc",
|
|
}
|
|
assert lyr.GetMetadataDomainList() == [""]
|
|
ds = None
|
|
|
|
assert validate("/vsimem/ogr_gpkg_19.gpkg"), "validation failed"
|
|
|
|
gdal.Unlink("/vsimem/ogr_gpkg_19.gpkg")
|
|
gdal.Unlink("/vsimem/ogr_gpkg_19.gpkg.aux.xml")
|
|
|
|
|
|
###############################################################################
|
|
# Test spatial reference system
|
|
|
|
|
|
def test_ogr_gpkg_20():
|
|
|
|
ds = gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpkg_20.gpkg")
|
|
|
|
# "Conflict" with EPSG:4326
|
|
srs = osr.SpatialReference()
|
|
srs.SetFromUserInput(
|
|
"""GEOGCS["my geogcs",
|
|
DATUM["my datum",
|
|
SPHEROID["my spheroid",1000,0]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433],
|
|
AUTHORITY["my_org","4326"]]"""
|
|
)
|
|
lyr = ds.CreateLayer("my_org_4326", srs=srs)
|
|
|
|
# No authority node
|
|
srs = osr.SpatialReference()
|
|
srs.SetFromUserInput(
|
|
"""GEOGCS["another geogcs",
|
|
DATUM["another datum",
|
|
SPHEROID["another spheroid",1000,0]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433]]"""
|
|
)
|
|
lyr = ds.CreateLayer("without_org", srs=srs)
|
|
|
|
ds = None
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_20.gpkg")
|
|
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT * FROM gpkg_spatial_ref_sys WHERE srs_name='my geogcs' AND srs_id = 100000 AND organization='MY_ORG' AND organization_coordsys_id=4326 AND description is NULL"
|
|
)
|
|
fc = sql_lyr.GetFeatureCount()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
assert fc == 1
|
|
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT * FROM gpkg_spatial_ref_sys WHERE srs_name='another geogcs' AND srs_id = 100001 AND organization='NONE' AND organization_coordsys_id=100001 AND description is NULL"
|
|
)
|
|
fc = sql_lyr.GetFeatureCount()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
assert fc == 1
|
|
|
|
lyr = ds.GetLayer("my_org_4326")
|
|
assert lyr.GetSpatialRef().ExportToWkt().find("my geogcs") >= 0
|
|
lyr = ds.GetLayer("without_org")
|
|
assert lyr.GetSpatialRef().ExportToWkt().find("another geogcs") >= 0
|
|
ds = None
|
|
|
|
assert validate("/vsimem/ogr_gpkg_20.gpkg"), "validation failed"
|
|
|
|
gdal.Unlink("/vsimem/ogr_gpkg_20.gpkg")
|
|
|
|
ds = gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpkg_20.gpkg")
|
|
srs = osr.SpatialReference()
|
|
srs.ImportFromEPSG(4326)
|
|
lyr = ds.CreateLayer("foo4326", srs=srs)
|
|
ds.ExecuteSQL(
|
|
"UPDATE gpkg_spatial_ref_sys SET definition='invalid', "
|
|
"organization='', organization_coordsys_id = 0 "
|
|
"WHERE srs_id = 4326"
|
|
)
|
|
ds = None
|
|
|
|
# Unable to parse srs_id '4326' well-known text 'invalid'
|
|
with gdaltest.error_handler():
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_20.gpkg", update=1)
|
|
|
|
ds.ExecuteSQL("DELETE FROM gpkg_spatial_ref_sys WHERE srs_id = 4326")
|
|
ds = None
|
|
with gdal.config_option(
|
|
"OGR_GPKG_FOREIGN_KEY_CHECK", "NO"
|
|
), gdaltest.error_handler():
|
|
# Warning 1: unable to read srs_id '4326' from gpkg_spatial_ref_sys
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_20.gpkg", update=1)
|
|
ds = None
|
|
|
|
gdal.Unlink("/vsimem/ogr_gpkg_20.gpkg")
|
|
|
|
ds = gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpkg_20.gpkg")
|
|
srs = osr.SpatialReference()
|
|
srs.ImportFromEPSG(4326)
|
|
lyr = ds.CreateLayer("foo4326", srs=srs)
|
|
|
|
ds.ExecuteSQL("DROP TABLE gpkg_spatial_ref_sys")
|
|
ds.ExecuteSQL(
|
|
"CREATE TABLE gpkg_spatial_ref_sys (srs_name TEXT, "
|
|
"srs_id INTEGER, organization TEXT, "
|
|
"organization_coordsys_id INTEGER, definition TEXT)"
|
|
)
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_spatial_ref_sys "
|
|
"(srs_name,srs_id,organization,organization_coordsys_id,"
|
|
"definition) VALUES (NULL,4326,NULL,NULL,NULL)"
|
|
)
|
|
ds = None
|
|
|
|
# Warning 1: null definition for srs_id '4326' in gpkg_spatial_ref_sys
|
|
with gdal.config_option(
|
|
"OGR_GPKG_FOREIGN_KEY_CHECK", "NO"
|
|
), gdaltest.error_handler():
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_20.gpkg", update=1)
|
|
ds = None
|
|
|
|
gdal.Unlink("/vsimem/ogr_gpkg_20.gpkg")
|
|
|
|
|
|
def test_ogr_gpkg_srs_non_duplication_custom_crs():
|
|
|
|
ds = gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpkg_20.gpkg")
|
|
srs = osr.SpatialReference()
|
|
srs.SetFromUserInput(
|
|
"""GEOGCS["my custom geogcs",
|
|
DATUM["my datum",
|
|
SPHEROID["my spheroid",1000,0]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433]]"""
|
|
)
|
|
lyr = ds.CreateLayer("test", srs=srs)
|
|
assert lyr
|
|
lyr = ds.CreateLayer("test2", srs=srs)
|
|
assert lyr
|
|
|
|
sql_lyr = ds.ExecuteSQL("SELECT * FROM gpkg_spatial_ref_sys")
|
|
fc = sql_lyr.GetFeatureCount()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
assert fc == 4 # srs_id 0, 1, 4326 + custom one
|
|
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT * FROM gpkg_spatial_ref_sys WHERE srs_name='my custom geogcs'"
|
|
)
|
|
assert sql_lyr.GetFeatureCount() == 1
|
|
f = sql_lyr.GetNextFeature()
|
|
assert f["srs_id"] == 100000
|
|
assert f["organization"] == "NONE"
|
|
assert f["organization_coordsys_id"] == 100000
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
|
|
# Test now transitionning to definition_12_063 / WKT2 database structure...
|
|
srs_3d = osr.SpatialReference()
|
|
srs_3d.SetFromUserInput(
|
|
"""GEOGCRS["srs 3d",
|
|
DATUM["some datum",
|
|
ELLIPSOID["some ellipsoid",6378137,298.257223563,
|
|
LENGTHUNIT["metre",1]]],
|
|
PRIMEM["Greenwich",0,
|
|
ANGLEUNIT["degree",0.0174532925199433]],
|
|
CS[ellipsoidal,3],
|
|
AXIS["geodetic latitude (Lat)",north,
|
|
ORDER[1],
|
|
ANGLEUNIT["degree",0.0174532925199433]],
|
|
AXIS["geodetic longitude (Lon)",east,
|
|
ORDER[2],
|
|
ANGLEUNIT["degree",0.0174532925199433]],
|
|
AXIS["ellipsoidal height (h)",up,
|
|
ORDER[3],
|
|
LENGTHUNIT["metre",1]]]"""
|
|
)
|
|
lyr = ds.CreateLayer("test_3d", srs=srs_3d)
|
|
assert lyr
|
|
lyr = ds.CreateLayer("test_3d_bis", srs=srs_3d)
|
|
assert lyr
|
|
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT * FROM gpkg_spatial_ref_sys WHERE srs_name='srs 3d'"
|
|
)
|
|
assert sql_lyr.GetFeatureCount() == 1
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
|
|
# Test again with SRS that can be represented in WKT1
|
|
lyr = ds.CreateLayer("test3", srs=srs)
|
|
assert lyr
|
|
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT * FROM gpkg_spatial_ref_sys WHERE srs_name='my custom geogcs'"
|
|
)
|
|
assert sql_lyr.GetFeatureCount() == 1
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
|
|
ds = None
|
|
gdal.Unlink("/vsimem/ogr_gpkg_20.gpkg")
|
|
|
|
|
|
def test_ogr_gpkg_srs_non_consistent_with_official_definition():
|
|
|
|
ds = gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpkg_20.gpkg")
|
|
test_fake_4267 = osr.SpatialReference()
|
|
test_fake_4267.SetFromUserInput(
|
|
"""GEOGCS["my geogcs 4267",
|
|
DATUM["WGS_1984",
|
|
SPHEROID["my spheroid",1000,0]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433],
|
|
AUTHORITY["EPSG","4267"]]"""
|
|
)
|
|
gdal.ErrorReset()
|
|
with gdaltest.error_handler():
|
|
lyr = ds.CreateLayer("test_fake_4267", srs=test_fake_4267)
|
|
assert (
|
|
gdal.GetLastErrorMsg()
|
|
== "Passed SRS uses EPSG:4267 identification, but its definition is not compatible with the official definition of the object. Registering it as a non-EPSG entry into the database."
|
|
)
|
|
assert lyr
|
|
|
|
# EPSG:4326 already in the database
|
|
test_fake_4326 = osr.SpatialReference()
|
|
test_fake_4326.SetFromUserInput(
|
|
"""GEOGCS["my geogcs 4326",
|
|
DATUM["WGS_1984",
|
|
SPHEROID["my spheroid",1000,0]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433],
|
|
AUTHORITY["EPSG","4326"]]"""
|
|
)
|
|
gdal.ErrorReset()
|
|
with gdaltest.error_handler():
|
|
lyr = ds.CreateLayer("test_fake_4326", srs=test_fake_4326)
|
|
assert (
|
|
gdal.GetLastErrorMsg()
|
|
== "Passed SRS uses EPSG:4326 identification, but its definition is not compatible with the definition of that object already in the database. Registering it as a new entry into the database."
|
|
)
|
|
assert lyr
|
|
|
|
ds = None
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_20.gpkg", update=1)
|
|
lyr = ds.GetLayer("test_fake_4267")
|
|
assert (
|
|
lyr.GetSpatialRef().ExportToWkt().replace(',AUTHORITY["EPSG","9122"]', "")
|
|
== 'GEOGCS["my geogcs 4267",DATUM["WGS_1984",SPHEROID["my spheroid",1000,0]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433],AXIS["Latitude",NORTH],AXIS["Longitude",EAST],AUTHORITY["EPSG","4267"]]'
|
|
)
|
|
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT * FROM gpkg_spatial_ref_sys WHERE srs_name='my geogcs 4267'"
|
|
)
|
|
assert sql_lyr.GetFeatureCount() == 1
|
|
f = sql_lyr.GetNextFeature()
|
|
assert f["srs_id"] == 100000
|
|
assert f["organization"] == "NONE"
|
|
assert f["organization_coordsys_id"] == 100000
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
|
|
lyr = ds.GetLayer("test_fake_4326")
|
|
assert (
|
|
lyr.GetSpatialRef().ExportToWkt().replace(',AUTHORITY["EPSG","9122"]', "")
|
|
== 'GEOGCS["my geogcs 4326",DATUM["WGS_1984",SPHEROID["my spheroid",1000,0]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433],AXIS["Latitude",NORTH],AXIS["Longitude",EAST],AUTHORITY["EPSG","4326"]]'
|
|
)
|
|
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT * FROM gpkg_spatial_ref_sys WHERE srs_name='my geogcs 4326'"
|
|
)
|
|
assert sql_lyr.GetFeatureCount() == 1
|
|
f = sql_lyr.GetNextFeature()
|
|
assert f["srs_id"] == 100001
|
|
assert f["organization"] == "NONE"
|
|
assert f["organization_coordsys_id"] == 100001
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
|
|
sql_lyr = ds.ExecuteSQL("SELECT * FROM gpkg_spatial_ref_sys")
|
|
fc_before = sql_lyr.GetFeatureCount()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
|
|
gdal.ErrorReset()
|
|
gdal.ErrorReset()
|
|
lyr = ds.CreateLayer("test_fake_4267_bis", srs=test_fake_4267)
|
|
assert gdal.GetLastErrorMsg() == ""
|
|
|
|
sql_lyr = ds.ExecuteSQL("SELECT * FROM gpkg_spatial_ref_sys")
|
|
fc_after = sql_lyr.GetFeatureCount()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
|
|
assert fc_before == fc_after
|
|
ds = None
|
|
|
|
gdal.Unlink("/vsimem/ogr_gpkg_20.gpkg")
|
|
|
|
|
|
def test_ogr_gpkg_write_srs_undefined_geographic():
|
|
|
|
gdal.Unlink("tmp/ogr_gpkg_srs_undefined_geographic.gpkg")
|
|
|
|
gpkg_ds = gdaltest.gpkg_dr.CreateDataSource(
|
|
"tmp/ogr_gpkg_srs_undefined_geographic.gpkg"
|
|
)
|
|
assert gpkg_ds is not None
|
|
|
|
# Check initial default SRS entries in gpkg_spatial_ref_sys
|
|
sql_lyr = gpkg_ds.ExecuteSQL("SELECT COUNT(*) FROM gpkg_spatial_ref_sys")
|
|
gpkg_spatial_ref_sys_total = sql_lyr.GetNextFeature().GetField(0)
|
|
assert gpkg_spatial_ref_sys_total == 3 # entries with SRS IDs: -1, 0, 4326
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
srs = osr.SpatialReference()
|
|
srs.SetFromUserInput(
|
|
'GEOGCS["Undefined geographic SRS",DATUM["unknown",SPHEROID["unknown",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AXIS["Latitude",NORTH],AXIS["Longitude",EAST]]'
|
|
)
|
|
lyr = gpkg_ds.CreateLayer(
|
|
"srs_test_geographic_layer", geom_type=ogr.wkbPoint, srs=srs
|
|
)
|
|
srs_wkt = lyr.GetSpatialRef().ExportToWkt()
|
|
assert srs_wkt.find("Undefined geographic SRS") >= 0, srs_wkt
|
|
assert lyr.GetSpatialRef().IsGeographic()
|
|
|
|
gpkg_ds = None
|
|
gpkg_ds = ogr.Open("tmp/ogr_gpkg_srs_undefined_geographic.gpkg")
|
|
|
|
# Check no new SRS entries have been inserted into gpkg_spatial_ref_sys
|
|
sql_lyr = gpkg_ds.ExecuteSQL("SELECT COUNT(*) FROM gpkg_spatial_ref_sys")
|
|
assert gpkg_spatial_ref_sys_total == sql_lyr.GetNextFeature().GetField(0)
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
lyr = gpkg_ds.GetLayer(0)
|
|
srs_wkt = lyr.GetSpatialRef().ExportToWkt()
|
|
assert srs_wkt.find("Undefined geographic SRS") >= 0, srs_wkt
|
|
assert lyr.GetSpatialRef().IsGeographic()
|
|
|
|
gpkg_ds = None
|
|
gdal.Unlink("tmp/ogr_gpkg_srs_undefined_geographic.gpkg")
|
|
|
|
|
|
def test_ogr_gpkg_write_srs_undefined_Cartesian():
|
|
|
|
gdal.Unlink("tmp/ogr_gpkg_srs_Cartesian.gpkg")
|
|
|
|
gpkg_ds = gdaltest.gpkg_dr.CreateDataSource("tmp/ogr_gpkg_srs_Cartesian.gpkg")
|
|
assert gpkg_ds is not None
|
|
|
|
# Check initial default SRS entries in gpkg_spatial_ref_sys
|
|
sql_lyr = gpkg_ds.ExecuteSQL("SELECT COUNT(*) FROM gpkg_spatial_ref_sys")
|
|
gpkg_spatial_ref_sys_total = sql_lyr.GetNextFeature().GetField(0)
|
|
assert gpkg_spatial_ref_sys_total == 3 # SRS with IDs: -1, 0, 4326
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
srs = osr.SpatialReference()
|
|
srs.SetFromUserInput('LOCAL_CS["Undefined Cartesian SRS"]')
|
|
lyr = gpkg_ds.CreateLayer(
|
|
"srs_test_Cartesian_layer", geom_type=ogr.wkbPoint, srs=srs
|
|
)
|
|
srs_wkt = lyr.GetSpatialRef().ExportToWkt()
|
|
assert srs_wkt.find("Undefined Cartesian SRS") >= 0
|
|
assert lyr.GetSpatialRef().IsLocal()
|
|
|
|
gpkg_ds = None
|
|
gpkg_ds = ogr.Open("tmp/ogr_gpkg_srs_Cartesian.gpkg")
|
|
|
|
# Check no new SRS entries have been inserted into gpkg_spatial_ref_sys
|
|
sql_lyr = gpkg_ds.ExecuteSQL("SELECT COUNT(*) FROM gpkg_spatial_ref_sys")
|
|
assert gpkg_spatial_ref_sys_total == sql_lyr.GetNextFeature().GetField(0)
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
lyr = gpkg_ds.GetLayer(0)
|
|
srs_wkt = lyr.GetSpatialRef().ExportToWkt()
|
|
assert srs_wkt.find("Undefined Cartesian SRS") >= 0, srs_wkt
|
|
assert lyr.GetSpatialRef().IsLocal()
|
|
|
|
gpkg_ds = None
|
|
gdal.Unlink("tmp/ogr_gpkg_srs_Cartesian.gpkg")
|
|
|
|
|
|
###############################################################################
|
|
# Test maximum width of text fields
|
|
|
|
|
|
def test_ogr_gpkg_21():
|
|
|
|
ds = gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpkg_21.gpkg")
|
|
lyr = ds.CreateLayer("test")
|
|
field_defn = ogr.FieldDefn("str", ogr.OFTString)
|
|
field_defn.SetWidth(2)
|
|
lyr.CreateField(field_defn)
|
|
ds = None
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_21.gpkg", update=1)
|
|
lyr = ds.GetLayer(0)
|
|
assert lyr.GetLayerDefn().GetFieldDefn(0).GetWidth() == 2
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetField(0, "ab")
|
|
gdal.ErrorReset()
|
|
lyr.CreateFeature(f)
|
|
assert gdal.GetLastErrorMsg() == ""
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetFieldBinaryFromHexString(0, "41E9")
|
|
gdal.ErrorReset()
|
|
with gdaltest.error_handler():
|
|
lyr.CreateFeature(f)
|
|
assert gdal.GetLastErrorMsg() != ""
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetField(0, "abc")
|
|
gdal.ErrorReset()
|
|
with gdaltest.error_handler():
|
|
lyr.CreateFeature(f)
|
|
assert gdal.GetLastErrorMsg() != ""
|
|
|
|
f = lyr.GetFeature(f.GetFID())
|
|
assert f.GetField(0) == "abc"
|
|
|
|
gdal.Unlink("/vsimem/ogr_gpkg_21.gpkg")
|
|
|
|
ds = gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpkg_21.gpkg")
|
|
lyr = ds.CreateLayer("test", options=["TRUNCATE_FIELDS=YES"])
|
|
field_defn = ogr.FieldDefn("str", ogr.OFTString)
|
|
field_defn.SetWidth(2)
|
|
lyr.CreateField(field_defn)
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetFieldBinaryFromHexString(0, "41E9")
|
|
gdal.ErrorReset()
|
|
with gdaltest.error_handler():
|
|
lyr.CreateFeature(f)
|
|
assert gdal.GetLastErrorMsg() != ""
|
|
|
|
f = lyr.GetFeature(f.GetFID())
|
|
assert f.GetField(0) == "A_"
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetField(0, "abc")
|
|
gdal.ErrorReset()
|
|
with gdaltest.error_handler():
|
|
lyr.CreateFeature(f)
|
|
assert gdal.GetLastErrorMsg() != ""
|
|
|
|
f = lyr.GetFeature(f.GetFID())
|
|
assert f.GetField(0) == "ab"
|
|
|
|
gdal.Unlink("/vsimem/ogr_gpkg_21.gpkg")
|
|
|
|
|
|
###############################################################################
|
|
|
|
|
|
def test_ogr_gpkg_table_in_gpkg_content_but_missing():
|
|
|
|
filename = "/vsimem/test_ogr_gpkg_table_in_gpkg_content_but_missing.gpkg"
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(filename)
|
|
ds.CreateLayer("valid")
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_contents VALUES('non_existent','attributes','non_existent','','2022-05-18T17:24:49.837Z',NULL,NULL,NULL,NULL,-1);"
|
|
)
|
|
ds = None
|
|
gdal.ErrorReset()
|
|
with gdaltest.error_handler():
|
|
ds = ogr.Open(filename)
|
|
assert "non_existent" in gdal.GetLastErrorMsg()
|
|
assert ds.GetLayerCount() == 1
|
|
|
|
gdal.Unlink(filename)
|
|
ds = None
|
|
|
|
|
|
###############################################################################
|
|
# Test FID64 support
|
|
|
|
|
|
def test_ogr_gpkg_22():
|
|
|
|
ds = gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpkg_22.gpkg")
|
|
lyr = ds.CreateLayer("test")
|
|
field_defn = ogr.FieldDefn("foo", ogr.OFTString)
|
|
lyr.CreateField(field_defn)
|
|
|
|
feat = ogr.Feature(lyr.GetLayerDefn())
|
|
feat.SetField("foo", "bar")
|
|
feat.SetFID(1234567890123)
|
|
lyr.CreateFeature(feat)
|
|
feat = None
|
|
|
|
ds = None
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_22.gpkg")
|
|
lyr = ds.GetLayerByName("test")
|
|
assert lyr.GetMetadataItem(ogr.OLMD_FID64) is not None
|
|
f = lyr.GetNextFeature()
|
|
assert f.GetFID() == 1234567890123
|
|
|
|
gdal.Unlink("/vsimem/ogr_gpkg_22.gpkg")
|
|
|
|
|
|
###############################################################################
|
|
# Test not nullable fields
|
|
|
|
|
|
def test_ogr_gpkg_23():
|
|
|
|
ds = gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpkg_23.gpkg")
|
|
lyr = ds.CreateLayer("test", geom_type=ogr.wkbNone)
|
|
field_defn = ogr.FieldDefn("field_not_nullable", ogr.OFTString)
|
|
field_defn.SetNullable(0)
|
|
lyr.CreateField(field_defn)
|
|
field_defn = ogr.FieldDefn("field_nullable", ogr.OFTString)
|
|
lyr.CreateField(field_defn)
|
|
field_defn = ogr.GeomFieldDefn("geomfield_not_nullable", ogr.wkbPoint)
|
|
field_defn.SetNullable(0)
|
|
lyr.CreateGeomField(field_defn)
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetField("field_not_nullable", "not_null")
|
|
f.SetGeomFieldDirectly(
|
|
"geomfield_not_nullable", ogr.CreateGeometryFromWkt("POINT(0 0)")
|
|
)
|
|
lyr.CreateFeature(f)
|
|
f = None
|
|
|
|
# Error case: missing geometry
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetField("field_not_nullable", "not_null")
|
|
with gdaltest.error_handler():
|
|
ret = lyr.CreateFeature(f)
|
|
assert ret != 0
|
|
f = None
|
|
|
|
# Error case: missing non-nullable field
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("POINT(0 0)"))
|
|
with gdaltest.error_handler():
|
|
ret = lyr.CreateFeature(f)
|
|
assert ret != 0
|
|
f = None
|
|
|
|
# Nullable geometry field
|
|
lyr = ds.CreateLayer("test2", geom_type=ogr.wkbPoint, options=["SPATIAL_INDEX=NO"])
|
|
|
|
# Cannot add more than one geometry field
|
|
with gdaltest.error_handler():
|
|
ret = lyr.CreateGeomField(ogr.GeomFieldDefn("foo", ogr.wkbPoint))
|
|
assert ret != 0
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
lyr.CreateFeature(f)
|
|
f = None
|
|
|
|
# Not-nullable fields and geometry fields created after table creation
|
|
lyr = ds.CreateLayer("test3", geom_type=ogr.wkbNone)
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
lyr.CreateFeature(f)
|
|
f = None
|
|
|
|
field_defn = ogr.FieldDefn("field_not_nullable", ogr.OFTString)
|
|
field_defn.SetNullable(0)
|
|
lyr.CreateField(field_defn)
|
|
|
|
field_defn = ogr.FieldDefn("field_nullable", ogr.OFTString)
|
|
lyr.CreateField(field_defn)
|
|
|
|
sql_lyr = ds.ExecuteSQL("SELECT * FROM gpkg_contents WHERE data_type = 'features'")
|
|
fc = sql_lyr.GetFeatureCount()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
assert fc == 2
|
|
|
|
sql_lyr = ds.ExecuteSQL("SELECT 1 FROM sqlite_master WHERE name='gpkg_extensions'")
|
|
has_gpkg_extensions = sql_lyr.GetFeatureCount() == 1
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
assert not has_gpkg_extensions
|
|
|
|
sql_lyr = ds.ExecuteSQL("SELECT * FROM gpkg_geometry_columns")
|
|
fc = sql_lyr.GetFeatureCount()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
assert fc == 2
|
|
|
|
field_defn = ogr.GeomFieldDefn("geomfield_not_nullable", ogr.wkbPoint)
|
|
field_defn.SetNullable(0)
|
|
lyr.CreateGeomField(field_defn)
|
|
|
|
sql_lyr = ds.ExecuteSQL("SELECT * FROM gpkg_contents WHERE data_type = 'features'")
|
|
fc = sql_lyr.GetFeatureCount()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
assert fc == 3
|
|
|
|
sql_lyr = ds.ExecuteSQL("SELECT 1 FROM sqlite_master WHERE name='gpkg_extensions'")
|
|
has_gpkg_extensions = sql_lyr.GetFeatureCount() == 1
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
assert not has_gpkg_extensions
|
|
|
|
sql_lyr = ds.ExecuteSQL("SELECT * FROM gpkg_geometry_columns")
|
|
fc = sql_lyr.GetFeatureCount()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
assert fc == 3
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetField("field_not_nullable", "not_null")
|
|
f.SetGeomFieldDirectly(
|
|
"geomfield_not_nullable", ogr.CreateGeometryFromWkt("POINT(0 0)")
|
|
)
|
|
lyr.CreateFeature(f)
|
|
f = None
|
|
|
|
# Not Nullable geometry field
|
|
lyr = ds.CreateLayer(
|
|
"test4", geom_type=ogr.wkbPoint, options=["GEOMETRY_NULLABLE=NO"]
|
|
)
|
|
assert lyr.GetLayerDefn().GetGeomFieldDefn(0).IsNullable() == 0
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("POINT(0 0)"))
|
|
lyr.CreateFeature(f)
|
|
f = None
|
|
|
|
ds.CreateLayer("test5", geom_type=ogr.wkbNone)
|
|
|
|
ds = None
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_23.gpkg")
|
|
|
|
lyr = ds.GetLayerByName("test5")
|
|
field_defn = ogr.GeomFieldDefn("", ogr.wkbPoint)
|
|
with gdaltest.error_handler():
|
|
assert lyr.CreateGeomField(field_defn) != 0
|
|
|
|
lyr = ds.GetLayerByName("test")
|
|
assert (
|
|
lyr.GetLayerDefn()
|
|
.GetFieldDefn(lyr.GetLayerDefn().GetFieldIndex("field_not_nullable"))
|
|
.IsNullable()
|
|
== 0
|
|
)
|
|
assert (
|
|
lyr.GetLayerDefn()
|
|
.GetFieldDefn(lyr.GetLayerDefn().GetFieldIndex("field_nullable"))
|
|
.IsNullable()
|
|
== 1
|
|
)
|
|
assert (
|
|
lyr.GetLayerDefn()
|
|
.GetGeomFieldDefn(
|
|
lyr.GetLayerDefn().GetGeomFieldIndex("geomfield_not_nullable")
|
|
)
|
|
.IsNullable()
|
|
== 0
|
|
)
|
|
|
|
lyr = ds.GetLayerByName("test2")
|
|
assert lyr.GetLayerDefn().GetGeomFieldDefn(0).IsNullable() == 1
|
|
|
|
lyr = ds.GetLayerByName("test3")
|
|
assert (
|
|
lyr.GetLayerDefn()
|
|
.GetFieldDefn(lyr.GetLayerDefn().GetFieldIndex("field_not_nullable"))
|
|
.IsNullable()
|
|
== 0
|
|
)
|
|
assert (
|
|
lyr.GetLayerDefn()
|
|
.GetFieldDefn(lyr.GetLayerDefn().GetFieldIndex("field_nullable"))
|
|
.IsNullable()
|
|
== 1
|
|
)
|
|
assert (
|
|
lyr.GetLayerDefn()
|
|
.GetGeomFieldDefn(
|
|
lyr.GetLayerDefn().GetGeomFieldIndex("geomfield_not_nullable")
|
|
)
|
|
.IsNullable()
|
|
== 0
|
|
)
|
|
|
|
lyr = ds.GetLayerByName("test4")
|
|
assert lyr.GetLayerDefn().GetGeomFieldDefn(0).IsNullable() == 0
|
|
|
|
ds = None
|
|
|
|
gdal.Unlink("/vsimem/ogr_gpkg_23.gpkg")
|
|
|
|
|
|
###############################################################################
|
|
# Test unique constraints on fields
|
|
|
|
|
|
def test_ogr_gpkg_unique():
|
|
|
|
ds = ogr.GetDriverByName("GPKG").CreateDataSource("/vsimem/ogr_gpkg_unique.gpkg")
|
|
lyr = ds.CreateLayer("test", geom_type=ogr.wkbNone)
|
|
|
|
# Default: no unique constraints
|
|
field_defn = ogr.FieldDefn("field_default", ogr.OFTString)
|
|
lyr.CreateField(field_defn)
|
|
|
|
# Explicit: no unique constraints
|
|
field_defn = ogr.FieldDefn("field_no_unique", ogr.OFTString)
|
|
field_defn.SetUnique(0)
|
|
lyr.CreateField(field_defn)
|
|
|
|
# Explicit: unique constraints
|
|
field_defn = ogr.FieldDefn("field_unique", ogr.OFTString)
|
|
field_defn.SetUnique(1)
|
|
lyr.CreateField(field_defn)
|
|
|
|
# Now check for getters
|
|
layerDefinition = lyr.GetLayerDefn()
|
|
fldDef = layerDefinition.GetFieldDefn(0)
|
|
assert not fldDef.IsUnique()
|
|
fldDef = layerDefinition.GetFieldDefn(1)
|
|
assert not fldDef.IsUnique()
|
|
fldDef = layerDefinition.GetFieldDefn(2)
|
|
assert fldDef.IsUnique()
|
|
|
|
f = ogr.Feature(layerDefinition)
|
|
assert lyr.CreateFeature(f) == ogr.OGRERR_NONE
|
|
f = None
|
|
|
|
# Test adding columns after "crystallization"
|
|
field_defn = ogr.FieldDefn("field_unique_failure", ogr.OFTString)
|
|
field_defn.SetUnique(1)
|
|
# Not allowed by sqlite3. Could potentially be improved
|
|
with gdaltest.error_handler():
|
|
assert lyr.CreateField(field_defn) == ogr.OGRERR_FAILURE
|
|
|
|
# Create another layer from SQL to test quoting of fields
|
|
# and indexes
|
|
# Note: leave create table in a single line because of regex spaces testing
|
|
sql = (
|
|
'CREATE TABLE IF NOT EXISTS "test2" ( "fid" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "field_default" TEXT, "field_no_unique" TEXT, "field_unique" TEXT UNIQUE,`field unique2` TEXT UNIQUE,field_unique3 TEXT UNIQUE, FIELD_UNIQUE_INDEX TEXT, `field unique index2` TEXT, "field_unique_index3" TEXT, NOT_UNIQUE TEXT);',
|
|
"CREATE UNIQUE INDEX test2_unique_idx ON test2(field_unique_index);", # field_unique_index in lowercase whereas in uppercase in CREATE TABLE statement
|
|
"CREATE UNIQUE INDEX test2_unique_idx2 ON test2(`field unique index2`);",
|
|
'CREATE UNIQUE INDEX test2_unique_idx3 ON test2("field_unique_index3");',
|
|
"INSERT INTO gpkg_contents VALUES('test2','attributes','test2','','2020-05-27T12:27:30.136Z',NULL,NULL,NULL,NULL,0);",
|
|
"INSERT INTO gpkg_ogr_contents VALUES('test2',NULL);",
|
|
)
|
|
|
|
for s in sql:
|
|
ds.ExecuteSQL(s)
|
|
|
|
ds = None
|
|
|
|
# Reload
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_unique.gpkg")
|
|
|
|
lyr = ds.GetLayerByName("test")
|
|
|
|
layerDefinition = lyr.GetLayerDefn()
|
|
fldDef = layerDefinition.GetFieldDefn(0)
|
|
assert not fldDef.IsUnique()
|
|
fldDef = layerDefinition.GetFieldDefn(1)
|
|
assert not fldDef.IsUnique()
|
|
fldDef = layerDefinition.GetFieldDefn(2)
|
|
assert fldDef.IsUnique()
|
|
|
|
lyr = ds.GetLayerByName("test2")
|
|
|
|
layerDefinition = lyr.GetLayerDefn()
|
|
fldDef = layerDefinition.GetFieldDefn(0)
|
|
assert not fldDef.IsUnique()
|
|
fldDef = layerDefinition.GetFieldDefn(1)
|
|
assert not fldDef.IsUnique()
|
|
fldDef = layerDefinition.GetFieldDefn(2)
|
|
assert fldDef.IsUnique()
|
|
fldDef = layerDefinition.GetFieldDefn(3)
|
|
assert fldDef.IsUnique()
|
|
fldDef = layerDefinition.GetFieldDefn(4)
|
|
assert fldDef.IsUnique()
|
|
|
|
# Check the last 3 field where the unique constraint is defined
|
|
# from an index
|
|
fldDef = layerDefinition.GetFieldDefn(5)
|
|
assert fldDef.IsUnique()
|
|
fldDef = layerDefinition.GetFieldDefn(6)
|
|
assert fldDef.IsUnique()
|
|
fldDef = layerDefinition.GetFieldDefn(7)
|
|
assert fldDef.IsUnique()
|
|
|
|
fldDef = layerDefinition.GetFieldDefn(8)
|
|
assert not fldDef.IsUnique()
|
|
|
|
ds = None
|
|
gdal.Unlink("/vsimem/ogr_gpkg_unique.gpkg")
|
|
|
|
|
|
###############################################################################
|
|
# Test default values
|
|
|
|
|
|
def test_ogr_gpkg_24():
|
|
|
|
ds = gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpkg_24.gpkg")
|
|
lyr = ds.CreateLayer("test", geom_type=ogr.wkbNone)
|
|
|
|
field_defn = ogr.FieldDefn("field_string", ogr.OFTString)
|
|
field_defn.SetDefault("'a''b'")
|
|
lyr.CreateField(field_defn)
|
|
|
|
field_defn = ogr.FieldDefn("field_int", ogr.OFTInteger)
|
|
field_defn.SetDefault("123")
|
|
lyr.CreateField(field_defn)
|
|
|
|
field_defn = ogr.FieldDefn("field_real", ogr.OFTReal)
|
|
field_defn.SetDefault("1.23")
|
|
lyr.CreateField(field_defn)
|
|
|
|
field_defn = ogr.FieldDefn("field_nodefault", ogr.OFTInteger)
|
|
lyr.CreateField(field_defn)
|
|
|
|
# This will be translated as "(strftime('%Y-%m-%dT%H:%M:%fZ','now'))"
|
|
field_defn = ogr.FieldDefn("field_datetime", ogr.OFTDateTime)
|
|
field_defn.SetDefault("CURRENT_TIMESTAMP")
|
|
lyr.CreateField(field_defn)
|
|
|
|
field_defn = ogr.FieldDefn("field_datetime2", ogr.OFTDateTime)
|
|
field_defn.SetDefault("'2015/06/30 12:34:56'")
|
|
lyr.CreateField(field_defn)
|
|
|
|
field_defn = ogr.FieldDefn("field_datetime3", ogr.OFTDateTime)
|
|
field_defn.SetDefault("(strftime('%Y-%m-%dT%H:%M:%fZ','now'))")
|
|
lyr.CreateField(field_defn)
|
|
|
|
field_defn = ogr.FieldDefn("field_datetime4", ogr.OFTDateTime)
|
|
field_defn.SetDefault("'2015/06/30 12:34:56.123'")
|
|
lyr.CreateField(field_defn)
|
|
|
|
field_defn = ogr.FieldDefn("field_date", ogr.OFTDate)
|
|
field_defn.SetDefault("CURRENT_DATE")
|
|
lyr.CreateField(field_defn)
|
|
|
|
# field_defn = ogr.FieldDefn( 'field_time', ogr.OFTTime )
|
|
# field_defn.SetDefault("CURRENT_TIME")
|
|
# lyr.CreateField(field_defn)
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
lyr.CreateFeature(f)
|
|
f = None
|
|
|
|
# Test adding columns after "crystallization"
|
|
field_defn = ogr.FieldDefn("field_datetime5", ogr.OFTDateTime)
|
|
field_defn.SetDefault("'2016/06/30 12:34:56.123'")
|
|
lyr.CreateField(field_defn)
|
|
|
|
field_defn = ogr.FieldDefn("field_datetime6", ogr.OFTDateTime)
|
|
field_defn.SetDefault("'2016/06/30 12:34:56'")
|
|
lyr.CreateField(field_defn)
|
|
|
|
field_defn = ogr.FieldDefn("field_string2", ogr.OFTString)
|
|
field_defn.SetDefault("'X'")
|
|
lyr.CreateField(field_defn)
|
|
|
|
# Doesn't work currently. Would require rewriting the whole table
|
|
# field_defn = ogr.FieldDefn( 'field_datetimeX', ogr.OFTDateTime )
|
|
# field_defn.SetDefault("CURRENT_TIMESTAMP")
|
|
# lyr.CreateField(field_defn)
|
|
|
|
ds = None
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_24.gpkg", update=1)
|
|
lyr = ds.GetLayerByName("test")
|
|
assert (
|
|
lyr.GetLayerDefn()
|
|
.GetFieldDefn(lyr.GetLayerDefn().GetFieldIndex("field_string"))
|
|
.GetDefault()
|
|
== "'a''b'"
|
|
)
|
|
assert (
|
|
lyr.GetLayerDefn()
|
|
.GetFieldDefn(lyr.GetLayerDefn().GetFieldIndex("field_int"))
|
|
.GetDefault()
|
|
== "123"
|
|
)
|
|
assert (
|
|
lyr.GetLayerDefn()
|
|
.GetFieldDefn(lyr.GetLayerDefn().GetFieldIndex("field_real"))
|
|
.GetDefault()
|
|
== "1.23"
|
|
)
|
|
assert (
|
|
lyr.GetLayerDefn()
|
|
.GetFieldDefn(lyr.GetLayerDefn().GetFieldIndex("field_nodefault"))
|
|
.GetDefault()
|
|
is None
|
|
)
|
|
# Translated from "(strftime('%Y-%m-%dT%H:%M:%fZ','now'))" to CURRENT_TIMESTAMP
|
|
assert (
|
|
lyr.GetLayerDefn()
|
|
.GetFieldDefn(lyr.GetLayerDefn().GetFieldIndex("field_datetime"))
|
|
.GetDefault()
|
|
== "CURRENT_TIMESTAMP"
|
|
)
|
|
assert (
|
|
lyr.GetLayerDefn()
|
|
.GetFieldDefn(lyr.GetLayerDefn().GetFieldIndex("field_datetime2"))
|
|
.GetDefault()
|
|
== "'2015/06/30 12:34:56'"
|
|
)
|
|
assert (
|
|
lyr.GetLayerDefn()
|
|
.GetFieldDefn(lyr.GetLayerDefn().GetFieldIndex("field_datetime3"))
|
|
.GetDefault()
|
|
== "CURRENT_TIMESTAMP"
|
|
)
|
|
assert (
|
|
lyr.GetLayerDefn()
|
|
.GetFieldDefn(lyr.GetLayerDefn().GetFieldIndex("field_datetime4"))
|
|
.GetDefault()
|
|
== "'2015/06/30 12:34:56.123'"
|
|
)
|
|
assert (
|
|
lyr.GetLayerDefn()
|
|
.GetFieldDefn(lyr.GetLayerDefn().GetFieldIndex("field_date"))
|
|
.GetDefault()
|
|
== "CURRENT_DATE"
|
|
)
|
|
# if lyr.GetLayerDefn().GetFieldDefn(lyr.GetLayerDefn().GetFieldIndex('field_time')).GetDefault() != "CURRENT_TIME":
|
|
# gdaltest.post_reason('fail')
|
|
# return 'fail'
|
|
f = lyr.GetNextFeature()
|
|
if (
|
|
f.GetField("field_string") != "a'b"
|
|
or f.GetField("field_int") != 123
|
|
or f.GetField("field_real") != 1.23
|
|
or not f.IsFieldNull("field_nodefault")
|
|
or not f.IsFieldSet("field_datetime")
|
|
or f.GetField("field_datetime2") != "2015/06/30 12:34:56+00"
|
|
or f.GetField("field_datetime4") != "2015/06/30 12:34:56.123+00"
|
|
or not f.IsFieldSet("field_datetime3")
|
|
or not f.IsFieldSet("field_date")
|
|
or f.GetField("field_datetime5") != "2016/06/30 12:34:56.123+00"
|
|
or f.GetField("field_datetime6") != "2016/06/30 12:34:56+00"
|
|
or f.GetField("field_string2") != "X"
|
|
):
|
|
f.DumpReadable()
|
|
pytest.fail()
|
|
|
|
ds = None
|
|
|
|
gdal.Unlink("/vsimem/ogr_gpkg_24.gpkg")
|
|
|
|
|
|
###############################################################################
|
|
# Test creating a field with the fid name
|
|
|
|
|
|
def test_ogr_gpkg_25():
|
|
|
|
ds = gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpkg_25.gpkg")
|
|
lyr = ds.CreateLayer("test", geom_type=ogr.wkbNone, options=["FID=myfid"])
|
|
|
|
lyr.CreateField(ogr.FieldDefn("str", ogr.OFTString))
|
|
with gdaltest.error_handler():
|
|
ret = lyr.CreateField(ogr.FieldDefn("myfid", ogr.OFTString))
|
|
assert ret != 0
|
|
|
|
ret = lyr.CreateField(ogr.FieldDefn("myfid", ogr.OFTInteger))
|
|
assert ret == 0
|
|
lyr.CreateField(ogr.FieldDefn("str2", ogr.OFTString))
|
|
|
|
feat = ogr.Feature(lyr.GetLayerDefn())
|
|
feat.SetField("str", "first string")
|
|
feat.SetField("myfid", 10)
|
|
feat.SetField("str2", "second string")
|
|
ret = lyr.CreateFeature(feat)
|
|
assert ret == 0
|
|
assert feat.GetFID() == 10
|
|
|
|
feat = ogr.Feature(lyr.GetLayerDefn())
|
|
feat.SetField("str2", "second string")
|
|
ret = lyr.CreateFeature(feat)
|
|
assert ret == 0
|
|
if feat.GetFID() < 0:
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
if feat.GetField("myfid") != feat.GetFID():
|
|
feat.DumpReadable()
|
|
pytest.fail()
|
|
|
|
feat.SetField("str", "foo")
|
|
ret = lyr.SetFeature(feat)
|
|
assert ret == 0
|
|
|
|
feat = ogr.Feature(lyr.GetLayerDefn())
|
|
feat.SetFID(1)
|
|
feat.SetField("myfid", 10)
|
|
with gdaltest.error_handler():
|
|
ret = lyr.CreateFeature(feat)
|
|
assert ret != 0
|
|
|
|
with gdaltest.error_handler():
|
|
ret = lyr.SetFeature(feat)
|
|
assert ret != 0
|
|
|
|
feat.UnsetField("myfid")
|
|
with gdaltest.error_handler():
|
|
ret = lyr.SetFeature(feat)
|
|
assert ret != 0
|
|
|
|
lyr.ResetReading()
|
|
f = lyr.GetNextFeature()
|
|
if (
|
|
f.GetFID() != 10
|
|
or f.GetField("str") != "first string"
|
|
or f.GetField("str2") != "second string"
|
|
or f.GetField("myfid") != 10
|
|
):
|
|
f.DumpReadable()
|
|
pytest.fail()
|
|
f = lyr.GetFeature(f.GetFID())
|
|
if (
|
|
f.GetFID() != 10
|
|
or f.GetField("str") != "first string"
|
|
or f.GetField("str2") != "second string"
|
|
or f.GetField("myfid") != 10
|
|
):
|
|
f.DumpReadable()
|
|
pytest.fail()
|
|
f = None
|
|
|
|
ds = None
|
|
|
|
gdaltest.gpkg_dr.DeleteDataSource("/vsimem/ogr_gpkg_25.gpkg")
|
|
|
|
|
|
###############################################################################
|
|
# Test dataset transactions
|
|
|
|
|
|
def test_ogr_gpkg_26():
|
|
|
|
ds = gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpkg_26.gpkg")
|
|
|
|
assert ds.TestCapability(ogr.ODsCTransactions) == 1
|
|
|
|
ret = ds.StartTransaction()
|
|
assert ret == 0
|
|
with gdaltest.error_handler():
|
|
ret = ds.StartTransaction()
|
|
assert ret != 0
|
|
|
|
lyr = ds.CreateLayer("test")
|
|
lyr.CreateField(ogr.FieldDefn("foo", ogr.OFTString))
|
|
ret = ds.RollbackTransaction()
|
|
assert ret == 0
|
|
with gdaltest.error_handler():
|
|
ret = ds.RollbackTransaction()
|
|
assert ret != 0
|
|
ds = None
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_26.gpkg", update=1)
|
|
assert ds.GetLayerCount() == 0
|
|
ret = ds.StartTransaction()
|
|
assert ret == 0
|
|
with gdaltest.error_handler():
|
|
ret = ds.StartTransaction()
|
|
assert ret != 0
|
|
|
|
lyr = ds.CreateLayer("test")
|
|
lyr.CreateField(ogr.FieldDefn("foo", ogr.OFTString))
|
|
ret = ds.CommitTransaction()
|
|
assert ret == 0
|
|
with gdaltest.error_handler():
|
|
ret = ds.CommitTransaction()
|
|
assert ret != 0
|
|
ds = None
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_26.gpkg", update=1)
|
|
assert ds.GetLayerCount() == 1
|
|
lyr = ds.GetLayerByName("test")
|
|
|
|
ds.StartTransaction()
|
|
lyr.CreateFeature(ogr.Feature(lyr.GetLayerDefn()))
|
|
lyr.ResetReading()
|
|
f = lyr.GetNextFeature()
|
|
assert f is not None
|
|
assert lyr.GetFeatureCount() == 1
|
|
ds.RollbackTransaction()
|
|
assert lyr.GetFeatureCount() == 0
|
|
|
|
ds.StartTransaction()
|
|
lyr.CreateFeature(ogr.Feature(lyr.GetLayerDefn()))
|
|
lyr.CreateFeature(ogr.Feature(lyr.GetLayerDefn()))
|
|
lyr.ResetReading()
|
|
f = lyr.GetNextFeature()
|
|
assert f is not None and f.GetFID() == 1
|
|
ds.CommitTransaction()
|
|
# the cursor is still valid after CommitTransaction(), which isn't the case for other backends such as PG !
|
|
f = lyr.GetNextFeature()
|
|
assert f is not None and f.GetFID() == 2
|
|
assert lyr.GetFeatureCount() == 2
|
|
|
|
ds.StartTransaction()
|
|
lyr = ds.CreateLayer("test2", geom_type=ogr.wkbPoint)
|
|
lyr.CreateField(ogr.FieldDefn("foo", ogr.OFTString))
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("POINT(0 0)"))
|
|
ret = lyr.CreateFeature(f)
|
|
ds.CommitTransaction()
|
|
assert ret == 0
|
|
|
|
ds.StartTransaction()
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("POINT(0 0)"))
|
|
ret = lyr.CreateFeature(f)
|
|
ds.CommitTransaction()
|
|
assert ret == 0
|
|
|
|
if False: # pylint: disable=using-constant-test
|
|
ds.StartTransaction()
|
|
lyr = ds.CreateLayer("test3", geom_type=ogr.wkbPoint)
|
|
lyr.CreateField(ogr.FieldDefn("foo", ogr.OFTString))
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("POINT(0 0)"))
|
|
ret = lyr.CreateFeature(f)
|
|
|
|
# ds.CommitTransaction()
|
|
ds.ReleaseResultSet(ds.ExecuteSQL("SELECT 1"))
|
|
# ds = None
|
|
# ds = ogr.Open('/vsimem/ogr_gpkg_26.gpkg', update = 1)
|
|
# lyr = ds.GetLayerByName('test3')
|
|
# ds.StartTransaction()
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("POINT(0 0)"))
|
|
ret = lyr.CreateFeature(f)
|
|
ds.CommitTransaction()
|
|
# For some reason fails with SQLite 3.6.X with 'failed to execute insert : callback requested query abort'
|
|
# but not with later versions...
|
|
assert ret == 0
|
|
|
|
ds = None
|
|
|
|
gdaltest.gpkg_dr.DeleteDataSource("/vsimem/ogr_gpkg_26.gpkg")
|
|
|
|
|
|
###############################################################################
|
|
# Test interface with Spatialite
|
|
|
|
|
|
def test_ogr_gpkg_27():
|
|
|
|
ds = gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpkg_27.gpkg")
|
|
with gdaltest.error_handler():
|
|
sql_lyr = ds.ExecuteSQL("SELECT GeomFromGPB(null)")
|
|
if sql_lyr is None:
|
|
ds = None
|
|
gdaltest.gpkg_dr.DeleteDataSource("/vsimem/ogr_gpkg_27.gpkg")
|
|
pytest.skip()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
|
|
lyr = ds.CreateLayer("test")
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT (2 49)"))
|
|
lyr.CreateFeature(f)
|
|
sql_lyr = ds.ExecuteSQL("SELECT GeomFromGPB(geom) FROM test")
|
|
f = sql_lyr.GetNextFeature()
|
|
if f.GetGeometryRef().ExportToWkt() != "POINT (2 49)":
|
|
f.DumpReadable()
|
|
pytest.fail()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
|
|
ds = None
|
|
gdaltest.gpkg_dr.DeleteDataSource("/vsimem/ogr_gpkg_27.gpkg")
|
|
|
|
|
|
###############################################################################
|
|
# Test ogr2ogr -a_srs (as the geopackage driver doesn't clone the passed SRS
|
|
# but inc/dec its ref count, which can exhibit issues in GDALVectorTanslate())
|
|
|
|
|
|
def test_ogr_gpkg_28():
|
|
|
|
srcDS = gdal.OpenEx("../ogr/data/poly.shp")
|
|
ds = gdal.VectorTranslate(
|
|
"/vsimem/ogr_gpkg_28.gpkg", srcDS, format="GPKG", dstSRS="EPSG:4326"
|
|
)
|
|
assert str(ds.GetLayer(0).GetSpatialRef()).find("1984") != -1
|
|
|
|
ds = None
|
|
gdaltest.gpkg_dr.DeleteDataSource("/vsimem/ogr_gpkg_28.gpkg")
|
|
|
|
|
|
###############################################################################
|
|
# Test XYM / XYZM support
|
|
|
|
|
|
def test_ogr_gpkg_29():
|
|
|
|
ds = gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpkg_29.gpkg")
|
|
assert ds.TestCapability(ogr.ODsCMeasuredGeometries) == 1
|
|
lyr = ds.CreateLayer("pointm", geom_type=ogr.wkbPointM)
|
|
assert lyr.TestCapability(ogr.OLCMeasuredGeometries) == 1
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("POINT M (1 2 3)"))
|
|
lyr.CreateFeature(f)
|
|
lyr = ds.CreateLayer("pointzm", geom_type=ogr.wkbPointZM)
|
|
assert lyr.TestCapability(ogr.OLCMeasuredGeometries) == 1
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("POINT ZM (1 2 3 4)"))
|
|
lyr.CreateFeature(f)
|
|
ds = None
|
|
|
|
assert validate("/vsimem/ogr_gpkg_29.gpkg"), "validation failed"
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_29.gpkg", update=1)
|
|
lyr = ds.GetLayerByName("pointm")
|
|
assert lyr.GetGeomType() == ogr.wkbPointM
|
|
f = lyr.GetNextFeature()
|
|
if f.GetGeometryRef().ExportToIsoWkt() != "POINT M (1 2 3)":
|
|
f.DumpReadable()
|
|
pytest.fail()
|
|
|
|
# Generate a XYM envelope
|
|
ds.ExecuteSQL(
|
|
"UPDATE pointm SET geom = x'4750000700000000000000000000F03F000000000000F03F000000000000004000000000000000400000000000000840000000000000084001D1070000000000000000F03F00000000000000400000000000000840'"
|
|
)
|
|
|
|
lyr = ds.GetLayerByName("pointzm")
|
|
assert lyr.GetGeomType() == ogr.wkbPointZM
|
|
f = lyr.GetNextFeature()
|
|
if f.GetGeometryRef().ExportToIsoWkt() != "POINT ZM (1 2 3 4)":
|
|
f.DumpReadable()
|
|
pytest.fail()
|
|
|
|
# Generate a XYZM envelope
|
|
ds.ExecuteSQL(
|
|
"UPDATE pointzm SET geom = x'4750000900000000000000000000F03F000000000000F03F00000000000000400000000000000040000000000000084000000000000008400000000000001040000000000000104001B90B0000000000000000F03F000000000000004000000000000008400000000000001040'"
|
|
)
|
|
|
|
ds = None
|
|
|
|
# Check again
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_29.gpkg")
|
|
lyr = ds.GetLayerByName("pointm")
|
|
assert lyr.GetGeomType() == ogr.wkbPointM
|
|
f = lyr.GetNextFeature()
|
|
if f.GetGeometryRef().ExportToIsoWkt() != "POINT M (1 2 3)":
|
|
f.DumpReadable()
|
|
pytest.fail()
|
|
lyr = ds.GetLayerByName("pointzm")
|
|
assert lyr.GetGeomType() == ogr.wkbPointZM
|
|
f = lyr.GetNextFeature()
|
|
if f.GetGeometryRef().ExportToIsoWkt() != "POINT ZM (1 2 3 4)":
|
|
f.DumpReadable()
|
|
pytest.fail()
|
|
ds = None
|
|
|
|
gdaltest.gpkg_dr.DeleteDataSource("/vsimem/ogr_gpkg_29.gpkg")
|
|
|
|
|
|
###############################################################################
|
|
# Test non standard file extension (#6396)
|
|
|
|
|
|
def test_ogr_gpkg_30():
|
|
|
|
gdal.ErrorReset()
|
|
with gdaltest.error_handler():
|
|
ds = gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpkg_30.geopkg")
|
|
assert ds is not None
|
|
assert gdal.GetLastErrorMsg() != ""
|
|
ds = None
|
|
|
|
gdal.ErrorReset()
|
|
with gdaltest.error_handler():
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_30.geopkg", update=1)
|
|
assert ds is not None
|
|
assert gdal.GetLastErrorMsg() != ""
|
|
ds = None
|
|
|
|
with gdaltest.error_handler():
|
|
gdaltest.gpkg_dr.DeleteDataSource("/vsimem/ogr_gpkg_30.geopkg")
|
|
|
|
|
|
###############################################################################
|
|
# Test CURVE and SURFACE types
|
|
|
|
|
|
def test_ogr_gpkg_31():
|
|
|
|
ds = gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpkg_31.gpkg")
|
|
lyr = ds.CreateLayer("curve", geom_type=ogr.wkbCurve)
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("LINESTRING (1 2,3 4)"))
|
|
lyr.CreateFeature(f)
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("COMPOUNDCURVE ((1 2,3 4))"))
|
|
lyr.CreateFeature(f)
|
|
lyr = ds.CreateLayer("surface", geom_type=ogr.wkbSurface)
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("POLYGON ((0 0,0 1,1 1,0 0))"))
|
|
lyr.CreateFeature(f)
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("CURVEPOLYGON ((0 0,0 1,1 1,0 0))"))
|
|
lyr.CreateFeature(f)
|
|
ds = None
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_31.gpkg")
|
|
lyr = ds.GetLayerByName("curve")
|
|
assert lyr.GetGeomType() == ogr.wkbCurve
|
|
lyr = ds.GetLayerByName("surface")
|
|
assert lyr.GetGeomType() == ogr.wkbSurface
|
|
ds = None
|
|
|
|
assert validate("/vsimem/ogr_gpkg_31.gpkg"), "validation failed"
|
|
|
|
gdaltest.gpkg_dr.DeleteDataSource("/vsimem/ogr_gpkg_31.gpkg")
|
|
|
|
|
|
###############################################################################
|
|
# Run creating a non-spatial layer that isn't registered as 'aspatial' and
|
|
# read it back
|
|
|
|
|
|
def test_ogr_gpkg_32():
|
|
|
|
ds = gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpkg_32.gpkg")
|
|
ds.CreateLayer(
|
|
"aspatial", geom_type=ogr.wkbNone, options=["ASPATIAL_VARIANT=NOT_REGISTERED"]
|
|
)
|
|
ds = None
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_32.gpkg")
|
|
assert ds.GetLayerCount() == 1
|
|
sql_lyr = ds.ExecuteSQL("SELECT * FROM gpkg_contents")
|
|
assert sql_lyr.GetFeatureCount() == 0
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
sql_lyr = ds.ExecuteSQL("SELECT * FROM gpkg_geometry_columns")
|
|
assert sql_lyr.GetFeatureCount() == 0
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT * FROM sqlite_master WHERE name = 'gpkg_extensions'"
|
|
)
|
|
assert sql_lyr.GetFeatureCount() == 0
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
ds = None
|
|
|
|
assert validate("/vsimem/ogr_gpkg_32.gpkg"), "validation failed"
|
|
|
|
gdaltest.gpkg_dr.DeleteDataSource("/vsimem/ogr_gpkg_32.gpkg")
|
|
|
|
|
|
###############################################################################
|
|
# Test OGR_CURRENT_DATE
|
|
|
|
|
|
def test_ogr_gpkg_33():
|
|
|
|
with gdal.config_option("OGR_CURRENT_DATE", "2000-01-01T:00:00:00.000Z"):
|
|
ds = gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpkg_33.gpkg")
|
|
ds.CreateLayer("test", geom_type=ogr.wkbNone)
|
|
ds = None
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_33.gpkg")
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT * FROM gpkg_contents WHERE last_change = '2000-01-01T:00:00:00.000Z'"
|
|
)
|
|
assert sql_lyr.GetFeatureCount() == 1
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
ds = None
|
|
|
|
gdaltest.gpkg_dr.DeleteDataSource("/vsimem/ogr_gpkg_33.gpkg")
|
|
|
|
|
|
###############################################################################
|
|
# Test rename and delete a layer registered in extensions, metadata, spatial index etc
|
|
|
|
|
|
def test_ogr_gpkg_34():
|
|
|
|
layer_name = """weird'layer"name"""
|
|
|
|
dbname = "/vsimem/ogr_gpkg_34.gpkg"
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(dbname)
|
|
lyr = ds.CreateLayer(layer_name, geom_type=ogr.wkbCurvePolygon)
|
|
lyr.CreateField(ogr.FieldDefn("foo", ogr.OFTString))
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("CURVEPOLYGON ((0 0,0 1,1 1,0 0))"))
|
|
lyr.CreateFeature(f)
|
|
lyr.SetMetadataItem("FOO", "BAR")
|
|
ds.ExecuteSQL(
|
|
"""CREATE TABLE gpkg_data_columns (
|
|
table_name TEXT NOT NULL,
|
|
column_name TEXT NOT NULL,
|
|
name TEXT,
|
|
title TEXT,
|
|
description TEXT,
|
|
mime_type TEXT,
|
|
constraint_name TEXT,
|
|
CONSTRAINT pk_gdc PRIMARY KEY (table_name, column_name),
|
|
CONSTRAINT gdc_tn UNIQUE (table_name, name)
|
|
)"""
|
|
)
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_data_columns VALUES('weird''layer\"name', 'foo', 'foo_constraints', NULL, NULL, NULL, NULL)"
|
|
)
|
|
|
|
# QGIS layer_styles extension: https://github.com/pka/qgpkg/blob/master/qgis_geopackage_extension.md
|
|
ds.ExecuteSQL(
|
|
"""CREATE TABLE "layer_styles" ( "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "f_table_catalog" TEXT(256), "f_table_schema" TEXT(256), "f_table_name" TEXT(256), "f_geometry_column" TEXT(256), "styleName" TEXT(30), "styleQML" TEXT, "styleSLD" TEXT, "useAsDefault" BOOLEAN, "description" TEXT, "owner" TEXT(30), "ui" TEXT(30), "update_time" DATETIME DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')))"""
|
|
)
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO layer_styles VALUES(1, NULL, NULL, 'weird''layer\"name', 'geom', 'styleName', 'styleQML', 'styleSLD', 0, 'description', 'owner', 'ui', NULL)"
|
|
)
|
|
ds = None
|
|
|
|
# Check that there are reference to the layer
|
|
f = gdal.VSIFOpenL(dbname, "rb")
|
|
content = gdal.VSIFReadL(1, 1000000, f).decode("latin1")
|
|
gdal.VSIFCloseL(f)
|
|
|
|
assert layer_name in content
|
|
|
|
ds = ogr.Open(dbname, update=1)
|
|
new_layer_name = """weird2'layer"name"""
|
|
gdal.ErrorReset()
|
|
with gdaltest.error_handler():
|
|
ds.ExecuteSQL('ALTER TABLE "weird\'layer""name" RENAME TO gpkg_contents')
|
|
assert gdal.GetLastErrorMsg() != ""
|
|
gdal.ErrorReset()
|
|
ds.ExecuteSQL('ALTER TABLE "weird\'layer""name" RENAME TO "weird2\'layer""name"')
|
|
ds = None
|
|
|
|
ds = ogr.Open(dbname, update=1)
|
|
gdal.ErrorReset()
|
|
ds.ExecuteSQL('ALTER TABLE "weird2\'layer""name" RENAME COLUMN "foo" TO "bar"')
|
|
assert gdal.GetLastErrorMsg() == ""
|
|
lyr = ds.GetLayerByName(new_layer_name)
|
|
assert lyr.GetLayerDefn().GetFieldIndex("bar") >= 0
|
|
|
|
gdal.ErrorReset()
|
|
ds.ExecuteSQL('ALTER TABLE "weird2\'layer""name" DROP COLUMN "bar"')
|
|
assert gdal.GetLastErrorMsg() == ""
|
|
assert lyr.GetLayerDefn().GetFieldIndex("bar") < 0
|
|
|
|
ds.ExecuteSQL("VACUUM")
|
|
ds = None
|
|
|
|
# Check that there is no more any reference to the layer
|
|
f = gdal.VSIFOpenL(dbname, "rb")
|
|
content = gdal.VSIFReadL(1, 1000000, f).decode("latin1")
|
|
gdal.VSIFCloseL(f)
|
|
|
|
assert layer_name not in content
|
|
layer_name = new_layer_name
|
|
|
|
ds = ogr.Open(dbname, update=1)
|
|
# currently we don't suppress rows from layer_styles
|
|
ds.ExecuteSQL("DELETE FROM layer_styles")
|
|
gdal.ErrorReset()
|
|
with gdaltest.error_handler():
|
|
ds.ExecuteSQL("DELLAYER:does_not_exist")
|
|
assert gdal.GetLastErrorMsg() != ""
|
|
gdal.ErrorReset()
|
|
ds.ExecuteSQL("DELLAYER:" + layer_name)
|
|
assert gdal.GetLastErrorMsg() == ""
|
|
ds.ExecuteSQL("VACUUM")
|
|
ds = None
|
|
|
|
# Check that there is no more any reference to the layer
|
|
f = gdal.VSIFOpenL(dbname, "rb")
|
|
content = gdal.VSIFReadL(1, 1000000, f).decode("latin1")
|
|
gdal.VSIFCloseL(f)
|
|
|
|
assert layer_name not in content
|
|
|
|
gdaltest.gpkg_dr.DeleteDataSource(dbname)
|
|
|
|
# Try again with DROP TABLE syntax
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(dbname)
|
|
lyr = ds.CreateLayer(layer_name, geom_type=ogr.wkbCurvePolygon)
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("CURVEPOLYGON ((0 0,0 1,1 1,0 0))"))
|
|
lyr.CreateFeature(f)
|
|
lyr.SetMetadataItem("FOO", "BAR")
|
|
lyr = ds.CreateLayer("another_layer_name")
|
|
ds = None
|
|
|
|
ds = ogr.Open(dbname, update=1)
|
|
gdal.ErrorReset()
|
|
ds.ExecuteSQL('DROP TABLE "weird2\'layer""name"')
|
|
assert gdal.GetLastErrorMsg() == ""
|
|
ds.ExecuteSQL("DROP TABLE another_layer_name")
|
|
assert gdal.GetLastErrorMsg() == ""
|
|
with gdaltest.error_handler():
|
|
ds.ExecuteSQL('DROP TABLE "foobar"')
|
|
assert gdal.GetLastErrorMsg() != ""
|
|
gdal.ErrorReset()
|
|
ds.ExecuteSQL("VACUUM")
|
|
ds = None
|
|
|
|
# Check that there is no more any reference to the layer
|
|
f = gdal.VSIFOpenL(dbname, "rb")
|
|
content = gdal.VSIFReadL(1, 1000000, f).decode("latin1")
|
|
gdal.VSIFCloseL(f)
|
|
|
|
assert layer_name not in content
|
|
|
|
assert "another_layer_name" not in content
|
|
|
|
gdaltest.gpkg_dr.DeleteDataSource(dbname)
|
|
|
|
|
|
###############################################################################
|
|
# Test DeleteField()
|
|
|
|
|
|
def test_ogr_gpkg_35():
|
|
|
|
dbname = "/vsimem/ogr_gpkg_35.gpkg"
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(dbname)
|
|
lyr = ds.CreateLayer("test", geom_type=ogr.wkbPolygon)
|
|
lyr.CreateField(ogr.FieldDefn("foo", ogr.OFTString))
|
|
lyr.CreateField(ogr.FieldDefn("bar_i_will_disappear", ogr.OFTString))
|
|
lyr.CreateField(ogr.FieldDefn("baz", ogr.OFTString))
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetFID(10)
|
|
f.SetField("foo", "fooval")
|
|
f.SetField("bar_i_will_disappear", "barval")
|
|
f.SetField("baz", "bazval")
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("POLYGON ((0 0,0 1,1 1,0 0))"))
|
|
lyr.CreateFeature(f)
|
|
|
|
lyr_nonspatial = ds.CreateLayer("test_nonspatial", geom_type=ogr.wkbNone)
|
|
lyr_nonspatial.CreateField(ogr.FieldDefn("foo", ogr.OFTString))
|
|
lyr_nonspatial.CreateField(ogr.FieldDefn("bar_i_will_disappear", ogr.OFTString))
|
|
lyr_nonspatial.CreateField(ogr.FieldDefn("baz", ogr.OFTString))
|
|
|
|
# Metadata
|
|
lyr_nonspatial.SetMetadataItem("FOO", "BAR")
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_metadata_reference VALUES ('column', 'test_nonspatial', 'bar_i_will_disappear', NULL, '2021-01-01T00:00:00.000Z', 1, NULL)"
|
|
)
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_metadata VALUES (2, 'dataset','http://gdal.org','text/plain','bla')"
|
|
)
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_metadata_reference VALUES ('column', 'test_nonspatial', 'bar_i_will_disappear', NULL, '2021-01-01T00:00:00.000Z', 2, NULL)"
|
|
)
|
|
|
|
f = ogr.Feature(lyr_nonspatial.GetLayerDefn())
|
|
f.SetFID(10)
|
|
f.SetField("foo", "fooval")
|
|
f.SetField("bar_i_will_disappear", "barval")
|
|
f.SetField("baz", "bazval")
|
|
lyr_nonspatial.CreateFeature(f)
|
|
|
|
ds.ExecuteSQL(
|
|
"""CREATE TABLE gpkg_data_columns (
|
|
table_name TEXT NOT NULL,
|
|
column_name TEXT NOT NULL,
|
|
name TEXT,
|
|
title TEXT,
|
|
description TEXT,
|
|
mime_type TEXT,
|
|
constraint_name TEXT,
|
|
CONSTRAINT pk_gdc PRIMARY KEY (table_name, column_name),
|
|
CONSTRAINT gdc_tn UNIQUE (table_name, name)
|
|
)"""
|
|
)
|
|
ds.ExecuteSQL(
|
|
"""CREATE TABLE gpkg_data_column_constraints (
|
|
constraint_name TEXT NOT NULL,
|
|
constraint_type TEXT NOT NULL,
|
|
value TEXT,
|
|
min NUMERIC,
|
|
min_is_inclusive BOOLEAN,
|
|
max NUMERIC,
|
|
max_is_inclusive BOOLEAN,
|
|
description TEXT,
|
|
CONSTRAINT gdcc_ntv UNIQUE (constraint_name,
|
|
constraint_type, value))"""
|
|
)
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_data_columns VALUES('test', 'bar_i_will_disappear', 'bar_constraints', NULL, NULL, NULL, NULL)"
|
|
)
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_extensions VALUES('test', 'bar_i_will_disappear', 'extension_name', 'definition', 'scope')"
|
|
)
|
|
|
|
assert lyr.TestCapability(ogr.OLCDeleteField) == 1
|
|
|
|
with gdaltest.error_handler():
|
|
ret = lyr.DeleteField(-1)
|
|
assert ret != 0
|
|
|
|
with gdaltest.error_handler():
|
|
ret = lyr.DeleteField(lyr.GetLayerDefn().GetFieldCount())
|
|
assert ret != 0
|
|
|
|
assert lyr.DeleteField(1) == 0
|
|
assert lyr.GetLayerDefn().GetFieldCount() == 2
|
|
|
|
lyr.ResetReading()
|
|
f = lyr.GetNextFeature()
|
|
if (
|
|
f.GetFID() != 10
|
|
or f["foo"] != "fooval"
|
|
or f["baz"] != "bazval"
|
|
or f.GetGeometryRef().ExportToWkt() != "POLYGON ((0 0,0 1,1 1,0 0))"
|
|
):
|
|
f.DumpReadable()
|
|
pytest.fail()
|
|
|
|
lyr.StartTransaction()
|
|
ret = lyr_nonspatial.DeleteField(1)
|
|
lyr.CommitTransaction()
|
|
assert ret == 0
|
|
lyr_nonspatial.ResetReading()
|
|
f = lyr_nonspatial.GetNextFeature()
|
|
if f.GetFID() != 10 or f["foo"] != "fooval" or f["baz"] != "bazval":
|
|
f.DumpReadable()
|
|
pytest.fail()
|
|
|
|
ds.ExecuteSQL("VACUUM")
|
|
|
|
ds = None
|
|
|
|
assert validate(dbname)
|
|
|
|
# Try on read-only dataset
|
|
ds = ogr.Open(dbname)
|
|
|
|
sql_lyr = ds.ExecuteSQL("SELECT * FROM gpkg_metadata WHERE id = 1")
|
|
assert sql_lyr.GetFeatureCount() == 1
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
|
|
sql_lyr = ds.ExecuteSQL("SELECT * FROM gpkg_metadata WHERE id = 2")
|
|
assert sql_lyr.GetFeatureCount() == 0
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
|
|
lyr = ds.GetLayerByName("test_nonspatial")
|
|
assert lyr.GetMetadataItem("FOO") == "BAR"
|
|
|
|
lyr = ds.GetLayer(0)
|
|
with gdaltest.error_handler():
|
|
ret = lyr.DeleteField(0)
|
|
assert ret != 0
|
|
ds = None
|
|
|
|
# Check that there is no more any reference to the layer
|
|
f = gdal.VSIFOpenL(dbname, "rb")
|
|
content = gdal.VSIFReadL(1, 1000000, f).decode("latin1")
|
|
gdal.VSIFCloseL(f)
|
|
|
|
assert "bar_i_will_disappear" not in content
|
|
|
|
gdaltest.gpkg_dr.DeleteDataSource(dbname)
|
|
|
|
|
|
###############################################################################
|
|
# Test AlterFieldDefn()
|
|
|
|
|
|
def test_ogr_gpkg_36():
|
|
|
|
dbname = "/vsimem/ogr_gpkg_36.gpkg"
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(dbname)
|
|
lyr = ds.CreateLayer("test", geom_type=ogr.wkbPolygon)
|
|
lyr.CreateField(ogr.FieldDefn("foo", ogr.OFTString))
|
|
lyr.CreateField(ogr.FieldDefn("baz", ogr.OFTString))
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetFID(10)
|
|
f.SetField("foo", "10.5")
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("POLYGON ((0 0,0 1,1 1,0 0))"))
|
|
lyr.CreateFeature(f)
|
|
f = None
|
|
|
|
ds.ExecuteSQL(
|
|
"""CREATE TABLE gpkg_data_columns (
|
|
table_name TEXT NOT NULL,
|
|
column_name TEXT NOT NULL,
|
|
name TEXT,
|
|
title TEXT,
|
|
description TEXT,
|
|
mime_type TEXT,
|
|
constraint_name TEXT,
|
|
CONSTRAINT pk_gdc PRIMARY KEY (table_name, column_name),
|
|
CONSTRAINT gdc_tn UNIQUE (table_name, name)
|
|
)"""
|
|
)
|
|
ds.ExecuteSQL(
|
|
"""CREATE TABLE gpkg_data_column_constraints (
|
|
constraint_name TEXT NOT NULL,
|
|
constraint_type TEXT NOT NULL,
|
|
value TEXT,
|
|
min NUMERIC,
|
|
min_is_inclusive BOOLEAN,
|
|
max NUMERIC,
|
|
max_is_inclusive BOOLEAN,
|
|
description TEXT,
|
|
CONSTRAINT gdcc_ntv UNIQUE (constraint_name,
|
|
constraint_type, value))"""
|
|
)
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_data_columns VALUES('test', 'foo', 'constraint', NULL, NULL, NULL, NULL)"
|
|
)
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_extensions VALUES('test', 'foo', 'extension_name', 'definition', 'read-write')"
|
|
)
|
|
ds.ExecuteSQL("CREATE INDEX my_idx ON test(foo)")
|
|
|
|
# Metadata
|
|
lyr.SetMetadataItem("FOO", "BAR")
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_metadata_reference VALUES ('column', 'test', 'foo', NULL, '2021-01-01T00:00:00.000Z', 1, NULL)"
|
|
)
|
|
|
|
assert lyr.TestCapability(ogr.OLCAlterFieldDefn) == 1
|
|
|
|
with gdaltest.error_handler():
|
|
ret = lyr.AlterFieldDefn(-1, ogr.FieldDefn("foo"), ogr.ALTER_ALL_FLAG)
|
|
assert ret != 0
|
|
|
|
with gdaltest.error_handler():
|
|
ret = lyr.AlterFieldDefn(1, ogr.FieldDefn("foo"), ogr.ALTER_ALL_FLAG)
|
|
assert ret != 0
|
|
|
|
with gdaltest.error_handler():
|
|
ret = lyr.AlterFieldDefn(
|
|
0, ogr.FieldDefn(lyr.GetGeometryColumn()), ogr.ALTER_ALL_FLAG
|
|
)
|
|
assert ret != 0
|
|
|
|
with gdaltest.error_handler():
|
|
ret = lyr.AlterFieldDefn(
|
|
0, ogr.FieldDefn(lyr.GetFIDColumn()), ogr.ALTER_ALL_FLAG
|
|
)
|
|
assert ret != 0
|
|
|
|
with gdaltest.error_handler():
|
|
ret = lyr.AlterFieldDefn(0, ogr.FieldDefn("baz"), ogr.ALTER_ALL_FLAG)
|
|
assert ret != 0
|
|
|
|
new_field_defn = ogr.FieldDefn("bar", ogr.OFTReal)
|
|
new_field_defn.SetSubType(ogr.OFSTFloat32)
|
|
new_field_defn.SetWidth(10)
|
|
new_field_defn.SetDefault("5")
|
|
|
|
# Schema only change
|
|
assert lyr.AlterFieldDefn(0, new_field_defn, ogr.ALTER_ALL_FLAG) == 0
|
|
|
|
# Full table rewrite
|
|
new_field_defn.SetNullable(False)
|
|
assert lyr.AlterFieldDefn(0, new_field_defn, ogr.ALTER_ALL_FLAG) == 0
|
|
|
|
# Full table rewrite
|
|
new_field_defn.SetUnique(True)
|
|
assert lyr.AlterFieldDefn(0, new_field_defn, ogr.ALTER_ALL_FLAG) == 0
|
|
|
|
# Violation of not-null constraint
|
|
new_field_defn = ogr.FieldDefn("baz", ogr.OFTString)
|
|
new_field_defn.SetNullable(False)
|
|
with gdaltest.error_handler():
|
|
assert lyr.AlterFieldDefn(1, new_field_defn, ogr.ALTER_ALL_FLAG) != 0
|
|
|
|
lyr.ResetReading()
|
|
f = lyr.GetNextFeature()
|
|
if (
|
|
f.GetFID() != 10
|
|
or f["bar"] != 10.5
|
|
or f.GetGeometryRef().ExportToWkt() != "POLYGON ((0 0,0 1,1 1,0 0))"
|
|
):
|
|
f.DumpReadable()
|
|
pytest.fail()
|
|
f = None
|
|
|
|
# Just change the name, and run it outside an existing transaction
|
|
lyr.StartTransaction()
|
|
new_field_defn = ogr.FieldDefn("baw2", ogr.OFTString)
|
|
assert lyr.AlterFieldDefn(0, new_field_defn, ogr.ALTER_ALL_FLAG) == 0
|
|
lyr.CommitTransaction()
|
|
|
|
# Just change the name, and run it under an existing transaction
|
|
lyr.StartTransaction()
|
|
new_field_defn = ogr.FieldDefn("baw", ogr.OFTString)
|
|
assert lyr.AlterFieldDefn(0, new_field_defn, ogr.ALTER_ALL_FLAG) == 0
|
|
lyr.CommitTransaction()
|
|
|
|
lyr.ResetReading()
|
|
f = lyr.GetNextFeature()
|
|
if (
|
|
f.GetFID() != 10
|
|
or f["baw"] != "10.5"
|
|
or f.GetGeometryRef().ExportToWkt() != "POLYGON ((0 0,0 1,1 1,0 0))"
|
|
):
|
|
f.DumpReadable()
|
|
pytest.fail()
|
|
f = None
|
|
|
|
# Check that index has been recreated
|
|
sql_lyr = ds.ExecuteSQL("SELECT * FROM sqlite_master WHERE name = 'my_idx'")
|
|
f = sql_lyr.GetNextFeature()
|
|
assert f is not None
|
|
f = None
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
|
|
ds.ExecuteSQL("VACUUM")
|
|
|
|
ds = None
|
|
|
|
assert validate(dbname)
|
|
|
|
# Try on read-only dataset
|
|
ds = ogr.Open(dbname)
|
|
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT * FROM gpkg_data_columns WHERE table_name = 'test' AND column_name = 'baw'"
|
|
)
|
|
assert sql_lyr.GetFeatureCount() == 1
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT * FROM gpkg_metadata_reference WHERE table_name = 'test' AND column_name = 'baw'"
|
|
)
|
|
assert sql_lyr.GetFeatureCount() == 1
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
|
|
lyr = ds.GetLayer(0)
|
|
with gdaltest.error_handler():
|
|
ret = lyr.AlterFieldDefn(0, ogr.FieldDefn("foo"), ogr.ALTER_ALL_FLAG)
|
|
assert ret != 0
|
|
ds = None
|
|
|
|
# Check that there is no more any reference to the layer
|
|
f = gdal.VSIFOpenL(dbname, "rb")
|
|
content = gdal.VSIFReadL(1, 1000000, f).decode("latin1")
|
|
gdal.VSIFCloseL(f)
|
|
|
|
assert "foo" not in content
|
|
|
|
gdaltest.gpkg_dr.DeleteDataSource(dbname)
|
|
|
|
# Test failed DB re-opening
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(dbname)
|
|
lyr = ds.CreateLayer("test", geom_type=ogr.wkbPolygon)
|
|
lyr.CreateField(ogr.FieldDefn("foo", ogr.OFTString))
|
|
lyr.CreateFeature(ogr.Feature(lyr.GetLayerDefn()))
|
|
# Unlink before AlterFieldDefn
|
|
gdal.Unlink(dbname)
|
|
with gdaltest.error_handler():
|
|
new_field_defn = ogr.FieldDefn("bar")
|
|
new_field_defn.SetNullable(False)
|
|
ret = lyr.AlterFieldDefn(0, new_field_defn, ogr.ALTER_ALL_FLAG)
|
|
assert ret != 0
|
|
with gdaltest.error_handler():
|
|
ds = None
|
|
|
|
gdaltest.gpkg_dr.DeleteDataSource(dbname)
|
|
|
|
|
|
###############################################################################
|
|
# Test ReorderFields()
|
|
|
|
|
|
def test_ogr_gpkg_37():
|
|
|
|
dbname = "/vsimem/ogr_gpkg_37.gpkg"
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(dbname)
|
|
lyr = ds.CreateLayer("test", geom_type=ogr.wkbPolygon)
|
|
lyr.CreateField(ogr.FieldDefn("foo", ogr.OFTString))
|
|
lyr.CreateField(ogr.FieldDefn("bar", ogr.OFTString))
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetFID(10)
|
|
f.SetField("foo", "fooval")
|
|
f.SetField("bar", "barval")
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("POLYGON ((0 0,0 1,1 1,0 0))"))
|
|
lyr.CreateFeature(f)
|
|
|
|
ds.ExecuteSQL(
|
|
"""CREATE TABLE gpkg_data_columns (
|
|
table_name TEXT NOT NULL,
|
|
column_name TEXT NOT NULL,
|
|
name TEXT,
|
|
title TEXT,
|
|
description TEXT,
|
|
mime_type TEXT,
|
|
constraint_name TEXT,
|
|
CONSTRAINT pk_gdc PRIMARY KEY (table_name, column_name),
|
|
CONSTRAINT gdc_tn UNIQUE (table_name, name)
|
|
)"""
|
|
)
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_data_columns VALUES('test', 'foo', 'constraint', NULL, NULL, NULL, NULL)"
|
|
)
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_extensions VALUES('test', 'foo', 'extension_name', 'definition', 'scope')"
|
|
)
|
|
ds.ExecuteSQL("CREATE INDEX my_idx_foo ON test(foo)")
|
|
ds.ExecuteSQL("CREATE INDEX my_idx_bar ON test(bar)")
|
|
|
|
assert lyr.TestCapability(ogr.OLCReorderFields) == 1
|
|
|
|
with gdaltest.error_handler():
|
|
ret = lyr.ReorderFields([-1, -1])
|
|
assert ret != 0
|
|
|
|
assert lyr.ReorderFields([1, 0]) == 0
|
|
|
|
lyr.ResetReading()
|
|
assert (
|
|
lyr.GetLayerDefn().GetFieldIndex("foo") == 1
|
|
and lyr.GetLayerDefn().GetFieldIndex("bar") == 0
|
|
)
|
|
f = lyr.GetNextFeature()
|
|
if (
|
|
f.GetFID() != 10
|
|
or f["foo"] != "fooval"
|
|
or f["bar"] != "barval"
|
|
or f.GetGeometryRef().ExportToWkt() != "POLYGON ((0 0,0 1,1 1,0 0))"
|
|
):
|
|
f.DumpReadable()
|
|
pytest.fail()
|
|
|
|
# Check that index has been recreated
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT * FROM sqlite_master WHERE name = 'my_idx_foo' OR name = 'my_idx_bar'"
|
|
)
|
|
assert sql_lyr.GetFeatureCount() == 2
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
|
|
ds = None
|
|
|
|
# Try on read-only dataset
|
|
ds = ogr.Open(dbname)
|
|
lyr = ds.GetLayer(0)
|
|
with gdaltest.error_handler():
|
|
ret = lyr.ReorderFields([1, 0])
|
|
assert ret != 0
|
|
ds = None
|
|
|
|
gdaltest.gpkg_dr.DeleteDataSource(dbname)
|
|
|
|
|
|
###############################################################################
|
|
# Test GetExtent() and RECOMPUTE EXTENT ON
|
|
|
|
|
|
def test_ogr_gpkg_38(options=["SPATIAL_INDEX=YES"]):
|
|
|
|
dbname = "/vsimem/ogr_gpkg_38.gpkg"
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(dbname)
|
|
lyr = ds.CreateLayer("test", geom_type=ogr.wkbLineString, options=options)
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("LINESTRING (1 2,3 4)"))
|
|
lyr.CreateFeature(f)
|
|
ds = None
|
|
|
|
# Simulate that extent is not recorded
|
|
ds = ogr.Open(dbname, update=1)
|
|
ds.ExecuteSQL(
|
|
"UPDATE gpkg_contents SET min_x = NULL, min_y = NULL, max_x = NULL, max_y = NULL"
|
|
)
|
|
ds = None
|
|
|
|
ds = ogr.Open(dbname, update=1)
|
|
lyr = ds.GetLayer(0)
|
|
extent = lyr.GetExtent(force=0, can_return_null=True)
|
|
assert extent is None
|
|
# Test that we can compute the extent of a layer that has none registered in gpkg_contents
|
|
extent = lyr.GetExtent(force=1)
|
|
assert extent == (1, 3, 2, 4)
|
|
sql_lyr = ds.ExecuteSQL("SELECT min_x, min_y, max_x, max_y FROM gpkg_contents")
|
|
f = sql_lyr.GetNextFeature()
|
|
if f["min_x"] != 1 or f["min_y"] != 2 or f["max_x"] != 3 or f["max_y"] != 4:
|
|
f.DumpReadable()
|
|
pytest.fail()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
extent = lyr.GetExtent(force=0)
|
|
assert extent == (1, 3, 2, 4)
|
|
|
|
# Modify feature
|
|
f = lyr.GetFeature(1)
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("LINESTRING (-1 -2,-3 -4)"))
|
|
lyr.SetFeature(f)
|
|
|
|
# The extent has grown
|
|
extent = lyr.GetExtent(force=0)
|
|
assert extent == (-3.0, 3.0, -4.0, 4.0)
|
|
|
|
ds.ExecuteSQL("RECOMPUTE EXTENT ON test")
|
|
extent = lyr.GetExtent(force=0)
|
|
assert extent == (-3.0, -1.0, -4.0, -2.0)
|
|
ds = None
|
|
|
|
ds = ogr.Open(dbname)
|
|
lyr = ds.GetLayer(0)
|
|
extent = lyr.GetExtent(force=0)
|
|
assert extent == (-3.0, -1.0, -4.0, -2.0)
|
|
ds = None
|
|
|
|
ds = ogr.Open(dbname, update=1)
|
|
lyr = ds.GetLayer(0)
|
|
# Delete last feature
|
|
lyr.DeleteFeature(1)
|
|
|
|
# This should cancel NULLify the extent in gpkg_contents
|
|
ds.ExecuteSQL("RECOMPUTE EXTENT ON test")
|
|
extent = lyr.GetExtent(force=0, can_return_null=True)
|
|
assert extent is None
|
|
ds = None
|
|
|
|
ds = ogr.Open(dbname)
|
|
lyr = ds.GetLayer(0)
|
|
extent = lyr.GetExtent(force=0, can_return_null=True)
|
|
assert extent is None
|
|
ds = None
|
|
|
|
gdaltest.gpkg_dr.DeleteDataSource(dbname)
|
|
|
|
|
|
def test_ogr_gpkg_38_nospi():
|
|
return test_ogr_gpkg_38(options=["SPATIAL_INDEX=NO"])
|
|
|
|
|
|
###############################################################################
|
|
# Test checking of IDENTIFIER unicity
|
|
|
|
|
|
def test_ogr_gpkg_39():
|
|
|
|
dbname = "/vsimem/ogr_gpkg_39.gpkg"
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(dbname)
|
|
|
|
ds.CreateLayer("test")
|
|
|
|
lyr = ds.CreateLayer(
|
|
"test_with_explicit_identifier", options=["IDENTIFIER=explicit_identifier"]
|
|
)
|
|
assert lyr is not None
|
|
|
|
# Allow overwriting
|
|
lyr = ds.CreateLayer(
|
|
"test_with_explicit_identifier",
|
|
options=["IDENTIFIER=explicit_identifier", "OVERWRITE=YES"],
|
|
)
|
|
assert lyr is not None
|
|
|
|
with gdaltest.error_handler():
|
|
lyr = ds.CreateLayer("test2", options=["IDENTIFIER=test"])
|
|
assert lyr is None
|
|
|
|
with gdaltest.error_handler():
|
|
lyr = ds.CreateLayer("test2", options=["IDENTIFIER=explicit_identifier"])
|
|
assert lyr is None
|
|
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_contents ( table_name, identifier, data_type ) VALUES ( 'some_table', 'another_identifier', 'some_data_type' )"
|
|
)
|
|
with gdaltest.error_handler():
|
|
lyr = ds.CreateLayer("test2", options=["IDENTIFIER=another_identifier"])
|
|
assert lyr is None
|
|
ds = None
|
|
|
|
gdaltest.gpkg_dr.DeleteDataSource(dbname)
|
|
|
|
|
|
###############################################################################
|
|
# Run creating a non-spatial layer that is registered as 'attributes' and
|
|
# read it back
|
|
|
|
|
|
def test_ogr_gpkg_40():
|
|
|
|
ds = gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpkg_40.gpkg")
|
|
ds.CreateLayer("aspatial", geom_type=ogr.wkbNone)
|
|
ds = None
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_40.gpkg")
|
|
assert ds.GetLayerCount() == 1
|
|
sql_lyr = ds.ExecuteSQL("SELECT * FROM gpkg_contents")
|
|
assert sql_lyr.GetFeatureCount() == 1
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
sql_lyr = ds.ExecuteSQL("SELECT * FROM gpkg_geometry_columns")
|
|
assert sql_lyr.GetFeatureCount() == 0
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT * FROM sqlite_master WHERE name = 'gpkg_extensions'"
|
|
)
|
|
assert sql_lyr.GetFeatureCount() == 0
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
ds = None
|
|
|
|
assert validate("/vsimem/ogr_gpkg_40.gpkg"), "validation failed"
|
|
|
|
gdaltest.gpkg_dr.DeleteDataSource("/vsimem/ogr_gpkg_40.gpkg")
|
|
|
|
|
|
###############################################################################
|
|
# Test tables without integer primary key (#6799), and unrecognized column type
|
|
|
|
|
|
def test_ogr_gpkg_41():
|
|
|
|
ds = gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpkg_41.gpkg")
|
|
ds.ExecuteSQL("CREATE TABLE foo (mycol VARCHAR_ILLEGAL)")
|
|
ds.ExecuteSQL("INSERT INTO foo VALUES ('myval')")
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_contents (table_name,data_type,identifier,description,last_change,srs_id) VALUES ('foo','attributes','foo','','',0)"
|
|
)
|
|
ds = None
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_41.gpkg")
|
|
lyr = ds.GetLayer(0)
|
|
with gdaltest.error_handler():
|
|
f = lyr.GetNextFeature()
|
|
if f["mycol"] != "myval" or f.GetFID() != 1:
|
|
f.DumpReadable()
|
|
pytest.fail()
|
|
ds = None
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_41.gpkg")
|
|
lyr = ds.GetLayer(0)
|
|
with gdaltest.error_handler():
|
|
f = lyr.GetFeature(1)
|
|
if f["mycol"] != "myval" or f.GetFID() != 1:
|
|
f.DumpReadable()
|
|
pytest.fail()
|
|
ds = None
|
|
|
|
gdaltest.gpkg_dr.DeleteDataSource("/vsimem/ogr_gpkg_41.gpkg")
|
|
|
|
|
|
###############################################################################
|
|
# Test feature_count
|
|
|
|
|
|
def foo_has_trigger(ds):
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT COUNT(*) FROM sqlite_master WHERE name = 'trigger_insert_feature_count_foo'",
|
|
dialect="DEBUG",
|
|
)
|
|
f = sql_lyr.GetNextFeature()
|
|
has_trigger = f.GetField(0) == 1
|
|
f = None
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
return has_trigger
|
|
|
|
|
|
def get_feature_count_from_gpkg_contents(ds):
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT feature_count FROM gpkg_ogr_contents", dialect="DEBUG"
|
|
)
|
|
f = sql_lyr.GetNextFeature()
|
|
val = f.GetField(0)
|
|
f = None
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
return val
|
|
|
|
|
|
def test_ogr_gpkg_42():
|
|
|
|
ds = gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpkg_42.gpkg")
|
|
lyr = ds.CreateLayer("foo", geom_type=ogr.wkbNone)
|
|
lyr.CreateField(ogr.FieldDefn("i", ogr.OFTInteger))
|
|
for i in range(5):
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetField(0, i)
|
|
lyr.CreateFeature(f)
|
|
ds = None
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_42.gpkg")
|
|
lyr = ds.GetLayer(0)
|
|
assert get_feature_count_from_gpkg_contents(ds) == 5
|
|
assert foo_has_trigger(ds)
|
|
assert lyr.TestCapability(ogr.OLCFastFeatureCount) != 0
|
|
ds = None
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_42.gpkg", update=1)
|
|
lyr = ds.GetLayer(0)
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetField(0, 10)
|
|
lyr.CreateFeature(f)
|
|
|
|
# Has been invalidated for now
|
|
assert get_feature_count_from_gpkg_contents(ds) is None
|
|
|
|
assert not foo_has_trigger(ds)
|
|
|
|
fc = lyr.GetFeatureCount()
|
|
assert fc == 6
|
|
|
|
ds.ExecuteSQL("DELETE FROM foo WHERE i = 1")
|
|
|
|
assert foo_has_trigger(ds)
|
|
|
|
assert get_feature_count_from_gpkg_contents(ds) == 5
|
|
|
|
fc = lyr.GetFeatureCount()
|
|
assert fc == 5
|
|
|
|
assert get_feature_count_from_gpkg_contents(ds) == 5
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
assert lyr.CreateFeature(f) == ogr.OGRERR_NONE
|
|
assert get_feature_count_from_gpkg_contents(ds) is None
|
|
assert lyr.SyncToDisk() == ogr.OGRERR_NONE
|
|
|
|
assert get_feature_count_from_gpkg_contents(ds) == 6
|
|
|
|
assert lyr.DeleteFeature(f.GetFID()) == ogr.OGRERR_NONE
|
|
assert get_feature_count_from_gpkg_contents(ds) is None
|
|
|
|
ds.ExecuteSQL("INSERT OR REPLACE INTO foo (fid) VALUES (1)")
|
|
|
|
assert get_feature_count_from_gpkg_contents(ds) == 5
|
|
|
|
ds = None
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_42.gpkg", update=1)
|
|
lyr = ds.GetLayer(0)
|
|
fc = lyr.GetFeatureCount()
|
|
assert fc == 5
|
|
ds.ExecuteSQL("UPDATE gpkg_ogr_contents SET feature_count = NULL")
|
|
ds = None
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_42.gpkg", update=1)
|
|
lyr = ds.GetLayer(0)
|
|
assert get_feature_count_from_gpkg_contents(ds) is None
|
|
fc = lyr.GetFeatureCount()
|
|
assert fc == 5
|
|
ds = None
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_42.gpkg", update=1)
|
|
assert get_feature_count_from_gpkg_contents(ds) == 5
|
|
|
|
# So as to test that we really read from gpkg_ogr_contents
|
|
ds.ExecuteSQL("UPDATE gpkg_ogr_contents SET feature_count = 5000")
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_42.gpkg", update=1)
|
|
lyr = ds.GetLayer(0)
|
|
fc = lyr.GetFeatureCount()
|
|
assert fc == 5000
|
|
|
|
# Test renaming
|
|
assert lyr.TestCapability(ogr.OLCRename) == 1
|
|
assert lyr.Rename("bar") == ogr.OGRERR_NONE
|
|
assert lyr.GetDescription() == "bar"
|
|
assert lyr.GetLayerDefn().GetName() == "bar"
|
|
with gdaltest.error_handler():
|
|
assert lyr.Rename("bar") != ogr.OGRERR_NONE
|
|
with gdaltest.error_handler():
|
|
assert lyr.Rename("gpkg_ogr_contents") != ogr.OGRERR_NONE
|
|
assert lyr.GetDescription() == "bar"
|
|
assert lyr.GetLayerDefn().GetName() == "bar"
|
|
|
|
ds = None
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_42.gpkg", update=1)
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT feature_count FROM gpkg_ogr_contents WHERE table_name = 'bar'",
|
|
dialect="DEBUG",
|
|
)
|
|
f = sql_lyr.GetNextFeature()
|
|
val = f.GetField(0)
|
|
f = None
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
assert val == 5000
|
|
|
|
# Test layer deletion
|
|
ds.DeleteLayer(0)
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT feature_count FROM gpkg_ogr_contents", dialect="DEBUG"
|
|
)
|
|
f = sql_lyr.GetNextFeature()
|
|
assert f is None
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
ds = None
|
|
|
|
# Test without feature_count column
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(
|
|
"/vsimem/ogr_gpkg_42.gpkg", options=["ADD_GPKG_OGR_CONTENTS=FALSE"]
|
|
)
|
|
lyr = ds.CreateLayer("foo", geom_type=ogr.wkbNone)
|
|
lyr.CreateField(ogr.FieldDefn("i", ogr.OFTInteger))
|
|
for i in range(5):
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetField(0, i)
|
|
lyr.CreateFeature(f)
|
|
ds = None
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_42.gpkg", update=1)
|
|
|
|
# Check that feature_count column is missing
|
|
sql_lyr = ds.ExecuteSQL("PRAGMA table_info(gpkg_contents)")
|
|
fc = sql_lyr.GetFeatureCount()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
assert fc == 10
|
|
|
|
assert not foo_has_trigger(ds)
|
|
|
|
lyr = ds.GetLayer(0)
|
|
assert lyr.TestCapability(ogr.OLCFastFeatureCount) == 0
|
|
fc = lyr.GetFeatureCount()
|
|
assert fc == 5
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetField(0, 10)
|
|
lyr.CreateFeature(f)
|
|
lyr = ds.GetLayer(0)
|
|
fc = lyr.GetFeatureCount()
|
|
assert fc == 6
|
|
ds.ExecuteSQL("DELETE FROM foo WHERE i = 1")
|
|
fc = lyr.GetFeatureCount()
|
|
assert fc == 5
|
|
ds = None
|
|
|
|
gdaltest.gpkg_dr.DeleteDataSource("/vsimem/ogr_gpkg_42.gpkg")
|
|
|
|
|
|
###############################################################################
|
|
# Test limitations on number of tables
|
|
|
|
|
|
def test_ogr_gpkg_43():
|
|
|
|
ds = gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpkg_43.gpkg")
|
|
ds.StartTransaction()
|
|
for i in range(1001):
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_contents (table_name, data_type, identifier) "
|
|
+ "VALUES ('tiles%d', 'tiles', 'tiles%d')" % (i + 1, i + 1)
|
|
)
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_tile_matrix_set VALUES "
|
|
+ "('tiles%d', 0, 440720, 3750120, 441920, 3751320)" % (i + 1)
|
|
)
|
|
for i in range(1001):
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_contents (table_name, data_type, identifier) "
|
|
+ "VALUES ('attr%d', 'attributes', 'attr%d')" % (i + 1, i + 1)
|
|
)
|
|
ds.ExecuteSQL(
|
|
"CREATE TABLE attr%d (id INTEGER PRIMARY KEY AUTOINCREMENT)" % (i + 1)
|
|
)
|
|
ds.CommitTransaction()
|
|
ds = None
|
|
|
|
ds = gdal.OpenEx("/vsimem/ogr_gpkg_43.gpkg")
|
|
assert len(ds.GetMetadata_List("SUBDATASETS")) == 2 * 1001
|
|
assert ds.GetLayerCount() == 1001
|
|
|
|
with gdaltest.config_option("OGR_TABLE_LIMIT", "1000"):
|
|
with gdaltest.error_handler():
|
|
ds = gdal.OpenEx("/vsimem/ogr_gpkg_43.gpkg")
|
|
assert len(ds.GetMetadata_List("SUBDATASETS")) == 2 * 1000
|
|
assert ds.GetLayerCount() == 1000
|
|
ds = None
|
|
|
|
gdaltest.gpkg_dr.DeleteDataSource("/vsimem/ogr_gpkg_43.gpkg")
|
|
|
|
|
|
###############################################################################
|
|
# Test GeoPackage without metadata table
|
|
|
|
|
|
def test_ogr_gpkg_44():
|
|
|
|
with gdal.config_option("CREATE_METADATA_TABLES", "NO"):
|
|
ds = gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpkg_44.gpkg")
|
|
ds.CreateLayer("foo")
|
|
ds = None
|
|
|
|
assert validate("/vsimem/ogr_gpkg_44.gpkg"), "validation failed"
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_44.gpkg")
|
|
md = ds.GetMetadata()
|
|
assert md == {}
|
|
md = ds.GetLayer(0).GetMetadata()
|
|
assert md == {}
|
|
sql_lyr = ds.ExecuteSQL("SELECT * FROM sqlite_master WHERE name = 'gpkg_metadata'")
|
|
fc = sql_lyr.GetFeatureCount()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
assert fc == 0
|
|
ds = None
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_44.gpkg", update=1)
|
|
ds.SetMetadataItem("FOO", "BAR")
|
|
ds = None
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_44.gpkg")
|
|
md = ds.GetMetadata()
|
|
assert md == {"FOO": "BAR"}
|
|
ds = None
|
|
|
|
gdaltest.gpkg_dr.DeleteDataSource("/vsimem/ogr_gpkg_44.gpkg")
|
|
|
|
|
|
###############################################################################
|
|
# Test non conformant GeoPackage: table with non INTEGER PRIMARY KEY
|
|
|
|
|
|
def test_ogr_gpkg_45():
|
|
|
|
ds = gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpkg_45.gpkg")
|
|
ds.ExecuteSQL(
|
|
"CREATE TABLE test (a INTEGER, b INTEGER, CONSTRAINT pkid_constraint PRIMARY KEY (a, b))"
|
|
)
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_contents ( table_name, identifier, data_type ) VALUES ( 'test', 'test', 'attributes' )"
|
|
)
|
|
ds = None
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_45.gpkg")
|
|
lyr = ds.GetLayer(0)
|
|
assert lyr.GetFIDColumn() == ""
|
|
assert lyr.GetLayerDefn().GetFieldCount() == 2
|
|
ds = None
|
|
|
|
gdaltest.gpkg_dr.DeleteDataSource("/vsimem/ogr_gpkg_45.gpkg")
|
|
|
|
|
|
###############################################################################
|
|
# Test spatial view and spatial index
|
|
|
|
|
|
def test_ogr_gpkg_46():
|
|
|
|
ds = gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpkg_46.gpkg")
|
|
lyr = ds.CreateLayer("foo")
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT(0 0)"))
|
|
lyr.CreateFeature(f)
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT(1 1)"))
|
|
lyr.CreateFeature(f)
|
|
# Note: this definition of a view is non conformant with GPKG 1.3 clarifications on views
|
|
ds.ExecuteSQL(
|
|
"CREATE VIEW my_view AS SELECT geom AS my_geom, fid AS my_fid FROM foo"
|
|
)
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_contents (table_name, identifier, data_type, srs_id) VALUES ( 'my_view', 'my_view', 'features', 0 )"
|
|
)
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_geometry_columns (table_name, column_name, geometry_type_name, srs_id, z, m) values ('my_view', 'my_geom', 'GEOMETRY', 0, 0, 0)"
|
|
)
|
|
|
|
# Note: this definition of a view is non conformant with GPKG 1.3 clarifications on views
|
|
ds.ExecuteSQL(
|
|
"CREATE VIEW my_view2 AS SELECT geom, fid AS OGC_FID, 'bla' as another_column FROM foo"
|
|
)
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_contents (table_name, identifier, data_type, srs_id) VALUES ( 'my_view2', 'my_view2', 'features', 0 )"
|
|
)
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_geometry_columns (table_name, column_name, geometry_type_name, srs_id, z, m) values ('my_view2', 'geom', 'GEOMETRY', 0, 0, 0)"
|
|
)
|
|
|
|
ds.ExecuteSQL(
|
|
"CREATE VIEW my_view3 AS SELECT a.fid * 10000 + b.fid as my_fid, a.fid as fid1, a.geom, b.fid as fid2 FROM foo a, foo b"
|
|
)
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_contents (table_name, identifier, data_type, srs_id) VALUES ( 'my_view3', 'my_view3', 'features', 0 )"
|
|
)
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_geometry_columns (table_name, column_name, geometry_type_name, srs_id, z, m) values ('my_view3', 'geom', 'GEOMETRY', 0, 0, 0)"
|
|
)
|
|
|
|
ds = None
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_46.gpkg", update=1)
|
|
lyr = ds.GetLayerByName("my_view")
|
|
assert lyr.GetLayerDefn().GetFieldCount() == 1
|
|
assert lyr.GetGeometryColumn() == "my_geom"
|
|
|
|
# Operations not valid on a view
|
|
with gdaltest.error_handler():
|
|
ds.ReleaseResultSet(
|
|
ds.ExecuteSQL("SELECT CreateSpatialIndex('my_view', 'my_geom')")
|
|
)
|
|
ds.ReleaseResultSet(
|
|
ds.ExecuteSQL("SELECT DisableSpatialIndex('my_view', 'my_geom')")
|
|
)
|
|
lyr.AlterFieldDefn(0, lyr.GetLayerDefn().GetFieldDefn(0), ogr.ALTER_ALL_FLAG)
|
|
lyr.DeleteField(0)
|
|
lyr.ReorderFields([0])
|
|
lyr.CreateField(ogr.FieldDefn("bar"))
|
|
|
|
# Check if spatial index is recognized
|
|
sql_lyr = ds.ExecuteSQL("SELECT HasSpatialIndex('my_view', 'my_geom')")
|
|
f = sql_lyr.GetNextFeature()
|
|
has_spatial_index = f.GetField(0) == 1
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
if not has_spatial_index:
|
|
ds = None
|
|
gdaltest.gpkg_dr.DeleteDataSource("/vsimem/ogr_gpkg_46.gpkg")
|
|
pytest.skip("SQLite likely built without SQLITE_HAS_COLUMN_METADATA")
|
|
|
|
# Effectively test spatial index
|
|
lyr.SetSpatialFilterRect(-0.5, -0.5, 0.5, 0.5)
|
|
assert lyr.GetFeatureCount() == 1
|
|
f = lyr.GetNextFeature()
|
|
assert f is not None
|
|
f = lyr.GetNextFeature()
|
|
assert f is None
|
|
|
|
# View with FID in non-first position
|
|
lyr = ds.GetLayerByName("my_view2")
|
|
assert lyr.GetLayerDefn().GetFieldCount() == 1
|
|
assert lyr.GetFIDColumn() == "OGC_FID"
|
|
f = lyr.GetNextFeature()
|
|
if f.GetFID() != 1 or f.GetField(0) != "bla":
|
|
f.DumpReadable()
|
|
pytest.fail()
|
|
|
|
# View with FID in first position
|
|
lyr = ds.GetLayerByName("my_view3")
|
|
assert lyr.GetLayerDefn().GetFieldCount() == 2
|
|
f = lyr.GetNextFeature()
|
|
assert f.GetFID() == 10001
|
|
f = lyr.GetNextFeature()
|
|
assert f.GetFID() == 10002
|
|
f2 = lyr.GetFeature(10002)
|
|
assert f.Equal(f2)
|
|
ds = None
|
|
|
|
gdaltest.gpkg_dr.DeleteDataSource("/vsimem/ogr_gpkg_46.gpkg")
|
|
|
|
|
|
###############################################################################
|
|
# Test corner case of Identify()
|
|
|
|
|
|
def test_ogr_gpkg_47():
|
|
|
|
gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpkg_47.gpkg")
|
|
# Set wrong application_id
|
|
fp = gdal.VSIFOpenL("/vsimem/ogr_gpkg_47.gpkg", "rb+")
|
|
gdal.VSIFSeekL(fp, 68, 0)
|
|
gdal.VSIFWriteL(struct.pack("B" * 4, 0, 0, 0, 0), 4, 1, fp)
|
|
gdal.VSIFCloseL(fp)
|
|
|
|
gdal.ErrorReset()
|
|
with gdaltest.error_handler():
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_47.gpkg", update=1)
|
|
assert ds is not None
|
|
assert gdal.GetLastErrorMsg() != ""
|
|
|
|
gdal.ErrorReset()
|
|
with gdal.config_option("GPKG_WARN_UNRECOGNIZED_APPLICATION_ID", "NO"):
|
|
ogr.Open("/vsimem/ogr_gpkg_47.gpkg")
|
|
assert gdal.GetLastErrorMsg() == ""
|
|
|
|
gdaltest.gpkg_dr.CreateDataSource(
|
|
"/vsimem/ogr_gpkg_47.gpkg", options=["VERSION=1.2"]
|
|
)
|
|
# Set wrong user_version
|
|
fp = gdal.VSIFOpenL("/vsimem/ogr_gpkg_47.gpkg", "rb+")
|
|
gdal.VSIFSeekL(fp, 60, 0)
|
|
gdal.VSIFWriteL(struct.pack("B" * 4, 0, 0, 0, 0), 4, 1, fp)
|
|
gdal.VSIFCloseL(fp)
|
|
|
|
gdal.ErrorReset()
|
|
with gdaltest.error_handler():
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_47.gpkg", update=1)
|
|
assert ds is not None
|
|
assert gdal.GetLastErrorMsg() != ""
|
|
ds = None
|
|
|
|
gdal.ErrorReset()
|
|
with gdal.config_option("GPKG_WARN_UNRECOGNIZED_APPLICATION_ID", "NO"):
|
|
ogr.Open("/vsimem/ogr_gpkg_47.gpkg")
|
|
assert gdal.GetLastErrorMsg() == ""
|
|
|
|
# Set GPKG 1.2.1
|
|
gdaltest.gpkg_dr.CreateDataSource(
|
|
"/vsimem/ogr_gpkg_47.gpkg", options=["VERSION=1.2"]
|
|
)
|
|
# Set user_version
|
|
fp = gdal.VSIFOpenL("/vsimem/ogr_gpkg_47.gpkg", "rb+")
|
|
gdal.VSIFSeekL(fp, 60, 0)
|
|
assert struct.unpack(">I", gdal.VSIFReadL(4, 1, fp))[0] == 10200
|
|
gdal.VSIFSeekL(fp, 60, 0)
|
|
gdal.VSIFWriteL(struct.pack(">I", 10201), 4, 1, fp)
|
|
gdal.VSIFCloseL(fp)
|
|
|
|
gdal.ErrorReset()
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_47.gpkg", update=1)
|
|
assert ds is not None
|
|
assert gdal.GetLastErrorMsg() == ""
|
|
ds = None
|
|
|
|
gdal.ErrorReset()
|
|
with gdal.config_option("GPKG_WARN_UNRECOGNIZED_APPLICATION_ID", "NO"):
|
|
ogr.Open("/vsimem/ogr_gpkg_47.gpkg")
|
|
assert gdal.GetLastErrorMsg() == ""
|
|
|
|
# Set GPKG 1.3.0
|
|
gdaltest.gpkg_dr.CreateDataSource(
|
|
"/vsimem/ogr_gpkg_47.gpkg", options=["VERSION=1.3"]
|
|
)
|
|
# Check user_version
|
|
fp = gdal.VSIFOpenL("/vsimem/ogr_gpkg_47.gpkg", "rb")
|
|
gdal.VSIFSeekL(fp, 60, 0)
|
|
assert struct.unpack(">I", gdal.VSIFReadL(4, 1, fp))[0] == 10300
|
|
gdal.VSIFCloseL(fp)
|
|
|
|
gdal.ErrorReset()
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_47.gpkg", update=1)
|
|
assert ds is not None
|
|
assert gdal.GetLastErrorMsg() == ""
|
|
ds = None
|
|
|
|
gdal.ErrorReset()
|
|
with gdal.config_option("GPKG_WARN_UNRECOGNIZED_APPLICATION_ID", "NO"):
|
|
ogr.Open("/vsimem/ogr_gpkg_47.gpkg")
|
|
assert gdal.GetLastErrorMsg() == ""
|
|
|
|
# Set GPKG 1.99.0
|
|
gdaltest.gpkg_dr.CreateDataSource(
|
|
"/vsimem/ogr_gpkg_47.gpkg", options=["VERSION=1.2"]
|
|
)
|
|
# Set user_version
|
|
fp = gdal.VSIFOpenL("/vsimem/ogr_gpkg_47.gpkg", "rb+")
|
|
gdal.VSIFSeekL(fp, 60, 0)
|
|
gdal.VSIFWriteL(struct.pack(">I", 19900), 4, 1, fp)
|
|
gdal.VSIFCloseL(fp)
|
|
|
|
gdal.ErrorReset()
|
|
with gdaltest.error_handler():
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_47.gpkg", update=1)
|
|
assert ds is not None
|
|
assert gdal.GetLastErrorMsg() != ""
|
|
|
|
gdal.ErrorReset()
|
|
with gdal.config_option("GPKG_WARN_UNRECOGNIZED_APPLICATION_ID", "NO"):
|
|
ogr.Open("/vsimem/ogr_gpkg_47.gpkg")
|
|
assert gdal.GetLastErrorMsg() == ""
|
|
|
|
# Just for the sake of coverage testing in DEBUG mode
|
|
with gdaltest.error_handler():
|
|
gdaltest.gpkg_dr.CreateDataSource("/vsimem/.cur_input")
|
|
# Set wrong application_id
|
|
fp = gdal.VSIFOpenL("/vsimem/.cur_input", "rb+")
|
|
gdal.VSIFSeekL(fp, 68, 0)
|
|
gdal.VSIFWriteL(struct.pack("B" * 4, 0, 0, 0, 0), 4, 1, fp)
|
|
gdal.VSIFCloseL(fp)
|
|
ogr.Open("/vsimem/.cur_input")
|
|
gdal.Unlink("/vsimem/.cur_input")
|
|
|
|
with gdaltest.error_handler():
|
|
gdaltest.gpkg_dr.CreateDataSource("/vsimem/.cur_input", options=["VERSION=1.2"])
|
|
# Set wrong user_version
|
|
fp = gdal.VSIFOpenL("/vsimem/.cur_input", "rb+")
|
|
gdal.VSIFSeekL(fp, 60, 0)
|
|
gdal.VSIFWriteL(struct.pack("B" * 4, 0, 0, 0, 0), 4, 1, fp)
|
|
gdal.VSIFCloseL(fp)
|
|
ogr.Open("/vsimem/.cur_input")
|
|
gdal.Unlink("/vsimem/.cur_input")
|
|
|
|
# Test reading in a zip
|
|
ds = gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpkg_47.gpkg")
|
|
ds.CreateLayer("foo")
|
|
ds = None
|
|
fp = gdal.VSIFOpenL("/vsimem/ogr_gpkg_47.gpkg", "rb")
|
|
content = gdal.VSIFReadL(1, 1000000, fp)
|
|
gdal.VSIFCloseL(fp)
|
|
fzip = gdal.VSIFOpenL("/vsizip//vsimem/ogr_gpkg_47.zip", "wb")
|
|
fp = gdal.VSIFOpenL("/vsizip//vsimem/ogr_gpkg_47.zip/my.gpkg", "wb")
|
|
gdal.VSIFWriteL(content, 1, len(content), fp)
|
|
gdal.VSIFCloseL(fp)
|
|
gdal.VSIFCloseL(fzip)
|
|
ds = ogr.Open("/vsizip//vsimem/ogr_gpkg_47.zip")
|
|
assert ds.GetDriver().GetName() == "GPKG"
|
|
ds = None
|
|
|
|
gdal.Unlink("/vsimem/ogr_gpkg_47.zip")
|
|
gdaltest.gpkg_dr.DeleteDataSource("/vsimem/ogr_gpkg_47.gpkg")
|
|
|
|
|
|
###############################################################################
|
|
# Test insertion of features with unset fields
|
|
|
|
|
|
def test_ogr_gpkg_48():
|
|
|
|
ds = gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpkg_48.gpkg")
|
|
lyr = ds.CreateLayer("foo")
|
|
lyr.CreateField(ogr.FieldDefn("a"))
|
|
lyr.CreateField(ogr.FieldDefn("b"))
|
|
lyr.CreateField(ogr.FieldDefn("c"))
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetField("a", "a")
|
|
lyr.CreateFeature(f)
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetField("b", "b")
|
|
f.SetField("c", "c")
|
|
lyr.CreateFeature(f)
|
|
lyr.ResetReading()
|
|
f = lyr.GetNextFeature()
|
|
if f.GetField("a") != "a" or f.GetField("b") is not None:
|
|
f.DumpReadable()
|
|
pytest.fail()
|
|
f = lyr.GetNextFeature()
|
|
if f.GetField("b") != "b" or f.GetField("c") != "c" or f.GetField("a") is not None:
|
|
f.DumpReadable()
|
|
pytest.fail()
|
|
|
|
# No geom field, one single field with default value
|
|
lyr = ds.CreateLayer("default_field_no_geom", geom_type=ogr.wkbNone)
|
|
fld_defn = ogr.FieldDefn("foo")
|
|
fld_defn.SetDefault("'x'")
|
|
lyr.CreateField(fld_defn)
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
assert lyr.CreateFeature(f) == 0
|
|
lyr.ResetReading()
|
|
f = lyr.GetNextFeature()
|
|
if f.GetField("foo") != "x":
|
|
f.DumpReadable()
|
|
pytest.fail()
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetFID(1)
|
|
assert lyr.SetFeature(f) == 0
|
|
lyr.ResetReading()
|
|
f = lyr.GetNextFeature()
|
|
if f.GetField("foo") != "x":
|
|
f.DumpReadable()
|
|
pytest.fail()
|
|
ds = None
|
|
|
|
gdaltest.gpkg_dr.DeleteDataSource("/vsimem/ogr_gpkg_48.gpkg")
|
|
|
|
|
|
###############################################################################
|
|
# Test CreateGeomField() on a attributes layer
|
|
|
|
|
|
def test_ogr_gpkg_49():
|
|
|
|
ds = gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpkg_49.gpkg")
|
|
|
|
lyr = ds.CreateLayer(
|
|
"test", geom_type=ogr.wkbNone, options=["ASPATIAL_VARIANT=GPKG_ATTRIBUTES"]
|
|
)
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
lyr.CreateFeature(f)
|
|
f = None
|
|
|
|
field_defn = ogr.GeomFieldDefn("", ogr.wkbPoint)
|
|
assert lyr.CreateGeomField(field_defn) == 0
|
|
ds = None
|
|
|
|
gdaltest.gpkg_dr.DeleteDataSource("/vsimem/ogr_gpkg_49.gpkg")
|
|
|
|
|
|
###############################################################################
|
|
# Test minimalistic support of definition_12_063
|
|
|
|
|
|
def test_ogr_gpkg_50():
|
|
|
|
with gdal.config_option("GPKG_ADD_DEFINITION_12_063", "YES"):
|
|
gdaltest.gpkg_dr.CreateDataSource("/vsimem/ogr_gpkg_50.gpkg")
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_50.gpkg", update=1)
|
|
srs32631 = osr.SpatialReference()
|
|
srs32631.ImportFromEPSG(32631)
|
|
ds.CreateLayer("test", srs=srs32631)
|
|
|
|
# No authority node
|
|
srs_without_org = osr.SpatialReference()
|
|
srs_without_org.SetFromUserInput(
|
|
"""GEOGCS["another geogcs",
|
|
DATUM["another datum",
|
|
SPHEROID["another spheroid",1000,0]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433]]"""
|
|
)
|
|
lyr = ds.CreateLayer("without_org", srs=srs_without_org)
|
|
|
|
ds = None
|
|
|
|
assert validate("/vsimem/ogr_gpkg_50.gpkg"), "validation failed"
|
|
|
|
ds = ogr.Open("/vsimem/ogr_gpkg_50.gpkg")
|
|
lyr = ds.GetLayer("test")
|
|
assert lyr.GetSpatialRef().IsSame(srs32631)
|
|
lyr = ds.GetLayer("without_org")
|
|
assert lyr.GetSpatialRef().IsSame(srs_without_org)
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT definition_12_063 FROM gpkg_spatial_ref_sys WHERE srs_id = 32631"
|
|
)
|
|
f = sql_lyr.GetNextFeature()
|
|
assert f.GetField(0).startswith('PROJCRS["WGS 84 / UTM zone 31N"')
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
ds = None
|
|
|
|
gdaltest.gpkg_dr.DeleteDataSource("/vsimem/ogr_gpkg_50.gpkg")
|
|
|
|
|
|
###############################################################################
|
|
# Test opening a .gpkg.sql file
|
|
|
|
|
|
def test_ogr_gpkg_51():
|
|
|
|
if gdaltest.gpkg_dr.GetMetadataItem("ENABLE_SQL_GPKG_FORMAT") != "YES":
|
|
pytest.skip()
|
|
|
|
ds = ogr.Open("data/gpkg/poly.gpkg.sql")
|
|
lyr = ds.GetLayer(0)
|
|
f = lyr.GetNextFeature()
|
|
assert f is not None
|
|
|
|
|
|
###############################################################################
|
|
# Test opening a .gpkg file
|
|
|
|
|
|
def test_ogr_gpkg_52():
|
|
|
|
ds = ogr.Open("data/gpkg/poly_non_conformant.gpkg")
|
|
lyr = ds.GetLayer(0)
|
|
with gdaltest.error_handler():
|
|
f = lyr.GetNextFeature()
|
|
assert f is not None
|
|
|
|
|
|
###############################################################################
|
|
# Test opening a .gpkg file with inconsistency regarding table case (#6916)
|
|
|
|
|
|
def test_ogr_gpkg_53():
|
|
|
|
if gdaltest.gpkg_dr.GetMetadataItem("ENABLE_SQL_GPKG_FORMAT") != "YES":
|
|
pytest.skip()
|
|
|
|
ds = ogr.Open("data/gpkg/poly_inconsistent_case.gpkg.sql")
|
|
assert ds.GetLayerCount() == 1
|
|
lyr = ds.GetLayer(0)
|
|
f = lyr.GetNextFeature()
|
|
assert f is not None
|
|
|
|
import test_cli_utilities
|
|
|
|
if test_cli_utilities.get_test_ogrsf_path() is not None:
|
|
ret = gdaltest.runexternal(
|
|
test_cli_utilities.get_test_ogrsf_path()
|
|
+ " data/gpkg/poly_inconsistent_case.gpkg.sql"
|
|
)
|
|
|
|
assert ret.find("INFO") != -1 and ret.find("ERROR") == -1
|
|
|
|
|
|
###############################################################################
|
|
# Test editing of a database with 2 layers (https://issues.qgis.org/issues/17034)
|
|
|
|
|
|
def test_ogr_gpkg_54():
|
|
|
|
# Must be on a real file system to demonstrate potential locking
|
|
# issue
|
|
tmpfile = "tmp/ogr_gpkg_54.gpkg"
|
|
ds = ogr.GetDriverByName("GPKG").CreateDataSource(tmpfile)
|
|
lyr = ds.CreateLayer("layer1", geom_type=ogr.wkbPoint)
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT(0 0)"))
|
|
lyr.CreateFeature(f)
|
|
f = None
|
|
lyr = ds.CreateLayer("layer2", geom_type=ogr.wkbPoint)
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT(1 1)"))
|
|
lyr.CreateFeature(f)
|
|
f = None
|
|
ds = None
|
|
|
|
ds1 = ogr.Open(tmpfile, update=1)
|
|
ds2 = ogr.Open(tmpfile, update=1)
|
|
|
|
lyr1 = ds1.GetLayer(0)
|
|
lyr2 = ds2.GetLayer(1)
|
|
|
|
f1 = lyr1.GetFeature(1)
|
|
f1.SetGeometry(ogr.CreateGeometryFromWkt("POINT (1 2)"))
|
|
lyr1.SetFeature(f1)
|
|
|
|
f2 = lyr2.GetFeature(1)
|
|
f2.SetGeometry(ogr.CreateGeometryFromWkt("POINT (3 4)"))
|
|
lyr2.SetFeature(f2)
|
|
|
|
f1 = lyr1.GetFeature(1)
|
|
f1.SetGeometry(ogr.CreateGeometryFromWkt("POINT (5 6)"))
|
|
lyr1.SetFeature(f1)
|
|
|
|
f2 = lyr2.GetFeature(1)
|
|
f2.SetGeometry(ogr.CreateGeometryFromWkt("POINT (7 8)"))
|
|
lyr2.SetFeature(f2)
|
|
|
|
ds1 = None
|
|
ds2 = None
|
|
|
|
ds = ogr.Open(tmpfile)
|
|
lyr1 = ds.GetLayer(0)
|
|
f = lyr1.GetNextFeature()
|
|
if f.GetGeometryRef().ExportToWkt() != "POINT (5 6)":
|
|
f.DumpReadable()
|
|
pytest.fail()
|
|
lyr2 = ds.GetLayer(1)
|
|
f = lyr2.GetNextFeature()
|
|
if f.GetGeometryRef().ExportToWkt() != "POINT (7 8)":
|
|
f.DumpReadable()
|
|
pytest.fail()
|
|
ds = None
|
|
|
|
gdal.Unlink(tmpfile)
|
|
|
|
|
|
###############################################################################
|
|
# Test inserting geometries incompatible with declared layer geometry type
|
|
|
|
|
|
def test_ogr_gpkg_55():
|
|
|
|
tmpfile = "/vsimem/ogr_gpkg_55.gpkg"
|
|
ds = ogr.GetDriverByName("GPKG").CreateDataSource(tmpfile)
|
|
lyr = ds.CreateLayer("layer1", geom_type=ogr.wkbLineString)
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT(0 0)"))
|
|
gdal.ErrorReset()
|
|
with gdaltest.error_handler():
|
|
lyr.CreateFeature(f)
|
|
assert gdal.GetLastErrorMsg() != "", "should have warned"
|
|
f = None
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT(1 1)"))
|
|
gdal.ErrorReset()
|
|
lyr.CreateFeature(f)
|
|
assert gdal.GetLastErrorMsg() == "", "should NOT have warned"
|
|
f = None
|
|
ds = None
|
|
|
|
gdal.Unlink(tmpfile)
|
|
|
|
|
|
###############################################################################
|
|
# Test FID identification on SQL result layer
|
|
|
|
|
|
def test_ogr_gpkg_56():
|
|
|
|
ds = gdal.VectorTranslate(
|
|
"/vsimem/ogr_gpkg_56.gpkg", "data/poly.shp", format="GPKG"
|
|
)
|
|
lyr = ds.ExecuteSQL(
|
|
"select a.fid as fid1, b.fid as fid2 from poly a, poly b order by fid1, fid2"
|
|
)
|
|
lyr.GetNextFeature()
|
|
f = lyr.GetNextFeature()
|
|
if f.GetField("fid1") != 1 or f.GetField("fid2") != 2:
|
|
f.DumpReadable()
|
|
pytest.fail()
|
|
ds.ReleaseResultSet(lyr)
|
|
ds = None
|
|
gdal.Unlink("/vsimem/ogr_gpkg_56.gpkg")
|
|
|
|
|
|
###############################################################################
|
|
# Test creation of a field which is the same as the FID column
|
|
|
|
|
|
def test_ogr_gpkg_creation_fid():
|
|
|
|
filename = "/vsimem/test_ogr_gpkg_creation_fid.gpkg"
|
|
ds = ogr.GetDriverByName("GPKG").CreateDataSource(filename)
|
|
|
|
lyr = ds.CreateLayer("fid_integer")
|
|
assert lyr.CreateField(ogr.FieldDefn("fid", ogr.OFTInteger)) == ogr.OGRERR_NONE
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f["fid"] = 12
|
|
assert lyr.CreateFeature(f) == ogr.OGRERR_NONE
|
|
assert f.GetFID() == 12
|
|
f = lyr.GetFeature(f.GetFID())
|
|
assert f is not None
|
|
assert lyr.SetFeature(f) == ogr.OGRERR_NONE
|
|
f = None
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f["fid"] = 13
|
|
f.SetFID(13)
|
|
assert lyr.CreateFeature(f) == ogr.OGRERR_NONE
|
|
assert f.GetFID() == 13
|
|
f = lyr.GetFeature(f.GetFID())
|
|
assert f is not None
|
|
assert lyr.SetFeature(f) == ogr.OGRERR_NONE
|
|
f = None
|
|
|
|
lyr = ds.CreateLayer("fid_integer64")
|
|
assert lyr.CreateField(ogr.FieldDefn("fid", ogr.OFTInteger64)) == ogr.OGRERR_NONE
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f["fid"] = 1234567890123
|
|
assert lyr.CreateFeature(f) == ogr.OGRERR_NONE
|
|
assert f.GetFID() == 1234567890123
|
|
f = lyr.GetFeature(f.GetFID())
|
|
assert f is not None
|
|
assert lyr.SetFeature(f) == ogr.OGRERR_NONE
|
|
f = None
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f["fid"] = 1234567890124
|
|
f.SetFID(1234567890124)
|
|
assert lyr.CreateFeature(f) == ogr.OGRERR_NONE
|
|
assert f.GetFID() == 1234567890124
|
|
f = lyr.GetFeature(f.GetFID())
|
|
assert f is not None
|
|
assert lyr.SetFeature(f) == ogr.OGRERR_NONE
|
|
f = None
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f["fid"] = 1234567890125
|
|
f.SetFID(1)
|
|
with gdaltest.error_handler():
|
|
assert lyr.CreateFeature(f) == ogr.OGRERR_FAILURE
|
|
|
|
# Simulates the situation of GeoPackage ---QGIS---> Shapefile --> GeoPackage
|
|
# See https://github.com/qgis/QGIS/pull/43118
|
|
lyr = ds.CreateLayer("fid_real")
|
|
fld_defn = ogr.FieldDefn("fid", ogr.OFTReal)
|
|
fld_defn.SetWidth(20)
|
|
fld_defn.SetPrecision(0)
|
|
assert lyr.CreateField(fld_defn) == ogr.OGRERR_NONE
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f["fid"] = 1234567890123
|
|
assert lyr.CreateFeature(f) == ogr.OGRERR_NONE
|
|
assert f.GetFID() == 1234567890123
|
|
f = lyr.GetFeature(f.GetFID())
|
|
assert f is not None
|
|
assert lyr.SetFeature(f) == ogr.OGRERR_NONE
|
|
f = None
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f["fid"] = 1234567890124
|
|
f.SetFID(1234567890124)
|
|
assert lyr.CreateFeature(f) == ogr.OGRERR_NONE
|
|
assert f.GetFID() == 1234567890124
|
|
f = lyr.GetFeature(f.GetFID())
|
|
assert f is not None
|
|
assert lyr.SetFeature(f) == ogr.OGRERR_NONE
|
|
f = None
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f["fid"] = 1234567890123.5
|
|
with gdaltest.error_handler():
|
|
assert lyr.CreateFeature(f) == ogr.OGRERR_FAILURE
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f["fid"] = 1234567890125
|
|
f.SetFID(1)
|
|
with gdaltest.error_handler():
|
|
assert lyr.CreateFeature(f) == ogr.OGRERR_FAILURE
|
|
|
|
ds = None
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test opening a corrupted gpkg with duplicated layer names
|
|
|
|
|
|
def test_ogr_gpkg_57():
|
|
|
|
out_filename = "/vsimem/test_ogr_gpkg_57.gpkg"
|
|
ogr.GetDriverByName("GPKG").CreateDataSource(out_filename)
|
|
|
|
ds = ogr.Open(out_filename, update=1)
|
|
ds.ExecuteSQL("DROP TABLE gpkg_contents")
|
|
ds.ExecuteSQL(
|
|
"CREATE TABLE gpkg_contents (table_name,data_type,identifier,description,last_change,min_x, min_y,max_x, max_y,srs_id)"
|
|
)
|
|
ds.ExecuteSQL(
|
|
"""INSERT INTO "gpkg_contents" VALUES('poly','features','poly','','',NULL,NULL,NULL,NULL,0)"""
|
|
)
|
|
ds.ExecuteSQL(
|
|
"""INSERT INTO "gpkg_contents" VALUES('poly','features','poly','','',NULL,NULL,NULL,NULL,0)"""
|
|
)
|
|
ds.ExecuteSQL(
|
|
"""INSERT INTO "gpkg_geometry_columns" VALUES('poly','geom','POLYGON',0,0,0)"""
|
|
)
|
|
ds.ExecuteSQL("""CREATE TABLE "poly"("fid" INTEGER PRIMARY KEY, "geom" POLYGON)""")
|
|
ds = None
|
|
|
|
gdal.ErrorReset()
|
|
with gdaltest.error_handler():
|
|
ds = ogr.Open(out_filename)
|
|
assert ds.GetLayerCount() == 1, "bad layer count"
|
|
assert gdal.GetLastErrorMsg() != ""
|
|
ds = None
|
|
|
|
gdal.Unlink(out_filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test opening a non-standard GeoPackage with multiple geometry columns
|
|
|
|
|
|
def test_ogr_gpkg_multiple_geom_columns():
|
|
|
|
out_filename = "/vsimem/test_ogr_gpkg_multiple_geom_columns.gpkg"
|
|
ogr.GetDriverByName("GPKG").CreateDataSource(out_filename)
|
|
|
|
ds = ogr.Open(out_filename, update=1)
|
|
ds.ExecuteSQL("DROP TABLE gpkg_geometry_columns")
|
|
# Modified gpkg_geometry_columns definition with a UNIQUE constraint on both (table_name, column_name)
|
|
ds.ExecuteSQL(
|
|
"""CREATE TABLE gpkg_geometry_columns (table_name TEXT NOT NULL,column_name TEXT NOT NULL,geometry_type_name TEXT NOT NULL,srs_id INTEGER NOT NULL,z TINYINT NOT NULL,m TINYINT NOT NULL,CONSTRAINT pk_geom_cols PRIMARY KEY (table_name, column_name),CONSTRAINT uk_gc_table_name_column_name UNIQUE (table_name, column_name),CONSTRAINT fk_gc_tn FOREIGN KEY (table_name) REFERENCES gpkg_contents(table_name),CONSTRAINT fk_gc_srs FOREIGN KEY (srs_id) REFERENCES gpkg_spatial_ref_sys (srs_id));"""
|
|
)
|
|
ds.ExecuteSQL(
|
|
"""CREATE TABLE "test" ( "ogc_fid" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "poly" POLYGON, "pt" POINT, "area" REAL, "eas_id" INTEGER, "prfedea" TEXT(16))"""
|
|
)
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO test VALUES(1,X'4750000300000000000000401f401d41000000e05e511d4100000080322c5241000000001d2d52410103000000010000001b000000000000c01a481d4100000080072d5241000000e0814b1d41000000001d2d524100000040c44b1d41000000000f2d5241000000002c4c1d41000000a0002d524100000000774d1d41000000c0072d5241000000a0c44e1d4100000080112d52410000002008501d41000000c0172d5241000000e05e511d4100000020dd2c5241000000405e511d4100000040cf2c524100000000f0501d41000000c0ba2c52410000008084501d4100000020af2c524100000040a94f1d4100000000a42c524100000080744e1d41000000a09a2c524100000040014f1d41000000c0852c5241000000e0e04d1d4100000020872c524100000040f8441d41000000e0432c5241000000c012441d4100000080322c524100000000ff431d4100000020362c5241000000004b431d4100000080552c52410000000030431d41000000c05d2c5241000000c09b421d4100000000712c524100000080d6411d4100000080912c52410000008027411d4100000040b22c5241000000401f401d4100000040d62c5241000000a043441d4100000060f02c524100000060aa461d4100000080ff2c5241000000c01a481d4100000080072d5241',X'4750000100000000010100000000000000804b1d41000000001d2d5241',NULL,170,NULL);"
|
|
)
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_contents VALUES('test','features','test',NULL,'2023-04-21T13:53:59.009Z',478315.53124999999998,4762880.5,481645.3125,4765610.4999999999998,0);"
|
|
)
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_geometry_columns VALUES('test','poly','POLYGON',-1,0,0);"
|
|
)
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_geometry_columns VALUES('test','pt','POINT',4326,0,0);"
|
|
)
|
|
ds = None
|
|
|
|
gdal.ErrorReset()
|
|
with gdaltest.error_handler():
|
|
ds = ogr.Open(out_filename)
|
|
assert gdal.GetLastErrorMsg() != ""
|
|
assert ds.GetLayerCount() == 2
|
|
|
|
lyr = ds.GetLayerByName("test (poly)")
|
|
assert lyr.GetGeomType() == ogr.wkbPolygon
|
|
assert lyr.GetGeometryColumn() == "poly"
|
|
assert lyr.GetSpatialRef().GetName() == "Undefined Cartesian SRS"
|
|
assert lyr.GetLayerDefn().GetFieldCount() == 3
|
|
f = lyr.GetNextFeature()
|
|
assert f.GetFID() == 1
|
|
assert f["eas_id"] == 170
|
|
assert f.GetGeometryRef().ExportToWkt().startswith("POLYGON ((479750")
|
|
|
|
lyr = ds.GetLayerByName("test (pt)")
|
|
assert lyr.GetGeomType() == ogr.wkbPoint
|
|
assert lyr.GetGeometryColumn() == "pt"
|
|
assert lyr.GetSpatialRef().GetAuthorityCode(None) == "4326"
|
|
assert lyr.GetLayerDefn().GetFieldCount() == 3
|
|
f = lyr.GetNextFeature()
|
|
assert f.GetFID() == 1
|
|
assert f["eas_id"] == 170
|
|
assert f.GetGeometryRef().ExportToWkt() == "POINT (479968 4764788)"
|
|
|
|
ds = None
|
|
|
|
gdal.Unlink(out_filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test overwriting a layer
|
|
|
|
|
|
def test_ogr_gpkg_58():
|
|
|
|
out_filename = "/vsimem/ogr_gpkg_58.gpkg"
|
|
gdal.VectorTranslate(out_filename, "data/poly.shp", format="GPKG")
|
|
gdal.VectorTranslate(
|
|
out_filename, "data/poly.shp", format="GPKG", accessMode="overwrite"
|
|
)
|
|
|
|
ds = ogr.Open(out_filename)
|
|
sql_lyr = ds.ExecuteSQL("SELECT HasSpatialIndex('poly', 'geom')")
|
|
f = sql_lyr.GetNextFeature()
|
|
assert f.GetField(0) == 1
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
ds = None
|
|
|
|
gdal.Unlink(out_filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test CreateSpatialIndex()
|
|
|
|
|
|
def test_ogr_gpkg_59():
|
|
|
|
out_filename = "/vsimem/ogr_gpkg_59.gpkg"
|
|
gdal.VectorTranslate(
|
|
out_filename,
|
|
"data/poly.shp",
|
|
format="GPKG",
|
|
layerCreationOptions=["SPATIAL_INDEX=NO"],
|
|
)
|
|
|
|
ds = ogr.Open(out_filename, update=1)
|
|
sql_lyr = ds.ExecuteSQL("SELECT CreateSpatialIndex('poly', 'geom')")
|
|
f = sql_lyr.GetNextFeature()
|
|
assert f.GetField(0) == 1
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
ds = None
|
|
|
|
gdal.Unlink(out_filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test savepoints
|
|
|
|
|
|
def test_ogr_gpkg_savepoint():
|
|
|
|
filename = "/vsimem/ogr_gpkg_savepoint.gpkg"
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(filename)
|
|
lyr = ds.CreateLayer("foo")
|
|
lyr.CreateField(ogr.FieldDefn("str", ogr.OFTString))
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f["str"] = "foo"
|
|
lyr.CreateFeature(f)
|
|
ds = None
|
|
|
|
ds = ogr.Open(filename, update=1)
|
|
lyr = ds.GetLayer(0)
|
|
ds.StartTransaction()
|
|
ds.ExecuteSQL("SAVEPOINT pt")
|
|
lyr.DeleteFeature(1)
|
|
ds.ExecuteSQL("ROLLBACK TO SAVEPOINT pt")
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f["str"] = "bar"
|
|
lyr.CreateFeature(f)
|
|
ds.CommitTransaction()
|
|
ds = None
|
|
|
|
ds = ogr.Open(filename)
|
|
lyr = ds.GetLayer(0)
|
|
assert lyr.GetFeatureCount() == 2
|
|
ds = None
|
|
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test that we don't open file handles behind the back of sqlite3
|
|
|
|
|
|
def test_ogr_gpkg_wal():
|
|
|
|
import test_cli_utilities
|
|
|
|
if test_cli_utilities.get_ogrinfo_path() is None:
|
|
pytest.skip()
|
|
|
|
# needs to be a real file
|
|
filename = "tmp/ogr_gpkg_wal.gpkg"
|
|
|
|
with gdaltest.config_option("OGR_SQLITE_JOURNAL", "WAL"):
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(filename)
|
|
ds.CreateLayer("foo")
|
|
ds = None
|
|
|
|
ds = ogr.Open(filename, update=1)
|
|
os.stat(filename + "-wal")
|
|
|
|
# Re-open in read-only mode
|
|
ds_ro = ogr.Open(filename)
|
|
ds_ro.GetName()
|
|
os.stat(filename + "-wal")
|
|
|
|
# Test external process to read the file
|
|
gdaltest.runexternal(test_cli_utilities.get_ogrinfo_path() + " " + filename)
|
|
|
|
# The file must still exist
|
|
os.stat(filename + "-wal")
|
|
|
|
ds = None
|
|
ds_ro = None
|
|
|
|
gdal.Unlink(filename)
|
|
gdal.Unlink(filename + "-wal")
|
|
gdal.Unlink(filename + "-shm")
|
|
|
|
|
|
###############################################################################
|
|
# Test NOLOCK open option
|
|
|
|
|
|
def test_ogr_gpkg_nolock():
|
|
def get_nolock(ds):
|
|
sql_lyr = ds.ExecuteSQL("SELECT nolock", dialect="DEBUG")
|
|
f = sql_lyr.GetNextFeature()
|
|
res = True if f[0] == 1 else False
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
return res
|
|
|
|
# needs to be a real file
|
|
filename = "tmp/test_ogr_gpkg_#_nolock.gpkg"
|
|
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(filename)
|
|
lyr = ds.CreateLayer("foo")
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
lyr.CreateFeature(f)
|
|
f = None
|
|
ds = None
|
|
|
|
# Special case on Windows for files that start with drive letters
|
|
full_filename = os.path.join(os.getcwd(), "tmp", "test_ogr_gpkg_#_nolock.gpkg")
|
|
ds = gdal.OpenEx(full_filename, gdal.OF_VECTOR, open_options=["NOLOCK=YES"])
|
|
assert ds
|
|
ds = None
|
|
|
|
ds = gdal.OpenEx(filename, gdal.OF_VECTOR, open_options=["NOLOCK=YES"])
|
|
assert ds
|
|
assert get_nolock(ds)
|
|
|
|
lyr = ds.GetLayer(0)
|
|
f = lyr.GetNextFeature()
|
|
ds2 = ogr.Open(filename, update=1)
|
|
lyr2 = ds2.GetLayer(0)
|
|
f = ogr.Feature(lyr2.GetLayerDefn())
|
|
# Without lockless mode on ds, this would timeout and fail
|
|
assert lyr2.CreateFeature(f) == ogr.OGRERR_NONE
|
|
f = None
|
|
ds2 = None
|
|
ds = None
|
|
|
|
# Lockless mode should NOT be honored by GDAL in update mode
|
|
ds = gdal.OpenEx(
|
|
filename, gdal.OF_VECTOR | gdal.OF_UPDATE, open_options=["NOLOCK=YES"]
|
|
)
|
|
assert ds
|
|
assert not get_nolock(ds)
|
|
ds = None
|
|
|
|
# Now turn on WAL
|
|
ds = ogr.Open(filename, update=1)
|
|
ds.ReleaseResultSet(ds.ExecuteSQL("PRAGMA journal_mode = WAL"))
|
|
ds = None
|
|
|
|
# Lockless mode should NOT be honored by GDAL on a WAL enabled file
|
|
ds = gdal.OpenEx(filename, gdal.OF_VECTOR, open_options=["NOLOCK=YES"])
|
|
assert ds
|
|
assert not get_nolock(ds)
|
|
ds = None
|
|
|
|
gdal.Unlink(filename)
|
|
gdal.Unlink(filename + "-wal")
|
|
gdal.Unlink(filename + "-shm")
|
|
|
|
ds = gdal.OpenEx(
|
|
"/vsizip/data/gpkg/poly.gpkg.zip/poly.gpkg",
|
|
gdal.OF_VECTOR,
|
|
open_options=["NOLOCK=YES"],
|
|
)
|
|
assert ds
|
|
|
|
ds = gdal.OpenEx(
|
|
"/vsizip/" + os.getcwd() + "/data/gpkg/poly.gpkg.zip/poly.gpkg",
|
|
gdal.OF_VECTOR,
|
|
open_options=["NOLOCK=YES"],
|
|
)
|
|
assert ds
|
|
|
|
|
|
###############################################################################
|
|
# Run test_ogrsf
|
|
|
|
|
|
def test_ogr_gpkg_test_ogrsf():
|
|
|
|
# Do integrity check first
|
|
gpkg_ds = ogr.Open("tmp/gpkg_test.gpkg")
|
|
sql_lyr = gpkg_ds.ExecuteSQL("PRAGMA integrity_check")
|
|
feat = sql_lyr.GetNextFeature()
|
|
assert feat.GetField(0) == "ok", "integrity check failed"
|
|
gpkg_ds.ReleaseResultSet(sql_lyr)
|
|
|
|
import test_cli_utilities
|
|
|
|
if test_cli_utilities.get_test_ogrsf_path() is None:
|
|
pytest.skip()
|
|
|
|
gpkg_ds = None
|
|
# sys.exit(0)
|
|
ret = gdaltest.runexternal(
|
|
test_cli_utilities.get_test_ogrsf_path()
|
|
+ " tmp/gpkg_test.gpkg --config OGR_SQLITE_SYNCHRONOUS OFF"
|
|
)
|
|
|
|
assert ret.find("INFO") != -1 and ret.find("ERROR") == -1
|
|
|
|
ret = gdaltest.runexternal(
|
|
test_cli_utilities.get_test_ogrsf_path()
|
|
+ ' tmp/gpkg_test.gpkg -sql "select * from tbl_linestring_renamed" --config OGR_SQLITE_SYNCHRONOUS OFF'
|
|
)
|
|
|
|
assert ret.find("INFO") != -1 and ret.find("ERROR") == -1
|
|
|
|
|
|
###############################################################################
|
|
# Test JSon subtype support
|
|
|
|
|
|
def test_ogr_gpkg_json():
|
|
|
|
filename = "/vsimem/ogr_gpkg_json.gpkg"
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(filename)
|
|
lyr = ds.CreateLayer("test")
|
|
|
|
fld_defn = ogr.FieldDefn("test_json", ogr.OFTString)
|
|
fld_defn.SetSubType(ogr.OFSTJSON)
|
|
lyr.CreateField(fld_defn)
|
|
assert lyr.GetLayerDefn().GetFieldDefn(0).GetSubType() == ogr.OFSTJSON
|
|
|
|
ds.ReleaseResultSet(ds.ExecuteSQL("SELECT 1 FROM test")) # will crystalize
|
|
|
|
fld_defn = ogr.FieldDefn("test2_json", ogr.OFTString)
|
|
fld_defn.SetSubType(ogr.OFSTJSON)
|
|
lyr.CreateField(fld_defn)
|
|
assert lyr.GetLayerDefn().GetFieldDefn(1).GetSubType() == ogr.OFSTJSON
|
|
|
|
fld_defn = ogr.FieldDefn("test_string", ogr.OFTString)
|
|
lyr.CreateField(fld_defn)
|
|
|
|
ds = None
|
|
|
|
ds = ogr.Open(filename, update=1)
|
|
lyr = ds.GetLayer(0)
|
|
assert lyr.GetLayerDefn().GetFieldDefn(0).GetSubType() == ogr.OFSTJSON
|
|
assert lyr.GetLayerDefn().GetFieldDefn(1).GetSubType() == ogr.OFSTJSON
|
|
|
|
# Demote field from JSON
|
|
new_defn = ogr.FieldDefn("test_was_json_now_string", ogr.OFTString)
|
|
assert (
|
|
lyr.AlterFieldDefn(
|
|
lyr.GetLayerDefn().GetFieldIndex("test2_json"), new_defn, ogr.ALTER_ALL_FLAG
|
|
)
|
|
== 0
|
|
)
|
|
|
|
# Alter field to JSON
|
|
new_defn = ogr.FieldDefn("test_was_string_now_json", ogr.OFTString)
|
|
new_defn.SetSubType(ogr.OFSTJSON)
|
|
assert (
|
|
lyr.AlterFieldDefn(
|
|
lyr.GetLayerDefn().GetFieldIndex("test_string"),
|
|
new_defn,
|
|
ogr.ALTER_ALL_FLAG,
|
|
)
|
|
== 0
|
|
)
|
|
|
|
# Delete JSON field
|
|
assert lyr.DeleteField(lyr.GetLayerDefn().GetFieldIndex("test_json")) == 0
|
|
|
|
ds = None
|
|
|
|
assert validate(filename), "validation failed"
|
|
|
|
ds = ogr.Open(filename)
|
|
lyr = ds.GetLayer(0)
|
|
assert (
|
|
lyr.GetLayerDefn()
|
|
.GetFieldDefn(lyr.GetLayerDefn().GetFieldIndex("test_was_json_now_string"))
|
|
.GetSubType()
|
|
== ogr.OFSTNone
|
|
)
|
|
assert (
|
|
lyr.GetLayerDefn()
|
|
.GetFieldDefn(lyr.GetLayerDefn().GetFieldIndex("test_was_string_now_json"))
|
|
.GetSubType()
|
|
== ogr.OFSTJSON
|
|
)
|
|
|
|
sql_lyr = ds.ExecuteSQL("SELECT 1 FROM gpkg_data_columns WHERE table_name = 'test'")
|
|
fc = sql_lyr.GetFeatureCount()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
assert fc == 1
|
|
|
|
ds = None
|
|
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test invalid/non-standard content in records
|
|
|
|
|
|
def test_ogr_gpkg_invalid_values_in_records():
|
|
|
|
filename = "/vsimem/test_ogr_gpkg_invalid_date_content.gpkg"
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(filename)
|
|
lyr = ds.CreateLayer("test")
|
|
|
|
fld_defn = ogr.FieldDefn("dt", ogr.OFTDateTime)
|
|
lyr.CreateField(fld_defn)
|
|
fld_defn = ogr.FieldDefn("d", ogr.OFTDate)
|
|
lyr.CreateField(fld_defn)
|
|
|
|
for i in range(6):
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
lyr.CreateFeature(f)
|
|
ds.ExecuteSQL("UPDATE test SET dt = 'foo' WHERE fid = 1")
|
|
ds.ExecuteSQL("UPDATE test SET d = 'bar' WHERE fid = 2")
|
|
ds.ExecuteSQL("UPDATE test SET dt = 3 WHERE fid = 3")
|
|
ds.ExecuteSQL("UPDATE test SET d = 4 WHERE fid = 4")
|
|
ds.ExecuteSQL("UPDATE test SET dt = '2020/01/21 12:34:56+01' WHERE fid = 5")
|
|
ds.ExecuteSQL("UPDATE test SET d = '2020/01/21' WHERE fid = 6")
|
|
|
|
lyr.ResetReading()
|
|
|
|
gdal.ErrorReset()
|
|
with gdaltest.error_handler():
|
|
f = lyr.GetNextFeature()
|
|
assert gdal.GetLastErrorMsg() == "Invalid content for record 1 in column dt: foo"
|
|
assert not f.IsFieldSet("dt")
|
|
|
|
gdal.ErrorReset()
|
|
with gdaltest.error_handler():
|
|
f = lyr.GetNextFeature()
|
|
assert gdal.GetLastErrorMsg() == "Invalid content for record 2 in column d: bar"
|
|
assert not f.IsFieldSet("d")
|
|
|
|
gdal.ErrorReset()
|
|
with gdaltest.error_handler():
|
|
f = lyr.GetNextFeature()
|
|
assert gdal.GetLastErrorMsg() == "Unexpected data type for record 3 in column dt"
|
|
assert not f.IsFieldSet("dt")
|
|
|
|
gdal.ErrorReset()
|
|
with gdaltest.error_handler():
|
|
f = lyr.GetNextFeature()
|
|
assert gdal.GetLastErrorMsg() == "Unexpected data type for record 4 in column d"
|
|
assert not f.IsFieldSet("d")
|
|
|
|
gdal.ErrorReset()
|
|
with gdaltest.error_handler():
|
|
f = lyr.GetNextFeature()
|
|
assert (
|
|
gdal.GetLastErrorMsg()
|
|
== "Non-conformant content for record 5 in column dt, 2020/01/21 12:34:56+01, successfully parsed"
|
|
)
|
|
assert f.IsFieldSet("dt")
|
|
assert f["dt"] == "2020/01/21 12:34:56+01"
|
|
|
|
gdal.ErrorReset()
|
|
with gdaltest.error_handler():
|
|
f = lyr.GetNextFeature()
|
|
assert (
|
|
gdal.GetLastErrorMsg()
|
|
== "Non-conformant content for record 6 in column d, 2020/01/21, successfully parsed"
|
|
)
|
|
assert f.IsFieldSet("d")
|
|
assert f["d"] == "2020/01/21"
|
|
|
|
ds = None
|
|
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test creating a table with layer geometry type unknown/GEOMETRY and
|
|
# geometries of mixed dimensionality
|
|
|
|
|
|
def test_ogr_gpkg_mixed_dimensionality_unknown_layer_geometry_type():
|
|
|
|
filename = (
|
|
"/vsimem/test_ogr_gpkg_mixed_dimensionality_unknown_layer_geometry_type.gpkg"
|
|
)
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(filename)
|
|
lyr = ds.CreateLayer("test")
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT (1 2)"))
|
|
lyr.CreateFeature(f)
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT (1 2 3)"))
|
|
lyr.CreateFeature(f)
|
|
|
|
ds = None
|
|
|
|
assert validate(filename), "validation failed"
|
|
|
|
ds = ogr.Open(filename)
|
|
lyr = ds.GetLayer(0)
|
|
assert lyr.GetGeomType() == ogr.wkbUnknown
|
|
|
|
sql_lyr = ds.ExecuteSQL("SELECT z FROM gpkg_geometry_columns")
|
|
f = sql_lyr.GetNextFeature()
|
|
assert f.GetField(0) == 2
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
ds = None
|
|
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test fixing up wrong RTree update3 trigger from GeoPackage < 1.2.1
|
|
|
|
|
|
def test_ogr_gpkg_fixup_wrong_rtree_trigger():
|
|
|
|
filename = "/vsimem/test_ogr_gpkg_fixup_wrong_rtree_trigger.gpkg"
|
|
ds = ogr.GetDriverByName("GPKG").CreateDataSource(filename)
|
|
ds.CreateLayer("test-with-dash")
|
|
ds.CreateLayer("test2")
|
|
ds = None
|
|
with gdaltest.error_handler():
|
|
ds = ogr.Open(filename, update=1)
|
|
# inject wrong trigger on purpose with the wrong 'OF "geometry" ' part
|
|
ds.ExecuteSQL('DROP TRIGGER "rtree_test-with-dash_geometry_update3"')
|
|
wrong_trigger = 'CREATE TRIGGER "rtree_test-with-dash_geometry_update3" AFTER UPDATE OF "geometry" ON "test-with-dash" WHEN OLD."fid" != NEW."fid" AND (NEW."geometry" NOTNULL AND NOT ST_IsEmpty(NEW."geometry")) BEGIN DELETE FROM "rtree_test_geometry" WHERE id = OLD."fid"; INSERT OR REPLACE INTO "rtree_test_geometry" VALUES (NEW."fid",ST_MinX(NEW."geometry"), ST_MaxX(NEW."geometry"),ST_MinY(NEW."geometry"), ST_MaxY(NEW."geometry")); END'
|
|
ds.ExecuteSQL(wrong_trigger)
|
|
|
|
ds.ExecuteSQL("DROP TRIGGER rtree_test2_geometry_update3")
|
|
# Test another potential variant (although not generated by OGR)
|
|
wrong_trigger2 = 'CREATE TRIGGER "rtree_test2_geometry_update3" AFTER UPDATE OF geometry ON test2 WHEN OLD."fid" != NEW."fid" AND (NEW."geometry" NOTNULL AND NOT ST_IsEmpty(NEW."geometry")) BEGIN DELETE FROM "rtree_test_geometry" WHERE id = OLD."fid"; INSERT OR REPLACE INTO "rtree_test_geometry" VALUES (NEW."fid",ST_MinX(NEW."geometry"), ST_MaxX(NEW."geometry"),ST_MinY(NEW."geometry"), ST_MaxY(NEW."geometry")); END'
|
|
ds.ExecuteSQL(wrong_trigger2)
|
|
|
|
ds = None
|
|
|
|
# Open in read-only mode
|
|
ds = ogr.Open(filename)
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT sql FROM sqlite_master WHERE type = 'trigger' AND name = 'rtree_test-with-dash_geometry_update3'"
|
|
)
|
|
f = sql_lyr.GetNextFeature()
|
|
sql = f["sql"]
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
ds = None
|
|
assert sql == wrong_trigger
|
|
|
|
# Open in update mode
|
|
ds = ogr.Open(filename, update=1)
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT sql FROM sqlite_master WHERE type = 'trigger' AND name = 'rtree_test-with-dash_geometry_update3'"
|
|
)
|
|
f = sql_lyr.GetNextFeature()
|
|
sql = f["sql"]
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT sql FROM sqlite_master WHERE type = 'trigger' AND name = 'rtree_test2_geometry_update3'"
|
|
)
|
|
f = sql_lyr.GetNextFeature()
|
|
sql2 = f["sql"]
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
ds = None
|
|
|
|
gdal.Unlink(filename)
|
|
assert (
|
|
sql
|
|
== 'CREATE TRIGGER "rtree_test-with-dash_geometry_update3" AFTER UPDATE ON "test-with-dash" WHEN OLD."fid" != NEW."fid" AND (NEW."geometry" NOTNULL AND NOT ST_IsEmpty(NEW."geometry")) BEGIN DELETE FROM "rtree_test_geometry" WHERE id = OLD."fid"; INSERT OR REPLACE INTO "rtree_test_geometry" VALUES (NEW."fid",ST_MinX(NEW."geometry"), ST_MaxX(NEW."geometry"),ST_MinY(NEW."geometry"), ST_MaxY(NEW."geometry")); END'
|
|
)
|
|
assert (
|
|
sql2
|
|
== 'CREATE TRIGGER "rtree_test2_geometry_update3" AFTER UPDATE ON test2 WHEN OLD."fid" != NEW."fid" AND (NEW."geometry" NOTNULL AND NOT ST_IsEmpty(NEW."geometry")) BEGIN DELETE FROM "rtree_test_geometry" WHERE id = OLD."fid"; INSERT OR REPLACE INTO "rtree_test_geometry" VALUES (NEW."fid",ST_MinX(NEW."geometry"), ST_MaxX(NEW."geometry"),ST_MinY(NEW."geometry"), ST_MaxY(NEW."geometry")); END'
|
|
)
|
|
|
|
|
|
###############################################################################
|
|
# Test PRELUDE_STATEMENTS open option
|
|
|
|
|
|
def test_ogr_gpkg_prelude_statements():
|
|
|
|
gdal.VectorTranslate("/vsimem/test.gpkg", "data/poly.shp", format="GPKG")
|
|
ds = gdal.OpenEx(
|
|
"/vsimem/test.gpkg",
|
|
open_options=[
|
|
"PRELUDE_STATEMENTS=ATTACH DATABASE '/vsimem/test.gpkg' AS other"
|
|
],
|
|
)
|
|
sql_lyr = ds.ExecuteSQL("SELECT * FROM poly JOIN other.poly USING (eas_id)")
|
|
assert sql_lyr.GetFeatureCount() == 10
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
gdal.Unlink("/vsimem/test.gpkg")
|
|
|
|
|
|
###############################################################################
|
|
# Test DATETIME_FORMAT
|
|
|
|
|
|
def test_ogr_gpkg_datetime_timezones():
|
|
|
|
filename = "/vsimem/test_ogr_gpkg_datetime_timezones.gpkg"
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(filename, options=["DATETIME_FORMAT=UTC"])
|
|
lyr = ds.CreateLayer("test")
|
|
lyr.CreateField(ogr.FieldDefn("dt", ogr.OFTDateTime))
|
|
for val in [
|
|
"2020/01/01 01:34:56",
|
|
"2020/01/01 01:34:56+00",
|
|
"2020/01/01 01:34:56.789+02",
|
|
]:
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetField("dt", val)
|
|
lyr.CreateFeature(f)
|
|
ds = None
|
|
|
|
ds = ogr.Open(filename)
|
|
lyr = ds.GetLayer(0)
|
|
f = lyr.GetNextFeature()
|
|
assert f.GetField("dt") == "2020/01/01 01:34:56+00"
|
|
f = lyr.GetNextFeature()
|
|
assert f.GetField("dt") == "2020/01/01 01:34:56+00"
|
|
f = lyr.GetNextFeature()
|
|
assert f.GetField("dt") == "2019/12/31 23:34:56.789+00"
|
|
|
|
sql_lyr = ds.ExecuteSQL("SELECT dt || '' FROM test")
|
|
f = sql_lyr.GetNextFeature()
|
|
# check that milliseconds are written to be strictly compliant with the GPKG spec
|
|
assert f.GetField(0) == "2020-01-01T01:34:56.000Z"
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
ds = None
|
|
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test AbortSQL
|
|
|
|
|
|
def test_abort_sql():
|
|
|
|
filename = "data/gpkg/poly_non_conformant.gpkg"
|
|
ds = ogr.Open(filename)
|
|
|
|
def abortAfterDelay():
|
|
# print("Aborting SQL...")
|
|
assert ds.AbortSQL() == ogr.OGRERR_NONE
|
|
|
|
t = threading.Timer(0.5, abortAfterDelay)
|
|
t.start()
|
|
|
|
start = time.time()
|
|
|
|
# Long running query
|
|
sql = """
|
|
WITH RECURSIVE r(i) AS (
|
|
VALUES(0)
|
|
UNION ALL
|
|
SELECT i FROM r
|
|
LIMIT 100000000
|
|
)
|
|
SELECT i FROM r WHERE i = 1;"""
|
|
|
|
with gdaltest.error_handler():
|
|
ds.ExecuteSQL(sql)
|
|
|
|
end = time.time()
|
|
assert int(end - start) < 2
|
|
|
|
# Same test with a GDAL dataset
|
|
ds2 = gdal.OpenEx(filename, gdal.OF_VECTOR)
|
|
|
|
def abortAfterDelay2():
|
|
# print("Aborting SQL...")
|
|
assert ds2.AbortSQL() == ogr.OGRERR_NONE
|
|
|
|
t = threading.Timer(0.5, abortAfterDelay2)
|
|
t.start()
|
|
|
|
start = time.time()
|
|
|
|
# Long running query
|
|
with gdaltest.error_handler():
|
|
ds2.ExecuteSQL(sql)
|
|
|
|
end = time.time()
|
|
assert int(end - start) < 2
|
|
|
|
|
|
###############################################################################
|
|
# Test ST_Transform() with no record in gpkg_spatial_ref_sys and thus we
|
|
# fallback to EPSG
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
sys.platform == "win32",
|
|
reason="f.GetGeometryRef() returns None on the current Windows CI",
|
|
)
|
|
def test_ogr_gpkg_st_transform_no_record_spatial_ref_sys():
|
|
|
|
ds = ogr.GetDriverByName("GPKG").CreateDataSource("/vsimem/test.gpkg")
|
|
lyr = ds.CreateLayer("test")
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("POINT (500000 0)"))
|
|
lyr.CreateFeature(f)
|
|
f = None
|
|
|
|
if not _has_spatialite_4_3_or_later(ds):
|
|
ds = None
|
|
gdal.Unlink("/vsimem/test.gpkg")
|
|
pytest.skip("Spatialite missing or too old")
|
|
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT ST_Transform(SetSRID(geom, 32631), 32731) FROM test"
|
|
)
|
|
# Fails on a number of configs
|
|
# assert sql_lyr.GetSpatialRef().GetAuthorityCode(None) == '32731'
|
|
f = sql_lyr.GetNextFeature()
|
|
assert f.GetGeometryRef().ExportToWkt() == "POINT (500000 10000000)"
|
|
f = None
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
|
|
ds = None
|
|
gdal.Unlink("/vsimem/test.gpkg")
|
|
|
|
|
|
###############################################################################
|
|
# Test deferred spatial index creation
|
|
|
|
|
|
def test_ogr_gpkg_deferred_spi_creation():
|
|
def has_spi(ds):
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT 1 FROM sqlite_master WHERE name = 'rtree_test_geom'",
|
|
dialect="DEBUG",
|
|
)
|
|
res = sql_lyr.GetNextFeature() is not None
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
return res
|
|
|
|
ds = ogr.GetDriverByName("GPKG").CreateDataSource("/vsimem/test.gpkg")
|
|
|
|
lyr = ds.CreateLayer("test")
|
|
assert not has_spi(ds)
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("POINT (0 0)"))
|
|
lyr.CreateFeature(f)
|
|
fid = f.GetFID()
|
|
f = None
|
|
assert not has_spi(ds)
|
|
|
|
lyr.ResetReading()
|
|
assert lyr.GetNextFeature() is not None
|
|
assert not has_spi(ds)
|
|
|
|
assert lyr.GetFeature(fid) is not None
|
|
assert not has_spi(ds)
|
|
|
|
assert lyr.CreateField(ogr.FieldDefn("foo", ogr.OFTString)) == ogr.OGRERR_NONE
|
|
assert not has_spi(ds)
|
|
|
|
assert lyr.DeleteField(0) == ogr.OGRERR_NONE
|
|
assert not has_spi(ds)
|
|
|
|
ds.ReleaseResultSet(ds.ExecuteSQL("SELECT 1"))
|
|
assert has_spi(ds)
|
|
|
|
# GetNextFeature() with spatial filter should cause SPI creation
|
|
ds.ExecuteSQL("DELLAYER:test")
|
|
lyr = ds.CreateLayer("test")
|
|
assert not has_spi(ds)
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("POINT (0 0)"))
|
|
lyr.CreateFeature(f)
|
|
fid = f.GetFID()
|
|
f = None
|
|
assert not has_spi(ds)
|
|
|
|
lyr.SetSpatialFilterRect(-1, -1, 1, 1)
|
|
|
|
lyr.ResetReading()
|
|
assert lyr.GetNextFeature() is not None
|
|
assert has_spi(ds)
|
|
|
|
ds = None
|
|
gdal.Unlink("/vsimem/test.gpkg")
|
|
|
|
|
|
###############################################################################
|
|
# Test deferred spatial index update
|
|
|
|
|
|
@pytest.mark.parametrize("gpkg_version", ["1.2", "1.4"])
|
|
def test_ogr_gpkg_deferred_spi_update(gpkg_version):
|
|
def has_spi_triggers(ds):
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT * FROM sqlite_master WHERE type = 'trigger' AND name LIKE 'rtree_test_geom%'",
|
|
dialect="DEBUG",
|
|
)
|
|
res = sql_lyr.GetFeatureCount()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
return res == 6 or res == 7
|
|
|
|
filename = "/vsimem/test.gpkg"
|
|
|
|
# Basic test
|
|
ds = ogr.GetDriverByName("GPKG").CreateDataSource(
|
|
filename, options=["VERSION=" + gpkg_version]
|
|
)
|
|
ds.CreateLayer("test")
|
|
ds = None
|
|
with gdaltest.config_option("OGR_GPKG_DEFERRED_SPI_UPDATE_THRESHOLD", "2"):
|
|
ds = ogr.Open(filename, update=1)
|
|
lyr = ds.GetLayer(0)
|
|
|
|
ds.StartTransaction()
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("POINT (0 0)"))
|
|
lyr.CreateFeature(f)
|
|
assert has_spi_triggers(ds)
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("POINT (1 1)"))
|
|
lyr.CreateFeature(f)
|
|
assert not has_spi_triggers(ds)
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("POINT (2 2)"))
|
|
lyr.CreateFeature(f)
|
|
assert not has_spi_triggers(ds)
|
|
|
|
sql_lyr = ds.ExecuteSQL("SELECT * FROM rtree_test_geom", dialect="DEBUG")
|
|
res = sql_lyr.GetFeatureCount()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
assert res == 2
|
|
|
|
ds.CommitTransaction()
|
|
assert has_spi_triggers(ds)
|
|
|
|
sql_lyr = ds.ExecuteSQL("SELECT * FROM rtree_test_geom", dialect="DEBUG")
|
|
res = sql_lyr.GetFeatureCount()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
assert res == 3
|
|
|
|
ds = None
|
|
|
|
# Check effect of RollbackTransaction()
|
|
ds = ogr.GetDriverByName("GPKG").CreateDataSource(
|
|
filename, options=["VERSION=" + gpkg_version]
|
|
)
|
|
ds.CreateLayer("test")
|
|
ds = None
|
|
with gdaltest.config_option("OGR_GPKG_DEFERRED_SPI_UPDATE_THRESHOLD", "1"):
|
|
ds = ogr.Open(filename, update=1)
|
|
lyr = ds.GetLayer(0)
|
|
|
|
ds.StartTransaction()
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("POINT (0 0)"))
|
|
lyr.CreateFeature(f)
|
|
assert not has_spi_triggers(ds)
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("POINT (1 1)"))
|
|
lyr.CreateFeature(f)
|
|
assert not has_spi_triggers(ds)
|
|
|
|
sql_lyr = ds.ExecuteSQL("SELECT * FROM rtree_test_geom", dialect="DEBUG")
|
|
res = sql_lyr.GetFeatureCount()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
assert res == 1
|
|
|
|
ds.RollbackTransaction()
|
|
assert has_spi_triggers(ds)
|
|
|
|
sql_lyr = ds.ExecuteSQL("SELECT * FROM rtree_test_geom", dialect="DEBUG")
|
|
res = sql_lyr.GetFeatureCount()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
assert res == 0
|
|
|
|
ds = None
|
|
|
|
# Check that GetNextFeature() with a spatial filter causes flushing of
|
|
# deferred SPI values
|
|
ds = ogr.GetDriverByName("GPKG").CreateDataSource(
|
|
filename, options=["VERSION=" + gpkg_version]
|
|
)
|
|
ds.CreateLayer("test")
|
|
ds = None
|
|
with gdaltest.config_option("OGR_GPKG_DEFERRED_SPI_UPDATE_THRESHOLD", "1"):
|
|
ds = ogr.Open(filename, update=1)
|
|
lyr = ds.GetLayer(0)
|
|
|
|
ds.StartTransaction()
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("POINT (0 0)"))
|
|
lyr.CreateFeature(f)
|
|
assert not has_spi_triggers(ds)
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("POINT (1 1)"))
|
|
lyr.CreateFeature(f)
|
|
|
|
lyr.SetSpatialFilterRect(0, 0, 1, 1)
|
|
lyr.ResetReading()
|
|
assert lyr.GetNextFeature() is not None
|
|
assert has_spi_triggers(ds)
|
|
assert lyr.GetNextFeature() is not None
|
|
assert lyr.GetNextFeature() is None
|
|
ds = None
|
|
|
|
gdal.Unlink("/vsimem/test.gpkg")
|
|
|
|
|
|
###############################################################################
|
|
# Test field domains
|
|
|
|
|
|
def test_ogr_gpkg_field_domains():
|
|
|
|
filename = "/vsimem/test.gpkg"
|
|
|
|
# Test write support
|
|
ds = gdal.GetDriverByName("GPKG").Create(filename, 0, 0, 0, gdal.GDT_Unknown)
|
|
|
|
assert ds.TestCapability(ogr.ODsCAddFieldDomain)
|
|
|
|
assert ds.GetFieldDomainNames() is None
|
|
|
|
assert ds.GetFieldDomain("does_not_exist") is None
|
|
|
|
assert ds.AddFieldDomain(
|
|
ogr.CreateRangeFieldDomain(
|
|
"range_domain_int",
|
|
"my desc",
|
|
ogr.OFTInteger,
|
|
ogr.OFSTNone,
|
|
1,
|
|
True,
|
|
2,
|
|
False,
|
|
)
|
|
)
|
|
assert ds.GetFieldDomain("range_domain_int") is not None
|
|
|
|
assert set(ds.GetFieldDomainNames()) == {"range_domain_int"}
|
|
|
|
assert not ds.AddFieldDomain(
|
|
ogr.CreateRangeFieldDomain(
|
|
"range_domain_int",
|
|
"my desc",
|
|
ogr.OFTInteger,
|
|
ogr.OFSTNone,
|
|
1,
|
|
True,
|
|
2,
|
|
True,
|
|
)
|
|
)
|
|
|
|
assert ds.AddFieldDomain(
|
|
ogr.CreateRangeFieldDomain(
|
|
"range_domain_int64",
|
|
"",
|
|
ogr.OFTInteger64,
|
|
ogr.OFSTNone,
|
|
-1234567890123,
|
|
False,
|
|
1234567890123,
|
|
True,
|
|
)
|
|
)
|
|
assert ds.GetFieldDomain("range_domain_int64") is not None
|
|
|
|
assert ds.AddFieldDomain(
|
|
ogr.CreateRangeFieldDomain(
|
|
"range_domain_real", "", ogr.OFTReal, ogr.OFSTNone, 1.5, True, 2.5, True
|
|
)
|
|
)
|
|
assert ds.GetFieldDomain("range_domain_real") is not None
|
|
|
|
assert ds.AddFieldDomain(
|
|
ogr.CreateRangeFieldDomain(
|
|
"range_domain_real_inf",
|
|
"",
|
|
ogr.OFTReal,
|
|
ogr.OFSTNone,
|
|
-math.inf,
|
|
True,
|
|
math.inf,
|
|
True,
|
|
)
|
|
)
|
|
assert ds.GetFieldDomain("range_domain_real_inf") is not None
|
|
|
|
assert ds.AddFieldDomain(
|
|
ogr.CreateGlobFieldDomain(
|
|
"glob_domain", "my desc", ogr.OFTString, ogr.OFSTNone, "*"
|
|
)
|
|
)
|
|
assert ds.GetFieldDomain("glob_domain") is not None
|
|
|
|
assert ds.AddFieldDomain(
|
|
ogr.CreateCodedFieldDomain(
|
|
"enum_domain", "", ogr.OFTInteger64, ogr.OFSTNone, {1: "one", "2": None}
|
|
)
|
|
)
|
|
assert ds.GetFieldDomain("enum_domain") is not None
|
|
|
|
assert ds.AddFieldDomain(
|
|
ogr.CreateCodedFieldDomain(
|
|
"enum_domain_guess_int_single",
|
|
"my desc",
|
|
ogr.OFTInteger,
|
|
ogr.OFSTNone,
|
|
{1: "one"},
|
|
)
|
|
)
|
|
assert ds.AddFieldDomain(
|
|
ogr.CreateCodedFieldDomain(
|
|
"enum_domain_guess_int",
|
|
"",
|
|
ogr.OFTInteger,
|
|
ogr.OFSTNone,
|
|
{1: "one", 2: "two"},
|
|
)
|
|
)
|
|
assert ds.AddFieldDomain(
|
|
ogr.CreateCodedFieldDomain(
|
|
"enum_domain_guess_int64_single_1",
|
|
"",
|
|
ogr.OFTInteger64,
|
|
ogr.OFSTNone,
|
|
{1234567890123: "1234567890123"},
|
|
)
|
|
)
|
|
assert ds.AddFieldDomain(
|
|
ogr.CreateCodedFieldDomain(
|
|
"enum_domain_guess_int64_single_2",
|
|
"",
|
|
ogr.OFTInteger64,
|
|
ogr.OFSTNone,
|
|
{-1234567890123: "-1234567890123"},
|
|
)
|
|
)
|
|
assert ds.AddFieldDomain(
|
|
ogr.CreateCodedFieldDomain(
|
|
"enum_domain_guess_int64",
|
|
"",
|
|
ogr.OFTInteger64,
|
|
ogr.OFSTNone,
|
|
{1: "one", 1234567890123: "1234567890123", 3: "three"},
|
|
)
|
|
)
|
|
assert ds.AddFieldDomain(
|
|
ogr.CreateCodedFieldDomain(
|
|
"enum_domain_guess_real_single",
|
|
"",
|
|
ogr.OFTReal,
|
|
ogr.OFSTNone,
|
|
{1.5: "one dot five"},
|
|
)
|
|
)
|
|
assert ds.AddFieldDomain(
|
|
ogr.CreateCodedFieldDomain(
|
|
"enum_domain_guess_real",
|
|
"",
|
|
ogr.OFTReal,
|
|
ogr.OFSTNone,
|
|
{1: "one", 1.5: "one dot five", 1234567890123: "1234567890123", 3: "three"},
|
|
)
|
|
)
|
|
assert ds.AddFieldDomain(
|
|
ogr.CreateCodedFieldDomain(
|
|
"enum_domain_guess_string_single",
|
|
"",
|
|
ogr.OFTString,
|
|
ogr.OFSTNone,
|
|
{"three": "three"},
|
|
)
|
|
)
|
|
assert ds.AddFieldDomain(
|
|
ogr.CreateCodedFieldDomain(
|
|
"enum_domain_guess_string",
|
|
"",
|
|
ogr.OFTString,
|
|
ogr.OFSTNone,
|
|
{1: "one", 1.5: "one dot five", "three": "three", 4: "four"},
|
|
)
|
|
)
|
|
|
|
assert len(ds.GetFieldDomainNames()) == len(set(ds.GetFieldDomainNames()))
|
|
assert set(ds.GetFieldDomainNames()) == {
|
|
"enum_domain",
|
|
"enum_domain_guess_int",
|
|
"enum_domain_guess_int64",
|
|
"enum_domain_guess_int64_single_1",
|
|
"enum_domain_guess_int64_single_2",
|
|
"enum_domain_guess_int_single",
|
|
"enum_domain_guess_real",
|
|
"enum_domain_guess_real_single",
|
|
"enum_domain_guess_string",
|
|
"enum_domain_guess_string_single",
|
|
"glob_domain",
|
|
"range_domain_int",
|
|
"range_domain_int64",
|
|
"range_domain_real",
|
|
"range_domain_real_inf",
|
|
}
|
|
|
|
lyr = ds.CreateLayer("test")
|
|
|
|
fld_defn = ogr.FieldDefn("with_range_domain_int", ogr.OFTInteger)
|
|
fld_defn.SetDomainName("range_domain_int")
|
|
lyr.CreateField(fld_defn)
|
|
|
|
fld_defn = ogr.FieldDefn("with_range_domain_int64", ogr.OFTInteger64)
|
|
fld_defn.SetDomainName("range_domain_int64")
|
|
lyr.CreateField(fld_defn)
|
|
|
|
fld_defn = ogr.FieldDefn("with_range_domain_real", ogr.OFTReal)
|
|
fld_defn.SetDomainName("range_domain_real")
|
|
lyr.CreateField(fld_defn)
|
|
|
|
fld_defn = ogr.FieldDefn("with_glob_domain", ogr.OFTString)
|
|
fld_defn.SetDomainName("glob_domain")
|
|
lyr.CreateField(fld_defn)
|
|
|
|
fld_defn = ogr.FieldDefn("with_enum_domain", ogr.OFTInteger64)
|
|
fld_defn.SetDomainName("enum_domain")
|
|
lyr.CreateField(fld_defn)
|
|
|
|
fld_defn = ogr.FieldDefn("without_domain_initially", ogr.OFTInteger)
|
|
lyr.CreateField(fld_defn)
|
|
|
|
ds = None
|
|
|
|
assert validate(filename), "validation failed"
|
|
|
|
# Test read support
|
|
ds = gdal.OpenEx(filename, gdal.OF_VECTOR)
|
|
|
|
sql_lyr = ds.ExecuteSQL("SELECT * FROM gpkg_data_column_constraints")
|
|
assert sql_lyr is not None
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
|
|
assert set(ds.GetFieldDomainNames()) == {
|
|
"enum_domain",
|
|
"enum_domain_guess_int",
|
|
"enum_domain_guess_int64",
|
|
"enum_domain_guess_int64_single_1",
|
|
"enum_domain_guess_int64_single_2",
|
|
"enum_domain_guess_int_single",
|
|
"enum_domain_guess_real",
|
|
"enum_domain_guess_real_single",
|
|
"enum_domain_guess_string",
|
|
"enum_domain_guess_string_single",
|
|
"glob_domain",
|
|
"range_domain_int",
|
|
"range_domain_int64",
|
|
"range_domain_real",
|
|
"range_domain_real_inf",
|
|
}
|
|
|
|
domain = ds.GetFieldDomain("range_domain_int")
|
|
assert domain is not None
|
|
assert domain.GetName() == "range_domain_int"
|
|
assert domain.GetDescription() == "my desc"
|
|
assert domain.GetDomainType() == ogr.OFDT_RANGE
|
|
assert domain.GetFieldType() == ogr.OFTInteger
|
|
assert domain.GetMinAsDouble() == 1.0
|
|
assert domain.IsMinInclusive()
|
|
assert domain.GetMaxAsDouble() == 2.0
|
|
assert not domain.IsMaxInclusive()
|
|
|
|
domain = ds.GetFieldDomain("range_domain_int64")
|
|
assert domain is not None
|
|
assert domain.GetName() == "range_domain_int64"
|
|
assert domain.GetDescription() == ""
|
|
assert domain.GetDomainType() == ogr.OFDT_RANGE
|
|
assert domain.GetFieldType() == ogr.OFTInteger64
|
|
assert domain.GetMinAsDouble() == -1234567890123
|
|
assert not domain.IsMinInclusive()
|
|
assert domain.GetMaxAsDouble() == 1234567890123
|
|
assert domain.IsMaxInclusive()
|
|
|
|
domain = ds.GetFieldDomain("range_domain_real")
|
|
assert domain is not None
|
|
assert domain.GetName() == "range_domain_real"
|
|
assert domain.GetDescription() == ""
|
|
assert domain.GetDomainType() == ogr.OFDT_RANGE
|
|
assert domain.GetFieldType() == ogr.OFTReal
|
|
assert domain.GetMinAsDouble() == 1.5
|
|
assert domain.IsMinInclusive()
|
|
assert domain.GetMaxAsDouble() == 2.5
|
|
assert domain.IsMaxInclusive()
|
|
|
|
domain = ds.GetFieldDomain("range_domain_real_inf")
|
|
assert domain is not None
|
|
assert domain.GetName() == "range_domain_real_inf"
|
|
assert domain.GetDescription() == ""
|
|
assert domain.GetDomainType() == ogr.OFDT_RANGE
|
|
assert domain.GetFieldType() == ogr.OFTReal
|
|
assert domain.GetMinAsDouble() == -math.inf
|
|
assert domain.IsMinInclusive()
|
|
assert domain.GetMaxAsDouble() == math.inf
|
|
assert domain.IsMaxInclusive()
|
|
|
|
domain = ds.GetFieldDomain("glob_domain")
|
|
assert domain is not None
|
|
assert domain.GetName() == "glob_domain"
|
|
assert domain.GetDescription() == "my desc"
|
|
assert domain.GetDomainType() == ogr.OFDT_GLOB
|
|
assert domain.GetFieldType() == ogr.OFTString
|
|
assert domain.GetGlob() == "*"
|
|
|
|
domain = ds.GetFieldDomain("enum_domain")
|
|
assert domain is not None
|
|
assert domain.GetName() == "enum_domain"
|
|
assert domain.GetDescription() == ""
|
|
assert domain.GetDomainType() == ogr.OFDT_CODED
|
|
assert domain.GetFieldType() == ogr.OFTInteger64
|
|
assert domain.GetEnumeration() == {"1": "one", "2": None}
|
|
|
|
domain = ds.GetFieldDomain("enum_domain_guess_int_single")
|
|
assert domain.GetDescription() == "my desc"
|
|
assert domain.GetFieldType() == ogr.OFTInteger
|
|
|
|
domain = ds.GetFieldDomain("enum_domain_guess_int")
|
|
assert domain.GetFieldType() == ogr.OFTInteger
|
|
|
|
domain = ds.GetFieldDomain("enum_domain_guess_int64_single_1")
|
|
assert domain.GetFieldType() == ogr.OFTInteger64
|
|
|
|
domain = ds.GetFieldDomain("enum_domain_guess_int64_single_2")
|
|
assert domain.GetFieldType() == ogr.OFTInteger64
|
|
|
|
domain = ds.GetFieldDomain("enum_domain_guess_int64")
|
|
assert domain.GetFieldType() == ogr.OFTInteger64
|
|
|
|
domain = ds.GetFieldDomain("enum_domain_guess_real_single")
|
|
assert domain.GetFieldType() == ogr.OFTReal
|
|
|
|
domain = ds.GetFieldDomain("enum_domain_guess_real")
|
|
assert domain.GetFieldType() == ogr.OFTReal
|
|
|
|
domain = ds.GetFieldDomain("enum_domain_guess_string_single")
|
|
assert domain.GetFieldType() == ogr.OFTString
|
|
|
|
domain = ds.GetFieldDomain("enum_domain_guess_string")
|
|
assert domain.GetFieldType() == ogr.OFTString
|
|
|
|
lyr = ds.GetLayerByName("test")
|
|
lyr_defn = lyr.GetLayerDefn()
|
|
fld_defn = lyr_defn.GetFieldDefn(lyr_defn.GetFieldIndex("with_range_domain_int"))
|
|
assert fld_defn.GetDomainName() == "range_domain_int"
|
|
|
|
if gdal.GetDriverByName("GPKG").GetMetadataItem("SQLITE_HAS_COLUMN_METADATA"):
|
|
sql_lyr = ds.ExecuteSQL("SELECT with_range_domain_int FROM test")
|
|
assert (
|
|
sql_lyr.GetLayerDefn().GetFieldDefn(0).GetDomainName() == "range_domain_int"
|
|
)
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
|
|
ds = None
|
|
|
|
# Test AlterFieldDefn() support
|
|
ds = gdal.OpenEx(filename, gdal.OF_VECTOR | gdal.OF_UPDATE)
|
|
lyr = ds.GetLayer(0)
|
|
lyr_defn = lyr.GetLayerDefn()
|
|
|
|
# Unset domain name
|
|
idx = lyr_defn.GetFieldIndex("with_range_domain_int")
|
|
fld_defn = lyr_defn.GetFieldDefn(idx)
|
|
fld_defn = ogr.FieldDefn(fld_defn.GetName(), fld_defn.GetType())
|
|
fld_defn.SetDomainName("")
|
|
assert lyr.AlterFieldDefn(idx, fld_defn, ogr.ALTER_ALL_FLAG) == 0
|
|
|
|
# Change domain name
|
|
idx = lyr_defn.GetFieldIndex("with_range_domain_int64")
|
|
fld_defn = lyr_defn.GetFieldDefn(idx)
|
|
fld_defn = ogr.FieldDefn(fld_defn.GetName(), fld_defn.GetType())
|
|
fld_defn.SetDomainName("with_enum_domain")
|
|
assert lyr.AlterFieldDefn(idx, fld_defn, ogr.ALTER_ALL_FLAG) == 0
|
|
|
|
# Set domain name
|
|
idx = lyr_defn.GetFieldIndex("without_domain_initially")
|
|
fld_defn = lyr_defn.GetFieldDefn(idx)
|
|
fld_defn = ogr.FieldDefn(fld_defn.GetName(), fld_defn.GetType())
|
|
fld_defn.SetDomainName("range_domain_int")
|
|
assert lyr.AlterFieldDefn(idx, fld_defn, ogr.ALTER_ALL_FLAG) == 0
|
|
|
|
# Don't change anything
|
|
idx = lyr_defn.GetFieldIndex("with_glob_domain")
|
|
fld_defn = lyr_defn.GetFieldDefn(idx)
|
|
assert lyr.AlterFieldDefn(idx, fld_defn, ogr.ALTER_ALL_FLAG) == 0
|
|
|
|
ds = None
|
|
|
|
assert validate(filename), "validation failed"
|
|
|
|
# Test read support
|
|
ds = gdal.OpenEx(filename, gdal.OF_VECTOR)
|
|
lyr = ds.GetLayer(0)
|
|
lyr_defn = lyr.GetLayerDefn()
|
|
|
|
idx = lyr_defn.GetFieldIndex("with_range_domain_int")
|
|
fld_defn = lyr_defn.GetFieldDefn(idx)
|
|
assert fld_defn.GetDomainName() == ""
|
|
|
|
idx = lyr_defn.GetFieldIndex("with_range_domain_int64")
|
|
fld_defn = lyr_defn.GetFieldDefn(idx)
|
|
assert fld_defn.GetDomainName() == "with_enum_domain"
|
|
|
|
idx = lyr_defn.GetFieldIndex("without_domain_initially")
|
|
fld_defn = lyr_defn.GetFieldDefn(idx)
|
|
assert fld_defn.GetDomainName() == "range_domain_int"
|
|
|
|
idx = lyr_defn.GetFieldIndex("with_glob_domain")
|
|
fld_defn = lyr_defn.GetFieldDefn(idx)
|
|
assert fld_defn.GetDomainName() == "glob_domain"
|
|
|
|
assert set(ds.GetFieldDomainNames()) == {
|
|
"enum_domain",
|
|
"enum_domain_guess_int",
|
|
"enum_domain_guess_int64",
|
|
"enum_domain_guess_int64_single_1",
|
|
"enum_domain_guess_int64_single_2",
|
|
"enum_domain_guess_int_single",
|
|
"enum_domain_guess_real",
|
|
"enum_domain_guess_real_single",
|
|
"enum_domain_guess_string",
|
|
"enum_domain_guess_string_single",
|
|
"glob_domain",
|
|
"range_domain_int",
|
|
"range_domain_int64",
|
|
"range_domain_real",
|
|
"range_domain_real_inf",
|
|
}
|
|
|
|
ds = None
|
|
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test error cases in field domains
|
|
|
|
|
|
def test_ogr_gpkg_field_domains_errors():
|
|
|
|
filename = "/vsimem/test.gpkg"
|
|
|
|
ds = gdal.GetDriverByName("GPKG").Create(filename, 0, 0, 0, gdal.GDT_Unknown)
|
|
ds.CreateLayer("test")
|
|
# The DDL lacks on purpose the NOT NULL constraints on constraint_name and constraint_type
|
|
ds.ExecuteSQL(
|
|
"CREATE TABLE gpkg_data_column_constraints ("
|
|
+ "constraint_name TEXT,constraint_type TEXT,value TEXT,"
|
|
+ "min NUMERIC,min_is_inclusive BOOLEAN,"
|
|
+ "max NUMERIC,max_is_inclusive BOOLEAN,description TEXT)"
|
|
)
|
|
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_data_column_constraints VALUES "
|
|
+ "('null_constraint_type', NULL, NULL, NULL, NULL, NULL, NULL, NULL)"
|
|
)
|
|
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_data_column_constraints VALUES "
|
|
+ "('invalid_constraint_type', 'invalid', NULL, NULL, NULL, NULL, NULL, NULL)"
|
|
)
|
|
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_data_column_constraints VALUES "
|
|
+ "('mix_glob_enum', 'glob', '*', NULL, NULL, NULL, NULL, NULL)"
|
|
)
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_data_column_constraints VALUES "
|
|
+ "('mix_glob_enum', 'enum', 'foo', NULL, NULL, NULL, NULL, 'bar')"
|
|
)
|
|
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_data_column_constraints VALUES "
|
|
+ "('null_in_enum_code', 'enum', NULL, NULL, NULL, NULL, NULL, 'bar')"
|
|
)
|
|
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_data_column_constraints VALUES "
|
|
+ "('null_in_glob_value', 'glob', NULL, NULL, NULL, NULL, NULL, NULL)"
|
|
)
|
|
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_data_column_constraints VALUES "
|
|
+ "('null_in_range', 'range', NULL, NULL, NULL, NULL, NULL, NULL)"
|
|
)
|
|
ds = None
|
|
|
|
ds = gdal.OpenEx(filename, gdal.OF_VECTOR)
|
|
|
|
assert ds.GetFieldDomain("null_constraint_type") is None
|
|
|
|
with gdaltest.error_handler():
|
|
gdal.ErrorReset()
|
|
assert ds.GetFieldDomain("invalid_constraint_type") is None
|
|
assert gdal.GetLastErrorMsg() != ""
|
|
|
|
with gdaltest.error_handler():
|
|
gdal.ErrorReset()
|
|
assert ds.GetFieldDomain("mix_glob_enum") is None
|
|
assert gdal.GetLastErrorMsg() != ""
|
|
|
|
with gdaltest.error_handler():
|
|
gdal.ErrorReset()
|
|
assert ds.GetFieldDomain("null_in_enum_code") is None
|
|
assert gdal.GetLastErrorMsg() != ""
|
|
|
|
with gdaltest.error_handler():
|
|
gdal.ErrorReset()
|
|
assert ds.GetFieldDomain("null_in_glob_value") is None
|
|
assert gdal.GetLastErrorMsg() != ""
|
|
|
|
# This is non conformant, but we accept it
|
|
domain = ds.GetFieldDomain("null_in_range")
|
|
assert domain is not None
|
|
assert domain.GetMinAsDouble() == -math.inf
|
|
assert domain.GetMaxAsDouble() == math.inf
|
|
|
|
ds = None
|
|
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test gpkg_data_column_constraints of GPKG 1.0
|
|
|
|
|
|
def test_ogr_gpkg_field_domain_gpkg_1_0():
|
|
|
|
filename = "/vsimem/test.gpkg"
|
|
|
|
ds = gdal.GetDriverByName("GPKG").Create(
|
|
filename, 0, 0, 0, gdal.GDT_Unknown, options=["VERSION=1.0"]
|
|
)
|
|
ds.CreateLayer("test")
|
|
assert ds.AddFieldDomain(
|
|
ogr.CreateRangeFieldDomain(
|
|
"range_domain_int",
|
|
"my desc",
|
|
ogr.OFTReal,
|
|
ogr.OFSTNone,
|
|
1.5,
|
|
True,
|
|
2.5,
|
|
False,
|
|
)
|
|
)
|
|
ds = None
|
|
|
|
assert validate(filename)
|
|
|
|
ds = gdal.OpenEx(filename, gdal.OF_VECTOR)
|
|
|
|
gdal.ErrorReset()
|
|
domain = ds.GetFieldDomain("range_domain_int")
|
|
assert gdal.GetLastErrorMsg() == ""
|
|
assert domain is not None
|
|
assert domain.GetName() == "range_domain_int"
|
|
assert domain.GetDescription() == "my desc"
|
|
assert domain.GetDomainType() == ogr.OFDT_RANGE
|
|
assert domain.GetFieldType() == ogr.OFTReal
|
|
assert domain.GetMinAsDouble() == 1.5
|
|
assert domain.IsMinInclusive()
|
|
assert domain.GetMaxAsDouble() == 2.5
|
|
assert not domain.IsMaxInclusive()
|
|
|
|
ds = None
|
|
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test attribute and spatial views
|
|
|
|
|
|
def test_ogr_gpkg_views():
|
|
|
|
filename = "/vsimem/test_ogr_gpkg_views.gpkg"
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(filename)
|
|
lyr = ds.CreateLayer("foo", geom_type=ogr.wkbPoint)
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT(0 0)"))
|
|
lyr.CreateFeature(f)
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT(1 1)"))
|
|
lyr.CreateFeature(f)
|
|
|
|
ds.ExecuteSQL(
|
|
"CREATE VIEW geom_view AS SELECT fid AS my_fid, geom AS my_geom FROM foo"
|
|
)
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_contents (table_name, identifier, data_type, srs_id) VALUES ( 'geom_view', 'geom_view', 'features', 0 )"
|
|
)
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_geometry_columns (table_name, column_name, geometry_type_name, srs_id, z, m) values ('geom_view', 'my_geom', 'POINT', 0, 0, 0)"
|
|
)
|
|
|
|
ds.ExecuteSQL("CREATE VIEW attr_view AS SELECT fid AS my_fid FROM foo")
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_contents (table_name, identifier, data_type) VALUES ( 'attr_view', 'attr_view', 'attributes' )"
|
|
)
|
|
|
|
ds = None
|
|
|
|
assert validate(filename), "validation failed"
|
|
|
|
ds = ogr.Open(filename)
|
|
assert ds.GetLayerCount() == 3
|
|
|
|
lyr = ds.GetLayerByName("geom_view")
|
|
assert lyr.GetGeomType() == ogr.wkbPoint
|
|
|
|
lyr = ds.GetLayerByName("attr_view")
|
|
assert lyr.GetGeomType() == ogr.wkbNone
|
|
|
|
ds = None
|
|
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test a spatial view where the geometry column is computed with a
|
|
# Spatialite function
|
|
|
|
|
|
def test_ogr_gpkg_spatial_view_computed_geom_column():
|
|
|
|
filename = "/vsimem/test_ogr_gpkg_spatial_view_computed_geom_column.gpkg"
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(filename)
|
|
|
|
if not _has_spatialite_4_3_or_later(ds):
|
|
ds = None
|
|
gdal.Unlink(filename)
|
|
pytest.skip("spatialite missing")
|
|
|
|
lyr = ds.CreateLayer("foo", geom_type=ogr.wkbPoint)
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT(1 2)"))
|
|
lyr.CreateFeature(f)
|
|
|
|
ds.ExecuteSQL(
|
|
"CREATE VIEW geom_view AS SELECT fid AS my_fid, AsGPB(ST_Multi(geom)) AS my_geom FROM foo"
|
|
)
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_contents (table_name, identifier, data_type, srs_id) VALUES ( 'geom_view', 'geom_view', 'features', 4326 )"
|
|
)
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_geometry_columns (table_name, column_name, geometry_type_name, srs_id, z, m) values ('geom_view', 'my_geom', 'MULTIPOINT', 4326, 0, 0)"
|
|
)
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_extensions VALUES('geom_view', 'my_geom', 'gdal_spatialite_computed_geom_column', 'https://gdal.org/drivers/vector/gpkg_spatialite_computed_column.html', 'read-write')"
|
|
)
|
|
|
|
ds = None
|
|
|
|
import sqlite3
|
|
|
|
conn = sqlite3.connect(":memory:")
|
|
can_use_validate = False
|
|
try:
|
|
conn.enable_load_extension(True)
|
|
conn.execute('SELECT load_extension("mod_spatialite")')
|
|
can_use_validate = True
|
|
except Exception:
|
|
pass
|
|
conn.close()
|
|
if can_use_validate:
|
|
assert validate(filename), "validation failed"
|
|
else:
|
|
print("Cannot validate() due to mod_spatialite not being loadable")
|
|
|
|
ds = ogr.Open(filename)
|
|
|
|
lyr = ds.GetLayerByName("geom_view")
|
|
assert lyr.GetGeomType() == ogr.wkbMultiPoint
|
|
assert lyr.GetSpatialRef().GetAuthorityCode(None) == "4326"
|
|
f = lyr.GetNextFeature()
|
|
assert f.GetGeometryRef().ExportToWkt() == "MULTIPOINT (1 2)"
|
|
|
|
ds = None
|
|
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test read support for legacy gdal_aspatial extension
|
|
|
|
|
|
def test_ogr_gpkg_read_deprecated_gdal_aspatial():
|
|
|
|
filename = "/vsimem/test_ogr_gpkg_aspatial.gpkg"
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(filename)
|
|
ds.ExecuteSQL(
|
|
"CREATE TABLE gpkg_extensions ("
|
|
"table_name TEXT,"
|
|
"column_name TEXT,"
|
|
"extension_name TEXT NOT NULL,"
|
|
"definition TEXT NOT NULL,"
|
|
"scope TEXT NOT NULL,"
|
|
"CONSTRAINT ge_tce UNIQUE (table_name, column_name, extension_name)"
|
|
")"
|
|
)
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_extensions "
|
|
"(table_name, column_name, extension_name, definition, scope) "
|
|
"VALUES "
|
|
"(NULL, NULL, 'gdal_aspatial', 'http://gdal.org/geopackage_aspatial.html', 'read-write')"
|
|
)
|
|
ds.ExecuteSQL("CREATE TABLE aspatial_layer(fid INTEGER PRIMARY KEY,bar TEXT)")
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_contents (table_name, data_type) VALUES ('aspatial_layer', 'aspatial')"
|
|
)
|
|
ds.CreateLayer("spatial_layer", options=["SPATIAL_INDEX=NO"])
|
|
ds = None
|
|
|
|
ds = ogr.Open(filename)
|
|
assert ds.GetLayerCount() == 2
|
|
ds = None
|
|
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test fixing up wrong gpkg_metadata_reference_column_name_update trigger (GDAL < 2.4.0)
|
|
|
|
|
|
def test_ogr_gpkg_fixup_wrong_mr_column_name_update_trigger():
|
|
|
|
filename = "/vsimem/test_ogr_gpkg_fixup_wrong_mr_column_name_update_trigger.gpkg"
|
|
ds = ogr.GetDriverByName("GPKG").CreateDataSource(filename)
|
|
ds.SetMetadata("FOO", "BAR")
|
|
ds = None
|
|
|
|
ds = ogr.Open(filename, update=1)
|
|
# inject wrong trigger on purpose
|
|
wrong_trigger = (
|
|
"CREATE TRIGGER 'gpkg_metadata_reference_column_name_update' "
|
|
+ "BEFORE UPDATE OF column_name ON 'gpkg_metadata_reference' "
|
|
+ "FOR EACH ROW BEGIN "
|
|
+ "SELECT RAISE(ABORT, 'update on table gpkg_metadata_reference "
|
|
+ "violates constraint: column name must be NULL when reference_scope "
|
|
+ 'is "geopackage", "table" or "row"\') '
|
|
+ "WHERE (NEW.reference_scope IN ('geopackage','table','row') "
|
|
+ "AND NEW.column_nameIS NOT NULL); END;"
|
|
)
|
|
ds.ExecuteSQL(wrong_trigger)
|
|
ds = None
|
|
|
|
# Open in update mode
|
|
ds = ogr.Open(filename, update=1)
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT sql FROM sqlite_master WHERE type = 'trigger' "
|
|
+ "AND name = 'gpkg_metadata_reference_column_name_update'"
|
|
)
|
|
f = sql_lyr.GetNextFeature()
|
|
sql = f["sql"]
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
ds = None
|
|
|
|
gdal.Unlink(filename)
|
|
assert "column_nameIS" not in sql
|
|
|
|
|
|
###############################################################################
|
|
# Test support for CRS coordinate_epoch
|
|
|
|
|
|
def test_ogr_gpkg_crs_coordinate_epoch():
|
|
|
|
filename = "/vsimem/test_ogr_gpkg_crs_coordinate_epoch.gpkg"
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(filename)
|
|
|
|
srs = osr.SpatialReference()
|
|
srs.SetFromUserInput("+proj=longlat +ellps=GRS80 +towgs84=0,0,0")
|
|
srs.SetCoordinateEpoch(2021.3)
|
|
ds.CreateLayer("lyr_with_coordinate_epoch_unknown_srs", srs=srs)
|
|
|
|
srs = osr.SpatialReference()
|
|
srs.ImportFromEPSG(7665) # WGS 84 (G1762) (3D)
|
|
srs.SetCoordinateEpoch(2021.3)
|
|
ds.CreateLayer("lyr_with_coordinate_epoch", srs=srs)
|
|
|
|
srs.SetCoordinateEpoch(2021.3)
|
|
ds.CreateLayer("lyr_with_same_coordinate_epoch", srs=srs)
|
|
|
|
srs.SetCoordinateEpoch(2021.2)
|
|
ds.CreateLayer("lyr_with_different_coordinate_epoch", srs=srs)
|
|
|
|
srs = osr.SpatialReference()
|
|
srs.ImportFromEPSG(4258) # ETRS89
|
|
ds.CreateLayer("lyr_without_coordinate_epoch", srs=srs)
|
|
|
|
ds = None
|
|
|
|
assert validate(filename), "validation failed"
|
|
|
|
ds = ogr.Open(filename)
|
|
|
|
sql_lyr = ds.ExecuteSQL("SELECT * FROM gpkg_spatial_ref_sys ORDER BY srs_id")
|
|
assert sql_lyr.GetFeatureCount() == 7
|
|
|
|
sql_lyr.GetNextFeature()
|
|
sql_lyr.GetNextFeature()
|
|
|
|
f = sql_lyr.GetNextFeature()
|
|
assert f
|
|
assert f["srs_id"] == 4258
|
|
assert f["organization"] == "EPSG"
|
|
assert f["organization_coordsys_id"] == 4258
|
|
assert f["epoch"] is None
|
|
|
|
f = sql_lyr.GetNextFeature()
|
|
assert f
|
|
assert f["srs_id"] == 4326
|
|
assert f["organization"] == "EPSG"
|
|
assert f["organization_coordsys_id"] == 4326
|
|
assert f["epoch"] is None
|
|
|
|
f = sql_lyr.GetNextFeature()
|
|
assert f
|
|
assert f["srs_id"] == 100000
|
|
assert f["organization"] == "NONE"
|
|
assert f["organization_coordsys_id"] == 100000
|
|
assert f["epoch"] == 2021.3
|
|
|
|
f = sql_lyr.GetNextFeature()
|
|
assert f
|
|
assert f["srs_id"] == 100001
|
|
assert f["organization"] == "EPSG"
|
|
assert f["organization_coordsys_id"] == 7665
|
|
assert f["epoch"] == 2021.3
|
|
|
|
f = sql_lyr.GetNextFeature()
|
|
assert f
|
|
assert f["srs_id"] == 100002
|
|
assert f["organization"] == "EPSG"
|
|
assert f["organization_coordsys_id"] == 7665
|
|
assert f["epoch"] == 2021.2
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
|
|
lyr = ds.GetLayerByName("lyr_with_coordinate_epoch_unknown_srs")
|
|
srs = lyr.GetSpatialRef()
|
|
assert srs.GetCoordinateEpoch() == 2021.3
|
|
|
|
lyr = ds.GetLayerByName("lyr_with_coordinate_epoch")
|
|
srs = lyr.GetSpatialRef()
|
|
assert srs.GetCoordinateEpoch() == 2021.3
|
|
|
|
lyr = ds.GetLayerByName("lyr_with_same_coordinate_epoch")
|
|
srs = lyr.GetSpatialRef()
|
|
assert srs.GetCoordinateEpoch() == 2021.3
|
|
|
|
lyr = ds.GetLayerByName("lyr_with_different_coordinate_epoch")
|
|
srs = lyr.GetSpatialRef()
|
|
assert srs.GetCoordinateEpoch() == 2021.2
|
|
|
|
lyr = ds.GetLayerByName("lyr_without_coordinate_epoch")
|
|
srs = lyr.GetSpatialRef()
|
|
assert srs.GetCoordinateEpoch() == 0
|
|
|
|
ds = None
|
|
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test CPL_VSIL_USE_TEMP_FILE_FOR_RANDOM_WRITE
|
|
|
|
|
|
def test_ogr_gpkg_CPL_VSIL_USE_TEMP_FILE_FOR_RANDOM_WRITE():
|
|
|
|
# First check that CPL_TMPDIR is ignored for regular files
|
|
filename = "/vsimem/test_ogr_gpkg_CPL_VSIL_USE_TEMP_FILE_FOR_RANDOM_WRITE.gpkg"
|
|
with gdaltest.config_option("CPL_TMPDIR", "/i_do/not/exist"):
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(filename)
|
|
assert ds is not None
|
|
ds = None
|
|
gdal.Unlink(filename)
|
|
|
|
# Now check that CPL_TMPDIR is honored for CPL_VSIL_USE_TEMP_FILE_FOR_RANDOM_WRITE=FORCED
|
|
with gdaltest.config_options(
|
|
{
|
|
"CPL_TMPDIR": "/vsimem/temporary_location",
|
|
"CPL_VSIL_USE_TEMP_FILE_FOR_RANDOM_WRITE": "FORCED",
|
|
}
|
|
):
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(filename)
|
|
assert ds is not None
|
|
assert gdal.VSIStatL(filename) is None
|
|
assert len(gdal.ReadDir("/vsimem/temporary_location")) != 0
|
|
ds = None
|
|
assert gdal.VSIStatL(filename) is not None
|
|
assert gdal.ReadDir("/vsimem/temporary_location") is None
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test support for related tables extension
|
|
|
|
|
|
def test_ogr_gpkg_relations():
|
|
|
|
filename = "/vsimem/test_ogr_gpkg_relations.gpkg"
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(filename)
|
|
lyr = ds.CreateLayer("a")
|
|
lyr.CreateField(ogr.FieldDefn("some_id", ogr.OFTInteger))
|
|
lyr = ds.CreateLayer("b")
|
|
lyr.CreateField(ogr.FieldDefn("other_id", ogr.OFTInteger))
|
|
ds = None
|
|
|
|
ds = gdal.OpenEx(filename, gdal.OF_VECTOR | gdal.OF_UPDATE)
|
|
assert ds.GetRelationshipNames() is None
|
|
|
|
ds.ExecuteSQL(
|
|
"""CREATE TABLE 'gpkgext_relations' (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
base_table_name TEXT NOT NULL,
|
|
base_primary_column TEXT NOT NULL DEFAULT 'id',
|
|
related_table_name TEXT NOT NULL,
|
|
related_primary_column TEXT NOT NULL DEFAULT 'id',
|
|
relation_name TEXT NOT NULL,
|
|
mapping_table_name TEXT NOT NULL UNIQUE
|
|
);"""
|
|
)
|
|
|
|
# not yet valid...
|
|
ds = gdal.OpenEx(filename, gdal.OF_VECTOR | gdal.OF_UPDATE)
|
|
assert ds.GetRelationshipNames() is None
|
|
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkgext_relations VALUES(1, 'a', 'some_id', 'b', 'other_id', 'attributes', 'my_mapping_table')"
|
|
)
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_extensions VALUES('gpkgext_relations',NULL,'gpkg_related_tables','http://www.geopackage.org/18-000.html','read-write');"
|
|
)
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_extensions VALUES('my_mapping_table',NULL,'gpkg_related_tables','http://www.geopackage.org/18-000.html','read-write');"
|
|
)
|
|
ds = None
|
|
|
|
ds = gdal.OpenEx(filename, gdal.OF_VECTOR | gdal.OF_UPDATE)
|
|
assert ds.GetRelationshipNames() is None
|
|
ds.ExecuteSQL(
|
|
"""CREATE TABLE my_mapping_table(base_id INTEGER NOT NULL, related_id INTEGER NOT NULL);"""
|
|
)
|
|
|
|
assert validate(filename), "validation failed"
|
|
|
|
ds = gdal.OpenEx(filename, gdal.OF_VECTOR | gdal.OF_UPDATE)
|
|
assert ds.GetRelationshipNames() == ["a_b_attributes"]
|
|
assert ds.GetRelationship("xxx") is None
|
|
rel = ds.GetRelationship("a_b_attributes")
|
|
assert rel is not None
|
|
assert rel.GetName() == "a_b_attributes"
|
|
assert rel.GetLeftTableName() == "a"
|
|
assert rel.GetRightTableName() == "b"
|
|
assert rel.GetMappingTableName() == "my_mapping_table"
|
|
assert rel.GetCardinality() == gdal.GRC_MANY_TO_MANY
|
|
assert rel.GetType() == gdal.GRT_ASSOCIATION
|
|
assert rel.GetLeftTableFields() == ["some_id"]
|
|
assert rel.GetRightTableFields() == ["other_id"]
|
|
assert rel.GetLeftMappingTableFields() == ["base_id"]
|
|
assert rel.GetRightMappingTableFields() == ["related_id"]
|
|
assert rel.GetRelatedTableType() == "attributes"
|
|
|
|
lyr = ds.GetLayer("a")
|
|
lyr.Rename("a_renamed")
|
|
lyr.AlterFieldDefn(
|
|
lyr.GetLayerDefn().GetFieldIndex("some_id"),
|
|
ogr.FieldDefn("some_id_renamed", ogr.OFTInteger),
|
|
ogr.ALTER_ALL_FLAG,
|
|
)
|
|
lyr = ds.GetLayer("b")
|
|
lyr.Rename("b_renamed")
|
|
lyr.AlterFieldDefn(
|
|
lyr.GetLayerDefn().GetFieldIndex("other_id"),
|
|
ogr.FieldDefn("other_id_renamed", ogr.OFTInteger),
|
|
ogr.ALTER_ALL_FLAG,
|
|
)
|
|
|
|
assert ds.GetRelationshipNames() == ["a_renamed_b_renamed_attributes"]
|
|
assert ds.GetRelationship("xxx") is None
|
|
rel = ds.GetRelationship("a_renamed_b_renamed_attributes")
|
|
assert rel is not None
|
|
assert rel.GetName() == "a_renamed_b_renamed_attributes"
|
|
assert rel.GetLeftTableName() == "a_renamed"
|
|
assert rel.GetRightTableName() == "b_renamed"
|
|
assert rel.GetMappingTableName() == "my_mapping_table"
|
|
assert rel.GetCardinality() == gdal.GRC_MANY_TO_MANY
|
|
assert rel.GetType() == gdal.GRT_ASSOCIATION
|
|
assert rel.GetLeftTableFields() == ["some_id_renamed"]
|
|
assert rel.GetRightTableFields() == ["other_id_renamed"]
|
|
assert rel.GetLeftMappingTableFields() == ["base_id"]
|
|
assert rel.GetRightMappingTableFields() == ["related_id"]
|
|
assert rel.GetRelatedTableType() == "attributes"
|
|
|
|
ds = None
|
|
assert validate(filename), "validation failed"
|
|
|
|
ds = gdal.OpenEx(filename, gdal.OF_VECTOR | gdal.OF_UPDATE)
|
|
ds.ExecuteSQL("DELLAYER:a_renamed")
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT * FROM gpkg_extensions WHERE extension_name IN ('related_tables', 'gpkg_related_tables')"
|
|
)
|
|
f = sql_lyr.GetNextFeature()
|
|
assert f is None
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
assert ds.GetRelationshipNames() is None
|
|
ds = None
|
|
|
|
assert validate(filename), "validation failed"
|
|
|
|
# user defined relation
|
|
ds = ogr.Open(filename, update=1)
|
|
lyr = ds.CreateLayer("a")
|
|
lyr.CreateField(ogr.FieldDefn("some_id", ogr.OFTInteger))
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkgext_relations VALUES(1, 'a', 'some_id', 'b', 'other_id', 'custom_type', 'my_mapping_table')"
|
|
)
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_extensions VALUES('gpkgext_relations',NULL,'gpkg_related_tables','http://www.geopackage.org/18-000.html','read-write');"
|
|
)
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_extensions VALUES('my_mapping_table',NULL,'gpkg_related_tables','http://www.geopackage.org/18-000.html','read-write');"
|
|
)
|
|
|
|
ds = gdal.OpenEx(filename, gdal.OF_VECTOR)
|
|
assert ds.GetRelationshipNames() == ["custom_type"]
|
|
assert ds.GetRelationship("xxx") is None
|
|
rel = ds.GetRelationship("custom_type")
|
|
assert rel is not None
|
|
assert rel.GetName() == "custom_type"
|
|
assert rel.GetLeftTableName() == "a"
|
|
assert rel.GetRightTableName() == "b"
|
|
assert rel.GetMappingTableName() == "my_mapping_table"
|
|
assert rel.GetCardinality() == gdal.GRC_MANY_TO_MANY
|
|
assert rel.GetType() == gdal.GRT_ASSOCIATION
|
|
assert rel.GetLeftTableFields() == ["some_id"]
|
|
assert rel.GetRightTableFields() == ["other_id"]
|
|
assert rel.GetLeftMappingTableFields() == ["base_id"]
|
|
assert rel.GetRightMappingTableFields() == ["related_id"]
|
|
assert rel.GetRelatedTableType() == "features"
|
|
|
|
ds = None
|
|
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test support for relations taken from sqlite foreign keys when related tables
|
|
# extension is not used
|
|
|
|
|
|
def test_ogr_gpkg_relations_sqlite_foreign_keys():
|
|
try:
|
|
tmpfilename = "/vsimem/test_ogr_gpkg_relations_sqlite.gpkg"
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(tmpfilename)
|
|
lyr = ds.CreateLayer("a")
|
|
lyr.CreateField(ogr.FieldDefn("some_id", ogr.OFTInteger))
|
|
lyr = ds.CreateLayer("b")
|
|
lyr.CreateField(ogr.FieldDefn("other_id", ogr.OFTInteger))
|
|
ds = None
|
|
|
|
ds = gdal.OpenEx(tmpfilename, gdal.OF_VECTOR | gdal.OF_UPDATE)
|
|
assert ds.GetRelationshipNames() is None
|
|
|
|
ds.ExecuteSQL(
|
|
"CREATE TABLE test_relation_a(artistid INTEGER PRIMARY KEY, artistname TEXT)"
|
|
)
|
|
ds.ExecuteSQL(
|
|
"CREATE TABLE test_relation_b(trackid INTEGER, trackname TEXT, trackartist INTEGER, FOREIGN KEY(trackartist) REFERENCES test_relation_a(artistid))"
|
|
)
|
|
ds = None
|
|
|
|
ds = gdal.OpenEx(tmpfilename, gdal.OF_VECTOR | gdal.OF_UPDATE)
|
|
assert ds.GetRelationshipNames() == ["test_relation_a_test_relation_b"]
|
|
assert ds.GetRelationship("xxx") is None
|
|
rel = ds.GetRelationship("test_relation_a_test_relation_b")
|
|
assert rel is not None
|
|
assert rel.GetName() == "test_relation_a_test_relation_b"
|
|
assert rel.GetLeftTableName() == "test_relation_a"
|
|
assert rel.GetRightTableName() == "test_relation_b"
|
|
assert rel.GetCardinality() == gdal.GRC_ONE_TO_MANY
|
|
assert rel.GetType() == gdal.GRT_ASSOCIATION
|
|
assert rel.GetLeftTableFields() == ["artistid"]
|
|
assert rel.GetRightTableFields() == ["trackartist"]
|
|
assert rel.GetRelatedTableType() == "features"
|
|
|
|
finally:
|
|
gdal.Unlink(tmpfilename)
|
|
|
|
|
|
###############################################################################
|
|
# Test support for altering relationships
|
|
|
|
|
|
def test_ogr_gpkg_alter_relations():
|
|
def clone_relationship(relationship):
|
|
res = gdal.Relationship(
|
|
relationship.GetName(),
|
|
relationship.GetLeftTableName(),
|
|
relationship.GetRightTableName(),
|
|
relationship.GetCardinality(),
|
|
)
|
|
res.SetLeftTableFields(relationship.GetLeftTableFields())
|
|
res.SetRightTableFields(relationship.GetRightTableFields())
|
|
res.SetMappingTableName(relationship.GetMappingTableName())
|
|
res.SetLeftMappingTableFields(relationship.GetLeftMappingTableFields())
|
|
res.SetRightMappingTableFields(relationship.GetRightMappingTableFields())
|
|
res.SetType(relationship.GetType())
|
|
res.SetForwardPathLabel(relationship.GetForwardPathLabel())
|
|
res.SetBackwardPathLabel(relationship.GetBackwardPathLabel())
|
|
res.SetRelatedTableType(relationship.GetRelatedTableType())
|
|
|
|
return res
|
|
|
|
filename = "/vsimem/test_ogr_gpkg_relation_create.gpkg"
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(filename)
|
|
|
|
def get_query_row_count(query):
|
|
sql_lyr = ds.ExecuteSQL(query)
|
|
res = sql_lyr.GetFeatureCount()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
return res
|
|
|
|
relationship = gdal.Relationship(
|
|
"my_relationship", "origin_table", "dest_table", gdal.GRC_MANY_TO_MANY
|
|
)
|
|
relationship.SetLeftTableFields(["o_pkey"])
|
|
relationship.SetRightTableFields(["dest_pkey"])
|
|
relationship.SetRelatedTableType("media")
|
|
|
|
ds = gdal.OpenEx(filename, gdal.OF_VECTOR | gdal.OF_UPDATE)
|
|
|
|
# no tables yet
|
|
assert not ds.AddRelationship(relationship)
|
|
|
|
lyr = ds.CreateLayer("origin_table", geom_type=ogr.wkbNone)
|
|
fld_defn = ogr.FieldDefn("o_pkey", ogr.OFTInteger)
|
|
assert lyr.CreateField(fld_defn) == ogr.OGRERR_NONE
|
|
fld_defn = ogr.FieldDefn("o_pkey2", ogr.OFTInteger)
|
|
assert lyr.CreateField(fld_defn) == ogr.OGRERR_NONE
|
|
ds.ExecuteSQL("CREATE UNIQUE INDEX origin_table_o_pkey_idx ON origin_table(o_pkey)")
|
|
|
|
assert not ds.AddRelationship(relationship)
|
|
|
|
lyr = ds.CreateLayer("dest_table", geom_type=ogr.wkbNone)
|
|
fld_defn = ogr.FieldDefn("dest_pkey", ogr.OFTInteger)
|
|
assert lyr.CreateField(fld_defn) == ogr.OGRERR_NONE
|
|
fld_defn = ogr.FieldDefn("dest_pkey2", ogr.OFTInteger)
|
|
assert lyr.CreateField(fld_defn) == ogr.OGRERR_NONE
|
|
ds.ExecuteSQL(
|
|
"CREATE UNIQUE INDEX dest_table_dest_pkey_idx ON dest_table(dest_pkey)"
|
|
)
|
|
|
|
# left table fields must be set, only one field
|
|
relationship.SetLeftTableFields([])
|
|
assert not ds.AddRelationship(relationship)
|
|
relationship.SetLeftTableFields(["o_pkey", "another"])
|
|
assert not ds.AddRelationship(relationship)
|
|
# left table field must exist
|
|
relationship.SetLeftTableFields(["o_pkey_nope"])
|
|
assert not ds.AddRelationship(relationship)
|
|
|
|
relationship.SetLeftTableFields(["o_pkey"])
|
|
|
|
# right table fields must be set, only one field
|
|
relationship.SetRightTableFields([])
|
|
assert not ds.AddRelationship(relationship)
|
|
relationship.SetRightTableFields(["dest_pkey", "another"])
|
|
assert not ds.AddRelationship(relationship)
|
|
# right table field must exist
|
|
relationship.SetRightTableFields(["dest_pkey_nope"])
|
|
assert not ds.AddRelationship(relationship)
|
|
|
|
relationship.SetRightTableFields(["dest_pkey"])
|
|
|
|
assert ds.AddRelationship(relationship)
|
|
|
|
assert set(ds.GetRelationshipNames()) == {"origin_table_dest_table_media"}
|
|
retrieved_rel = ds.GetRelationship("origin_table_dest_table_media")
|
|
assert retrieved_rel.GetCardinality() == gdal.GRC_MANY_TO_MANY
|
|
assert retrieved_rel.GetType() == gdal.GRT_ASSOCIATION
|
|
assert retrieved_rel.GetLeftTableName() == "origin_table"
|
|
assert retrieved_rel.GetRightTableName() == "dest_table"
|
|
assert retrieved_rel.GetLeftTableFields() == ["o_pkey"]
|
|
assert retrieved_rel.GetRightTableFields() == ["dest_pkey"]
|
|
assert retrieved_rel.GetRelatedTableType() == "media"
|
|
assert retrieved_rel.GetMappingTableName() == "origin_table_dest_table"
|
|
assert retrieved_rel.GetLeftMappingTableFields() == ["base_id"]
|
|
assert retrieved_rel.GetRightMappingTableFields() == ["related_id"]
|
|
|
|
# try again, should fail because relationship already exists
|
|
assert not ds.AddRelationship(relationship)
|
|
|
|
# validate that extensions table exists and is correctly populated
|
|
assert (
|
|
get_query_row_count(
|
|
"SELECT * FROM gpkg_extensions WHERE table_name = 'gpkgext_relations' AND extension_name = 'gpkg_related_tables'"
|
|
)
|
|
== 1
|
|
)
|
|
assert (
|
|
get_query_row_count(
|
|
"SELECT * FROM gpkg_extensions WHERE table_name = 'origin_table_dest_table' AND extension_name = 'gpkg_related_tables'"
|
|
)
|
|
== 1
|
|
)
|
|
|
|
# validate gpkgext_relations has been populated correctly
|
|
assert (
|
|
get_query_row_count(
|
|
"SELECT * FROM gpkgext_relations WHERE base_table_name = 'origin_table' AND "
|
|
"base_primary_column = 'o_pkey' AND "
|
|
"related_table_name = 'dest_table' AND "
|
|
"related_primary_column = 'dest_pkey' AND "
|
|
"relation_name = 'media' AND "
|
|
"mapping_table_name = 'origin_table_dest_table'"
|
|
)
|
|
== 1
|
|
)
|
|
|
|
lyr = ds.CreateLayer("origin_table2", geom_type=ogr.wkbNone)
|
|
fld_defn = ogr.FieldDefn("o_pkey", ogr.OFTInteger)
|
|
assert lyr.CreateField(fld_defn) == ogr.OGRERR_NONE
|
|
|
|
lyr = ds.CreateLayer("dest_table2", geom_type=ogr.wkbNone)
|
|
fld_defn = ogr.FieldDefn("dest_pkey", ogr.OFTInteger)
|
|
assert lyr.CreateField(fld_defn) == ogr.OGRERR_NONE
|
|
|
|
# only many-to-many relationships are supported
|
|
relationship = gdal.Relationship(
|
|
"my_relationship", "origin_table2", "dest_table2", gdal.GRC_ONE_TO_ONE
|
|
)
|
|
relationship.SetLeftTableFields(["o_pkey"])
|
|
relationship.SetRightTableFields(["dest_pkey"])
|
|
relationship.SetRelatedTableType("features")
|
|
assert not ds.AddRelationship(relationship)
|
|
|
|
relationship = gdal.Relationship(
|
|
"my_relationship", "origin_table2", "dest_table2", gdal.GRC_ONE_TO_MANY
|
|
)
|
|
relationship.SetLeftTableFields(["o_pkey"])
|
|
relationship.SetRightTableFields(["dest_pkey"])
|
|
relationship.SetRelatedTableType("features")
|
|
assert not ds.AddRelationship(relationship)
|
|
|
|
relationship = gdal.Relationship(
|
|
"my_relationship", "origin_table2", "dest_table2", gdal.GRC_MANY_TO_ONE
|
|
)
|
|
relationship.SetLeftTableFields(["o_pkey"])
|
|
relationship.SetRightTableFields(["dest_pkey"])
|
|
relationship.SetRelatedTableType("features")
|
|
assert not ds.AddRelationship(relationship)
|
|
|
|
# only features/media/simple_attributes/attributes/tiles related table type are supported
|
|
relationship = gdal.Relationship(
|
|
"my_relationship", "origin_table2", "dest_table2", gdal.GRC_MANY_TO_MANY
|
|
)
|
|
relationship.SetLeftTableFields(["o_pkey"])
|
|
relationship.SetRightTableFields(["dest_pkey"])
|
|
relationship.SetRelatedTableType("something else")
|
|
assert not ds.AddRelationship(relationship)
|
|
|
|
# should default to "features" related table type if nothing explicitly specified
|
|
relationship.SetRelatedTableType("")
|
|
assert ds.AddRelationship(relationship)
|
|
|
|
assert set(ds.GetRelationshipNames()) == {
|
|
"origin_table_dest_table_media",
|
|
"origin_table2_dest_table2_features",
|
|
}
|
|
retrieved_rel = ds.GetRelationship("origin_table2_dest_table2_features")
|
|
assert retrieved_rel.GetCardinality() == gdal.GRC_MANY_TO_MANY
|
|
assert retrieved_rel.GetType() == gdal.GRT_ASSOCIATION
|
|
assert retrieved_rel.GetLeftTableName() == "origin_table2"
|
|
assert retrieved_rel.GetRightTableName() == "dest_table2"
|
|
assert retrieved_rel.GetLeftTableFields() == ["o_pkey"]
|
|
assert retrieved_rel.GetRightTableFields() == ["dest_pkey"]
|
|
assert retrieved_rel.GetRelatedTableType() == "features"
|
|
|
|
# validate that extensions table exists is correctly populated
|
|
assert (
|
|
get_query_row_count(
|
|
"SELECT * FROM gpkg_extensions WHERE table_name = 'origin_table2_dest_table2' AND extension_name = 'gpkg_related_tables'"
|
|
)
|
|
== 1
|
|
)
|
|
# validate gpkgext_relations has been populated correctly
|
|
assert (
|
|
get_query_row_count(
|
|
"SELECT * FROM gpkgext_relations WHERE base_table_name = 'origin_table2' AND "
|
|
"base_primary_column = 'o_pkey' AND "
|
|
"related_table_name = 'dest_table2' AND "
|
|
"related_primary_column = 'dest_pkey' AND "
|
|
"relation_name = 'features' AND "
|
|
"mapping_table_name = 'origin_table2_dest_table2'"
|
|
)
|
|
== 1
|
|
)
|
|
|
|
# try with an existing mapping table
|
|
lyr = ds.CreateLayer("origin_table3", geom_type=ogr.wkbNone)
|
|
fld_defn = ogr.FieldDefn("o_pkey", ogr.OFTInteger)
|
|
assert lyr.CreateField(fld_defn) == ogr.OGRERR_NONE
|
|
|
|
lyr = ds.CreateLayer("dest_table3", geom_type=ogr.wkbNone)
|
|
fld_defn = ogr.FieldDefn("dest_pkey", ogr.OFTInteger)
|
|
assert lyr.CreateField(fld_defn) == ogr.OGRERR_NONE
|
|
|
|
lyr = ds.CreateLayer("origin_table3_to_dest_table_3_mapping", geom_type=ogr.wkbNone)
|
|
fld_defn = ogr.FieldDefn("base_id", ogr.OFTInteger)
|
|
assert lyr.CreateField(fld_defn) == ogr.OGRERR_NONE
|
|
fld_defn = ogr.FieldDefn("related_id", ogr.OFTInteger)
|
|
assert lyr.CreateField(fld_defn) == ogr.OGRERR_NONE
|
|
assert (
|
|
get_query_row_count(
|
|
"SELECT 1 FROM sqlite_master WHERE name = 'origin_table3_to_dest_table_3_mapping' AND type in ('table', 'view')"
|
|
)
|
|
== 1
|
|
)
|
|
|
|
relationship = gdal.Relationship(
|
|
"my_relationship", "origin_table3", "dest_table3", gdal.GRC_MANY_TO_MANY
|
|
)
|
|
# fid fields should be permitted for relationship use
|
|
relationship.SetLeftTableFields(["fid"])
|
|
relationship.SetRightTableFields(["fid"])
|
|
relationship.SetMappingTableName("nope")
|
|
assert not ds.AddRelationship(relationship)
|
|
|
|
relationship.SetMappingTableName("origin_table3_to_dest_table_3_mapping")
|
|
assert ds.AddRelationship(relationship)
|
|
|
|
assert set(ds.GetRelationshipNames()) == {
|
|
"origin_table_dest_table_media",
|
|
"origin_table2_dest_table2_features",
|
|
"origin_table3_dest_table3_features",
|
|
}
|
|
retrieved_rel = ds.GetRelationship("origin_table3_dest_table3_features")
|
|
assert retrieved_rel.GetCardinality() == gdal.GRC_MANY_TO_MANY
|
|
assert retrieved_rel.GetType() == gdal.GRT_ASSOCIATION
|
|
assert retrieved_rel.GetLeftTableName() == "origin_table3"
|
|
assert retrieved_rel.GetRightTableName() == "dest_table3"
|
|
assert retrieved_rel.GetLeftTableFields() == ["fid"]
|
|
assert retrieved_rel.GetRightTableFields() == ["fid"]
|
|
assert retrieved_rel.GetRelatedTableType() == "features"
|
|
assert (
|
|
retrieved_rel.GetMappingTableName() == "origin_table3_to_dest_table_3_mapping"
|
|
)
|
|
|
|
# validate that extensions table exists is correctly populated
|
|
assert (
|
|
get_query_row_count(
|
|
"SELECT * FROM gpkg_extensions WHERE table_name = 'origin_table3_to_dest_table_3_mapping' AND extension_name = 'gpkg_related_tables'"
|
|
)
|
|
== 1
|
|
)
|
|
|
|
# validate gpkgext_relations has been populated correctly
|
|
assert (
|
|
get_query_row_count(
|
|
"SELECT * FROM gpkgext_relations WHERE base_table_name = 'origin_table3' AND "
|
|
"base_primary_column = 'fid' AND "
|
|
"related_table_name = 'dest_table3' AND "
|
|
"related_primary_column = 'fid' AND "
|
|
"relation_name = 'features' AND "
|
|
"mapping_table_name = 'origin_table3_to_dest_table_3_mapping'"
|
|
)
|
|
== 1
|
|
)
|
|
|
|
# try again, with a mapping table which doesn't match requirements
|
|
lyr = ds.CreateLayer("origin_table4", geom_type=ogr.wkbNone)
|
|
fld_defn = ogr.FieldDefn("o_pkey", ogr.OFTInteger)
|
|
assert lyr.CreateField(fld_defn) == ogr.OGRERR_NONE
|
|
lyr = ds.CreateLayer("dest_table4", geom_type=ogr.wkbNone)
|
|
fld_defn = ogr.FieldDefn("dest_pkey", ogr.OFTInteger)
|
|
assert lyr.CreateField(fld_defn) == ogr.OGRERR_NONE
|
|
lyr = ds.CreateLayer("origin_table4_to_dest_table_4_mapping", geom_type=ogr.wkbNone)
|
|
fld_defn = ogr.FieldDefn("not_base_id", ogr.OFTInteger)
|
|
assert lyr.CreateField(fld_defn) == ogr.OGRERR_NONE
|
|
fld_defn = ogr.FieldDefn("related_id", ogr.OFTInteger)
|
|
assert lyr.CreateField(fld_defn) == ogr.OGRERR_NONE
|
|
assert (
|
|
get_query_row_count(
|
|
"SELECT 1 FROM sqlite_master WHERE name = 'origin_table4_to_dest_table_4_mapping' AND type in ('table', 'view')"
|
|
)
|
|
== 1
|
|
)
|
|
relationship = gdal.Relationship(
|
|
"my_relationship", "origin_table4", "dest_table4", gdal.GRC_MANY_TO_MANY
|
|
)
|
|
relationship.SetLeftTableFields(["o_pkey"])
|
|
relationship.SetRightTableFields(["dest_pkey"])
|
|
relationship.SetMappingTableName("origin_table4_to_dest_table_4_mapping")
|
|
assert not ds.AddRelationship(relationship)
|
|
lyr = ds.CreateLayer(
|
|
"origin_table4_to_dest_table_4_mappingv2", geom_type=ogr.wkbNone
|
|
)
|
|
fld_defn = ogr.FieldDefn("base_id", ogr.OFTInteger)
|
|
assert lyr.CreateField(fld_defn) == ogr.OGRERR_NONE
|
|
fld_defn = ogr.FieldDefn("not_related_id", ogr.OFTInteger)
|
|
assert lyr.CreateField(fld_defn) == ogr.OGRERR_NONE
|
|
assert (
|
|
get_query_row_count(
|
|
"SELECT 1 FROM sqlite_master WHERE name = 'origin_table4_to_dest_table_4_mappingv2' AND type in ('table', 'view')"
|
|
)
|
|
== 1
|
|
)
|
|
relationship.SetMappingTableName("origin_table4_to_dest_table_4_mappingv2")
|
|
assert not ds.AddRelationship(relationship)
|
|
|
|
ds = None
|
|
assert validate(filename), "validation failed"
|
|
ds = gdal.OpenEx(filename, gdal.OF_VECTOR | gdal.OF_UPDATE)
|
|
|
|
# delete relationship
|
|
assert not ds.DeleteRelationship("nope")
|
|
|
|
assert set(ds.GetRelationshipNames()) == {
|
|
"origin_table_dest_table_media",
|
|
"origin_table2_dest_table2_features",
|
|
"origin_table3_dest_table3_features",
|
|
}
|
|
|
|
assert ds.DeleteRelationship("origin_table2_dest_table2_features")
|
|
|
|
# validate that extensions table was correctly updated
|
|
assert (
|
|
get_query_row_count(
|
|
"SELECT * FROM gpkg_extensions WHERE table_name = 'origin_table2_dest_table2' AND extension_name = 'gpkg_related_tables'"
|
|
)
|
|
== 0
|
|
)
|
|
# validate gpkgext_relations has been updated correctly
|
|
assert (
|
|
get_query_row_count(
|
|
"SELECT * FROM gpkgext_relations WHERE base_table_name = 'origin_table2' AND "
|
|
"base_primary_column = 'o_pkey' AND "
|
|
"related_table_name = 'dest_table2' AND "
|
|
"related_primary_column = 'dest_pkey' AND "
|
|
"relation_name = 'features' AND "
|
|
"mapping_table_name = 'origin_table2_dest_table2'"
|
|
)
|
|
== 0
|
|
)
|
|
# validate that mapping table was deleted
|
|
assert (
|
|
get_query_row_count(
|
|
"SELECT 1 FROM sqlite_master WHERE name = 'origin_table2_dest_table2' AND type in ('table', 'view')"
|
|
)
|
|
== 0
|
|
)
|
|
|
|
assert set(ds.GetRelationshipNames()) == {
|
|
"origin_table_dest_table_media",
|
|
"origin_table3_dest_table3_features",
|
|
}
|
|
|
|
ds = None
|
|
assert validate(filename), "validation failed"
|
|
ds = gdal.OpenEx(filename, gdal.OF_VECTOR | gdal.OF_UPDATE)
|
|
|
|
# update relationship
|
|
retrieved_rel = ds.GetRelationship("origin_table_dest_table_media")
|
|
|
|
# can't update a relationship which doesn't exit
|
|
relationship = gdal.Relationship(
|
|
"nope",
|
|
retrieved_rel.GetLeftTableName(),
|
|
retrieved_rel.GetRightTableName(),
|
|
gdal.GRC_MANY_TO_MANY,
|
|
)
|
|
relationship.SetLeftTableFields(retrieved_rel.GetLeftTableFields())
|
|
relationship.SetRightTableFields(retrieved_rel.GetRightTableFields())
|
|
relationship.SetMappingTableName(retrieved_rel.GetMappingTableName())
|
|
relationship.SetLeftMappingTableFields(retrieved_rel.GetLeftMappingTableFields())
|
|
relationship.SetRightMappingTableFields(retrieved_rel.GetRightMappingTableFields())
|
|
|
|
assert not ds.UpdateRelationship(clone_relationship(relationship))
|
|
|
|
retrieved_rel = ds.GetRelationship("origin_table_dest_table_media")
|
|
retrieved_rel.SetRelatedTableType("nope")
|
|
# relationship will be validated before updates
|
|
assert not ds.UpdateRelationship(clone_relationship(retrieved_rel))
|
|
|
|
# change related table type
|
|
retrieved_rel = ds.GetRelationship("origin_table_dest_table_media")
|
|
retrieved_rel.SetRelatedTableType("attributes")
|
|
assert ds.UpdateRelationship(clone_relationship(retrieved_rel))
|
|
|
|
ds = None
|
|
assert validate(filename), "validation failed"
|
|
ds = gdal.OpenEx(filename, gdal.OF_VECTOR | gdal.OF_UPDATE)
|
|
|
|
assert set(ds.GetRelationshipNames()) == {
|
|
"origin_table_dest_table_attributes",
|
|
"origin_table3_dest_table3_features",
|
|
}
|
|
|
|
retrieved_rel = ds.GetRelationship("origin_table_dest_table_attributes")
|
|
assert retrieved_rel.GetCardinality() == gdal.GRC_MANY_TO_MANY
|
|
assert retrieved_rel.GetType() == gdal.GRT_ASSOCIATION
|
|
assert retrieved_rel.GetLeftTableName() == "origin_table"
|
|
assert retrieved_rel.GetRightTableName() == "dest_table"
|
|
assert retrieved_rel.GetLeftTableFields() == ["o_pkey"]
|
|
assert retrieved_rel.GetRightTableFields() == ["dest_pkey"]
|
|
assert retrieved_rel.GetRelatedTableType() == "attributes"
|
|
|
|
# change base table field
|
|
retrieved_rel.SetLeftTableFields(["o_pkey2"])
|
|
assert ds.UpdateRelationship(clone_relationship(retrieved_rel))
|
|
|
|
assert set(ds.GetRelationshipNames()) == {
|
|
"origin_table_dest_table_attributes",
|
|
"origin_table3_dest_table3_features",
|
|
}
|
|
|
|
retrieved_rel = ds.GetRelationship("origin_table_dest_table_attributes")
|
|
assert retrieved_rel.GetCardinality() == gdal.GRC_MANY_TO_MANY
|
|
assert retrieved_rel.GetType() == gdal.GRT_ASSOCIATION
|
|
assert retrieved_rel.GetLeftTableName() == "origin_table"
|
|
assert retrieved_rel.GetRightTableName() == "dest_table"
|
|
assert retrieved_rel.GetLeftTableFields() == ["o_pkey2"]
|
|
assert retrieved_rel.GetRightTableFields() == ["dest_pkey"]
|
|
assert retrieved_rel.GetRelatedTableType() == "attributes"
|
|
|
|
retrieved_rel.SetRightTableFields(["dest_pkey2"])
|
|
assert ds.UpdateRelationship(clone_relationship(retrieved_rel))
|
|
|
|
assert set(ds.GetRelationshipNames()) == {
|
|
"origin_table_dest_table_attributes",
|
|
"origin_table3_dest_table3_features",
|
|
}
|
|
|
|
retrieved_rel = ds.GetRelationship("origin_table_dest_table_attributes")
|
|
assert retrieved_rel.GetCardinality() == gdal.GRC_MANY_TO_MANY
|
|
assert retrieved_rel.GetType() == gdal.GRT_ASSOCIATION
|
|
assert retrieved_rel.GetLeftTableName() == "origin_table"
|
|
assert retrieved_rel.GetRightTableName() == "dest_table"
|
|
assert retrieved_rel.GetLeftTableFields() == ["o_pkey2"]
|
|
assert retrieved_rel.GetRightTableFields() == ["dest_pkey2"]
|
|
assert retrieved_rel.GetRelatedTableType() == "attributes"
|
|
|
|
# try updating to field which doesn't exist
|
|
retrieved_rel.SetRightTableFields(["dest_pkey2xxx"])
|
|
assert not ds.UpdateRelationship(clone_relationship(retrieved_rel))
|
|
|
|
# delete all relationships
|
|
|
|
assert ds.DeleteRelationship("origin_table_dest_table_attributes")
|
|
|
|
# validate that extensions table was correctly updated
|
|
assert (
|
|
get_query_row_count(
|
|
"SELECT * FROM gpkg_extensions WHERE table_name = 'origin_table_dest_table' AND extension_name = 'gpkg_related_tables'"
|
|
)
|
|
== 0
|
|
)
|
|
# validate gpkgext_relations has been updated correctly
|
|
assert (
|
|
get_query_row_count(
|
|
"SELECT * FROM gpkgext_relations WHERE base_table_name = 'origin_table' AND "
|
|
"base_primary_column = 'o_pkey2' AND "
|
|
"related_table_name = 'dest_table' AND "
|
|
"related_primary_column = 'dest_pkey2' AND "
|
|
"relation_name = 'attributes' AND "
|
|
"mapping_table_name = 'origin_table_dest_table'"
|
|
)
|
|
== 0
|
|
)
|
|
# validate that mapping table was deleted
|
|
assert (
|
|
get_query_row_count(
|
|
"SELECT 1 FROM sqlite_master WHERE name = 'origin_table_dest_table' AND type in ('table', 'view')"
|
|
)
|
|
== 0
|
|
)
|
|
|
|
assert set(ds.GetRelationshipNames()) == {"origin_table3_dest_table3_features"}
|
|
# should still be two extension rows for gpkg_related_tables: one for the remaining relationship, one for the extension itself
|
|
assert (
|
|
get_query_row_count(
|
|
"SELECT * FROM gpkg_extensions WHERE extension_name = 'gpkg_related_tables'"
|
|
)
|
|
== 2
|
|
)
|
|
|
|
assert ds.DeleteRelationship("origin_table3_dest_table3_features")
|
|
|
|
# should be no remaining gpkg_related_tables extension records
|
|
assert (
|
|
get_query_row_count(
|
|
"SELECT * FROM gpkg_extensions WHERE extension_name = 'gpkg_related_tables'"
|
|
)
|
|
== 0
|
|
)
|
|
# validate gpkgext_relations has been updated correctly
|
|
assert get_query_row_count("SELECT * FROM gpkgext_relations") == 0
|
|
|
|
ds = None
|
|
assert validate(filename), "validation failed"
|
|
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test creating relationships with complex names
|
|
|
|
|
|
def test_ogr_gpkg_add_relationship_complex_names():
|
|
|
|
filename = "/vsimem/test_ogr_gpkg_relation_create_complex.gpkg"
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(filename)
|
|
|
|
def get_query_row_count(query):
|
|
sql_lyr = ds.ExecuteSQL(query)
|
|
res = sql_lyr.GetFeatureCount()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
return res
|
|
|
|
relationship = gdal.Relationship(
|
|
"my_relationship", "Origin' [tàble!", "dést ]table$", gdal.GRC_MANY_TO_MANY
|
|
)
|
|
relationship.SetLeftTableFields(["o pkéy"])
|
|
relationship.SetRightTableFields(["Dest pkéy"])
|
|
relationship.SetRelatedTableType("media")
|
|
|
|
ds = gdal.OpenEx(filename, gdal.OF_VECTOR | gdal.OF_UPDATE)
|
|
|
|
lyr = ds.CreateLayer("Origin' [tàble!", geom_type=ogr.wkbNone)
|
|
fld_defn = ogr.FieldDefn("o pkéy", ogr.OFTInteger)
|
|
assert lyr.CreateField(fld_defn) == ogr.OGRERR_NONE
|
|
ds.ExecuteSQL(
|
|
'CREATE UNIQUE INDEX origin_table_o_pkey_idx ON "Origin\' [tàble!"("o pkéy")'
|
|
)
|
|
|
|
lyr = ds.CreateLayer("dést ]table$", geom_type=ogr.wkbNone)
|
|
fld_defn = ogr.FieldDefn("Dest pkéy", ogr.OFTInteger)
|
|
assert lyr.CreateField(fld_defn) == ogr.OGRERR_NONE
|
|
ds.ExecuteSQL(
|
|
'CREATE UNIQUE INDEX dest_table_dest_pkey_idx ON "dést ]table$"("Dest pkéy")'
|
|
)
|
|
|
|
relationship.SetLeftTableFields(["o pkéy"])
|
|
relationship.SetRightTableFields(["Dest pkéy"])
|
|
|
|
assert ds.AddRelationship(relationship)
|
|
|
|
assert set(ds.GetRelationshipNames()) == {"Origin' [tàble!_dést ]table$_media"}
|
|
retrieved_rel = ds.GetRelationship("Origin' [tàble!_dést ]table$_media")
|
|
assert retrieved_rel.GetCardinality() == gdal.GRC_MANY_TO_MANY
|
|
assert retrieved_rel.GetType() == gdal.GRT_ASSOCIATION
|
|
assert retrieved_rel.GetLeftTableName() == "Origin' [tàble!"
|
|
assert retrieved_rel.GetRightTableName() == "dést ]table$"
|
|
assert retrieved_rel.GetLeftTableFields() == ["o pkéy"]
|
|
assert retrieved_rel.GetRightTableFields() == ["Dest pkéy"]
|
|
assert retrieved_rel.GetRelatedTableType() == "media"
|
|
assert retrieved_rel.GetMappingTableName() == "Origin' [tàble!_dést ]table$"
|
|
assert retrieved_rel.GetLeftMappingTableFields() == ["base_id"]
|
|
assert retrieved_rel.GetRightMappingTableFields() == ["related_id"]
|
|
|
|
ds = None
|
|
assert validate(filename), "validation failed"
|
|
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test AlterGeomFieldDefn()
|
|
|
|
|
|
def test_ogr_gpkg_alter_geom_field_defn():
|
|
|
|
filename = "/vsimem/test_ogr_gpkg_alter_geom_field_defn.gpkg"
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(filename)
|
|
srs_4326 = osr.SpatialReference()
|
|
srs_4326.ImportFromEPSG(4326)
|
|
lyr = ds.CreateLayer("test", geom_type=ogr.wkbPoint, srs=srs_4326)
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("POINT (1 2)"))
|
|
lyr.CreateFeature(f)
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
lyr.CreateFeature(f)
|
|
ds = None
|
|
|
|
# Test renaming column (only supported for SQLite >= 3.26)
|
|
if get_sqlite_version() >= (3, 26, 0):
|
|
ds = ogr.Open(filename, update=1)
|
|
lyr = ds.GetLayer(0)
|
|
assert lyr.TestCapability(ogr.OLCAlterGeomFieldDefn)
|
|
|
|
new_geom_field_defn = ogr.GeomFieldDefn("new_geom_name", ogr.wkbNone)
|
|
assert (
|
|
lyr.AlterGeomFieldDefn(
|
|
0, new_geom_field_defn, ogr.ALTER_GEOM_FIELD_DEFN_NAME_FLAG
|
|
)
|
|
== ogr.OGRERR_NONE
|
|
)
|
|
assert lyr.GetGeometryColumn() == "new_geom_name"
|
|
|
|
ds = None
|
|
|
|
assert validate(filename), "validation failed"
|
|
|
|
ds = ogr.Open(filename)
|
|
lyr = ds.GetLayer(0)
|
|
assert lyr.GetGeometryColumn() == "new_geom_name"
|
|
srs = lyr.GetSpatialRef()
|
|
assert srs is not None
|
|
assert srs.GetAuthorityCode(None) == "4326"
|
|
ds = None
|
|
|
|
ds = ogr.Open(filename, update=1)
|
|
lyr = ds.GetLayer(0)
|
|
new_geom_field_defn = ogr.GeomFieldDefn("", ogr.wkbNone)
|
|
assert (
|
|
lyr.AlterGeomFieldDefn(
|
|
0, new_geom_field_defn, ogr.ALTER_GEOM_FIELD_DEFN_SRS_FLAG
|
|
)
|
|
== ogr.OGRERR_NONE
|
|
)
|
|
ds = None
|
|
|
|
assert validate(filename), "validation failed"
|
|
|
|
ds = ogr.Open(filename, update=1)
|
|
lyr = ds.GetLayer(0)
|
|
srs = lyr.GetSpatialRef()
|
|
assert srs is not None
|
|
assert srs.GetName() == "Undefined geographic SRS"
|
|
|
|
new_geom_field_defn = ogr.GeomFieldDefn("", ogr.wkbNone)
|
|
new_geom_field_defn.SetSpatialRef(srs_4326)
|
|
assert (
|
|
lyr.AlterGeomFieldDefn(
|
|
0, new_geom_field_defn, ogr.ALTER_GEOM_FIELD_DEFN_SRS_FLAG
|
|
)
|
|
== ogr.OGRERR_NONE
|
|
)
|
|
ds = None
|
|
|
|
assert validate(filename), "validation failed"
|
|
|
|
ds = ogr.Open(filename, update=1)
|
|
lyr = ds.GetLayer(0)
|
|
srs = lyr.GetSpatialRef()
|
|
assert srs is not None
|
|
assert srs.GetAuthorityCode(None) == "4326"
|
|
|
|
new_geom_field_defn = ogr.GeomFieldDefn("", ogr.wkbNone)
|
|
other_srs = osr.SpatialReference()
|
|
other_srs.ImportFromEPSG(4269)
|
|
new_geom_field_defn.SetSpatialRef(other_srs)
|
|
assert (
|
|
lyr.AlterGeomFieldDefn(
|
|
0, new_geom_field_defn, ogr.ALTER_GEOM_FIELD_DEFN_SRS_FLAG
|
|
)
|
|
== ogr.OGRERR_NONE
|
|
)
|
|
ds = None
|
|
|
|
ds = ogr.Open(filename, update=1)
|
|
lyr = ds.GetLayer(0)
|
|
srs = lyr.GetSpatialRef()
|
|
assert srs is not None
|
|
assert srs.GetAuthorityCode(None) == "4269"
|
|
|
|
new_geom_field_defn = ogr.GeomFieldDefn("", ogr.wkbNone)
|
|
srs = osr.SpatialReference()
|
|
srs.ImportFromEPSG(4269)
|
|
srs.SetCoordinateEpoch(2022)
|
|
new_geom_field_defn.SetSpatialRef(srs)
|
|
assert (
|
|
lyr.AlterGeomFieldDefn(
|
|
0, new_geom_field_defn, ogr.ALTER_GEOM_FIELD_DEFN_SRS_COORD_EPOCH_FLAG
|
|
)
|
|
== ogr.OGRERR_NONE
|
|
)
|
|
ds = None
|
|
|
|
ds = ogr.Open(filename, update=1)
|
|
lyr = ds.GetLayer(0)
|
|
srs = lyr.GetSpatialRef()
|
|
assert srs is not None
|
|
assert srs.GetAuthorityCode(None) == "4269"
|
|
assert srs.GetCoordinateEpoch() == 2022
|
|
ds = None
|
|
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test GetArrowStreamAsNumPy()
|
|
|
|
|
|
def test_ogr_gpkg_arrow_stream_numpy():
|
|
pytest.importorskip("osgeo.gdal_array")
|
|
numpy = pytest.importorskip("numpy")
|
|
|
|
ds = gdal.GetDriverByName("GPKG").Create(
|
|
"/vsimem/test.gpkg", 0, 0, 0, gdal.GDT_Unknown
|
|
)
|
|
lyr = ds.CreateLayer("test", geom_type=ogr.wkbPoint)
|
|
assert lyr.TestCapability(ogr.OLCFastGetArrowStream) == 1
|
|
|
|
field = ogr.FieldDefn("str", ogr.OFTString)
|
|
lyr.CreateField(field)
|
|
|
|
field = ogr.FieldDefn("bool", ogr.OFTInteger)
|
|
field.SetSubType(ogr.OFSTBoolean)
|
|
lyr.CreateField(field)
|
|
|
|
field = ogr.FieldDefn("int16", ogr.OFTInteger)
|
|
field.SetSubType(ogr.OFSTInt16)
|
|
lyr.CreateField(field)
|
|
|
|
assert ds.AddFieldDomain(
|
|
ogr.CreateCodedFieldDomain(
|
|
"enum_domain", "", ogr.OFTInteger, ogr.OFSTNone, {1: "one", "2": None}
|
|
)
|
|
)
|
|
field = ogr.FieldDefn("int32", ogr.OFTInteger)
|
|
field.SetDomainName("enum_domain")
|
|
lyr.CreateField(field)
|
|
|
|
field = ogr.FieldDefn("int64", ogr.OFTInteger64)
|
|
lyr.CreateField(field)
|
|
|
|
field = ogr.FieldDefn("float32", ogr.OFTReal)
|
|
field.SetSubType(ogr.OFSTFloat32)
|
|
lyr.CreateField(field)
|
|
|
|
field = ogr.FieldDefn("float64", ogr.OFTReal)
|
|
lyr.CreateField(field)
|
|
|
|
field = ogr.FieldDefn("date", ogr.OFTDate)
|
|
lyr.CreateField(field)
|
|
|
|
field = ogr.FieldDefn("datetime", ogr.OFTDateTime)
|
|
lyr.CreateField(field)
|
|
|
|
field = ogr.FieldDefn("binary", ogr.OFTBinary)
|
|
lyr.CreateField(field)
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetField("bool", 1)
|
|
f.SetField("int16", -12345)
|
|
f.SetField("int32", 12345678)
|
|
f.SetField("int64", 12345678901234)
|
|
f.SetField("float32", 1.25)
|
|
f.SetField("float64", 1.250123)
|
|
f.SetField("str", "abc")
|
|
f.SetField("date", "2022-05-31")
|
|
f.SetField("datetime", "2022-05-31T12:34:56.789Z")
|
|
f.SetFieldBinaryFromHexString("binary", "DEAD")
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("POINT(1 2)"))
|
|
lyr.CreateFeature(f)
|
|
|
|
f2 = ogr.Feature(lyr.GetLayerDefn())
|
|
f2.SetField("bool", 0)
|
|
lyr.CreateFeature(f2)
|
|
|
|
f3 = ogr.Feature(lyr.GetLayerDefn())
|
|
f3.SetField("int16", 123)
|
|
f3.SetGeometryDirectly(ogr.CreateGeometryFromWkt("POINT(-1 2)"))
|
|
lyr.CreateFeature(f3)
|
|
|
|
ds = None
|
|
|
|
ds = ogr.Open("/vsimem/test.gpkg")
|
|
lyr = ds.GetLayer(0)
|
|
stream = lyr.GetArrowStream()
|
|
array = stream.GetNextRecordBatch()
|
|
assert array.GetChildrenCount() == 12
|
|
del array
|
|
del stream
|
|
ds = None
|
|
|
|
ds = ogr.Open("/vsimem/test.gpkg")
|
|
lyr = ds.GetLayer(0)
|
|
|
|
try:
|
|
import pyarrow
|
|
|
|
pyarrow.__version__
|
|
has_pyarrow = True
|
|
except ImportError:
|
|
has_pyarrow = False
|
|
if has_pyarrow:
|
|
stream = lyr.GetArrowStreamAsPyArrow()
|
|
batches = [batch for batch in stream]
|
|
# print(batches)
|
|
|
|
for i in range(2):
|
|
with gdaltest.config_options(
|
|
{"OGR_GPKG_STREAM_BASE_IMPL": "YES"} if i == 1 else {}
|
|
):
|
|
stream = lyr.GetArrowStreamAsNumPy(
|
|
options=["USE_MASKED_ARRAYS=NO", "MAX_FEATURES_IN_BATCH=2"]
|
|
)
|
|
batches = [batch for batch in stream]
|
|
assert len(batches) == 2
|
|
batch = batches[0]
|
|
|
|
assert batch.keys() == {
|
|
"fid",
|
|
"str",
|
|
"bool",
|
|
"int16",
|
|
"int32",
|
|
"int64",
|
|
"float32",
|
|
"float64",
|
|
"date",
|
|
"datetime",
|
|
"binary",
|
|
"geom",
|
|
}
|
|
|
|
assert batch["fid"][0] == 1
|
|
assert len(batch["fid"]) == 2
|
|
for fieldname in ("bool", "int16", "int32", "int64", "float32", "float64"):
|
|
assert batch[fieldname][0] == f.GetField(fieldname)
|
|
assert batch["str"][0] == f.GetField("str").encode("utf-8")
|
|
assert batch["date"][0] == numpy.datetime64("2022-05-31")
|
|
assert batch["datetime"][0] == numpy.datetime64("2022-05-31T12:34:56.789")
|
|
assert bytes(batch["binary"][0]) == b"\xDE\xAD"
|
|
assert len(bytes(batch["geom"][0])) == 21
|
|
|
|
assert batch["fid"][1] == 2
|
|
assert batch["bool"][1] == False
|
|
assert batch["geom"][1] is None
|
|
|
|
batch = batches[1]
|
|
assert batch.keys() == {
|
|
"fid",
|
|
"str",
|
|
"bool",
|
|
"int16",
|
|
"int32",
|
|
"int64",
|
|
"float32",
|
|
"float64",
|
|
"date",
|
|
"datetime",
|
|
"binary",
|
|
"geom",
|
|
}
|
|
|
|
assert batch["fid"][0] == 3
|
|
assert batch["int16"][0] == 123
|
|
assert len(batch["fid"]) == 1
|
|
|
|
with lyr.GetArrowStreamAsNumPy(options=["MAX_FEATURES_IN_BATCH=1"]) as stream:
|
|
batches = [batch for batch in stream]
|
|
assert len(batches) == 3
|
|
assert len(batches[0]["fid"]) == 1
|
|
assert batches[0]["fid"][0] == 1
|
|
assert len(batches[1]["fid"]) == 1
|
|
assert batches[1]["fid"][0] == 2
|
|
assert len(batches[2]["fid"]) == 1
|
|
assert batches[2]["fid"][0] == 3
|
|
|
|
for i in range(2):
|
|
with lyr.GetArrowStreamAsNumPy(options=["MAX_FEATURES_IN_BATCH=1"]) as stream:
|
|
batch = stream.GetNextRecordBatch()
|
|
assert len(batch["fid"]) == 1, i
|
|
assert batch["fid"][0] == 1, i
|
|
|
|
lyr.SetIgnoredFields(
|
|
[
|
|
lyr.GetLayerDefn().GetFieldDefn(i).GetNameRef()
|
|
for i in range(lyr.GetLayerDefn().GetFieldCount())
|
|
]
|
|
)
|
|
with lyr.GetArrowStreamAsNumPy(options=["INCLUDE_FID=NO"]) as stream:
|
|
batch = stream.GetNextRecordBatch()
|
|
assert len(batch["geom"]) == 3, i
|
|
assert len(batch["geom"][0]) > 0, i
|
|
lyr.SetIgnoredFields([])
|
|
|
|
# Test attribute filter
|
|
lyr.SetAttributeFilter("int16 = 123")
|
|
stream = lyr.GetArrowStreamAsNumPy()
|
|
batches = [batch for batch in stream]
|
|
lyr.SetAttributeFilter(None)
|
|
assert len(batches) == 1
|
|
assert len(batches[0]["fid"]) == 1
|
|
assert batches[0]["fid"][0] == 3
|
|
|
|
for i in range(2):
|
|
lyr.SetAttributeFilter("1 = 1")
|
|
with lyr.GetArrowStreamAsNumPy(options=["MAX_FEATURES_IN_BATCH=1"]) as stream:
|
|
batch = stream.GetNextRecordBatch()
|
|
assert len(batch["fid"]) == 1, i
|
|
assert batch["fid"][0] == 1, i
|
|
batch = stream.GetNextRecordBatch()
|
|
assert len(batch["fid"]) == 1, i
|
|
assert batch["fid"][0] == 2, i
|
|
lyr.SetAttributeFilter(None)
|
|
|
|
# Test spatial filter
|
|
lyr.SetSpatialFilterRect(0, 0, 10, 10)
|
|
stream = lyr.GetArrowStreamAsNumPy()
|
|
batches = [batch for batch in stream]
|
|
lyr.SetSpatialFilter(None)
|
|
assert len(batches) == 1
|
|
assert len(batches[0]["fid"]) == 1
|
|
assert batches[0]["fid"][0] == 1
|
|
|
|
# Test ignored fields
|
|
assert lyr.SetIgnoredFields(["geom", "int16"]) == ogr.OGRERR_NONE
|
|
stream = lyr.GetArrowStreamAsNumPy(options=["INCLUDE_FID=NO"])
|
|
batches = [batch for batch in stream]
|
|
lyr.SetIgnoredFields([])
|
|
batch = batches[0]
|
|
assert batch.keys() == {
|
|
"str",
|
|
"bool",
|
|
"int32",
|
|
"int64",
|
|
"float32",
|
|
"float64",
|
|
"date",
|
|
"datetime",
|
|
"binary",
|
|
}
|
|
|
|
# Check that OGR_GPKG_FillArrowArray_INTERNAL() function is no longer
|
|
# registered
|
|
with gdaltest.error_handler():
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT 1 FROM pragma_function_list WHERE name=lower('OGR_GPKG_FillArrowArray_INTERNAL')"
|
|
)
|
|
if sql_lyr:
|
|
fc = sql_lyr.GetFeatureCount()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
assert fc == 0
|
|
|
|
ds = None
|
|
|
|
ogr.GetDriverByName("GPKG").DeleteDataSource("/vsimem/test.gpkg")
|
|
|
|
|
|
###############################################################################
|
|
# Test opening a file in WAL mode on a read-only storage
|
|
|
|
|
|
@pytest.mark.skipif(sys.platform != "linux", reason="Incorrect platform")
|
|
def test_ogr_gpkg_immutable():
|
|
|
|
if os.getuid() == 0:
|
|
pytest.skip("running as root... skipping")
|
|
|
|
try:
|
|
os.mkdir("tmp/read_only_test_ogr_gpkg_immutable", 0o755)
|
|
|
|
ds = ogr.GetDriverByName("GPKG").CreateDataSource(
|
|
"tmp/read_only_test_ogr_gpkg_immutable/test.gpkg"
|
|
)
|
|
ds.CreateLayer("foo")
|
|
ds.ExecuteSQL("PRAGMA journal_mode = WAL")
|
|
ds = None
|
|
|
|
# Turn directory in read-only mode
|
|
os.chmod("tmp/read_only_test_ogr_gpkg_immutable", 0o555)
|
|
|
|
with gdaltest.error_handler():
|
|
assert (
|
|
gdal.OpenEx(
|
|
"tmp/read_only_test_ogr_gpkg_immutable/test.gpkg",
|
|
gdal.OF_VECTOR | gdal.OF_UPDATE,
|
|
)
|
|
is None
|
|
)
|
|
assert (
|
|
gdal.OpenEx(
|
|
"tmp/read_only_test_ogr_gpkg_immutable/test.gpkg",
|
|
gdal.OF_VECTOR,
|
|
open_options=["IMMUTABLE=NO"],
|
|
)
|
|
is None
|
|
)
|
|
|
|
gdal.ErrorReset()
|
|
assert (
|
|
gdal.OpenEx(
|
|
"tmp/read_only_test_ogr_gpkg_immutable/test.gpkg",
|
|
gdal.OF_VECTOR,
|
|
open_options=["IMMUTABLE=YES"],
|
|
)
|
|
is not None
|
|
)
|
|
assert gdal.GetLastErrorMsg() == ""
|
|
|
|
gdal.ErrorReset()
|
|
with gdaltest.error_handler():
|
|
assert (
|
|
ogr.Open("tmp/read_only_test_ogr_gpkg_immutable/test.gpkg") is not None
|
|
)
|
|
assert gdal.GetLastErrorMsg() != ""
|
|
|
|
finally:
|
|
os.chmod("tmp/read_only_test_ogr_gpkg_immutable", 0o755)
|
|
os.unlink("tmp/read_only_test_ogr_gpkg_immutable/test.gpkg")
|
|
os.rmdir("tmp/read_only_test_ogr_gpkg_immutable")
|
|
|
|
|
|
###############################################################################
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
get_sqlite_version() < (3, 24, 0),
|
|
reason="sqlite >= 3.24 needed",
|
|
)
|
|
@pytest.mark.parametrize("with_geom", [True, False])
|
|
@pytest.mark.parametrize("gpkg_version", ["1.2", "1.4"])
|
|
def test_ogr_gpkg_upsert_without_fid(with_geom, gpkg_version):
|
|
|
|
filename = "/vsimem/test_ogr_gpkg_upsert_without_fid.gpkg"
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(
|
|
filename, options=["VERSION=" + gpkg_version]
|
|
)
|
|
lyr = ds.CreateLayer(
|
|
"foo", geom_type=(ogr.wkbUnknown if with_geom else ogr.wkbNone)
|
|
)
|
|
assert lyr.CreateField(ogr.FieldDefn("other", ogr.OFTString)) == ogr.OGRERR_NONE
|
|
unique_field = ogr.FieldDefn("unique_field", ogr.OFTString)
|
|
unique_field.SetUnique(True)
|
|
assert lyr.CreateField(unique_field) == ogr.OGRERR_NONE
|
|
for i in range(5):
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetField("unique_field", i + 1)
|
|
if i < 4 and with_geom:
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("POINT (%d %d)" % (i, i)))
|
|
assert lyr.CreateFeature(f) == ogr.OGRERR_NONE
|
|
ds = None
|
|
|
|
ds = ogr.Open(filename, update=1)
|
|
lyr = ds.GetLayer(0)
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetField("unique_field", "2")
|
|
f.SetField("other", "foo")
|
|
if with_geom:
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("POINT (10 10)"))
|
|
assert lyr.UpsertFeature(f) == ogr.OGRERR_NONE
|
|
|
|
if get_sqlite_version() >= (3, 35, 0):
|
|
assert f.GetFID() == 2
|
|
|
|
if with_geom and gpkg_version == "1.2":
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT 1 FROM sqlite_master WHERE name = 'rtree_foo_geom_update1'",
|
|
dialect="DEBUG",
|
|
)
|
|
assert sql_lyr.GetFeatureCount() == 0
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT 1 FROM sqlite_master WHERE name = 'rtree_foo_geom_update6'",
|
|
dialect="DEBUG",
|
|
)
|
|
assert sql_lyr.GetFeatureCount() == 1
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT 1 FROM sqlite_master WHERE name = 'rtree_foo_geom_update7'",
|
|
dialect="DEBUG",
|
|
)
|
|
assert sql_lyr.GetFeatureCount() == 1
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetField("unique_field", "3")
|
|
assert lyr.UpsertFeature(f) == ogr.OGRERR_NONE
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetField("unique_field", "4")
|
|
if with_geom:
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("POINT (20 20)"))
|
|
assert lyr.UpsertFeature(f) == ogr.OGRERR_NONE
|
|
|
|
ds = None
|
|
|
|
assert validate(filename)
|
|
|
|
ds = ogr.Open(filename)
|
|
|
|
if with_geom and gpkg_version == "1.2":
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT 1 FROM sqlite_master WHERE name = 'rtree_foo_geom_update1'",
|
|
dialect="DEBUG",
|
|
)
|
|
assert sql_lyr.GetFeatureCount() == 1
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT 1 FROM sqlite_master WHERE name = 'rtree_foo_geom_update6'",
|
|
dialect="DEBUG",
|
|
)
|
|
assert sql_lyr.GetFeatureCount() == 0
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT 1 FROM sqlite_master WHERE name = 'rtree_foo_geom_update7'",
|
|
dialect="DEBUG",
|
|
)
|
|
assert sql_lyr.GetFeatureCount() == 0
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
|
|
lyr = ds.GetLayer(0)
|
|
|
|
f = lyr.GetFeature(2)
|
|
assert f["unique_field"] == "2"
|
|
assert f["other"] == "foo"
|
|
if with_geom:
|
|
assert f.GetGeometryRef().ExportToWkt() == "POINT (10 10)"
|
|
|
|
f = lyr.GetFeature(3)
|
|
assert f.GetGeometryRef() is None
|
|
|
|
f = lyr.GetFeature(4)
|
|
if with_geom:
|
|
assert f.GetGeometryRef().ExportToWkt() == "POINT (20 20)"
|
|
|
|
ds = None
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
|
|
|
|
def test_ogr_gpkg_get_geometry_types():
|
|
"""Test Layer.GetGeometryTypes()"""
|
|
|
|
filename = "/vsimem/test_ogr_gpkg_get_geometry_types.gpkg"
|
|
ds = ogr.GetDriverByName("GPKG").CreateDataSource(filename)
|
|
lyr = ds.CreateLayer("layer")
|
|
|
|
assert lyr.GetGeometryTypes() == {}
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
lyr.CreateFeature(f)
|
|
assert lyr.GetGeometryTypes() == {ogr.wkbNone: 1}
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
lyr.CreateFeature(f)
|
|
assert lyr.GetGeometryTypes(callback=lambda x, y, z: 1) == {ogr.wkbNone: 2}
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT EMPTY"))
|
|
lyr.CreateFeature(f)
|
|
assert lyr.GetGeometryTypes() == {ogr.wkbNone: 2, ogr.wkbPoint: 1}
|
|
lyr.SetAttributeFilter("1")
|
|
assert lyr.GetGeometryTypes() == {ogr.wkbNone: 2, ogr.wkbPoint: 1}
|
|
lyr.SetAttributeFilter("0")
|
|
assert lyr.GetGeometryTypes() == {}
|
|
lyr.SetAttributeFilter(None)
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometry(ogr.CreateGeometryFromWkt("POLYGON EMPTY"))
|
|
lyr.CreateFeature(f)
|
|
assert lyr.GetGeometryTypes() == {
|
|
ogr.wkbNone: 2,
|
|
ogr.wkbPoint: 1,
|
|
ogr.wkbPolygon: 1,
|
|
}
|
|
assert lyr.GetGeometryTypes(flags=ogr.GGT_STOP_IF_MIXED) == {
|
|
ogr.wkbNone: 2,
|
|
ogr.wkbPoint: 1,
|
|
ogr.wkbPolygon: 1,
|
|
}
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometry(ogr.CreateGeometryFromWkt("LINESTRING (0 0,1 1)"))
|
|
lyr.CreateFeature(f)
|
|
assert lyr.GetGeometryTypes() == {
|
|
ogr.wkbNone: 2,
|
|
ogr.wkbPoint: 1,
|
|
ogr.wkbPolygon: 1,
|
|
ogr.wkbLineString: 1,
|
|
}
|
|
assert lyr.GetGeometryTypes(geom_field=0, flags=ogr.GGT_STOP_IF_MIXED) == {
|
|
ogr.wkbNone: 2,
|
|
ogr.wkbPoint: 1,
|
|
ogr.wkbPolygon: 1,
|
|
}
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometry(
|
|
ogr.CreateGeometryFromWkt(
|
|
"GEOMETRYCOLLECTION Z(TIN Z(((0 0 0,0 1 0,1 1 0,0 0 0))))"
|
|
)
|
|
)
|
|
lyr.CreateFeature(f)
|
|
assert lyr.GetGeometryTypes() == {
|
|
ogr.wkbNone: 2,
|
|
ogr.wkbPoint: 1,
|
|
ogr.wkbPolygon: 1,
|
|
ogr.wkbLineString: 1,
|
|
ogr.wkbGeometryCollection25D: 1,
|
|
}
|
|
assert lyr.GetGeometryTypes(flags=ogr.GGT_GEOMCOLLECTIONZ_TINZ) == {
|
|
ogr.wkbNone: 2,
|
|
ogr.wkbPoint: 1,
|
|
ogr.wkbPolygon: 1,
|
|
ogr.wkbLineString: 1,
|
|
ogr.wkbTINZ: 1,
|
|
}
|
|
|
|
with gdaltest.error_handler():
|
|
with pytest.raises(Exception):
|
|
lyr.GetGeometryTypes(geom_field=1)
|
|
|
|
lyr.StartTransaction()
|
|
for _ in range(
|
|
1000
|
|
): # 1000 because COUNT_VM_INSTRUCTIONS = 1000 in OGRGeoPackageTableLayer::GetGeometryTypes()
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT EMPTY"))
|
|
lyr.CreateFeature(f)
|
|
lyr.CommitTransaction()
|
|
with gdaltest.error_handler():
|
|
with pytest.raises(Exception):
|
|
lyr.GetGeometryTypes(callback=lambda x, y, z: 0)
|
|
|
|
assert lyr.GetGeometryTypes() == {
|
|
ogr.wkbNone: 2,
|
|
ogr.wkbPoint: 1001,
|
|
ogr.wkbPolygon: 1,
|
|
ogr.wkbLineString: 1,
|
|
ogr.wkbGeometryCollection25D: 1,
|
|
}
|
|
|
|
ds = None
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"filename",
|
|
[
|
|
"/vsimem/test_ogr_gpkg_background_rtree_build.gpkg",
|
|
"tmp/test_ogr_gpkg_background_rtree_build.gpkg",
|
|
],
|
|
)
|
|
def test_ogr_gpkg_background_rtree_build(filename):
|
|
|
|
# Batch insertion only
|
|
|
|
gdal.ErrorReset()
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(filename)
|
|
with gdaltest.config_option("OGR_GPKG_THREADED_RTREE_AT_FIRST_FEATURE", "YES"):
|
|
lyr = ds.CreateLayer("foo")
|
|
assert lyr.StartTransaction() == ogr.OGRERR_NONE
|
|
for i in range(1000):
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("POINT(%d %d)" % (i, i)))
|
|
assert lyr.CreateFeature(f) == ogr.OGRERR_NONE
|
|
if i == 500:
|
|
assert lyr.CommitTransaction() == ogr.OGRERR_NONE
|
|
assert lyr.StartTransaction() == ogr.OGRERR_NONE
|
|
assert lyr.CommitTransaction() == ogr.OGRERR_NONE
|
|
assert gdal.GetLastErrorMsg() == ""
|
|
|
|
with gdaltest.config_option("OGR_GPKG_THREADED_RTREE_AT_FIRST_FEATURE", "YES"):
|
|
lyr = ds.CreateLayer("bar")
|
|
assert lyr.StartTransaction() == ogr.OGRERR_NONE
|
|
for i in range(900):
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("POINT(%d %d)" % (-i, -i)))
|
|
assert lyr.CreateFeature(f) == ogr.OGRERR_NONE
|
|
if i == 500:
|
|
assert lyr.CommitTransaction() == ogr.OGRERR_NONE
|
|
assert lyr.StartTransaction() == ogr.OGRERR_NONE
|
|
assert lyr.CommitTransaction() == ogr.OGRERR_NONE
|
|
assert gdal.GetLastErrorMsg() == ""
|
|
|
|
ds = None
|
|
assert gdal.VSIStatL(filename + ".tmp_rtree_foo.db") is None
|
|
assert gdal.VSIStatL(filename + ".tmp_rtree_bar.db") is None
|
|
|
|
ds = ogr.Open(filename)
|
|
sql_lyr = ds.ExecuteSQL("SELECT * FROM rtree_foo_geom")
|
|
assert sql_lyr.GetFeatureCount() == 1000
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
sql_lyr = ds.ExecuteSQL("SELECT * FROM rtree_bar_geom")
|
|
assert sql_lyr.GetFeatureCount() == 900
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
ds = None
|
|
|
|
gdal.Unlink(filename)
|
|
|
|
# Test SetFeature() after batch insertion
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(filename)
|
|
with gdaltest.config_option("OGR_GPKG_THREADED_RTREE_AT_FIRST_FEATURE", "YES"):
|
|
lyr = ds.CreateLayer("footoooooooooooooooooooooooooooooooooooooooooolong")
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("POINT(0 0)"))
|
|
assert lyr.CreateFeature(f) == ogr.OGRERR_NONE
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("POINT(1 1)"))
|
|
assert lyr.SetFeature(f) == ogr.OGRERR_NONE
|
|
ds = None
|
|
|
|
ds = ogr.Open(filename)
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"SELECT * FROM rtree_footoooooooooooooooooooooooooooooooooooooooooolong_geom"
|
|
)
|
|
assert sql_lyr.GetFeatureCount() == 1
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
lyr = ds.GetLayer(0)
|
|
lyr.SetSpatialFilterRect(0.5, 0.5, 1.5, 1.5)
|
|
assert lyr.GetFeatureCount() == 1
|
|
ds = None
|
|
|
|
gdal.Unlink(filename)
|
|
|
|
# Test DeleteFeature() after batch insertion
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(filename)
|
|
with gdaltest.config_option("OGR_GPKG_THREADED_RTREE_AT_FIRST_FEATURE", "YES"):
|
|
lyr = ds.CreateLayer("foo with space")
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("POINT(0 0)"))
|
|
assert lyr.CreateFeature(f) == ogr.OGRERR_NONE
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("POINT(1 1)"))
|
|
assert lyr.DeleteFeature(f.GetFID()) == ogr.OGRERR_NONE
|
|
ds = None
|
|
|
|
ds = ogr.Open(filename)
|
|
sql_lyr = ds.ExecuteSQL('SELECT * FROM "rtree_foo with space_geom"')
|
|
assert sql_lyr.GetFeatureCount() == 0
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
ds = None
|
|
|
|
# Test RollbackTransaction() after batch insertion
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(filename)
|
|
with gdaltest.config_option("OGR_GPKG_THREADED_RTREE_AT_FIRST_FEATURE", "YES"):
|
|
lyr = ds.CreateLayer("foo")
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("POINT(0 0)"))
|
|
assert lyr.CreateFeature(f) == ogr.OGRERR_NONE
|
|
lyr.StartTransaction()
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt("POINT(1 1)"))
|
|
assert lyr.CreateFeature(f) == ogr.OGRERR_NONE
|
|
lyr.RollbackTransaction()
|
|
ds = None
|
|
|
|
ds = ogr.Open(filename)
|
|
sql_lyr = ds.ExecuteSQL("SELECT * FROM rtree_foo_geom")
|
|
assert sql_lyr.GetFeatureCount() == 1
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
lyr = ds.GetLayer(0)
|
|
lyr.SetSpatialFilterRect(-0.5, -0.5, 0.5, 0.5)
|
|
assert lyr.GetFeatureCount() == 1
|
|
ds = None
|
|
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
|
|
|
|
def test_ogr_gpkg_detect_broken_rtree_gdal_3_6_0():
|
|
|
|
filename = "/vsimem/test_ogr_gpkg_detect_broken_rtree_gdal_3_6_0.gpkg"
|
|
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(filename)
|
|
lyr = ds.CreateLayer("foo")
|
|
for i in range(100):
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometryDirectly(
|
|
ogr.CreateGeometryFromWkt("POINT(%d %d)" % (i % 10, i // 10))
|
|
)
|
|
lyr.CreateFeature(f)
|
|
ds = None
|
|
|
|
# Voluntary corrupt the RTree by removing the entry for the last feature
|
|
ds = ogr.Open(filename, update=1)
|
|
sql_lyr = ds.ExecuteSQL("DELETE FROM rtree_foo_geom WHERE id = 100")
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
ds = None
|
|
|
|
with gdaltest.config_option("OGR_GPKG_THRESHOLD_DETECT_BROKEN_RTREE", "100"):
|
|
ds = ogr.Open(filename)
|
|
lyr = ds.GetLayer(0)
|
|
with gdaltest.error_handler():
|
|
gdal.ErrorReset()
|
|
lyr.SetSpatialFilterRect(8.5, 8.5, 9.5, 9.5)
|
|
assert (
|
|
"Spatial index (perhaps created with GDAL 3.6.0) of table foo is corrupted"
|
|
in gdal.GetLastErrorMsg()
|
|
)
|
|
assert lyr.GetFeatureCount() == 1
|
|
ds = None
|
|
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test ST_Area()
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"wkt_or_binary,area",
|
|
[
|
|
(None, None),
|
|
("X'0001'", None),
|
|
("POINT EMPTY", 0),
|
|
("LINESTRING(1 2,3 4)", 0),
|
|
("POLYGON EMPTY", 0),
|
|
("POLYGON ((0 0,0 1,1 1,0 0))", 0.5),
|
|
("POLYGON Z ((0 0 100,0 1 100,1 1 100,0 0 100))", 0.5),
|
|
("POLYGON M ((0 0 100,0 1 100,1 1 100,0 0 100))", 0.5),
|
|
("POLYGON ZM ((0 0 100 200,0 1 100 200,1 1 100 200,0 0 100 200))", 0.5),
|
|
(
|
|
"POLYGON ((0 0,0 1,1 1,1 0,0 0),(0.25 0.25,0.25 0.75,0.75 0.75,0.75 0.25,0.25 0.25))",
|
|
0.75,
|
|
),
|
|
("MULTIPOLYGON EMPTY", 0),
|
|
("MULTIPOLYGON (((0 0,0 1,1 1,0 0)))", 0.5),
|
|
("MULTIPOLYGON (((0 0,0 1,1 1,0 0)),((10 0,10 1,11 1,10 0)))", 1),
|
|
("MULTIPOLYGON Z (((0 0 100,0 1 100,1 1 100,0 0 100)))", 0.5),
|
|
("MULTIPOLYGON M (((0 0 100,0 1 100,1 1 100,0 0 100)))", 0.5),
|
|
("MULTIPOLYGON ZM (((0 0 100 200,0 1 100 200,1 1 100 200,0 0 100 200)))", 0.5),
|
|
("CURVEPOLYGON ((0 0,0 1,1 1,0 0))", 0.5),
|
|
("MULTISURFACE (((0 0,0 1,1 1,0 0)))", 0.5),
|
|
# Below is Spatialite encoding of POLYGON((0 0,0 1,1 1,0 0))
|
|
(
|
|
"X'00010000000000000000000000000000000000000000000000000000f03f000000000000f03f7c030000000100000004000000000000000000000000000000000000000000000000000000000000000000f03f000000000000f03f000000000000f03f00000000000000000000000000000000fe'",
|
|
0.5,
|
|
),
|
|
],
|
|
)
|
|
def test_ogr_gpkg_st_area(wkt_or_binary, area):
|
|
|
|
filename = "/vsimem/test_ogr_gpkg_st_area.gpkg"
|
|
ds = ogr.GetDriverByName("GPKG").CreateDataSource(filename)
|
|
lyr = ds.CreateLayer("test")
|
|
if wkt_or_binary and wkt_or_binary.startswith("X'"):
|
|
sql = "INSERT INTO test(geom) VALUES (" + wkt_or_binary + ")"
|
|
ds.ExecuteSQL(sql)
|
|
else:
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
if wkt_or_binary:
|
|
f.SetGeometryDirectly(ogr.CreateGeometryFromWkt(wkt_or_binary))
|
|
lyr.CreateFeature(f)
|
|
sql_lyr = ds.ExecuteSQL("SELECT ST_Area(geom) FROM test")
|
|
f = sql_lyr.GetNextFeature()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
ds = None
|
|
gdal.Unlink(filename)
|
|
assert f.GetField(0) == area
|
|
|
|
|
|
###############################################################################
|
|
# Test reading a layer with a generated column
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
get_sqlite_version() < (3, 31, 0),
|
|
reason="sqlite >= 3.31 needed",
|
|
)
|
|
def test_ogr_gpkg_read_generated_column():
|
|
|
|
filename = "/vsimem/test_ogr_gpkg_read_generated_column.gpkg"
|
|
ds = ogr.GetDriverByName("GPKG").CreateDataSource(filename)
|
|
ds.ExecuteSQL(
|
|
"CREATE TABLE test (fid INTEGER PRIMARY KEY NOT NULL,unused TEXT,strfield TEXT,strfield_generated TEXT GENERATED ALWAYS AS (strfield || '_generated'),intfield_generated_stored INTEGER GENERATED ALWAYS AS (5) STORED)"
|
|
)
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_contents (table_name,data_type,identifier,description,last_change,srs_id) VALUES ('test','attributes','test','','',0)"
|
|
)
|
|
ds = None
|
|
|
|
ds = ogr.Open(filename, update=1)
|
|
lyr = ds.GetLayer(0)
|
|
assert lyr.GetLayerDefn().GetFieldCount() == 4
|
|
assert lyr.GetLayerDefn().GetFieldDefn(2).GetName() == "strfield_generated"
|
|
assert lyr.GetLayerDefn().GetFieldDefn(2).GetType() == ogr.OFTString
|
|
assert lyr.GetLayerDefn().GetFieldDefn(3).GetName() == "intfield_generated_stored"
|
|
assert lyr.GetLayerDefn().GetFieldDefn(3).GetType() == ogr.OFTInteger64
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetField("strfield", "foo")
|
|
assert lyr.CreateFeature(f) == ogr.OGRERR_NONE
|
|
|
|
lyr.ResetReading()
|
|
f = lyr.GetNextFeature()
|
|
assert f["strfield"] == "foo"
|
|
assert f["strfield_generated"] == "foo_generated"
|
|
assert f["intfield_generated_stored"] == 5
|
|
|
|
assert lyr.SetFeature(f) == ogr.OGRERR_NONE
|
|
lyr.ResetReading()
|
|
f = lyr.GetNextFeature()
|
|
assert f["strfield"] == "foo"
|
|
assert f["strfield_generated"] == "foo_generated"
|
|
|
|
f.SetField("strfield", "bar")
|
|
assert lyr.SetFeature(f) == ogr.OGRERR_NONE
|
|
lyr.ResetReading()
|
|
f = lyr.GetNextFeature()
|
|
assert f["strfield"] == "bar"
|
|
assert f["strfield_generated"] == "bar_generated"
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetField("strfield", "foo2")
|
|
f.SetField("strfield_generated", "ignored")
|
|
assert lyr.CreateFeature(f) == ogr.OGRERR_NONE
|
|
|
|
f = lyr.GetFeature(2)
|
|
assert f["strfield"] == "foo2"
|
|
assert f["strfield_generated"] == "foo2_generated"
|
|
f = None
|
|
|
|
assert lyr.DeleteField(0) == ogr.OGRERR_NONE
|
|
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetField("strfield", "foo3")
|
|
assert lyr.CreateFeature(f) == ogr.OGRERR_NONE
|
|
|
|
f = lyr.GetFeature(3)
|
|
assert f["strfield"] == "foo3"
|
|
# None for sqlite < 3.35.5 that uses table recreation for DeleteField() implementation
|
|
# and thus for now the generated column expression is lost
|
|
assert (
|
|
f["strfield_generated"] == "foo3_generated" or f["strfield_generated"] is None
|
|
)
|
|
|
|
ds = None
|
|
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test gdal_get_pixel_value() function
|
|
|
|
|
|
def test_ogr_gpkg_sql_gdal_get_pixel_value():
|
|
|
|
filename = "/vsimem/test_ogr_gpkg_sql_gdal_get_pixel_value.gpkg"
|
|
ds = ogr.GetDriverByName("GPKG").CreateDataSource(filename)
|
|
|
|
with gdaltest.config_option("OGR_SQLITE_ALLOW_EXTERNAL_ACCESS", "YES"):
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"select gdal_get_pixel_value('../gcore/data/byte.tif', 1, 'georef', 440780, 3751080)"
|
|
)
|
|
f = sql_lyr.GetNextFeature()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
assert f[0] == 156
|
|
|
|
with gdaltest.config_option("OGR_SQLITE_ALLOW_EXTERNAL_ACCESS", "YES"):
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"select gdal_get_pixel_value('../gcore/data/byte.tif', 1, 'pixel', 1, 4)"
|
|
)
|
|
f = sql_lyr.GetNextFeature()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
assert f[0] == 156
|
|
|
|
with gdaltest.config_option("OGR_SQLITE_ALLOW_EXTERNAL_ACCESS", "YES"):
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"select gdal_get_pixel_value('../gcore/data/float64.tif', 1, 'pixel', 0, 1)"
|
|
)
|
|
f = sql_lyr.GetNextFeature()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
assert f[0] == 115.0
|
|
|
|
# Invalid column
|
|
with gdaltest.config_option("OGR_SQLITE_ALLOW_EXTERNAL_ACCESS", "YES"):
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"select gdal_get_pixel_value('../gcore/data/byte.tif', 1, 'pixel', -1, 0)"
|
|
)
|
|
f = sql_lyr.GetNextFeature()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
assert f[0] is None
|
|
|
|
# Missing OGR_SQLITE_ALLOW_EXTERNAL_ACCESS
|
|
with gdaltest.error_handler():
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"select gdal_get_pixel_value('../gcore/data/byte.tif', 1, 'georef', 440720, 3751320)"
|
|
)
|
|
f = sql_lyr.GetNextFeature()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
assert f[0] is None
|
|
|
|
# NULL as 1st arg
|
|
with gdaltest.error_handler():
|
|
with gdaltest.config_option("OGR_SQLITE_ALLOW_EXTERNAL_ACCESS", "YES"):
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"select gdal_get_pixel_value(NULL, 1, 'pixel', 0, 0)"
|
|
)
|
|
f = sql_lyr.GetNextFeature()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
assert f[0] is None
|
|
|
|
# NULL as 2nd arg
|
|
with gdaltest.error_handler():
|
|
with gdaltest.config_option("OGR_SQLITE_ALLOW_EXTERNAL_ACCESS", "YES"):
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"select gdal_get_pixel_value('../gcore/data/byte.tif', NULL, 'pixel', 0, 0)"
|
|
)
|
|
f = sql_lyr.GetNextFeature()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
assert f[0] is None
|
|
|
|
# NULL as 3rd arg
|
|
with gdaltest.error_handler():
|
|
with gdaltest.config_option("OGR_SQLITE_ALLOW_EXTERNAL_ACCESS", "YES"):
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"select gdal_get_pixel_value('../gcore/data/byte.tif', 1, NULL, 0, 0)"
|
|
)
|
|
f = sql_lyr.GetNextFeature()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
assert f[0] is None
|
|
|
|
# NULL as 4th arg
|
|
with gdaltest.error_handler():
|
|
with gdaltest.config_option("OGR_SQLITE_ALLOW_EXTERNAL_ACCESS", "YES"):
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"select gdal_get_pixel_value('../gcore/data/byte.tif', 1, 'pixel', NULL, 0)"
|
|
)
|
|
f = sql_lyr.GetNextFeature()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
assert f[0] is None
|
|
|
|
# NULL as 5th arg
|
|
with gdaltest.error_handler():
|
|
with gdaltest.config_option("OGR_SQLITE_ALLOW_EXTERNAL_ACCESS", "YES"):
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"select gdal_get_pixel_value('../gcore/data/byte.tif', 1, 'pixel', 0, NULL)"
|
|
)
|
|
f = sql_lyr.GetNextFeature()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
assert f[0] is None
|
|
|
|
# Invalid band number
|
|
with gdaltest.error_handler():
|
|
with gdaltest.config_option("OGR_SQLITE_ALLOW_EXTERNAL_ACCESS", "YES"):
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"select gdal_get_pixel_value('../gcore/data/byte.tif', 0, 'pixel', 0, 0)"
|
|
)
|
|
f = sql_lyr.GetNextFeature()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
assert f[0] is None
|
|
|
|
# Invalid value for 3rd argument
|
|
with gdaltest.error_handler():
|
|
with gdaltest.config_option("OGR_SQLITE_ALLOW_EXTERNAL_ACCESS", "YES"):
|
|
sql_lyr = ds.ExecuteSQL(
|
|
"select gdal_get_pixel_value('../gcore/data/byte.tif', 1, 'invalid', 0, 0)"
|
|
)
|
|
f = sql_lyr.GetNextFeature()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
assert f[0] is None
|
|
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test SOZip writing and reading
|
|
|
|
|
|
def test_ogr_gpkg_sozip():
|
|
|
|
filename = "/vsimem/test_ogr_gpkg_sozip.gpkg.zip"
|
|
|
|
with gdaltest.config_options(
|
|
{"CPL_SOZIP_MIN_FILE_SIZE": "256", "CPL_VSIL_DEFLATE_CHUNK_SIZE": "128"}
|
|
):
|
|
ds = ogr.GetDriverByName("GPKG").CreateDataSource(filename)
|
|
ds.CreateLayer("foo")
|
|
ds = None
|
|
|
|
md = gdal.GetFileMetadata(f"/vsizip/{filename}/test_ogr_gpkg_sozip.gpkg", "ZIP")
|
|
assert md["SOZIP_VALID"] == "YES"
|
|
|
|
ds = ogr.Open(filename)
|
|
assert ds
|
|
assert ds.GetLayer(0).GetName() == "foo"
|
|
ds = None
|
|
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test inserting a non-spatial layer into a database that has non-spatial
|
|
# layers which are not registered in gpkg_contents
|
|
# Cf https://github.com/qgis/QGIS/issues/51721
|
|
|
|
|
|
@pytest.mark.parametrize("with_gpkg_ogr_contents", [True, False])
|
|
def test_ogr_gpkg_add_non_spatial_layer_in_existing_database_with_unregistered(
|
|
with_gpkg_ogr_contents,
|
|
):
|
|
|
|
filename = "/vsimem/ogr_gpkg_add_non_spatial_layer_in_existing_database_with_unregistered.gpkg"
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(filename)
|
|
ds.CreateLayer("point", geom_type=ogr.wkbPoint)
|
|
ds.ExecuteSQL(
|
|
"CREATE TABLE non_spatial(fid INTEGER PRIMARY KEY AUTOINCREMENT, str TEXT)"
|
|
)
|
|
ds = None
|
|
|
|
if not with_gpkg_ogr_contents:
|
|
ds = ogr.Open(filename, update=1)
|
|
ds.ExecuteSQL("DROP TABLE gpkg_ogr_contents")
|
|
ds = None
|
|
|
|
ds = ogr.Open(filename, update=1)
|
|
assert ds.GetLayerCount() == 2
|
|
assert set(ds.GetLayer(i).GetName() for i in range(ds.GetLayerCount())) == set(
|
|
["point", "non_spatial"]
|
|
)
|
|
sql_lyr = ds.ExecuteSQL("SELECT * FROM gpkg_contents")
|
|
assert sql_lyr.GetFeatureCount() == 1
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
|
|
assert ds.CreateLayer("non_spatial2", geom_type=ogr.wkbNone) is not None
|
|
ds = None
|
|
|
|
assert validate(filename), "validation failed"
|
|
|
|
ds = ogr.Open(filename)
|
|
assert ds.GetLayerCount() == 3
|
|
assert set(ds.GetLayer(i).GetName() for i in range(ds.GetLayerCount())) == set(
|
|
["point", "non_spatial", "non_spatial2"]
|
|
)
|
|
sql_lyr = ds.ExecuteSQL("SELECT * FROM gpkg_contents")
|
|
assert sql_lyr.GetFeatureCount() == 1
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
if with_gpkg_ogr_contents:
|
|
sql_lyr = ds.ExecuteSQL("SELECT * FROM gpkg_ogr_contents")
|
|
assert sql_lyr.GetFeatureCount() == 2
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
ds = None
|
|
|
|
gdaltest.gpkg_dr.DeleteDataSource(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test UpdateFeature()
|
|
|
|
|
|
def test_ogr_gpkg_update_feature():
|
|
|
|
filename = "/vsimem/test_ogr_gpkg_update_feature.gpkg.zip"
|
|
|
|
ds = ogr.GetDriverByName("GPKG").CreateDataSource(filename)
|
|
lyr = ds.CreateLayer("test")
|
|
lyr.CreateField(ogr.FieldDefn("int_field", ogr.OFTInteger))
|
|
lyr.CreateField(ogr.FieldDefn("str_field", ogr.OFTString))
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT (1 2)"))
|
|
f["int_field"] = 1
|
|
f["str_field"] = "foo"
|
|
assert lyr.CreateFeature(f) == ogr.OGRERR_NONE
|
|
assert lyr.TestCapability(ogr.OLCUpdateFeature) == 1
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetFID(1)
|
|
f["int_field"] = 123 # will be ignored
|
|
f["str_field"] = "bar"
|
|
assert lyr.UpdateFeature(f, [1], [], False) == ogr.OGRERR_NONE
|
|
# Check recycling of existing statement
|
|
f["str_field"] = "baz"
|
|
assert lyr.UpdateFeature(f, [1], [], False) == ogr.OGRERR_NONE
|
|
f = lyr.GetFeature(1)
|
|
assert f.GetGeometryRef().ExportToWkt() == "POINT (1 2)"
|
|
assert f["int_field"] == 1
|
|
assert f["str_field"] == "baz"
|
|
|
|
# Do not modify unset fields
|
|
f.UnsetField(1)
|
|
assert lyr.UpdateFeature(f, [1], [], False) == ogr.OGRERR_NONE
|
|
f = lyr.GetFeature(1)
|
|
assert f["str_field"] == "baz"
|
|
|
|
# Nullify geometry
|
|
f.SetGeometry(None)
|
|
assert lyr.UpdateFeature(f, [], [0], False) == ogr.OGRERR_NONE
|
|
f = lyr.GetFeature(1)
|
|
assert f.GetGeometryRef() is None
|
|
|
|
# Set non null geometry
|
|
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT (1 2)"))
|
|
assert lyr.UpdateFeature(f, [], [0], False) == ogr.OGRERR_NONE
|
|
assert f.GetGeometryRef().ExportToWkt() == "POINT (1 2)"
|
|
|
|
ds = None
|
|
|
|
gdal.Unlink(filename)
|
|
|
|
|
|
###############################################################################
|
|
# Test ogr_layer_Extent()
|
|
|
|
|
|
def test_ogr_gpkg_ogr_layer_Extent():
|
|
|
|
tmpfilename = "/vsimem/test_ogr_gpkg_ogr_layer_Extent.gpkg"
|
|
try:
|
|
ds = ogr.GetDriverByName("GPKG").CreateDataSource(tmpfilename)
|
|
lyr = ds.CreateLayer("my_layer", geom_type=ogr.wkbLineString)
|
|
feat = ogr.Feature(lyr.GetLayerDefn())
|
|
feat.SetGeometryDirectly(ogr.CreateGeometryFromWkt("LINESTRING (0 1,2 3)"))
|
|
lyr.CreateFeature(feat)
|
|
feat = None
|
|
|
|
# Test with invalid parameter
|
|
with gdaltest.error_handler():
|
|
sql_lyr = ds.ExecuteSQL("SELECT ogr_layer_Extent(12)")
|
|
feat = sql_lyr.GetNextFeature()
|
|
geom = feat.GetGeometryRef()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
|
|
assert geom is None
|
|
|
|
# Test on non existing layer
|
|
with gdaltest.error_handler():
|
|
sql_lyr = ds.ExecuteSQL("SELECT ogr_layer_Extent('foo')")
|
|
feat = sql_lyr.GetNextFeature()
|
|
geom = feat.GetGeometryRef()
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
|
|
assert geom is None
|
|
|
|
# Test ogr_layer_Extent()
|
|
sql_lyr = ds.ExecuteSQL("SELECT ogr_layer_Extent('my_layer')")
|
|
feat = sql_lyr.GetNextFeature()
|
|
geom_wkt = feat.GetGeometryRef().ExportToWkt()
|
|
feat = None
|
|
ds.ReleaseResultSet(sql_lyr)
|
|
|
|
assert geom_wkt == "POLYGON ((0 1,2 1,2 3,0 3,0 1))"
|
|
|
|
finally:
|
|
gdal.Unlink(tmpfilename)
|
|
|
|
|
|
###############################################################################
|
|
# Test field alternative names and comments
|
|
|
|
|
|
def test_ogr_gpkg_field_alternative_names_comment():
|
|
|
|
dbname = "/vsimem/ogr_gpkg_alternative_names.gpkg"
|
|
try:
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(dbname)
|
|
lyr = ds.CreateLayer("test", geom_type=ogr.wkbPolygon)
|
|
lyr.CreateField(ogr.FieldDefn("foo", ogr.OFTString))
|
|
lyr.CreateField(ogr.FieldDefn("baz", ogr.OFTString))
|
|
|
|
# with no gpkg_data_columns table
|
|
lyr = ds.GetLayer("test")
|
|
assert lyr.GetLayerDefn().GetFieldCount() == 2
|
|
assert lyr.GetLayerDefn().GetFieldDefn(0).GetName() == "foo"
|
|
assert lyr.GetLayerDefn().GetFieldDefn(0).GetAlternativeName() == ""
|
|
assert lyr.GetLayerDefn().GetFieldDefn(0).GetComment() == ""
|
|
assert lyr.GetLayerDefn().GetFieldDefn(1).GetName() == "baz"
|
|
assert lyr.GetLayerDefn().GetFieldDefn(1).GetAlternativeName() == ""
|
|
assert lyr.GetLayerDefn().GetFieldDefn(1).GetComment() == ""
|
|
|
|
ds.ExecuteSQL(
|
|
"""CREATE TABLE gpkg_data_columns (
|
|
table_name TEXT NOT NULL,
|
|
column_name TEXT NOT NULL,
|
|
name TEXT,
|
|
title TEXT,
|
|
description TEXT,
|
|
mime_type TEXT,
|
|
constraint_name TEXT,
|
|
CONSTRAINT pk_gdc PRIMARY KEY (table_name, column_name),
|
|
CONSTRAINT gdc_tn UNIQUE (table_name, name)
|
|
)"""
|
|
)
|
|
# name same as column name, won't be used as alternative name
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_data_columns('table_name', 'column_name', 'name', 'description') VALUES ('test', 'foo', 'foo', 'my description')"
|
|
)
|
|
ds = None
|
|
|
|
ds = gdal.OpenEx(dbname, gdal.OF_VECTOR | gdal.OF_UPDATE)
|
|
lyr = ds.GetLayer("test")
|
|
assert lyr.GetLayerDefn().GetFieldCount() == 2
|
|
assert lyr.GetLayerDefn().GetFieldDefn(0).GetName() == "foo"
|
|
assert lyr.GetLayerDefn().GetFieldDefn(0).GetAlternativeName() == ""
|
|
assert lyr.GetLayerDefn().GetFieldDefn(0).GetComment() == "my description"
|
|
assert lyr.GetLayerDefn().GetFieldDefn(1).GetName() == "baz"
|
|
assert lyr.GetLayerDefn().GetFieldDefn(1).GetAlternativeName() == ""
|
|
assert lyr.GetLayerDefn().GetFieldDefn(1).GetComment() == ""
|
|
|
|
# name different from column name, should be used as alternative names
|
|
ds.ExecuteSQL("DELETE FROM gpkg_data_columns")
|
|
ds.ExecuteSQL(
|
|
"INSERT INTO gpkg_data_columns('table_name', 'column_name', 'name') VALUES ('test', 'foo', 'Foo field')"
|
|
)
|
|
ds = None
|
|
|
|
ds = gdaltest.gpkg_dr.Open(dbname)
|
|
lyr = ds.GetLayer("test")
|
|
assert lyr.GetLayerDefn().GetFieldCount() == 2
|
|
assert lyr.GetLayerDefn().GetFieldDefn(0).GetName() == "foo"
|
|
assert lyr.GetLayerDefn().GetFieldDefn(0).GetAlternativeName() == "Foo field"
|
|
assert lyr.GetLayerDefn().GetFieldDefn(1).GetName() == "baz"
|
|
assert lyr.GetLayerDefn().GetFieldDefn(1).GetAlternativeName() == ""
|
|
ds = None
|
|
|
|
finally:
|
|
gdal.Unlink(dbname)
|
|
|
|
|
|
###############################################################################
|
|
# Test altering field definition to add alternative names and comments
|
|
|
|
|
|
def test_ogr_gpkg_field_alter_field_defn_alternative_names_comment():
|
|
|
|
dbname = "/vsimem/ogr_gpkg_alternative_names_alter_defn.gpkg"
|
|
try:
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(dbname)
|
|
lyr = ds.CreateLayer("test", geom_type=ogr.wkbPolygon)
|
|
lyr.CreateField(ogr.FieldDefn("foo", ogr.OFTString))
|
|
lyr.CreateField(ogr.FieldDefn("baz", ogr.OFTString))
|
|
|
|
# with no gpkg_data_columns table
|
|
lyr = ds.GetLayer("test")
|
|
assert lyr.GetLayerDefn().GetFieldCount() == 2
|
|
assert lyr.GetLayerDefn().GetFieldDefn(0).GetName() == "foo"
|
|
assert lyr.GetLayerDefn().GetFieldDefn(0).GetAlternativeName() == ""
|
|
assert lyr.GetLayerDefn().GetFieldDefn(0).GetComment() == ""
|
|
assert lyr.GetLayerDefn().GetFieldDefn(1).GetName() == "baz"
|
|
assert lyr.GetLayerDefn().GetFieldDefn(1).GetAlternativeName() == ""
|
|
assert lyr.GetLayerDefn().GetFieldDefn(1).GetComment() == ""
|
|
|
|
foo_with_alternative_name = ogr.FieldDefn("foo")
|
|
foo_with_alternative_name.SetAlternativeName("alt foo name")
|
|
|
|
ret = lyr.AlterFieldDefn(0, foo_with_alternative_name, ogr.ALTER_ALL_FLAG)
|
|
assert ret == 0
|
|
|
|
baz_with_comment = ogr.FieldDefn("baz")
|
|
baz_with_comment.SetComment("baz comment")
|
|
|
|
ret = lyr.AlterFieldDefn(1, baz_with_comment, ogr.ALTER_ALL_FLAG)
|
|
assert ret == 0
|
|
|
|
assert lyr.GetLayerDefn().GetFieldCount() == 2
|
|
assert lyr.GetLayerDefn().GetFieldDefn(0).GetName() == "foo"
|
|
assert lyr.GetLayerDefn().GetFieldDefn(0).GetAlternativeName() == "alt foo name"
|
|
assert lyr.GetLayerDefn().GetFieldDefn(0).GetComment() == ""
|
|
assert lyr.GetLayerDefn().GetFieldDefn(1).GetName() == "baz"
|
|
assert lyr.GetLayerDefn().GetFieldDefn(1).GetAlternativeName() == ""
|
|
assert lyr.GetLayerDefn().GetFieldDefn(1).GetComment() == "baz comment"
|
|
|
|
del lyr
|
|
ds = None
|
|
|
|
ds = gdal.OpenEx(dbname, gdal.OF_VECTOR | gdal.OF_UPDATE)
|
|
lyr = ds.GetLayer("test")
|
|
assert lyr.GetLayerDefn().GetFieldCount() == 2
|
|
assert lyr.GetLayerDefn().GetFieldDefn(0).GetName() == "foo"
|
|
assert lyr.GetLayerDefn().GetFieldDefn(0).GetAlternativeName() == "alt foo name"
|
|
assert lyr.GetLayerDefn().GetFieldDefn(0).GetComment() == ""
|
|
assert lyr.GetLayerDefn().GetFieldDefn(1).GetName() == "baz"
|
|
assert lyr.GetLayerDefn().GetFieldDefn(1).GetAlternativeName() == ""
|
|
assert lyr.GetLayerDefn().GetFieldDefn(1).GetComment() == "baz comment"
|
|
|
|
# create field
|
|
field_defn = ogr.FieldDefn("third", ogr.OFTString)
|
|
field_defn.SetAlternativeName("third alias")
|
|
field_defn.SetComment("third comment")
|
|
assert lyr.CreateField(field_defn) == 0
|
|
|
|
del lyr
|
|
ds = None
|
|
|
|
ds = gdal.OpenEx(dbname, gdal.OF_VECTOR)
|
|
lyr = ds.GetLayer("test")
|
|
|
|
assert lyr.GetLayerDefn().GetFieldCount() == 3
|
|
assert lyr.GetLayerDefn().GetFieldDefn(0).GetName() == "foo"
|
|
assert lyr.GetLayerDefn().GetFieldDefn(0).GetAlternativeName() == "alt foo name"
|
|
assert lyr.GetLayerDefn().GetFieldDefn(0).GetComment() == ""
|
|
assert lyr.GetLayerDefn().GetFieldDefn(1).GetName() == "baz"
|
|
assert lyr.GetLayerDefn().GetFieldDefn(1).GetAlternativeName() == ""
|
|
assert lyr.GetLayerDefn().GetFieldDefn(1).GetComment() == "baz comment"
|
|
assert lyr.GetLayerDefn().GetFieldDefn(2).GetName() == "third"
|
|
assert lyr.GetLayerDefn().GetFieldDefn(2).GetAlternativeName() == "third alias"
|
|
assert lyr.GetLayerDefn().GetFieldDefn(2).GetComment() == "third comment"
|
|
ds = None
|
|
|
|
finally:
|
|
gdal.Unlink(dbname)
|
|
|
|
|
|
###############################################################################
|
|
# Test RTree triggers
|
|
|
|
|
|
@pytest.mark.parametrize("gpkg_version", ["1.2", "1.4"])
|
|
def test_ogr_gpkg_rtree_triggers(gpkg_version):
|
|
def get_rtree_entry_count(ds):
|
|
with ds.ExecuteSQL("SELECT * FROM rtree_test_geom") as sql_lyr:
|
|
return sql_lyr.GetFeatureCount()
|
|
|
|
dbname = "/vsimem/test_ogr_gpkg_rtree_triggers.gpkg"
|
|
try:
|
|
ds = gdaltest.gpkg_dr.CreateDataSource(
|
|
dbname, options=["VERSION=" + gpkg_version]
|
|
)
|
|
ds.CreateLayer("test", geom_type=ogr.wkbPoint)
|
|
ds = None
|
|
|
|
ds = ogr.Open(dbname, update=1)
|
|
lyr = ds.GetLayer(0)
|
|
|
|
# Create a feature without geometry
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
assert lyr.CreateFeature(f) == ogr.OGRERR_NONE
|
|
assert get_rtree_entry_count(ds) == 0
|
|
|
|
# Update the feature with a geometry
|
|
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT (1 2)"))
|
|
assert lyr.SetFeature(f) == ogr.OGRERR_NONE
|
|
assert get_rtree_entry_count(ds) == 1
|
|
lyr.SetSpatialFilterRect(1, 2, 1, 2)
|
|
assert lyr.GetFeatureCount() == 1
|
|
|
|
# Update the feature with another geometry
|
|
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT (3 4)"))
|
|
assert lyr.SetFeature(f) == ogr.OGRERR_NONE
|
|
assert get_rtree_entry_count(ds) == 1
|
|
lyr.SetSpatialFilterRect(3, 4, 3, 4)
|
|
assert lyr.GetFeatureCount() == 1
|
|
|
|
# Upsert the feature with another geometry
|
|
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT (5 6)"))
|
|
assert lyr.UpsertFeature(f) == ogr.OGRERR_NONE
|
|
assert get_rtree_entry_count(ds) == 1
|
|
lyr.SetSpatialFilterRect(5, 6, 5, 6)
|
|
assert lyr.GetFeatureCount() == 1
|
|
|
|
# Upsert the feature without geometry
|
|
f.SetGeometry(None)
|
|
assert lyr.UpsertFeature(f) == ogr.OGRERR_NONE
|
|
assert get_rtree_entry_count(ds) == 0
|
|
|
|
# Upsert the feature with another geometry
|
|
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT (7 8)"))
|
|
assert lyr.UpsertFeature(f) == ogr.OGRERR_NONE
|
|
assert get_rtree_entry_count(ds) == 1
|
|
lyr.SetSpatialFilterRect(7, 8, 7, 8)
|
|
assert lyr.GetFeatureCount() == 1
|
|
|
|
# Upsert the feature with empty geometry
|
|
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT EMPTY"))
|
|
assert lyr.UpsertFeature(f) == ogr.OGRERR_NONE
|
|
assert get_rtree_entry_count(ds) == 0
|
|
|
|
# Upsert the feature with a geometry
|
|
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT (7 8)"))
|
|
assert lyr.UpsertFeature(f) == ogr.OGRERR_NONE
|
|
assert get_rtree_entry_count(ds) == 1
|
|
lyr.SetSpatialFilterRect(7, 8, 7, 8)
|
|
assert lyr.GetFeatureCount() == 1
|
|
|
|
# Remove the geometry
|
|
f.SetGeometry(None)
|
|
assert lyr.SetFeature(f) == ogr.OGRERR_NONE
|
|
assert get_rtree_entry_count(ds) == 0
|
|
|
|
# Delete the geometry
|
|
assert lyr.DeleteFeature(f.GetFID()) == ogr.OGRERR_NONE
|
|
assert get_rtree_entry_count(ds) == 0
|
|
|
|
# Create a feature with a geometry
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT (9 10)"))
|
|
assert lyr.CreateFeature(f) == ogr.OGRERR_NONE
|
|
assert get_rtree_entry_count(ds) == 1
|
|
lyr.SetSpatialFilterRect(9, 10, 9, 10)
|
|
assert lyr.GetFeatureCount() == 1
|
|
|
|
# Delete the geometry
|
|
assert lyr.DeleteFeature(f.GetFID()) == ogr.OGRERR_NONE
|
|
assert get_rtree_entry_count(ds) == 0
|
|
|
|
# Create a feature with a empty geometry
|
|
f = ogr.Feature(lyr.GetLayerDefn())
|
|
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT EMPTY"))
|
|
assert lyr.CreateFeature(f) == ogr.OGRERR_NONE
|
|
assert get_rtree_entry_count(ds) == 0
|
|
|
|
# Delete the geometry
|
|
assert lyr.DeleteFeature(f.GetFID()) == ogr.OGRERR_NONE
|
|
assert get_rtree_entry_count(ds) == 0
|
|
|
|
ds = None
|
|
|
|
finally:
|
|
gdal.Unlink(dbname)
|