mirror of
https://github.com/nillerusr/source-engine.git
synced 2026-08-08 01:39:36 +00:00
1
This commit is contained in:
@@ -0,0 +1,918 @@
|
||||
//========= 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
|
||||
Reference in New Issue
Block a user