dabo Commit
Revision 4822
Date: 2008-12-11 14:28:44 -0800 (Thu, 11 Dec 2008)
Author: Cito
Trac: http://trac.dabodev.com/dabo/changeset/4822
Changed:
U trunk/dabo/db/dbPostgreSQL.py
Log:
Some improvements of the PostgreSQL support (see ticket #1189).
Diff:
Modified: trunk/dabo/db/dbPostgreSQL.py
===================================================================
--- trunk/dabo/db/dbPostgreSQL.py 2008-12-11 18:52:01 UTC (rev 4821)
+++ trunk/dabo/db/dbPostgreSQL.py 2008-12-11 22:28:44 UTC (rev 4822)
@@ -20,7 +20,7 @@
port = str(connectInfo.Port)
if not port or port == "None":
port = "5432"
-
+
DSN = "host=%s port=%s dbname=%s user=%s password=%s" %
(connectInfo.Host,
port, connectInfo.Database, connectInfo.User,
connectInfo.revealPW())
# Instead of blindly appending kwargs here, it would be
preferable to only accept
@@ -39,45 +39,30 @@
def getDictCursorClass(self):
# the new psycopg 2.0 supports DictCursor
import psycopg2.extras as cursors
- return cursors.DictCursor
+ return cursors.DictCursor
def escQuote(self, val):
# escape backslashes and single quotes, and
# wrap the result in single quotes
- sl = "\\"
- qt = "\'"
- return qt + val.replace(sl, sl+sl).replace(qt, sl+qt) + qt
+ return "'%s'" % val.replace('\\', '\\\\').replace("'", "''")
def formatDateTime(self, val):
""" We need to wrap the value in quotes. """
- sqt = "'" # single quote
- val = self._stringify(val)
- return "%s%s%s" % (sqt, val, sqt)
+ return "'%s'" % self._stringify(val)
def getTables(self, cursor, includeSystemTables=False):
- # jfcs 11/01/04 assumed public schema
- # jfcs 01/22/07 added below to support schema
- # thanks to Phillip J. Allen who provided a Select state that
filtered for the user name
- if includeSystemTables:
- sqltablestr = (("SELECT schemaname || '.' || tablename
AS tablename FROM pg_tables WHERE has_table_privilege('%s', schemaname || '.'
|| tablename, 'SELECT')") % self.conn_user)
- else:
- #sqltablestr = (("SELECT schemaname || '.' || tablename
AS tablename FROM pg_tables WHERE (schemaname not like 'pg_%s' and schemaname
not like 'information%s') and has_table_privilege('%s', schemaname || '.' ||
tablename, 'SELECT')") % ('%', '%', self.conn_user))
- # jfcs 06/19/08
- sqltablestr = (("""SELECT schemaname || '.' ||
tablename AS tablename
- FROM pg_tables
- WHERE (schemaname not like 'pg_%s'
- and schemaname not like
'information%s')
- and has_table_privilege('%s',
schemaname || '.' || tablename, 'SELECT')
- """) % ("%", "%", self.conn_user))
- cursor.execute(sqltablestr)
- rs = cursor.getDataSet()
- tables = []
- for record in rs:
- tables.append(record["tablename"])
- return tuple(tables)
+ query = ["SELECT schemaname||'.'||tablename AS tablename"
+ " FROM pg_tables WHERE"]
+ if not includeSystemTables:
+ query.append("(schemaname NOT LIKE 'pg_%' AND "
+ "schemaname NOT LIKE 'information%')
AND")
+ query.append("has_schema_privilege(schemaname, 'usage') AND "
+
"has_table_privilege(schemaname||'.'||tablename, 'select')")
+ cursor.execute(' '.join(query))
+ return tuple(record["tablename"] for record in
cursor.getDataSet())
def getTableRecordCount(self, tableName, cursor):
@@ -85,60 +70,38 @@
return cursor.getDataSet()[0]["ncount"]
- def getFields(self, tableName, cursor):
- """JFCS support for 7.4 and greater
- Requires that each table have a primary key"""
+ def getFields(self, tableName, cursor, includeSystemFields=False):
try:
- localSchemaName, localTableName = tableName.split(".",
1)
+ schemaName, tableName = tableName.split(".", 1)
except ValueError:
- raise ValueError,_("Please use schema-qualified
datasource names (e.g. 'public.%s')" )% tableName
-
-
- cursor.execute("""SELECT a.attname, t.typname from pg_attribute
a, pg_type t, pg_class c
- LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
- where a.attrelid = c.oid
- and a.atttypid = t.oid
- AND n.nspname || '.'||c.relname = '%s'
- order by c.relname, a.attname""" % tableName)
-
- rs = cursor.getDataSet()
-
- #the code below may not work with 7.4 due to the use of the
function generate_series()
- #However a postgres function can be added to simulate
generate_series()
- #CREATE OR REPLACE FUNCTION generate_series(int, int) RETURNS
setof int AS
- #'BEGIN
- #FOR i IN $1..$2
- #LOOP
- #RETURN NEXT i;
- #END LOOP;
- #RETURN;
- #END; ' LANGUAGE plpgsql;
-
- sqlstr = """SELECT n.nspname AS schema_name, c.relname AS
table_name,
- c.oid AS table_oid, a.attname AS column_name, idx.n + 1 AS
ordinal_position
- FROM pg_class c, pg_attribute a, pg_index i, pg_namespace n,
generate_series(0, 31) idx(n)
- WHERE c.oid = a.attrelid AND c.oid = i.indrelid AND i.indisprimary AND
a.attnum = i.indkey[idx.n]
- AND NOT a.attisdropped
- AND has_schema_privilege(n.oid, 'USAGE'::text)
- AND n.nspname NOT LIKE 'pg!_%s' ESCAPE '!'
- AND has_table_privilege(c.oid, 'SELECT'::text)
- AND c.relnamespace = n.oid and c.relname = '%s' and n.nspname = '%s'
""" % ('%', localTableName, localSchemaName)
-
- cursor.execute(sqlstr)
- rs2 = cursor.getDataSet()
- if rs2 == ():
- thePKFieldName = None
- else:
- #thestr = rs2[0][3]
- thePKFieldName = rs2[0]["column_name"]
-
+ schemaName = None
+ sql = ["SELECT a.attname, t.typname,"
+ # Note that the generate_series() function does
not exist
+ # in Postgres < 8.0, but it can easily be
added, and that
+ # in Postgres > 8.1, we could use the ANY
syntax instead.
+ " EXISTS(SELECT * FROM generate_series(0, 31)
idx(n)"
+ " WHERE a.attnum = i.indkey[idx.n]) AS
isprimary"
+ " FROM pg_class c"
+ " JOIN pg_namespace n ON n.oid = c.relnamespace"
+ " JOIN pg_attribute a ON a.attrelid = c.oid"
+ " JOIN pg_type t ON t.oid = a.atttypid"
+ " LEFT JOIN pg_index i ON i.indrelid = c.oid
AND i.indisprimary",
+ "WHERE c.relname = '%s'" % tableName]
+ if schemaName:
+ sql.append("AND n.nspname = '%s'" % schemaName)
+ if not includeSystemFields:
+ sql.append("AND a.attname NOT IN "
+ " ('ctid', 'cmin', 'cmax', 'tableoid',
'xmax', 'xmin')")
+ sql.append("AND has_schema_privilege(n.oid, 'usage')"
+ " AND has_table_privilege(c.oid, 'select')"
+ " AND pg_table_is_visible(c.oid)"
+ " ORDER BY c.relname, a.attname")
+ cursor.execute(' '.join(sql))
fields = []
- for r in rs:
- name = r["attname"]
- fldType =r["typname"]
- pk = False
- if thePKFieldName is not None:
- pk = (name in thePKFieldName)
+ for r in cursor.getDataSet():
+ name = r["attname"].strip()
+ fldType = r["typname"]
+ pk = r["isprimary"]
if "int" in fldType:
fldType = "I"
elif "char" in fldType :
@@ -177,10 +140,9 @@
fldType = "C"
elif "oid" in fldType:
fldType = "I"
- #fldType = "G"
else:
fldType = "?"
- fields.append((name.strip(), fldType, pk))
+ fields.append((name, fldType, pk))
return tuple(fields)
@@ -188,7 +150,7 @@
""" By default, the update SQL statement will be in the form of
tablename.fieldname
but Postgres does not accept this syntax. If not, change
- this method to return an empty string, or whatever should
+ this method to return an empty string, or whatever should
preceed the field name in an update statement.
Postgres needs to return an empty string."""
return ""
@@ -198,15 +160,15 @@
""" Most backends will return a non-zero number if there are
updates.
Some do not, so this will have to be customized in those cases.
"""
- return
+ return
def noResultsOnDelete(self):
""" Most backends will return a non-zero number if there are
deletions.
Some do not, so this will have to be customized in those cases.
"""
- #raise dException.dException, _("No records deleted")
- return
+ # raise dException.dException(_("No records deleted"))
+ return
def flush(self, cursor):
@@ -219,54 +181,57 @@
def getLastInsertID(self, cursor):
""" Return the ID of the last inserted row, or None.
-
- When inserting a new record in a table that auto-generates a PK
(such
+
+ When inserting a new record in a table that auto-generates a PK
(such
as a serial data type) value, different databases have their
own way of retrieving that value.
- With Postgres a sequence is created. The SQL statement
determines the sequence name
+ With Postgres a sequence is created. The SQL statement
determines the sequence name
('table_pkid_seq') and needs three parameters the schema name,
table name, and the primary
key field for the table.
cursor.KeyField = primary field
cursor.Table = returns 'schema.table' for the cursor
-
+
Postgres uses 'currval(sequence_name)' to determine the last
value of the session.
If two different sessions are open (two users accessing the
same table for example)
currval() will return the correct value for each session.
-
+
"""
-
+
try:
- localSchemaName, localTableName =
cursor.Table.split(".",1) ##tableName.split(".", 1)
+ schemaName, tableName = tableName.split(".", 1)
except ValueError:
- raise ValueError,_("Please use schema-qualified
datasource names (e.g. 'public.%s')" )% tableName
-
+ schemaName = None
- tempCursor =self._connection.cursor()
-
#JFCS 01/13/08 changed the select statement to allow primary
fields that were not based
#on a serial data type to work.
# special thanks to Lorenzo Alberton for his help with parsing
of the fields.
# It has been confirmed that the statement works with 7.4
through 8.3.x
- sql="""
- SELECT substring((SELECT substring(pg_get_expr(d.adbin,
d.adrelid) for 128)
- FROM pg_attrdef d
- WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND
a.atthasdef)
- FROM 'nextval[^'']*''([^'']*)')
- FROM pg_attribute a
- LEFT JOIN pg_class c ON c.oid = a.attrelid
- LEFT JOIN pg_attrdef d ON d.adrelid = a.attrelid AND d.adnum =
a.attnum AND a.atthasdef
- LEFT JOIN pg_namespace n ON c.relnamespace = n.oid WHERE
(c.relname = %s)
- AND a.attname = %s and n.nspname=%s AND NOT a.attisdropped AND
a.attnum > 0 AND pg_get_expr(d.adbin, d.adrelid) LIKE 'nextval%%'
- """
+ sql = ["SELECT curval(substring((SELECT substring("
+ "pg_get_expr(d.adbin, d.adrelid) for 128)) as
curval"
+ " FROM pg_attrdef d WHERE d.adrelid =
a.attrelid"
+ " AND d.adnum = a.attnum AND a.atthasdef)"
+ " FROM 'nextval[^'']*''([^'']*)')"
+ " FROM pg_attribute a"
+ " LEFT JOIN pg_class c ON c.oid = a.attrelid"
+ " LEFT JOIN pg_attrdef d ON d.adrelid =
a.attrelid"
+ " AND d.adnum = a.attnum AND a.atthasdef"
+ " LEFT JOIN pg_namespace n ON c.relnamespace =
n.oid",
+ "WHERE a.attname = '%s'" % cursor.KeyField,
+ "AND (c.relname = '%s')" % tableName]
+ if schemaName:
+ sql.append(" AND n.nspname = '%s'" % schemaName)
+ else:
+ sql.append(" AND pg_table_is_visible(c.oid)")
+ sql.append("NOT a.attisdropped AND a.attnum > 0"
+ " AND pg_get_expr(d.adbin, d.adrelid) LIKE
'nextval%'")
- tempCursor.execute(sql, ( localTableName, cursor.KeyField,
localSchemaName))
- rs = tempCursor.fetchall()
- #if rs is None:
- #dabo.dbActivityLog.write("no data in getLastInsertID")
-
- sqlWithseq_name="""select currval('%s') as curval""" %
(rs[0][0], )
- tempCursor.execute(sqlWithseq_name)
- rs = tempCursor.fetchall()
- if not rs[0][0] is None:
- return rs[0][0]
+ tempCursor = self._connection.cursor()
+ try:
+ tempCursor.execute(' '.join(sql))
+ rs = tempCursor.fetchone()
+ finally:
+ tempCursor.close()
+ if not rs or rs[0] is None:
+ raise AttributeError("Unable to determine the sequence
used"
+ " or the sequence returns a strange
value.")
else:
- raise AttributeError, "Unable to determine the sequence
used or the sequence return a strange value."
+ return rs[0]
_______________________________________________
Post Messages to: [email protected]
Subscription Maintenance: http://leafe.com/mailman/listinfo/dabo-dev
Searchable Archives: http://leafe.com/archives/search/dabo-dev
This message:
http://leafe.com/archives/byMID/[email protected]