namespace GMap.NET.CacheProviders { #if PostgreSQL using System; using System.Diagnostics; using System.IO; using Npgsql; using NpgsqlTypes; using GMap.NET.MapProviders; /// /// image cache for postgresql server /// public class PostgreSQLPureImageCache : PureImageCache, IDisposable { string connectionString = string.Empty; public string ConnectionString { get { return connectionString; } set { if(connectionString != value) { connectionString = value; if(Initialized) { Dispose(); Initialize(); } } } } NpgsqlCommand cmdInsert; NpgsqlCommand cmdFetch; NpgsqlConnection cnGet; NpgsqlConnection cnSet; bool initialized = false; /// /// is cache initialized /// public bool Initialized { get { lock(this) { return initialized; } } private set { lock(this) { initialized = value; } } } /// /// inits connection to server /// /// public bool Initialize() { lock(this) { if(!Initialized) { #region prepare postgresql & cache table try { // different connections so the multi-thread inserts and selects don't collide on open readers. this.cnGet = new NpgsqlConnection(connectionString); this.cnGet.Open(); this.cnSet = new NpgsqlConnection(connectionString); this.cnSet.Open(); bool tableexists = false; using(NpgsqlCommand cmd = new NpgsqlCommand()) { cmd.CommandText = "SELECT COUNT(*) FROM information_schema.tables WHERE table_name='GMapNETcache'"; cmd.Connection = cnGet; object cnt = cmd.ExecuteScalar(); tableexists = ((long)cnt == 1); } if(!tableexists) { using(NpgsqlCommand cmd = new NpgsqlCommand()) { cmd.Connection = cnGet; // create tile-cache table cmd.CommandText = "CREATE TABLE \"GMapNETcache\" ( \n" + " \"Type\" integer NOT NULL, \n" + " \"Zoom\" integer NOT NULL, \n" + " \"X\" integer NOT NULL, \n" + " \"Y\" integer NOT NULL, \n" + " \"Tile\" bytea NOT NULL, \n" + " CONSTRAINT \"PK_GMapNETcache\" PRIMARY KEY ( \"Type\", \"Zoom\", \"X\", \"Y\" ) )"; cmd.ExecuteNonQuery(); // allows out-of-line storage but not compression of tile data // see http://www.postgresql.org/docs/9.0/static/storage-toast.html cmd.CommandText = "ALTER TABLE \"GMapNETcache\" \n" + " ALTER COLUMN \"Tile\" SET STORAGE EXTERNAL"; cmd.ExecuteNonQuery(); // select pk index for cluster operations cmd.CommandText = "ALTER TABLE \"GMapNETcache\" \n" + " CLUSTER ON \"PK_GMapNETcache\""; cmd.ExecuteNonQuery(); } } this.cmdFetch = new NpgsqlCommand("SELECT \"Tile\" FROM \"GMapNETcache\" WHERE \"X\"=@x AND \"Y\"=@y AND \"Zoom\"=@zoom AND \"Type\"=@type", cnGet); this.cmdFetch.Parameters.Add("@x", NpgsqlDbType.Integer); this.cmdFetch.Parameters.Add("@y", NpgsqlDbType.Integer); this.cmdFetch.Parameters.Add("@zoom", NpgsqlDbType.Integer); this.cmdFetch.Parameters.Add("@type", NpgsqlDbType.Integer); this.cmdFetch.Prepare(); this.cmdInsert = new NpgsqlCommand("INSERT INTO \"GMapNETcache\" ( \"X\", \"Y\", \"Zoom\", \"Type\", \"Tile\" ) VALUES ( @x, @y, @zoom, @type, @tile )", cnSet); this.cmdInsert.Parameters.Add("@x", NpgsqlDbType.Integer); this.cmdInsert.Parameters.Add("@y", NpgsqlDbType.Integer); this.cmdInsert.Parameters.Add("@zoom", NpgsqlDbType.Integer); this.cmdInsert.Parameters.Add("@type", NpgsqlDbType.Integer); this.cmdInsert.Parameters.Add("@tile", NpgsqlDbType.Bytea); this.cmdInsert.Prepare(); Initialized = true; } catch(Exception ex) { this.initialized = false; Debug.WriteLine(ex.Message); } #endregion } return Initialized; } } #region IDisposable Members public void Dispose() { lock(cmdInsert) { if(cmdInsert != null) { cmdInsert.Dispose(); cmdInsert = null; } if(cnSet != null) { cnSet.Dispose(); cnSet = null; } } lock(cmdFetch) { if(cmdFetch != null) { cmdFetch.Dispose(); cmdFetch = null; } if(cnGet != null) { cnGet.Dispose(); cnGet = null; } } Initialized = false; } #endregion #region PureImageCache Members public bool PutImageToCache(byte[] tile, int type, GPoint pos, int zoom) { bool ret = true; if(Initialize()) { try { lock(cmdInsert) { cmdInsert.Parameters["@x"].Value = pos.X; cmdInsert.Parameters["@y"].Value = pos.Y; cmdInsert.Parameters["@zoom"].Value = zoom; cmdInsert.Parameters["@type"].Value = type; cmdInsert.Parameters["@tile"].Value = tile; cmdInsert.ExecuteNonQuery(); } } catch(Exception ex) { Debug.WriteLine(ex.ToString()); ret = false; Dispose(); } } return ret; } public PureImage GetImageFromCache(int type, GPoint pos, int zoom) { PureImage ret = null; if(Initialize()) { try { object odata = null; lock(cmdFetch) { cmdFetch.Parameters["@x"].Value = pos.X; cmdFetch.Parameters["@y"].Value = pos.Y; cmdFetch.Parameters["@zoom"].Value = zoom; cmdFetch.Parameters["@type"].Value = type; odata = cmdFetch.ExecuteScalar(); } if(odata != null && odata != DBNull.Value) { byte[] tile = (byte[])odata; if(tile != null && tile.Length > 0) { if(GMapProvider.TileImageProxy != null) { ret = GMapProvider.TileImageProxy.FromArray(tile); } } tile = null; } } catch(Exception ex) { Debug.WriteLine(ex.ToString()); ret = null; Dispose(); } } return ret; } int PureImageCache.DeleteOlderThan(DateTime date, int ? type) { throw new NotImplementedException(); } #endregion } #endif }