483 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			483 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /**
 | |
|  * vim: set ts=4 :
 | |
|  * =============================================================================
 | |
|  * SourceMod SQLite Extension
 | |
|  * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
 | |
|  * =============================================================================
 | |
|  *
 | |
|  * This program is free software; you can redistribute it and/or modify it under
 | |
|  * the terms of the GNU General Public License, version 3.0, as published by the
 | |
|  * Free Software Foundation.
 | |
|  * 
 | |
|  * This program is distributed in the hope that it will be useful, but WITHOUT
 | |
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | |
|  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | |
|  * details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License along with
 | |
|  * this program.  If not, see <http://www.gnu.org/licenses/>.
 | |
|  *
 | |
|  * As a special exception, AlliedModders LLC gives you permission to link the
 | |
|  * code of this program (as well as its derivative works) to "Half-Life 2," the
 | |
|  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
 | |
|  * by the Valve Corporation.  You must obey the GNU General Public License in
 | |
|  * all respects for all other code used.  Additionally, AlliedModders LLC grants
 | |
|  * this exception to all derivative works.  AlliedModders LLC defines further
 | |
|  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
 | |
|  * or <http://www.sourcemod.net/license.php>.
 | |
|  *
 | |
|  * Version: $Id$
 | |
|  */
 | |
| 
 | |
| #include <stdlib.h>
 | |
| #include "extension.h"
 | |
| #include "SqResults.h"
 | |
| #include "SqQuery.h"
 | |
| 
 | |
| SqResults::SqResults(SqQuery *query) : 
 | |
| 	m_pStmt(query->GetStmt()), m_Strings(1024),
 | |
| 	m_RowCount(0), m_MaxRows(0), m_Rows(NULL),
 | |
| 	m_CurRow(0), m_NextRow(0)
 | |
| {
 | |
| 	m_ColCount = sqlite3_column_count(m_pStmt);
 | |
| 	if (m_ColCount)
 | |
| 	{
 | |
| 		m_ColNames = new String[m_ColCount];
 | |
| 		for (unsigned int i=0; i<m_ColCount; i++)
 | |
| 		{
 | |
| 			m_ColNames[i].assign(sqlite3_column_name(m_pStmt, i));
 | |
| 		}
 | |
| 	} else {
 | |
| 		m_ColNames = NULL;
 | |
| 	}
 | |
| 
 | |
| 	m_pMemory = m_Strings.GetMemTable();
 | |
| }
 | |
| 
 | |
| SqResults::~SqResults()
 | |
| {
 | |
| 	delete [] m_ColNames;
 | |
| 	free(m_Rows);
 | |
| }
 | |
| 
 | |
| unsigned int SqResults::GetRowCount()
 | |
| {
 | |
| 	return m_RowCount;
 | |
| }
 | |
| 
 | |
| unsigned int SqResults::GetFieldCount()
 | |
| {
 | |
| 	return m_ColCount;
 | |
| }
 | |
| 
 | |
| const char *SqResults::FieldNumToName(unsigned int columnId)
 | |
| {
 | |
| 	if (columnId >= m_ColCount)
 | |
| 	{
 | |
| 		return NULL;
 | |
| 	}
 | |
| 
 | |
| 	return m_ColNames[columnId].c_str();
 | |
| }
 | |
| 
 | |
| bool SqResults::FieldNameToNum(const char *name, unsigned int *columnId)
 | |
| {
 | |
| 	for (unsigned int i=0; i<m_ColCount; i++)
 | |
| 	{
 | |
| 		if (m_ColNames[i].compare(name) == 0)
 | |
| 		{
 | |
| 			if (columnId)
 | |
| 			{
 | |
| 				*columnId = i;
 | |
| 			}
 | |
| 			return true;
 | |
| 		}
 | |
| 	}
 | |
| 	return false;
 | |
| }
 | |
| 
 | |
| void SqResults::ResetResultCount()
 | |
| {
 | |
| 	m_RowCount = 0;
 | |
| 	m_CurRow = 0;
 | |
| 	m_NextRow = 0;
 | |
| 	m_pMemory->Reset();
 | |
| }
 | |
| 
 | |
| void SqResults::PushResult()
 | |
| {
 | |
| 	/* First make sure we can fit one more row */
 | |
| 	if (m_RowCount + 1 > m_MaxRows)
 | |
| 	{
 | |
| 		/* Create a new array */
 | |
| 		if (!m_Rows)
 | |
| 		{
 | |
| 			m_MaxRows = 8;
 | |
| 			m_Rows = (SqField *)malloc(sizeof(SqField) * m_ColCount * m_MaxRows);
 | |
| 		} else {
 | |
| 			m_MaxRows *= 2;
 | |
| 			m_Rows = (SqField *)realloc(m_Rows, sizeof(SqField) * m_ColCount * m_MaxRows);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	SqField *row = &m_Rows[m_RowCount * m_ColCount];
 | |
| 	for (unsigned int i=0; i<m_ColCount; i++)
 | |
| 	{
 | |
| 		row[i].type = sqlite3_column_type(m_pStmt, i);
 | |
| 		if (row[i].type == SQLITE_INTEGER)
 | |
| 		{
 | |
| 			row[i].u.idx = sqlite3_column_int(m_pStmt, i);
 | |
| 			row[i].size = sizeof(int);
 | |
| 		} else if (row[i].type == SQLITE_FLOAT) {
 | |
| 			row[i].u.f = (float)sqlite3_column_double(m_pStmt, i);
 | |
| 			row[i].size = sizeof(float);
 | |
| 		} else if (row[i].type == SQLITE_BLOB) {
 | |
| 			int bytes = sqlite3_column_bytes(m_pStmt, i);
 | |
| 			const void *pOrig;
 | |
| 			if ((pOrig = sqlite3_column_blob(m_pStmt, i)) != NULL)
 | |
| 			{
 | |
| 				void *pAddr;
 | |
| 				row[i].u.idx = m_pMemory->CreateMem(bytes, &pAddr);
 | |
| 				memcpy(pAddr, pOrig, bytes);
 | |
| 			} else {
 | |
| 				row[i].u.idx = -1;
 | |
| 			}
 | |
| 			row[i].size = sqlite3_column_bytes(m_pStmt, i);
 | |
| 		} else if (row[i].type == SQLITE_TEXT) {
 | |
| 			const char *str = (const char *)sqlite3_column_text(m_pStmt, i);
 | |
| 			if (str)
 | |
| 			{
 | |
| 				row[i].u.idx = m_Strings.AddString(str);
 | |
| 			} else {
 | |
| 				row[i].u.idx = -1;
 | |
| 			}
 | |
| 			row[i].size = sqlite3_column_bytes(m_pStmt, i);
 | |
| 		} else {
 | |
| 			row[i].size = 0;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* Finally, increase the row count */
 | |
| 	m_RowCount++;
 | |
| }
 | |
| 
 | |
| bool SqResults::MoreRows()
 | |
| {
 | |
| 	return (m_CurRow < m_RowCount);
 | |
| }
 | |
| 
 | |
| IResultRow *SqResults::FetchRow()
 | |
| {
 | |
| 	m_CurRow = m_NextRow;
 | |
| 	if (m_CurRow >= m_RowCount)
 | |
| 	{
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	m_NextRow++;
 | |
| 	return this;
 | |
| }
 | |
| 
 | |
| IResultRow *SqResults::CurrentRow()
 | |
| {
 | |
| 	if (!m_RowCount || m_CurRow >= m_RowCount)
 | |
| 	{
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	return this;
 | |
| }
 | |
| 
 | |
| bool SqResults::Rewind()
 | |
| {
 | |
| 	m_CurRow = 0;
 | |
| 	m_NextRow = 0;
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| SqField *SqResults::GetField(unsigned int col)
 | |
| {
 | |
| 	if (m_CurRow >= m_RowCount || col >= m_ColCount)
 | |
| 	{
 | |
| 		return NULL;
 | |
| 	}
 | |
| 
 | |
| 	return &m_Rows[(m_CurRow * m_ColCount) + col];
 | |
| }
 | |
| 
 | |
| DBType SqResults::GetFieldType(unsigned int field)
 | |
| {
 | |
| 	/* Leaving unimplemented... */
 | |
| 	return DBType_Unknown;
 | |
| }
 | |
| 
 | |
| DBType SqResults::GetFieldDataType(unsigned int field)
 | |
| {
 | |
| 	/* Leaving unimplemented... */
 | |
| 	return DBType_Unknown;
 | |
| }
 | |
| 
 | |
| DBResult SqResults::GetString(unsigned int columnId, const char **pString, size_t *_length)
 | |
| {
 | |
| 	SqField *field = GetField(columnId);
 | |
| 	if (!field)
 | |
| 	{
 | |
| 		return DBVal_Error;
 | |
| 	}
 | |
| 
 | |
| 	DBResult res = DBVal_Data;
 | |
| 	const char *ptr = NULL;
 | |
| 	size_t length = 0;
 | |
| 	if (field->type == SQLITE_TEXT || field->type == SQLITE_BLOB)
 | |
| 	{
 | |
| 		ptr = m_Strings.GetString(field->u.idx);
 | |
| 		length = field->size;
 | |
| 	} else if (field->type == SQLITE_INTEGER) {
 | |
| 		char number[24];
 | |
| 		field->size = UTIL_Format(number, sizeof(number), "%d", field->u.idx);
 | |
| 		field->type = SQLITE_TEXT;
 | |
| 		field->u.idx = m_Strings.AddString(number);
 | |
| 		ptr = m_Strings.GetString(field->u.idx);
 | |
| 		length = field->size;
 | |
| 	} else if (field->type == SQLITE_FLOAT) {
 | |
| 		char number[24];
 | |
| 		field->size = UTIL_Format(number, sizeof(number), "%f", field->u.f);
 | |
| 		field->type = SQLITE_TEXT;
 | |
| 		field->u.idx = m_Strings.AddString(number);
 | |
| 		ptr = m_Strings.GetString(field->u.idx);
 | |
| 		length = field->size;
 | |
| 	} else if (field->type == SQLITE_NULL) {
 | |
| 		res = DBVal_Null;
 | |
| 	}
 | |
| 
 | |
| 	if (!ptr)
 | |
| 	{
 | |
| 		ptr = "";
 | |
| 	}
 | |
| 
 | |
| 	if (pString)
 | |
| 	{
 | |
| 		*pString = ptr;
 | |
| 	}
 | |
| 	if (_length)
 | |
| 	{
 | |
| 		*_length = length;
 | |
| 	}
 | |
| 
 | |
| 	return res;
 | |
| }
 | |
| 
 | |
| DBResult SqResults::CopyString(unsigned int columnId, char *buffer, size_t maxlength, size_t *written)
 | |
| {
 | |
| 	SqField *field = GetField(columnId);
 | |
| 	if (!field)
 | |
| 	{
 | |
| 		return DBVal_Error;
 | |
| 	}
 | |
| 
 | |
| 	DBResult res = DBVal_Data;
 | |
| 	if (field->type == SQLITE_TEXT || field->type == SQLITE_BLOB)
 | |
| 	{
 | |
| 		const char *ptr = m_Strings.GetString(field->u.idx);
 | |
| 		if (!ptr)
 | |
| 		{
 | |
| 			ptr = "";
 | |
| 			field->type = SQLITE_TEXT;
 | |
| 			res = DBVal_Null;
 | |
| 		}
 | |
| 		size_t wr = 0;
 | |
| 		if (field->type == SQLITE_TEXT)
 | |
| 		{
 | |
| 			wr = strncopy(buffer, ptr, maxlength);
 | |
| 		} else if (field->type == SQLITE_BLOB) {
 | |
| 			wr = (maxlength < field->size) ? maxlength : field->size;
 | |
| 			memcpy(buffer, ptr, wr);
 | |
| 		}
 | |
| 		if (written)
 | |
| 		{
 | |
| 			*written = wr;
 | |
| 		}
 | |
| 		return res;
 | |
| 	} else if (field->type == SQLITE_INTEGER) {
 | |
| 		size_t wr = 0;
 | |
| 		if (buffer)
 | |
| 		{
 | |
| 			wr = UTIL_Format(buffer, maxlength, "%d", field->u.idx);
 | |
| 		}
 | |
| 		if (written)
 | |
| 		{
 | |
| 			*written = wr;
 | |
| 		}
 | |
| 		return DBVal_Data;
 | |
| 	} else if (field->type == SQLITE_FLOAT) {
 | |
| 		size_t wr = 0;
 | |
| 		if (buffer)
 | |
| 		{
 | |
| 			wr = UTIL_Format(buffer, maxlength, "%f", field->u.f);
 | |
| 		}
 | |
| 		if (written)
 | |
| 		{
 | |
| 			*written = wr;
 | |
| 		}
 | |
| 		return DBVal_Data;
 | |
| 	}
 | |
| 
 | |
| 	if (buffer)
 | |
| 	{
 | |
| 		strncopy(buffer, "", maxlength);
 | |
| 	}
 | |
| 	if (written)
 | |
| 	{
 | |
| 		*written = 0;
 | |
| 	}
 | |
| 
 | |
| 	return DBVal_Null;
 | |
| }
 | |
| 
 | |
| bool SqResults::IsNull(unsigned int columnId)
 | |
| {
 | |
| 	SqField *field = GetField(columnId);
 | |
| 	if (!field)
 | |
| 	{
 | |
| 		return true;
 | |
| 	}
 | |
| 
 | |
| 	return (field->type == SQLITE_NULL);
 | |
| }
 | |
| 
 | |
| size_t SqResults::GetDataSize(unsigned int columnId)
 | |
| {
 | |
| 	SqField *field = GetField(columnId);
 | |
| 	if (!field)
 | |
| 	{
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	return field->size;
 | |
| }
 | |
| 
 | |
| DBResult SqResults::GetFloat(unsigned int columnId, float *pFloat)
 | |
| {
 | |
| 	SqField *field = GetField(columnId);
 | |
| 	if (!field)
 | |
| 	{
 | |
| 		return DBVal_Error;
 | |
| 	} else if (field->type == SQLITE_BLOB) {
 | |
| 		return DBVal_Error;
 | |
| 	}
 | |
| 
 | |
| 	float fVal = 0.0f;
 | |
| 	if (field->type == SQLITE_FLOAT)
 | |
| 	{
 | |
| 		fVal = field->u.f;
 | |
| 	} else if (field->type == SQLITE_TEXT) {
 | |
| 		const char *ptr = m_Strings.GetString(field->u.idx);
 | |
| 		if (ptr)
 | |
| 		{
 | |
| 			fVal = (float)atof(ptr);
 | |
| 		}
 | |
| 	} else if (field->type == SQLITE_INTEGER) {
 | |
| 		fVal = (float)field->u.idx;
 | |
| 	}
 | |
| 
 | |
| 	if (pFloat)
 | |
| 	{
 | |
| 		*pFloat = fVal;
 | |
| 	}
 | |
| 
 | |
| 	return (field->type == SQLITE_NULL) ? DBVal_Null : DBVal_Data;
 | |
| }
 | |
| 
 | |
| DBResult SqResults::GetInt(unsigned int columnId, int *pInt)
 | |
| {
 | |
| 	SqField *field = GetField(columnId);
 | |
| 	if (!field)
 | |
| 	{
 | |
| 		return DBVal_Error;
 | |
| 	} else if (field->type == SQLITE_BLOB) {
 | |
| 		return DBVal_Error;
 | |
| 	}
 | |
| 
 | |
| 	int val = 0;
 | |
| 	if (field->type == SQLITE_INTEGER)
 | |
| 	{
 | |
| 		val = field->u.idx;
 | |
| 	} else if (field->type == SQLITE_TEXT) {
 | |
| 		const char *ptr = m_Strings.GetString(field->u.idx);
 | |
| 		if (ptr)
 | |
| 		{
 | |
| 			val = atoi(ptr);
 | |
| 		}
 | |
| 	} else if (field->type == SQLITE_FLOAT) {
 | |
| 		val = (int)field->u.f;
 | |
| 	}
 | |
| 
 | |
| 	if (pInt)
 | |
| 	{
 | |
| 		*pInt = val;
 | |
| 	}
 | |
| 
 | |
| 	return (field->type == SQLITE_NULL) ? DBVal_Null : DBVal_Data;
 | |
| }
 | |
| 
 | |
| DBResult SqResults::GetBlob(unsigned int columnId, const void **pData, size_t *length)
 | |
| {
 | |
| 	SqField *field = GetField(columnId);
 | |
| 	if (!field)
 | |
| 	{
 | |
| 		return DBVal_Error;
 | |
| 	} 
 | |
| 
 | |
| 	void *addr = NULL;
 | |
| 
 | |
| 	if (field->type == SQLITE_TEXT || field->type == SQLITE_BLOB)
 | |
| 	{
 | |
| 		addr = m_pMemory->GetAddress(field->u.idx);
 | |
| 	} else if (field->type == SQLITE_FLOAT || field->type == SQLITE_INTEGER) {
 | |
| 		addr = &(field->u);
 | |
| 	}
 | |
| 
 | |
| 	if (pData)
 | |
| 	{
 | |
| 		*pData = addr;
 | |
| 	}
 | |
| 
 | |
| 	if (length)
 | |
| 	{
 | |
| 		*length = field->size;
 | |
| 	}
 | |
| 
 | |
| 	return (field->type == SQLITE_NULL) ? DBVal_Null : DBVal_Data;
 | |
| }
 | |
| 
 | |
| DBResult SqResults::CopyBlob(unsigned int columnId, void *buffer, size_t maxlength, size_t *written)
 | |
| {
 | |
| 	SqField *field = GetField(columnId);
 | |
| 	if (!field)
 | |
| 	{
 | |
| 		return DBVal_Error;
 | |
| 	} 
 | |
| 
 | |
| 	void *addr = NULL;
 | |
| 
 | |
| 	if (field->type == SQLITE_TEXT || field->type == SQLITE_BLOB)
 | |
| 	{
 | |
| 		addr = m_pMemory->GetAddress(field->u.idx);
 | |
| 	} else if (field->type == SQLITE_FLOAT || field->type == SQLITE_INTEGER) {
 | |
| 		addr = &(field->u);
 | |
| 	}
 | |
| 
 | |
| 	size_t toCopy = field->size > maxlength ? maxlength : field->size;
 | |
| 	if (buffer && addr && toCopy)
 | |
| 	{
 | |
| 		memcpy(buffer, addr, toCopy);
 | |
| 	} else {
 | |
| 		toCopy = 0;
 | |
| 	}
 | |
| 
 | |
| 	if (written)
 | |
| 	{
 | |
| 		*written = toCopy;
 | |
| 	}
 | |
| 
 | |
| 	return (field->type == SQLITE_NULL) ? DBVal_Null : DBVal_Data;
 | |
| }
 |