#include "stdafx.h" #include #include #include "TSServer.h" #include "TSField.h" #include "da.h" /* Debugging allocator -- GDR 2000-09-01 */ const int TSRecord::RecordNew = 1; const int TSRecord::RecordNewWithID = 3; const int TSRecord::RecordUpdated = 4; const int TSRecord::RecordDeleted = 8; TSRecord::TSRecord(int tableId, TSServer *server, int fieldType /*=0*/) { Initialize(tableId, server, fieldType); } TSRecord::~TSRecord() { fieldList.EmptyAndDestroyList(); userDefinedFields.EmptyAndDestroyList(); } TSRecord::TSRecord() { Initialize(0, NULL, 0); } void TSRecord::Initialize(int tableId, TSServer *server, int fieldType) { serverRef = server; this->tableId = tableId; this->fieldType = fieldType; recordState = 0; fromPos = 0; userDefinedFields.SetIsFieldList( TRUE ); if (serverRef != NULL) { TSSchema *schema = serverRef->GetSchema(tableId); if( schema != NULL ) { tableName = schema->name; schema->fieldList.Duplicate(&fieldList); if( schema->userDefinedFields ) { serverRef->ReadUserDefinedFields(&userDefinedFields, tableId); CopyUserDefFieldsTo(); } } } } TSObject *TSRecord::NewObject() { return new TSRecord; } TSObject *TSRecord::Duplicate() { TSRecord *rec = new TSRecord; rec->tableId = tableId; rec->fieldType = fieldType; rec->serverRef = serverRef; fieldList.Duplicate(&rec->fieldList); userDefinedFields.Duplicate( &rec->userDefinedFields ); return rec; } void TSRecord::Copy(TSObject *obj) { TSRecord *rec = (TSRecord*)obj; tableId = rec->tableId; fieldType = rec->fieldType; serverRef = rec->serverRef; fieldList.Copy(&rec->fieldList); userDefinedFields.Copy( &rec->userDefinedFields ); } int TSRecord::Receive(TSSocket *socket) { char ch; /* First get DB Members. */ if( ReceiveFieldsBySchemaType(socket, TS_SCHEMATYPE_DBCOL) != TS_OK ) { return TS_ERROR; } /* If there are variable fields then attempt to receive them.*/ TSSchema *schema = serverRef->GetSchema( tableId ); if( schema == NULL ) { return TS_ERROR; } if( schema->userDefinedFields ) { ReceiveUserDefinedFields(socket); } /* Core Members are next. These members are not part of the schema so we can't call ReceiveFieldsBySchemaType(). Instead we'll just put the code directly here and get the data. */ if( tableId == TS_TBLID_FIELDS ) { if( socket->ReceiveInt(&sqlDataType) != TS_OK ) { return TS_ERROR; } } if( socket->ReceiveInt(&recordState) != TS_OK ) return TS_ERROR; if( socket->ReceiveInt(&fromPos) != TS_OK ) return TS_ERROR; /* Non-DB Members. */ if( ReceiveFieldsBySchemaType(socket, TS_SCHEMATYPE_NONDB) != TS_OK) { return TS_ERROR; } /* List members. */ if( ReceiveFieldsBySchemaType(socket, TS_SCHEMATYPE_LIST) != TS_OK) { return TS_ERROR; } /* TreeList members. */ if( ReceiveFieldsBySchemaType(socket, TS_SCHEMATYPE_TREELIST) != TS_OK) { return TS_ERROR; } /* * There's always a \r\n at the end of the record, * but usually the \r has already been read so read * up till the next \n. */ while ((ch = socket->ReadChar()) != 0) if( ch == '\n') break; return TS_OK; } int TSRecord::ReceiveUserDefinedFields(TSSocket *socket) { /* * First read the fields off of the socket. */ int errorCode = userDefinedFields.Receive(serverRef, socket); if( errorCode != TS_OK) { return errorCode; } /* * Now add fields to the fields list for each user defined field. */ CopyUserDefFieldsTo(); return TS_OK; } void TSRecord::CopyUserDefFieldsFrom() { for( TSPosition *pos = userDefinedFields.GetFirst(); pos != NULL; pos = userDefinedFields.GetNext(pos) ) { TSRecord *rec = userDefinedFields.GetAt(pos); char *searchString = rec->GetString("dbname"); char *oldString = NULL; TSField *fld = fieldList.FindFieldByName(searchString, rec->fieldType); switch( fld->dataType ) { case TS_DATATYPE_STRING: oldString = rec->GetString( "dbval" ); if ( oldString ) { if ( strcmp( oldString, fld->charValue ) != 0 ) { rec->SetString("val", fld->charValue); } } break; case TS_DATATYPE_INTEGER: if ( rec->fieldType == TS_FLDTYPE_NUMERIC ) { if ( rec->GetInt( "dbintval" ) != fld->intValue ) { rec->SetInt( "intval", fld->intValue ); } } else { if ( rec->GetInt("dbval") != fld->intValue ) { rec->SetInt("val", fld->intValue); } } break; case TS_DATATYPE_DOUBLE: if ( rec->GetDouble( "dbfloatval" ) != fld->doubleValue ) { rec->SetDouble("floatval", fld->doubleValue); } break; default: break; } } } // Copy the values from the user defined records to the regular field // list. To the user, it will look like a single list of fields from // the database. void TSRecord::CopyUserDefFieldsTo() { for(TSPosition *pos = userDefinedFields.GetFirst(); pos != NULL; pos = userDefinedFields.GetNext(pos)) { TSField *field = NULL; TSRecord *rec = (TSRecord*)userDefinedFields.GetAt(pos); char sDbName[64]; strncpy(sDbName, rec->GetString("dbname"), 63); // Use the existing field if it is there, otherwise create // a new one. field = fieldList.FindFieldByName( sDbName, fieldType ); if ( !field ) { field = new TSField(); strncpy(field->fieldName, rec->GetString("dbname"), 63); fieldList.AddTail(field); } switch(rec->sqlDataType) { case SQL_INTEGER: field->dataType = TS_DATATYPE_INTEGER; if ( rec->fieldType == TS_FLDTYPE_NUMERIC ) { field->intValue = rec->GetInt("dbintval"); } else { field->intValue = rec->GetInt("dbval"); } break; case SQL_CHAR: case SQL_VARCHAR: case SQL_LONGVARCHAR: case SQL_LONGVARCHAR1: case SQL_LONGVARCHAR2: { field->dataType = TS_DATATYPE_STRING; char *str = rec->GetString("dbval"); if( field->charValue ) delete field->charValue; if(str != NULL) { field->charValue = new char[strlen(str)+1]; strcpy(field->charValue, str); } else { field->charValue = NULL; } } break; case SQL_DOUBLE: field->dataType = TS_DATATYPE_DOUBLE; field->doubleValue = rec->GetDouble("dbfloatval"); break; default: field->dataType = TS_DATATYPE_UNKNOWN; } } } int TSRecord::ReceiveFieldsBySchemaType(TSSocket *socket, int schemaType) { TSField *field; TSPosition *pos = fieldList.GetFirst(); while(pos) { field = fieldList.GetAt(pos); if(field->schemaType == schemaType) { // If the field type is 0 or the field type for the record // matches the field type of the field then we can receive // this field. if(field->fieldType == 0 || field->fieldType == fieldType) { if(field->Receive(socket, serverRef) != TS_OK) { return TS_ERROR; } } } pos = fieldList.GetNext(pos); } return TS_OK; } /* * Convert this entire record to a string so that it can be * sent accross the socket to the server. */ int TSRecord::SocketString(TSString &str) { TSString s; str = ""; /* First encode the DB Members */ MembersToStringBySchemaType(s, TS_SCHEMATYPE_DBCOL); str += s; if( serverRef->GetSchema(tableId)->userDefinedFields ) { UserDefinedFieldsToSocketString(s); str += s; } /* Core members. */ if( tableId == TS_TBLID_FIELDS ) { TSEncodeInt(sqlDataType, s); str += s; } TSEncodeInt(recordState, s); str += s; TSEncodeInt(fromPos, s); str += s; /* Non DB Members.*/ MembersToStringBySchemaType(s, TS_SCHEMATYPE_NONDB); str += s; /* List Members */ MembersToStringBySchemaType(s, TS_SCHEMATYPE_LIST); str += s; /* Tree list members */ MembersToStringBySchemaType(s, TS_SCHEMATYPE_TREELIST); str += s; str += "\r\n"; return TS_OK; } int TSRecord::UserDefinedFieldsToSocketString(TSString &out) { /* * First copy the data from the fields list into the * userDefinedFields record list. */ CopyUserDefFieldsFrom(); /* * Now convert the userDefinedFields list to a string. */ return userDefinedFields.SocketString(out); } int TSRecord::MembersToStringBySchemaType(TSString &out, int schemaType) { TSString s; TSField *field; out = ""; TSPosition *pos = fieldList.GetFirst(); while(pos) { field = fieldList.GetAt(pos); if(field->schemaType == schemaType) { // If the field type is 0 or the field type for the record // matches the field type fo the field then we need to encode // this value into the string. if(field->fieldType == 0 || field->fieldType == fieldType) { field->SocketString(s); out += s; } } pos = fieldList.GetNext(pos); } return TS_OK; } /* * Debugging routine to dump the contents of the record to a * string to be printed out to the screen. */ TSString TSRecord::StringDump(int recursive, TSString indentation) { TSField *field; TSString s = indentation + "RECORD:\n"; s += indentation + " "; s += "Table Name = "; s += tableName + "\n"; for(TSPosition *pos = fieldList.GetFirst(); pos != NULL; pos = fieldList.GetNext(pos)) { field = fieldList.GetAt(pos); if(field->fieldType == 0 || field->fieldType == fieldType) { s += field->StringDump(recursive, indentation + " "); } } return s; } /********************************************************** * FUCTIONS TO RETRIEVE VALUES FROM FIELDS. **********************************************************/ int TSRecord::GetInt(const char *fieldName, int *value) { TSField *fld = fieldList.FindFieldByName(fieldName, fieldType); if(fld == NULL) { TSSetLastError(TS_NO_SUCH_FIELD); return TS_NO_SUCH_FIELD; } if(fld->dataType != TS_DATATYPE_INTEGER) { TSSetLastError(TS_INVALID_DATATYPE); return TS_INVALID_DATATYPE; } *value = fld->intValue; return TS_OK; } int TSRecord::GetInt(const char *fieldName) { TSField *fld = fieldList.FindFieldByName(fieldName, fieldType); if(fld == NULL) { TSSetLastError(TS_NO_SUCH_FIELD); return 0; } if(fld->dataType != TS_DATATYPE_INTEGER) { TSSetLastError(TS_INVALID_DATATYPE); return 0; } return fld->intValue; } int TSRecord::GetDouble(const char *fieldName, double *value) { TSField *fld = fieldList.FindFieldByName(fieldName, fieldType); if(fld == NULL) { TSSetLastError(TS_NO_SUCH_FIELD); return TS_NO_SUCH_FIELD; } if(fld->dataType != TS_DATATYPE_DOUBLE) { TSSetLastError(TS_INVALID_DATATYPE); return TS_INVALID_DATATYPE; } *value = fld->doubleValue; return TS_OK; } double TSRecord::GetDouble(const char *fieldName) { TSField *fld = fieldList.FindFieldByName(fieldName, fieldType); if(fld == NULL) { TSSetLastError(TS_NO_SUCH_FIELD); return 0.0; } if(fld->dataType != TS_DATATYPE_DOUBLE) { TSSetLastError(TS_INVALID_DATATYPE); return 0.0; } return fld->doubleValue; } int TSRecord::GetString(const char *fieldName, TSString *value) { TSField *fld = fieldList.FindFieldByName(fieldName, fieldType); if(fld == NULL) { TSSetLastError(TS_NO_SUCH_FIELD); return TS_NO_SUCH_FIELD; } if(fld->dataType != TS_DATATYPE_STRING) { TSSetLastError(TS_INVALID_DATATYPE); return TS_INVALID_DATATYPE; } *value = fld->charValue; return TS_OK; } // Allocates a buffer and returns a status value. new is used to allocate // the buffer so it should be destroyed by the caller with a call to 'delete'. int TSRecord::GetString(const char *fieldName, char **value) { TSField *fld = fieldList.FindFieldByName(fieldName, fieldType); if(fld == NULL) { TSSetLastError(TS_NO_SUCH_FIELD); return TS_NO_SUCH_FIELD; } if(fld->dataType != TS_DATATYPE_STRING) { TSSetLastError(TS_INVALID_DATATYPE); return TS_INVALID_DATATYPE; } *value = new char[strlen(fld->charValue) +1]; strcpy(*value, fld->charValue); return TS_OK; } // Copy the string into an existing buffer. Note that if the value to // be returned has a length of bufferSize or greater, then value will NOT // be NULL terminated. int TSRecord::GetString(const char *fieldName, char *value, int bufferSize) { TSField *fld = fieldList.FindFieldByName(fieldName, fieldType); if(fld == NULL) { TSSetLastError(TS_NO_SUCH_FIELD); return TS_NO_SUCH_FIELD; } if(fld->dataType != TS_DATATYPE_STRING) { TSSetLastError(TS_INVALID_DATATYPE); return TS_INVALID_DATATYPE; } strncpy(value,fld->charValue,bufferSize); return TS_OK; } // This function returns a pointer to internally allocated data and should // NOT be freed by the caller. char *TSRecord::GetString(const char *fieldName) { TSField *fld = fieldList.FindFieldByName(fieldName, fieldType); if(fld == NULL) { TSSetLastError(TS_NO_SUCH_FIELD); return NULL; } if(fld->dataType != TS_DATATYPE_STRING) { TSSetLastError(TS_INVALID_DATATYPE); return NULL; } return fld->charValue; } // The values in the list are internally allocated and should NOT be // freed by the caller. int TSRecord::GetRecordList(const char *fieldName, TSRecordList **value) { TSField *fld = fieldList.FindFieldByName(fieldName, fieldType); if(fld == NULL) { TSSetLastError(TS_NO_SUCH_FIELD); return TS_NO_SUCH_FIELD; } if(fld->dataType != TS_DATATYPE_RECORDLIST) { TSSetLastError(TS_INVALID_DATATYPE); return TS_INVALID_DATATYPE; } *value = fld->recordList; return TS_OK; } // The values in the list are internally allocated and should NOT be // freed by the caller. TSRecordList *TSRecord::GetRecordList(const char *fieldName) { TSField *fld = fieldList.FindFieldByName(fieldName, fieldType); if(fld == NULL) { TSSetLastError(TS_NO_SUCH_FIELD); return NULL; } if(fld->dataType != TS_DATATYPE_RECORDLIST) { TSSetLastError(TS_INVALID_DATATYPE); return NULL; } return fld->recordList; } TSTreeList *TSRecord::GetSubTreeList() { TSPosition *pos; for(pos = fieldList.GetFirst(); pos != NULL; pos = fieldList.GetNext(pos)) { TSField *fld = fieldList.GetAt(pos); if( fld->schemaType == TS_SCHEMATYPE_TREELIST ) { return (TSTreeList*)fld->recordList; } } return NULL; } /********************************************************** * FUCTIONS TO SET VALUES IN FIELDS. **********************************************************/ int TSRecord::SetInt(const char *fieldName, int value) { TSField *fld = fieldList.FindFieldByName(fieldName, fieldType); if(fld == NULL) return TS_NO_SUCH_FIELD; if(fld->dataType != TS_DATATYPE_INTEGER) return TS_INVALID_DATATYPE; if( fld->intValue == value ) { return TS_OK; } fld->intValue = value; SetUpdatedRecord(); return TS_OK; } int TSRecord::SetDouble(const char *fieldName, double value) { TSField *fld = fieldList.FindFieldByName(fieldName, fieldType); if(fld == NULL) return TS_NO_SUCH_FIELD; if(fld->dataType != TS_DATATYPE_DOUBLE) return TS_INVALID_DATATYPE; if( fld->doubleValue == value ) { return TS_OK; } fld->doubleValue = value; SetUpdatedRecord(); return TS_OK; } int TSRecord::SetString(const char *fieldName, const char *value) { TSField *fld = fieldList.FindFieldByName(fieldName, fieldType); if(fld == NULL) return TS_NO_SUCH_FIELD; if(fld->dataType != TS_DATATYPE_STRING) return TS_INVALID_DATATYPE; if(fld->charValue) { if( strcmp(fld->charValue, value) == 0 ) { return TS_OK; } delete fld->charValue; } fld->charValue = new char[strlen(value) +1]; strcpy(fld->charValue, value); SetUpdatedRecord(); return TS_OK; }