1 // Copyright (c) 1999-2018 David Muse
2 // See the file COPYING for more information
3 #include "../../config.h"
4 
5 #include <rudiments/environment.h>
6 
7 #include <sqlcli1.h>
8 
9 #include "sqlrbench.h"
10 
11 class db2bench : public sqlrbench {
12 	public:
13 		db2bench(const char *connectstring,
14 					const char *db,
15 					uint64_t queries,
16 					uint64_t rows,
17 					uint32_t cols,
18 					uint32_t colsize,
19 					uint16_t samples,
20 					uint64_t rsbs,
21 					bool debug);
22 };
23 
24 #define DB2_FETCH_AT_ONCE		10
25 #define DB2_MAX_SELECT_LIST_SIZE	256
26 #define DB2_MAX_ITEM_BUFFER_SIZE	32768
27 
28 struct db2column {
29 	char		name[4096];
30 	uint16_t	namelength;
31 	// SQLColAttribute requires that these are signed, 32 bit integers
32 	int32_t		type;
33 	int32_t		length;
34 	int32_t		precision;
35 	int32_t		scale;
36 	int32_t		nullable;
37 	uint16_t	primarykey;
38 	uint16_t	unique;
39 	uint16_t	partofkey;
40 	uint16_t	unsignednumber;
41 	uint16_t	zerofill;
42 	uint16_t	binary;
43 	uint16_t	autoincrement;
44 };
45 
46 class db2benchconnection : public sqlrbenchconnection {
47 	friend class db2benchcursor;
48 	public:
49 			db2benchconnection(const char *connectstring,
50 						const char *dbtype);
51 
52 		bool	connect();
53 		bool	disconnect();
54 
55 	private:
56 		const char	*dbname;
57 		const char	*lang;
58 		const char	*user;
59 		const char	*password;
60 
61 		SQLHENV		env;
62 		SQLRETURN	erg;
63 		SQLHDBC		dbc;
64 };
65 
66 class db2benchcursor : public sqlrbenchcursor {
67 	public:
68 			db2benchcursor(sqlrbenchconnection *con);
69 
70 		bool	open();
71 		bool	query(const char *query, bool getcolumns);
72 		bool	close();
73 
74 	private:
75 		db2benchconnection	*db2bcon;
76 
77 		SQLHSTMT	stmt;
78 		SQLRETURN	erg;
79 		SQLSMALLINT	ncols;
80 
81 		db2column	column[DB2_MAX_SELECT_LIST_SIZE];
82 		char		field[DB2_MAX_SELECT_LIST_SIZE]
83 						[DB2_FETCH_AT_ONCE]
84 						[DB2_MAX_ITEM_BUFFER_SIZE];
85 		SQLINTEGER	indicator[DB2_MAX_SELECT_LIST_SIZE]
86 						[DB2_FETCH_AT_ONCE];
87 		#if (DB2VERSION>7)
88 		SQLUSMALLINT	rowstat[DB2_FETCH_AT_ONCE];
89 		#endif
90 };
91 
db2bench(const char * connectstring,const char * db,uint64_t queries,uint64_t rows,uint32_t cols,uint32_t colsize,uint16_t samples,uint64_t rsbs,bool debug)92 db2bench::db2bench(const char *connectstring,
93 					const char *db,
94 					uint64_t queries,
95 					uint64_t rows,
96 					uint32_t cols,
97 					uint32_t colsize,
98 					uint16_t samples,
99 					uint64_t rsbs,
100 					bool debug) :
101 					sqlrbench(connectstring,db,
102 						queries,rows,cols,colsize,
103 						samples,rsbs,debug) {
104 	con=new db2benchconnection(connectstring,db);
105 	cur=new db2benchcursor(con);
106 }
107 
db2benchconnection(const char * connectstring,const char * db)108 db2benchconnection::db2benchconnection(
109 				const char *connectstring,
110 				const char *db) :
111 				sqlrbenchconnection(connectstring,db) {
112 	dbname=getParam("db");
113 	lang=getParam("lang");
114 	user=getParam("user");
115 	password=getParam("password");
116 
117 	environment::setValue("LANG",lang);
118 }
119 
connect()120 bool db2benchconnection::connect() {
121 	erg=SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&env);
122 	if (erg!=SQL_SUCCESS && erg!=SQL_SUCCESS_WITH_INFO) {
123 		stdoutput.printf("SQLAllocHandle (ENV) failed\n");
124 		return false;
125 	}
126 	erg=SQLAllocHandle(SQL_HANDLE_DBC,env,&dbc);
127 	if (erg!=SQL_SUCCESS && erg!=SQL_SUCCESS_WITH_INFO) {
128 		stdoutput.printf("SQLAllocHandle (DBC) failed\n");
129 		return false;
130 	}
131 	erg=SQLConnect(dbc,(SQLCHAR *)dbname,SQL_NTS,
132 				(SQLCHAR *)user,SQL_NTS,
133 				(SQLCHAR *)password,SQL_NTS);
134 	if (erg!=SQL_SUCCESS && erg!=SQL_SUCCESS_WITH_INFO) {
135 		stdoutput.printf("SQLConnect failed\n");
136 		return false;
137 	}
138 	return true;
139 }
140 
disconnect()141 bool db2benchconnection::disconnect() {
142 	SQLDisconnect(dbc);
143 	SQLFreeHandle(SQL_HANDLE_DBC,dbc);
144 	SQLFreeHandle(SQL_HANDLE_ENV,env);
145 	return true;
146 }
147 
db2benchcursor(sqlrbenchconnection * con)148 db2benchcursor::db2benchcursor(sqlrbenchconnection *con) :
149 						sqlrbenchcursor(con) {
150 	db2bcon=(db2benchconnection *)con;
151 }
152 
open()153 bool db2benchcursor::open() {
154 
155 	erg=SQLAllocHandle(SQL_HANDLE_STMT,db2bcon->dbc,&stmt);
156 	if (erg!=SQL_SUCCESS && erg!=SQL_SUCCESS_WITH_INFO) {
157 		stdoutput.printf("SQLAllocHandle (STMT) failed\n");
158 		return false;
159 	}
160 	erg=SQLSetStmtAttr(stmt,SQL_ATTR_ROW_ARRAY_SIZE,
161 				(SQLPOINTER)DB2_FETCH_AT_ONCE,0);
162 	if (erg!=SQL_SUCCESS && erg!=SQL_SUCCESS_WITH_INFO) {
163 		stdoutput.printf("SQLSetStmtAttr (ROW_ARRAY_SIZE) failed\n");
164 		return false;
165 	}
166 
167 	#if (DB2VERSION>7)
168 	erg=SQLSetStmtAttr(stmt,SQL_ATTR_ROW_STATUS_PTR,
169 					(SQLPOINTER)rowstat,0);
170 	if (erg!=SQL_SUCCESS && erg!=SQL_SUCCESS_WITH_INFO) {
171 		stdoutput.printf("SQLSetStmtAttr (ROW_STATUS_PTR) failed\n");
172 		return false;
173 	}
174 	#endif
175 
176 	return true;
177 }
178 
query(const char * query,bool getcolumns)179 bool db2benchcursor::query(const char *query, bool getcolumns) {
180 
181 	erg=SQLPrepare(stmt,(SQLCHAR *)query,charstring::length(query));
182 	if (erg!=SQL_SUCCESS && erg!=SQL_SUCCESS_WITH_INFO) {
183 		stdoutput.printf("SQLPrepare failed\n");
184 		return false;
185 	}
186 	erg=SQLExecute(stmt);
187 	if (erg!=SQL_SUCCESS && erg!=SQL_SUCCESS_WITH_INFO) {
188 		stdoutput.printf("SQLExecute failed\n");
189 		return false;
190 	}
191 	erg=SQLNumResultCols(stmt,&ncols);
192 	if (erg!=SQL_SUCCESS && erg!=SQL_SUCCESS_WITH_INFO) {
193 		stdoutput.printf("SQLNumResultCols failed\n");
194 		return false;
195 	}
196 
197 	for (SQLSMALLINT i=0; i<ncols; i++) {
198 
199 		if (getcolumns) {
200 
201 			// column name
202 			erg=SQLColAttribute(stmt,i+1,SQL_COLUMN_LABEL,
203 					column[i].name,4096,
204 					(SQLSMALLINT *)&(column[i].namelength),
205 					NULL);
206 			if (erg!=SQL_SUCCESS && erg!=SQL_SUCCESS_WITH_INFO) {
207 				return false;
208 			}
209 
210 			// column length
211 			erg=SQLColAttribute(stmt,i+1,SQL_COLUMN_LENGTH,
212 					NULL,0,NULL,&(column[i].length));
213 			if (erg!=SQL_SUCCESS && erg!=SQL_SUCCESS_WITH_INFO) {
214 				return false;
215 			}
216 
217 			// column type
218 			erg=SQLColAttribute(stmt,i+1,SQL_COLUMN_TYPE,
219 					NULL,0,NULL,&(column[i].type));
220 			if (erg!=SQL_SUCCESS && erg!=SQL_SUCCESS_WITH_INFO) {
221 				return false;
222 			}
223 
224 			// column precision
225 			erg=SQLColAttribute(stmt,i+1,SQL_COLUMN_PRECISION,
226 					NULL,0,NULL,&(column[i].precision));
227 			if (erg!=SQL_SUCCESS && erg!=SQL_SUCCESS_WITH_INFO) {
228 				return false;
229 			}
230 
231 			// column scale
232 			erg=SQLColAttribute(stmt,i+1,SQL_COLUMN_SCALE,
233 					NULL,0,NULL,&(column[i].scale));
234 			if (erg!=SQL_SUCCESS && erg!=SQL_SUCCESS_WITH_INFO) {
235 				return false;
236 			}
237 
238 			// column nullable
239 			erg=SQLColAttribute(stmt,i+1,SQL_COLUMN_NULLABLE,
240 					NULL,0,NULL,&(column[i].nullable));
241 			if (erg!=SQL_SUCCESS && erg!=SQL_SUCCESS_WITH_INFO) {
242 				return false;
243 			}
244 
245 			// unsigned number
246 			erg=SQLColAttribute(stmt,i+1,SQL_COLUMN_UNSIGNED,
247 					NULL,0,NULL,
248 					&(column[i].unsignednumber));
249 			if (erg!=SQL_SUCCESS && erg!=SQL_SUCCESS_WITH_INFO) {
250 				return false;
251 			}
252 
253 			// autoincrement
254 			erg=SQLColAttribute(stmt,i+1,SQL_COLUMN_AUTO_INCREMENT,
255 					NULL,0,NULL,&(column[i].autoincrement));
256 			if (erg!=SQL_SUCCESS && erg!=SQL_SUCCESS_WITH_INFO) {
257 				return false;
258 			}
259 		}
260 
261 		// bind field and null indicator
262 		erg=SQLBindCol(stmt,i+1,SQL_C_CHAR,
263 				field[i],DB2_MAX_ITEM_BUFFER_SIZE,
264 				(SQLINTEGER *)indicator[i]);
265 		if (erg!=SQL_SUCCESS && erg!=SQL_SUCCESS_WITH_INFO) {
266 			stdoutput.printf("SQLBindCol failed\n");
267 			return false;
268 		}
269 	}
270 
271 	// run through the cols
272 
273 	// run through the rows
274 	if (ncols) {
275 
276 		int32_t	oldrow=0;
277 		int32_t	rownumber=0;
278 		int32_t	totalrows=0;
279 		for (;;) {
280 			erg=SQLFetchScroll(stmt,SQL_FETCH_NEXT,0);
281 			if (erg!=SQL_SUCCESS && erg!=SQL_SUCCESS_WITH_INFO) {
282 				break;
283 			}
284 
285 			#if (DB2VERSION>7)
286 			// An apparant bug in version 8.1 causes the
287 			// SQL_ATTR_ROW_NUMBER to always be 1, running through
288 			// the row status buffer appears to work though.
289 			uint32_t	index=0;
290 			while (index<DB2_FETCH_AT_ONCE &&
291 				(rowstat[index]==SQL_ROW_SUCCESS ||
292 				rowstat[index]==SQL_ROW_SUCCESS_WITH_INFO)) {
293 				index++;
294 			}
295 			rownumber=totalrows+index;
296 			#else
297 			SQLGetStmtAttr(stmt,SQL_ATTR_ROW_NUMBER,
298 					(SQLPOINTER)&rownumber,0,NULL);
299 			#endif
300 
301 			if (rownumber==oldrow) {
302 				break;
303 			}
304 
305 			for (int32_t row=0; row<rownumber-oldrow; row++) {
306 				for (SQLSMALLINT col=0; col<ncols; col++) {
307 
308 					if (indicator[col][row]==
309 							SQL_NULL_DATA) {
310 						//stdoutput.printf("NULL,");
311 					} else {
312 						//stdoutput.printf(
313 							//"%s,",field[col][row]);
314 					}
315 				}
316 				//stdoutput.printf("\n");
317 			}
318 			oldrow=rownumber;
319 			totalrows=rownumber;
320 		}
321 	}
322 
323 	SQLCloseCursor(stmt);
324 	return true;
325 }
326 
close()327 bool db2benchcursor::close() {
328 	SQLFreeHandle(SQL_HANDLE_STMT,stmt);
329 	return true;
330 }
331 
332 extern "C" {
new_db2bench(const char * connectstring,const char * db,uint64_t queries,uint64_t rows,uint32_t cols,uint32_t colsize,uint16_t samples,uint64_t rsbs,bool debug)333 	sqlrbench *new_db2bench(const char *connectstring,
334 						const char *db,
335 						uint64_t queries,
336 						uint64_t rows,
337 						uint32_t cols,
338 						uint32_t colsize,
339 						uint16_t samples,
340 						uint64_t rsbs,
341 						bool debug) {
342 		return new db2bench(connectstring,db,queries,
343 						rows,cols,colsize,
344 						samples,rsbs,debug);
345 	}
346 }
347