static Variant variant_in_int(FunctionCallInfo fcinfo, char *input, int variant_typmod) { VariantCache *cache; bool isnull; Oid intTypeOid = InvalidOid; int32 typmod = 0; text *orgType; text *orgData; VariantInt vi = palloc0(sizeof(*vi)); /* Eventually getting rid of this crap, so segregate it */ intTypeOid = getIntOid(); FmgrInfo proc; Datum composite; HeapTupleHeader composite_tuple; Oid typioparam; Oid typIoFunc; /* Cast input data to our internal composite type */ getTypeInputInfo(intTypeOid, &typIoFunc, &typioparam); fmgr_info_cxt(typIoFunc, &proc, fcinfo->flinfo->fn_mcxt); composite=InputFunctionCall(&proc, input, typioparam, typmod); /* Extract data from internal composite type */ composite_tuple=DatumGetHeapTupleHeader(composite); orgType = (text *) GetAttributeByNum( composite_tuple, 1, &isnull ); if (isnull) elog(ERROR, "original_type of variant must not be NULL"); orgData = (text *) GetAttributeByNum( composite_tuple, 2, &vi->isnull ); /* End crap */ #ifdef LONG_PARSETYPE parseTypeString(text_to_cstring(orgType), &vi->typid, &vi->typmod, false); #else parseTypeString(text_to_cstring(orgType), &vi->typid, &vi->typmod); #endif /* * Verify we've been handed a valid typmod */ variant_get_variant_name(variant_typmod, vi->typid, false); cache = get_cache(fcinfo, vi, IOFunc_input); if (!vi->isnull) /* Actually need to be using stringTypeDatum(Type tp, char *string, int32 atttypmod) */ vi->data = InputFunctionCall(&cache->proc, text_to_cstring(orgData), cache->typioparam, vi->typmod); return make_variant(vi, fcinfo, IOFunc_input); }
int pg_to_regtype(char *typ_name) { Oid result; int32 typmod; /* * Invoke the full parser to deal with special cases such as array syntax. */ #if PG_VERSION_NUM < 90400 parseTypeString(typ_name, &result, &typmod); #else parseTypeString(typ_name, &result, &typmod, true); #endif if (OidIsValid(result)) return result; else return -1; }
bool MySqlDbAdapter::GetColumns(Table* pTab) { DatabaseLayerPtr dbLayer = this->GetDatabaseLayer(wxT("")); if (!dbLayer->IsOpen()) return NULL; // loading columns //TODO:SQL: DatabaseResultSet *database = dbLayer->RunQueryWithResults(wxString::Format(wxT("SHOW COLUMNS IN `%s`.`%s`"),pTab->GetParentName().c_str(),pTab->GetName().c_str())); while (database->Next()) { IDbType* pType = parseTypeString(database->GetResultString(2)); if (pType) { Column* pCol = new Column(database->GetResultString(1),pTab->GetName(), pType); pTab->AddChild(pCol); } } dbLayer->CloseResultSet(database); //TODO:SQL: wxString constrSql = wxT("SELECT K.CONSTRAINT_SCHEMA, K.CONSTRAINT_NAME,K.TABLE_NAME,K.COLUMN_NAME,K.REFERENCED_TABLE_NAME,K.REFERENCED_COLUMN_NAME,R.UPDATE_RULE, R.DELETE_RULE FROM information_schema.KEY_COLUMN_USAGE K LEFT JOIN information_schema.REFERENTIAL_CONSTRAINTS R ON R.CONSTRAINT_NAME = K.CONSTRAINT_NAME AND K.CONSTRAINT_SCHEMA = R.CONSTRAINT_SCHEMA WHERE K.CONSTRAINT_SCHEMA = '%s' AND K.TABLE_NAME = '%s'"); database = dbLayer->RunQueryWithResults(wxString::Format(constrSql, pTab->GetParentName().c_str(),pTab->GetName().c_str())); while (database->Next()) { Constraint* constr = new Constraint(); constr->SetName(database->GetResultString(wxT("CONSTRAINT_NAME"))); constr->SetLocalColumn(database->GetResultString(wxT("COLUMN_NAME"))); constr->SetType(Constraint::primaryKey); if (database->GetResultString(wxT("REFERENCED_TABLE_NAME")) != wxT("") ) { constr->SetType(Constraint::foreignKey); constr->SetRefTable(database->GetResultString(wxT("REFERENCED_TABLE_NAME"))); constr->SetRefCol(database->GetResultString(wxT("REFERENCED_COLUMN_NAME"))); wxString onDelete = database->GetResultString(wxT("UPDATE_RULE")); if (onDelete == wxT("RESTRICT")) constr->SetOnUpdate(Constraint::restrict); if (onDelete == wxT("CASCADE")) constr->SetOnUpdate(Constraint::cascade); if (onDelete == wxT("SET NULL")) constr->SetOnUpdate(Constraint::setNull); if (onDelete == wxT("NO ACTION")) constr->SetOnUpdate(Constraint::noAction); wxString onUpdate = database->GetResultString(wxT("DELETE_RULE")); if (onUpdate == wxT("RESTRICT")) constr->SetOnDelete(Constraint::restrict); if (onUpdate == wxT("CASCADE")) constr->SetOnDelete(Constraint::cascade); if (onUpdate == wxT("SET NULL")) constr->SetOnDelete(Constraint::setNull); if (onUpdate == wxT("NO ACTION")) constr->SetOnDelete(Constraint::noAction); } pTab->AddChild(constr); } dbLayer->CloseResultSet(database); dbLayer->Close(); return true; }
/* * to_regtype - converts "typename" to type OID * * If the name is not found, we return NULL. */ Datum to_regtype(PG_FUNCTION_ARGS) { char *typ_name = PG_GETARG_CSTRING(0); Oid result; int32 typmod; /* * Invoke the full parser to deal with special cases such as array syntax. */ parseTypeString(typ_name, &result, &typmod, true); if (OidIsValid(result)) PG_RETURN_OID(result); else PG_RETURN_NULL(); }
/* * ColumnDefinitionList creates and returns a list of column definition objects * from two lists of column names and types. As an example, this function takes * in two single elements lists: "l_quantity" and "decimal(15, 2)". The function * then returns a list with one column definition, where the column's name is * l_quantity, its type is numeric, and the type modifier represents (15, 2). */ List * ColumnDefinitionList(List *columnNameList, List *columnTypeList) { List *columnDefinitionList = NIL; ListCell *columnNameCell = NULL; ListCell *columnTypeCell = NULL; forboth(columnNameCell, columnNameList, columnTypeCell, columnTypeList) { const char *columnName = (const char *) lfirst(columnNameCell); const char *columnType = (const char *) lfirst(columnTypeCell); /* * We should have a SQL compatible column type declaration; we first * convert this type to PostgreSQL's type identifiers and modifiers. */ Oid columnTypeId = InvalidOid; int32 columnTypeMod = -1; bool missingOK = false; TypeName *typeName = NULL; ColumnDef *columnDefinition = NULL; parseTypeString(columnType, &columnTypeId, &columnTypeMod, missingOK); typeName = makeTypeNameFromOid(columnTypeId, columnTypeMod); /* we then create the column definition */ columnDefinition = makeNode(ColumnDef); columnDefinition->colname = (char *) columnName; columnDefinition->typeName = typeName; columnDefinition->is_local = true; columnDefinition->is_not_null = false; columnDefinition->raw_default = NULL; columnDefinition->cooked_default = NULL; columnDefinition->constraints = NIL; columnDefinitionList = lappend(columnDefinitionList, columnDefinition); } return columnDefinitionList; }
/* * regtypein - converts "typename" to type OID * * We also accept a numeric OID, for symmetry with the output routine. * * '-' signifies unknown (OID 0). In all other cases, the input must * match an existing pg_type entry. * * In bootstrap mode the name must just equal some existing name in pg_type. * In normal mode the type name can be specified using the full type syntax * recognized by the parser; for example, DOUBLE PRECISION and INTEGER[] will * work and be translated to the correct type names. (We ignore any typmod * info generated by the parser, however.) */ Datum regtypein(PG_FUNCTION_ARGS) { char *typ_name_or_oid = PG_GETARG_CSTRING(0); Oid result = InvalidOid; int32 typmod; /* '-' ? */ if (strcmp(typ_name_or_oid, "-") == 0) PG_RETURN_OID(InvalidOid); /* Numeric OID? */ if (typ_name_or_oid[0] >= '0' && typ_name_or_oid[0] <= '9' && strspn(typ_name_or_oid, "0123456789") == strlen(typ_name_or_oid)) { result = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(typ_name_or_oid))); PG_RETURN_OID(result); } /* Else it's a type name, possibly schema-qualified or decorated */ /* * In bootstrap mode we assume the given name is not schema-qualified, and * just search pg_type for a match. This is needed for initializing other * system catalogs (pg_namespace may not exist yet, and certainly there * are no schemas other than pg_catalog). */ if (IsBootstrapProcessingMode()) { Relation hdesc; ScanKeyData skey[1]; SysScanDesc sysscan; HeapTuple tuple; ScanKeyInit(&skey[0], Anum_pg_type_typname, BTEqualStrategyNumber, F_NAMEEQ, CStringGetDatum(typ_name_or_oid)); hdesc = heap_open(TypeRelationId, AccessShareLock); sysscan = systable_beginscan(hdesc, TypeNameNspIndexId, true, NULL, 1, skey); if (HeapTupleIsValid(tuple = systable_getnext(sysscan))) result = HeapTupleGetOid(tuple); else ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("type \"%s\" does not exist", typ_name_or_oid))); /* We assume there can be only one match */ systable_endscan(sysscan); heap_close(hdesc, AccessShareLock); PG_RETURN_OID(result); } /* * Normal case: invoke the full parser to deal with special cases such as * array syntax. */ parseTypeString(typ_name_or_oid, &result, &typmod); PG_RETURN_OID(result); }
/* * regtypein - converts "typename" to type OID * * We also accept a numeric OID, for symmetry with the output routine. * * '-' signifies unknown (OID 0). In all other cases, the input must * match an existing pg_type entry. * * In bootstrap mode the name must just equal some existing name in pg_type. * In normal mode the type name can be specified using the full type syntax * recognized by the parser; for example, DOUBLE PRECISION and INTEGER[] will * work and be translated to the correct type names. (We ignore any typmod * info generated by the parser, however.) */ Datum regtypein(PG_FUNCTION_ARGS) { char *typ_name_or_oid = PG_GETARG_CSTRING(0); Oid result = InvalidOid; int32 typmod; /* '-' ? */ if (strcmp(typ_name_or_oid, "-") == 0) PG_RETURN_OID(InvalidOid); /* Numeric OID? */ if (typ_name_or_oid[0] >= '0' && typ_name_or_oid[0] <= '9' && strspn(typ_name_or_oid, "0123456789") == strlen(typ_name_or_oid)) { result = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(typ_name_or_oid))); PG_RETURN_OID(result); } /* Else it's a type name, possibly schema-qualified or decorated */ /* * In bootstrap mode we assume the given name is not schema-qualified, and * just search pg_type for a match. This is needed for initializing other * system catalogs (pg_namespace may not exist yet, and certainly there * are no schemas other than pg_catalog). */ if (IsBootstrapProcessingMode()) { int matches = 0; result = caql_getoid_plus( NULL, &matches, NULL, cql("SELECT oid FROM pg_type " " WHERE typname = :1 ", CStringGetDatum(typ_name_or_oid))); if (0 == matches) { ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("type \"%s\" does not exist", typ_name_or_oid))); } /* We assume there can be only one match */ PG_RETURN_OID(result); } /* * Normal case: invoke the full parser to deal with special cases such as * array syntax. */ parseTypeString(typ_name_or_oid, &result, &typmod); PG_RETURN_OID(result); }
/* prepare(query="select * from foo") * prepare(query="select * from foo where bar = $1", params=["text"]) * prepare(query="select * from foo where bar = $1", params=["text"], limit=5) */ PyObject * PLy_spi_prepare(PyObject *self, PyObject *args) { PLyPlanObject *plan; PyObject *list = NULL; PyObject *volatile optr = NULL; char *query; volatile MemoryContext oldcontext; volatile ResourceOwner oldowner; volatile int nargs; if (!PyArg_ParseTuple(args, "s|O", &query, &list)) return NULL; if (list && (!PySequence_Check(list))) { PLy_exception_set(PyExc_TypeError, "second argument of plpy.prepare must be a sequence"); return NULL; } if ((plan = (PLyPlanObject *) PLy_plan_new()) == NULL) return NULL; nargs = list ? PySequence_Length(list) : 0; plan->nargs = nargs; plan->types = nargs ? PLy_malloc(sizeof(Oid) * nargs) : NULL; plan->values = nargs ? PLy_malloc(sizeof(Datum) * nargs) : NULL; plan->args = nargs ? PLy_malloc(sizeof(PLyTypeInfo) * nargs) : NULL; oldcontext = CurrentMemoryContext; oldowner = CurrentResourceOwner; PLy_spi_subtransaction_begin(oldcontext, oldowner); PG_TRY(); { int i; /* * the other loop might throw an exception, if PLyTypeInfo member * isn't properly initialized the Py_DECREF(plan) will go boom */ for (i = 0; i < nargs; i++) { PLy_typeinfo_init(&plan->args[i]); plan->values[i] = PointerGetDatum(NULL); } for (i = 0; i < nargs; i++) { char *sptr; HeapTuple typeTup; Oid typeId; int32 typmod; Form_pg_type typeStruct; optr = PySequence_GetItem(list, i); if (PyString_Check(optr)) sptr = PyString_AsString(optr); else if (PyUnicode_Check(optr)) sptr = PLyUnicode_AsString(optr); else { ereport(ERROR, (errmsg("plpy.prepare: type name at ordinal position %d is not a string", i))); sptr = NULL; /* keep compiler quiet */ } /******************************************************** * Resolve argument type names and then look them up by * oid in the system cache, and remember the required *information for input conversion. ********************************************************/ parseTypeString(sptr, &typeId, &typmod); typeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeId)); if (!HeapTupleIsValid(typeTup)) elog(ERROR, "cache lookup failed for type %u", typeId); Py_DECREF(optr); /* * set optr to NULL, so we won't try to unref it again in case of * an error */ optr = NULL; plan->types[i] = typeId; typeStruct = (Form_pg_type) GETSTRUCT(typeTup); if (typeStruct->typtype != TYPTYPE_COMPOSITE) PLy_output_datum_func(&plan->args[i], typeTup); else ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("plpy.prepare does not support composite types"))); ReleaseSysCache(typeTup); } pg_verifymbstr(query, strlen(query), false); plan->plan = SPI_prepare(query, plan->nargs, plan->types); if (plan->plan == NULL) elog(ERROR, "SPI_prepare failed: %s", SPI_result_code_string(SPI_result)); /* transfer plan from procCxt to topCxt */ if (SPI_keepplan(plan->plan)) elog(ERROR, "SPI_keepplan failed"); PLy_spi_subtransaction_commit(oldcontext, oldowner); } PG_CATCH(); { Py_DECREF(plan); Py_XDECREF(optr); PLy_spi_subtransaction_abort(oldcontext, oldowner); return NULL; } PG_END_TRY(); Assert(plan->plan != NULL); return (PyObject *) plan; }
static bool GetNextArgument(const char *ptr, char **arg, Oid *argtype, const char **endptr, const char *path, bool argistype) { const char *p; bool first_arg = false; int len; p = ptr; while (isspace((unsigned char) *p)) p++; if (*p == '(') first_arg = true; else if (*p == ',') first_arg = false; else if (*p == ')') { p++; while (isspace((unsigned char) *p)) p++; if (*p != '\0') ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("function call syntax error: %s", path))); *endptr = p; return false; } else ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("function call syntax error: %s", path))); p++; while (isspace((unsigned char) *p)) p++; if (first_arg && *p == ')') { p++; while (isspace((unsigned char) *p)) p++; if (*p != '\0') ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("function call syntax error: %s", path))); *endptr = p; return false; } *argtype = UNKNOWNOID; if (argistype) { /* argument is data type name */ const char *startptr; bool inparenthesis; int nparentheses; char *str; int32 typmod; startptr = p; inparenthesis = false; nparentheses = 0; while (*p != '\0' && (inparenthesis || (*p != ',' && *p != ')'))) { if (*p == '(') { if (inparenthesis) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("function call syntax error: %s", path))); inparenthesis = true; nparentheses++; if (nparentheses > 1) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("function call syntax error: %s", path))); } if (*p == ')') inparenthesis = false; p++; } if (p == startptr) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("function call syntax error: %s", path))); while (isspace((unsigned char) *(p - 1))) p--; len = p - startptr; str = palloc(len + 1); memcpy(str, startptr, len); str[len] = '\0'; *arg = str; /* Use full parser to resolve the type name */ parseTypeString(*arg, argtype, &typmod); } else if (*p == '\'') { /* argument is string constants */ StringInfoData buf; initStringInfo(&buf); p++; while (*p != '\0') { if (*p == '\'') { if (*(p + 1) == '\'') p++; else break; } appendStringInfoCharMacro(&buf, *p++); } if (*p != '\'') ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("function call syntax error: %s", path))); p++; *arg = buf.data; } else if (pg_strncasecmp(p, "NULL", strlen("NULL")) == 0) { /* argument is NULL */ p += strlen("NULL"); *arg = NULL; } else { /* argument is numeric constants */ bool minus; const char *startptr; char *str; int64 val64; /* parse plus operator and minus operator */ minus = false; while (*p == '+' || *p == '-') { if (*p == '-') { /* this is standard SQL comment */ if (*(p + 1) == '-') ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("function call syntax error: %s", path))); minus = !minus; } p++; while (isspace((unsigned char) *p)) p++; } startptr = p; while (!isspace((unsigned char) *p) && *p != ',' && *p != ')' && *p != '\0') p++; len = p - startptr; if (len == 0) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("function call syntax error: %s", path))); str = palloc(len + 2); snprintf(str, len + 2, "%c%s", minus ? '-' : '+', startptr); /* could be an oversize integer as well as a float ... */ if (scanint8(str, true, &val64)) { /* * It might actually fit in int32. Probably only INT_MIN can * occur, but we'll code the test generally just to be sure. */ int32 val32 = (int32) val64; if (val64 == (int64) val32) *argtype = INT4OID; else *argtype = INT8OID; } else { /* arrange to report location if numeric_in() fails */ DirectFunctionCall3(numeric_in, CStringGetDatum(str + 1), ObjectIdGetDatum(InvalidOid), Int32GetDatum(-1)); *argtype = NUMERICOID; } /* Check for numeric constants. */ *arg = str; } *endptr = p; return true; }
bool PostgreSqlDbAdapter::GetColumns(Table* pTab) { if (pTab) { // SetDatabase(pTab->GetParentName()); DatabaseLayerPtr dbLayer = this->GetDatabaseLayer(pTab->GetParentName()); if (!dbLayer->IsOpen()) return NULL; // loading columns //TODO:SQL: //DatabaseResultSet *database = dbLayer->RunQueryWithResults(wxString::Format(wxT("SHOW COLUMNS IN `%s`.`%s`"),pTab->getParentName().c_str(),pTab->getName().c_str())); DatabaseResultSet *database = dbLayer->RunQueryWithResults(wxString::Format(wxT("SELECT * FROM information_schema.columns WHERE table_name = '%s'"),pTab->GetName().c_str())); while (database->Next()) { IDbType* pType = parseTypeString(database->GetResultString(wxT("data_type"))); if (pType) { pType->SetSize(database->GetResultInt(wxT("numeric_precision"))); pType->SetSize2(database->GetResultInt(wxT("numeric_precision_radix"))); pType->SetNotNull(database->GetResultString(wxT("is_nullable")) == wxT("NO")); Column* pCol = new Column(database->GetResultString(wxT("column_name")),pTab->GetName(), pType); pTab->AddChild(pCol); } } dbLayer->CloseResultSet(database); //wxT("SELECT tc.constraint_name, tc.constraint_type, tc.table_name, kcu.column_name, tc.is_deferrable, tc.initially_deferred, rc.match_option AS match_type, rc.update_rule AS on_update, rc.delete_rule AS on_delete, ccu.table_name AS references_table, ccu.column_name AS references_field FROM information_schema.table_constraints tc LEFT JOIN information_schema.key_column_usage kcu ON tc.constraint_catalog = kcu.constraint_catalog AND tc.constraint_schema = kcu.constraint_schema AND tc.constraint_name = kcu.constraint_name LEFT JOIN information_schema.referential_constraints rc ON tc.constraint_catalog = rc.constraint_catalog AND tc.constraint_schema = rc.constraint_schema AND tc.constraint_name = rc.constraint_name LEFT JOIN information_schema.constraint_column_usage ccu ON rc.unique_constraint_catalog = ccu.constraint_catalog AND rc.unique_constraint_schema = ccu.constraint_schema AND rc.unique_constraint_name = ccu.constraint_name WHERE tc.table_name = '%s'"); //TODO:SQL: wxString constrSql = wxT("SELECT tc.constraint_name, tc.constraint_type, tc.table_name, kcu.column_name, tc.is_deferrable, tc.initially_deferred, rc.match_option AS match_type, rc.update_rule AS on_update, rc.delete_rule AS on_delete, ccu.table_name AS references_table, ccu.column_name AS references_field FROM information_schema.table_constraints tc LEFT JOIN information_schema.key_column_usage kcu ON tc.constraint_catalog = kcu.constraint_catalog AND tc.constraint_schema = kcu.constraint_schema AND tc.constraint_name = kcu.constraint_name LEFT JOIN information_schema.referential_constraints rc ON tc.constraint_catalog = rc.constraint_catalog AND tc.constraint_schema = rc.constraint_schema AND tc.constraint_name = rc.constraint_name LEFT JOIN information_schema.constraint_column_usage ccu ON rc.unique_constraint_catalog = ccu.constraint_catalog AND rc.unique_constraint_schema = ccu.constraint_schema AND rc.unique_constraint_name = ccu.constraint_name WHERE tc.table_name = '%s'"); database = dbLayer->RunQueryWithResults(wxString::Format(constrSql, pTab->GetName().c_str())); while (database->Next()) { if ((database->GetResultString(wxT("constraint_type")) == wxT("PRIMARY KEY"))||(database->GetResultString(wxT("constraint_type")) == wxT("FOREIGN KEY"))) { Constraint* constr = new Constraint(); constr->SetName(database->GetResultString(wxT("constraint_name"))); constr->SetLocalColumn(database->GetResultString(wxT("column_name"))); constr->SetType(Constraint::primaryKey); if (database->GetResultString(wxT("references_table")) != wxT("") ) { constr->SetType(Constraint::foreignKey); constr->SetRefTable(database->GetResultString(wxT("references_table"))); constr->SetRefCol(database->GetResultString(wxT("references_field"))); wxString onDelete = database->GetResultString(wxT("on_update")); if (onDelete == wxT("RESTRICT")) constr->SetOnUpdate(Constraint::restrict); if (onDelete == wxT("CASCADE")) constr->SetOnUpdate(Constraint::cascade); if (onDelete == wxT("SET NULL")) constr->SetOnUpdate(Constraint::setNull); if (onDelete == wxT("NO ACTION")) constr->SetOnUpdate(Constraint::noAction); wxString onUpdate = database->GetResultString(wxT("on_delete")); if (onUpdate == wxT("RESTRICT")) constr->SetOnDelete(Constraint::restrict); if (onUpdate == wxT("CASCADE")) constr->SetOnDelete(Constraint::cascade); if (onUpdate == wxT("SET NULL")) constr->SetOnDelete(Constraint::setNull); if (onUpdate == wxT("NO ACTION")) constr->SetOnDelete(Constraint::noAction); } pTab->AddChild(constr); } } dbLayer->CloseResultSet(database); dbLayer->Close(); } return true; }