On Sat, 2006-06-03 at 13:43 -0400, Michael Bayer wrote:
> we've had a lot of issues with Postgres and using
> information_schema. whereas MySQL's information_schema support was
> pointless, Postgres mostly works, but performs very slowly unless you
> VACUUM your database every hour. we've had reports of primary keys
> not being reflected for tables across user names as well.
The information schema is implemented as a set of views on
system relations. I suspect vacuuming every hour is not really
the issue, unless you are doing a lot of metadata changes.
A "VACUUM FULL ANALYZE" by a database superuser is probably
a good thing once your table metadata settles down. This
is something the DBA would take care of, one of the reasons
that implementation programmers aren't usually permitted
to create ad hock changes to tables.
As to the reason that some attributes are only visible to
relation owners look for the "u.usesysid = c.relowner" and
the subsequent criteria in the definition of the view.
So it is on purpose. I would guess that it meets some security
criteria in the standard since they actually went to the trouble
of adding it in. One question to ask might be are people relying
on being DB superusers or were specific grants made? (examine
the output of \z from psql). The information schema doesn't
take into account DB superusers:
sat=# \d information_schema.columns
View "information_schema.columns"
Column | Type |
Modifiers
--------------------------+------------------------------------+-----------
table_catalog | information_schema.sql_identifier |
table_schema | information_schema.sql_identifier |
table_name | information_schema.sql_identifier |
column_name | information_schema.sql_identifier |
ordinal_position | information_schema.cardinal_number |
column_default | information_schema.character_data |
is_nullable | information_schema.character_data |
data_type | information_schema.character_data |
character_maximum_length | information_schema.cardinal_number |
character_octet_length | information_schema.cardinal_number |
numeric_precision | information_schema.cardinal_number |
numeric_precision_radix | information_schema.cardinal_number |
numeric_scale | information_schema.cardinal_number |
datetime_precision | information_schema.cardinal_number |
interval_type | information_schema.character_data |
interval_precision | information_schema.character_data |
character_set_catalog | information_schema.sql_identifier |
character_set_schema | information_schema.sql_identifier |
character_set_name | information_schema.sql_identifier |
collation_catalog | information_schema.sql_identifier |
collation_schema | information_schema.sql_identifier |
collation_name | information_schema.sql_identifier |
domain_catalog | information_schema.sql_identifier |
domain_schema | information_schema.sql_identifier |
domain_name | information_schema.sql_identifier |
udt_catalog | information_schema.sql_identifier |
udt_schema | information_schema.sql_identifier |
udt_name | information_schema.sql_identifier |
scope_catalog | information_schema.sql_identifier |
scope_schema | information_schema.sql_identifier |
scope_name | information_schema.sql_identifier |
maximum_cardinality | information_schema.cardinal_number |
dtd_identifier | information_schema.sql_identifier |
is_self_referencing | information_schema.character_data |
View definition:
SELECT current_database()::information_schema.sql_identifier AS
table_catalog, nc.nspname::information_schema.sql_identifier AS
table_schema, c.relname::information_schema.sql_identifier AS
table_name, a.attname::information_schema.sql_identifier AS column_name,
a.attnum::information_schema.cardinal_number AS ordinal_position,
CASE
WHEN u.usename = "current_user"() THEN ad.adsrc
ELSE NULL::text
END::information_schema.character_data AS column_default,
CASE
WHEN a.attnotnull OR t.typtype = 'd'::"char" AND
t.typnotnull THEN 'NO'::text
ELSE 'YES'::text
END::information_schema.character_data AS is_nullable,
CASE
WHEN t.typtype = 'd'::"char" THEN
CASE
WHEN bt.typelem <> 0::oid AND bt.typlen = -1 THEN
'ARRAY'::text
WHEN nbt.nspname = 'pg_catalog'::name THEN
format_type(t.typbasetype, NULL::integer)
ELSE 'USER-DEFINED'::text
END
ELSE
CASE
WHEN t.typelem <> 0::oid AND t.typlen = -1 THEN
'ARRAY'::text
WHEN nt.nspname = 'pg_catalog'::name THEN
format_type(a.atttypid, NULL::integer)
ELSE 'USER-DEFINED'::text
END
END::information_schema.character_data AS data_type,
information_schema._pg_char_max_length(information_schema._pg_truetypid(a.*,
t.*), information_schema._pg_truetypmod(a.*,
t.*))::information_schema.cardinal_number AS character_maximum_length,
information_schema._pg_char_octet_length(information_schema._pg_truetypid(a.*,
t.*), information_schema._pg_truetypmod(a.*,
t.*))::information_schema.cardinal_number AS character_octet_length,
information_schema._pg_numeric_precision(information_schema._pg_truetypid(a.*,
t.*), information_schema._pg_truetypmod(a.*,
t.*))::information_schema.cardinal_number AS numeric_precision,
information_schema._pg_numeric_precision_radix(information_schema._pg_truetypid(a.*,
t.*), information_schema._pg_truetypmod(a.*,
t.*))::information_schema.cardinal_number AS numeric_precision_radix,
information_schema._pg_numeric_scale(information_schema._pg_truetypid(a.*,
t.*), information_schema._pg_truetypmod(a.*,
t.*))::information_schema.cardinal_number AS numeric_scale,
information_schema._pg_datetime_prec
ision(information_schema._pg_truetypid(a.*, t.*),
information_schema._pg_truetypmod(a.*,
t.*))::information_schema.cardinal_number AS datetime_precision,
NULL::information_schema.character_data::information_schema.character_data AS
interval_type,
NULL::information_schema.character_data::information_schema.character_data AS
interval_precision,
NULL::information_schema.sql_identifier::information_schema.sql_identifier AS
character_set_catalog,
NULL::information_schema.sql_identifier::information_schema.sql_identifier AS
character_set_schema,
NULL::information_schema.sql_identifier::information_schema.sql_identifier AS
character_set_name,
NULL::information_schema.sql_identifier::information_schema.sql_identifier AS
collation_catalog,
NULL::information_schema.sql_identifier::information_schema.sql_identifier AS
collation_schema,
NULL::information_schema.sql_identifier::information_schema.sql_identifier AS
collation_name,
CASE
WHEN t.typtype = 'd'::"char" THEN current_database()
ELSE NULL::name
END::information_schema.sql_identifier AS domain_catalog,
CASE
WHEN t.typtype = 'd'::"char" THEN nt.nspname
ELSE NULL::name
END::information_schema.sql_identifier AS domain_schema,
CASE
WHEN t.typtype = 'd'::"char" THEN t.typname
ELSE NULL::name
END::information_schema.sql_identifier AS domain_name,
current_database()::information_schema.sql_identifier AS udt_catalog,
COALESCE(nbt.nspname, nt.nspname)::information_schema.sql_identifier AS
udt_schema, COALESCE(bt.typname,
t.typname)::information_schema.sql_identifier AS udt_name,
NULL::information_schema.sql_identifier::information_schema.sql_identifier AS
scope_catalog,
NULL::information_schema.sql_identifier::information_schema.sql_identifier AS
scope_schema,
NULL::information_schema.sql_identifier::information_schema.sql_identifier AS
scope_name,
NULL::information_schema.cardinal_number::information_schema.cardinal_number AS
maximum_cardinality, a.attnum::information_schema.sql_identifier AS
dtd_identifier,
'NO'::information_schema.character_data::information_schema.character_data AS
is_self_referencing
FROM pg_attribute a
LEFT JOIN pg_attrdef ad ON a.attrelid = ad.adrelid AND a.attnum =
ad.adnum, pg_class c, pg_namespace nc, pg_user u, pg_type t
JOIN pg_namespace nt ON t.typnamespace = nt.oid
LEFT JOIN (pg_type bt
JOIN pg_namespace nbt ON bt.typnamespace = nbt.oid) ON t.typtype =
'd'::"char" AND t.typbasetype = bt.oid
WHERE a.attrelid = c.oid AND a.atttypid = t.oid AND u.usesysid =
c.relowner AND nc.oid = c.relnamespace AND a.attnum > 0 AND NOT
a.attisdropped AND (c.relkind = 'r'::"char" OR c.relkind = 'v'::"char")
AND (u.usename = "current_user"() OR has_table_privilege(c.oid,
'SELECT'::text) OR has_table_privilege(c.oid, 'INSERT'::text) OR
has_table_privilege(c.oid, 'UPDATE'::text) OR has_table_privilege(c.oid,
'REFERENCES'::text));
For column usage information only the table owner is permitted.
(note r.relowner = u.usesysid AND u.usename = "current_user"()):
sat=# \d information_schema.key_column_usage
View "information_schema.key_column_usage"
Column | Type | Modifiers
--------------------+------------------------------------+-----------
constraint_catalog | information_schema.sql_identifier |
constraint_schema | information_schema.sql_identifier |
constraint_name | information_schema.sql_identifier |
table_catalog | information_schema.sql_identifier |
table_schema | information_schema.sql_identifier |
table_name | information_schema.sql_identifier |
column_name | information_schema.sql_identifier |
ordinal_position | information_schema.cardinal_number |
View definition:
SELECT current_database()::information_schema.sql_identifier AS
constraint_catalog, nc.nspname::information_schema.sql_identifier AS
constraint_schema, c.conname::information_schema.sql_identifier AS
constraint_name, current_database()::information_schema.sql_identifier
AS table_catalog, nr.nspname::information_schema.sql_identifier AS
table_schema, r.relname::information_schema.sql_identifier AS
table_name, a.attname::information_schema.sql_identifier AS column_name,
pos.n::information_schema.cardinal_number AS ordinal_position
FROM pg_namespace nr, pg_class r, pg_attribute a, pg_namespace nc,
pg_constraint c, pg_user u, information_schema._pg_keypositions() pos(n)
WHERE nr.oid = r.relnamespace AND r.oid = a.attrelid AND r.oid =
c.conrelid AND nc.oid = c.connamespace AND c.conkey[pos.n] = a.attnum
AND NOT a.attisdropped AND (c.contype = 'p'::"char" OR c.contype =
'u'::"char" OR c.contype = 'f'::"char") AND r.relkind = 'r'::"char" AND
r.relowner = u.usesysid AND u.usename = "current_user"();
> what does the list think of dumping information_schema usage
> altogther and using PG's own schema tables for reflection ?
The problem with this of course will be that it can/will
change from release to release. And since you'll still be
accessing the metadata tables it will still require the
"VACUUM FULL ANALYZE" step after many metadata operations.
Our approach was to write a python program, using DBAPI, to
create the python files with the class definitions.
The program is only run by the database owner which is
always different than any applications that perform
data operations.
The DB tables are always and only defined by an external
entity diagram (logical data model and physical schema).
Users of the tables, particularly web applications, are
only granted the minimum SQL privileges necessary for their
tasks. The capability to execute DDL operations would
specifically be prohibited. It is only in small personal
type projects or, at most, initial development that that type
of interaction would occur. For most deployed applications
the "db.create(table)" would/should never (for security
reasons) be possible.
HTH,
William.
_______________________________________________
Sqlalchemy-users mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/sqlalchemy-users