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 }