// TSRecord.cpp: implementation of the TSRecord class. // ///////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include #include #include "TSField.h" #include "TSRecord.h" #include "TSServer.h" 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; this->sqlDataType = SQL_EMPTY; 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( int /*type = 0*/ ) { TSRecord* rec = new TSRecord; rec->tableId = tableId; rec->fieldType = fieldType; rec->serverRef = serverRef; rec->tableName = tableName; recordState = 0; fromPos = 0; fieldList.Duplicate( &rec->fieldList, fieldType ); userDefinedFields.Duplicate( &rec->userDefinedFields, fieldType ); return rec; } void TSRecord::Copy( TSObject* sourceRecord ) { TSRecord* rec = static_cast( sourceRecord ); tableId = rec->tableId; fieldType = rec->fieldType; serverRef = rec->serverRef; fieldList.Copy( &rec->fieldList ); userDefinedFields.Copy( &rec->userDefinedFields ); } int TSRecord::Receive( TSSocket* socket, bool bIgnoreNonDbMembers /*=false*/ ) { char ch; // First get DB Members. if ( ReceiveFieldsBySchemaType( socket, TS_SCHEMATYPE_DBCOL ) != TS_OK ) { // TSErrorCode is set in ReceiveFieldsBySchemaType() return TSGetLastError(); } // If there are variable fields then attempt to receive them. TSSchema* schema = serverRef->GetSchema( tableId ); if ( schema == NULL ) { // TSErrorCode is set in GetSchema() return TSGetLastError(); } if ( schema->userDefinedFields ) { if ( ReceiveUserDefinedFields( socket ) != TS_OK ) { // TSErrorCode is set in ReceiveUserDefinedFields() return TSGetLastError(); } } // 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 ) { // TSErrorCode is set in ReceiveInt() return TSGetLastError(); } } if ( socket->ReceiveInt( &recordState ) != TS_OK ) { // TSErrorCode is set in ReceiveInt() return TSGetLastError(); } if ( socket->ReceiveInt( &fromPos ) != TS_OK ) { // TSErrorCode is set in ReceiveInt() return TSGetLastError(); } if ( !bIgnoreNonDbMembers ) { // Non-DB Members. if ( ReceiveFieldsBySchemaType( socket, TS_SCHEMATYPE_NONDB ) != TS_OK ) { // TSErrorCode is set in ReceiveFieldsBySchemaType() return TSGetLastError(); } // List members. if ( ReceiveFieldsBySchemaType( socket, TS_SCHEMATYPE_LIST ) != TS_OK ) { // TSErrorCode is set in ReceiveFieldsBySchemaType() return TSGetLastError(); } // TreeList members. if ( ReceiveFieldsBySchemaType( socket, TS_SCHEMATYPE_TREELIST ) != TS_OK ) { // TSErrorCode is set in ReceiveFieldsBySchemaType() return TSGetLastError(); } } // 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. if ( userDefinedFields.Receive( serverRef, socket ) != TS_OK ) { // TSErrorCode is set in Receive() return TSGetLastError(); } // 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 ); if ( !fld ) { continue; } 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 = static_cast( userDefinedFields.GetAt( pos )); const int szName = 64; char sDbName[szName]; // Get the dbname and verify it is something. char* tempName = rec->GetString( "dbname" ); if ( tempName == NULL ) { continue; } // Store the dbname into something we control. strncpy( sDbName, tempName, szName ); sDbName[szName-1] = '\0'; // only changes something if the length of the name length // wants to be greater buffer size. // 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, sDbName, szName ); field->fieldName[szName-1] = '\0'; 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 = 0; } } 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 ) { // TSErrorCode is set in Receive() return TSGetLastError(); } } } 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; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Functions 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 TSGetLastError(); } if ( fld->dataType != TS_DATATYPE_INTEGER ) { TSSetLastError( TS_INVALID_DATATYPE ); return TSGetLastError(); } *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 TSGetLastError(); } if ( fld->dataType != TS_DATATYPE_INTEGER ) { TSSetLastError( TS_INVALID_DATATYPE ); return TSGetLastError(); } 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 TSGetLastError(); } if ( fld->dataType != TS_DATATYPE_DOUBLE ) { TSSetLastError( TS_INVALID_DATATYPE ); return TSGetLastError(); } *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 TSGetLastError(); } if ( fld->dataType != TS_DATATYPE_DOUBLE ) { TSSetLastError( TS_INVALID_DATATYPE ); return TSGetLastError(); } 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 TSGetLastError(); } if ( fld->dataType == TS_DATATYPE_INTLIST ) { *value = fld->intList->CommaSeparatedList(); return TS_OK; } if ( fld->dataType != TS_DATATYPE_STRING ) { TSSetLastError( TS_INVALID_DATATYPE ); return TSGetLastError(); } *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 TSGetLastError(); } if ( fld->dataType == TS_DATATYPE_INTLIST ) { TSString sTemp; sTemp = fld->intList->CommaSeparatedList(); *value = new char[ sTemp.Length() + 1 ]; strcpy( *value, sTemp ); return TS_OK; } if ( fld->dataType != TS_DATATYPE_STRING ) { TSSetLastError( TS_INVALID_DATATYPE ); return TSGetLastError(); } *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 TSGetLastError(); } if ( fld->dataType == TS_DATATYPE_INTLIST ) { TSString sTemp; sTemp = fld->intList->CommaSeparatedList(); strncpy( value, sTemp, bufferSize ); return TS_OK; } if ( fld->dataType != TS_DATATYPE_STRING ) { TSSetLastError( TS_INVALID_DATATYPE ); return TSGetLastError(); } strncpy( value, fld->charValue,bufferSize ); // value[bufferSize-1] = '\0'; // probably should turn on, but not now GKG return TS_OK; } // This function returns a pointer to internally allocated data and should // NOT be freed by the caller. This pointer is only valid as long as the field // is valid. 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_INTLIST ) { if ( fld->charValue ) { delete [] fld->charValue; fld->charValue = NULL; } TSString sTemp = fld->intList->CommaSeparatedList(); fld->charValue = new char[ sTemp.Length() + 1 ]; strcpy( fld->charValue, sTemp ); return fld->charValue; } 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 TSGetLastError(); } if ( fld->dataType != TS_DATATYPE_RECORDLIST ) { TSSetLastError( TS_INVALID_DATATYPE ); return TSGetLastError(); } *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 static_cast( fld->recordList ); } } return NULL; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Functions to set values in fields. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ int TSRecord::SetInt( const char* fieldName, int value ) { TSField* fld = fieldList.FindFieldByName( fieldName, fieldType ); if ( fld == NULL ) { TSSetLastError( TS_NO_SUCH_FIELD ); return TSGetLastError(); } if ( fld->dataType != TS_DATATYPE_INTEGER ) { TSSetLastError( TS_INVALID_DATATYPE ); return TSGetLastError(); } 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 ) { TSSetLastError( TS_NO_SUCH_FIELD ); return TSGetLastError(); } if ( fld->dataType != TS_DATATYPE_DOUBLE ) { TSSetLastError( TS_INVALID_DATATYPE ); return TSGetLastError(); } 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 ) { TSSetLastError( TS_NO_SUCH_FIELD ); return TSGetLastError(); } if ( fld->dataType == TS_DATATYPE_INTLIST ) { fld->intList->PopulateByCommaSeparatedList( value ); SetUpdatedRecord(); return TS_OK; } if ( fld->dataType != TS_DATATYPE_STRING ) { TSSetLastError( TS_INVALID_DATATYPE ); return TSGetLastError(); } if ( fld->charValue ) { if ( strcmp( fld->charValue, value ) == 0 ) { return TS_OK; } delete [] fld->charValue; fld->charValue = 0; } fld->charValue = new char[strlen(value) +1]; strcpy( fld->charValue, value ); SetUpdatedRecord(); return TS_OK; }