1 /// Retrieve metadata from a DB. 2 module mysql.metadata; 3 4 import std.conv; 5 import std.datetime; 6 import std.exception; 7 8 import mysql.commands; 9 import mysql.exceptions; 10 import mysql.protocol.sockets; 11 import mysql.result; 12 13 /// A struct to hold column metadata 14 struct ColumnInfo 15 { 16 /// The database that the table having this column belongs to. 17 string schema; 18 /// The table that this column belongs to. 19 string table; 20 /// The name of the column. 21 string name; 22 /// Zero based index of the column within a table row. 23 size_t index; 24 /// Is the default value NULL? 25 bool defaultNull; 26 /// The default value as a string if not NULL 27 string defaultValue; 28 /// Can the column value be set to NULL 29 bool nullable; 30 /// What type is the column - tinyint, char, varchar, blob, date etc 31 string type; 32 /// Capacity in characters, -1L if not applicable 33 long charsMax; 34 /// Capacity in bytes - same as chars if not a unicode table definition, -1L if not applicable. 35 long octetsMax; 36 /// Presentation information for numerics, -1L if not applicable. 37 short numericPrecision; 38 /// Scale information for numerics or NULL, -1L if not applicable. 39 short numericScale; 40 /// Character set, "<NULL>" if not applicable. 41 string charSet; 42 /// Collation, "<NULL>" if not applicable. 43 string collation; 44 /// More detail about the column type, e.g. "int(10) unsigned". 45 string colType; 46 /// Information about the column's key status, blank if none. 47 string key; 48 /// Extra information. 49 string extra; 50 /// Privileges for logged in user. 51 string privileges; 52 /// Any comment that was set at table definition time. 53 string comment; 54 } 55 56 /// A struct to hold stored function metadata 57 struct MySQLProcedure 58 { 59 string db; 60 string name; 61 string type; 62 string definer; 63 DateTime modified; 64 DateTime created; 65 string securityType; 66 string comment; 67 string charSetClient; 68 string collationConnection; 69 string collationDB; 70 } 71 72 /++ 73 Facilities to recover meta-data from a connection 74 75 It is important to bear in mind that the methods provided will only return the 76 information that is available to the connected user. This may well be quite limited. 77 +/ 78 struct MetaData 79 { 80 import mysql.connection; 81 82 private: 83 Connection _con; 84 85 MySQLProcedure[] stored(bool procs) 86 { 87 enforceEx!MYX(_con.currentDB.length, "There is no selected database"); 88 string query = procs ? "SHOW PROCEDURE STATUS WHERE db='": "SHOW FUNCTION STATUS WHERE db='"; 89 query ~= _con.currentDB ~ "'"; 90 91 auto rs = _con.querySet(query); 92 MySQLProcedure[] pa; 93 pa.length = rs.length; 94 foreach (size_t i; 0..rs.length) 95 { 96 MySQLProcedure foo; 97 Row r = rs[i]; 98 foreach (int j; 0..11) 99 { 100 if (r.isNull(j)) 101 continue; 102 auto value = r[j].toString(); 103 switch (j) 104 { 105 case 0: 106 foo.db = value; 107 break; 108 case 1: 109 foo.name = value; 110 break; 111 case 2: 112 foo.type = value; 113 break; 114 case 3: 115 foo.definer = value; 116 break; 117 case 4: 118 foo.modified = r[j].get!(DateTime); 119 break; 120 case 5: 121 foo.created = r[j].get!(DateTime); 122 break; 123 case 6: 124 foo.securityType = value; 125 break; 126 case 7: 127 foo.comment = value; 128 break; 129 case 8: 130 foo.charSetClient = value; 131 break; 132 case 9: 133 foo.collationConnection = value; 134 break; 135 case 10: 136 foo.collationDB = value; 137 break; 138 default: 139 assert(0); 140 } 141 } 142 pa[i] = foo; 143 } 144 return pa; 145 } 146 147 public: 148 this(Connection con) 149 { 150 _con = con; 151 } 152 153 /++ 154 List the available databases 155 156 Note that if you have connected using the credentials of a user with 157 limited permissions you may not get many results. 158 159 Returns: 160 An array of strings 161 +/ 162 string[] databases() 163 { 164 auto rs = _con.querySet("SHOW DATABASES"); 165 string[] dbNames; 166 dbNames.length = rs.length; 167 foreach (size_t i; 0..rs.length) 168 dbNames[i] = rs[i][0].toString(); 169 return dbNames; 170 } 171 172 /++ 173 List the tables in the current database 174 175 Returns: 176 An array of strings 177 +/ 178 string[] tables() 179 { 180 auto rs = _con.querySet("SHOW TABLES"); 181 string[] tblNames; 182 tblNames.length = rs.length; 183 foreach (size_t i; 0..rs.length) 184 tblNames[i] = rs[i][0].toString(); 185 return tblNames; 186 } 187 188 /++ 189 Get column metadata for a table in the current database 190 191 Params: 192 table = The table name 193 Returns: 194 An array of ColumnInfo structs 195 +/ 196 ColumnInfo[] columns(string table) 197 { 198 // Manually specify all fields to avoid problems when newer versions of 199 // the server add or rearrange fields. (Issue #45) 200 string query = 201 "SELECT " ~ 202 " TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME," ~ 203 " COLUMN_NAME, ORDINAL_POSITION, COLUMN_DEFAULT," ~ 204 " IS_NULLABLE, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH," ~ 205 " CHARACTER_OCTET_LENGTH, NUMERIC_PRECISION, NUMERIC_SCALE," ~ 206 " CHARACTER_SET_NAME, COLLATION_NAME, COLUMN_TYPE," ~ 207 " COLUMN_KEY, EXTRA, PRIVILEGES, COLUMN_COMMENT" ~ 208 " FROM information_schema.COLUMNS WHERE" ~ 209 " table_schema='" ~ _con.currentDB ~ "' AND table_name='" ~ table ~ "'"; 210 auto rs = _con.querySet(query); 211 ColumnInfo[] ca; 212 ca.length = rs.length; 213 foreach (size_t i; 0..rs.length) 214 { 215 ColumnInfo col; 216 Row r = rs[i]; 217 for (int j = 1; j < 19; j++) 218 { 219 string t; 220 bool isNull = r.isNull(j); 221 if (!isNull) 222 t = to!string(r[j]); 223 switch (j) 224 { 225 case 1: 226 col.schema = t; 227 break; 228 case 2: 229 col.table = t; 230 break; 231 case 3: 232 col.name = t; 233 break; 234 case 4: 235 if(isNull) 236 col.index = -1; 237 else 238 col.index = cast(size_t)(r[j].coerce!ulong() - 1); 239 //col.index = cast(size_t)(r[j].get!(ulong)-1); 240 break; 241 case 5: 242 if (isNull) 243 col.defaultNull = true; 244 else 245 col.defaultValue = t; 246 break; 247 case 6: 248 if (t == "YES") 249 col.nullable = true; 250 break; 251 case 7: 252 col.type = t; 253 break; 254 case 8: 255 col.charsMax = cast(long)(isNull? -1L: r[j].coerce!(ulong)); 256 break; 257 case 9: 258 col.octetsMax = cast(long)(isNull? -1L: r[j].coerce!(ulong)); 259 break; 260 case 10: 261 col.numericPrecision = cast(short) (isNull? -1: r[j].coerce!(ulong)); 262 break; 263 case 11: 264 col.numericScale = cast(short) (isNull? -1: r[j].coerce!(ulong)); 265 break; 266 case 12: 267 col.charSet = isNull? "<NULL>": t; 268 break; 269 case 13: 270 col.collation = isNull? "<NULL>": t; 271 break; 272 case 14: 273 col.colType = r[j].get!string(); 274 break; 275 case 15: 276 col.key = t; 277 break; 278 case 16: 279 col.extra = t; 280 break; 281 case 17: 282 col.privileges = t; 283 break; 284 case 18: 285 col.comment = t; 286 break; 287 default: 288 break; 289 } 290 } 291 ca[i] = col; 292 } 293 return ca; 294 } 295 296 /// Get list of stored functions in the current database, and their properties 297 MySQLProcedure[] functions() 298 { 299 return stored(false); 300 } 301 302 /// Get list of stored procedures in the current database, and their properties 303 MySQLProcedure[] procedures() 304 { 305 return stored(true); 306 } 307 }