|
|
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================
#include "stdafx.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
namespace GCSDK { const char *GetInsertArgString() { static char s_str[1024]; static bool s_bInit = false;
if ( !s_bInit ) { for ( int i = 0; i < 1023; i++ ) { s_str[i] = i % 2 == 0 ? '?' : ','; }
s_str[1023] = NULL; s_bInit = true; }
return s_str; }
uint32 GetInsertArgStringChars( uint32 nNumParams ) { AssertMsg( nNumParams <= GetInsertArgStringMaxParams(), "Error: Requested more characters than are provided by the GetInsertArgString" ); if( nNumParams == 0 ) return 0;
return nNumParams * 2 - 1; }
uint32 GetInsertArgStringMaxParams() { return 512; }
//-----------------------------------------------------------------------------
// Purpose: Converts array of field data to text for SQL IN clause
// Input: columnInfo - schema of column being converted
// pubData - pointer to array of data to convert
// cubData - size of array of data
// rgchResult - pointer to output buffer
// cubResultLen - size of output buffer
// bForPreparedStatement - Should we prepare the text for a prepared statement or directly place the values?
//-----------------------------------------------------------------------------
void ConvertFieldArrayToInText( const CColumnInfo &columnInfo, byte *pubData, int cubData, char *rgchResult, int cubResultLen, bool bForPreparedStatement ) { int32 cubLength = columnInfo.GetFixedSize(); Assert( cubData % cubLength == 0 ); int32 nArrayCount = cubData / cubLength;
int32 len = 0; rgchResult[len++] = '('; for( int i = 0; i < nArrayCount; ++i ) { if ( bForPreparedStatement ) { if ( i < nArrayCount - 1 ) { rgchResult[len++] = '?'; rgchResult[len++] = ','; } else { rgchResult[len++] = '?'; rgchResult[len++] = ')'; } } else { switch ( columnInfo.GetType() ) { case k_EGCSQLType_int8: if ( i < nArrayCount - 1 ) len += Q_snprintf( rgchResult + len, cubResultLen - len, "%d,", *( (byte *) pubData ) ); else len += Q_snprintf( rgchResult + len, cubResultLen - len, "%d)", *( (byte *) pubData ) ); break; case k_EGCSQLType_int16: if ( i < nArrayCount - 1 ) len += Q_snprintf( rgchResult + len, cubResultLen - len, "%d,", *( (short *) pubData ) ); else len += Q_snprintf( rgchResult + len, cubResultLen - len, "%d)", *( (short *) pubData ) ); break; case k_EGCSQLType_int32: if ( i < nArrayCount - 1 ) len += Q_snprintf( rgchResult + len, cubResultLen - len, "%d,", *( (int *) pubData ) ); else len += Q_snprintf( rgchResult + len, cubResultLen - len, "%d)", *( (int *) pubData ) ); break; case k_EGCSQLType_int64: if ( i < nArrayCount - 1 ) len += Q_snprintf( rgchResult + len, cubResultLen - len, "%lld,", *( (int64 *) pubData ) ); else len += Q_snprintf( rgchResult + len, cubResultLen - len, "%lld)", *( (int64 *) pubData ) ); break; default: AssertMsg( false, "Unsupported data type for non prepares statement with IN clause\n" ); rgchResult[0] = 0; return; } }
if( len >= cubResultLen - 1 ) { AssertMsg( false, "Generation of IN clause foverflowed\n" ); rgchResult[0] = 0; return; } pubData += cubLength; }
rgchResult[len] = 0; return; }
//-----------------------------------------------------------------------------
// Purpose: Converts field data to text equivalent for SQL statement
// Input: eFieldType - The type of the field to convert to text
// pubRecord - pointer to record data to convert
// cubRecord - size of record data
// rgchField - pointer to output buffer
// cchField - size of output buffer
//-----------------------------------------------------------------------------
void ConvertFieldToText( EGCSQLType eFieldType, uint8 *pubRecord, int cubRecord, char *rgchField, int cchField, bool bQuoteString ) { char rgchTmp[k_cMedBuff];
switch ( eFieldType ) { case k_EGCSQLType_int8: Q_snprintf( rgchField, cchField, "%d", *( (byte *) pubRecord ) ); break; case k_EGCSQLType_int16: Q_snprintf( rgchField, cchField, "%d", *( (short *) pubRecord ) ); break; case k_EGCSQLType_int32: Q_snprintf( rgchField, cchField, "%d", *( (int *) pubRecord ) ); break; case k_EGCSQLType_int64: Q_snprintf( rgchField, cchField, "%lld", *( (int64 *) pubRecord ) ); break; case k_EGCSQLType_float: Q_snprintf( rgchField, cchField, "%f", *((float*) pubRecord) ); break; case k_EGCSQLType_double: Q_snprintf( rgchField, cchField, "%f", *((double*) pubRecord) ); break; case k_EGCSQLType_String: if ( pubRecord && *pubRecord ) { Assert( cubRecord + 1 < Q_ARRAYSIZE( rgchTmp ) );
Q_memcpy( rgchTmp, (char *) pubRecord, cubRecord ); rgchTmp[cubRecord] = 0;
if ( bQuoteString ) { EscapeStringValue( rgchTmp, Q_ARRAYSIZE( rgchTmp ) ); Q_snprintf( rgchField, cchField, "'%s'", rgchTmp ); } else { Q_strncpy( rgchField, rgchTmp, cchField ); } } else { if ( bQuoteString ) { Q_strncpy( rgchField, "''", cchField ); } else { Q_strncpy( rgchField, "", cchField ); } } break; case k_EGCSQLType_Blob: case k_EGCSQLType_Image: Q_strncpy( rgchField, "0x", cchField ); Q_binarytohex( pubRecord, cubRecord, rgchField + 2, cchField - 2 ); break; default: Assert( false ); break; } }
//-----------------------------------------------------------------------------
// Purpose: Returns the text SQL type for a given field
// Input: field - field to determine type for
// pchBuf - pointer to output buffer
// cchBuf - size of output buffer
// Output: returns pchBuf for convenience of one-line usage
//-----------------------------------------------------------------------------
char *SQLTypeFromField( const CColumnInfo &colInfo, char *pchBuf, int cchBuf ) { EGCSQLType eType = colInfo.GetType(); *pchBuf = 0; switch ( eType ) { case k_EGCSQLType_int8: Q_strncpy( pchBuf, "TINYINT", cchBuf ); break; case k_EGCSQLType_int16: Q_strncpy( pchBuf, "SMALLINT", cchBuf ); break; case k_EGCSQLType_int32: Q_strncpy( pchBuf, "INT", cchBuf ); break; case k_EGCSQLType_int64: Q_strncpy( pchBuf, "BIGINT", cchBuf ); break; case k_EGCSQLType_float: Q_strncpy( pchBuf, "REAL", cchBuf ); break; case k_EGCSQLType_double: Q_strncpy( pchBuf, "FLOAT", cchBuf ); break; case k_EGCSQLType_String: Q_snprintf( pchBuf, cchBuf, "VARCHAR(%d)", colInfo.GetMaxSize() ); break; case k_EGCSQLType_Blob: Q_snprintf( pchBuf, cchBuf, "VARBINARY(%d)", colInfo.GetMaxSize() ); break; case k_EGCSQLType_Image: Q_strncpy( pchBuf, "IMAGE", cchBuf ); break; default: Assert( false ); break; }
return pchBuf; }
//-----------------------------------------------------------------------------
// Purpose: Escapes any single quotes to a string value to double single quotes
// Input: rgchField - text to escape
// cchField - size of text buffer
// Notes: The text will be escaped and expanded in place in the buffer.
// In the worst case, the text may expand by 2x. (If the field is all
// single quotes.) So, you must pass in a buffer which is at least
// twice as long as the text length so we can guarantee to be able to
// escape the string.
//-----------------------------------------------------------------------------
void EscapeStringValue( char *rgchField, int cchField ) { // TODO - what else do we need to escape? %() ...
char *pubCur = rgchField; int nLen = 0; int cSingleQuotes = 0;
// This function gets called on every text field we write but most text fields
// don't need to be escaped, so try to be as fast as possible in the normal case.
// first, walk through the string and count the string length and number of single quotes
while ( *pubCur ) { if ( '\'' == *pubCur ) cSingleQuotes++; nLen ++; pubCur++; }
// if no single quotes, nothing to do
if ( !cSingleQuotes ) return;
// caller must pass in a buffer that's long enough for expansion
Assert( nLen + cSingleQuotes + 1 <= cchField ); if ( !( nLen + cSingleQuotes + 1 <= cchField ) ) return;
// We know exactly how many characters the string will expand by (the # of single quotes). Walk backward
// and copy the characters into the right places. This touches each character only once.
pubCur = rgchField + nLen + cSingleQuotes; *pubCur = 0; pubCur--; while ( pubCur > rgchField && cSingleQuotes > 0 ) { // read pointer is offset from write pointer by # of remaining single quotes
char *pubRead = pubCur - cSingleQuotes; Assert( pubRead >= rgchField ); // copy each character
*pubCur = *pubRead; if ( '\'' == *pubRead ) { // if the character is a single quote, back up one more and insert another single quote to escape it
pubCur --; *pubCur = '\''; // decrement # of single quotes remaining
cSingleQuotes --; Assert( cSingleQuotes >= 0 ); } pubCur--; } } //-----------------------------------------------------------------------------
// Purpose: Adds constraint information to a SQL command to add or remove constraint
// Input: pchTableName - name of table
// pchColumnName - name of column
// nColFlagConstraint - flag with which constraint to
// bForAdd - whether constraint is being added or removed
// pchCmd - buffer to append SQL command to
// cchCmd - size of buffer
//-----------------------------------------------------------------------------
void AppendConstraint( const char *pchTableName, const char *pchColumnName, int nColFlagConstraint, bool bForAdd, bool bClustered, CFmtStrMax & sCmd, int nFillFactor ) { Assert( pchTableName && pchTableName[0] ); Assert( pchColumnName && pchColumnName[0] );
switch ( nColFlagConstraint ) { case k_nColFlagPrimaryKey: sCmd.AppendFormat( " CONSTRAINT %s_%s_PrimaryKey", pchTableName, pchColumnName); if ( bForAdd ) { sCmd += " PRIMARY KEY "; if ( bClustered ) { sCmd.AppendFormat( " CLUSTERED WITH (FILLFACTOR = %d) ", nFillFactor ); } else { sCmd += "NONCLUSTERED"; } } break; case k_nColFlagUnique: /* do nothing - the uniqueness will be handled by creation of an index */ break; case k_nColFlagAutoIncrement: sCmd += " IDENTITY"; break; default: AssertMsg( false, "CSQLThread::AppendContraint: invalid constraint type" ); break; } }
//-----------------------------------------------------------------------------
// Purpose: Adds constraint information to a SQL command to add or remove constraint
// Input: pRecordInfo - record info describing table
// pColumnInfo - record info describing column
// bForAdd - whether constraint is being added or removed
// pchCmd - buffer to append SQL command to
// cchCmd - size of buffer
//-----------------------------------------------------------------------------
void AppendConstraints( const CRecordInfo *pRecordInfo, const CColumnInfo *pColumnInfo, bool bForAdd, CFmtStrMax & sCmd ) { Assert( pRecordInfo != NULL ); Assert( pColumnInfo != NULL );
if ( pColumnInfo->BIsPrimaryKey() ) { // any column in a PK can't be NULL.
if ( bForAdd ) { sCmd += " NOT NULL"; }
// only add primary key constraint here if it is a single-column PK
if ( pRecordInfo->GetPrimaryKeyType() == k_EPrimaryKeyTypeSingle ) { // get the fields on the primary key
const CUtlVector< FieldSet_t > &refFields = pRecordInfo->GetIndexFields( ); int nFillFactor = refFields.Element( pRecordInfo->GetPKIndex() ).GetFillFactor(); AppendConstraint( pRecordInfo->GetName(), pColumnInfo->GetName(), k_nColFlagPrimaryKey, bForAdd, pColumnInfo->BIsClustered(), sCmd, nFillFactor ); } } else if ( pColumnInfo->BIsUnique() ) { AppendConstraint( pRecordInfo->GetName(), pColumnInfo->GetName(), k_nColFlagUnique, bForAdd, pColumnInfo->BIsClustered(), sCmd, 0 ); } if ( pColumnInfo->BIsAutoIncrement() ) { AppendConstraint( pRecordInfo->GetName(), pColumnInfo->GetName(), k_nColFlagAutoIncrement, bForAdd, pColumnInfo->BIsClustered(), sCmd, 0 ); } }
//-----------------------------------------------------------------------------
// Purpose: Generates the "CONSTRAINT ..." text for the table primary key
//-----------------------------------------------------------------------------
void BuildTablePKConstraintText( TSQLCmdStr *psStatement, CRecordInfo *pRecordInfo ) { const FieldSet_t& vecFields = pRecordInfo->GetPKFields( );
psStatement->sprintf( "CONSTRAINT %s_PrimaryKey PRIMARY KEY %s ( ", pRecordInfo->GetName(), vecFields.IsClustered() ? "CLUSTERED" : "NONCLUSTERED" );
for ( int nField = 0; nField < vecFields.GetCount(); nField++ ) { // what field is the next column in our index?
int nThisField = vecFields.GetField( nField ); const CColumnInfo& columnInfo = pRecordInfo->GetColumnInfo(nThisField);
if (nField != 0) { *psStatement += ", "; } *psStatement += columnInfo.GetName(); }
// close our list
*psStatement += ") ";
if ( vecFields.GetFillFactor() != 0 ) { // non-default fill factor, so specify it
psStatement->AppendFormat( " WITH FILLFACTOR = %d ", vecFields.GetFillFactor() ); } }
//-----------------------------------------------------------------------------
// Purpose: Adds constraint information to a SQL command to add or remove table-level constraints
// Input: pRecordInfo - record info describing table
// pchCmd - buffer to append SQL command to
// cchCmd - size of buffer
//-----------------------------------------------------------------------------
void AppendTableConstraints( CRecordInfo *pRecordInfo, CFmtStrMax & sCmd ) { // the only supported table constraint is for PKs or FKs
if ( pRecordInfo->GetPrimaryKeyType() == k_EPrimaryKeyTypeMulti ) { TSQLCmdStr tmp; BuildTablePKConstraintText( &tmp, pRecordInfo ); sCmd += ", "; sCmd += tmp; }
// Look for FKs required on this table
// the only supported table constraint is for PKs or FKs
int cFKs = pRecordInfo->GetFKCount(); for( int i=0; i < cFKs; ++i ) { FKData_t &fkData = pRecordInfo->GetFKData( i );
CFmtStr sColumns, sParentColumns; FOR_EACH_VEC( fkData.m_VecColumnRelations, nCol ) { FKColumnRelation_t &colRelation = fkData.m_VecColumnRelations[nCol]; if ( nCol > 0) { sColumns += ","; sParentColumns += ","; } sColumns += colRelation.m_rgchCol; sParentColumns += colRelation.m_rgchParentCol; }
TSQLCmdStr sTmp; sTmp.sprintf( ", CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s(%s) ON DELETE %s ON UPDATE %s", fkData.m_rgchName, sColumns.Access(), fkData.m_rgchParentTableName, sParentColumns.Access(), PchNameFromEForeignKeyAction( fkData.m_eOnDeleteAction ), PchNameFromEForeignKeyAction( fkData.m_eOnUpdateAction ) );
// add to the command
sCmd += sTmp; } }
//-----------------------------------------------------------------------------
// Purpose: Builds a SQL INSERT statement
// Input: psStatement - The string to put the statement into
// pRecordInfo - record info describing table inserting into
//-----------------------------------------------------------------------------
void BuildInsertStatementText( TSQLCmdStr *psStatement, const CRecordInfo *pRecordInfo ) { psStatement->sprintf("INSERT INTO %s.%s (", GSchemaFull().GetDefaultSchemaNameForCatalog( pRecordInfo->GetESchemaCatalog() ), pRecordInfo->GetName() );
// build a string of the field names
int cColumns = pRecordInfo->GetNumColumns(); int nInsertable = 0; bool bAddedBefore = false; for ( int iColumn = 0; iColumn < cColumns; iColumn++ ) { const CColumnInfo &columnInfo = pRecordInfo->GetColumnInfo( iColumn ); if ( !columnInfo.BIsInsertable() ) continue;
nInsertable++;
if ( bAddedBefore ) psStatement->Append( ',' ); bAddedBefore = true; psStatement->Append( columnInfo.GetName() ); } psStatement->AppendFormat( ") VALUES (%.*s)", GetInsertArgStringChars( nInsertable ), GetInsertArgString() ); }
//-----------------------------------------------------------------------------
// Purpose: Builds a SQL INSERT statement
// IMPORTANT NOTE - This Insert statement will use the Microsoft SQL Server
// specific clause 'OUTPUT Inserted.ColumnName'
// The result of that will be that the SQL statement will return to us
// the columns that could not be specified by the Insert.
// At the time of writing, that is primarily AutoIncrement columns,
// however in theory we should be able to recover any computed column
// from SQL server, with the caveats specified at :
// http://msdn.microsoft.com/en-us/library/ms177564.aspx
//
// Input: psStatement - The output statement string
// pRecordInfo - record info describing table inserting into
//-----------------------------------------------------------------------------
void BuildInsertAndReadStatementText( TSQLCmdStr *psStatement, CUtlVector<int> *pvecOutputFields, const CRecordInfo *pRecordInfo ) { psStatement->sprintf("INSERT INTO %s.%s (", GSchemaFull().GetDefaultSchemaNameForCatalog( pRecordInfo->GetESchemaCatalog() ), pRecordInfo->GetName() );
// build a string of the field names
int nInsertable = 0; int cColumns = pRecordInfo->GetNumColumns(); bool bAddedBefore = false; for ( int iColumn = 0; iColumn < cColumns; iColumn++ ) { const CColumnInfo &columnInfo = pRecordInfo->GetColumnInfo( iColumn ); if ( !columnInfo.BIsInsertable() ) continue;
nInsertable++;
if ( bAddedBefore ) psStatement->Append( ',' ); bAddedBefore = true; psStatement->Append( columnInfo.GetName() ); }
bAddedBefore = false ; int nOutputColumn = 0; for( int iColumn = 0; iColumn < cColumns; iColumn++ ) { const CColumnInfo &columnInfo = pRecordInfo->GetColumnInfo( iColumn ) ; //
// If we can't Insert it - we want SQL Server to tell us what value was stored
// in the column !!
//
if( !columnInfo.BIsInsertable() ) { if( bAddedBefore ) psStatement->Append( ", INSERTED." ); else psStatement->Append( ") OUTPUT INSERTED." ); bAddedBefore = true ; psStatement->Append( columnInfo.GetName() ); pvecOutputFields->AddToTail( iColumn ); nOutputColumn++; } }
// add field values to SQL statement
psStatement->AppendFormat( " VALUES (%.*s)", GetInsertArgStringChars( nInsertable ), GetInsertArgString() ); }
//-----------------------------------------------------------------------------
// Purpose: Builds a SQL MERGE statement update or insert using in-flight values table
// Input: psStatement - The string to put the statement into
// pRecordInfo - record info describing table inserting into
//-----------------------------------------------------------------------------
void BuildMergeStatementTextOnPKWhenMatchedUpdateWhenNotMatchedInsert( TSQLCmdStr *psStatement, const CRecordInfo *pRecordInfo ) { psStatement->sprintf( "MERGE INTO %s.%s WITH( HOLDLOCK, ROWLOCK ) T USING ( VALUES (%.*s) ) AS S(", GSchemaFull().GetDefaultSchemaNameForCatalog( pRecordInfo->GetESchemaCatalog() ), pRecordInfo->GetName(), GetInsertArgStringChars( pRecordInfo->GetNumColumns() ), GetInsertArgString() );
{ int cColumns = pRecordInfo->GetNumColumns(); for ( int iColumn = 0; iColumn < cColumns; iColumn++ ) { const CColumnInfo &columnInfo = pRecordInfo->GetColumnInfo( iColumn ); if ( iColumn ) psStatement->Append( ',' ); psStatement->Append( columnInfo.GetName() ); } }
psStatement->Append( ") ON " );
// build a string of the PK columns
const FieldSet_t &fsPK = pRecordInfo->GetIndexFields()[pRecordInfo->GetPKIndex()]; { int cColumns = fsPK.GetCount(); for ( int iColumn = 0; iColumn < cColumns; iColumn++ ) { const CColumnInfo &columnInfo = pRecordInfo->GetColumnInfo( fsPK.GetField( iColumn ) ); if ( iColumn ) psStatement->Append( " AND " ); psStatement->Append( "T." ); psStatement->Append( columnInfo.GetName() ); psStatement->Append( "=S." ); psStatement->Append( columnInfo.GetName() ); } }
psStatement->Append( " WHEN MATCHED THEN UPDATE SET " );
// build the update string
{ int cColumns = pRecordInfo->GetNumColumns(); bool bAddedBefore = false; for ( int iColumn = 0; iColumn < cColumns; iColumn++ ) { bool bThisColumnIsPartOfPK = false; for ( int ipkCheck = 0; ipkCheck < fsPK.GetCount(); ++ipkCheck ) { if ( iColumn == fsPK.GetField( ipkCheck ) ) { bThisColumnIsPartOfPK = true; break; } } if ( bThisColumnIsPartOfPK ) continue;
const CColumnInfo &columnInfo = pRecordInfo->GetColumnInfo( iColumn ); if ( bAddedBefore ) psStatement->Append( ',' ); bAddedBefore = true; psStatement->Append( columnInfo.GetName() ); psStatement->Append( "=S." ); psStatement->Append( columnInfo.GetName() ); } }
psStatement->Append( " WHEN NOT MATCHED BY TARGET THEN INSERT (" );
// build a string of the field names
{ int cColumns = pRecordInfo->GetNumColumns(); bool bAddedBefore = false; for ( int iColumn = 0; iColumn < cColumns; iColumn++ ) { const CColumnInfo &columnInfo = pRecordInfo->GetColumnInfo( iColumn ); if ( !columnInfo.BIsInsertable() ) continue;
if ( bAddedBefore ) psStatement->Append( ',' ); bAddedBefore = true; psStatement->Append( columnInfo.GetName() ); } }
psStatement->Append( ") VALUES (" ); { int cColumns = pRecordInfo->GetNumColumns(); bool bAddedBefore = false; for ( int iColumn = 0; iColumn < cColumns; iColumn++ ) { const CColumnInfo &columnInfo = pRecordInfo->GetColumnInfo( iColumn ); if ( !columnInfo.BIsInsertable() ) continue;
if ( bAddedBefore ) psStatement->Append( ',' ); bAddedBefore = true; psStatement->Append( "S." ); psStatement->Append( columnInfo.GetName() ); } } psStatement->Append( ");" ); }
//-----------------------------------------------------------------------------
// Purpose: Builds a SQL MERGE statement using CTE_MergeParams as supplied table holding rows
// Input: psStatement - The string to put the statement into
// pRecordInfo - record info describing table inserting into
//-----------------------------------------------------------------------------
void BuildMergeStatementTextOnPKWhenNotMatchedInsert( TSQLCmdStr *psStatement, const CRecordInfo *pRecordInfo ) { psStatement->sprintf( "MERGE INTO %s.%s WITH( HOLDLOCK, ROWLOCK ) T USING ( VALUES (%.*s) ) AS S(", GSchemaFull().GetDefaultSchemaNameForCatalog( pRecordInfo->GetESchemaCatalog() ), pRecordInfo->GetName(), GetInsertArgStringChars( pRecordInfo->GetNumColumns() ), GetInsertArgString() );
{ int cColumns = pRecordInfo->GetNumColumns(); for ( int iColumn = 0; iColumn < cColumns; iColumn++ ) { const CColumnInfo &columnInfo = pRecordInfo->GetColumnInfo( iColumn ); if ( iColumn ) psStatement->Append( ',' ); psStatement->Append( columnInfo.GetName() ); } }
psStatement->Append( ") ON " );
// build a string of the PK columns
const FieldSet_t &fsPK = pRecordInfo->GetIndexFields()[pRecordInfo->GetPKIndex()]; { int cColumns = fsPK.GetCount(); for ( int iColumn = 0; iColumn < cColumns; iColumn++ ) { const CColumnInfo &columnInfo = pRecordInfo->GetColumnInfo( fsPK.GetField( iColumn ) ); if ( iColumn ) psStatement->Append( " AND " ); psStatement->Append( "T." ); psStatement->Append( columnInfo.GetName() ); psStatement->Append( "=S." ); psStatement->Append( columnInfo.GetName() ); } }
psStatement->Append( " WHEN NOT MATCHED BY TARGET THEN INSERT (" );
// build a string of the field names
{ int cColumns = pRecordInfo->GetNumColumns(); bool bAddedBefore = false; for ( int iColumn = 0; iColumn < cColumns; iColumn++ ) { const CColumnInfo &columnInfo = pRecordInfo->GetColumnInfo( iColumn ); if ( !columnInfo.BIsInsertable() ) continue;
if ( bAddedBefore ) psStatement->Append( ',' ); bAddedBefore = true; psStatement->Append( columnInfo.GetName() ); } }
psStatement->Append( ") VALUES (" ); { int cColumns = pRecordInfo->GetNumColumns(); bool bAddedBefore = false; for ( int iColumn = 0; iColumn < cColumns; iColumn++ ) { const CColumnInfo &columnInfo = pRecordInfo->GetColumnInfo( iColumn ); if ( !columnInfo.BIsInsertable() ) continue;
if ( bAddedBefore ) psStatement->Append( ',' ); bAddedBefore = true; psStatement->Append( "S." ); psStatement->Append( columnInfo.GetName() ); } } psStatement->Append( ");" ); }
void BuildSelectStatementText( TSQLCmdStr *psStatement, const CColumnSet & selectSet, const char *pchTopClause ) { *psStatement = "SELECT ";
if( pchTopClause ) { psStatement->Append( pchTopClause ); psStatement->Append( ' ' ); }
// build a string of the field names
bool bAddedBefore = false; FOR_EACH_COLUMN_IN_SET( selectSet, nColumnIndex ) { const CColumnInfo &columnInfo = selectSet.GetColumnInfo( nColumnIndex ); if ( bAddedBefore ) psStatement->Append( ',' ); bAddedBefore = true; psStatement->Append( columnInfo.GetName() ); }
psStatement->Append( " FROM "); psStatement->Append( GSchemaFull().GetDefaultSchemaNameForCatalog( selectSet.GetRecordInfo()->GetESchemaCatalog() ) ); psStatement->Append( '.' ); psStatement->Append( selectSet.GetRecordInfo()->GetName() ); }
//-----------------------------------------------------------------------------
// Purpose: Builds a SQL UPDATE statement
// Input: pRecordInfo - record info describing table inserting into
// bForPreparedStatement - if true, inserts values as '?' for later
// binding. If false, values are inserted in text.
// pchStatement - pointer to buffer to build statement in
// cchStatement - size of buffer
// pSQLRecord - pointer to record with data to update
// iColumnMatch - column to use for WHERE condition
// pvMatch - data value to use for WHERE condition
// cubMatch - size of pvMatch data
// rgiColumnUpdate - array of column #'s to update
// ciColumnUpdate - count of column #'s to update
//-----------------------------------------------------------------------------
void BuildUpdateStatementText( TSQLCmdStr *psStatement, const CColumnSet & updateColumns ) { // build the UPDATE statement
psStatement->sprintf( "UPDATE %s.%s SET ", GSchemaFull().GetDefaultSchemaNameForCatalog( updateColumns.GetRecordInfo()->GetESchemaCatalog() ), updateColumns.GetRecordInfo()->GetName() );
// add each field we're updating to the UPDATE statement
FOR_EACH_COLUMN_IN_SET( updateColumns, nColumnIndex ) { const CColumnInfo &columnInfo = updateColumns.GetColumnInfo( nColumnIndex );
if( nColumnIndex > 0 ) psStatement->Append( ',' ); psStatement->Append( columnInfo.GetName() ); psStatement->Append( "=?" ); } }
//-----------------------------------------------------------------------------
// Purpose: Builds a SQL UPDATE statement
//-----------------------------------------------------------------------------
void BuildDeleteStatementText( TSQLCmdStr *psStatement, const CRecordInfo *pRecordInfo ) { psStatement->sprintf( "DELETE FROM %s.%s", GSchemaFull().GetDefaultSchemaNameForCatalog( pRecordInfo->GetESchemaCatalog() ), pRecordInfo->GetName() ); }
//-----------------------------------------------------------------------------
// Purpose: Builds a where clause for the provided fields
//-----------------------------------------------------------------------------
void AppendWhereClauseText( TSQLCmdStr *psClause, const CColumnSet & columnSet ) { // add each field we're updating to the UPDATE statement
FOR_EACH_COLUMN_IN_SET( columnSet, nColumnIndex ) { const CColumnInfo &columnInfo = columnSet.GetColumnInfo( nColumnIndex );
if( nColumnIndex > 0 ) psClause->Append( " AND "); psClause->Append( columnInfo.GetName() ); psClause->Append( "=?" ); } }
//-----------------------------------------------------------------------------
// Purpose: Builds an OUTPUT [fields] INTO [table] for the provided fields/data
//-----------------------------------------------------------------------------
void BuildOutputClauseText( TSQLCmdStr *psClause, const CColumnSet & columnSet ) { *psClause = " OUTPUT ";
FOR_EACH_COLUMN_IN_SET( columnSet, nColumnIndex ) { const CColumnInfo &columnInfo = columnSet.GetColumnInfo( nColumnIndex );
if( nColumnIndex > 0 ) psClause->Append( ", "); psClause->Append( " ? AS " ); psClause->Append( columnInfo.GetName() ); }
psClause->Append( " INTO " ); psClause->Append( columnSet.GetRecordInfo()->GetName() ); }
////-----------------------------------------------------------------------------
//// Purpose: our own special "upsert" into a column with a uniqueness constraint
////-----------------------------------------------------------------------------
//EResult UpdateOrInsertUnique( CSQLAccess &sqlAccess, int iTable, int iField, CRecordBase *pRecordBase, int iIndexID )
//{
// // attempt an update - if it fails due to duplicate primary key, they can't use this
// // url (it's taken) - if it succeeds but affects 0 rows, they didn't have a vanity url
// // and we need to do an insert (which could again fail due to primary key constraints)
// int cRecordsUpdated = 0;
// bool bRet = sqlAccess.BYieldingUpdateFieldFromRecordWithIndex( iTable, &cRecordsUpdated, iField, pRecordBase, iIndexID );
// if ( !bRet )
// {
// // ODBC is the suck - give me Spring JDBC templates, please.
// if ( sqlAccess.GetLastError()->IsDuplicateInsertAttempt() )
// {
// return k_EResultDuplicateName;
// }
// return k_EResultFail;
// }
// else if ( 0 == cRecordsUpdated )
// {
// // the user didn't have an entry, so insert one.
// bRet = sqlAccess.BYieldingInsertRecord( iTable, pRecordBase );
// if ( !bRet )
// {
// // ODBC is the suck - give me Spring JDBC templates, please.
// if ( sqlAccess.GetLastError()->IsDuplicateInsertAttempt() )
// {
// return k_EResultDuplicateName;
// }
// return k_EResultFail;
// }
// }
// return k_EResultOK;
//}
//
} // namespace GCSDK
|