Ejemplo n.º 1
0
def test_ogr_gmt_coord_only():

    with gdaltest.tempfile('/vsimem/test.gmt', """1 2 3\n"""):
        ds = ogr.Open('/vsimem/test.gmt')
        lyr = ds.GetLayer(0)
        f = lyr.GetNextFeature()
        assert not ogrtest.check_feature_geometry(f, 'POINT Z (1 2 3)'), f.GetGeometryRef().ExportToIsoWkt()
Ejemplo n.º 2
0
def test_ogr2ogr_lib_makevalid():

    # Check if MakeValid() is available
    g = ogr.CreateGeometryFromWkt('POLYGON ((0 0,10 10,0 10,10 0,0 0))')
    with gdaltest.error_handler():
        make_valid_available = g.MakeValid() is not None

    tmpfilename = '/vsimem/tmp.csv'
    with gdaltest.tempfile(
            tmpfilename, """id,WKT
1,"POLYGON ((0 0,10 10,0 10,10 0,0 0))"
2,"POLYGON ((0 0,0 1,0.5 1,0.5 0.75,0.5 1,1 1,1 0,0 0))"
"""):
        if make_valid_available:
            ds = gdal.VectorTranslate('',
                                      tmpfilename,
                                      format='Memory',
                                      makeValid=True)
        else:
            with gdaltest.error_handler():
                with pytest.raises(Exception):
                    gdal.VectorTranslate('',
                                         tmpfilename,
                                         format='Memory',
                                         makeValid=True)
                return

    lyr = ds.GetLayer(0)
    f = lyr.GetNextFeature()
    assert ogrtest.check_feature_geometry(
        f, "MULTIPOLYGON (((0 0,5 5,10 0,0 0)),((5 5,0 10,10 10,5 5)))") == 0
    f = lyr.GetNextFeature()
    assert ogrtest.check_feature_geometry(
        f, "POLYGON ((0 0,0 1,0.5 1.0,1 1,1 0,0 0))") == 0
Ejemplo n.º 3
0
Archivo: jpegxl.py Proyecto: edzer/gdal
def test_jpegxl_lossless_copy_of_jpeg():

    has_box_api = 'COMPRESS_BOX' in gdal.GetDriverByName('JPEGXL').GetMetadataItem('DMD_CREATIONOPTIONLIST')

    src_ds = gdal.Open('data/jpeg/albania.jpg')
    outfilename = '/vsimem/out.jxl'
    gdal.GetDriverByName('JPEGXL').CreateCopy(outfilename, src_ds)
    if has_box_api:
        assert gdal.VSIStatL(outfilename + '.aux.xml') is None
    ds = gdal.Open(outfilename)
    assert ds is not None
    if has_box_api:
        assert set(ds.GetMetadataDomainList()) == set(['DERIVED_SUBDATASETS', 'EXIF', 'IMAGE_STRUCTURE'])
        assert ds.GetMetadataItem('HAS_JPEG_RECONSTRUCTION_DATA', '_DEBUG_') == 'YES'

    ds = None
    gdal.GetDriverByName('JPEGXL').Delete(outfilename)

    # Test failure in JxlEncoderAddJPEGFrame() by adding a truncated JPEG file
    data = open('data/jpeg/albania.jpg', 'rb').read()
    data = data[0:len(data)//2]
    with gdaltest.tempfile('/vsimem/truncated.jpg', data):
        src_ds = gdal.Open('/vsimem/truncated.jpg')
        with gdaltest.error_handler():
            assert gdal.GetDriverByName('JPEGXL').CreateCopy(outfilename, src_ds) is None
Ejemplo n.º 4
0
def test_ogr_gmt_coord_only():

    with gdaltest.tempfile('/vsimem/test.gmt', """1 2 3\n"""):
        ds = ogr.Open('/vsimem/test.gmt')
        lyr = ds.GetLayer(0)
        f = lyr.GetNextFeature()
        assert not ogrtest.check_feature_geometry(f, 'POINT Z (1 2 3)'), f.GetGeometryRef().ExportToIsoWkt()
Ejemplo n.º 5
0
def test_ogr2ogr_lib_sql_filename():

    with gdaltest.tempfile('/vsimem/my.sql', """-- initial comment\nselect\n'--''--' as literalfield,* from --comment\npoly\n-- trailing comment"""):
        ds = gdal.VectorTranslate('', '../ogr/data/poly.shp', options = '-f Memory -sql @/vsimem/my.sql')
    lyr = ds.GetLayer(0)
    assert lyr.GetFeatureCount() == 10
    assert lyr.GetLayerDefn().GetFieldIndex('literalfield') == 0
Ejemplo n.º 6
0
def test_contour_too_many_levels():

    ogr_ds = ogr.GetDriverByName('ESRI Shapefile').CreateDataSource(
        '/vsimem/contour.shp')
    ogr_lyr = ogr_ds.CreateLayer('contour', geom_type=ogr.wkbLineString)
    field_defn = ogr.FieldDefn('ID', ogr.OFTInteger)
    ogr_lyr.CreateField(field_defn)

    content1 = """ncols        2
nrows        2
xllcorner    0
yllcorner    0
cellsize     1
 1e30 0
 0 0"""

    content2 = """ncols        2
nrows        2
xllcorner    0
yllcorner    0
cellsize     1
 1e6 0
 0 0"""
    for content in (content1, content2):

        with gdaltest.tempfile('/vsimem/test.asc', content):
            ds = gdal.Open('/vsimem/test.asc')
            with gdaltest.error_handler():
                assert gdal.ContourGenerateEx(
                    ds.GetRasterBand(1),
                    ogr_lyr,
                    options=["LEVEL_INTERVAL=1", "ID_FIELD=0"]) != 0

        with gdaltest.tempfile('/vsimem/test.asc', content):
            ds = gdal.Open('/vsimem/test.asc')
            with gdaltest.error_handler():
                assert gdal.ContourGenerateEx(
                    ds.GetRasterBand(1),
                    ogr_lyr,
                    options=[
                        "LEVEL_INTERVAL=1", "LEVEL_EXP_BASE=1.0001",
                        "ID_FIELD=0"
                    ]) != 0

    ogr_ds = None
    ogr.GetDriverByName('ESRI Shapefile').DeleteDataSource(
        '/vsimem/contour.shp')
Ejemplo n.º 7
0
Archivo: xyz.py Proyecto: visr/gdal
def test_xyz_9():

    content = """"A"    "B"     "C"
0 0 50
10 0 100
0 10 150
10 10 200
"""

    with gdaltest.tempfile('/vsimem/grid.xyz', content):
        ds = gdal.Open('/vsimem/grid.xyz')
        assert ds.RasterXSize == 2 and ds.RasterYSize == 2
        cs = ds.GetRasterBand(1).Checksum()

    assert cs == 22
Ejemplo n.º 8
0
def test_xyz_organized_by_columns_float32():

    content = """X Y Z
0 20 50.5
0 10 100
0 0 150
10 20 200
10 10 250
10 0 300

"""

    with gdaltest.tempfile('/vsimem/grid.xyz', content):
        ds = gdal.Open('/vsimem/grid.xyz')
        assert ds.RasterXSize == 2 and ds.RasterYSize == 3
        assert ds.GetGeoTransform() == (-5.0, 10.0, 0.0, 25.0, 0.0, -10.0)
        assert ds.GetRasterBand(1).DataType == gdal.GDT_Float32
        assert struct.unpack('f' * 6, ds.ReadRaster()) == (50.5, 200.0, 100.0,
                                                           250.0, 150.0, 300.0)
Ejemplo n.º 9
0
def test_xyz_floating_point_step_organized_by_columns_float32():

    content = """X Y Z
1.1 1.3 50.5
1.1 1.2 100
1.1 1.1 150
1.2 1.3 200
1.2 1.2 250
1.2 1.1 300
1.3 1.3 350
1.3 1.2 400
1.3 1.1 450

"""

    with gdaltest.tempfile('/vsimem/grid.xyz', content):
        ds = gdal.Open('/vsimem/grid.xyz')
        assert ds.RasterXSize == 3 and ds.RasterYSize == 3
        assert ds.GetGeoTransform() == pytest.approx(
            (1.05, 0.1, 0.0, 1.35, 0, -0.1))
        assert ds.GetRasterBand(1).DataType == gdal.GDT_Float32
        assert struct.unpack('f' * 9, ds.ReadRaster()) == (50.5, 200.0, 350.0,
                                                           100.0, 250.0, 400.0,
                                                           150.0, 300.0, 450.0)
Ejemplo n.º 10
0
def test_xyz_floating_point_step_organized_by_rows_int16():

    content = """X Y Z
1.1 1.1 50
1.2 1.1 100
1.3 1.1 150
1.1 1.2 200
1.2 1.2 250
1.3 1.2 300
1.1 1.3 350
1.2 1.3 400
1.3 1.3 450

"""

    with gdaltest.tempfile('/vsimem/grid.xyz', content):
        ds = gdal.Open('/vsimem/grid.xyz')
        assert ds.RasterXSize == 3 and ds.RasterYSize == 3
        assert ds.GetGeoTransform() == pytest.approx(
            (1.05, 0.1, 0.0, 1.05, 0, 0.1))
        assert ds.GetRasterBand(1).DataType == gdal.GDT_Int16
        assert struct.unpack('h' * 9,
                             ds.ReadRaster()) == (50, 100, 150, 200, 250, 300,
                                                  350, 400, 450)
Ejemplo n.º 11
0
def test_ogr_carto_vsimem():
    if ogrtest.carto_drv is None:
        pytest.skip()

    ogrtest.carto_api_key_ori = gdal.GetConfigOption('CARTO_API_KEY')
    gdal.SetConfigOption('CARTO_API_URL', '/vsimem/carto')
    gdal.SetConfigOption('CPL_CURL_ENABLE_VSIMEM', 'YES')

    gdal.FileFromMemBuffer(
        '/vsimem/carto&POSTFIELDS=q=SELECT postgis_version() LIMIT 500 OFFSET 0&api_key=foo',
        """{"rows":[{"postgis_version":"2.1 USE_GEOS=1 USE_PROJ=1 USE_STATS=1"}],"time":0.001,"fields":{"postgis_version":{"type":"string"}},"total_rows":1}"""
    )

    gdal.PushErrorHandler()
    ds = ogr.Open('CARTO:foo')
    gdal.PopErrorHandler()
    assert ds is None

    gdal.FileFromMemBuffer(
        '/vsimem/carto&POSTFIELDS=q=SELECT current_schema() LIMIT 500 OFFSET 0',
        """Content-Type: text/html\r

Error""")
    gdal.PushErrorHandler()
    ds = ogr.Open('CARTO:foo')
    gdal.PopErrorHandler()
    assert ds is None and gdal.GetLastErrorMsg().find('HTML error page') >= 0

    gdal.FileFromMemBuffer(
        '/vsimem/carto&POSTFIELDS=q=SELECT current_schema() LIMIT 500 OFFSET 0',
        """""")
    ds = ogr.Open('CARTO:foo')
    assert ds is None, gdal.GetLastErrorMsg()

    gdal.FileFromMemBuffer(
        '/vsimem/carto&POSTFIELDS=q=SELECT current_schema() LIMIT 500 OFFSET 0',
        """{""")
    gdal.PushErrorHandler()
    ds = ogr.Open('CARTO:foo')
    gdal.PopErrorHandler()
    assert ds is None and gdal.GetLastErrorMsg().find(
        'JSON parsing error') >= 0

    gdal.FileFromMemBuffer(
        '/vsimem/carto&POSTFIELDS=q=SELECT current_schema() LIMIT 500 OFFSET 0',
        """ "not_expected_json" """)
    ds = ogr.Open('CARTO:foo')
    assert ds is None, gdal.GetLastErrorMsg()

    gdal.FileFromMemBuffer(
        '/vsimem/carto&POSTFIELDS=q=SELECT current_schema() LIMIT 500 OFFSET 0',
        """{ "error" : [ "bla"] }""")
    gdal.PushErrorHandler()
    ds = ogr.Open('CARTO:foo')
    gdal.PopErrorHandler()
    assert ds is None and gdal.GetLastErrorMsg().find(
        'Error returned by server : bla') >= 0

    gdal.FileFromMemBuffer(
        '/vsimem/carto&POSTFIELDS=q=SELECT current_schema() LIMIT 500 OFFSET 0',
        """{ "fields" : null } """)
    ds = ogr.Open('CARTO:foo')
    assert ds is None, gdal.GetLastErrorMsg()

    gdal.FileFromMemBuffer(
        '/vsimem/carto&POSTFIELDS=q=SELECT current_schema() LIMIT 500 OFFSET 0',
        """{ "fields" : "invalid" } """)
    ds = ogr.Open('CARTO:foo')
    assert ds is None, gdal.GetLastErrorMsg()

    gdal.FileFromMemBuffer(
        '/vsimem/carto&POSTFIELDS=q=SELECT current_schema() LIMIT 500 OFFSET 0',
        """{ "fields" : {} } """)
    ds = ogr.Open('CARTO:foo')
    assert ds is None, gdal.GetLastErrorMsg()

    gdal.FileFromMemBuffer(
        '/vsimem/carto&POSTFIELDS=q=SELECT current_schema() LIMIT 500 OFFSET 0',
        """{ "fields" : { "foo": "invalid" } } """)
    ds = ogr.Open('CARTO:foo')
    assert ds is None, gdal.GetLastErrorMsg()

    gdal.FileFromMemBuffer(
        '/vsimem/carto&POSTFIELDS=q=SELECT current_schema() LIMIT 500 OFFSET 0',
        """{ "fields" : { "foo": {} } } """)
    ds = ogr.Open('CARTO:foo')
    assert ds is None, gdal.GetLastErrorMsg()

    gdal.FileFromMemBuffer(
        '/vsimem/carto&POSTFIELDS=q=SELECT current_schema() LIMIT 500 OFFSET 0',
        """{ "fields" : { "foo": { "type" : null } } } """)
    ds = ogr.Open('CARTO:foo')
    assert ds is None, gdal.GetLastErrorMsg()

    gdal.FileFromMemBuffer(
        '/vsimem/carto&POSTFIELDS=q=SELECT current_schema() LIMIT 500 OFFSET 0',
        """{ "fields" : { "foo": { "type" : {} } } } """)
    ds = ogr.Open('CARTO:foo')
    assert ds is None, gdal.GetLastErrorMsg()

    gdal.FileFromMemBuffer(
        '/vsimem/carto&POSTFIELDS=q=SELECT current_schema() LIMIT 500 OFFSET 0',
        """{ "fields" : { "foo": { "type" : "string" } } } """)
    ds = ogr.Open('CARTO:foo')
    assert ds is None, gdal.GetLastErrorMsg()

    gdal.FileFromMemBuffer(
        '/vsimem/carto&POSTFIELDS=q=SELECT current_schema() LIMIT 500 OFFSET 0',
        """{"rows":[ {"field1": "foo", "field2": "bar"} ],"fields":{"field1":{"type":"string"}, "field2":{"type":"string"}}}"""
    )
    ds = ogr.Open('CARTO:foo')
    assert ds is None, gdal.GetLastErrorMsg()

    gdal.FileFromMemBuffer(
        '/vsimem/carto&POSTFIELDS=q=SELECT current_schema() LIMIT 500 OFFSET 0',
        """{"rows":[],"fields":{"current_schema":{"type":"string"}}}""")
    gdal.PushErrorHandler()
    ds = ogr.Open('CARTO:foo')
    gdal.PopErrorHandler()
    assert ds is None, gdal.GetLastErrorMsg()

    gdal.FileFromMemBuffer(
        '/vsimem/carto&POSTFIELDS=q=SELECT current_schema() LIMIT 500 OFFSET 0',
        """{"rows":[{"current_schema":"public"}],"fields":{"current_schema":{"type":"unknown(19)"}}}"""
    )
    gdal.PushErrorHandler()
    ds = ogr.Open('CARTO:foo')
    gdal.PopErrorHandler()
    assert ds is None, gdal.GetLastErrorMsg()

    gdal.FileFromMemBuffer(
        '/vsimem/carto&POSTFIELDS=q=SELECT CDB_UserTables() LIMIT 500 OFFSET 0',
        """{"rows":[{"cdb_usertables":"table1"}],"fields":{"cdb_usertables":{"type":"string"}}}"""
    )
    ds = ogr.Open('CARTO:foo')
    assert ds is not None and ds.GetLayerCount() == 1, gdal.GetLastErrorMsg()

    gdal.PushErrorHandler()
    lyr_defn = ds.GetLayer(0).GetLayerDefn()
    gdal.PopErrorHandler()

    assert lyr_defn.GetFieldCount() == 0

    # Empty layer
    gdal.FileFromMemBuffer(
        '/vsimem/carto&POSTFIELDS=q=SELECT * FROM "table1" LIMIT 0',
        """{"rows":[],"fields":{}}""")
    ds = ogr.Open('CARTO:foo')
    lyr = ds.GetLayer(0)
    lyr_defn = lyr.GetLayerDefn()
    assert lyr_defn.GetFieldCount() == 0

    gdal.FileFromMemBuffer(
        '/vsimem/carto&POSTFIELDS=q=SELECT * FROM "table1" LIMIT 500 OFFSET 0',
        """{"rows":[{}],"fields":{}}}""")
    f = lyr.GetNextFeature()
    if f.GetFID() != 0:
        f.DumpReadable()
        pytest.fail()

    # Layer without geometry or primary key
    gdal.FileFromMemBuffer(
        '/vsimem/carto&POSTFIELDS=q=SELECT * FROM "table1" LIMIT 0',
        """{"rows":[],"fields":{"strfield":{"type":"string"}, "realfield":{"type":"number"}, "boolfield":{"type":"boolean"}, "datefield":{"type":"date"}}}"""
    )
    ds = ogr.Open('CARTO:foo')
    lyr = ds.GetLayer(0)
    lyr_defn = lyr.GetLayerDefn()
    assert lyr_defn.GetFieldCount() == 4
    assert (lyr_defn.GetFieldDefn(0).GetName() == 'strfield' and \
       lyr_defn.GetFieldDefn(0).GetType() == ogr.OFTString)
    assert (lyr_defn.GetFieldDefn(1).GetName() == 'realfield' and \
       lyr_defn.GetFieldDefn(1).GetType() == ogr.OFTReal)
    assert (lyr_defn.GetFieldDefn(2).GetName() == 'boolfield' and \
       lyr_defn.GetFieldDefn(2).GetType() == ogr.OFTInteger and \
       lyr_defn.GetFieldDefn(2).GetSubType() == ogr.OFSTBoolean)
    assert (lyr_defn.GetFieldDefn(3).GetName() == 'datefield' and \
       lyr_defn.GetFieldDefn(3).GetType() == ogr.OFTDateTime)

    gdal.FileFromMemBuffer(
        '/vsimem/carto&POSTFIELDS=q=SELECT "strfield", "realfield", "boolfield", "datefield" FROM "table1" LIMIT 500 OFFSET 0',
        """{"rows":[{ "strfield": "foo", "realfield": 1.23, "boolfield": true, "datefield": "2015-04-24T12:34:56.123Z" }],"fields":{"strfield":{"type":"string"}, "realfield":{"type":"number"}, "boolfield":{"type":"boolean"}, "datefield":{"type":"date"}}}"""
    )
    f = lyr.GetNextFeature()
    if f['strfield'] != 'foo' or f['realfield'] != 1.23 or f['boolfield'] != 1 or \
       f['datefield'] != '2015/04/24 12:34:56.123+00':
        f.DumpReadable()
        pytest.fail()

    gdal.SetConfigOption('CARTO_API_KEY', 'foo')
    gdal.FileFromMemBuffer(
        '/vsimem/carto&POSTFIELDS=q=SELECT current_schema() LIMIT 500 OFFSET 0&api_key=foo',
        """{"rows":[{"current_schema":"public"}],"fields":{"current_schema":{"type":"unknown(19)"}}}"""
    )
    gdal.FileFromMemBuffer(
        '/vsimem/carto&POSTFIELDS=q=SELECT CDB_UserTables() LIMIT 500 OFFSET 0&api_key=foo',
        """{"rows":[{"cdb_usertables":"table1"}],"fields":{"cdb_usertables":{"type":"string"}}}"""
    )
    ds = ogr.Open('CARTO:foo')
    gdal.PushErrorHandler()
    lyr_defn = ds.GetLayer(0).GetLayerDefn()
    gdal.PopErrorHandler()
    assert lyr_defn.GetFieldCount() == 0

    get_full_details_fields_url = """/vsimem/carto&POSTFIELDS=q=SELECT a.attname, t.typname, a.attlen, format_type(a.atttypid,a.atttypmod), a.attnum, a.attnotnull, i.indisprimary, pg_get_expr(def.adbin, c.oid) AS defaultexpr, postgis_typmod_dims(a.atttypmod) dim, postgis_typmod_srid(a.atttypmod) srid, postgis_typmod_type(a.atttypmod)::text geomtyp, srtext FROM pg_class c JOIN pg_attribute a ON a.attnum > 0 AND a.attrelid = c.oid AND c.relname = 'table1' JOIN pg_type t ON a.atttypid = t.oid JOIN pg_namespace n ON c.relnamespace=n.oid AND n.nspname= 'public' LEFT JOIN pg_index i ON c.oid = i.indrelid AND i.indisprimary = 't' AND a.attnum = ANY(i.indkey) LEFT JOIN pg_attrdef def ON def.adrelid = c.oid AND def.adnum = a.attnum LEFT JOIN spatial_ref_sys srs ON srs.srid = postgis_typmod_srid(a.atttypmod) ORDER BY a.attnum LIMIT 500 OFFSET 0&api_key=foo"""
    gdal.FileFromMemBuffer(get_full_details_fields_url, '')
    ds = ogr.Open('CARTO:foo')
    gdal.PushErrorHandler()
    lyr_defn = ds.GetLayer(0).GetLayerDefn()
    gdal.PopErrorHandler()
    assert lyr_defn.GetFieldCount() == 0

    gdal.FileFromMemBuffer(
        get_full_details_fields_url,
        """{"rows":[{"attname":"foo"}], "fields":{"attname":{"type":"string"}}}"""
    )
    ds = ogr.Open('CARTO:foo')
    lyr = ds.GetLayer(0)
    gdal.PushErrorHandler()
    lyr_defn = lyr.GetLayerDefn()
    gdal.PopErrorHandler()
    assert lyr_defn.GetFieldCount() == 1

    gdal.PushErrorHandler()
    f = lyr.GetFeature(0)
    gdal.PopErrorHandler()
    assert f is None

    gdal.FileFromMemBuffer(
        get_full_details_fields_url,
        """{"rows":[{"attname":"strfield", "typname":"varchar", "attnotnull": true, "defaultexpr": "def_value"},
               {"attname":"intfield", "typname":"int4"},
               {"attname":"doublefield", "typname":"float"},
               {"attname":"boolfield", "typname":"bool"},
               {"attname":"datetimefield", "typname":"timestamp"},
               {"attname":"cartodb_id","typname":"int4","indisprimary":true},
               {"attname":"created_at","typname":"date"},
               {"attname":"updated_at","typname":"date"},
               {"attname":"my_geom","typname":"geometry","dim":3,"srid":4326,"geomtyp":"Point",
                "srtext":"GEOGCS[\\"WGS 84\\",DATUM[\\"WGS_1984\\",SPHEROID[\\"WGS 84\\",6378137,298.257223563,AUTHORITY[\\"EPSG\\",\\"7030\\"]],AUTHORITY[\\"EPSG\\",\\"6326\\"]],PRIMEM[\\"Greenwich\\",0,AUTHORITY[\\"EPSG\\",\\"8901\\"]],UNIT[\\"degree\\",0.0174532925199433,AUTHORITY[\\"EPSG\\",\\"9122\\"]],AUTHORITY[\\"EPSG\\",\\"4326\\"]]"},
               {"attname":"the_geom_webmercator","typname":"geometry"}],
        "fields":{"attname":{"type":"string"},
                  "typname":{"type":"string"},
                  "attlen":{"type":"number"},
                  "format_type":{"type":"string"},
                  "attnum":{"type":"number"},
                  "attnotnull":{"type":"boolean"},
                  "indisprimary":{"type":"boolean"},
                  "defaultexpr":{"type":"string"},
                  "dim":{"type":"number"},
                  "srid":{"type":"number"},
                  "geomtyp":{"type":"string"},
                  "srtext":{"type":"string"}}}""")

    ds = ogr.Open('CARTO:foo')
    lyr = ds.GetLayer(0)
    lyr_defn = lyr.GetLayerDefn()
    assert lyr_defn.GetFieldCount() == 5
    assert (lyr_defn.GetFieldDefn(0).GetName() == 'strfield' and \
       lyr_defn.GetFieldDefn(0).GetType() == ogr.OFTString and not \
       lyr_defn.GetFieldDefn(0).IsNullable() and \
       lyr_defn.GetFieldDefn(0).GetDefault() == 'def_value')
    assert lyr_defn.GetGeomFieldCount() == 1
    assert lyr_defn.GetGeomFieldDefn(0).GetName() == 'my_geom'
    assert lyr_defn.GetGeomFieldDefn(0).GetType() == ogr.wkbPoint25D
    assert lyr_defn.GetGeomFieldDefn(0).GetSpatialRef().ExportToWkt().find(
        '4326') >= 0

    gdal.PushErrorHandler()
    fc = lyr.GetFeatureCount()
    gdal.PopErrorHandler()
    assert fc == 0

    gdal.FileFromMemBuffer(
        """/vsimem/carto&POSTFIELDS=q=SELECT COUNT(*) FROM "table1"&api_key=foo""",
        """{}""")
    gdal.PushErrorHandler()
    fc = lyr.GetFeatureCount()
    gdal.PopErrorHandler()
    assert fc == 0

    gdal.FileFromMemBuffer(
        """/vsimem/carto&POSTFIELDS=q=SELECT COUNT(*) FROM "table1"&api_key=foo""",
        """{"rows":[{"foo":1}],
            "fields":{"foo":{"type":"number"}}}""")
    gdal.PushErrorHandler()
    fc = lyr.GetFeatureCount()
    gdal.PopErrorHandler()
    assert fc == 0

    gdal.FileFromMemBuffer(
        """/vsimem/carto&POSTFIELDS=q=SELECT COUNT(*) FROM "table1"&api_key=foo""",
        """{"rows":[{"count":9876543210}],
            "fields":{"count":{"type":"number"}}}""")
    assert lyr.GetFeatureCount() == 9876543210

    gdal.PushErrorHandler()
    extent = lyr.GetExtent()
    gdal.PopErrorHandler()
    assert extent == (0, 0, 0, 0)

    gdal.FileFromMemBuffer(
        """/vsimem/carto&POSTFIELDS=q=SELECT ST_Extent("my_geom") FROM "table1"&api_key=foo""",
        """{"rows":[{"foo":1}],
            "fields":{"foo":{"type":"number"}}}""")

    gdal.PushErrorHandler()
    extent = lyr.GetExtent()
    gdal.PopErrorHandler()
    assert extent == (0, 0, 0, 0)

    gdal.FileFromMemBuffer(
        """/vsimem/carto&POSTFIELDS=q=SELECT ST_Extent("my_geom") FROM "table1"&api_key=foo""",
        """{"rows":[{"st_extent":""}],
            "fields":{"st_extent":{"type":"string"}}}""")
    gdal.ErrorReset()
    gdal.PushErrorHandler()
    lyr.GetExtent()
    gdal.PopErrorHandler()
    assert gdal.GetLastErrorMsg() != ''

    gdal.FileFromMemBuffer(
        """/vsimem/carto&POSTFIELDS=q=SELECT ST_Extent("my_geom") FROM "table1"&api_key=foo""",
        """{"rows":[{"st_extent":"("}],
            "fields":{"st_extent":{"type":"string"}}}""")
    gdal.ErrorReset()
    gdal.PushErrorHandler()
    lyr.GetExtent()
    gdal.PopErrorHandler()
    assert gdal.GetLastErrorMsg() != ''

    gdal.FileFromMemBuffer(
        """/vsimem/carto&POSTFIELDS=q=SELECT ST_Extent("my_geom") FROM "table1"&api_key=foo""",
        """{"rows":[{"st_extent":"BOX()"}],
            "fields":{"st_extent":{"type":"string"}}}""")
    gdal.ErrorReset()
    gdal.PushErrorHandler()
    lyr.GetExtent()
    gdal.PopErrorHandler()
    assert gdal.GetLastErrorMsg() != ''

    gdal.FileFromMemBuffer(
        """/vsimem/carto&POSTFIELDS=q=SELECT ST_Extent("my_geom") FROM "table1"&api_key=foo""",
        """{"rows":[{"st_extent":"BOX(0,1,2,3)"}],
            "fields":{"st_extent":{"type":"string"}}}""")
    assert lyr.GetExtent() == (0.0, 2.0, 1.0, 3.0)

    gdal.PushErrorHandler()
    f = lyr.GetFeature(0)
    gdal.PopErrorHandler()
    assert f is None

    gdal.FileFromMemBuffer(
        """/vsimem/carto&POSTFIELDS=q=SELECT "cartodb_id", "my_geom", "strfield", "intfield", "doublefield", "boolfield", "datetimefield" FROM "table1" WHERE "cartodb_id" = 0&api_key=foo""",
        """""")

    gdal.PushErrorHandler()
    f = lyr.GetFeature(0)
    gdal.PopErrorHandler()
    assert f is None

    gdal.FileFromMemBuffer(
        """/vsimem/carto&POSTFIELDS=q=SELECT "cartodb_id", "my_geom", "strfield", "intfield", "doublefield", "boolfield", "datetimefield" FROM "table1" WHERE "cartodb_id" = 0&api_key=foo""",
        """{"rows":[{"st_extent":"BOX(0,1,2,3)"}],
            "fields":{"st_extent":{"type":"string"}}}""")

    f = lyr.GetFeature(0)
    assert f.GetFID() == -1 and f.IsFieldNull(0)

    gdal.FileFromMemBuffer(
        """/vsimem/carto&POSTFIELDS=q=SELECT "cartodb_id", "my_geom", "strfield", "intfield", "doublefield", "boolfield", "datetimefield" FROM "table1" WHERE "cartodb_id" = 0&api_key=foo""",
        """{"rows":[{"cartodb_id":0}],
            "fields":{"cartodb_id":{"type":"numeric"}}}""")

    f = lyr.GetFeature(0)
    assert f.GetFID() == 0

    lyr.ResetReading()
    gdal.PushErrorHandler()
    f = lyr.GetNextFeature()
    gdal.PopErrorHandler()
    assert f is None

    gdal.FileFromMemBuffer(
        """/vsimem/carto&POSTFIELDS=q=SELECT "cartodb_id", "my_geom", "strfield", "intfield", "doublefield", "boolfield", "datetimefield" FROM "table1" WHERE "cartodb_id" >= 0 ORDER BY "cartodb_id" ASC LIMIT 500&api_key=foo""",
        """{"rows":[{"cartodb_id":0}],
            "fields":{"cartodb_id":{"type":"numeric"}}}""")
    lyr.ResetReading()
    f = lyr.GetNextFeature()
    assert f.GetFID() == 0
    gdal.PushErrorHandler()
    f = lyr.GetNextFeature()
    gdal.PopErrorHandler()
    assert f is None

    gdal.SetConfigOption('CARTO_PAGE_SIZE', '2')
    gdal.FileFromMemBuffer(
        """/vsimem/carto&POSTFIELDS=q=SELECT "cartodb_id", "my_geom", "strfield", "intfield", "doublefield", "boolfield", "datetimefield" FROM "table1" WHERE "cartodb_id" >= 0 ORDER BY "cartodb_id" ASC LIMIT 2&api_key=foo""",
        """{"rows":[{"cartodb_id":0},{"cartodb_id":10}],
            "fields":{"cartodb_id":{"type":"numeric"}}}""")
    lyr.ResetReading()
    f = lyr.GetNextFeature()
    assert f.GetFID() == 0
    f = lyr.GetNextFeature()
    assert f.GetFID() == 10

    gdal.FileFromMemBuffer(
        """/vsimem/carto&POSTFIELDS=q=SELECT "cartodb_id", "my_geom", "strfield", "intfield", "doublefield", "boolfield", "datetimefield" FROM "table1" WHERE "cartodb_id" >= 11 ORDER BY "cartodb_id" ASC LIMIT 2&api_key=foo""",
        """{"rows":[{"cartodb_id":12}],
            "fields":{"cartodb_id":{"type":"numeric"}}}""")
    f = lyr.GetNextFeature()
    assert f.GetFID() == 12
    gdal.ErrorReset()
    f = lyr.GetNextFeature()
    assert f is None and gdal.GetLastErrorMsg() == ''

    lyr.SetAttributeFilter('strfield is NULL')

    gdal.PushErrorHandler()
    fc = lyr.GetFeatureCount()
    gdal.PopErrorHandler()
    assert fc == 0

    gdal.FileFromMemBuffer(
        """/vsimem/carto&POSTFIELDS=q=SELECT "cartodb_id", "my_geom", "strfield", "intfield", "doublefield", "boolfield", "datetimefield" FROM "table1" WHERE (strfield is NULL) AND "cartodb_id" >= 0 ORDER BY "cartodb_id" ASC LIMIT 2&api_key=foo""",
        """{"rows":[{"cartodb_id":0}],
            "fields":{"cartodb_id":{"type":"numeric"}}}""")
    lyr.ResetReading()
    f = lyr.GetNextFeature()
    assert f is not None
    gdal.FileFromMemBuffer(
        """/vsimem/carto&POSTFIELDS=q=SELECT "cartodb_id", "my_geom", "strfield", "intfield", "doublefield", "boolfield", "datetimefield" FROM "table1" WHERE (strfield is NULL) AND "cartodb_id" >= 1 ORDER BY "cartodb_id" ASC LIMIT 2&api_key=foo""",
        """{"rows":[],
            "fields":{"cartodb_id":{"type":"numeric"}}}""")
    gdal.ErrorReset()
    f = lyr.GetNextFeature()
    assert f is None and gdal.GetLastErrorMsg() == ''

    gdal.FileFromMemBuffer(
        """/vsimem/carto&POSTFIELDS=q=SELECT COUNT(*) FROM "table1" WHERE (strfield is NULL)&api_key=foo""",
        """{"rows":[{"count":9876543210}],
            "fields":{"count":{"type":"number"}}}""")
    assert lyr.GetFeatureCount() == 9876543210

    lyr.SetSpatialFilterRect(-180, -90, 180, 90)

    gdal.PushErrorHandler()
    fc = lyr.GetFeatureCount()
    gdal.PopErrorHandler()
    assert fc == 0

    gdal.FileFromMemBuffer(
        """/vsimem/carto&POSTFIELDS=q=SELECT "cartodb_id", "my_geom", "strfield", "intfield", "doublefield", "boolfield", "datetimefield" FROM "table1" WHERE ("my_geom" %26%26 'BOX3D(-180 -90, 180 90)'::box3d) AND (strfield is NULL) AND "cartodb_id" >= 0 ORDER BY "cartodb_id" ASC LIMIT 2&api_key=foo""",
        """{"rows":[{"cartodb_id":20, "my_geom": "010100000000000000000000400000000000804840" }],
            "fields":{"cartodb_id":{"type":"numeric"}, "my_geom":{"type":"string"}}}"""
    )

    lyr.ResetReading()
    f = lyr.GetNextFeature()
    assert f is not None and f.GetGeometryRef().ExportToWkt() == 'POINT (2 49)'

    gdal.PushErrorHandler()
    fc = lyr.GetFeatureCount()
    gdal.PopErrorHandler()
    assert fc == 1

    gdal.FileFromMemBuffer(
        """/vsimem/carto&POSTFIELDS=q=SELECT COUNT(*) FROM "table1" WHERE ("my_geom" %26%26 'BOX3D(-180 -90, 180 90)'::box3d) AND (strfield is NULL)&api_key=foo""",
        """{"rows":[{"count":9876543210}],
            "fields":{"count":{"type":"number"}}}""")
    assert lyr.GetFeatureCount() == 9876543210

    # Not permitted in read-only mode
    f = ogr.Feature(lyr.GetLayerDefn())
    gdal.PushErrorHandler()
    ds.CreateLayer('foo')
    ds.DeleteLayer(0)
    lyr.CreateFeature(f)
    lyr.SetFeature(f)
    lyr.DeleteFeature(0)
    lyr.CreateField(ogr.FieldDefn('foo'))
    lyr.DeleteField(0)
    gdal.PopErrorHandler()

    ds = None

    gdal.FileFromMemBuffer(
        """/vsimem/carto&POSTFIELDS=q=DROP FUNCTION IF EXISTS ogr_table_metadata(TEXT,TEXT); CREATE OR REPLACE FUNCTION ogr_table_metadata(schema_name TEXT, table_name TEXT) RETURNS TABLE (attname TEXT, typname TEXT, attlen INT, format_type TEXT, attnum INT, attnotnull BOOLEAN, indisprimary BOOLEAN, defaultexpr TEXT, dim INT, srid INT, geomtyp TEXT, srtext TEXT) AS $$ SELECT a.attname::text, t.typname::text, a.attlen::int, format_type(a.atttypid,a.atttypmod)::text, a.attnum::int, a.attnotnull::boolean, i.indisprimary::boolean, pg_get_expr(def.adbin, c.oid)::text AS defaultexpr, (CASE WHEN t.typname = 'geometry' THEN postgis_typmod_dims(a.atttypmod) ELSE NULL END)::int dim, (CASE WHEN t.typname = 'geometry' THEN postgis_typmod_srid(a.atttypmod) ELSE NULL END)::int srid, (CASE WHEN t.typname = 'geometry' THEN postgis_typmod_type(a.atttypmod) ELSE NULL END)::text geomtyp, srtext FROM pg_class c JOIN pg_attribute a ON a.attnum > 0 AND a.attrelid = c.oid AND c.relname = $2 AND c.relname IN (SELECT CDB_UserTables())JOIN pg_type t ON a.atttypid = t.oid JOIN pg_namespace n ON c.relnamespace=n.oid AND n.nspname = $1 LEFT JOIN pg_index i ON c.oid = i.indrelid AND i.indisprimary = 't' AND a.attnum = ANY(i.indkey) LEFT JOIN pg_attrdef def ON def.adrelid = c.oid AND def.adnum = a.attnum LEFT JOIN spatial_ref_sys srs ON srs.srid = postgis_typmod_srid(a.atttypmod) ORDER BY a.attnum $$ LANGUAGE SQL&api_key=foo""",
        """"""
        "")
    gdal.SetConfigOption('CARTO_PAGE_SIZE', None)
    ds = ogr.Open('CARTO:foo', update=1)
    lyr = ds.CreateLayer('MY_LAYER')

    gdal.ErrorReset()
    gdal.PushErrorHandler()
    lyr.GetNextFeature()
    gdal.PopErrorHandler()
    assert gdal.GetLastErrorMsg() != ''

    gdal.FileFromMemBuffer(
        """/vsimem/carto&POSTFIELDS=q=SELECT cdb_cartodbfytable('my_layer')&api_key=foo""",
        """{"rows":[],
            "fields":{}}""")
    ds = None
    gdal.Unlink(
        """/vsimem/carto&POSTFIELDS=q=SELECT cdb_cartodbfytable('my_layer')&api_key=foo"""
    )

    ds = gdal.OpenEx('CARTO:foo',
                     gdal.OF_VECTOR | gdal.OF_UPDATE,
                     open_options=['COPY_MODE=NO'])
    gdal.SetConfigOption('CARTO_MAX_CHUNK_SIZE', '0')
    sr = osr.SpatialReference()
    sr.ImportFromEPSG(4326)
    lyr = ds.CreateLayer('MY_LAYER', srs=sr)
    fld_defn = ogr.FieldDefn('STRFIELD', ogr.OFTString)
    fld_defn.SetNullable(0)
    fld_defn.SetDefault("'DEFAULT VAL'")
    fld_defn.SetWidth(20)
    lyr.CreateField(fld_defn)

    gdal.FileFromMemBuffer(
        """/vsimem/carto&POSTFIELDS=q=CREATE TABLE "my_layer" ( cartodb_id SERIAL,the_geom GEOMETRY(GEOMETRY, 4326),"strfield" VARCHAR NOT NULL DEFAULT 'DEFAULT VAL',PRIMARY KEY (cartodb_id) );DROP SEQUENCE IF EXISTS "my_layer_cartodb_id_seq" CASCADE;CREATE SEQUENCE "my_layer_cartodb_id_seq" START 1;ALTER SEQUENCE "my_layer_cartodb_id_seq" OWNED BY "my_layer".cartodb_id;ALTER TABLE "my_layer" ALTER COLUMN cartodb_id SET DEFAULT nextval('"my_layer_cartodb_id_seq"')&api_key=foo""",
        """{"rows":[],
            "fields":{}}""")

    f = ogr.Feature(lyr.GetLayerDefn())
    gdal.PushErrorHandler()
    ret = lyr.CreateFeature(f)
    gdal.PopErrorHandler()
    assert ret != 0
    f = None

    fld_defn = ogr.FieldDefn('INTFIELD', ogr.OFTInteger)
    # No server answer
    with gdaltest.error_handler():
        ret = lyr.CreateField(fld_defn)
    assert ret != 0

    gdal.FileFromMemBuffer(
        """/vsimem/carto&POSTFIELDS=q=ALTER TABLE "my_layer" ADD COLUMN "intfield" INTEGER&api_key=foo""",
        """{"rows":[],
            "fields":{}}""")
    assert lyr.CreateField(fld_defn) == 0

    fld_defn = ogr.FieldDefn('boolfield', ogr.OFTInteger)
    fld_defn.SetSubType(ogr.OFSTBoolean)

    gdal.FileFromMemBuffer(
        """/vsimem/carto&POSTFIELDS=q=ALTER TABLE "my_layer" ADD COLUMN "boolfield" BOOLEAN&api_key=foo""",
        """{"rows":[],
            "fields":{}}""")
    assert lyr.CreateField(fld_defn) == 0

    # Invalid field
    with gdaltest.error_handler():
        assert lyr.DeleteField(-1) != 0

    # No server answer
    with gdaltest.error_handler():
        assert lyr.DeleteField(0) != 0

    gdal.FileFromMemBuffer(
        """/vsimem/carto&POSTFIELDS=q=ALTER TABLE "my_layer" DROP COLUMN "boolfield"&api_key=foo""",
        """{"rows":[],
            "fields":{}}""")
    fld_pos = lyr.GetLayerDefn().GetFieldIndex(fld_defn.GetName())
    assert lyr.DeleteField(fld_pos) == 0

    gdal.FileFromMemBuffer(
        """/vsimem/carto&POSTFIELDS=q=ALTER TABLE "my_layer" ADD COLUMN "boolfield" BOOLEAN&api_key=foo""",
        """{"rows":[],
            "fields":{}}""")
    assert lyr.CreateField(fld_defn) == 0

    f = ogr.Feature(lyr.GetLayerDefn())
    gdal.PushErrorHandler()
    ret = lyr.CreateFeature(f)
    gdal.PopErrorHandler()
    assert ret != 0

    f = ogr.Feature(lyr.GetLayerDefn())
    f.SetField('strfield', 'foo')
    f.SetField('intfield', 1)
    f.SetField('boolfield', 1)
    f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(2 49)'))
    gdal.FileFromMemBuffer(
        """/vsimem/carto&POSTFIELDS=q=INSERT INTO "my_layer" ("strfield", "intfield", "boolfield", "the_geom") VALUES ('foo', 1, 't', '0101000020E610000000000000000000400000000000804840') RETURNING "cartodb_id"&api_key=foo""",
        """{"rows":[ {"cartodb_id": 1} ],
            "fields":{"cartodb_id":{"type":"integer"}}}""")
    ret = lyr.CreateFeature(f)
    assert ret == 0 and f.GetFID() == 1

    f.SetFID(-1)
    gdal.PushErrorHandler()
    ret = lyr.SetFeature(f)
    gdal.PopErrorHandler()
    assert ret != 0

    f.SetFID(3)
    gdal.PushErrorHandler()
    ret = lyr.SetFeature(f)
    gdal.PopErrorHandler()
    assert ret != 0

    gdal.FileFromMemBuffer(
        """/vsimem/carto&POSTFIELDS=q=UPDATE "my_layer" SET "strfield" = 'foo', "intfield" = 1, "boolfield" = 't', "the_geom" = '0101000020E610000000000000000000400000000000804840' WHERE "cartodb_id" = 3&api_key=foo""",
        """{"total_rows": 0}""")
    ret = lyr.SetFeature(f)
    assert ret == ogr.OGRERR_NON_EXISTING_FEATURE

    gdal.FileFromMemBuffer(
        """/vsimem/carto&POSTFIELDS=q=UPDATE "my_layer" SET "strfield" = 'foo', "intfield" = 1, "boolfield" = 't', "the_geom" = '0101000020E610000000000000000000400000000000804840' WHERE "cartodb_id" = 3&api_key=foo""",
        """{"total_rows": 1}""")
    ret = lyr.SetFeature(f)
    assert ret == 0

    f = ogr.Feature(lyr.GetLayerDefn())
    gdal.PushErrorHandler()
    ret = lyr.CreateFeature(f)
    gdal.PopErrorHandler()
    assert ret != 0

    gdal.FileFromMemBuffer(
        """/vsimem/carto&POSTFIELDS=q=INSERT INTO "my_layer" DEFAULT VALUES RETURNING "cartodb_id"&api_key=foo""",
        """{"rows":[ {"cartodb_id": 4} ],
            "fields":{"cartodb_id":{"type":"integer"}}}""")
    ret = lyr.CreateFeature(f)
    assert ret == 0 and f.GetFID() == 4

    gdal.PushErrorHandler()
    ret = lyr.DeleteFeature(0)
    gdal.PopErrorHandler()
    assert ret != 0

    gdal.FileFromMemBuffer(
        """/vsimem/carto&POSTFIELDS=q=DELETE FROM "my_layer" WHERE "cartodb_id" = 0&api_key=foo""",
        """{"total_rows": 0}""")
    ret = lyr.DeleteFeature(0)
    assert ret == ogr.OGRERR_NON_EXISTING_FEATURE

    gdal.FileFromMemBuffer(
        """/vsimem/carto&POSTFIELDS=q=DELETE FROM "my_layer" WHERE "cartodb_id" = 0&api_key=foo""",
        """{"total_rows": 1}""")
    ret = lyr.DeleteFeature(0)
    assert ret == 0

    gdal.FileFromMemBuffer(
        """/vsimem/carto&POSTFIELDS=q=SELECT cdb_cartodbfytable('my_layer')&api_key=foo""",
        """{"rows":[],
            "fields":{}}""")
    ds = None

    gdal.SetConfigOption('CARTO_MAX_CHUNK_SIZE', None)
    ds = gdal.OpenEx('CARTO:foo',
                     gdal.OF_VECTOR | gdal.OF_UPDATE,
                     open_options=['COPY_MODE=NO'])
    lyr = ds.GetLayer(0)

    gdal.FileFromMemBuffer(
        """/vsimem/carto&POSTFIELDS=q=SELECT pg_catalog.pg_get_serial_sequence('table1', 'cartodb_id') AS seq_name&api_key=foo""",
        """{"rows":[{"seq_name":"table1_cartodb_id_seq0"}],"fields":{"seq_name":{"type":"string"}}}"""
    )

    gdal.FileFromMemBuffer(
        """/vsimem/carto&POSTFIELDS=q=SELECT nextval('table1_cartodb_id_seq0') AS nextid&api_key=foo""",
        """{"rows":[{"nextid":11}],"fields":{"nextid":{"type":"number"}}}""")

    f = ogr.Feature(lyr.GetLayerDefn())
    f.SetField('strfield', 'foo')
    ret = lyr.CreateFeature(f)
    assert ret == 0 and f.GetFID() == 11

    f = ogr.Feature(lyr.GetLayerDefn())

    with gdaltest.tempfile(
            """/vsimem/carto&POSTFIELDS=q=BEGIN;INSERT INTO "table1" ("strfield", "cartodb_id") VALUES ('foo', 11);INSERT INTO "table1" DEFAULT VALUES;COMMIT;&api_key=foo""",
            """{"rows":[],
            "fields":{}}"""):
        ret = lyr.CreateFeature(f)
        ds = None
    if ret != 0 or f.GetFID() != 12:
        f.DumpReadable()
        pytest.fail()

    ds = gdal.OpenEx('CARTO:foo',
                     gdal.OF_VECTOR | gdal.OF_UPDATE,
                     open_options=['COPY_MODE=NO'])
    lyr = ds.GetLayer(0)
    f = ogr.Feature(lyr.GetLayerDefn())
    f.SetFieldNull('strfield')
    with gdaltest.tempfile(
            """/vsimem/carto&POSTFIELDS=q=BEGIN;INSERT INTO "table1" ("strfield", "cartodb_id") VALUES (NULL, 11);COMMIT;&api_key=foo""",
            """{"rows":[],
            "fields":{}}"""):
        ret = lyr.CreateFeature(f)
        ds = None

    if ret != 0 or f.GetFID() != 11:
        f.DumpReadable()
        pytest.fail()

    # Now remove default value to strfield
    gdal.FileFromMemBuffer(
        get_full_details_fields_url,
        """{"rows":[{"attname":"strfield", "typname":"varchar"},
               {"attname":"intfield", "typname":"int4"},
               {"attname":"doublefield", "typname":"float"},
               {"attname":"boolfield", "typname":"bool"},
               {"attname":"datetimefield", "typname":"timestamp"},
               {"attname":"cartodb_id","typname":"int4","indisprimary":true},
               {"attname":"created_at","typname":"date"},
               {"attname":"updated_at","typname":"date"},
               {"attname":"my_geom","typname":"geometry","dim":3,"srid":4326,"geomtyp":"Point",
                "srtext":"GEOGCS[\\"WGS 84\\",DATUM[\\"WGS_1984\\",SPHEROID[\\"WGS 84\\",6378137,298.257223563,AUTHORITY[\\"EPSG\\",\\"7030\\"]],AUTHORITY[\\"EPSG\\",\\"6326\\"]],PRIMEM[\\"Greenwich\\",0,AUTHORITY[\\"EPSG\\",\\"8901\\"]],UNIT[\\"degree\\",0.0174532925199433,AUTHORITY[\\"EPSG\\",\\"9122\\"]],AUTHORITY[\\"EPSG\\",\\"4326\\"]]"},
               {"attname":"the_geom_webmercator","typname":"geometry"}],
        "fields":{"attname":{"type":"string"},
                  "typname":{"type":"string"},
                  "attlen":{"type":"number"},
                  "format_type":{"type":"string"},
                  "attnum":{"type":"number"},
                  "attnotnull":{"type":"boolean"},
                  "indisprimary":{"type":"boolean"},
                  "defaultexpr":{"type":"string"},
                  "dim":{"type":"number"},
                  "srid":{"type":"number"},
                  "geomtyp":{"type":"string"},
                  "srtext":{"type":"string"}}}""")

    ds = gdal.OpenEx('CARTO:foo',
                     gdal.OF_VECTOR | gdal.OF_UPDATE,
                     open_options=['COPY_MODE=NO'])
    lyr = ds.GetLayer(0)

    f = ogr.Feature(lyr.GetLayerDefn())
    f.SetField('strfield', 'foo')
    with gdaltest.tempfile(
            """/vsimem/carto&POSTFIELDS=q=SELECT nextval('table1_cartodb_id_seq') AS nextid&api_key=foo""",
            """{"rows":[{"nextid":11}],"fields":{"nextid":{"type":"number"}}}"""
    ):
        ret = lyr.CreateFeature(f)
    assert ret == 0 and f.GetFID() == 11

    f = ogr.Feature(lyr.GetLayerDefn())
    f.SetField('strfield', 'bar')
    ret = lyr.CreateFeature(f)
    assert ret == 0 and f.GetFID() == 12

    f = ogr.Feature(lyr.GetLayerDefn())
    f.SetField('strfield', 'baz')
    ret = lyr.CreateFeature(f)
    assert ret == 0 and f.GetFID() == 13

    gdal.ErrorReset()
    with gdaltest.tempfile(
            """/vsimem/carto&POSTFIELDS=q=BEGIN;INSERT INTO "table1" ("strfield", "cartodb_id") VALUES ('foo', 11);INSERT INTO "table1" ("strfield", "intfield", "doublefield", "boolfield", "datetimefield", "my_geom") VALUES ('bar', NULL, NULL, NULL, NULL, NULL), ('baz', NULL, NULL, NULL, NULL, NULL);COMMIT;&api_key=foo""",
            """{"rows":[], "fields":{}}"""):
        ds = None
    assert gdal.GetLastErrorMsg() == ''

    ds = gdal.OpenEx('CARTO:foo',
                     gdal.OF_VECTOR | gdal.OF_UPDATE,
                     open_options=['COPY_MODE=NO'])

    gdal.PushErrorHandler()
    lyr = ds.CreateLayer('table1')
    gdal.PopErrorHandler()
    assert lyr is None

    with gdaltest.tempfile(
            """/vsimem/carto&POSTFIELDS=q=BEGIN; DROP TABLE IF EXISTS "table1";CREATE TABLE "table1" ( cartodb_id SERIAL,the_geom Geometry(MULTIPOLYGON,0),PRIMARY KEY (cartodb_id) );DROP SEQUENCE IF EXISTS "table1_cartodb_id_seq" CASCADE;CREATE SEQUENCE "table1_cartodb_id_seq" START 1;ALTER SEQUENCE "table1_cartodb_id_seq" OWNED BY "table1".cartodb_id;ALTER TABLE "table1" ALTER COLUMN cartodb_id SET DEFAULT nextval('"table1_cartodb_id_seq"'); COMMIT;&api_key=foo""",
            """{"rows":[], "fields":{}}"""):
        lyr = ds.CreateLayer('table1',
                             geom_type=ogr.wkbPolygon,
                             options=['OVERWRITE=YES', 'CARTODBFY=NO'])
        f = ogr.Feature(lyr.GetLayerDefn())
        f.SetGeometry(ogr.CreateGeometryFromWkt('POLYGON((0 0,0 1,1 0,0 0))'))
        assert lyr.CreateFeature(f) == 0

    gdal.ErrorReset()
    with gdaltest.tempfile(
            """/vsimem/carto&POSTFIELDS=q=BEGIN;INSERT INTO "table1" ("the_geom") VALUES ('0106000020E61000000100000001030000000100000004000000000000000000000000000000000000000000000000000000000000000000F03F000000000000F03F000000000000000000000000000000000000000000000000');COMMIT;&api_key=foo""",
            """{"rows":[],
            "fields":{}}"""):
        ds = None
    assert gdal.GetLastErrorMsg() == ''

    ds = gdal.OpenEx('CARTO:foo',
                     gdal.OF_VECTOR | gdal.OF_UPDATE,
                     open_options=['COPY_MODE=NO'])

    with gdaltest.tempfile(
            """/vsimem/carto&POSTFIELDS=q=BEGIN; DROP TABLE IF EXISTS "table1";CREATE TABLE "table1" ( cartodb_id SERIAL,the_geom Geometry(MULTIPOLYGON,0),PRIMARY KEY (cartodb_id) );DROP SEQUENCE IF EXISTS "table1_cartodb_id_seq" CASCADE;CREATE SEQUENCE "table1_cartodb_id_seq" START 1;ALTER SEQUENCE "table1_cartodb_id_seq" OWNED BY "table1".cartodb_id;ALTER TABLE "table1" ALTER COLUMN cartodb_id SET DEFAULT nextval('"table1_cartodb_id_seq"'); COMMIT;&api_key=foo""",
            """{"rows":[], "fields":{}}"""):
        lyr = ds.CreateLayer('table1',
                             geom_type=ogr.wkbPolygon,
                             options=['OVERWRITE=YES', 'CARTODBFY=NO'])
        f = ogr.Feature(lyr.GetLayerDefn())
        f.SetFID(100)

        with gdaltest.tempfile(
                """/vsimem/carto&POSTFIELDS=q=BEGIN;INSERT INTO "table1" ("cartodb_id") VALUES (100);COMMIT;&api_key=foo""",
                """{"rows":[], "fields":{}}"""):
            assert lyr.CreateFeature(f) == 0
            assert f.GetFID() == 100
            ds = None

    ds = ogr.Open('CARTO:foo', update=1)

    gdal.PushErrorHandler()
    ret = ds.DeleteLayer(0)
    gdal.PopErrorHandler()
    assert ret != 0

    gdal.ErrorReset()
    ds = gdal.OpenEx('CARTO:foo',
                     gdal.OF_VECTOR | gdal.OF_UPDATE,
                     open_options=['COPY_MODE=YES'])
    assert ds is not None
    lyr = ds.GetLayerByName('table1')
    assert lyr is not None

    with gdaltest.tempfile(
            """/vsimem/carto/copyfrom?q=COPY%20"table1"%20("strfield","my_geom","cartodb_id")%20FROM%20STDIN%20WITH%20(FORMAT%20text,%20ENCODING%20UTF8)&api_key=foo&POSTFIELDS=copytest\t0101000020E610000000000000000059400000000000005940\t11\n\\.\n""",
            """{}"""):

        with gdaltest.tempfile(
                """/vsimem/carto/copyfrom?q=COPY%20"table1"%20("intfield","my_geom")%20FROM%20STDIN%20WITH%20(FORMAT%20text,%20ENCODING%20UTF8)&api_key=foo&POSTFIELDS=12\t0101000020E610000000000000000059400000000000005940\n\\.\n""",
                """{}"""):

            f = ogr.Feature(lyr.GetLayerDefn())
            f.SetField('strfield', 'copytest')
            f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(100 100)'))
            assert lyr.CreateFeature(f) == 0

            f = ogr.Feature(lyr.GetLayerDefn())
            f.SetField('intfield', 12)
            f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(100 100)'))
            assert lyr.CreateFeature(f) == 0

            ds = None  # force flush

    ds = ogr.Open('CARTO:foo', update=1)

    gdal.ErrorReset()
    with gdaltest.tempfile(
            """/vsimem/carto&POSTFIELDS=q=DROP TABLE "table1"&api_key=foo""",
            """{"rows":[], "fields":{}}"""):
        ds.ExecuteSQL('DELLAYER:table1')
    assert gdal.GetLastErrorMsg() == '' and ds.GetLayerByName('table1') is None

    with gdaltest.tempfile(
            '/vsimem/carto&POSTFIELDS=q=SELECT current_schema() LIMIT 500 OFFSET 0&api_key=foo',
            """{"rows":[{"current_schema":"my_schema"}],"fields":{"current_schema":{"type":"unknown(19)"}}}"""
    ):
        with gdaltest.tempfile(
                '/vsimem/carto&POSTFIELDS=q=SELECT CDB_UserTables() LIMIT 500 OFFSET 0&api_key=foo',
                """{"rows":[],"fields":{"cdb_usertables":{"type":"string"}}}"""
        ):
            with gdaltest.tempfile(
                    """/vsimem/carto&POSTFIELDS=q=SELECT c.relname FROM pg_class c, pg_namespace n WHERE c.relkind in ('r', 'v') AND c.relname !~ '^pg_' AND c.relnamespace=n.oid AND n.nspname = 'my_schema' LIMIT 500 OFFSET 0&api_key=foo""",
                    """{"rows":[{"relname": "a_layer"}],"fields":{"relname":{"type":"string"}}}"""
            ):
                ds = ogr.Open('CARTO:foo')
    assert ds.GetLayerByName('a_layer') is not None
Ejemplo n.º 12
0
def test_eeda_2():

    gdal.FileFromMemBuffer(
        '/vsimem/ee/projects/earthengine-public/assets/collection:listImages?pageSize=1',
        json.dumps({
            'images': [{
                'properties': {
                    'string_field': 'bar',
                    'int_field': 1,
                    'int64_field': 123456789012,
                    'double_field': 1.23
                }
            }]
        }))

    # To please the unregistering of the persistent connection
    gdal.FileFromMemBuffer('/vsimem/ee/', '')

    gdal.SetConfigOption('EEDA_BEARER', 'mybearer')
    gdal.SetConfigOption('EEDA_URL', '/vsimem/ee/')
    ds = ogr.Open('EEDA:collection')
    gdal.SetConfigOption('EEDA_URL', None)

    lyr = ds.GetLayer(0)

    assert lyr.TestCapability(ogr.OLCStringsAsUTF8) == 1

    assert lyr.TestCapability('foo') == 0

    assert lyr.GetLayerDefn().GetFieldCount() == 8 + 7 + 4

    assert lyr.GetExtent() == (-180.0, 180.0, -90.0, 90.0)

    assert lyr.GetFeatureCount() == -1

    gdal.FileFromMemBuffer(
        '/vsimem/ee/projects/earthengine-public/assets/collection:listImages',
        json.dumps({
            'images': [{
                'name':
                'projects/earthengine-public/assets/collection/first_feature',
                'id':
                'collection/first_feature',
                'updateTime':
                '2017-01-04T12:34:56.789Z',
                'startTime':
                '2017-01-02T12:34:56.789Z',
                'endTime':
                '2017-01-03T12:34:56.789Z',
                'sizeBytes':
                1,
                'geometry': {
                    'type':
                    'Polygon',
                    'coordinates': [[[2, 49], [2.1, 49], [2.1, 49.1],
                                     [2, 49.1], [2, 49]]]
                },
                'properties': {
                    'string_field': 'bar',
                    'int_field': 1,
                    'int64_field': 123456789012,
                    'double_field': 1.23,
                    'another_prop': 3
                },
                'bands': [{
                    "id": "B1",
                    "dataType": {
                        "precision": "INT",
                        "range": {
                            "max": 255
                        }
                    },
                    "grid": {
                        "crsCode": "EPSG:32610",
                        "affineTransform": {
                            "translateX": 499980,
                            "translateY": 4200000,
                            "scaleX": 60,
                            "scaleY": -60
                        },
                        "dimensions": {
                            "width": 1830,
                            "height": 1831
                        }
                    }
                }]
            }, {
                'name':
                'projects/earthengine-public/assets/collection/second_feature'
            }],
            'nextPageToken':
            'myToken'
        }))

    f = lyr.GetNextFeature()
    if f.GetField('name') != 'projects/earthengine-public/assets/collection/first_feature' or \
       f.GetField('id') != 'collection/first_feature' or \
       f.GetField('gdal_dataset') != 'EEDAI:projects/earthengine-public/assets/collection/first_feature' or \
       f.GetField('updateTime') != '2017/01/04 12:34:56.789+00' or \
       f.GetField('startTime') != '2017/01/02 12:34:56.789+00' or \
       f.GetField('endTime') != '2017/01/03 12:34:56.789+00' or \
       f.GetField('sizeBytes') != 1 or \
       f.GetField('band_count') != 1 or \
       f.GetField('band_max_width') != 1830 or \
       f.GetField('band_max_height') != 1831 or \
       f.GetField('band_min_pixel_size') != 60 or \
       f.GetField('band_upper_left_x') != 499980 or \
       f.GetField('band_upper_left_y') != 4200000 or \
       f.GetField('band_crs') != 'EPSG:32610' or \
       f.GetField('string_field') != 'bar' or \
       f.GetField('int_field') != 1 or \
       f.GetField('int64_field') != 123456789012 or \
       f.GetField('double_field') != 1.23 or \
       f.GetField('other_properties') != '{ "another_prop": 3 }' or \
       f.GetGeometryRef().ExportToWkt() != 'MULTIPOLYGON (((2 49,2.1 49.0,2.1 49.1,2.0 49.1,2 49)))':
        f.DumpReadable()
        pytest.fail()

    f = lyr.GetNextFeature()
    if f.GetField(
            'name'
    ) != 'projects/earthengine-public/assets/collection/second_feature':
        f.DumpReadable()
        pytest.fail()

    gdal.FileFromMemBuffer(
        '/vsimem/ee/projects/earthengine-public/assets/collection:listImages?pageToken=myToken',
        json.dumps({
            'images': [{
                'name':
                'projects/earthengine-public/assets/collection/third_feature'
            }]
        }))

    f = lyr.GetNextFeature()
    if f.GetField(
            'name'
    ) != 'projects/earthengine-public/assets/collection/third_feature':
        f.DumpReadable()
        pytest.fail()

    f = lyr.GetNextFeature()
    assert f is None

    lyr.ResetReading()

    f = lyr.GetNextFeature()
    if f.GetField(
            'name'
    ) != 'projects/earthengine-public/assets/collection/first_feature':
        f.DumpReadable()
        pytest.fail()

    lyr.SetAttributeFilter('EEDA:raw_filter')

    gdal.FileFromMemBuffer(
        '/vsimem/ee/projects/earthengine-public/assets/collection:listImages?filter=raw%5Ffilter',
        json.dumps({
            'images': [{
                'name':
                'projects/earthengine-public/assets/collection/raw_filter'
            }]
        }))

    f = lyr.GetNextFeature()
    assert f.GetField(
        'name') == 'projects/earthengine-public/assets/collection/raw_filter'

    lyr.SetAttributeFilter(None)
    lyr.SetAttributeFilter("startTime >= '1980-01-01T00:00:00Z' AND " +
                           "string_field = 'bar' AND " + "int_field > 0 AND " +
                           "int_field < 2 AND " + "int64_field >= 0 AND " +
                           "int64_field <= 9999999999999 AND " +
                           "double_field != 3.5 AND " +
                           "string_field IN ('bar', 'baz') AND " +
                           "NOT( int_field IN (0) OR double_field IN (3.5) )")

    tmpfile = '/vsimem/ee/projects/earthengine-public/assets/collection:listImages?region=%7B%20%22type%22%3A%20%22Polygon%22%2C%20%22coordinates%22%3A%20%5B%20%5B%20%5B%20%2D180%2E0%2C%20%2D90%2E0%20%5D%2C%20%5B%20%2D180%2E0%2C%2090%2E0%20%5D%2C%20%5B%20180%2E0%2C%2090%2E0%20%5D%2C%20%5B%20180%2E0%2C%20%2D90%2E0%20%5D%2C%20%5B%20%2D180%2E0%2C%20%2D90%2E0%20%5D%20%5D%20%5D%20%7D&filter=%28%28%28string%5Ffield%20%3D%20%22bar%22%20AND%20%28int%5Ffield%20%3E%200%20AND%20int%5Ffield%20%3C%202%29%29%20AND%20%28%28int64%5Ffield%20%3E%3D%200%20AND%20int64%5Ffield%20%3C%3D%209999999999999%29%20AND%20%28double%5Ffield%20%21%3D%203%2E5%20AND%20string%5Ffield%20%3D%20%22bar%22%20OR%20string%5Ffield%20%3D%20%22baz%22%29%29%29%20AND%20%28NOT%20%28int%5Ffield%20%3D%200%20OR%20double%5Ffield%20%3D%203%2E5%29%29%29&startTime=1980%2D01%2D01T00%3A00%3A00Z'
    with gdaltest.tempfile(
            tmpfile,
            json.dumps({
                'images': [{
                    'name':
                    'projects/earthengine-public/assets/collection/filtered_feature',
                    'updateTime': '2017-01-03T12:34:56.789Z',
                    'startTime': '2017-01-02T12:34:56.789Z',
                    'sizeBytes': 1,
                    'geometry': {
                        'type':
                        'Polygon',
                        'coordinates': [[[2, 49], [2.1, 49], [2.1, 49.1],
                                         [2, 49.1], [2, 49]]]
                    },
                    'properties': {
                        'string_field': 'bar',
                        'int_field': 1,
                        'int64_field': 123456789012,
                        'double_field': 1.23,
                        'another_prop': 3
                    }
                }, {
                    'name':
                    'projects/earthengine-public/assets/collection/second_feature'
                }]
            })):

        lyr.SetSpatialFilterRect(-180, -90, 180, 90)

        f = lyr.GetNextFeature()

    assert f.GetField(
        'name'
    ) == 'projects/earthengine-public/assets/collection/filtered_feature'

    lyr.SetSpatialFilter(None)

    # Test time equality with second granularity
    lyr.SetAttributeFilter(
        "startTime >= '1980-01-01T00:00:00Z' AND endTime <= '1980-01-02T23:59:59Z'"
    )

    tmpfile = '/vsimem/ee/projects/earthengine-public/assets/collection:listImages?startTime=1980%2D01%2D01T00%3A00%3A00Z&endTime=1980%2D01%2D02T23%3A59%3A59Z'
    with gdaltest.tempfile(
            tmpfile,
            json.dumps({
                'images': [{
                    'name':
                    'projects/earthengine-public/assets/collection/filtered_feature',
                    'startTime': '1980-01-01T00:00:00Z',
                    'endTime': '1980-01-02T23:59:59Z'
                }, {
                    'name':
                    'projects/earthengine-public/assets/collection/second_feature'
                }]
            })):

        f = lyr.GetNextFeature()

    assert f.GetField(
        'name'
    ) == 'projects/earthengine-public/assets/collection/filtered_feature'

    # Test time equality with day granularity
    lyr.SetAttributeFilter(
        "startTime = '1980-01-01' AND endTime = '1980-01-02'")

    tmpfile = '/vsimem/ee/projects/earthengine-public/assets/collection:listImages?startTime=1980%2D01%2D01T00%3A00%3A00Z&endTime=1980%2D01%2D02T23%3A59%3A59Z'
    with gdaltest.tempfile(
            tmpfile,
            json.dumps({
                'images': [{
                    'name':
                    'projects/earthengine-public/assets/collection/filtered_feature',
                    'startTime': '1980-01-01T12:00:00Z',
                    'endTime': '1980-01-02T23:59:59Z'
                }, {
                    'name':
                    'projects/earthengine-public/assets/collection/second_feature'
                }]
            })):

        f = lyr.GetNextFeature()

    assert f.GetField(
        'name'
    ) == 'projects/earthengine-public/assets/collection/filtered_feature'

    ds = None

    gdal.SetConfigOption('EEDA_BEARER', None)
Ejemplo n.º 13
0
def test_eeda_4():

    gdal.SetConfigOption('EEDA_BEARER', 'mybearer')
    gdal.SetConfigOption('EEDA_URL', '/vsimem/ee/')

    # User asset ID ("users/**").
    tmpfile = '/vsimem/ee/projects/earthengine-legacy/assets/users/foo:listImages?pageSize=1'
    with gdaltest.tempfile(
            tmpfile,
            json.dumps({
                'images': [{
                    'name':
                    'projects/earthengine-legacy/assets/users/foo/bar'
                }]
            })):
        assert ogr.Open('EEDA:users/foo').GetLayer(0)

    # Project asset ID ("projects/**").
    tmpfile = '/vsimem/ee/projects/earthengine-legacy/assets/projects/foo:listImages?pageSize=1'
    with gdaltest.tempfile(
            tmpfile,
            json.dumps({
                'images': [{
                    'name':
                    'projects/earthengine-legacy/assets/projects/foo/bar'
                }]
            })):
        ds = ogr.Open('EEDA:projects/foo')
        assert ds.GetLayer(0)
    ds = None

    # Multi-folder project asset ID ("projects/foo/bar/baz").
    tmpfile = '/vsimem/ee/projects/earthengine-legacy/assets/projects/foo/bar/baz:listImages?pageSize=1'
    with gdaltest.tempfile(
            tmpfile,
            json.dumps({
                'images': [{
                    'name':
                    'projects/earthengine-legacy/assets/projects/foo/bar/baz/qux'
                }]
            })):
        ds = ogr.Open('EEDA:projects/foo/bar/baz')
        assert ds.GetLayer(0)
    ds = None

    # Public-catalog asset ID (e.g. "LANDSAT").
    tmpfile = '/vsimem/ee/projects/earthengine-public/assets/foo:listImages?pageSize=1'
    with gdaltest.tempfile(
            tmpfile,
            json.dumps({
                'images': [{
                    'name': 'projects/earthengine-public/assets/foo/bar'
                }]
            })):
        ds = ogr.Open('EEDA:foo')
        assert ds.GetLayer(0)
    ds = None

    # Asset name ("projects/*/assets/**").
    tmpfile = '/vsimem/ee/projects/foo/assets/bar:listImages?pageSize=1'
    with gdaltest.tempfile(
            tmpfile,
            json.dumps({'images': [{
                'name': 'projects/foo/assets/bar/baz'
            }]})):
        ds = ogr.Open('EEDA:projects/foo/assets/bar')
        assert ds.GetLayer(0)
    ds = None

    gdal.SetConfigOption('EEDA_BEARER', None)
    gdal.SetConfigOption('EEDA_URL', None)
Ejemplo n.º 14
0
def test_ogr_carto_vsimem():
    if ogrtest.carto_drv is None:
        pytest.skip()

    ogrtest.carto_api_key_ori = gdal.GetConfigOption('CARTO_API_KEY')
    gdal.SetConfigOption('CARTO_API_URL', '/vsimem/carto')
    gdal.SetConfigOption('CPL_CURL_ENABLE_VSIMEM', 'YES')

    gdal.FileFromMemBuffer('/vsimem/carto&POSTFIELDS=q=SELECT postgis_version() LIMIT 500 OFFSET 0&api_key=foo',
                           """{"rows":[{"postgis_version":"2.1 USE_GEOS=1 USE_PROJ=1 USE_STATS=1"}],"time":0.001,"fields":{"postgis_version":{"type":"string"}},"total_rows":1}""")

    gdal.PushErrorHandler()
    ds = ogr.Open('CARTO:foo')
    gdal.PopErrorHandler()
    assert ds is None

    gdal.FileFromMemBuffer('/vsimem/carto&POSTFIELDS=q=SELECT current_schema() LIMIT 500 OFFSET 0',
                           """Content-Type: text/html\r

Error""")
    gdal.PushErrorHandler()
    ds = ogr.Open('CARTO:foo')
    gdal.PopErrorHandler()
    assert ds is None and gdal.GetLastErrorMsg().find('HTML error page') >= 0

    gdal.FileFromMemBuffer('/vsimem/carto&POSTFIELDS=q=SELECT current_schema() LIMIT 500 OFFSET 0',
                           """""")
    ds = ogr.Open('CARTO:foo')
    assert ds is None, gdal.GetLastErrorMsg()

    gdal.FileFromMemBuffer('/vsimem/carto&POSTFIELDS=q=SELECT current_schema() LIMIT 500 OFFSET 0',
                           """{""")
    gdal.PushErrorHandler()
    ds = ogr.Open('CARTO:foo')
    gdal.PopErrorHandler()
    assert ds is None and gdal.GetLastErrorMsg().find('JSON parsing error') >= 0

    gdal.FileFromMemBuffer('/vsimem/carto&POSTFIELDS=q=SELECT current_schema() LIMIT 500 OFFSET 0',
                           """ "not_expected_json" """)
    ds = ogr.Open('CARTO:foo')
    assert ds is None, gdal.GetLastErrorMsg()

    gdal.FileFromMemBuffer('/vsimem/carto&POSTFIELDS=q=SELECT current_schema() LIMIT 500 OFFSET 0',
                           """{ "error" : [ "bla"] }""")
    gdal.PushErrorHandler()
    ds = ogr.Open('CARTO:foo')
    gdal.PopErrorHandler()
    assert ds is None and gdal.GetLastErrorMsg().find('Error returned by server : bla') >= 0

    gdal.FileFromMemBuffer('/vsimem/carto&POSTFIELDS=q=SELECT current_schema() LIMIT 500 OFFSET 0',
                           """{ "fields" : null } """)
    ds = ogr.Open('CARTO:foo')
    assert ds is None, gdal.GetLastErrorMsg()

    gdal.FileFromMemBuffer('/vsimem/carto&POSTFIELDS=q=SELECT current_schema() LIMIT 500 OFFSET 0',
                           """{ "fields" : "invalid" } """)
    ds = ogr.Open('CARTO:foo')
    assert ds is None, gdal.GetLastErrorMsg()

    gdal.FileFromMemBuffer('/vsimem/carto&POSTFIELDS=q=SELECT current_schema() LIMIT 500 OFFSET 0',
                           """{ "fields" : {} } """)
    ds = ogr.Open('CARTO:foo')
    assert ds is None, gdal.GetLastErrorMsg()

    gdal.FileFromMemBuffer('/vsimem/carto&POSTFIELDS=q=SELECT current_schema() LIMIT 500 OFFSET 0',
                           """{ "fields" : { "foo": "invalid" } } """)
    ds = ogr.Open('CARTO:foo')
    assert ds is None, gdal.GetLastErrorMsg()

    gdal.FileFromMemBuffer('/vsimem/carto&POSTFIELDS=q=SELECT current_schema() LIMIT 500 OFFSET 0',
                           """{ "fields" : { "foo": {} } } """)
    ds = ogr.Open('CARTO:foo')
    assert ds is None, gdal.GetLastErrorMsg()

    gdal.FileFromMemBuffer('/vsimem/carto&POSTFIELDS=q=SELECT current_schema() LIMIT 500 OFFSET 0',
                           """{ "fields" : { "foo": { "type" : null } } } """)
    ds = ogr.Open('CARTO:foo')
    assert ds is None, gdal.GetLastErrorMsg()

    gdal.FileFromMemBuffer('/vsimem/carto&POSTFIELDS=q=SELECT current_schema() LIMIT 500 OFFSET 0',
                           """{ "fields" : { "foo": { "type" : {} } } } """)
    ds = ogr.Open('CARTO:foo')
    assert ds is None, gdal.GetLastErrorMsg()

    gdal.FileFromMemBuffer('/vsimem/carto&POSTFIELDS=q=SELECT current_schema() LIMIT 500 OFFSET 0',
                           """{ "fields" : { "foo": { "type" : "string" } } } """)
    ds = ogr.Open('CARTO:foo')
    assert ds is None, gdal.GetLastErrorMsg()

    gdal.FileFromMemBuffer('/vsimem/carto&POSTFIELDS=q=SELECT current_schema() LIMIT 500 OFFSET 0',
                           """{"rows":[ {"field1": "foo", "field2": "bar"} ],"fields":{"field1":{"type":"string"}, "field2":{"type":"string"}}}""")
    ds = ogr.Open('CARTO:foo')
    assert ds is None, gdal.GetLastErrorMsg()

    gdal.FileFromMemBuffer('/vsimem/carto&POSTFIELDS=q=SELECT current_schema() LIMIT 500 OFFSET 0',
                           """{"rows":[],"fields":{"current_schema":{"type":"string"}}}""")
    gdal.PushErrorHandler()
    ds = ogr.Open('CARTO:foo')
    gdal.PopErrorHandler()
    assert ds is None, gdal.GetLastErrorMsg()

    gdal.FileFromMemBuffer('/vsimem/carto&POSTFIELDS=q=SELECT current_schema() LIMIT 500 OFFSET 0',
                           """{"rows":[{"current_schema":"public"}],"fields":{"current_schema":{"type":"unknown(19)"}}}""")
    gdal.PushErrorHandler()
    ds = ogr.Open('CARTO:foo')
    gdal.PopErrorHandler()
    assert ds is None, gdal.GetLastErrorMsg()

    gdal.FileFromMemBuffer('/vsimem/carto&POSTFIELDS=q=SELECT CDB_UserTables() LIMIT 500 OFFSET 0',
                           """{"rows":[{"cdb_usertables":"table1"}],"fields":{"cdb_usertables":{"type":"string"}}}""")
    ds = ogr.Open('CARTO:foo')
    assert ds is not None and ds.GetLayerCount() == 1, gdal.GetLastErrorMsg()

    gdal.PushErrorHandler()
    lyr_defn = ds.GetLayer(0).GetLayerDefn()
    gdal.PopErrorHandler()

    assert lyr_defn.GetFieldCount() == 0

    # Empty layer
    gdal.FileFromMemBuffer('/vsimem/carto&POSTFIELDS=q=SELECT * FROM "table1" LIMIT 0',
                           """{"rows":[],"fields":{}}""")
    ds = ogr.Open('CARTO:foo')
    lyr = ds.GetLayer(0)
    lyr_defn = lyr.GetLayerDefn()
    assert lyr_defn.GetFieldCount() == 0

    gdal.FileFromMemBuffer('/vsimem/carto&POSTFIELDS=q=SELECT * FROM "table1" LIMIT 500 OFFSET 0',
                           """{"rows":[{}],"fields":{}}}""")
    f = lyr.GetNextFeature()
    if f.GetFID() != 0:
        f.DumpReadable()
        pytest.fail()

    # Layer without geometry or primary key
    gdal.FileFromMemBuffer('/vsimem/carto&POSTFIELDS=q=SELECT * FROM "table1" LIMIT 0',
                           """{"rows":[],"fields":{"strfield":{"type":"string"}, "realfield":{"type":"number"}, "boolfield":{"type":"boolean"}, "datefield":{"type":"date"}}}""")
    ds = ogr.Open('CARTO:foo')
    lyr = ds.GetLayer(0)
    lyr_defn = lyr.GetLayerDefn()
    assert lyr_defn.GetFieldCount() == 4
    assert (lyr_defn.GetFieldDefn(0).GetName() == 'strfield' and \
       lyr_defn.GetFieldDefn(0).GetType() == ogr.OFTString)
    assert (lyr_defn.GetFieldDefn(1).GetName() == 'realfield' and \
       lyr_defn.GetFieldDefn(1).GetType() == ogr.OFTReal)
    assert (lyr_defn.GetFieldDefn(2).GetName() == 'boolfield' and \
       lyr_defn.GetFieldDefn(2).GetType() == ogr.OFTInteger and \
       lyr_defn.GetFieldDefn(2).GetSubType() == ogr.OFSTBoolean)
    assert (lyr_defn.GetFieldDefn(3).GetName() == 'datefield' and \
       lyr_defn.GetFieldDefn(3).GetType() == ogr.OFTDateTime)

    gdal.FileFromMemBuffer('/vsimem/carto&POSTFIELDS=q=SELECT "strfield", "realfield", "boolfield", "datefield" FROM "table1" LIMIT 500 OFFSET 0',
                           """{"rows":[{ "strfield": "foo", "realfield": 1.23, "boolfield": true, "datefield": "2015-04-24T12:34:56.123Z" }],"fields":{"strfield":{"type":"string"}, "realfield":{"type":"number"}, "boolfield":{"type":"boolean"}, "datefield":{"type":"date"}}}""")
    f = lyr.GetNextFeature()
    if f['strfield'] != 'foo' or f['realfield'] != 1.23 or f['boolfield'] != 1 or \
       f['datefield'] != '2015/04/24 12:34:56.123+00':
        f.DumpReadable()
        pytest.fail()

    gdal.SetConfigOption('CARTO_API_KEY', 'foo')
    gdal.FileFromMemBuffer('/vsimem/carto&POSTFIELDS=q=SELECT current_schema() LIMIT 500 OFFSET 0&api_key=foo',
                           """{"rows":[{"current_schema":"public"}],"fields":{"current_schema":{"type":"unknown(19)"}}}""")
    gdal.FileFromMemBuffer('/vsimem/carto&POSTFIELDS=q=SELECT CDB_UserTables() LIMIT 500 OFFSET 0&api_key=foo',
                           """{"rows":[{"cdb_usertables":"table1"}],"fields":{"cdb_usertables":{"type":"string"}}}""")
    ds = ogr.Open('CARTO:foo')
    gdal.PushErrorHandler()
    lyr_defn = ds.GetLayer(0).GetLayerDefn()
    gdal.PopErrorHandler()
    assert lyr_defn.GetFieldCount() == 0

    get_full_details_fields_url = """/vsimem/carto&POSTFIELDS=q=SELECT a.attname, t.typname, a.attlen, format_type(a.atttypid,a.atttypmod), a.attnum, a.attnotnull, i.indisprimary, pg_get_expr(def.adbin, c.oid) AS defaultexpr, postgis_typmod_dims(a.atttypmod) dim, postgis_typmod_srid(a.atttypmod) srid, postgis_typmod_type(a.atttypmod)::text geomtyp, srtext FROM pg_class c JOIN pg_attribute a ON a.attnum > 0 AND a.attrelid = c.oid AND c.relname = 'table1' JOIN pg_type t ON a.atttypid = t.oid JOIN pg_namespace n ON c.relnamespace=n.oid AND n.nspname= 'public' LEFT JOIN pg_index i ON c.oid = i.indrelid AND i.indisprimary = 't' AND a.attnum = ANY(i.indkey) LEFT JOIN pg_attrdef def ON def.adrelid = c.oid AND def.adnum = a.attnum LEFT JOIN spatial_ref_sys srs ON srs.srid = postgis_typmod_srid(a.atttypmod) ORDER BY a.attnum LIMIT 500 OFFSET 0&api_key=foo"""
    gdal.FileFromMemBuffer(get_full_details_fields_url, '')
    ds = ogr.Open('CARTO:foo')
    gdal.PushErrorHandler()
    lyr_defn = ds.GetLayer(0).GetLayerDefn()
    gdal.PopErrorHandler()
    assert lyr_defn.GetFieldCount() == 0

    gdal.FileFromMemBuffer(get_full_details_fields_url,
                           """{"rows":[{"attname":"foo"}], "fields":{"attname":{"type":"string"}}}""")
    ds = ogr.Open('CARTO:foo')
    lyr = ds.GetLayer(0)
    gdal.PushErrorHandler()
    lyr_defn = lyr.GetLayerDefn()
    gdal.PopErrorHandler()
    assert lyr_defn.GetFieldCount() == 1

    gdal.PushErrorHandler()
    f = lyr.GetFeature(0)
    gdal.PopErrorHandler()
    assert f is None

    gdal.FileFromMemBuffer(get_full_details_fields_url,
                           """{"rows":[{"attname":"strfield", "typname":"varchar", "attnotnull": true, "defaultexpr": "def_value"},
               {"attname":"intfield", "typname":"int4"},
               {"attname":"doublefield", "typname":"float"},
               {"attname":"boolfield", "typname":"bool"},
               {"attname":"datetimefield", "typname":"timestamp"},
               {"attname":"cartodb_id","typname":"int4","indisprimary":true},
               {"attname":"created_at","typname":"date"},
               {"attname":"updated_at","typname":"date"},
               {"attname":"my_geom","typname":"geometry","dim":3,"srid":4326,"geomtyp":"Point",
                "srtext":"GEOGCS[\\"WGS 84\\",DATUM[\\"WGS_1984\\",SPHEROID[\\"WGS 84\\",6378137,298.257223563,AUTHORITY[\\"EPSG\\",\\"7030\\"]],AUTHORITY[\\"EPSG\\",\\"6326\\"]],PRIMEM[\\"Greenwich\\",0,AUTHORITY[\\"EPSG\\",\\"8901\\"]],UNIT[\\"degree\\",0.0174532925199433,AUTHORITY[\\"EPSG\\",\\"9122\\"]],AUTHORITY[\\"EPSG\\",\\"4326\\"]]"},
               {"attname":"the_geom_webmercator","typname":"geometry"}],
        "fields":{"attname":{"type":"string"},
                  "typname":{"type":"string"},
                  "attlen":{"type":"number"},
                  "format_type":{"type":"string"},
                  "attnum":{"type":"number"},
                  "attnotnull":{"type":"boolean"},
                  "indisprimary":{"type":"boolean"},
                  "defaultexpr":{"type":"string"},
                  "dim":{"type":"number"},
                  "srid":{"type":"number"},
                  "geomtyp":{"type":"string"},
                  "srtext":{"type":"string"}}}""")

    ds = ogr.Open('CARTO:foo')
    lyr = ds.GetLayer(0)
    lyr_defn = lyr.GetLayerDefn()
    assert lyr_defn.GetFieldCount() == 5
    assert (lyr_defn.GetFieldDefn(0).GetName() == 'strfield' and \
       lyr_defn.GetFieldDefn(0).GetType() == ogr.OFTString and not \
       lyr_defn.GetFieldDefn(0).IsNullable() and \
       lyr_defn.GetFieldDefn(0).GetDefault() == 'def_value')
    assert lyr_defn.GetGeomFieldCount() == 1
    assert lyr_defn.GetGeomFieldDefn(0).GetName() == 'my_geom'
    assert lyr_defn.GetGeomFieldDefn(0).GetType() == ogr.wkbPoint25D
    assert lyr_defn.GetGeomFieldDefn(0).GetSpatialRef().ExportToWkt().find('4326') >= 0

    gdal.PushErrorHandler()
    fc = lyr.GetFeatureCount()
    gdal.PopErrorHandler()
    assert fc == 0

    gdal.FileFromMemBuffer("""/vsimem/carto&POSTFIELDS=q=SELECT COUNT(*) FROM "table1"&api_key=foo""",
                           """{}""")
    gdal.PushErrorHandler()
    fc = lyr.GetFeatureCount()
    gdal.PopErrorHandler()
    assert fc == 0

    gdal.FileFromMemBuffer("""/vsimem/carto&POSTFIELDS=q=SELECT COUNT(*) FROM "table1"&api_key=foo""",
                           """{"rows":[{"foo":1}],
            "fields":{"foo":{"type":"number"}}}""")
    gdal.PushErrorHandler()
    fc = lyr.GetFeatureCount()
    gdal.PopErrorHandler()
    assert fc == 0

    gdal.FileFromMemBuffer("""/vsimem/carto&POSTFIELDS=q=SELECT COUNT(*) FROM "table1"&api_key=foo""",
                           """{"rows":[{"count":9876543210}],
            "fields":{"count":{"type":"number"}}}""")
    assert lyr.GetFeatureCount() == 9876543210

    gdal.PushErrorHandler()
    extent = lyr.GetExtent()
    gdal.PopErrorHandler()
    assert extent == (0, 0, 0, 0)

    gdal.FileFromMemBuffer("""/vsimem/carto&POSTFIELDS=q=SELECT ST_Extent("my_geom") FROM "table1"&api_key=foo""",
                           """{"rows":[{"foo":1}],
            "fields":{"foo":{"type":"number"}}}""")

    gdal.PushErrorHandler()
    extent = lyr.GetExtent()
    gdal.PopErrorHandler()
    assert extent == (0, 0, 0, 0)

    gdal.FileFromMemBuffer("""/vsimem/carto&POSTFIELDS=q=SELECT ST_Extent("my_geom") FROM "table1"&api_key=foo""",
                           """{"rows":[{"st_extent":""}],
            "fields":{"st_extent":{"type":"string"}}}""")
    gdal.ErrorReset()
    gdal.PushErrorHandler()
    lyr.GetExtent()
    gdal.PopErrorHandler()
    assert gdal.GetLastErrorMsg() != ''

    gdal.FileFromMemBuffer("""/vsimem/carto&POSTFIELDS=q=SELECT ST_Extent("my_geom") FROM "table1"&api_key=foo""",
                           """{"rows":[{"st_extent":"("}],
            "fields":{"st_extent":{"type":"string"}}}""")
    gdal.ErrorReset()
    gdal.PushErrorHandler()
    lyr.GetExtent()
    gdal.PopErrorHandler()
    assert gdal.GetLastErrorMsg() != ''

    gdal.FileFromMemBuffer("""/vsimem/carto&POSTFIELDS=q=SELECT ST_Extent("my_geom") FROM "table1"&api_key=foo""",
                           """{"rows":[{"st_extent":"BOX()"}],
            "fields":{"st_extent":{"type":"string"}}}""")
    gdal.ErrorReset()
    gdal.PushErrorHandler()
    lyr.GetExtent()
    gdal.PopErrorHandler()
    assert gdal.GetLastErrorMsg() != ''

    gdal.FileFromMemBuffer("""/vsimem/carto&POSTFIELDS=q=SELECT ST_Extent("my_geom") FROM "table1"&api_key=foo""",
                           """{"rows":[{"st_extent":"BOX(0,1,2,3)"}],
            "fields":{"st_extent":{"type":"string"}}}""")
    assert lyr.GetExtent() == (0.0, 2.0, 1.0, 3.0)

    gdal.PushErrorHandler()
    f = lyr.GetFeature(0)
    gdal.PopErrorHandler()
    assert f is None

    gdal.FileFromMemBuffer("""/vsimem/carto&POSTFIELDS=q=SELECT "cartodb_id", "my_geom", "strfield", "intfield", "doublefield", "boolfield", "datetimefield" FROM "table1" WHERE "cartodb_id" = 0&api_key=foo""",
                           """""")

    gdal.PushErrorHandler()
    f = lyr.GetFeature(0)
    gdal.PopErrorHandler()
    assert f is None

    gdal.FileFromMemBuffer("""/vsimem/carto&POSTFIELDS=q=SELECT "cartodb_id", "my_geom", "strfield", "intfield", "doublefield", "boolfield", "datetimefield" FROM "table1" WHERE "cartodb_id" = 0&api_key=foo""",
                           """{"rows":[{"st_extent":"BOX(0,1,2,3)"}],
            "fields":{"st_extent":{"type":"string"}}}""")

    f = lyr.GetFeature(0)
    assert f.GetFID() == -1 and f.IsFieldNull(0)

    gdal.FileFromMemBuffer("""/vsimem/carto&POSTFIELDS=q=SELECT "cartodb_id", "my_geom", "strfield", "intfield", "doublefield", "boolfield", "datetimefield" FROM "table1" WHERE "cartodb_id" = 0&api_key=foo""",
                           """{"rows":[{"cartodb_id":0}],
            "fields":{"cartodb_id":{"type":"numeric"}}}""")

    f = lyr.GetFeature(0)
    assert f.GetFID() == 0

    lyr.ResetReading()
    gdal.PushErrorHandler()
    f = lyr.GetNextFeature()
    gdal.PopErrorHandler()
    assert f is None

    gdal.FileFromMemBuffer("""/vsimem/carto&POSTFIELDS=q=SELECT "cartodb_id", "my_geom", "strfield", "intfield", "doublefield", "boolfield", "datetimefield" FROM "table1" WHERE "cartodb_id" >= 0 ORDER BY "cartodb_id" ASC LIMIT 500&api_key=foo""",
                           """{"rows":[{"cartodb_id":0}],
            "fields":{"cartodb_id":{"type":"numeric"}}}""")
    lyr.ResetReading()
    f = lyr.GetNextFeature()
    assert f.GetFID() == 0
    gdal.PushErrorHandler()
    f = lyr.GetNextFeature()
    gdal.PopErrorHandler()
    assert f is None

    gdal.SetConfigOption('CARTO_PAGE_SIZE', '2')
    gdal.FileFromMemBuffer("""/vsimem/carto&POSTFIELDS=q=SELECT "cartodb_id", "my_geom", "strfield", "intfield", "doublefield", "boolfield", "datetimefield" FROM "table1" WHERE "cartodb_id" >= 0 ORDER BY "cartodb_id" ASC LIMIT 2&api_key=foo""",
                           """{"rows":[{"cartodb_id":0},{"cartodb_id":10}],
            "fields":{"cartodb_id":{"type":"numeric"}}}""")
    lyr.ResetReading()
    f = lyr.GetNextFeature()
    assert f.GetFID() == 0
    f = lyr.GetNextFeature()
    assert f.GetFID() == 10

    gdal.FileFromMemBuffer("""/vsimem/carto&POSTFIELDS=q=SELECT "cartodb_id", "my_geom", "strfield", "intfield", "doublefield", "boolfield", "datetimefield" FROM "table1" WHERE "cartodb_id" >= 11 ORDER BY "cartodb_id" ASC LIMIT 2&api_key=foo""",
                           """{"rows":[{"cartodb_id":12}],
            "fields":{"cartodb_id":{"type":"numeric"}}}""")
    f = lyr.GetNextFeature()
    assert f.GetFID() == 12
    gdal.ErrorReset()
    f = lyr.GetNextFeature()
    assert f is None and gdal.GetLastErrorMsg() == ''

    lyr.SetAttributeFilter('strfield is NULL')

    gdal.PushErrorHandler()
    fc = lyr.GetFeatureCount()
    gdal.PopErrorHandler()
    assert fc == 0

    gdal.FileFromMemBuffer("""/vsimem/carto&POSTFIELDS=q=SELECT "cartodb_id", "my_geom", "strfield", "intfield", "doublefield", "boolfield", "datetimefield" FROM "table1" WHERE (strfield is NULL) AND "cartodb_id" >= 0 ORDER BY "cartodb_id" ASC LIMIT 2&api_key=foo""",
                           """{"rows":[{"cartodb_id":0}],
            "fields":{"cartodb_id":{"type":"numeric"}}}""")
    lyr.ResetReading()
    f = lyr.GetNextFeature()
    assert f is not None
    gdal.FileFromMemBuffer("""/vsimem/carto&POSTFIELDS=q=SELECT "cartodb_id", "my_geom", "strfield", "intfield", "doublefield", "boolfield", "datetimefield" FROM "table1" WHERE (strfield is NULL) AND "cartodb_id" >= 1 ORDER BY "cartodb_id" ASC LIMIT 2&api_key=foo""",
                           """{"rows":[],
            "fields":{"cartodb_id":{"type":"numeric"}}}""")
    gdal.ErrorReset()
    f = lyr.GetNextFeature()
    assert f is None and gdal.GetLastErrorMsg() == ''

    gdal.FileFromMemBuffer("""/vsimem/carto&POSTFIELDS=q=SELECT COUNT(*) FROM "table1" WHERE (strfield is NULL)&api_key=foo""",
                           """{"rows":[{"count":9876543210}],
            "fields":{"count":{"type":"number"}}}""")
    assert lyr.GetFeatureCount() == 9876543210

    lyr.SetSpatialFilterRect(-180, -90, 180, 90)

    gdal.PushErrorHandler()
    fc = lyr.GetFeatureCount()
    gdal.PopErrorHandler()
    assert fc == 0

    gdal.FileFromMemBuffer("""/vsimem/carto&POSTFIELDS=q=SELECT "cartodb_id", "my_geom", "strfield", "intfield", "doublefield", "boolfield", "datetimefield" FROM "table1" WHERE ("my_geom" %26%26 'BOX3D(-180 -90, 180 90)'::box3d) AND (strfield is NULL) AND "cartodb_id" >= 0 ORDER BY "cartodb_id" ASC LIMIT 2&api_key=foo""",
                           """{"rows":[{"cartodb_id":20, "my_geom": "010100000000000000000000400000000000804840" }],
            "fields":{"cartodb_id":{"type":"numeric"}, "my_geom":{"type":"string"}}}""")

    lyr.ResetReading()
    f = lyr.GetNextFeature()
    assert f is not None and f.GetGeometryRef().ExportToWkt() == 'POINT (2 49)'

    gdal.PushErrorHandler()
    fc = lyr.GetFeatureCount()
    gdal.PopErrorHandler()
    assert fc == 1

    gdal.FileFromMemBuffer("""/vsimem/carto&POSTFIELDS=q=SELECT COUNT(*) FROM "table1" WHERE ("my_geom" %26%26 'BOX3D(-180 -90, 180 90)'::box3d) AND (strfield is NULL)&api_key=foo""",
                           """{"rows":[{"count":9876543210}],
            "fields":{"count":{"type":"number"}}}""")
    assert lyr.GetFeatureCount() == 9876543210

    # Not permitted in read-only mode
    f = ogr.Feature(lyr.GetLayerDefn())
    gdal.PushErrorHandler()
    ds.CreateLayer('foo')
    ds.DeleteLayer(0)
    lyr.CreateFeature(f)
    lyr.SetFeature(f)
    lyr.DeleteFeature(0)
    lyr.CreateField(ogr.FieldDefn('foo'))
    lyr.DeleteField(0)
    gdal.PopErrorHandler()

    ds = None

    gdal.FileFromMemBuffer("""/vsimem/carto&POSTFIELDS=q=DROP FUNCTION IF EXISTS ogr_table_metadata(TEXT,TEXT); CREATE OR REPLACE FUNCTION ogr_table_metadata(schema_name TEXT, table_name TEXT) RETURNS TABLE (attname TEXT, typname TEXT, attlen INT, format_type TEXT, attnum INT, attnotnull BOOLEAN, indisprimary BOOLEAN, defaultexpr TEXT, dim INT, srid INT, geomtyp TEXT, srtext TEXT) AS $$ SELECT a.attname::text, t.typname::text, a.attlen::int, format_type(a.atttypid,a.atttypmod)::text, a.attnum::int, a.attnotnull::boolean, i.indisprimary::boolean, pg_get_expr(def.adbin, c.oid)::text AS defaultexpr, (CASE WHEN t.typname = 'geometry' THEN postgis_typmod_dims(a.atttypmod) ELSE NULL END)::int dim, (CASE WHEN t.typname = 'geometry' THEN postgis_typmod_srid(a.atttypmod) ELSE NULL END)::int srid, (CASE WHEN t.typname = 'geometry' THEN postgis_typmod_type(a.atttypmod) ELSE NULL END)::text geomtyp, srtext FROM pg_class c JOIN pg_attribute a ON a.attnum > 0 AND a.attrelid = c.oid AND c.relname = $2 AND c.relname IN (SELECT CDB_UserTables())JOIN pg_type t ON a.atttypid = t.oid JOIN pg_namespace n ON c.relnamespace=n.oid AND n.nspname = $1 LEFT JOIN pg_index i ON c.oid = i.indrelid AND i.indisprimary = 't' AND a.attnum = ANY(i.indkey) LEFT JOIN pg_attrdef def ON def.adrelid = c.oid AND def.adnum = a.attnum LEFT JOIN spatial_ref_sys srs ON srs.srid = postgis_typmod_srid(a.atttypmod) ORDER BY a.attnum $$ LANGUAGE SQL&api_key=foo""",
                           """""""")
    gdal.SetConfigOption('CARTO_PAGE_SIZE', None)
    ds = ogr.Open('CARTO:foo', update=1)
    lyr = ds.CreateLayer('MY_LAYER')

    gdal.ErrorReset()
    gdal.PushErrorHandler()
    lyr.GetNextFeature()
    gdal.PopErrorHandler()
    assert gdal.GetLastErrorMsg() != ''

    gdal.FileFromMemBuffer("""/vsimem/carto&POSTFIELDS=q=SELECT cdb_cartodbfytable('my_layer')&api_key=foo""",
                           """{"rows":[],
            "fields":{}}""")
    ds = None
    gdal.Unlink("""/vsimem/carto&POSTFIELDS=q=SELECT cdb_cartodbfytable('my_layer')&api_key=foo""")

    ds = gdal.OpenEx('CARTO:foo', gdal.OF_VECTOR | gdal.OF_UPDATE, open_options=['COPY_MODE=NO'])
    gdal.SetConfigOption('CARTO_MAX_CHUNK_SIZE', '0')
    sr = osr.SpatialReference()
    sr.ImportFromEPSG(4326)
    lyr = ds.CreateLayer('MY_LAYER', srs=sr)
    fld_defn = ogr.FieldDefn('STRFIELD', ogr.OFTString)
    fld_defn.SetNullable(0)
    fld_defn.SetDefault("'DEFAULT VAL'")
    fld_defn.SetWidth(20)
    lyr.CreateField(fld_defn)

    gdal.FileFromMemBuffer("""/vsimem/carto&POSTFIELDS=q=CREATE TABLE "my_layer" ( cartodb_id SERIAL,the_geom GEOMETRY(GEOMETRY, 4326),"strfield" VARCHAR NOT NULL DEFAULT 'DEFAULT VAL',PRIMARY KEY (cartodb_id) );DROP SEQUENCE IF EXISTS "my_layer_cartodb_id_seq" CASCADE;CREATE SEQUENCE "my_layer_cartodb_id_seq" START 1;ALTER SEQUENCE "my_layer_cartodb_id_seq" OWNED BY "my_layer".cartodb_id;ALTER TABLE "my_layer" ALTER COLUMN cartodb_id SET DEFAULT nextval('"my_layer_cartodb_id_seq"')&api_key=foo""",
                           """{"rows":[],
            "fields":{}}""")

    f = ogr.Feature(lyr.GetLayerDefn())
    gdal.PushErrorHandler()
    ret = lyr.CreateFeature(f)
    gdal.PopErrorHandler()
    assert ret != 0
    f = None

    fld_defn = ogr.FieldDefn('INTFIELD', ogr.OFTInteger)
    # No server answer
    with gdaltest.error_handler():
        ret = lyr.CreateField(fld_defn)
    assert ret != 0

    gdal.FileFromMemBuffer("""/vsimem/carto&POSTFIELDS=q=ALTER TABLE "my_layer" ADD COLUMN "intfield" INTEGER&api_key=foo""",
                           """{"rows":[],
            "fields":{}}""")
    assert lyr.CreateField(fld_defn) == 0

    fld_defn = ogr.FieldDefn('boolfield', ogr.OFTInteger)
    fld_defn.SetSubType(ogr.OFSTBoolean)

    gdal.FileFromMemBuffer("""/vsimem/carto&POSTFIELDS=q=ALTER TABLE "my_layer" ADD COLUMN "boolfield" BOOLEAN&api_key=foo""",
                           """{"rows":[],
            "fields":{}}""")
    assert lyr.CreateField(fld_defn) == 0

    # Invalid field
    with gdaltest.error_handler():
        assert lyr.DeleteField(-1) != 0

    # No server answer
    with gdaltest.error_handler():
        assert lyr.DeleteField(0) != 0

    gdal.FileFromMemBuffer("""/vsimem/carto&POSTFIELDS=q=ALTER TABLE "my_layer" DROP COLUMN "boolfield"&api_key=foo""",
                           """{"rows":[],
            "fields":{}}""")
    fld_pos = lyr.GetLayerDefn().GetFieldIndex(fld_defn.GetName())
    assert lyr.DeleteField(fld_pos) == 0

    gdal.FileFromMemBuffer("""/vsimem/carto&POSTFIELDS=q=ALTER TABLE "my_layer" ADD COLUMN "boolfield" BOOLEAN&api_key=foo""",
                           """{"rows":[],
            "fields":{}}""")
    assert lyr.CreateField(fld_defn) == 0

    f = ogr.Feature(lyr.GetLayerDefn())
    gdal.PushErrorHandler()
    ret = lyr.CreateFeature(f)
    gdal.PopErrorHandler()
    assert ret != 0

    f = ogr.Feature(lyr.GetLayerDefn())
    f.SetField('strfield', 'foo')
    f.SetField('intfield', 1)
    f.SetField('boolfield', 1)
    f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(2 49)'))
    gdal.FileFromMemBuffer("""/vsimem/carto&POSTFIELDS=q=INSERT INTO "my_layer" ("strfield", "intfield", "boolfield", "the_geom") VALUES ('foo', 1, 't', '0101000020E610000000000000000000400000000000804840') RETURNING "cartodb_id"&api_key=foo""",
                           """{"rows":[ {"cartodb_id": 1} ],
            "fields":{"cartodb_id":{"type":"integer"}}}""")
    ret = lyr.CreateFeature(f)
    assert ret == 0 and f.GetFID() == 1

    f.SetFID(-1)
    gdal.PushErrorHandler()
    ret = lyr.SetFeature(f)
    gdal.PopErrorHandler()
    assert ret != 0

    f.SetFID(3)
    gdal.PushErrorHandler()
    ret = lyr.SetFeature(f)
    gdal.PopErrorHandler()
    assert ret != 0

    gdal.FileFromMemBuffer("""/vsimem/carto&POSTFIELDS=q=UPDATE "my_layer" SET "strfield" = 'foo', "intfield" = 1, "boolfield" = 't', "the_geom" = '0101000020E610000000000000000000400000000000804840' WHERE "cartodb_id" = 3&api_key=foo""",
                           """{"total_rows": 0}""")
    ret = lyr.SetFeature(f)
    assert ret == ogr.OGRERR_NON_EXISTING_FEATURE

    gdal.FileFromMemBuffer("""/vsimem/carto&POSTFIELDS=q=UPDATE "my_layer" SET "strfield" = 'foo', "intfield" = 1, "boolfield" = 't', "the_geom" = '0101000020E610000000000000000000400000000000804840' WHERE "cartodb_id" = 3&api_key=foo""",
                           """{"total_rows": 1}""")
    ret = lyr.SetFeature(f)
    assert ret == 0

    f = ogr.Feature(lyr.GetLayerDefn())
    gdal.PushErrorHandler()
    ret = lyr.CreateFeature(f)
    gdal.PopErrorHandler()
    assert ret != 0

    gdal.FileFromMemBuffer("""/vsimem/carto&POSTFIELDS=q=INSERT INTO "my_layer" DEFAULT VALUES RETURNING "cartodb_id"&api_key=foo""",
                           """{"rows":[ {"cartodb_id": 4} ],
            "fields":{"cartodb_id":{"type":"integer"}}}""")
    ret = lyr.CreateFeature(f)
    assert ret == 0 and f.GetFID() == 4

    gdal.PushErrorHandler()
    ret = lyr.DeleteFeature(0)
    gdal.PopErrorHandler()
    assert ret != 0

    gdal.FileFromMemBuffer("""/vsimem/carto&POSTFIELDS=q=DELETE FROM "my_layer" WHERE "cartodb_id" = 0&api_key=foo""",
                           """{"total_rows": 0}""")
    ret = lyr.DeleteFeature(0)
    assert ret == ogr.OGRERR_NON_EXISTING_FEATURE

    gdal.FileFromMemBuffer("""/vsimem/carto&POSTFIELDS=q=DELETE FROM "my_layer" WHERE "cartodb_id" = 0&api_key=foo""",
                           """{"total_rows": 1}""")
    ret = lyr.DeleteFeature(0)
    assert ret == 0

    gdal.FileFromMemBuffer("""/vsimem/carto&POSTFIELDS=q=SELECT cdb_cartodbfytable('my_layer')&api_key=foo""",
                           """{"rows":[],
            "fields":{}}""")
    ds = None

    gdal.SetConfigOption('CARTO_MAX_CHUNK_SIZE', None)
    ds = gdal.OpenEx('CARTO:foo', gdal.OF_VECTOR | gdal.OF_UPDATE, open_options=['COPY_MODE=NO'])
    lyr = ds.GetLayer(0)

    gdal.FileFromMemBuffer("""/vsimem/carto&POSTFIELDS=q=SELECT pg_catalog.pg_get_serial_sequence('table1', 'cartodb_id') AS seq_name&api_key=foo""",
                           """{"rows":[{"seq_name":"table1_cartodb_id_seq0"}],"fields":{"seq_name":{"type":"string"}}}""")

    gdal.FileFromMemBuffer("""/vsimem/carto&POSTFIELDS=q=SELECT nextval('table1_cartodb_id_seq0') AS nextid&api_key=foo""",
                           """{"rows":[{"nextid":11}],"fields":{"nextid":{"type":"number"}}}""")

    f = ogr.Feature(lyr.GetLayerDefn())
    f.SetField('strfield', 'foo')
    ret = lyr.CreateFeature(f)
    assert ret == 0 and f.GetFID() == 11

    f = ogr.Feature(lyr.GetLayerDefn())

    with gdaltest.tempfile("""/vsimem/carto&POSTFIELDS=q=BEGIN;INSERT INTO "table1" ("strfield", "cartodb_id") VALUES ('foo', 11);INSERT INTO "table1" DEFAULT VALUES;COMMIT;&api_key=foo""",
                           """{"rows":[],
            "fields":{}}"""):
        ret = lyr.CreateFeature(f)
        ds = None
    if ret != 0 or f.GetFID() != 12:
        f.DumpReadable()
        pytest.fail()

    ds = gdal.OpenEx('CARTO:foo', gdal.OF_VECTOR | gdal.OF_UPDATE, open_options=['COPY_MODE=NO'])
    lyr = ds.GetLayer(0)
    f = ogr.Feature(lyr.GetLayerDefn())
    f.SetFieldNull('strfield')
    with gdaltest.tempfile("""/vsimem/carto&POSTFIELDS=q=BEGIN;INSERT INTO "table1" ("strfield", "cartodb_id") VALUES (NULL, 11);COMMIT;&api_key=foo""",
                           """{"rows":[],
            "fields":{}}"""):
        ret = lyr.CreateFeature(f)
        ds = None

    if ret != 0 or f.GetFID() != 11:
        f.DumpReadable()
        pytest.fail()

    # Now remove default value to strfield
    gdal.FileFromMemBuffer(get_full_details_fields_url,
                           """{"rows":[{"attname":"strfield", "typname":"varchar"},
               {"attname":"intfield", "typname":"int4"},
               {"attname":"doublefield", "typname":"float"},
               {"attname":"boolfield", "typname":"bool"},
               {"attname":"datetimefield", "typname":"timestamp"},
               {"attname":"cartodb_id","typname":"int4","indisprimary":true},
               {"attname":"created_at","typname":"date"},
               {"attname":"updated_at","typname":"date"},
               {"attname":"my_geom","typname":"geometry","dim":3,"srid":4326,"geomtyp":"Point",
                "srtext":"GEOGCS[\\"WGS 84\\",DATUM[\\"WGS_1984\\",SPHEROID[\\"WGS 84\\",6378137,298.257223563,AUTHORITY[\\"EPSG\\",\\"7030\\"]],AUTHORITY[\\"EPSG\\",\\"6326\\"]],PRIMEM[\\"Greenwich\\",0,AUTHORITY[\\"EPSG\\",\\"8901\\"]],UNIT[\\"degree\\",0.0174532925199433,AUTHORITY[\\"EPSG\\",\\"9122\\"]],AUTHORITY[\\"EPSG\\",\\"4326\\"]]"},
               {"attname":"the_geom_webmercator","typname":"geometry"}],
        "fields":{"attname":{"type":"string"},
                  "typname":{"type":"string"},
                  "attlen":{"type":"number"},
                  "format_type":{"type":"string"},
                  "attnum":{"type":"number"},
                  "attnotnull":{"type":"boolean"},
                  "indisprimary":{"type":"boolean"},
                  "defaultexpr":{"type":"string"},
                  "dim":{"type":"number"},
                  "srid":{"type":"number"},
                  "geomtyp":{"type":"string"},
                  "srtext":{"type":"string"}}}""")

    ds = gdal.OpenEx('CARTO:foo', gdal.OF_VECTOR | gdal.OF_UPDATE, open_options=['COPY_MODE=NO'])
    lyr = ds.GetLayer(0)

    f = ogr.Feature(lyr.GetLayerDefn())
    f.SetField('strfield', 'foo')
    with gdaltest.tempfile("""/vsimem/carto&POSTFIELDS=q=SELECT nextval('table1_cartodb_id_seq') AS nextid&api_key=foo""",
                           """{"rows":[{"nextid":11}],"fields":{"nextid":{"type":"number"}}}"""):
        ret = lyr.CreateFeature(f)
    assert ret == 0 and f.GetFID() == 11

    f = ogr.Feature(lyr.GetLayerDefn())
    f.SetField('strfield', 'bar')
    ret = lyr.CreateFeature(f)
    assert ret == 0 and f.GetFID() == 12

    f = ogr.Feature(lyr.GetLayerDefn())
    f.SetField('strfield', 'baz')
    ret = lyr.CreateFeature(f)
    assert ret == 0 and f.GetFID() == 13

    gdal.ErrorReset()
    with gdaltest.tempfile("""/vsimem/carto&POSTFIELDS=q=BEGIN;INSERT INTO "table1" ("strfield", "cartodb_id") VALUES ('foo', 11);INSERT INTO "table1" ("strfield", "intfield", "doublefield", "boolfield", "datetimefield", "my_geom") VALUES ('bar', NULL, NULL, NULL, NULL, NULL), ('baz', NULL, NULL, NULL, NULL, NULL);COMMIT;&api_key=foo""",
                           """{"rows":[], "fields":{}}"""):
        ds = None
    assert gdal.GetLastErrorMsg() == ''

    ds = gdal.OpenEx('CARTO:foo', gdal.OF_VECTOR | gdal.OF_UPDATE, open_options=['COPY_MODE=NO'])

    gdal.PushErrorHandler()
    lyr = ds.CreateLayer('table1')
    gdal.PopErrorHandler()
    assert lyr is None

    with gdaltest.tempfile("""/vsimem/carto&POSTFIELDS=q=DROP TABLE "table1"&api_key=foo""",
                           """{"rows":[], "fields":{}}"""):
        lyr = ds.CreateLayer('table1', geom_type=ogr.wkbPolygon, options=['OVERWRITE=YES', 'CARTODBFY=NO'])

    f = ogr.Feature(lyr.GetLayerDefn())
    f.SetGeometry(ogr.CreateGeometryFromWkt('POLYGON((0 0,0 1,1 0,0 0))'))
    with gdaltest.tempfile("""/vsimem/carto&POSTFIELDS=q=CREATE TABLE "table1" ( cartodb_id SERIAL,the_geom Geometry(MULTIPOLYGON,0),PRIMARY KEY (cartodb_id) );DROP SEQUENCE IF EXISTS "table1_cartodb_id_seq" CASCADE;CREATE SEQUENCE "table1_cartodb_id_seq" START 1;ALTER SEQUENCE "table1_cartodb_id_seq" OWNED BY "table1".cartodb_id;ALTER TABLE "table1" ALTER COLUMN cartodb_id SET DEFAULT nextval('"table1_cartodb_id_seq"')&api_key=foo""",
                           """{"rows":[], "fields":{}}"""):
        assert lyr.CreateFeature(f) == 0

    gdal.ErrorReset()
    with gdaltest.tempfile("""/vsimem/carto&POSTFIELDS=q=BEGIN;INSERT INTO "table1" ("the_geom") VALUES ('0106000020E61000000100000001030000000100000004000000000000000000000000000000000000000000000000000000000000000000F03F000000000000F03F000000000000000000000000000000000000000000000000');COMMIT;&api_key=foo""",
                           """{"rows":[],
            "fields":{}}"""):
        ds = None
    assert gdal.GetLastErrorMsg() == ''

    ds = gdal.OpenEx('CARTO:foo', gdal.OF_VECTOR | gdal.OF_UPDATE, open_options=['COPY_MODE=NO'])

    with gdaltest.tempfile("""/vsimem/carto&POSTFIELDS=q=DROP TABLE "table1"&api_key=foo""",
                           """{"rows":[], "fields":{}}"""):
        lyr = ds.CreateLayer('table1', geom_type=ogr.wkbPolygon, options=['OVERWRITE=YES', 'CARTODBFY=NO'])

    f = ogr.Feature(lyr.GetLayerDefn())
    f.SetFID(100)

    with gdaltest.tempfile("""/vsimem/carto&POSTFIELDS=q=CREATE TABLE "table1" ( cartodb_id SERIAL,the_geom Geometry(MULTIPOLYGON,0),PRIMARY KEY (cartodb_id) );DROP SEQUENCE IF EXISTS "table1_cartodb_id_seq" CASCADE;CREATE SEQUENCE "table1_cartodb_id_seq" START 1;ALTER SEQUENCE "table1_cartodb_id_seq" OWNED BY "table1".cartodb_id;ALTER TABLE "table1" ALTER COLUMN cartodb_id SET DEFAULT nextval('"table1_cartodb_id_seq"')&api_key=foo""",
                           """{"rows":[], "fields":{}}"""):
        with gdaltest.tempfile("""/vsimem/carto&POSTFIELDS=q=BEGIN;INSERT INTO "table1" ("cartodb_id") VALUES (100);COMMIT;&api_key=foo""",
                               """{"rows":[], "fields":{}}"""):
            assert lyr.CreateFeature(f) == 0
            assert f.GetFID() == 100
            ds = None

    ds = ogr.Open('CARTO:foo', update=1)

    gdal.PushErrorHandler()
    ret = ds.DeleteLayer(0)
    gdal.PopErrorHandler()
    assert ret != 0

    gdal.ErrorReset()
    ds = gdal.OpenEx('CARTO:foo', gdal.OF_VECTOR | gdal.OF_UPDATE, open_options=['COPY_MODE=YES'])
    assert ds is not None
    lyr = ds.GetLayerByName('table1')
    assert lyr is not None

    with gdaltest.tempfile("""/vsimem/carto/copyfrom?q=COPY%20"table1"%20("strfield","my_geom","cartodb_id")%20FROM%20STDIN%20WITH%20(FORMAT%20text,%20ENCODING%20UTF8)&api_key=foo&POSTFIELDS=copytest\t0101000020E610000000000000000059400000000000005940\t11\n\\.\n""","""{}"""):

        with gdaltest.tempfile("""/vsimem/carto/copyfrom?q=COPY%20"table1"%20("intfield","my_geom")%20FROM%20STDIN%20WITH%20(FORMAT%20text,%20ENCODING%20UTF8)&api_key=foo&POSTFIELDS=12\t0101000020E610000000000000000059400000000000005940\n\\.\n""","""{}"""):

            f = ogr.Feature(lyr.GetLayerDefn())
            f.SetField('strfield', 'copytest')
            f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(100 100)'))
            assert lyr.CreateFeature(f) == 0

            f = ogr.Feature(lyr.GetLayerDefn())
            f.SetField('intfield', 12)
            f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(100 100)'))
            assert lyr.CreateFeature(f) == 0

            ds = None # force flush

    ds = ogr.Open('CARTO:foo', update=1)

    gdal.ErrorReset()
    with gdaltest.tempfile("""/vsimem/carto&POSTFIELDS=q=DROP TABLE "table1"&api_key=foo""",
                           """{"rows":[], "fields":{}}"""):
        ds.ExecuteSQL('DELLAYER:table1')
    assert gdal.GetLastErrorMsg() == '' and ds.GetLayerByName('table1') is None

    with gdaltest.tempfile('/vsimem/carto&POSTFIELDS=q=SELECT current_schema() LIMIT 500 OFFSET 0&api_key=foo',
                           """{"rows":[{"current_schema":"my_schema"}],"fields":{"current_schema":{"type":"unknown(19)"}}}"""):
        with gdaltest.tempfile('/vsimem/carto&POSTFIELDS=q=SELECT CDB_UserTables() LIMIT 500 OFFSET 0&api_key=foo',
                               """{"rows":[],"fields":{"cdb_usertables":{"type":"string"}}}"""):
            with gdaltest.tempfile("""/vsimem/carto&POSTFIELDS=q=SELECT c.relname FROM pg_class c, pg_namespace n WHERE c.relkind in ('r', 'v') AND c.relname !~ '^pg_' AND c.relnamespace=n.oid AND n.nspname = 'my_schema' LIMIT 500 OFFSET 0&api_key=foo""",
                                   """{"rows":[{"relname": "a_layer"}],"fields":{"relname":{"type":"string"}}}"""):
                ds = ogr.Open('CARTO:foo')
    assert ds.GetLayerByName('a_layer') is not None