/** * Create a value for this schema using WKB * @param pointSchema a {@link Schema} instance which represents a point; may not be null * @param wkb the original Well-Known binary representation of the coordinate; may not be null * @param srid the coordinate reference system identifier; null if unset/unknown * @return a {@link Struct} which represents a Connect value for this schema; never null */ public static Struct createValue(Schema geomSchema, byte[] wkb, Integer srid) throws IllegalArgumentException { Struct result = Geometry.createValue(geomSchema, wkb, srid); double[] pt = parseWKBPoint(wkb); result.put(X_FIELD, pt[0]); result.put(Y_FIELD, pt[1]); return result; } }
/** * Returns a {@link SchemaBuilder} for a Geometry field, with all other default Schema settings. * * @return the schema * @see #builder() */ public static Schema schema() { return builder().build(); }
/** * Creates a value for this schema using 2 given coordinates. * * @param pointSchema a {@link Schema} instance which represents a point; may not be null * @param x the X coordinate of the point; may not be null * @param y the Y coordinate of the point; may not be null * @return a {@link Struct} which represents a Connect value for this schema; never null */ public static Struct createValue(Schema geomSchema, double x, double y){ // turn the specified points byte[] wkb = buildWKBPoint(x, y); Struct result = Geometry.createValue(geomSchema, wkb, null); result.put(X_FIELD, x); result.put(Y_FIELD, y); return result; }
protected List<SchemaAndValueField> schemaAndValuesForPostgisTypes() { Schema geomSchema = Geometry.builder().optional().build(); Schema geogSchema = Geography.builder().optional().build(); return Arrays.asList( // geometries are encoded here as HexEWKB new SchemaAndValueField("p", geomSchema, // 'SRID=3187;POINT(174.9479 -36.7208)'::postgis.geometry Geometry.createValue(geomSchema, DatatypeConverter.parseHexBinary("0101000020730C00001C7C613255DE6540787AA52C435C42C0"), 3187)), new SchemaAndValueField("ml", geogSchema, // 'MULTILINESTRING((169.1321 -44.7032, 167.8974 -44.6414))'::postgis.geography Geography.createValue(geogSchema, DatatypeConverter.parseHexBinary("0105000020E610000001000000010200000002000000A779C7293A2465400B462575025A46C0C66D3480B7FC6440C3D32B65195246C0"), 4326)) ); }
return Uuid.builder(); case PgOid.POINT: return Point.builder(); case PgOid.MONEY: return Decimal.builder(column.scale().get()); return Geometry.builder(); return Geography.builder(); return SchemaBuilder.array(Geometry.builder().optional().build()); return SchemaBuilder.array(Geography.builder().optional().build());
protected List<SchemaAndValueField> schemaAndValuesForPostgisArrayTypes() { Schema geomSchema = Geometry.builder() .optional() .build(); List<Struct> values = Arrays.asList( // 'GEOMETRYCOLLECTION EMPTY'::postgis.geometry Geometry.createValue(geomSchema, DatatypeConverter.parseHexBinary("010700000000000000"), null), // 'POLYGON((166.51 -46.64, 178.52 -46.64, 178.52 -34.45, 166.51 -34.45, 166.51 -46.64))'::postgis.geometry Geometry.createValue(geomSchema, DatatypeConverter.parseHexBinary("01030000000100000005000000B81E85EB51D0644052B81E85EB5147C0713D0AD7A350664052B81E85EB5147C0713D0AD7A35066409A999999993941C0B81E85EB51D064409A999999993941C0B81E85EB51D0644052B81E85EB5147C0"), null) ); return Arrays.asList( // geometries are encoded here as HexEWKB new SchemaAndValueField("ga", SchemaBuilder.array(geomSchema).optional().build(), values ), new SchemaAndValueField("gann", SchemaBuilder.array(geomSchema).build(), values ) ); }
/** * Convert the a value representing a POINT {@code byte[]} value to a Point value used in a {@link SourceRecord}. * * @param column the column in which the value appears * @param fieldDefn the field definition for the {@link SourceRecord}'s {@link Schema}; never null * @param data the data; may be null * @return the converted value, or null if the conversion could not be made and the column allows nulls * @throws IllegalArgumentException if the value could not be converted but the column does not allow nulls */ protected Object convertPoint(Column column, Field fieldDefn, Object data){ final MySqlGeometry empty = MySqlGeometry.createEmpty(); return convertValue(column, fieldDefn, data, io.debezium.data.geometry.Geometry.createValue(fieldDefn.schema(), empty.getWkb(), empty.getSrid()), (r) -> { if (data instanceof byte[]) { // The binlog utility sends a byte array for any Geometry type, we will use our own binaryParse to parse the byte to WKB, hence // to the suitable class MySqlGeometry mySqlGeometry = MySqlGeometry.fromBytes((byte[]) data); if (mySqlGeometry.isPoint()) { r.deliver(io.debezium.data.geometry.Point.createValue(fieldDefn.schema(), mySqlGeometry.getWkb(), mySqlGeometry.getSrid())); } else { throw new ConnectException("Failed to parse and read a value of type POINT on " + column); } } }); }
protected List<SchemaAndValueField> schemaAndValuesForGeomTypes() { Schema pointSchema = Point.builder().optional().build(); return Collections.singletonList(new SchemaAndValueField("p", pointSchema, Point.createValue(pointSchema, 1, 1))); }
@Test public void shouldLoadSchemaForPostgisTypes() throws Exception { TestHelper.executeDDL("init_postgis.ddl"); TestHelper.executeDDL("postgis_create_tables.ddl"); PostgresConnectorConfig config = new PostgresConnectorConfig(TestHelper.defaultConfig().build()); schema = TestHelper.getSchema(config); try (PostgresConnection connection = TestHelper.create()) { schema.refresh(connection, false); final String[] testTables = new String[] {"public.postgis_table"}; assertTablesIncluded(testTables); Arrays.stream(testTables).forEach(tableId -> assertKeySchema(tableId, "pk", Schema.INT32_SCHEMA)); assertTableSchema("public.postgis_table", "p, ml", Geometry.builder().optional().build(), Geography.builder().optional().build()); } }
/** * Convert the a value representing a GEOMETRY {@code byte[]} value to a Geometry value used in a {@link SourceRecord}. * * @param column the column in which the value appears * @param fieldDefn the field definition for the {@link SourceRecord}'s {@link Schema}; never null * @param data the data; may be null * @return the converted value, or null if the conversion could not be made and the column allows nulls * @throws IllegalArgumentException if the value could not be converted but the column does not allow nulls */ protected Object convertGeometry(Column column, Field fieldDefn, Object data) { final MySqlGeometry empty = MySqlGeometry.createEmpty(); return convertValue(column, fieldDefn, data, io.debezium.data.geometry.Geometry.createValue(fieldDefn.schema(), empty.getWkb(), empty.getSrid()), (r) -> { if (data instanceof byte[]) { // The binlog utility sends a byte array for any Geometry type, we will use our own binaryParse to parse the byte to WKB, hence // to the suitable class if (data instanceof byte[]) { // The binlog utility sends a byte array for any Geometry type, we will use our own binaryParse to parse the byte to WKB, hence // to the suitable class MySqlGeometry mySqlGeometry = MySqlGeometry.fromBytes((byte[]) data); r.deliver(io.debezium.data.geometry.Geometry.createValue(fieldDefn.schema(), mySqlGeometry.getWkb(), mySqlGeometry.getSrid())); } } }); }
/** * Converts a value representing a Postgres point for a column, to a Kafka Connect value. * * @param column the JDBC column; never null * @param fieldDefn the Connect field definition for this column; never null * @param data a data for the point column, either coming from the JDBC driver or logical decoding plugin * @return a value which will be used by Connect to represent the actual point value */ protected Object convertPoint(Column column, Field fieldDefn, Object data) { return convertValue(column, fieldDefn, data, Point.createValue(fieldDefn.schema(), 0, 0), (r) -> { final Schema schema = fieldDefn.schema(); if (data instanceof PGpoint) { PGpoint pgPoint = (PGpoint) data; r.deliver(Point.createValue(schema, pgPoint.x, pgPoint.y)); } else if (data instanceof String) { String dataString = data.toString(); try { PGpoint pgPoint = new PGpoint(dataString); r.deliver(Point.createValue(schema, pgPoint.x, pgPoint.y)); } catch (SQLException e) { logger.warn("Error converting the string '{}' to a PGPoint type for the column '{}'", dataString, column); } } else if (data instanceof PgProto.Point) { r.deliver(Point.createValue(schema, ((PgProto.Point) data).getX(), ((PgProto.Point) data).getY())); } }); }
Json.builder().optional().build(), Json.builder().optional().build(), Xml.builder().optional().build(), Uuid.builder().optional().build()); assertTableSchema("public.geom_table", "p", Point.builder().optional().build()); assertTableSchema("public.tstzrange_table", "unbounded_exclusive_range, bounded_inclusive_range", Schema.OPTIONAL_STRING_SCHEMA, Schema.OPTIONAL_STRING_SCHEMA);
protected Object convertGeography(Column column, Field fieldDefn, Object data) { final PostgisGeometry empty = PostgisGeometry.createEmpty(); return convertValue(column, fieldDefn, data, io.debezium.data.geometry.Geography.createValue(fieldDefn.schema(), empty.getWkb(), empty.getSrid()), (r) -> { final Schema schema = fieldDefn.schema(); try { if (data instanceof byte[]) { PostgisGeometry geom = PostgisGeometry.fromHexEwkb(new String((byte[])data, "ASCII")); r.deliver(io.debezium.data.geometry.Geography.createValue(schema, geom.getWkb(), geom.getSrid())); } else if (data instanceof PGobject) { PGobject pgo = (PGobject)data; PostgisGeometry geom = PostgisGeometry.fromHexEwkb(pgo.getValue()); r.deliver(io.debezium.data.geometry.Geography.createValue(schema, geom.getWkb(), geom.getSrid())); } else if (data instanceof String) { PostgisGeometry geom = PostgisGeometry.fromHexEwkb((String)data); r.deliver(io.debezium.data.geometry.Geography.createValue(schema, geom.getWkb(), geom.getSrid())); } } catch (IllegalArgumentException | UnsupportedEncodingException e) { logger.warn("Error converting to a Geography type", column); } }); }
@Test public void shouldConvertMySqlBytesToPoint() throws Exception { byte[] mysqlBytes = DatatypeConverter.parseHexBinary("000000000101000000e3a59bc420f01b4015a143a69d383240"); //This represents 'POINT(6.9845 18.22115554)' MySqlGeometry geom = MySqlGeometry.fromBytes(mysqlBytes); assertTrue(geom.isPoint()); assertEquals(geom.getSrid(), null); double[] coords = Point.parseWKBPoint(geom.getWkb()); assertEquals(coords[0], 6.9845, 0.0001); assertEquals(coords[1], 18.22115554, 0.0001); }
protected List<SchemaAndValueField> schemaAndValuesForPostgisTypes() { Schema geomSchema = Geometry.builder().optional().build(); Schema geogSchema = Geography.builder().optional().build(); return Arrays.asList( // geometries are encoded here as HexEWKB new SchemaAndValueField("p", geomSchema, // 'SRID=3187;POINT(174.9479 -36.7208)'::postgis.geometry Geometry.createValue(geomSchema, DatatypeConverter.parseHexBinary("0101000020730C00001C7C613255DE6540787AA52C435C42C0"), 3187)), new SchemaAndValueField("ml", geogSchema, // 'MULTILINESTRING((169.1321 -44.7032, 167.8974 -44.6414))'::postgis.geography Geography.createValue(geogSchema, DatatypeConverter.parseHexBinary("0105000020E610000001000000010200000002000000A779C7293A2465400B462575025A46C0C66D3480B7FC6440C3D32B65195246C0"), 4326)) ); }
/** * Create a value for this schema using WKB * @param pointSchema a {@link Schema} instance which represents a point; may not be null * @param wkb the original Well-Known binary representation of the coordinate; may not be null * @param srid the coordinate reference system identifier; null if unset/unknown * @return a {@link Struct} which represents a Connect value for this schema; never null */ public static Struct createValue(Schema geomSchema, byte[] wkb, Integer srid) throws IllegalArgumentException { Struct result = Geometry.createValue(geomSchema, wkb, srid); double[] pt = parseWKBPoint(wkb); result.put(X_FIELD, pt[0]); result.put(Y_FIELD, pt[1]); return result; } }
/** * Creates a value for this schema using 2 given coordinates. * * @param pointSchema a {@link Schema} instance which represents a point; may not be null * @param x the X coordinate of the point; may not be null * @param y the Y coordinate of the point; may not be null * @return a {@link Struct} which represents a Connect value for this schema; never null */ public static Struct createValue(Schema geomSchema, double x, double y){ // turn the specified points byte[] wkb = buildWKBPoint(x, y); Struct result = Geometry.createValue(geomSchema, wkb, null); result.put(X_FIELD, x); result.put(Y_FIELD, y); return result; }
/** * Returns a {@link SchemaBuilder} for a Geometry field, with all other default Schema settings. * * @return the schema * @see #builder() */ public static Schema schema() { return builder().build(); }
protected Object convertGeometry(Column column, Field fieldDefn, Object data) { final PostgisGeometry empty = PostgisGeometry.createEmpty(); return convertValue(column, fieldDefn, data, io.debezium.data.geometry.Geometry.createValue(fieldDefn.schema(), empty.getWkb(), empty.getSrid()), (r) -> { try { final Schema schema = fieldDefn.schema(); if (data instanceof byte[]) { PostgisGeometry geom = PostgisGeometry.fromHexEwkb(new String((byte[])data, "ASCII")); r.deliver(io.debezium.data.geometry.Geometry.createValue(schema, geom.getWkb(), geom.getSrid())); } else if (data instanceof PGobject) { PGobject pgo = (PGobject)data; PostgisGeometry geom = PostgisGeometry.fromHexEwkb(pgo.getValue()); r.deliver(io.debezium.data.geometry.Geometry.createValue(schema, geom.getWkb(), geom.getSrid())); } else if (data instanceof String) { PostgisGeometry geom = PostgisGeometry.fromHexEwkb((String)data); r.deliver(io.debezium.data.geometry.Geometry.createValue(schema, geom.getWkb(), geom.getSrid())); } } catch (IllegalArgumentException | UnsupportedEncodingException e) { logger.warn("Error converting to a Geometry type", column); } }); }