pgadmin3/schema/pgDatatype.cpp
2025-09-24 12:02:10 +05:00

325 lines
8.7 KiB
C++

//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// pgDatatype.cpp - PostgreSQL Datatypes
//
//////////////////////////////////////////////////////////////////////////
#include <wx/wx.h>
// App headers
#include "pgAdmin3.h"
#include "utils/misc.h"
#include "schema/pgDatatype.h"
#include "schema/pgDatabase.h"
#include "utils/pgDefs.h"
pgDatatype::pgDatatype(const wxString &nsp, const wxString &typname, bool isDup, long numdims, long typmod)
{
needSchema = isDup;
schema = nsp;
// Above 7.4, format_type also sends the schema name if it's not included
// in the search_path, so we need to skip it in the typname
if (typname.Contains(schema + wxT("\".")))
name = typname.Mid(schema.Len() + 3); // "+2" because of the two double quotes
else if (typname.Contains(schema + wxT(".")))
name = typname.Mid(schema.Len() + 1);
else
name = typname;
if (name.StartsWith(wxT("_")))
{
if (!numdims)
numdims = 1;
name = name.Mid(1);
}
if (name.Right(2) == wxT("[]"))
{
if (!numdims)
numdims = 1;
name = name.Left(name.Len() - 2);
}
if (name.StartsWith(wxT("\"")) && name.EndsWith(wxT("\"")))
name = name.Mid(1, name.Len() - 2);
if (numdims > 0)
{
while (numdims--)
array += wxT("[]");
}
if (typmod != -1)
{
length = wxT("(");
if (name == wxT("numeric"))
{
len = (typmod - 4L) >> 16L;
prec = (typmod - 4) & 0xffff;
length += NumToStr(len);
if (prec)
length += wxT(",") + NumToStr(prec);
}
else if (name == wxT("time") || name == wxT("timetz")
|| name == wxT("time without time zone") || name == wxT("time with time zone")
|| name == wxT("timestamp") || name == wxT("timestamptz")
|| name == wxT("timestamp without time zone") || name == wxT("timestamp with time zone")
|| name == wxT("bit") || name == wxT("bit varying") || name == wxT("varbit"))
{
prec = 0;
len = typmod;
length += NumToStr(len);
}
else if (name == wxT("interval"))
{
prec = 0;
len = (typmod & 0xffff);
length += NumToStr(len);
}
else if (name == wxT("date"))
{
len = prec = 0;
length = wxT(""); /* Clear Length */
}
else
{
prec = 0;
len = typmod - 4L;
length += NumToStr(len);
}
if (length.Length() > 0)
length += wxT(")");
}
else
len = prec = 0;
}
// Return the full name of the type, with dimension and array qualifiers
wxString pgDatatype::FullName() const
{
if (name == wxT("char") && schema == wxT("pg_catalog"))
return wxT("\"char\"") + array;
else if (name == wxT("time with time zone"))
return wxT("time") + length + wxT(" with time zone") + array;
else if (name == wxT("time without time zone"))
return wxT("time") + length + wxT(" without time zone") + array;
else if (name == wxT("timestamp with time zone"))
return wxT("timestamp") + length + wxT(" with time zone") + array;
else if (name == wxT("timestamp without time zone"))
return wxT("timestamp") + length + wxT(" without time zone") + array;
else
return name + length + array;
}
// Return the quoted full name of the type, with dimension and array qualifiers
wxString pgDatatype::QuotedFullName() const
{
if (name == wxT("char") && schema == wxT("pg_catalog"))
return wxT("\"char\"") + array;
else if (name == wxT("time with time zone"))
return wxT("time") + length + wxT(" with time zone") + array;
else if (name == wxT("time without time zone"))
return wxT("time") + length + wxT(" without time zone") + array;
else if (name == wxT("timestamp with time zone"))
return wxT("timestamp") + length + wxT(" with time zone") + array;
else if (name == wxT("timestamp without time zone"))
return wxT("timestamp") + length + wxT(" without time zone") + array;
else
return qtTypeIdent(name) + length + array;
}
wxString pgDatatype::GetSchemaPrefix(pgDatabase *db) const
{
if (schema.IsEmpty() || (!db && schema == wxT("pg_catalog")))
return wxEmptyString;
if (needSchema)
return schema + wxT(".");
return db->GetSchemaPrefix(schema);
}
wxString pgDatatype::GetQuotedSchemaPrefix(pgDatabase *db) const
{
wxString str = GetSchemaPrefix(db);
if (!str.IsEmpty())
return qtIdent(str.Left(str.Length() - 1)) + wxT(".");
return str;
}
long pgDatatype::GetTypmod(const wxString &name, const wxString &len, const wxString &prec)
{
if (len.IsEmpty())
return -1;
if (name == wxT("numeric"))
{
return (((long)StrToLong(len) << 16) + StrToLong(prec)) + 4;
}
else if (name == wxT("time") || name == wxT("timetz")
|| name == wxT("time without time zone") || name == wxT("time with time zone")
|| name == wxT("timestamp") || name == wxT("timestamptz")
|| name == wxT("timestamp without time zone") || name == wxT("timestamp with time zone")
|| name == wxT("interval") || name == wxT("bit") || name == wxT("bit varying") || name == wxT("varbit"))
{
return StrToLong(len);
}
else
{
return StrToLong(len) + 4;
}
}
DatatypeReader::DatatypeReader(pgDatabase *db, const wxString &condition, bool addSerials)
{
init(db, condition, addSerials);
}
DatatypeReader::DatatypeReader(pgDatabase *db, bool withDomains, bool addSerials)
{
wxString condition = wxT("typisdefined AND typtype ");
// We don't get pseudotypes here
if (withDomains)
condition += wxT("IN ('b', 'c', 'd', 'e', 'r')");
else
condition += wxT("IN ('b', 'c', 'e', 'r')");
condition += wxT("AND NOT EXISTS (select 1 from pg_class where relnamespace=typnamespace and relname = typname and relkind != 'c') AND (typname not like '_%' OR NOT EXISTS (select 1 from pg_class where relnamespace=typnamespace and relname = substring(typname from 2)::name and relkind != 'c')) ");
if (!settings->GetShowSystemObjects())
condition += wxT(" AND nsp.nspname != 'information_schema'");
init(db, condition, addSerials);
}
void DatatypeReader::init(pgDatabase *db, const wxString &condition, bool addSerials)
{
database = db;
wxString sql = wxT("SELECT * FROM (SELECT format_type(t.oid,NULL) AS typname, CASE WHEN typelem > 0 THEN typelem ELSE t.oid END as elemoid, typlen, typtype, t.oid, nspname,\n")
wxT(" (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname = t.typname) > 1 AS isdup, c.relpartbound is not null or c.relkind in('i','I') AS ispart\n")
wxT(" FROM pg_type t\n")
wxT(" JOIN pg_namespace nsp ON typnamespace=nsp.oid left join pg_class c on c.oid=t.typrelid\n")
wxT(" WHERE (NOT (typname = 'unknown' AND nspname = 'pg_catalog')) AND ") + condition + wxT("\n");
if (addSerials)
{
if (db->GetConnection()->BackendMinimumVersion(9, 2))
{
sql += wxT(" UNION SELECT 'smallserial', 0, 2, 'b', 0, 'pg_catalog', false, false\n");
}
sql += wxT(" UNION SELECT 'bigserial', 0, 8, 'b', 0, 'pg_catalog', false, false\n");
sql += wxT(" UNION SELECT 'serial', 0, 4, 'b', 0, 'pg_catalog', false, false\n");
}
sql += wxT(" ) AS dummy ORDER BY nspname <> 'pg_catalog', nspname <> 'public', nspname, 1");
set = db->GetConnection()->ExecuteSet(sql);
}
bool DatatypeReader::IsDomain() const
{
return set->GetVal(wxT("typtype")) == 'd';
}
bool DatatypeReader::IsPartition() const
{
return set->GetBool(wxT("ispart"));
}
bool DatatypeReader::IsVarlen() const
{
return set->GetLong(wxT("typlen")) == -1;
}
bool DatatypeReader::MaySpecifyLength() const
{
if (IsDomain())
return false;
switch ((long)set->GetOid(wxT("elemoid")))
{
case PGOID_TYPE_BIT:
case PGOID_TYPE_CHAR:
case PGOID_TYPE_VARCHAR:
case PGOID_TYPE_NUMERIC:
return true;
default:
return false;
}
}
bool DatatypeReader::MaySpecifyPrecision() const
{
if (IsDomain())
return false;
switch ((long)set->GetOid(wxT("elemoid")))
{
case PGOID_TYPE_NUMERIC:
return true;
default:
return false;
}
}
pgDatatype DatatypeReader::GetDatatype() const
{
return pgDatatype(set->GetVal(wxT("nspname")), set->GetVal(wxT("typname")), set->GetBool(wxT("isdup")));
}
wxString DatatypeReader::GetTypename() const
{
return set->GetVal(wxT("typname"));
}
wxString DatatypeReader::GetSchema() const
{
return set->GetVal(wxT("nspname"));
}
wxString DatatypeReader::GetSchemaPrefix() const
{
if (set->GetBool(wxT("isdup")))
return set->GetVal(wxT("nspname")) + wxT(".");
else
return database->GetSchemaPrefix(set->GetVal(wxT("nspname")));
}
wxString DatatypeReader::GetQuotedSchemaPrefix() const
{
if (set->GetBool(wxT("isdup")))
return qtIdent(set->GetVal(wxT("nspname"))) + wxT(".");
else
return database->GetQuotedSchemaPrefix(set->GetVal(wxT("nspname")));
}
wxString DatatypeReader::GetOidStr() const
{
return set->GetVal(wxT("oid"));
}
OID DatatypeReader::GetOid() const
{
return set->GetOid(wxT("oid"));
}