blob: 4ac4c38689a0ae419f1503f9460073fc03c4858f [file] [log] [blame]
/**********************************************************************
// @@@ START COPYRIGHT @@@
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
// @@@ END COPYRIGHT @@@
**********************************************************************/
/* -*-C++-*-
*****************************************************************************
*
* File: DefaultValidator.cpp
* Description: Validation of Defaults values.
*
*
*****************************************************************************
*/
#define SQLPARSERGLOBALS_NADEFAULTS
#define SQLPARSERGLOBALS_FLAGS
#include "Platform.h"
#include <ctype.h>
#include <stdio.h>
#include "charinfo.h"
#include "CmpCommon.h"
#include "ComMPLoc.h"
#include "DefaultValidator.h"
#include "ObjectNames.h"
#include "SchemaDB.h"
#include "ComLocationNames.h" // for ComIsGuardianVolumeNamePartValid()
#include "ComSqlText.h"
#include "ComSchemaName.h" // for ComSchemaName
#include "BindWA.h" // for extractOverrideSchemas()
#include "SqlParserGlobals.h" // must be last
#define ERRWARN(msg) ToErrorOrWarning(msg, errOrWarn)
void DefaultValidator::applyUpper(NAString &value) const
{
switch (caseSensitive()) {
case CASE_SENSITIVE: return;
case CASE_INSENSITIVE: value.toUpper(); return;
case CASE_SENSITIVE_ANSI: {
// Make copy of value so we can own its data()
// for casting away const-ness.
NAString tmp(value);
char *s = (char *)tmp.data();
for (NABoolean quoted = FALSE; *s; s++) {
if (*s == '"')
quoted = !quoted;
else if (!quoted)
*s = toupper(*s);
}
value = tmp;
return;
}
default: CMPASSERT(FALSE);
}
}
NAString DefaultValidator::getTypeText(DefaultValidatorType vt)
{
switch (vt) {
case VALID_ANY: return "ANY";
case VALID_INT: return "INTEGER";
case VALID_UINT: return "INTEGER UNSIGNED";
case VALID_FLT: return "FLOAT";
case VALID_KWD: return "DEFAULTS-KEYWORD";
default: return "???dv???";
}
}
Int32 DefaultValidator::validate( const char *,
const NADefaults *,
Int32,
Int32,
float *) const
{
return TRUE; // anything is valid; no error
}
Int32 ValidateKeyword::validate( const char *value,
const NADefaults *nad,
Int32 attrEnum,
Int32 errOrWarn,
float *) const
{
NAString tmp(value);
DefaultToken tok = nad->token(attrEnum, tmp, TRUE, errOrWarn);
return (tok != DF_noSuchToken);
}
Int32 ValidateTraceStr::validate( const char *value,
const NADefaults *nad,
Int32 attrEnum,
Int32 errOrWarn,
float *alreadyHaveFloat) const
{
// const char *curr = value;
return TRUE;
}
Int32 ValidateAnsiList::validate( const char *value,
const NADefaults *nad,
Int32 attrEnum,
Int32 errOrWarn,
float *) const
{
// Validate using a copy of "*value"
char tempStr[1000]; // max length of ATTR_VALUE
if ( strlen(value) >= 1000 ) return FALSE; // just in case
if ( strlen(value) == 0 ) return TRUE; // empty string ATTR_VALUE is OK
strcpy(tempStr, value);
// prepare to extract the partitions/tokens from the default string
const char *token, *sep = " ,:" ;
token = strtok( tempStr, sep );
// iterate thru list of volume names; return false iff any name is invalid
// (Also an appropriate error/warning would be issued.)
while ( token != NULL ) {
NAString tokenObj(token);
Int32 countPeriods = 0, inBetween = 0;
NABoolean anyError = tokenObj.isNull() ;
// check three part ANSI name
for (Int32 i = 0; !anyError && i < (Int32)tokenObj.length() ; i++ ) {
if ( ComSqlText.isDigit(token[i]) ||
ComSqlText.isSimpleLatinLetter(token[i]) ) inBetween++;
else {
if ( ComSqlText.getPeriod() == token[i] && // it is a period
countPeriods++ < 2 ) {
if ( inBetween == 0 ) anyError = TRUE; // no CATALOG or SCHEMA
else inBetween = 0 ; // start counting the next ( SCHEMA or NAME )
}
else anyError = TRUE;
}
}
if ( countPeriods != 2 || inBetween == 0 ) anyError = TRUE;
if ( anyError ) {
if (errOrWarn)
*CmpCommon::diags() << DgSqlCode(ERRWARN(2055)) << DgString0(token)
<< DgString1("INVALID QUALIFIED NAME");
return FALSE;
}
token = strtok( NULL, sep );
}
return TRUE;
}
Int32 ValidateRoleNameList::validate( const char *value,
const NADefaults *nad,
Int32 attrEnum,
Int32 errOrWarn,
float *) const
{
// Validate a list of role names. Based on ValidateAnsiList
// List format: comma separated list of role names which use either . or _
// example: "role.user1", "ROLE.user2", "role_user3"
// SeaQuest example: DB__Root, DB__Services, db_role12, db_user3
// Validate using a copy of "*value"
char tempStr[1000]; // max length of ATTR_VALUE
if ( strlen(value) >= 1000 ) return FALSE; // just in case
if ( strlen(value) == 0 ) return TRUE; // empty string ATTR_VALUE is OK
strcpy(tempStr, value);
// prepare to extract the role names/tokens from the default string
const char *token, *sep = " ," ;
token = strtok( tempStr, sep );
// iterate thru list of role names; return false iff any name is invalid
// (Also an appropriate error/warning would be issued.)
while ( token != NULL ) {
NAString tokenObj(token);
Lng32 sqlcode = ToInternalIdentifier(tokenObj, TRUE /*upCase*/,
FALSE /*acceptCircumflex*/,
0 /*toInternalIdentifierFlags*/);
if (sqlcode && ABS(sqlcode) != 3128)
{
// 3004 A delimited identifier must contain at least one character.
// 3118 Identifier too long.
// 3127 Illegal character in identifier $0~string0.
// 3128 $1~string1 is a reserved word.
if (errOrWarn)
*CmpCommon::diags() << DgSqlCode(ERRWARN(2055)) << DgString0(token)
<< DgString1("INVALID AUTHORIZATION IDENTIFIER");
}
token = strtok( NULL, sep );
}
return TRUE;
}
Int32 ValidatePOSTableSizes::validate(const char *value,
const NADefaults *nad,
Int32 attrEnum,
Int32 errOrWarn,
float *) const
{
char tempStr[1000]; // max length of ATTR_VALUE
if ( strlen(value) == 0 ) return TRUE; // empty string ATTR_VALUE is OK
if ( strlen(value) > 1000 )
{
*CmpCommon::diags() << DgSqlCode(ERRWARN(2055))
<< DgString0(value)
<< DgString1(nad->lookupAttrName(attrEnum, errOrWarn));
return FALSE;
}
strcpy(tempStr, value);
const char *token, *sep = " ,";
token = strtok(tempStr, sep);
float initialSize = -1;
float maxSize = -1;
ValidateUInt uint;
if (token != NULL)
{
// check if the first value is an unsigned int
if (!uint.validate(token, nad, attrEnum, -1))
return FALSE;
else
{
sscanf(token, "%g", &initialSize);
token = strtok(NULL, sep);
if (token != NULL)
{
// check if the second value is an unsigned int
if (!uint.validate(token, nad, attrEnum, -1))
return FALSE;
else
{
// if there is a third value or max table size is smaller than
// initial table size raise an error
sscanf(token, "%g", &maxSize);
token = strtok(NULL, sep);
if (token != NULL || maxSize < initialSize)
{
*CmpCommon::diags() << DgSqlCode(ERRWARN(2055))
<< DgString0(value)
<< DgString1(nad->lookupAttrName(attrEnum, errOrWarn));
if (maxSize < initialSize)
{
*CmpCommon::diags() << DgSqlCode(ERRWARN(2077))
<< DgInt0((Lng32)maxSize)
<< DgInt1((Lng32)initialSize);
}
return FALSE;
}
}
}
}
}
else
{
*CmpCommon::diags() << DgSqlCode(ERRWARN(2055))
<< DgString0(value)
<< DgString1(nad->lookupAttrName(attrEnum, errOrWarn));
return FALSE;
}
return TRUE;
}
Int32 ValidateNumericRange::validate( const char *value,
const NADefaults *nad,
Int32 attrEnum,
Int32 errOrWarn,
float *alreadyHaveFloat) const
{
Int32 isValid = FALSE, rangeOK = FALSE, multipleOK = FALSE;
float flt;
if (alreadyHaveFloat)
flt = *alreadyHaveFloat;
else {
isValid = nad->validateFloat(value, flt, attrEnum, SilentIfSYSTEM);
if (isValid == SilentIfSYSTEM) return SilentIfSYSTEM;
}
if (alreadyHaveFloat || isValid) {
rangeOK = (min_ <= flt && flt <= max_);
if (rangeOK) {
multipleOK = (multiple_ == 0 || ((ULng32)flt) % multiple_ == 0);
if (multipleOK) {
return TRUE; // valid
}
}
}
if (alreadyHaveFloat) *alreadyHaveFloat = min_;
if (errOrWarn) {
*CmpCommon::diags() << DgSqlCode(ERRWARN(2055))
<< DgString0(value)
<< DgString1(nad->lookupAttrName(attrEnum, errOrWarn));
if (!rangeOK) {
char minbuf[50], maxbuf[50];
if (type_ == VALID_INT) {
sprintf(minbuf, "%d", (Lng32)min_);
// A fudge factor of 64 is added for NT/Yos/Neo to include
// the precision lost. The value printed is 2147483520,
// while values are accepted till 2147483584.
sprintf(maxbuf, "%d", (Lng32)max_+64);
}
else if (type_ == VALID_UINT) {
sprintf(minbuf, "%u", (ULng32)min_);
if (max_ == 2147483520)
sprintf(maxbuf, "%u", (ULng32)max_+64);
else
sprintf(maxbuf, "%u", (ULng32)max_);
}
else {
sprintf(minbuf, "%g", min_);
sprintf(maxbuf, "%g", max_);
}
switch (attrEnum)
{
case HIVE_INSERT_ERROR_MODE:
{
sprintf(minbuf, "%u", 0);
sprintf(maxbuf, "%u", 3);
}
break;
default:
{
// do nothing
}
}
NAString range = NAString("[") + minbuf + "," + maxbuf + "]";
*CmpCommon::diags() << DgSqlCode(ERRWARN(2056)) << DgString0(range);
}
if (multiple_ && !multipleOK)
*CmpCommon::diags() << DgSqlCode(ERRWARN(2057)) << DgInt0(multiple_);
}
return FALSE; // not valid
}
//-----------------------------------------------------------------------------
// ValidateCollationList:
// The motivation for this class is as a temporary STOPGAP measure
// in MX-NSK-Rel1, to (half)support MP COLLATEd columns.
// See the MP_COLLATIONS default attr, the ReadTableDef code that
// also calls the insertIntoCDB (CollationDB) method below, and
// the "SYNTAX OF THE MP_COLLATIONS LIST" comments below.
//
// A more fully fledged support would require a new ReadTableDef request
// to garner information about the collation from the MP CPRULES table,
// namely, a) validation that the collation exists,
// b) the CPRULES.CHARACTERISTICS value,
// c) CPRULES.CHARACTERSET value.
//-----------------------------------------------------------------------------
Int32 ValidateCollationList::validate(const char *value,
const NADefaults *nad,
Int32 attrEnum,
Int32 errOrWarn,
float *) const
{
if (errOrWarn) {
CMPASSERT(nad && attrEnum);
errOrWarn = +1; // warnings only (else silent)
}
// Cast away constness on various private members which are really implicit
// arguments (the validate method by itself vs. from insertIntoCDB).
ValidateCollationList *ncThis = (ValidateCollationList *)this;
#ifndef NDEBUG
if (getenv("NCHAR_DEBUG")) ncThis->formatRetry_ = TRUE;
#endif
// If cdb is NULL, we are validating only; if nonNULL, we are inserting.
CollationDB *cdb = ncThis->cdb_;
const ComMPLoc *defaultMPLoc = NULL;
const SchemaName *defaultSchema = NULL;
if (cdb) {
CMPASSERT(sdb_);
defaultMPLoc = &SqlParser_MPLOC;
defaultSchema = &sdb_->getDefaultSchema();
}
// Make our own copy of the value string data,
// on which we can safely cast away const-ness for the loop,
// which chops the list up into individual names by replacing
// (in place, i.e. w/o additional copying)
// each semicolon with a string-terminating nul.
NAString collNAS(value);
char *collList = (char *)collNAS.data();
while (*collList) {
// SYNTAX OF THE MP_COLLATIONS LIST:
//
// The list may look like any of these:
// '' (empty)
// 'collname'
// 'c1; sv.c2; $v.sv.c3; \s.$v.sv.c4'
// 'c1; sv.c2; $v.sv.c3; \s.$v.sv.c4;'
// ' = c1; =sv.c2;=$v.sv.c3;\s.$v.sv.c4;'
// We also ignore pathologies like
// ' ' or '=========;;;;;===; ; = = == =; ; ; '
//
// The '=' flags the following collation name as one that has a 1:1 mapping,
// i.e. its CPRULES.CHARACTERISTICS == 'O',
// i.e. in Rel1 it allows only equality comparisons
// (SQL '=', '<>', DISTINCT, GROUP BY),
// although no other operations
// (other predicates, ordering, MIN/MAX, partitioning)
// would be disallowed -- the column wouldn't even be updatable,
// but it could be at least read and appear in some limited other places.
//
// The absence of a '=' means the collation's CHARACTERISTICS == 'N',
// and in Rel1 we cannot support *any* comparisons, predicates, MIN/MAX,
// ordering, partitioning, on it --
// it's basically just a name attached to a column,
// but the intent was to allow the column to be at least readable.
//
// See ReadTableDef and NATable to see if MP COLLATEd column support
// is or is not currently being enabled.
// Find the next semicolon separator outside of delim-ident quotes,
// or find end of string.
// Set collStr to be the fragment up to (excluding) the semicolon or zero;
// set collList to be the rest of itself (after the semicolon).
//
char *s = collList;
for (NABoolean quoted = FALSE; *s; s++) {
if (*s == '"')
quoted = !quoted;
else if (*s == ';' && !quoted)
break;
}
char sep = *s; // sep is either ';' or '\0'
*s = '\0';
NAString collStr(collList);
collList = sep ? ++s : s; // get past ';' OR stay on final '\0'
CollationInfo::CollationFlags flags = CollationInfo::ALL_CMP_ILLEGAL;
TrimNAStringSpace(collStr); // remove leading/trailing blanks
size_t i = 0, n = collStr.length();
while (i < n)
if (collStr[i] == '=' || collStr[i] == ' ')
i++; // get past leading '=' (and blanks)
else
break;
if (i) { // an '=' was seen, EQ_NE_CMP is LEGAL
flags = CollationInfo::ORDERED_CMP_ILLEGAL;
collStr.remove(0, i);
}
if (!collStr.isNull()) {
NABoolean ok = FALSE;
NABoolean nsk = formatNSK_;
NABoolean retry = formatRetry_;
retry_as_other_format:
if (nsk) {
ComMPLoc collMP(collStr, ComMPLoc::FILE);
if (collMP.isValid(ComMPLoc::FILE)) {
ok = TRUE;
if (cdb) {
ncThis->lastCoInserted_ =
cdb->insert(collMP, defaultMPLoc, flags);
}
}
}
else {
ComObjectName collMX(collStr);
if (collMX.isValid()) {
ok = TRUE;
if (cdb) {
QualifiedName collQN(collMX);
ncThis->lastCoInserted_ =
cdb->insert(collQN, defaultSchema, flags);
}
}
}
if (ok) ncThis->cntCoParsed_++;
if (!ok && retry) {
retry = FALSE; // Retry only once,
nsk = !nsk; // using the other name format.
goto retry_as_other_format;
}
if (!ok && errOrWarn)
*CmpCommon::diags() << DgSqlCode(ERRWARN(2055))
<< DgString0(collStr)
<< DgString1(nad->lookupAttrName(attrEnum, errOrWarn));
} // !collStr.isNull()
} // while
return TRUE; // warnings only; all is valid
}
UInt32 ValidateCollationList::insertIntoCDB(SchemaDB *sdb,
CollationDB *cdb,
const char *value,
NABoolean nsk)
{
NABoolean savformatNSK = formatNSK_;
formatNSK_ = nsk;
lastCoInserted_ = CharInfo::UNKNOWN_COLLATION;
cntCoParsed_ = 0;
CMPASSERT(!cdb_ && !sdb_); // our kludgy/dodgy passing mechanism...
cdb_ = cdb;
sdb_ = sdb;
validate(value, NULL, 0, 0/*silent*/);
cdb_ = NULL;
sdb_ = NULL;
formatNSK_ = savformatNSK;
#ifndef NDEBUG
if (getenv("NCHAR_DEBUG")) CollationDB::Display();
#endif
return cntCoParsed_;
}
// validate OVERRIDE_SCHEMA FROM_SCHEMA:TO_SCHEMA setting
// format validation for xxx:yyy
Int32 ValidateOverrideSchema::validate( const char *value,
const NADefaults *nad,
Int32 attrEnum,
Int32 errOrWarn,
float *flt ) const
{
NABoolean ok = TRUE;
Int32 len = strlen(value);
if (len == 0) // empty is ok
return ok;
NAString fromSchema, toSchema;
extractOverrideSchemas(value, fromSchema, toSchema);
if ( (fromSchema.isNull()) || (toSchema.isNull()) )
ok = FALSE;
else
{
ComSchemaName targetSchema(toSchema);
if (!targetSchema.isValid())
ok = FALSE;
else
if (fromSchema!="*") // reserve * for wildcard operation
{
ComSchemaName sourceSchema(fromSchema);
if (!sourceSchema.isValid())
ok = FALSE;
}
}
if (!ok && errOrWarn)
*CmpCommon::diags() << DgSqlCode(ERRWARN(2055))
<< DgString0(value)
<< DgString1(nad->lookupAttrName(attrEnum, errOrWarn));
return ok;
}
// validate PUBLIC_SCHEMA_NAME setting
// setting may be schema or catalog.schema
Int32 ValidatePublicSchema::validate( const char *value,
const NADefaults *nad,
Int32 attrEnum,
Int32 errOrWarn,
float *flt ) const
{
NABoolean ok = TRUE;
Int32 len = strlen(value);
if (len == 0) // empty is ok
return ok;
ComSchemaName pubSchema(value);
if (!pubSchema.isValid())
ok = FALSE;
if (!ok && errOrWarn)
*CmpCommon::diags() << DgSqlCode(ERRWARN(2055))
<< DgString0(value)
<< DgString1(nad->lookupAttrName(attrEnum, errOrWarn));
return ok;
}
// validate REPLICATE_IO_VERSION setting
// setting may be schema or catalog.schema
Int32 ValidateReplIoVersion::validate( const char *value,
const NADefaults *nad,
Int32 attrEnum,
Int32 errOrWarn,
float *alreadyHaveFloat ) const
{
Int32 isValid = FALSE;
float flt;
Int32 min;
if (alreadyHaveFloat)
flt = *alreadyHaveFloat;
else {
isValid = nad->validateFloat(value, flt, attrEnum, SilentIfSYSTEM);
if (isValid == SilentIfSYSTEM) return SilentIfSYSTEM;
}
if (alreadyHaveFloat || isValid)
{
if (Get_SqlParser_Flags(INTERNAL_QUERY_FROM_EXEUTIL))
min = 1;
else
min = min_;
if (flt >= min && flt <= max_)
return TRUE; // valid
}
if (alreadyHaveFloat) *alreadyHaveFloat = (float)min_;
if (errOrWarn)
*CmpCommon::diags() << DgSqlCode(ERRWARN(2055))
<< DgString0(value)
<< DgString1(nad->lookupAttrName(attrEnum, errOrWarn));
return FALSE;
}
// validate parameter for MV_AGE
Int32 ValidateMVAge::validate( const char *value,
const NADefaults *nad,
Int32 attrEnum,
Int32 errOrWarn,
float *alreadyHaveFloat ) const
{
Int32 isOK = FALSE;
float number=0;
char textChars[20];
if (strlen(value) < 15)
{
if (sscanf(value, "%f %s", &number, textChars) == 2)
{
const NAString text(textChars);
if (!text.compareTo("Seconds", NAString::ignoreCase))
{
isOK = TRUE;
}
else if (!text.compareTo("Minutes", NAString::ignoreCase))
{
isOK = TRUE;
}
else if (!text.compareTo("Hours", NAString::ignoreCase))
{
isOK = TRUE;
}
else if (!text.compareTo("Days", NAString::ignoreCase))
{
isOK = TRUE;
}
}
}
if (!isOK)
{
if (errOrWarn)
*CmpCommon::diags() << DgSqlCode(ERRWARN(2055))
<< DgString0(value)
<< DgString1(nad->lookupAttrName(attrEnum, errOrWarn));
}
return isOK;
}