@@ -232,7 +232,7 @@ public function createCollection(string $name, array $attributes = [], array $in
232232 $ sqlTenant = $ this ->sharedTables ? '_tenant INTEGER DEFAULT NULL, ' : '' ;
233233 $ collection = "
234234 CREATE TABLE {$ this ->getSQLTable ($ id )} (
235- _id SERIAL NOT NULL ,
235+ _id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY ,
236236 _uid VARCHAR(255) NOT NULL,
237237 " . $ sqlTenant . "
238238 \"_createdAt \" TIMESTAMP(3) DEFAULT NULL,
@@ -241,7 +241,7 @@ public function createCollection(string $name, array $attributes = [], array $in
241241 \"_updatedBy \" VARCHAR(255) DEFAULT NULL,
242242 _permissions TEXT DEFAULT NULL,
243243 " . \implode (' ' , $ attributeStrings ) . "
244- PRIMARY KEY (_id)
244+ _permissions TEXT DEFAULT NULL
245245 );
246246 " ;
247247
@@ -268,13 +268,12 @@ public function createCollection(string $name, array $attributes = [], array $in
268268
269269 $ permissions = "
270270 CREATE TABLE {$ this ->getSQLTable ($ id . '_perms ' )} (
271- _id SERIAL NOT NULL ,
271+ _id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY ,
272272 _tenant INTEGER DEFAULT NULL,
273273 _type VARCHAR(12) NOT NULL,
274274 _permission VARCHAR(255) NOT NULL,
275- _document VARCHAR(255) NOT NULL,
276- PRIMARY KEY (_id)
277- );
275+ _document VARCHAR(255) NOT NULL
276+ );
278277 " ;
279278
280279 if ($ this ->sharedTables ) {
@@ -2015,4 +2014,243 @@ public function getSupportForSpatialAxisOrder(): bool
20152014 {
20162015 return false ;
20172016 }
2017+
2018+ public function decodePoint (string $ wkb ): array
2019+ {
2020+ if (str_starts_with (strtoupper ($ wkb ), 'POINT( ' )) {
2021+ $ start = strpos ($ wkb , '( ' ) + 1 ;
2022+ $ end = strrpos ($ wkb , ') ' );
2023+ $ inside = substr ($ wkb , $ start , $ end - $ start );
2024+
2025+ $ coords = explode (' ' , trim ($ inside ));
2026+ return [(float )$ coords [0 ], (float )$ coords [1 ]];
2027+ }
2028+
2029+ $ bin = hex2bin ($ wkb );
2030+ if ($ bin === false ) {
2031+ throw new DatabaseException ('Invalid hex WKB string ' );
2032+ }
2033+
2034+ if (strlen ($ bin ) < 13 ) { // 1 byte endian + 4 bytes type + 8 bytes for X
2035+ throw new DatabaseException ('WKB too short ' );
2036+ }
2037+
2038+ $ isLE = ord ($ bin [0 ]) === 1 ;
2039+
2040+ // Type (4 bytes)
2041+ $ typeBytes = substr ($ bin , 1 , 4 );
2042+ if (strlen ($ typeBytes ) !== 4 ) {
2043+ throw new DatabaseException ('Failed to extract type bytes from WKB ' );
2044+ }
2045+
2046+ $ typeArr = unpack ($ isLE ? 'V ' : 'N ' , $ typeBytes );
2047+ if ($ typeArr === false || !isset ($ typeArr [1 ])) {
2048+ throw new DatabaseException ('Failed to unpack type from WKB ' );
2049+ }
2050+ $ type = $ typeArr [1 ];
2051+
2052+ // Offset to coordinates (skip SRID if present)
2053+ $ offset = 5 + (($ type & 0x20000000 ) ? 4 : 0 );
2054+
2055+ if (strlen ($ bin ) < $ offset + 16 ) { // 16 bytes for X,Y
2056+ throw new DatabaseException ('WKB too short for coordinates ' );
2057+ }
2058+
2059+ $ fmt = $ isLE ? 'e ' : 'E ' ; // little vs big endian double
2060+
2061+ // X coordinate
2062+ $ xArr = unpack ($ fmt , substr ($ bin , $ offset , 8 ));
2063+ if ($ xArr === false || !isset ($ xArr [1 ])) {
2064+ throw new DatabaseException ('Failed to unpack X coordinate ' );
2065+ }
2066+ $ x = (float )$ xArr [1 ];
2067+
2068+ // Y coordinate
2069+ $ yArr = unpack ($ fmt , substr ($ bin , $ offset + 8 , 8 ));
2070+ if ($ yArr === false || !isset ($ yArr [1 ])) {
2071+ throw new DatabaseException ('Failed to unpack Y coordinate ' );
2072+ }
2073+ $ y = (float )$ yArr [1 ];
2074+
2075+ return [$ x , $ y ];
2076+ }
2077+
2078+ public function decodeLinestring (mixed $ wkb ): array
2079+ {
2080+ if (str_starts_with (strtoupper ($ wkb ), 'LINESTRING( ' )) {
2081+ $ start = strpos ($ wkb , '( ' ) + 1 ;
2082+ $ end = strrpos ($ wkb , ') ' );
2083+ $ inside = substr ($ wkb , $ start , $ end - $ start );
2084+
2085+ $ points = explode (', ' , $ inside );
2086+ return array_map (function ($ point ) {
2087+ $ coords = explode (' ' , trim ($ point ));
2088+ return [(float )$ coords [0 ], (float )$ coords [1 ]];
2089+ }, $ points );
2090+ }
2091+
2092+ if (ctype_xdigit ($ wkb )) {
2093+ $ wkb = hex2bin ($ wkb );
2094+ if ($ wkb === false ) {
2095+ throw new DatabaseException ("Failed to convert hex WKB to binary. " );
2096+ }
2097+ }
2098+
2099+ if (strlen ($ wkb ) < 9 ) {
2100+ throw new DatabaseException ("WKB too short to be a valid geometry " );
2101+ }
2102+
2103+ $ byteOrder = ord ($ wkb [0 ]);
2104+ if ($ byteOrder === 0 ) {
2105+ throw new DatabaseException ("Big-endian WKB not supported " );
2106+ } elseif ($ byteOrder !== 1 ) {
2107+ throw new DatabaseException ("Invalid byte order in WKB " );
2108+ }
2109+
2110+ // Type + SRID flag
2111+ $ typeField = unpack ('V ' , substr ($ wkb , 1 , 4 ));
2112+ if ($ typeField === false ) {
2113+ throw new DatabaseException ('Failed to unpack the type field from WKB. ' );
2114+ }
2115+
2116+ $ typeField = $ typeField [1 ];
2117+ $ geomType = $ typeField & 0xFF ;
2118+ $ hasSRID = ($ typeField & 0x20000000 ) !== 0 ;
2119+
2120+ if ($ geomType !== 2 ) { // 2 = LINESTRING
2121+ throw new DatabaseException ("Not a LINESTRING geometry type, got {$ geomType }" );
2122+ }
2123+
2124+ $ offset = 5 ;
2125+ if ($ hasSRID ) {
2126+ $ offset += 4 ;
2127+ }
2128+
2129+ $ numPoints = unpack ('V ' , substr ($ wkb , $ offset , 4 ));
2130+ if ($ numPoints === false ) {
2131+ throw new DatabaseException ("Failed to unpack number of points at offset {$ offset }. " );
2132+ }
2133+
2134+ $ numPoints = $ numPoints [1 ];
2135+ $ offset += 4 ;
2136+
2137+ $ points = [];
2138+ for ($ i = 0 ; $ i < $ numPoints ; $ i ++) {
2139+ $ x = unpack ('e ' , substr ($ wkb , $ offset , 8 ));
2140+ if ($ x === false ) {
2141+ throw new DatabaseException ("Failed to unpack X coordinate at offset {$ offset }. " );
2142+ }
2143+
2144+ $ x = (float ) $ x [1 ];
2145+
2146+ $ offset += 8 ;
2147+
2148+ $ y = unpack ('e ' , substr ($ wkb , $ offset , 8 ));
2149+ if ($ y === false ) {
2150+ throw new DatabaseException ("Failed to unpack Y coordinate at offset {$ offset }. " );
2151+ }
2152+
2153+ $ y = (float ) $ y [1 ];
2154+
2155+ $ offset += 8 ;
2156+ $ points [] = [$ x , $ y ];
2157+ }
2158+
2159+ return $ points ;
2160+ }
2161+
2162+ public function decodePolygon (string $ wkb ): array
2163+ {
2164+ // POLYGON((x1,y1),(x2,y2))
2165+ if (str_starts_with ($ wkb , 'POLYGON(( ' )) {
2166+ $ start = strpos ($ wkb , '(( ' ) + 2 ;
2167+ $ end = strrpos ($ wkb , ')) ' );
2168+ $ inside = substr ($ wkb , $ start , $ end - $ start );
2169+
2170+ $ rings = explode ('),( ' , $ inside );
2171+ return array_map (function ($ ring ) {
2172+ $ points = explode (', ' , $ ring );
2173+ return array_map (function ($ point ) {
2174+ $ coords = explode (' ' , trim ($ point ));
2175+ return [(float )$ coords [0 ], (float )$ coords [1 ]];
2176+ }, $ points );
2177+ }, $ rings );
2178+ }
2179+
2180+ // Convert hex string to binary if needed
2181+ if (preg_match ('/^[0-9a-fA-F]+$/ ' , $ wkb )) {
2182+ $ wkb = hex2bin ($ wkb );
2183+ if ($ wkb === false ) {
2184+ throw new DatabaseException ("Invalid hex WKB " );
2185+ }
2186+ }
2187+
2188+ if (strlen ($ wkb ) < 9 ) {
2189+ throw new DatabaseException ("WKB too short " );
2190+ }
2191+
2192+ $ uInt32 = 'V ' ; // little-endian 32-bit unsigned
2193+ $ uDouble = 'd ' ; // little-endian double
2194+
2195+ $ typeInt = unpack ($ uInt32 , substr ($ wkb , 1 , 4 ));
2196+ if ($ typeInt === false ) {
2197+ throw new DatabaseException ('Failed to unpack type field from WKB. ' );
2198+ }
2199+
2200+ $ typeInt = (int ) $ typeInt [1 ];
2201+ $ hasSrid = ($ typeInt & 0x20000000 ) !== 0 ;
2202+ $ geomType = $ typeInt & 0xFF ;
2203+
2204+ if ($ geomType !== 3 ) { // 3 = POLYGON
2205+ throw new DatabaseException ("Not a POLYGON geometry type, got {$ geomType }" );
2206+ }
2207+
2208+ $ offset = 5 ;
2209+ if ($ hasSrid ) {
2210+ $ offset += 4 ;
2211+ }
2212+
2213+ // Number of rings
2214+ $ numRings = unpack ($ uInt32 , substr ($ wkb , $ offset , 4 ));
2215+ if ($ numRings === false ) {
2216+ throw new DatabaseException ('Failed to unpack number of rings from WKB. ' );
2217+ }
2218+
2219+ $ numRings = (int ) $ numRings [1 ];
2220+ $ offset += 4 ;
2221+
2222+ $ rings = [];
2223+ for ($ r = 0 ; $ r < $ numRings ; $ r ++) {
2224+ $ numPoints = unpack ($ uInt32 , substr ($ wkb , $ offset , 4 ));
2225+ if ($ numPoints === false ) {
2226+ throw new DatabaseException ('Failed to unpack number of points from WKB. ' );
2227+ }
2228+
2229+ $ numPoints = (int ) $ numPoints [1 ];
2230+ $ offset += 4 ;
2231+ $ points = [];
2232+ for ($ i = 0 ; $ i < $ numPoints ; $ i ++) {
2233+ $ x = unpack ($ uDouble , substr ($ wkb , $ offset , 8 ));
2234+ if ($ x === false ) {
2235+ throw new DatabaseException ('Failed to unpack X coordinate from WKB. ' );
2236+ }
2237+
2238+ $ x = (float ) $ x [1 ];
2239+
2240+ $ y = unpack ($ uDouble , substr ($ wkb , $ offset + 8 , 8 ));
2241+ if ($ y === false ) {
2242+ throw new DatabaseException ('Failed to unpack Y coordinate from WKB. ' );
2243+ }
2244+
2245+ $ y = (float ) $ y [1 ];
2246+
2247+ $ points [] = [$ x , $ y ];
2248+ $ offset += 16 ;
2249+ }
2250+ $ rings [] = $ points ;
2251+ }
2252+
2253+ return $ rings ; // array of rings, each ring is array of [x,y]
2254+ }
2255+
20182256}
0 commit comments