core/smn_database.cpp
author David Anderson <dvander@alliedmods.net>
Tue Jul 03 20:46:17 2007 +0000 (2007-07-03)
changeset 1036 2a073d5b3454
parent 1025 2baaa750e2b7
child 1096 8e11b2ebdc22
permissions -rw-r--r--
added amb505
     1 #include "sm_globals.h"
     2 #include "HandleSys.h"
     3 #include "Database.h"
     4 #include "ExtensionSys.h"
     5 #include "PluginSys.h"
     6 
     7 HandleType_t hQueryType;
     8 HandleType_t hStmtType;
     9 
    10 class DatabaseHelpers : 
    11 	public SMGlobalClass,
    12 	public IHandleTypeDispatch
    13 {
    14 public:
    15 	virtual void OnSourceModAllInitialized()
    16 	{
    17 		HandleAccess acc;
    18 
    19 		/* Disable cloning */
    20 		g_HandleSys.InitAccessDefaults(NULL, &acc);
    21 		acc.access[HandleAccess_Clone] = HANDLE_RESTRICT_OWNER|HANDLE_RESTRICT_IDENTITY;
    22 
    23 		TypeAccess tacc;
    24 
    25 		g_HandleSys.InitAccessDefaults(&tacc, NULL);
    26 		tacc.ident = g_pCoreIdent;
    27 
    28 		hQueryType = g_HandleSys.CreateType("IQuery", this, 0, &tacc, &acc, g_pCoreIdent, NULL);
    29 		hStmtType = g_HandleSys.CreateType("IPreparedQuery", this, hQueryType, &tacc, &acc, g_pCoreIdent, NULL);
    30 	}
    31 
    32 	virtual void OnSourceModShutdown()
    33 	{
    34 		g_HandleSys.RemoveType(hStmtType, g_pCoreIdent);
    35 		g_HandleSys.RemoveType(hQueryType, g_pCoreIdent);
    36 	}
    37 
    38 	virtual void OnHandleDestroy(HandleType_t type, void *object)
    39 	{
    40 		if (type == hQueryType)
    41 		{
    42 			IQuery *query = (IQuery *)object;
    43 			query->Destroy();
    44 		} else if (type == hStmtType) {
    45 			IPreparedQuery *query = (IPreparedQuery *)object;
    46 			query->Destroy();
    47 		}
    48 	}
    49 } s_DatabaseNativeHelpers;
    50 
    51 //is this safe for stmt handles? i think since it's single inheritance, it always will be.
    52 inline HandleError ReadQueryHndl(Handle_t hndl, IPluginContext *pContext, IQuery **query)
    53 {
    54 	HandleSecurity sec;
    55 	sec.pOwner = pContext->GetIdentity();
    56 	sec.pIdentity = g_pCoreIdent;
    57 
    58 	return g_HandleSys.ReadHandle(hndl, hQueryType, &sec, (void **)query);
    59 }
    60 
    61 inline HandleError ReadStmtHndl(Handle_t hndl, IPluginContext *pContext, IPreparedQuery **query)
    62 {
    63 	HandleSecurity sec;
    64 	sec.pOwner = pContext->GetIdentity();
    65 	sec.pIdentity = g_pCoreIdent;
    66 
    67 	return g_HandleSys.ReadHandle(hndl, hStmtType, &sec, (void **)query);
    68 }
    69 
    70 inline HandleError ReadDbOrStmtHndl(Handle_t hndl, IPluginContext *pContext, IDatabase **db, IPreparedQuery **query)
    71 {
    72 	HandleError err;
    73 	if ((err = g_DBMan.ReadHandle(hndl, DBHandle_Database, (void **)db)) == HandleError_Type)
    74 	{
    75 		*db = NULL;
    76 		return ReadStmtHndl(hndl, pContext, query);
    77 	}
    78 	return err;
    79 }
    80 
    81 static cell_t SQL_Connect(IPluginContext *pContext, const cell_t *params)
    82 {
    83 	char *conf, *err;
    84 
    85 	size_t maxlength = (size_t)params[4];
    86 	bool persistent = params[2] ? true : false;
    87 	pContext->LocalToString(params[1], &conf);
    88 	pContext->LocalToString(params[3], &err);
    89 	
    90 	IDBDriver *driver;
    91 	IDatabase *db;
    92 	if (!g_DBMan.Connect(conf, &driver, &db, persistent, err, maxlength))
    93 	{
    94 		return BAD_HANDLE;
    95 	}
    96 
    97 	Handle_t hndl = g_DBMan.CreateHandle(DBHandle_Database, db, pContext->GetIdentity());
    98 	if (!hndl)
    99 	{
   100 		db->Close();
   101 		return BAD_HANDLE;
   102 	}
   103 
   104 	/* HACK! Add us to the dependency list */
   105 	CExtension *pExt = g_Extensions.GetExtensionFromIdent(driver->GetIdentity());
   106 	if (pExt)
   107 	{
   108 		g_Extensions.BindChildPlugin(pExt, g_PluginSys.FindPluginByContext(pContext->GetContext()));
   109 	}
   110 
   111 	return hndl;
   112 }
   113 
   114 static cell_t SQL_ConnectEx(IPluginContext *pContext, const cell_t *params)
   115 {
   116 	IDBDriver *driver;
   117 	if (params[1] == BAD_HANDLE)
   118 	{
   119 		if ((driver = g_DBMan.GetDefaultDriver()) == NULL)
   120 		{
   121 			return pContext->ThrowNativeError("Could not find any default driver");
   122 		}
   123 	} else {
   124 		HandleError err;
   125 		if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Driver, (void **)&driver))
   126 			!= HandleError_None)
   127 		{
   128 			return pContext->ThrowNativeError("Invalid driver Handle %x (error: %d)", params[1], err);
   129 		}
   130 	}
   131  
   132 	char *host, *user, *pass, *database, *error;
   133 	size_t maxlength = (size_t)params[7];
   134 	bool persistent = params[8] ? true : false;
   135 	unsigned int port = params[9];
   136 	unsigned int maxTimeout = params[10];
   137 	pContext->LocalToString(params[2], &host);
   138 	pContext->LocalToString(params[3], &user);
   139 	pContext->LocalToString(params[4], &pass);
   140 	pContext->LocalToString(params[5], &database);
   141 	pContext->LocalToString(params[6], &error);
   142 
   143 	DatabaseInfo info;
   144 	info.database = database;
   145 	info.driver = driver->GetIdentifier();
   146 	info.host = host;
   147 	info.maxTimeout = maxTimeout;
   148 	info.pass = pass;
   149 	info.port = port;
   150 	info.user = user;
   151 	
   152 	IDatabase *db = driver->Connect(&info, persistent, error, maxlength);
   153 
   154 	if (db)
   155 	{
   156 		Handle_t hndl = g_DBMan.CreateHandle(DBHandle_Database, db, pContext->GetIdentity());
   157 		if (!hndl)
   158 		{
   159 			db->Close();
   160 			return BAD_HANDLE;
   161 		}
   162 
   163 		/* HACK! Add us to the dependency list */
   164 		CExtension *pExt = g_Extensions.GetExtensionFromIdent(driver->GetIdentity());
   165 		if (pExt)
   166 		{
   167 			g_Extensions.BindChildPlugin(pExt, g_PluginSys.FindPluginByContext(pContext->GetContext()));
   168 		}
   169 
   170 		return hndl;
   171 	}
   172 	
   173 	return BAD_HANDLE;
   174 }
   175 
   176 static cell_t SQL_GetDriverIdent(IPluginContext *pContext, const cell_t *params)
   177 {
   178 	IDBDriver *driver;
   179 	if (params[1] == BAD_HANDLE)
   180 	{
   181 		if ((driver = g_DBMan.GetDefaultDriver()) == NULL)
   182 		{
   183 			return pContext->ThrowNativeError("Could not find any default driver");
   184 		}
   185 	} else {
   186 		HandleError err;
   187 		if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Driver, (void **)&driver))
   188 			!= HandleError_None)
   189 		{
   190 			return pContext->ThrowNativeError("Invalid driver Handle %x (error: %d)", params[1], err);
   191 		}
   192 	}
   193 
   194 	pContext->StringToLocalUTF8(params[2], params[3], driver->GetIdentifier(), NULL);
   195 
   196 	return 1;
   197 }
   198 
   199 static cell_t SQL_GetDriver(IPluginContext *pContext, const cell_t *params)
   200 {
   201 	char *name;
   202 	pContext->LocalToString(params[1], &name);
   203 
   204 	IDBDriver *driver = NULL;
   205 	if (name[0] == '\0')
   206 	{
   207 		driver = g_DBMan.GetDefaultDriver();
   208 	} else {
   209 		driver = g_DBMan.FindOrLoadDriver(name);
   210 	}
   211 
   212 	return (driver != NULL) ? driver->GetHandle() : BAD_HANDLE;
   213 }
   214 
   215 static cell_t SQL_GetDriverProduct(IPluginContext *pContext, const cell_t *params)
   216 {
   217 	IDBDriver *driver;
   218 	if (params[1] == BAD_HANDLE)
   219 	{
   220 		if ((driver = g_DBMan.GetDefaultDriver()) == NULL)
   221 		{
   222 			return pContext->ThrowNativeError("Could not find any default driver");
   223 		}
   224 	} else {
   225 		HandleError err;
   226 		if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Driver, (void **)&driver))
   227 			!= HandleError_None)
   228 		{
   229 			return pContext->ThrowNativeError("Invalid driver Handle %x (error: %d)", params[1], err);
   230 		}
   231 	}
   232 
   233 	pContext->StringToLocalUTF8(params[2], params[3], driver->GetProductName(), NULL);
   234 
   235 	return 1;
   236 }
   237 
   238 static cell_t SQL_GetAffectedRows(IPluginContext *pContext, const cell_t *params)
   239 {
   240 	IDatabase *db = NULL;
   241 	IPreparedQuery *stmt = NULL;
   242 	HandleError err;
   243 
   244 	if ((err = ReadDbOrStmtHndl(params[1], pContext, &db, &stmt)) != HandleError_None)
   245 	{
   246 		return pContext->ThrowNativeError("Invalid statement or db Handle %x (error: %d)", params[1], err);
   247 	}
   248 
   249 	if (db)
   250 	{
   251 		return db->GetAffectedRows();
   252 	} else if (stmt) {
   253 		return stmt->GetAffectedRows();
   254 	}
   255 
   256 	return pContext->ThrowNativeError("Unknown error reading db/stmt handles");
   257 }
   258 
   259 static cell_t SQL_GetInsertId(IPluginContext *pContext, const cell_t *params)
   260 {
   261 	IDatabase *db = NULL;
   262 	IPreparedQuery *stmt = NULL;
   263 	HandleError err;
   264 
   265 	if ((err = ReadDbOrStmtHndl(params[1], pContext, &db, &stmt)) != HandleError_None)
   266 	{
   267 		return pContext->ThrowNativeError("Invalid statement or db Handle %x (error: %d)", params[1], err);
   268 	}
   269 
   270 	if (db)
   271 	{
   272 		return db->GetInsertID();
   273 	} else if (stmt) {
   274 		return stmt->GetInsertID();
   275 	}
   276 
   277 	return pContext->ThrowNativeError("Unknown error reading db/stmt handles");
   278 }
   279 
   280 static cell_t SQL_GetError(IPluginContext *pContext, const cell_t *params)
   281 {
   282 	IDatabase *db = NULL;
   283 	IPreparedQuery *stmt = NULL;
   284 	HandleError err;
   285 
   286 	if ((err = ReadDbOrStmtHndl(params[1], pContext, &db, &stmt)) != HandleError_None)
   287 	{
   288 		return pContext->ThrowNativeError("Invalid statement or db Handle %x (error: %d)", params[1], err);
   289 	}
   290 
   291 	const char *error = "";
   292 	if (db)
   293 	{
   294 		error = db->GetError();
   295 	} else if (stmt) {
   296 		error = stmt->GetError();
   297 	}
   298 
   299 	if (error[0] == '\0')
   300 	{
   301 		return false;
   302 	}
   303 
   304 	pContext->StringToLocalUTF8(params[2], params[3], error, NULL);
   305 
   306 	return 1;
   307 }
   308 
   309 static cell_t SQL_QuoteString(IPluginContext *pContext, const cell_t *params)
   310 {
   311 	IDatabase *db = NULL;
   312 	HandleError err;
   313 
   314 	if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Database, (void **)&db))
   315 		!= HandleError_None)
   316 	{
   317 		return pContext->ThrowNativeError("Invalid database Handle %x (error: %d)", params[1], err);
   318 	}
   319 
   320 	char *input, *output;
   321 	size_t maxlength = (size_t)params[4];
   322 	pContext->LocalToString(params[2], &input);
   323 	pContext->LocalToString(params[3], &output);
   324 
   325 	size_t written;
   326 	bool s = db->QuoteString(input, output, maxlength, &written);
   327 
   328 	cell_t *addr;
   329 	pContext->LocalToPhysAddr(params[5], &addr);
   330 	*addr = (cell_t)written;
   331 
   332 	return s ? 1 : 0;
   333 }
   334 
   335 static cell_t SQL_FastQuery(IPluginContext *pContext, const cell_t *params)
   336 {
   337 	IDatabase *db = NULL;
   338 	HandleError err;
   339 
   340 	if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Database, (void **)&db))
   341 		!= HandleError_None)
   342 	{
   343 		return pContext->ThrowNativeError("Invalid database Handle %x (error: %d)", params[1], err);
   344 	}
   345 
   346 	char *query;
   347 	pContext->LocalToString(params[2], &query);
   348 
   349 	return db->DoSimpleQuery(query) ? 1 : 0;
   350 }
   351 
   352 static cell_t SQL_Query(IPluginContext *pContext, const cell_t *params)
   353 {
   354 	IDatabase *db = NULL;
   355 	HandleError err;
   356 
   357 	if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Database, (void **)&db))
   358 		!= HandleError_None)
   359 	{
   360 		return pContext->ThrowNativeError("Invalid database Handle %x (error: %d)", params[1], err);
   361 	}
   362 
   363 	char *query;
   364 	pContext->LocalToString(params[2], &query);
   365 
   366 	IQuery *qr = db->DoQuery(query);
   367 
   368 	if (!qr)
   369 	{
   370 		return BAD_HANDLE;
   371 	}
   372 
   373 	Handle_t hndl = g_HandleSys.CreateHandle(hQueryType, qr, pContext->GetIdentity(), g_pCoreIdent, NULL);
   374 	if (hndl == BAD_HANDLE)
   375 	{
   376 		qr->Destroy();
   377 		return BAD_HANDLE;
   378 	}
   379 
   380 	return hndl;
   381 }
   382 
   383 static cell_t SQL_PrepareQuery(IPluginContext *pContext, const cell_t *params)
   384 {
   385 	IDatabase *db = NULL;
   386 	HandleError err;
   387 
   388 	if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Database, (void **)&db))
   389 		!= HandleError_None)
   390 	{
   391 		return pContext->ThrowNativeError("Invalid database Handle %x (error: %d)", params[1], err);
   392 	}
   393 
   394 	char *query, *error;
   395 	size_t maxlength = (size_t)params[4];
   396 	pContext->LocalToString(params[2], &query);
   397 	pContext->LocalToString(params[3], &error);
   398 
   399 	IPreparedQuery *qr = db->PrepareQuery(query, error, maxlength);
   400 
   401 	if (!qr)
   402 	{
   403 		return BAD_HANDLE;
   404 	}
   405 
   406 	Handle_t hndl = g_HandleSys.CreateHandle(hStmtType, qr, pContext->GetIdentity(), g_pCoreIdent, NULL);
   407 	if (hndl == BAD_HANDLE)
   408 	{
   409 		qr->Destroy();
   410 		return BAD_HANDLE;
   411 	}
   412 
   413 	return hndl;
   414 }
   415 
   416 static cell_t SQL_FetchMoreResults(IPluginContext *pContext, const cell_t *params)
   417 {
   418 	IQuery *query;
   419 	HandleError err;
   420 
   421 	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
   422 	{
   423 		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
   424 	}
   425 
   426 	return query->FetchMoreResults() ? 1 : 0;
   427 }
   428 
   429 static cell_t SQL_HasResultSet(IPluginContext *pContext, const cell_t *params)
   430 {
   431 	IQuery *query;
   432 	HandleError err;
   433 
   434 	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
   435 	{
   436 		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
   437 	}
   438 
   439 	return query->GetResultSet() != NULL ? true : false;
   440 }
   441 
   442 static cell_t SQL_GetRowCount(IPluginContext *pContext, const cell_t *params)
   443 {
   444 	IQuery *query;
   445 	HandleError err;
   446 
   447 	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
   448 	{
   449 		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
   450 	}
   451 
   452 	IResultSet *rs = query->GetResultSet();
   453 	if (!rs)
   454 	{
   455 		return 0;
   456 	}
   457 
   458 	return rs->GetRowCount();
   459 }
   460 
   461 static cell_t SQL_GetFieldCount(IPluginContext *pContext, const cell_t *params)
   462 {
   463 	IQuery *query;
   464 	HandleError err;
   465 
   466 	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
   467 	{
   468 		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
   469 	}
   470 
   471 	IResultSet *rs = query->GetResultSet();
   472 	if (!rs)
   473 	{
   474 		return 0;
   475 	}
   476 
   477 	return rs->GetFieldCount();
   478 }
   479 
   480 static cell_t SQL_FieldNumToName(IPluginContext *pContext, const cell_t *params)
   481 {
   482 	IQuery *query;
   483 	HandleError err;
   484 
   485 	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
   486 	{
   487 		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
   488 	}
   489 
   490 	IResultSet *rs = query->GetResultSet();
   491 	if (!rs)
   492 	{
   493 		return pContext->ThrowNativeError("No current result set");
   494 	}
   495 
   496 	unsigned int field = params[2];
   497 
   498 	const char *fldname;
   499 	if ((fldname = rs->FieldNumToName(field)) == NULL)
   500 	{
   501 		return pContext->ThrowNativeError("Invalid field index %d", field);
   502 	}
   503 
   504 	pContext->StringToLocalUTF8(params[3], params[4], fldname, NULL);
   505 
   506 	return 1;
   507 }
   508 
   509 static cell_t SQL_FieldNameToNum(IPluginContext *pContext, const cell_t *params)
   510 {
   511 	IQuery *query;
   512 	HandleError err;
   513 
   514 	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
   515 	{
   516 		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
   517 	}
   518 
   519 	IResultSet *rs = query->GetResultSet();
   520 	if (!rs)
   521 	{
   522 		return pContext->ThrowNativeError("No current result set");
   523 	}
   524 
   525 	char *field;
   526 	pContext->LocalToString(params[2], &field);
   527 
   528 	cell_t *num;
   529 	pContext->LocalToPhysAddr(params[3], &num);
   530 
   531 	return rs->FieldNameToNum(field, (unsigned int *)num) ? 1 : 0;
   532 }
   533 
   534 static cell_t SQL_FetchRow(IPluginContext *pContext, const cell_t *params)
   535 {
   536 	IQuery *query;
   537 	HandleError err;
   538 
   539 	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
   540 	{
   541 		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
   542 	}
   543 
   544 	IResultSet *rs = query->GetResultSet();
   545 	if (!rs)
   546 	{
   547 		return pContext->ThrowNativeError("No current result set");
   548 	}
   549 
   550 	return (rs->FetchRow() != NULL) ? true : false;
   551 }
   552 
   553 static cell_t SQL_MoreRows(IPluginContext *pContext, const cell_t *params)
   554 {
   555 	IQuery *query;
   556 	HandleError err;
   557 
   558 	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
   559 	{
   560 		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
   561 	}
   562 
   563 	IResultSet *rs = query->GetResultSet();
   564 	if (!rs)
   565 	{
   566 		return pContext->ThrowNativeError("No current result set");
   567 	}
   568 
   569 	return rs->MoreRows() ? 1 : 0;
   570 }
   571 
   572 static cell_t SQL_Rewind(IPluginContext *pContext, const cell_t *params)
   573 {
   574 	IQuery *query;
   575 	HandleError err;
   576 
   577 	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
   578 	{
   579 		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
   580 	}
   581 
   582 	IResultSet *rs = query->GetResultSet();
   583 	if (!rs)
   584 	{
   585 		return pContext->ThrowNativeError("No current result set");
   586 	}
   587 
   588 	return rs->Rewind() ? 1 : 0;
   589 }
   590 
   591 static cell_t SQL_FetchString(IPluginContext *pContext, const cell_t *params)
   592 {
   593 	IQuery *query;
   594 	HandleError err;
   595 
   596 	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
   597 	{
   598 		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
   599 	}
   600 
   601 	IResultSet *rs = query->GetResultSet();
   602 	if (!rs)
   603 	{
   604 		return pContext->ThrowNativeError("No current result set");
   605 	}
   606 
   607 	IResultRow *row = rs->CurrentRow();
   608 	if (!row)
   609 	{
   610 		return pContext->ThrowNativeError("Current result set has no fetched rows");
   611 	}
   612 
   613 	const char *str;
   614 	size_t length;
   615 	DBResult res = row->GetString(params[2], &str, &length);
   616 
   617 	if (res == DBVal_Error)
   618 	{
   619 		return pContext->ThrowNativeError("Error fetching data from field %d", params[2]);
   620 	} else if (res == DBVal_TypeMismatch) {
   621 		return pContext->ThrowNativeError("Could not fetch data in field %d as a string", params[2]);
   622 	}
   623 
   624 	pContext->StringToLocalUTF8(params[3], params[4], str, &length);
   625 
   626 	cell_t *addr;
   627 	pContext->LocalToPhysAddr(params[5], &addr);
   628 	*addr = (cell_t)res;
   629 
   630 	return (cell_t)length;
   631 }
   632 
   633 static cell_t SQL_FetchFloat(IPluginContext *pContext, const cell_t *params)
   634 {
   635 	IQuery *query;
   636 	HandleError err;
   637 
   638 	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
   639 	{
   640 		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
   641 	}
   642 
   643 	IResultSet *rs = query->GetResultSet();
   644 	if (!rs)
   645 	{
   646 		return pContext->ThrowNativeError("No current result set");
   647 	}
   648 
   649 	IResultRow *row = rs->CurrentRow();
   650 	if (!row)
   651 	{
   652 		return pContext->ThrowNativeError("Current result set has no fetched rows");
   653 	}
   654 
   655 	float f;
   656 	DBResult res = row->GetFloat(params[2], &f);
   657 
   658 	if (res == DBVal_Error)
   659 	{
   660 		return pContext->ThrowNativeError("Error fetching data from field %d", params[2]);
   661 	} else if (res == DBVal_TypeMismatch) {
   662 		return pContext->ThrowNativeError("Could not fetch data in field %d as a float", params[2]);
   663 	}
   664 
   665 	cell_t *addr;
   666 	pContext->LocalToPhysAddr(params[3], &addr);
   667 	*addr = (cell_t)res;
   668 
   669 	return sp_ftoc(f);
   670 }
   671 
   672 static cell_t SQL_FetchInt(IPluginContext *pContext, const cell_t *params)
   673 {
   674 	IQuery *query;
   675 	HandleError err;
   676 
   677 	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
   678 	{
   679 		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
   680 	}
   681 
   682 	IResultSet *rs = query->GetResultSet();
   683 	if (!rs)
   684 	{
   685 		return pContext->ThrowNativeError("No current result set");
   686 	}
   687 
   688 	IResultRow *row = rs->CurrentRow();
   689 	if (!row)
   690 	{
   691 		return pContext->ThrowNativeError("Current result set has no fetched rows");
   692 	}
   693 
   694 	int iv;
   695 	DBResult res = row->GetInt(params[2], &iv);
   696 
   697 	if (res == DBVal_Error)
   698 	{
   699 		return pContext->ThrowNativeError("Error fetching data from field %d", params[2]);
   700 	} else if (res == DBVal_TypeMismatch) {
   701 		return pContext->ThrowNativeError("Could not fetch data in field %d as an integer", params[2]);
   702 	}
   703 
   704 	cell_t *addr;
   705 	pContext->LocalToPhysAddr(params[3], &addr);
   706 	*addr = (cell_t)res;
   707 
   708 	return iv;
   709 }
   710 
   711 static cell_t SQL_IsFieldNull(IPluginContext *pContext, const cell_t *params)
   712 {
   713 	IQuery *query;
   714 	HandleError err;
   715 
   716 	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
   717 	{
   718 		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
   719 	}
   720 
   721 	IResultSet *rs = query->GetResultSet();
   722 	if (!rs)
   723 	{
   724 		return pContext->ThrowNativeError("No current result set");
   725 	}
   726 
   727 	IResultRow *row = rs->CurrentRow();
   728 	if (!row)
   729 	{
   730 		return pContext->ThrowNativeError("Current result set has no fetched rows");
   731 	}
   732 
   733 	if ((unsigned)params[2] >= rs->GetFieldCount())
   734 	{
   735 		return pContext->ThrowNativeError("Invalid field index %d", params[2]);
   736 	}
   737 
   738 	return row->IsNull(params[2]) ? 1 : 0;
   739 }
   740 
   741 static cell_t SQL_FetchSize(IPluginContext *pContext, const cell_t *params)
   742 {
   743 	IQuery *query;
   744 	HandleError err;
   745 
   746 	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
   747 	{
   748 		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
   749 	}
   750 
   751 	IResultSet *rs = query->GetResultSet();
   752 	if (!rs)
   753 	{
   754 		return pContext->ThrowNativeError("No current result set");
   755 	}
   756 
   757 	IResultRow *row = rs->CurrentRow();
   758 	if (!row)
   759 	{
   760 		return pContext->ThrowNativeError("Current result set has no fetched rows");
   761 	}
   762 
   763 	if ((unsigned)params[2] >= rs->GetFieldCount())
   764 	{
   765 		return pContext->ThrowNativeError("Invalid field index %d", params[2]);
   766 	}
   767 
   768 	return row->GetDataSize(params[2]);
   769 }
   770 
   771 static cell_t SQL_BindParamInt(IPluginContext *pContext, const cell_t *params)
   772 {
   773 	IPreparedQuery *stmt;
   774 	HandleError err;
   775 
   776 	if ((err = ReadStmtHndl(params[1], pContext, &stmt)) != HandleError_None)
   777 	{
   778 		return pContext->ThrowNativeError("Invalid statement Handle %x (error: %d)", params[1], err);
   779 	}
   780 
   781 	if (!stmt->BindParamInt(params[2], params[3], params[4] ? true : false))
   782 	{
   783 		return pContext->ThrowNativeError("Could not bind parameter %d as an integer", params[2]);
   784 	}
   785 
   786 	return 1;
   787 }
   788 
   789 static cell_t SQL_BindParamFloat(IPluginContext *pContext, const cell_t *params)
   790 {
   791 	IPreparedQuery *stmt;
   792 	HandleError err;
   793 
   794 	if ((err = ReadStmtHndl(params[1], pContext, &stmt)) != HandleError_None)
   795 	{
   796 		return pContext->ThrowNativeError("Invalid statement Handle %x (error: %d)", params[1], err);
   797 	}
   798 
   799 	if (!stmt->BindParamFloat(params[2], sp_ctof(params[3])))
   800 	{
   801 		return pContext->ThrowNativeError("Could not bind parameter %d as a float", params[2]);
   802 	}
   803 
   804 	return 1;
   805 }
   806 
   807 static cell_t SQL_BindParamString(IPluginContext *pContext, const cell_t *params)
   808 {
   809 	IPreparedQuery *stmt;
   810 	HandleError err;
   811 
   812 	if ((err = ReadStmtHndl(params[1], pContext, &stmt)) != HandleError_None)
   813 	{
   814 		return pContext->ThrowNativeError("Invalid statement Handle %x (error: %d)", params[1], err);
   815 	}
   816 
   817 	char *str;
   818 	pContext->LocalToString(params[3], &str);
   819 
   820 	if (!stmt->BindParamString(params[2], str, params[4] ? true : false))
   821 	{
   822 		return pContext->ThrowNativeError("Could not bind parameter %d as a string", params[2]);
   823 	}
   824 
   825 	return 1;
   826 }
   827 
   828 static cell_t SQL_Execute(IPluginContext *pContext, const cell_t *params)
   829 {
   830 	IPreparedQuery *stmt;
   831 	HandleError err;
   832 
   833 	if ((err = ReadStmtHndl(params[1], pContext, &stmt)) != HandleError_None)
   834 	{
   835 		return pContext->ThrowNativeError("Invalid statement Handle %x (error: %d)", params[1], err);
   836 	}
   837 
   838 	return stmt->Execute() ? 1 : 0;
   839 }
   840 
   841 REGISTER_NATIVES(dbNatives)
   842 {
   843 	{"SQL_BindParamInt",		SQL_BindParamInt},
   844 	{"SQL_BindParamFloat",		SQL_BindParamFloat},
   845 	{"SQL_BindParamString",		SQL_BindParamString},
   846 	{"SQL_Connect",				SQL_Connect},
   847 	{"SQL_ConnectEx",			SQL_ConnectEx},
   848 	{"SQL_Execute",				SQL_Execute},
   849 	{"SQL_FastQuery",			SQL_FastQuery},
   850 	{"SQL_FetchFloat",			SQL_FetchFloat},
   851 	{"SQL_FetchInt",			SQL_FetchInt},
   852 	{"SQL_FetchMoreResults",	SQL_FetchMoreResults},
   853 	{"SQL_FetchRow",			SQL_FetchRow},
   854 	{"SQL_FetchSize",			SQL_FetchSize},
   855 	{"SQL_FetchString",			SQL_FetchString},
   856 	{"SQL_FieldNameToNum",		SQL_FieldNameToNum},
   857 	{"SQL_FieldNumToName",		SQL_FieldNumToName},
   858 	{"SQL_GetAffectedRows",		SQL_GetAffectedRows},
   859 	{"SQL_GetDriver",			SQL_GetDriver},
   860 	{"SQL_GetDriverIdent",		SQL_GetDriverIdent},
   861 	{"SQL_GetDriverProduct",	SQL_GetDriverProduct},
   862 	{"SQL_GetError",			SQL_GetError},
   863 	{"SQL_GetFieldCount",		SQL_GetFieldCount},
   864 	{"SQL_GetInsertId",			SQL_GetInsertId},
   865 	{"SQL_GetRowCount",			SQL_GetRowCount},
   866 	{"SQL_HasResultSet",		SQL_HasResultSet},
   867 	{"SQL_IsFieldNull",			SQL_IsFieldNull},
   868 	{"SQL_MoreRows",			SQL_MoreRows},
   869 	{"SQL_Query",				SQL_Query},
   870 	{"SQL_QuoteString",			SQL_QuoteString},
   871 	{"SQL_PrepareQuery",		SQL_PrepareQuery},
   872 	{"SQL_Rewind",				SQL_Rewind},
   873 	{NULL,						NULL},
   874 };