- class="parameter">command, similar to ordinary
- back-tick
substitution.
-
-
+
+
The output of
+ class="parameter">command, similar to ordinary
+ back-tick
substitution.
+
+
%[ ... %]
-
+
Prompts may contain terminal control characters which, for
example, change the color, background, or style of the prompt
results in a boldfaced (1;) yellow-on-black
(33;40) prompt on VT100-compatible, color-capable
- terminals.
-
+ terminals.
+
Before starting up,
psql attempts to
read and execute commands from the the system-wide
- psqlrc file and the
+ psqlrc file and the
$HOME/.psqlrc file in the user's home
- directory. See PREFIX>/share/psqlrc.sample>
- for information on setting up the system-wide file. It could be used
+ directory. See PREFIX>/share/psqlrc.sample>
+ for information on setting up the system-wide file. It could be used
to set up the client or the server to taste (using the \set
and SET commands).
FROM { username | GROUP groupname | PUBLIC } [, ...]
[ CASCADE | RESTRICT ]
+REVOKE [ GRANT OPTION FOR ]
+ { CREATE | ALL [ PRIVILEGES ] }
+ ON TABLESPACE tablespacename [, ...]
+ FROM { username | GROUP groupname | PUBLIC } [, ...]
+ [ CASCADE | RESTRICT ]
+
REVOKE [ GRANT OPTION FOR ]
{ EXECUTE | ALL [ PRIVILEGES ] }
ON FUNCTION funcname ([type, ...]) [, ...]
&createSequence;
&createTable;
&createTableAs;
+ &createTableSpace;
&createTrigger;
&createType;
&createUser;
&dropSchema;
&dropSequence;
&dropTable;
+ &dropTableSpace;
&dropTrigger;
&dropType;
&dropUser;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.168 2004/05/27 17:12:37 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.169 2004/06/18 06:13:09 tgl Exp $
*
*
* INTERFACE ROUTINES
static void
out_target(char *buf, xl_heaptid *target)
{
- sprintf(buf + strlen(buf), "node %u/%u; tid %u/%u",
- target->node.tblNode, target->node.relNode,
+ sprintf(buf + strlen(buf), "rel %u/%u/%u; tid %u/%u",
+ target->node.spcNode, target->node.dbNode, target->node.relNode,
ItemPointerGetBlockNumber(&(target->tid)),
ItemPointerGetOffsetNumber(&(target->tid)));
}
{
xl_heap_clean *xlrec = (xl_heap_clean *) rec;
- sprintf(buf + strlen(buf), "clean: node %u/%u; blk %u",
- xlrec->node.tblNode, xlrec->node.relNode, xlrec->block);
+ sprintf(buf + strlen(buf), "clean: rel %u/%u/%u; blk %u",
+ xlrec->node.spcNode, xlrec->node.dbNode,
+ xlrec->node.relNode, xlrec->block);
}
else
strcat(buf, "UNKNOWN");
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtxlog.c,v 1.13 2004/06/02 17:28:17 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtxlog.c,v 1.14 2004/06/18 06:13:11 tgl Exp $
*
*-------------------------------------------------------------------------
*/
static void
out_target(char *buf, xl_btreetid *target)
{
- sprintf(buf + strlen(buf), "node %u/%u; tid %u/%u",
- target->node.tblNode, target->node.relNode,
+ sprintf(buf + strlen(buf), "rel %u/%u/%u; tid %u/%u",
+ target->node.spcNode, target->node.dbNode, target->node.relNode,
ItemPointerGetBlockNumber(&(target->tid)),
ItemPointerGetOffsetNumber(&(target->tid)));
}
{
xl_btree_delete *xlrec = (xl_btree_delete *) rec;
- sprintf(buf + strlen(buf), "delete: node %u/%u; blk %u",
- xlrec->node.tblNode, xlrec->node.relNode, xlrec->block);
+ sprintf(buf + strlen(buf), "delete: rel %u/%u/%u; blk %u",
+ xlrec->node.spcNode, xlrec->node.dbNode,
+ xlrec->node.relNode, xlrec->block);
break;
}
case XLOG_BTREE_DELETE_PAGE:
{
xl_btree_newroot *xlrec = (xl_btree_newroot *) rec;
- sprintf(buf + strlen(buf), "newroot: node %u/%u; root %u lev %u",
- xlrec->node.tblNode, xlrec->node.relNode,
+ sprintf(buf + strlen(buf), "newroot: rel %u/%u/%u; root %u lev %u",
+ xlrec->node.spcNode, xlrec->node.dbNode,
+ xlrec->node.relNode,
xlrec->rootblk, xlrec->level);
break;
}
{
xl_btree_newmeta *xlrec = (xl_btree_newmeta *) rec;
- sprintf(buf + strlen(buf), "newmeta: node %u/%u; root %u lev %u fast %u lev %u",
- xlrec->node.tblNode, xlrec->node.relNode,
+ sprintf(buf + strlen(buf), "newmeta: rel %u/%u/%u; root %u lev %u fast %u lev %u",
+ xlrec->node.spcNode, xlrec->node.dbNode,
+ xlrec->node.relNode,
xlrec->meta.root, xlrec->meta.level,
xlrec->meta.fastroot, xlrec->meta.fastlevel);
break;
{
xl_btree_newpage *xlrec = (xl_btree_newpage *) rec;
- sprintf(buf + strlen(buf), "newpage: node %u/%u; page %u",
- xlrec->node.tblNode, xlrec->node.relNode,
- xlrec->blkno);
+ sprintf(buf + strlen(buf), "newpage: rel %u/%u/%u; page %u",
+ xlrec->node.spcNode, xlrec->node.dbNode,
+ xlrec->node.relNode, xlrec->blkno);
break;
}
default:
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/backend/access/transam/xlogutils.c,v 1.30 2004/02/11 22:55:24 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/access/transam/xlogutils.c,v 1.31 2004/06/18 06:13:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* though, since we are presumably running by ourselves and can't
* have any lock conflicts ...
*/
- res->reldata.rd_lockInfo.lockRelId.dbId = rnode.tblNode;
+ res->reldata.rd_lockInfo.lockRelId.dbId = rnode.dbNode;
res->reldata.rd_lockInfo.lockRelId.relId = rnode.relNode;
hentry = (XLogRelCacheEntry *)
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.69 2004/06/03 02:08:02 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.70 2004/06/18 06:13:17 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "catalog/pg_attribute.h"
#include "catalog/pg_class.h"
#include "catalog/pg_namespace.h"
+#include "catalog/pg_tablespace.h"
#include "commands/defrem.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
boot_reldesc = heap_create(LexIDStr($5),
PG_CATALOG_NAMESPACE,
+ $3 ? GLOBALTABLESPACE_OID : 0,
tupdesc,
$3,
true,
id = heap_create_with_catalog(LexIDStr($5),
PG_CATALOG_NAMESPACE,
+ $3 ? GLOBALTABLESPACE_OID : 0,
tupdesc,
RELKIND_RELATION,
$3,
DefineIndex(makeRangeVar(NULL, LexIDStr($5)),
LexIDStr($3),
LexIDStr($7),
+ NULL,
$9,
NULL, NIL,
false, false, false,
DefineIndex(makeRangeVar(NULL, LexIDStr($6)),
LexIDStr($4),
LexIDStr($8),
+ NULL,
$10,
NULL, NIL,
true, false, false,
#
# Makefile for backend/catalog
#
-# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.50 2004/01/04 05:57:21 tgl Exp $
+# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.51 2004/06/18 06:13:19 tgl Exp $
#
#-------------------------------------------------------------------------
pg_language.h pg_largeobject.h pg_aggregate.h pg_statistic.h \
pg_rewrite.h pg_trigger.h pg_listener.h pg_description.h pg_cast.h \
pg_namespace.h pg_conversion.h pg_database.h pg_shadow.h pg_group.h \
- pg_depend.h indexing.h \
+ pg_tablespace.h pg_depend.h indexing.h \
)
pg_includes := $(sort -I$(top_srcdir)/src/include -I$(top_builddir)/src/include)
uninstall-data:
rm -f $(addprefix $(DESTDIR)$(datadir)/, $(BKIFILES) system_views.sql information_schema.sql sql_features.txt)
-clean:
+clean:
rm -f SUBSYS.o $(OBJS) $(BKIFILES)
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.103 2004/06/01 21:49:22 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.104 2004/06/18 06:13:19 tgl Exp $
*
* NOTES
* See acl.h.
#include "catalog/pg_operator.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_shadow.h"
+#include "catalog/pg_tablespace.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
#include "parser/parse_func.h"
static void ExecuteGrantStmt_Function(GrantStmt *stmt);
static void ExecuteGrantStmt_Language(GrantStmt *stmt);
static void ExecuteGrantStmt_Namespace(GrantStmt *stmt);
+static void ExecuteGrantStmt_Tablespace(GrantStmt *stmt);
static const char *privilege_to_string(AclMode privilege);
case ACL_OBJECT_NAMESPACE:
ExecuteGrantStmt_Namespace(stmt);
break;
+ case ACL_OBJECT_TABLESPACE:
+ ExecuteGrantStmt_Tablespace(stmt);
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) stmt->objtype);
}
}
+
static void
ExecuteGrantStmt_Relation(GrantStmt *stmt)
{
}
}
+static void
+ExecuteGrantStmt_Tablespace(GrantStmt *stmt)
+{
+ AclMode privileges;
+ bool all_privs;
+ ListCell *i;
+
+ if (linitial_int(stmt->privileges) == ACL_ALL_RIGHTS)
+ {
+ all_privs = true;
+ privileges = ACL_ALL_RIGHTS_TABLESPACE;
+ }
+ else
+ {
+ all_privs = false;
+ privileges = ACL_NO_RIGHTS;
+ foreach(i, stmt->privileges)
+ {
+ AclMode priv = lfirst_int(i);
+
+ if (priv & ~((AclMode) ACL_ALL_RIGHTS_TABLESPACE))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_GRANT_OPERATION),
+ errmsg("invalid privilege type %s for tablespace",
+ privilege_to_string(priv))));
+ privileges |= priv;
+ }
+ }
+
+ foreach(i, stmt->objects)
+ {
+ char *spcname = strVal(lfirst(i));
+ Relation relation;
+ ScanKeyData entry[1];
+ HeapScanDesc scan;
+ HeapTuple tuple;
+ Form_pg_tablespace pg_tablespace_tuple;
+ Datum aclDatum;
+ bool isNull;
+ AclMode my_goptions;
+ AclMode this_privileges;
+ Acl *old_acl;
+ Acl *new_acl;
+ AclId grantorId;
+ AclId ownerId;
+ HeapTuple newtuple;
+ Datum values[Natts_pg_tablespace];
+ char nulls[Natts_pg_tablespace];
+ char replaces[Natts_pg_tablespace];
+
+ relation = heap_openr(TableSpaceRelationName, RowExclusiveLock);
+ ScanKeyInit(&entry[0],
+ Anum_pg_tablespace_spcname,
+ BTEqualStrategyNumber, F_NAMEEQ,
+ CStringGetDatum(spcname));
+ scan = heap_beginscan(relation, SnapshotNow, 1, entry);
+ tuple = heap_getnext(scan, ForwardScanDirection);
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("tablespace \"%s\" does not exist", spcname)));
+ pg_tablespace_tuple = (Form_pg_tablespace) GETSTRUCT(tuple);
+
+ ownerId = pg_tablespace_tuple->spcowner;
+ grantorId = select_grantor(ownerId);
+
+ /*
+ * Must be owner or have some privilege on the object (per spec,
+ * any privilege will get you by here). The owner is always
+ * treated as having all grant options.
+ */
+ if (pg_tablespace_ownercheck(HeapTupleGetOid(tuple), GetUserId()))
+ my_goptions = ACL_ALL_RIGHTS_TABLESPACE;
+ else
+ {
+ AclMode my_rights;
+
+ my_rights = pg_tablespace_aclmask(HeapTupleGetOid(tuple),
+ GetUserId(),
+ ACL_ALL_RIGHTS_TABLESPACE | ACL_GRANT_OPTION_FOR(ACL_ALL_RIGHTS_TABLESPACE),
+ ACLMASK_ALL);
+ if (my_rights == ACL_NO_RIGHTS)
+ aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_TABLESPACE,
+ spcname);
+ my_goptions = ACL_OPTION_TO_PRIVS(my_rights);
+ }
+
+ /*
+ * Restrict the operation to what we can actually grant or revoke,
+ * and issue a warning if appropriate. (For REVOKE this isn't quite
+ * what the spec says to do: the spec seems to want a warning only
+ * if no privilege bits actually change in the ACL. In practice
+ * that behavior seems much too noisy, as well as inconsistent with
+ * the GRANT case.)
+ */
+ this_privileges = privileges & my_goptions;
+ if (stmt->is_grant)
+ {
+ if (this_privileges == 0)
+ ereport(WARNING,
+ (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
+ errmsg("no privileges were granted")));
+ else if (!all_privs && this_privileges != privileges)
+ ereport(WARNING,
+ (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
+ errmsg("not all privileges were granted")));
+ }
+ else
+ {
+ if (this_privileges == 0)
+ ereport(WARNING,
+ (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
+ errmsg("no privileges could be revoked")));
+ else if (!all_privs && this_privileges != privileges)
+ ereport(WARNING,
+ (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
+ errmsg("not all privileges could be revoked")));
+ }
+
+ /*
+ * If there's no ACL, substitute the proper default.
+ */
+ aclDatum = heap_getattr(tuple, Anum_pg_tablespace_spcacl,
+ RelationGetDescr(relation), &isNull);
+ if (isNull)
+ old_acl = acldefault(ACL_OBJECT_TABLESPACE, ownerId);
+ else
+ /* get a detoasted copy of the ACL */
+ old_acl = DatumGetAclPCopy(aclDatum);
+
+ new_acl = merge_acl_with_grant(old_acl, stmt->is_grant,
+ stmt->grant_option, stmt->behavior,
+ stmt->grantees, this_privileges,
+ grantorId, ownerId);
+
+ /* finished building new ACL value, now insert it */
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, ' ', sizeof(nulls));
+ MemSet(replaces, ' ', sizeof(replaces));
+
+ replaces[Anum_pg_tablespace_spcacl - 1] = 'r';
+ values[Anum_pg_tablespace_spcacl - 1] = PointerGetDatum(new_acl);
+
+ newtuple = heap_modifytuple(tuple, relation, values, nulls, replaces);
+
+ simple_heap_update(relation, &newtuple->t_self, newtuple);
+
+ /* keep the catalog indexes up to date */
+ CatalogUpdateIndexes(relation, newtuple);
+
+ pfree(new_acl);
+
+ heap_endscan(scan);
+ heap_close(relation, RowExclusiveLock);
+ }
+}
+
static const char *
privilege_to_string(AclMode privilege)
/* ACL_KIND_OPCLASS */
gettext_noop("permission denied for operator class %s"),
/* ACL_KIND_CONVERSION */
- gettext_noop("permission denied for conversion %s")
+ gettext_noop("permission denied for conversion %s"),
+ /* ACL_KIND_TABLESPACE */
+ gettext_noop("permission denied for tablespace %s")
};
static const char *const not_owner_msg[MAX_ACL_KIND] =
/* ACL_KIND_OPCLASS */
gettext_noop("must be owner of operator class %s"),
/* ACL_KIND_CONVERSION */
- gettext_noop("must be owner of conversion %s")
+ gettext_noop("must be owner of conversion %s"),
+ /* ACL_KIND_TABLESPACE */
+ gettext_noop("must be owner of tablespace %s")
};
return result;
}
+/*
+ * Exported routine for examining a user's privileges for a tablespace
+ */
+AclMode
+pg_tablespace_aclmask(Oid spc_oid, AclId userid,
+ AclMode mask, AclMaskHow how)
+{
+ AclMode result;
+ Relation pg_tablespace;
+ ScanKeyData entry[1];
+ HeapScanDesc scan;
+ HeapTuple tuple;
+ Datum aclDatum;
+ bool isNull;
+ Acl *acl;
+ AclId ownerId;
+
+ /*
+ * Only shared relations can be stored in global space; don't let
+ * even superusers override this
+ */
+ if (spc_oid == GLOBALTABLESPACE_OID && !IsBootstrapProcessingMode())
+ return 0;
+
+ /* Otherwise, superusers bypass all permission checking. */
+ if (superuser_arg(userid))
+ return mask;
+
+ /*
+ * Get the tablespace's ACL from pg_tablespace
+ *
+ * There's no syscache for pg_tablespace, so must look the hard way
+ */
+ pg_tablespace = heap_openr(TableSpaceRelationName, AccessShareLock);
+ ScanKeyInit(&entry[0],
+ ObjectIdAttributeNumber,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(spc_oid));
+ scan = heap_beginscan(pg_tablespace, SnapshotNow, 1, entry);
+ tuple = heap_getnext(scan, ForwardScanDirection);
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("tablespace with OID %u does not exist", spc_oid)));
+
+ ownerId = ((Form_pg_tablespace) GETSTRUCT(tuple))->spcowner;
+
+ aclDatum = heap_getattr(tuple, Anum_pg_tablespace_spcacl,
+ RelationGetDescr(pg_tablespace), &isNull);
+
+ if (isNull)
+ {
+ /* No ACL, so build default ACL */
+ acl = acldefault(ACL_OBJECT_TABLESPACE, ownerId);
+ aclDatum = (Datum) 0;
+ }
+ else
+ {
+ /* detoast ACL if necessary */
+ acl = DatumGetAclP(aclDatum);
+ }
+
+ result = aclmask(acl, userid, ownerId, mask, how);
+
+ /* if we have a detoasted copy, free it */
+ if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
+ pfree(acl);
+
+ heap_endscan(scan);
+ heap_close(pg_tablespace, AccessShareLock);
+
+ return result;
+}
+
/*
* Exported routine for checking a user's access privileges to a table
return ACLCHECK_NO_PRIV;
}
+/*
+ * Exported routine for checking a user's access privileges to a tablespace
+ */
+AclResult
+pg_tablespace_aclcheck(Oid spc_oid, AclId userid, AclMode mode)
+{
+ if (pg_tablespace_aclmask(spc_oid, userid, mode, ACLMASK_ANY) != 0)
+ return ACLCHECK_OK;
+ else
+ return ACLCHECK_NO_PRIV;
+}
+
/*
* Ownership check for a relation (specified by OID).
return userid == owner_id;
}
+/*
+ * Ownership check for a tablespace (specified by OID).
+ */
+bool
+pg_tablespace_ownercheck(Oid spc_oid, AclId userid)
+{
+ Relation pg_tablespace;
+ ScanKeyData entry[1];
+ HeapScanDesc scan;
+ HeapTuple spctuple;
+ int32 spcowner;
+
+ /* Superusers bypass all permission checking. */
+ if (superuser_arg(userid))
+ return true;
+
+ /* There's no syscache for pg_tablespace, so must look the hard way */
+ pg_tablespace = heap_openr(TableSpaceRelationName, AccessShareLock);
+ ScanKeyInit(&entry[0],
+ ObjectIdAttributeNumber,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(spc_oid));
+ scan = heap_beginscan(pg_tablespace, SnapshotNow, 1, entry);
+
+ spctuple = heap_getnext(scan, ForwardScanDirection);
+
+ if (!HeapTupleIsValid(spctuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("tablespace with OID %u does not exist", spc_oid)));
+
+ spcowner = ((Form_pg_tablespace) GETSTRUCT(spctuple))->spcowner;
+
+ heap_endscan(scan);
+ heap_close(pg_tablespace, AccessShareLock);
+
+ return userid == spcowner;
+}
+
/*
* Ownership check for an operator class (specified by OID).
*/
return userid == owner_id;
}
-
/*
- * Ownership check for database (specified as OID)
+ * Ownership check for a database (specified by OID).
*/
bool
pg_database_ownercheck(Oid db_oid, AclId userid)
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/catalog.c,v 1.51 2004/01/06 18:07:31 neilc Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/catalog.c,v 1.52 2004/06/18 06:13:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "catalog/catalog.h"
#include "catalog/catname.h"
#include "catalog/pg_namespace.h"
+#include "catalog/pg_tablespace.h"
#include "miscadmin.h"
+#define OIDCHARS 10 /* max chars printed by %u */
+
+
/*
* relpath - construct path to a relation's file
*
* Result is a palloc'd string.
*/
-
char *
relpath(RelFileNode rnode)
{
+ int pathlen;
char *path;
- if (rnode.tblNode == (Oid) 0) /* "global tablespace" */
+ if (rnode.spcNode == GLOBALTABLESPACE_OID)
{
/* Shared system relations live in {datadir}/global */
- path = (char *) palloc(strlen(DataDir) + 8 + sizeof(NameData) + 1);
- sprintf(path, "%s/global/%u", DataDir, rnode.relNode);
+ Assert(rnode.dbNode == 0);
+ pathlen = strlen(DataDir) + 8 + OIDCHARS + 1;
+ path = (char *) palloc(pathlen);
+ snprintf(path, pathlen, "%s/global/%u",
+ DataDir, rnode.relNode);
+ }
+ else if (rnode.spcNode == DEFAULTTABLESPACE_OID)
+ {
+ /* The default tablespace is {datadir}/base */
+ pathlen = strlen(DataDir) + 6 + OIDCHARS + 1 + OIDCHARS + 1;
+ path = (char *) palloc(pathlen);
+ snprintf(path, pathlen, "%s/base/%u/%u",
+ DataDir, rnode.dbNode, rnode.relNode);
}
else
{
- path = (char *) palloc(strlen(DataDir) + 6 + 2 * sizeof(NameData) + 3);
- sprintf(path, "%s/base/%u/%u", DataDir, rnode.tblNode, rnode.relNode);
+ /* All other tablespaces are accessed via symlinks */
+ pathlen = strlen(DataDir) + 16 + OIDCHARS + 1 + OIDCHARS + 1 + OIDCHARS + 1;
+ path = (char *) palloc(pathlen);
+ snprintf(path, pathlen, "%s/pg_tablespaces/%u/%u/%u",
+ DataDir, rnode.spcNode, rnode.dbNode, rnode.relNode);
}
return path;
}
* GetDatabasePath - construct path to a database dir
*
* Result is a palloc'd string.
+ *
+ * XXX this must agree with relpath()!
*/
-
char *
-GetDatabasePath(Oid tblNode)
+GetDatabasePath(Oid dbNode, Oid spcNode)
{
+ int pathlen;
char *path;
- if (tblNode == (Oid) 0) /* "global tablespace" */
+ if (spcNode == GLOBALTABLESPACE_OID)
{
/* Shared system relations live in {datadir}/global */
- path = (char *) palloc(strlen(DataDir) + 8);
- sprintf(path, "%s/global", DataDir);
+ Assert(dbNode == 0);
+ pathlen = strlen(DataDir) + 7 + 1;
+ path = (char *) palloc(pathlen);
+ snprintf(path, pathlen, "%s/global",
+ DataDir);
+ }
+ else if (spcNode == DEFAULTTABLESPACE_OID)
+ {
+ /* The default tablespace is {datadir}/base */
+ pathlen = strlen(DataDir) + 6 + OIDCHARS + 1;
+ path = (char *) palloc(pathlen);
+ snprintf(path, pathlen, "%s/base/%u",
+ DataDir, dbNode);
}
else
{
- path = (char *) palloc(strlen(DataDir) + 6 + sizeof(NameData) + 1);
- sprintf(path, "%s/base/%u", DataDir, tblNode);
+ /* All other tablespaces are accessed via symlinks */
+ pathlen = strlen(DataDir) + 16 + OIDCHARS + 1 + OIDCHARS + 1;
+ path = (char *) palloc(pathlen);
+ snprintf(path, pathlen, "%s/pg_tablespaces/%u/%u",
+ DataDir, spcNode, dbNode);
}
return path;
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.270 2004/06/10 17:55:53 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.271 2004/06/18 06:13:19 tgl Exp $
*
*
* INTERFACE ROUTINES
#include "catalog/pg_statistic.h"
#include "catalog/pg_type.h"
#include "commands/tablecmds.h"
+#include "commands/tablespace.h"
#include "commands/trigger.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
Relation
heap_create(const char *relname,
Oid relnamespace,
+ Oid reltablespace,
TupleDesc tupDesc,
bool shared_relation,
bool storage_create,
bool allow_system_table_mods)
{
Oid relid;
- Oid dbid = shared_relation ? InvalidOid : MyDatabaseId;
bool nailme = false;
- RelFileNode rnode;
Relation rel;
/*
relid = RelOid_pg_group;
else if (strcmp(DatabaseRelationName, relname) == 0)
relid = RelOid_pg_database;
+ else if (strcmp(TableSpaceRelationName, relname) == 0)
+ relid = RelOid_pg_tablespace;
else
relid = newoid();
}
else
relid = newoid();
- /*
- * For now, the physical identifier of the relation is the same as the
- * logical identifier.
- */
- rnode.tblNode = dbid;
- rnode.relNode = relid;
-
/*
* build the relcache entry.
*/
rel = RelationBuildLocalRelation(relname,
relnamespace,
tupDesc,
- relid, dbid,
- rnode,
+ relid,
+ reltablespace,
+ shared_relation,
nailme);
/*
void
heap_storage_create(Relation rel)
{
+ /*
+ * We may be using the target table space for the first time in this
+ * database, so create a per-database subdirectory if needed.
+ *
+ * XXX it might be better to do this right in smgrcreate...
+ */
+ TablespaceCreateDbspace(rel->rd_node.spcNode, rel->rd_node.dbNode);
+ /*
+ * Now we can make the file.
+ */
Assert(rel->rd_smgr == NULL);
rel->rd_smgr = smgropen(rel->rd_node);
smgrcreate(rel->rd_smgr, rel->rd_istemp, false);
Oid
heap_create_with_catalog(const char *relname,
Oid relnamespace,
+ Oid reltablespace,
TupleDesc tupdesc,
char relkind,
bool shared_relation,
*/
new_rel_desc = heap_create(relname,
relnamespace,
+ reltablespace,
tupdesc,
shared_relation,
(relkind != RELKIND_VIEW &&
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.233 2004/05/31 19:24:05 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.234 2004/06/18 06:13:19 tgl Exp $
*
*
* INTERFACE ROUTINES
const char *indexRelationName,
IndexInfo *indexInfo,
Oid accessMethodObjectId,
+ Oid tableSpaceId,
Oid *classObjectId,
bool primary,
bool isconstraint,
*/
indexRelation = heap_create(indexRelationName,
namespaceId,
+ tableSpaceId,
indexTupDesc,
shared_relation,
true,
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.66 2004/05/28 16:17:14 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.67 2004/06/18 06:13:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* that access the temp namespace for my own backend skip
* permissions checks on it.
*/
- namespaceId = NamespaceCreate(namespaceName, BOOTSTRAP_USESYSID);
+ namespaceId = NamespaceCreate(namespaceName, BOOTSTRAP_USESYSID, 0);
/* Advance command counter to make namespace visible */
CommandCounterIncrement();
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/pg_namespace.c,v 1.8 2003/11/29 19:51:46 pgsql Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/pg_namespace.c,v 1.9 2004/06/18 06:13:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* ---------------
*/
Oid
-NamespaceCreate(const char *nspName, int32 ownerSysId)
+NamespaceCreate(const char *nspName, int32 ownerSysId, Oid nspTablespace)
{
Relation nspdesc;
HeapTuple tup;
namestrcpy(&nname, nspName);
values[Anum_pg_namespace_nspname - 1] = NameGetDatum(&nname);
values[Anum_pg_namespace_nspowner - 1] = Int32GetDatum(ownerSysId);
+ values[Anum_pg_namespace_nsptablespace - 1] = Int32GetDatum(nspTablespace);
nulls[Anum_pg_namespace_nspacl - 1] = 'n';
nspdesc = heap_openr(NamespaceRelationName, RowExclusiveLock);
# Makefile for backend/commands
#
# IDENTIFICATION
-# $PostgreSQL: pgsql/src/backend/commands/Makefile,v 1.33 2003/11/29 19:51:47 pgsql Exp $
+# $PostgreSQL: pgsql/src/backend/commands/Makefile,v 1.34 2004/06/18 06:13:22 tgl Exp $
#
#-------------------------------------------------------------------------
dbcommands.o define.o explain.o functioncmds.o \
indexcmds.o lockcmds.o operatorcmds.o opclasscmds.o \
portalcmds.o prepare.o proclang.o \
- schemacmds.o sequence.o tablecmds.o trigger.o typecmds.o user.o \
- vacuum.o vacuumlazy.o variable.o view.o
+ schemacmds.o sequence.o tablecmds.o tablespace.o trigger.o \
+ typecmds.o user.o vacuum.o vacuumlazy.o variable.o view.o
all: SUBSYS.o
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.125 2004/05/31 19:24:05 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.126 2004/06/18 06:13:22 tgl Exp $
*
*-------------------------------------------------------------------------
*/
OIDNewHeap = heap_create_with_catalog(NewName,
RelationGetNamespace(OldHeap),
+ OldHeap->rd_rel->reltablespace,
tupdesc,
OldHeap->rd_rel->relkind,
OldHeap->rd_rel->relisshared,
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.135 2004/06/10 22:26:18 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.136 2004/06/18 06:13:22 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
-#include
#include
#include
#include
#include "catalog/catalog.h"
#include "catalog/pg_database.h"
#include "catalog/pg_shadow.h"
+#include "catalog/pg_tablespace.h"
#include "catalog/indexing.h"
#include "commands/comment.h"
#include "commands/dbcommands.h"
+#include "commands/tablespace.h"
+#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "storage/fd.h"
#include "storage/freespace.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
-#include "mb/pg_wchar.h" /* encoding check */
-
/* non-export function prototypes */
static bool get_db_info(const char *name, Oid *dbIdP, int4 *ownerIdP,
int *encodingP, bool *dbIsTemplateP, Oid *dbLastSysOidP,
TransactionId *dbVacuumXidP, TransactionId *dbFrozenXidP,
- char *dbpath);
+ Oid *dbTablespace);
static bool have_createdb_privilege(void);
-static char *resolve_alt_dbpath(const char *dbpath, Oid dboid);
-static bool remove_dbdirs(const char *real_loc, const char *altloc);
+static void remove_dbtablespaces(Oid db_id);
+
/*
* CREATE DATABASE
*/
-
void
createdb(const CreatedbStmt *stmt)
{
- char *nominal_loc;
- char *alt_loc;
- char *target_dir;
- char src_loc[MAXPGPATH];
-#ifndef WIN32
- char buf[2 * MAXPGPATH + 100];
-#endif
+ HeapScanDesc scan;
+ Relation rel;
Oid src_dboid;
AclId src_owner;
int src_encoding;
Oid src_lastsysoid;
TransactionId src_vacuumxid;
TransactionId src_frozenxid;
- char src_dbpath[MAXPGPATH];
+ Oid src_deftablespace;
+ Oid dst_deftablespace;
Relation pg_database_rel;
HeapTuple tuple;
TupleDesc pg_database_dsc;
Oid dboid;
AclId datdba;
ListCell *option;
+ DefElem *dtablespacename = NULL;
DefElem *downer = NULL;
- DefElem *dpath = NULL;
DefElem *dtemplate = NULL;
DefElem *dencoding = NULL;
char *dbname = stmt->dbname;
char *dbowner = NULL;
- char *dbpath = NULL;
char *dbtemplate = NULL;
int encoding = -1;
+#ifndef WIN32
+ char buf[2 * MAXPGPATH + 100];
+#endif
+
+ /* don't call this in a transaction block */
+ PreventTransactionChain((void *) stmt, "CREATE DATABASE");
/* Extract options from the statement node tree */
foreach(option, stmt->options)
{
DefElem *defel = (DefElem *) lfirst(option);
- if (strcmp(defel->defname, "owner") == 0)
+ if (strcmp(defel->defname, "tablespace") == 0)
{
- if (downer)
+ if (dtablespacename)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("conflicting or redundant options")));
- downer = defel;
+ dtablespacename = defel;
}
- else if (strcmp(defel->defname, "location") == 0)
+ else if (strcmp(defel->defname, "owner") == 0)
{
- if (dpath)
+ if (downer)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("conflicting or redundant options")));
- dpath = defel;
+ downer = defel;
}
else if (strcmp(defel->defname, "template") == 0)
{
errmsg("conflicting or redundant options")));
dencoding = defel;
}
+ else if (strcmp(defel->defname, "location") == 0)
+ {
+ ereport(WARNING,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("LOCATION is not supported anymore"),
+ errhint("Consider using tablespaces instead.")));
+ }
else
elog(ERROR, "option \"%s\" not recognized",
defel->defname);
if (downer && downer->arg)
dbowner = strVal(downer->arg);
- if (dpath && dpath->arg)
- dbpath = strVal(dpath->arg);
if (dtemplate && dtemplate->arg)
dbtemplate = strVal(dtemplate->arg);
if (dencoding && dencoding->arg)
errmsg("must be superuser to create database for another user")));
}
- /* don't call this in a transaction block */
- PreventTransactionChain((void *) stmt, "CREATE DATABASE");
-
- /* alternate location requires symlinks */
-#ifndef HAVE_SYMLINK
- if (dbpath != NULL)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot use an alternative location on this platform")));
-#endif
-
/*
* Check for db name conflict. There is a race condition here, since
* another backend could create the same DB name before we commit.
if (!get_db_info(dbtemplate, &src_dboid, &src_owner, &src_encoding,
&src_istemplate, &src_lastsysoid,
- &src_vacuumxid, &src_frozenxid,
- src_dbpath))
+ &src_vacuumxid, &src_frozenxid, &src_deftablespace))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_DATABASE),
errmsg("template database \"%s\" does not exist", dbtemplate)));
dbtemplate)));
}
- /*
- * Determine physical path of source database
- */
- alt_loc = resolve_alt_dbpath(src_dbpath, src_dboid);
- if (!alt_loc)
- alt_loc = GetDatabasePath(src_dboid);
- strcpy(src_loc, alt_loc);
-
/*
* The source DB can't have any active backends, except this one
* (exception is to allow CREATE DB while connected to template1).
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("invalid server encoding %d", encoding)));
+ /* Resolve default tablespace for new database */
+ if (dtablespacename && dtablespacename->arg)
+ {
+ char *tablespacename;
+ AclResult aclresult;
+
+ tablespacename = strVal(dtablespacename->arg);
+ dst_deftablespace = get_tablespace_oid(tablespacename);
+ if (!OidIsValid(dst_deftablespace))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("tablespace \"%s\" does not exist",
+ tablespacename)));
+ /* check permissions */
+ aclresult = pg_tablespace_aclcheck(dst_deftablespace, GetUserId(),
+ ACL_CREATE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_TABLESPACE,
+ tablespacename);
+ }
+ else
+ {
+ /* Use template database's default tablespace */
+ dst_deftablespace = src_deftablespace;
+ /* Note there is no additional permission check in this path */
+ }
+
/*
* Preassign OID for pg_database tuple, so that we can compute db
* path.
*/
dboid = newoid();
- /*
- * Compute nominal location (where we will try to access the
- * database), and resolve alternate physical location if one is
- * specified.
- *
- * If an alternate location is specified but is the same as the normal
- * path, just drop the alternate-location spec (this seems friendlier
- * than erroring out). We must test this case to avoid creating a
- * circular symlink below.
- */
- nominal_loc = GetDatabasePath(dboid);
- alt_loc = resolve_alt_dbpath(dbpath, dboid);
-
- if (alt_loc && strcmp(alt_loc, nominal_loc) == 0)
- {
- alt_loc = NULL;
- dbpath = NULL;
- }
-
- if (strchr(nominal_loc, '\''))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_NAME),
- errmsg("database path may not contain single quotes")));
- if (alt_loc && strchr(alt_loc, '\''))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_NAME),
- errmsg("database path may not contain single quotes")));
- if (strchr(src_loc, '\''))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_NAME),
- errmsg("database path may not contain single quotes")));
- /* ... otherwise we'd be open to shell exploits below */
-
/*
* Force dirty buffers out to disk, to ensure source database is
* up-to-date for the copy. (We really only need to flush buffers for
/*
* Close virtual file descriptors so the kernel has more available for
- * the mkdir() and system() calls below.
+ * the system() calls below.
*/
closeAllVfds();
/*
- * Check we can create the target directory --- but then remove it
- * because we rely on cp(1) to create it for real.
+ * Iterate through all tablespaces of the template database, and
+ * copy each one to the new database.
+ *
+ * If we are trying to change the default tablespace of the template,
+ * we require that the template not have any files in the new default
+ * tablespace. This avoids the need to merge two subdirectories.
+ * This could probably be improved later.
*/
- target_dir = alt_loc ? alt_loc : nominal_loc;
+ rel = heap_openr(TableSpaceRelationName, AccessShareLock);
+ scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
+ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ {
+ Oid srctablespace = HeapTupleGetOid(tuple);
+ Oid dsttablespace;
+ char *srcpath;
+ char *dstpath;
+ struct stat st;
- if (mkdir(target_dir, S_IRWXU) != 0)
- ereport(ERROR,
- (errcode_for_file_access(),
- errmsg("could not create database directory \"%s\": %m",
- target_dir)));
- if (rmdir(target_dir) != 0)
- ereport(ERROR,
- (errcode_for_file_access(),
- errmsg("could not remove temporary directory \"%s\": %m",
- target_dir)));
+ /* No need to copy global tablespace */
+ if (srctablespace == GLOBALTABLESPACE_OID)
+ continue;
- /* Make the symlink, if needed */
- if (alt_loc)
- {
-#ifdef HAVE_SYMLINK /* already throws error above */
- if (symlink(alt_loc, nominal_loc) != 0)
-#endif
+ srcpath = GetDatabasePath(src_dboid, srctablespace);
+
+ if (stat(srcpath, &st) < 0 || !S_ISDIR(st.st_mode))
+ {
+ /* Assume we can ignore it */
+ pfree(srcpath);
+ continue;
+ }
+
+ if (srctablespace == src_deftablespace)
+ dsttablespace = dst_deftablespace;
+ else
+ dsttablespace = srctablespace;
+
+ dstpath = GetDatabasePath(dboid, dsttablespace);
+
+ if (stat(dstpath, &st) == 0 || errno != ENOENT)
+ {
+ remove_dbtablespaces(dboid);
ereport(ERROR,
- (errcode_for_file_access(),
- errmsg("could not link file \"%s\" to \"%s\": %m",
- nominal_loc, alt_loc)));
- }
+ (errmsg("could not initialize database directory"),
+ errdetail("Directory \"%s\" already exists.", dstpath)));
+ }
- /*
- * Copy the template database to the new location
- *
- * XXX use of cp really makes this code pretty grotty, particularly
- * with respect to lack of ability to report errors well. Someday
- * rewrite to do it for ourselves.
- */
#ifndef WIN32
- /* We might need to use cp -R one day for portability */
- snprintf(buf, sizeof(buf), "cp -r '%s' '%s'", src_loc, target_dir);
- if (system(buf) != 0)
- {
- if (remove_dbdirs(nominal_loc, alt_loc))
+ /*
+ * Copy this subdirectory to the new location
+ *
+ * XXX use of cp really makes this code pretty grotty, particularly
+ * with respect to lack of ability to report errors well. Someday
+ * rewrite to do it for ourselves.
+ */
+
+ /* We might need to use cp -R one day for portability */
+ snprintf(buf, sizeof(buf), "cp -r '%s' '%s'",
+ srcpath, dstpath);
+ if (system(buf) != 0)
+ {
+ remove_dbtablespaces(dboid);
ereport(ERROR,
(errmsg("could not initialize database directory"),
errdetail("Failing system command was: %s", buf),
errhint("Look in the postmaster's stderr log for more information.")));
- else
- ereport(ERROR,
- (errmsg("could not initialize database directory; delete failed as well"),
- errdetail("Failing system command was: %s", buf),
- errhint("Look in the postmaster's stderr log for more information.")));
- }
+ }
#else /* WIN32 */
- if (copydir(src_loc, target_dir) != 0)
- {
- /* copydir should already have given details of its troubles */
- if (remove_dbdirs(nominal_loc, alt_loc))
+ if (copydir(srcpath, dstpath) != 0)
+ {
+ /* copydir should already have given details of its troubles */
+ remove_dbtablespaces(dboid);
ereport(ERROR,
(errmsg("could not initialize database directory")));
- else
- ereport(ERROR,
- (errmsg("could not initialize database directory; delete failed as well")));
- }
+ }
#endif /* WIN32 */
+ }
+ heap_endscan(scan);
+ heap_close(rel, AccessShareLock);
/*
* Now OK to grab exclusive lock on pg_database.
{
/* Don't hold lock while doing recursive remove */
heap_close(pg_database_rel, AccessExclusiveLock);
- remove_dbdirs(nominal_loc, alt_loc);
+ remove_dbtablespaces(dboid);
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_DATABASE),
errmsg("database \"%s\" already exists", dbname)));
new_record[Anum_pg_database_datlastsysoid - 1] = ObjectIdGetDatum(src_lastsysoid);
new_record[Anum_pg_database_datvacuumxid - 1] = TransactionIdGetDatum(src_vacuumxid);
new_record[Anum_pg_database_datfrozenxid - 1] = TransactionIdGetDatum(src_frozenxid);
- /* do not set datpath to null, GetRawDatabaseInfo won't cope */
- new_record[Anum_pg_database_datpath - 1] =
- DirectFunctionCall1(textin, CStringGetDatum(dbpath ? dbpath : ""));
+ new_record[Anum_pg_database_dattablespace - 1] = ObjectIdGetDatum(dst_deftablespace);
/*
* We deliberately set datconfig and datacl to defaults (NULL), rather
int4 db_owner;
bool db_istemplate;
Oid db_id;
- char *alt_loc;
- char *nominal_loc;
- char dbpath[MAXPGPATH];
Relation pgdbrel;
SysScanDesc pgdbscan;
ScanKeyData key;
HeapTuple tup;
+ PreventTransactionChain((void *) dbname, "DROP DATABASE");
+
AssertArg(dbname);
if (strcmp(dbname, get_database_name(MyDatabaseId)) == 0)
(errcode(ERRCODE_OBJECT_IN_USE),
errmsg("cannot drop the currently open database")));
- PreventTransactionChain((void *) dbname, "DROP DATABASE");
-
/*
* Obtain exclusive lock on pg_database. We need this to ensure that
* no new backend starts up in the target database while we are
pgdbrel = heap_openr(DatabaseRelationName, AccessExclusiveLock);
if (!get_db_info(dbname, &db_id, &db_owner, NULL,
- &db_istemplate, NULL, NULL, NULL, dbpath))
+ &db_istemplate, NULL, NULL, NULL, NULL))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_DATABASE),
errmsg("database \"%s\" does not exist", dbname)));
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot drop a template database")));
- nominal_loc = GetDatabasePath(db_id);
- alt_loc = resolve_alt_dbpath(dbpath, db_id);
-
/*
* Check for active backends in the target database.
*/
FreeSpaceMapForgetDatabase(db_id);
/*
- * Remove the database's subdirectory and everything in it.
+ * Remove all tablespace subdirs belonging to the database.
*/
- remove_dbdirs(nominal_loc, alt_loc);
+ remove_dbtablespaces(db_id);
/*
* Force dirty buffers out to disk, so that newly-connecting backends
get_db_info(const char *name, Oid *dbIdP, int4 *ownerIdP,
int *encodingP, bool *dbIsTemplateP, Oid *dbLastSysOidP,
TransactionId *dbVacuumXidP, TransactionId *dbFrozenXidP,
- char *dbpath)
+ Oid *dbTablespace)
{
Relation relation;
ScanKeyData scanKey;
/* limit of frozen XIDs */
if (dbFrozenXidP)
*dbFrozenXidP = dbform->datfrozenxid;
- /* database path (as registered in pg_database) */
- if (dbpath)
- {
- Datum datum;
- bool isnull;
-
- datum = heap_getattr(tuple,
- Anum_pg_database_datpath,
- RelationGetDescr(relation),
- &isnull);
- if (!isnull)
- {
- text *pathtext = DatumGetTextP(datum);
- int pathlen = VARSIZE(pathtext) - VARHDRSZ;
-
- Assert(pathlen >= 0 && pathlen < MAXPGPATH);
- strncpy(dbpath, VARDATA(pathtext), pathlen);
- *(dbpath + pathlen) = '\0';
- }
- else
- strcpy(dbpath, "");
- }
+ /* default tablespace for this database */
+ if (dbTablespace)
+ *dbTablespace = dbform->dattablespace;
}
systable_endscan(scan);
return retval;
}
-
-static char *
-resolve_alt_dbpath(const char *dbpath, Oid dboid)
+/*
+ * Remove tablespace directories
+ *
+ * We don't know what tablespaces db_id is using, so iterate through all
+ * tablespaces removing
/db_id
+ */
+static void
+remove_dbtablespaces(Oid db_id)
{
- const char *prefix;
- char *ret;
- size_t len;
-
- if (dbpath == NULL || dbpath[0] == '\0')
- return NULL;
-
- if (first_dir_separator(dbpath))
- {
- if (!is_absolute_path(dbpath))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("relative paths are not allowed as database locations")));
-#ifndef ALLOW_ABSOLUTE_DBPATHS
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("absolute paths are not allowed as database locations")));
-#endif
- prefix = dbpath;
- }
- else
+ Relation rel;
+ HeapScanDesc scan;
+ HeapTuple tuple;
+ char buf[MAXPGPATH + 100];
+
+ rel = heap_openr(TableSpaceRelationName, AccessShareLock);
+ scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
+ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
- /* must be environment variable */
- char *var = getenv(dbpath);
-
- if (!var)
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("postmaster environment variable \"%s\" not found",
- dbpath)));
- if (!is_absolute_path(var))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_NAME),
- errmsg("postmaster environment variable \"%s\" must be absolute path",
- dbpath)));
- prefix = var;
- }
+ Oid dsttablespace = HeapTupleGetOid(tuple);
+ char *dstpath;
+ struct stat st;
- len = strlen(prefix) + 6 + sizeof(Oid) * 8 + 1;
- if (len >= MAXPGPATH - 100)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_NAME),
- errmsg("alternative path is too long")));
+ /* Don't mess with the global tablespace */
+ if (dsttablespace == GLOBALTABLESPACE_OID)
+ continue;
- ret = palloc(len);
- snprintf(ret, len, "%s/base/%u", prefix, dboid);
+ dstpath = GetDatabasePath(db_id, dsttablespace);
- return ret;
-}
-
-
-static bool
-remove_dbdirs(const char *nominal_loc, const char *alt_loc)
-{
- const char *target_dir;
- char buf[MAXPGPATH + 100];
- bool success = true;
-
- target_dir = alt_loc ? alt_loc : nominal_loc;
-
- /*
- * Close virtual file descriptors so the kernel has more available for
- * the system() call below.
- */
- closeAllVfds();
-
- if (alt_loc)
- {
- /* remove symlink */
- if (unlink(nominal_loc) != 0)
+ if (stat(dstpath, &st) < 0 || !S_ISDIR(st.st_mode))
{
- ereport(WARNING,
- (errcode_for_file_access(),
- errmsg("could not remove file \"%s\": %m", nominal_loc)));
- success = false;
+ /* Assume we can ignore it */
+ pfree(dstpath);
+ continue;
}
- }
#ifndef WIN32
- snprintf(buf, sizeof(buf), "rm -rf '%s'", target_dir);
+ snprintf(buf, sizeof(buf), "rm -rf '%s'", dstpath);
#else
- snprintf(buf, sizeof(buf), "rmdir /s /q \"%s\"", target_dir);
+ snprintf(buf, sizeof(buf), "rmdir /s /q \"%s\"", dstpath);
#endif
-
- if (system(buf) != 0)
- {
- ereport(WARNING,
+ if (system(buf) != 0)
+ {
+ ereport(WARNING,
(errmsg("could not remove database directory \"%s\"",
- target_dir),
+ dstpath),
errdetail("Failing system command was: %s", buf),
errhint("Look in the postmaster's stderr log for more information.")));
- success = false;
+ }
+
+ pfree(dstpath);
}
- return success;
+ heap_endscan(scan);
+ heap_close(rel, AccessShareLock);
}
/*
* get_database_name - given a database OID, look up the name
*
- * Returns InvalidOid if database name not found.
+ * Returns a palloc'd string, or NULL if no such database.
*
* This is not actually used in this file, but is exported for use elsewhere.
*/
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.121 2004/06/10 17:55:56 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.122 2004/06/18 06:13:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "commands/dbcommands.h"
#include "commands/defrem.h"
#include "commands/tablecmds.h"
+#include "commands/tablespace.h"
#include "executor/executor.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
* 'indexRelationName': the name for the new index, or NULL to indicate
* that a nonconflicting default name should be picked.
* 'accessMethodName': name of the AM to use.
+ * 'tableSpaceName': name of the tablespace to create the index in.
+ * NULL specifies using the same tablespace as the parent relation.
* 'attributeList': a list of IndexElem specifying columns and expressions
* to index on.
* 'predicate': the partial-index condition, or NULL if none.
DefineIndex(RangeVar *heapRelation,
char *indexRelationName,
char *accessMethodName,
+ char *tableSpaceName,
List *attributeList,
Expr *predicate,
List *rangetable,
Oid accessMethodId;
Oid relationId;
Oid namespaceId;
+ Oid tablespaceId;
Relation rel;
HeapTuple tuple;
Form_pg_am accessMethodForm;
get_namespace_name(namespaceId));
}
+ /* Determine tablespace to use */
+ if (tableSpaceName)
+ {
+ AclResult aclresult;
+
+ tablespaceId = get_tablespace_oid(tableSpaceName);
+ if (!OidIsValid(tablespaceId))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("tablespace \"%s\" does not exist",
+ tableSpaceName)));
+ /* check permissions */
+ aclresult = pg_tablespace_aclcheck(tablespaceId, GetUserId(),
+ ACL_CREATE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_TABLESPACE,
+ tableSpaceName);
+ } else {
+ /* Use the parent rel's tablespace */
+ tablespaceId = get_rel_tablespace(relationId);
+ /* Note there is no additional permission check in this path */
+ }
+
/*
* Select name for index if caller didn't specify
*/
indexRelationName, RelationGetRelationName(rel))));
index_create(relationId, indexRelationName,
- indexInfo, accessMethodId, classObjectId,
+ indexInfo, accessMethodId, tablespaceId, classObjectId,
primary, isconstraint,
allowSystemTableMods, skip_build);
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/schemacmds.c,v 1.18 2004/05/26 04:41:11 neilc Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/schemacmds.c,v 1.19 2004/06/18 06:13:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "catalog/pg_namespace.h"
#include "commands/dbcommands.h"
#include "commands/schemacmds.h"
+#include "commands/tablespace.h"
#include "miscadmin.h"
#include "parser/analyze.h"
#include "tcop/utility.h"
const char *schemaName = stmt->schemaname;
const char *authId = stmt->authid;
Oid namespaceId;
+ Oid tablespaceId;
List *parsetree_list;
ListCell *parsetree_item;
const char *owner_name;
errmsg("unacceptable schema name \"%s\"", schemaName),
errdetail("The prefix \"pg_\" is reserved for system schemas.")));
+ /*
+ * Select default tablespace for schema. If not given, use zero
+ * which implies the database's default tablespace.
+ */
+ if (stmt->tablespacename)
+ {
+ AclResult aclresult;
+
+ tablespaceId = get_tablespace_oid(stmt->tablespacename);
+ if (!OidIsValid(tablespaceId))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("tablespace \"%s\" does not exist",
+ stmt->tablespacename)));
+ /* check permissions */
+ aclresult = pg_tablespace_aclcheck(tablespaceId, GetUserId(),
+ ACL_CREATE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_TABLESPACE,
+ stmt->tablespacename);
+ } else {
+ tablespaceId = InvalidOid;
+ /* note there is no permission check in this path */
+ }
+
/* Create the schema's namespace */
- namespaceId = NamespaceCreate(schemaName, owner_userid);
+ namespaceId = NamespaceCreate(schemaName, owner_userid, tablespaceId);
/* Advance cmd counter to make the namespace visible */
CommandCounterIncrement();
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.111 2004/05/26 04:41:11 neilc Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.112 2004/06/18 06:13:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
stmt->constraints = NIL;
stmt->hasoids = MUST_NOT_HAVE_OIDS;
stmt->oncommit = ONCOMMIT_NOOP;
+ stmt->tablespacename = seq->tablespacename;
seqoid = DefineRelation(stmt, RELKIND_SEQUENCE);
buffer = XLogReadBuffer(true, reln, 0);
if (!BufferIsValid(buffer))
- elog(PANIC, "seq_redo: can't read block of %u/%u",
- xlrec->node.tblNode, xlrec->node.relNode);
+ elog(PANIC, "seq_redo: can't read block 0 of rel %u/%u/%u",
+ xlrec->node.spcNode, xlrec->node.dbNode, xlrec->node.relNode);
page = (Page) BufferGetPage(buffer);
return;
}
- sprintf(buf + strlen(buf), "node %u/%u",
- xlrec->node.tblNode, xlrec->node.relNode);
+ sprintf(buf + strlen(buf), "rel %u/%u/%u",
+ xlrec->node.spcNode, xlrec->node.dbNode, xlrec->node.relNode);
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.115 2004/06/10 18:34:45 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.116 2004/06/18 06:13:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "commands/cluster.h"
#include "commands/defrem.h"
#include "commands/tablecmds.h"
+#include "commands/tablespace.h"
#include "commands/trigger.h"
#include "executor/executor.h"
#include "lib/stringinfo.h"
Oid namespaceId;
List *schema = stmt->tableElts;
Oid relationId;
+ Oid tablespaceId;
Relation rel;
TupleDesc descriptor;
List *inheritOids;
get_namespace_name(namespaceId));
}
+ /*
+ * Select tablespace to use. If not specified, use containing schema's
+ * default tablespace (which may in turn default to database's default).
+ */
+ if (stmt->tablespacename)
+ {
+ AclResult aclresult;
+
+ tablespaceId = get_tablespace_oid(stmt->tablespacename);
+ if (!OidIsValid(tablespaceId))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("tablespace \"%s\" does not exist",
+ stmt->tablespacename)));
+ /* check permissions */
+ aclresult = pg_tablespace_aclcheck(tablespaceId, GetUserId(),
+ ACL_CREATE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_TABLESPACE,
+ stmt->tablespacename);
+ } else {
+ tablespaceId = get_namespace_tablespace(namespaceId);
+ /* note no permission check on tablespace in this case */
+ }
+
/*
* Look up inheritance ancestors and generate relation schema,
* including inherited attributes.
relationId = heap_create_with_catalog(relname,
namespaceId,
+ tablespaceId,
descriptor,
relkind,
false,
/*
* ATPrepCmd
*
- * Traffic cop for ALTER TABLE Phase 1 operations, including simple
+ * Traffic cop for ALTER TABLE Phase 1 operations, including simple
* recursion and permission checks.
*
* Caller must have acquired AccessExclusiveLock on relation already.
}
-/*
+/*
* ALTER TABLE ADD COLUMN
*
* Adds an additional attribute to a relation making the assumption that
DefineIndex(stmt->relation, /* relation */
stmt->idxname, /* index name */
stmt->accessMethod, /* am name */
+ stmt->tableSpace,
stmt->indexParams, /* parameters */
(Expr *) stmt->whereClause,
stmt->rangetable,
list_make1(constr));
/* Add each constraint to Phase 3's queue */
foreach(lcon, newcons)
- {
+ {
CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
NewConstraint *newcon;
int16 fkattnum[INDEX_MAX_KEYS];
Oid pktypoid[INDEX_MAX_KEYS];
Oid fktypoid[INDEX_MAX_KEYS];
- Oid opclasses[INDEX_MAX_KEYS];
+ Oid opclasses[INDEX_MAX_KEYS];
int i;
int numfks,
numpks;
* will be incurred to check FK validity.
*/
if (!op_in_opclass(oprid(o), opclasses[i]))
- ereport(WARNING,
+ ereport(WARNING,
(errmsg("foreign key constraint \"%s\" "
"will require costly sequential scans",
fkconstraint->constr_name),
/*
* Add a work queue item to make ATRewriteTable update the column
* contents.
- */
+ */
newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
newval->attnum = attnum;
newval->expr = (Expr *) transform;
*/
toast_relid = heap_create_with_catalog(toast_relname,
PG_TOAST_NAMESPACE,
+ rel->rd_rel->reltablespace,
tupdesc,
RELKIND_TOASTVALUE,
shared_relation,
classObjectId[1] = INT4_BTREE_OPS_OID;
toast_idxid = index_create(toast_relid, toast_idxname, indexInfo,
- BTREE_AM_OID, classObjectId,
+ BTREE_AM_OID,
+ rel->rd_rel->reltablespace,
+ classObjectId,
true, false, true, false);
/*
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * tablespace.c
+ * Commands to manipulate table spaces
+ *
+ *
+ * Tablespaces in PostgreSQL are designed to allow users to determine
+ * where the data file(s) for a given database object reside on the file
+ * system.
+ *
+ * A tablespace represents a directory on the file system. At tablespace
+ * creation time, the directory must be empty. To simplify things and
+ * remove the possibility of having file name conflicts, we isolate
+ * files within a tablespace into database-specific subdirectories.
+ *
+ * To support file access via the information given in RelFileNode, we
+ * maintain a symbolic-link map in $PGDATA/pg_tablespaces. The symlinks are
+ * named by tablespace OIDs and point to the actual tablespace directories.
+ * Thus the full path to an arbitrary file is
+ * $PGDATA/pg_tablespaces/spcoid/dboid/relfilenode
+ *
+ * There are two tablespaces created at initdb time: global (for shared
+ * tables) and default (for everything else). For backwards compatibility
+ * and to remain functional on platforms without symlinks, these tablespaces
+ * are accessed specially: they are respectively
+ * $PGDATA/global/relfilenode
+ * $PGDATA/base/dboid/relfilenode
+ *
+ * The implementation is designed to be backwards compatible. For this reason
+ * (and also as a feature unto itself) when a user creates an object without
+ * specifying a tablespace, we look at the object's parent and place
+ * the object in the parent's tablespace. The hierarchy is as follows:
+ * database > schema > table > index
+ *
+ * To allow CREATE DATABASE to give a new database a default tablespace
+ * that's different from the template database's default, we make the
+ * provision that a zero in pg_class.reltablespace means the database's
+ * default tablespace. Without this, CREATE DATABASE would have to go in
+ * and munge the system catalogs of the new database. This special meaning
+ * of zero also applies in pg_namespace.nsptablespace.
+ *
+ *
+ * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $PostgreSQL: pgsql/src/backend/commands/tablespace.c,v 1.1 2004/06/18 06:13:23 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include
+#include
+#include
+#include
+
+#include "access/heapam.h"
+#include "catalog/catalog.h"
+#include "catalog/catname.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_tablespace.h"
+#include "commands/tablespace.h"
+#include "miscadmin.h"
+#include "storage/fd.h"
+#include "storage/smgr.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
+#include "utils/syscache.h"
+
+
+static void set_short_version(const char *path);
+static bool directory_is_empty(const char *path);
+
+
+/*
+ * Each database using a table space is isolated into its own name space
+ * by a subdirectory named for the database OID. On first creation of an
+ * object in the tablespace, create the subdirectory. If the subdirectory
+ * already exists, just fall through quietly.
+ *
+ * If tablespaces are not supported, this is just a no-op; CREATE DATABASE
+ * is expected to create the default subdirectory for the database.
+ */
+void
+TablespaceCreateDbspace(Oid spcNode, Oid dbNode)
+{
+#ifdef HAVE_SYMLINK
+ struct stat st;
+ char *dir;
+
+ /*
+ * The global tablespace doesn't have per-database subdirectories,
+ * so nothing to do for it.
+ */
+ if (spcNode == GLOBALTABLESPACE_OID)
+ return;
+
+ Assert(OidIsValid(spcNode));
+ Assert(OidIsValid(dbNode));
+
+ dir = GetDatabasePath(dbNode, spcNode);
+
+ if (stat(dir, &st) < 0)
+ {
+ if (errno == ENOENT)
+ {
+ /*
+ * Acquire ExclusiveLock on pg_tablespace to ensure that no
+ * DROP TABLESPACE or TablespaceCreateDbspace is running
+ * concurrently. Simple reads from pg_tablespace are OK.
+ */
+ Relation rel;
+
+ rel = heap_openr(TableSpaceRelationName, ExclusiveLock);
+
+ /*
+ * Recheck to see if someone created the directory while
+ * we were waiting for lock.
+ */
+ if (stat(dir, &st) == 0 && S_ISDIR(st.st_mode))
+ {
+ /* need not do anything */
+ }
+ else
+ {
+ /* OK, go for it */
+ if (mkdir(dir, S_IRWXU) < 0)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not create directory \"%s\": %m",
+ dir)));
+ }
+
+ /* OK to drop the exclusive lock */
+ heap_close(rel, ExclusiveLock);
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not stat directory \"%s\": %m", dir)));
+ }
+ }
+ else
+ {
+ /* be paranoid */
+ if (!S_ISDIR(st.st_mode))
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" exists but is not a directory",
+ dir)));
+ }
+
+ pfree(dir);
+#endif /* HAVE_SYMLINK */
+}
+
+/*
+ * Create a table space
+ *
+ * Only superusers can create a tablespace. This seems a reasonable restriction
+ * since we're determining the system layout and, anyway, we probably have
+ * root if we're doing this kind of activity
+ */
+void
+CreateTableSpace(CreateTableSpaceStmt *stmt)
+{
+#ifdef HAVE_SYMLINK
+ Relation rel;
+ Datum values[Natts_pg_tablespace];
+ char nulls[Natts_pg_tablespace];
+ HeapTuple tuple;
+ Oid tablespaceoid;
+ char *location;
+ char *linkloc;
+ AclId ownerid;
+
+ /* validate */
+
+ /* don't call this in a transaction block */
+ PreventTransactionChain((void *) stmt, "CREATE TABLESPACE");
+
+ /* Must be super user */
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to create tablespace \"%s\"",
+ stmt->tablespacename),
+ errhint("Must be superuser to create a tablespace.")));
+
+ /* However, the eventual owner of the tablespace need not be */
+ if (stmt->owner)
+ {
+ /* No need to check result, get_usesysid() does that */
+ ownerid = get_usesysid(stmt->owner);
+ }
+ else
+ ownerid = GetUserId();
+
+ /* Unix-ify the offered path, and strip any trailing slashes */
+ location = pstrdup(stmt->location);
+ canonicalize_path(location);
+
+ /* disallow quotes, else CREATE DATABASE would be at risk */
+ if (strchr(location, '\''))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_NAME),
+ errmsg("tablespace location may not contain single quotes")));
+
+ /*
+ * Allowing relative paths seems risky
+ *
+ * this also helps us ensure that location is not empty or whitespace
+ */
+ if (!is_absolute_path(location))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("tablespace location must be an absolute path")));
+
+ /*
+ * Check that location isn't too long. Remember that we're going to append
+ * '//.' (XXX but do we ever form the whole path
+ * explicitly? This may be overly conservative.)
+ */
+ if (strlen(location) >= (MAXPGPATH - 1 - 10 - 1 - 10 - 1 - 10))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("tablespace location \"%s\" is too long",
+ location)));
+
+ /*
+ * Check that there is no other tablespace by this name. (The
+ * unique index would catch this anyway, but might as well give
+ * a friendlier message.)
+ */
+ if (OidIsValid(get_tablespace_oid(stmt->tablespacename)))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("tablespace \"%s\" already exists",
+ stmt->tablespacename)));
+
+ /*
+ * Insert tuple into pg_tablespace. The purpose of doing this first
+ * is to lock the proposed tablename against other would-be creators.
+ * The insertion will roll back if we find problems below.
+ */
+ rel = heap_openr(TableSpaceRelationName, RowExclusiveLock);
+
+ MemSet(nulls, ' ', Natts_pg_tablespace);
+
+ values[Anum_pg_tablespace_spcname - 1] =
+ DirectFunctionCall1(namein, CStringGetDatum(stmt->tablespacename));
+ values[Anum_pg_tablespace_spcowner - 1] =
+ Int32GetDatum(ownerid);
+ values[Anum_pg_tablespace_spclocation - 1] =
+ DirectFunctionCall1(textin, CStringGetDatum(location));
+ nulls[Anum_pg_tablespace_spcacl - 1] = 'n';
+
+ tuple = heap_formtuple(rel->rd_att, values, nulls);
+
+ tablespaceoid = newoid();
+
+ HeapTupleSetOid(tuple, tablespaceoid);
+
+ simple_heap_insert(rel, tuple);
+
+ CatalogUpdateIndexes(rel, tuple);
+
+ heap_freetuple(tuple);
+
+ /*
+ * Attempt to coerce target directory to safe permissions. If this
+ * fails, it doesn't exist or has the wrong owner.
+ */
+ if (chmod(location, 0700) != 0)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not set permissions on directory \"%s\": %m",
+ location)));
+
+ /*
+ * Check the target directory is empty.
+ */
+ if (!directory_is_empty(location))
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("directory \"%s\" is not empty",
+ location)));
+
+ /*
+ * Create the PG_VERSION file in the target directory. This has several
+ * purposes: to make sure we can write in the directory, to prevent
+ * someone from creating another tablespace pointing at the same
+ * directory (the emptiness check above will fail), and to label
+ * tablespace directories by PG version.
+ */
+ set_short_version(location);
+
+ /*
+ * All seems well, create the symlink
+ */
+ linkloc = (char *) palloc(strlen(DataDir) + 16 + 10 + 1);
+ sprintf(linkloc, "%s/pg_tablespaces/%u", DataDir, tablespaceoid);
+
+ if (symlink(location, linkloc) < 0)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not create symbolic link \"%s\": %m",
+ linkloc)));
+
+ pfree(linkloc);
+ pfree(location);
+
+ heap_close(rel, RowExclusiveLock);
+
+#else /* !HAVE_SYMLINK */
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("tablespaces are not supported on this platform")));
+#endif /* HAVE_SYMLINK */
+}
+
+/*
+ * Drop a table space
+ *
+ * Be careful to check that the tablespace is empty.
+ */
+void
+DropTableSpace(DropTableSpaceStmt *stmt)
+{
+#ifdef HAVE_SYMLINK
+ char *tablespacename = stmt->tablespacename;
+ HeapScanDesc scandesc;
+ Relation rel;
+ HeapTuple tuple;
+ ScanKeyData entry[1];
+ char *location;
+ Oid tablespaceoid;
+ DIR *dirdesc;
+ struct dirent *de;
+ char *subfile;
+
+ /* don't call this in a transaction block */
+ PreventTransactionChain((void *) stmt, "DROP TABLESPACE");
+
+ /*
+ * Acquire ExclusiveLock on pg_tablespace to ensure that no one else
+ * is trying to do DROP TABLESPACE or TablespaceCreateDbspace concurrently.
+ */
+ rel = heap_openr(TableSpaceRelationName, ExclusiveLock);
+
+ /*
+ * Find the target tuple
+ */
+ ScanKeyInit(&entry[0],
+ Anum_pg_tablespace_spcname,
+ BTEqualStrategyNumber, F_NAMEEQ,
+ CStringGetDatum(tablespacename));
+ scandesc = heap_beginscan(rel, SnapshotNow, 1, entry);
+ tuple = heap_getnext(scandesc, ForwardScanDirection);
+
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("tablespace \"%s\" does not exist",
+ tablespacename)));
+
+ tablespaceoid = HeapTupleGetOid(tuple);
+
+ /* Must be superuser or owner */
+ if (GetUserId() != ((Form_pg_tablespace) GETSTRUCT(tuple))->spcowner &&
+ !superuser())
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TABLESPACE,
+ tablespacename);
+
+ /* Disallow drop of the standard tablespaces, even by superuser */
+ if (tablespaceoid == GLOBALTABLESPACE_OID ||
+ tablespaceoid == DEFAULTTABLESPACE_OID)
+ aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_TABLESPACE,
+ tablespacename);
+
+ location = (char *) palloc(strlen(DataDir) + 16 + 10 + 1);
+ sprintf(location, "%s/pg_tablespaces/%u", DataDir, tablespaceoid);
+
+ /*
+ * Check if the tablespace still contains any files. We try to rmdir
+ * each per-database directory we find in it. rmdir failure implies
+ * there are still files in that subdirectory, so give up. (We do not
+ * have to worry about undoing any already completed rmdirs, since
+ * the next attempt to use the tablespace from that database will simply
+ * recreate the subdirectory via TablespaceCreateDbspace.)
+ *
+ * Since we hold exclusive lock, no one else should be creating any
+ * fresh subdirectories in parallel. It is possible that new files
+ * are being created within subdirectories, though, so the rmdir
+ * call could fail. Worst consequence is a less friendly error message.
+ */
+ dirdesc = AllocateDir(location);
+ if (dirdesc == NULL)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not open directory \"%s\": %m",
+ location)));
+
+ errno = 0;
+ while ((de = readdir(dirdesc)) != NULL)
+ {
+ /* Note we ignore PG_VERSION for the nonce */
+ if (strcmp(de->d_name, ".") == 0 ||
+ strcmp(de->d_name, "..") == 0 ||
+ strcmp(de->d_name, "PG_VERSION") == 0)
+ {
+ errno = 0;
+ continue;
+ }
+
+ subfile = palloc(strlen(location) + 1 + strlen(de->d_name) + 1);
+ sprintf(subfile, "%s/%s", location, de->d_name);
+
+ /* This check is just to deliver a friendlier error message */
+ if (!directory_is_empty(subfile))
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("tablespace \"%s\" is not empty",
+ tablespacename)));
+
+ /* Do the real deed */
+ if (rmdir(subfile) < 0)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not delete directory \"%s\": %m",
+ subfile)));
+
+ pfree(subfile);
+ }
+#ifdef WIN32
+ /* This fix is in mingw cvs (runtime/mingwex/dirent.c rev 1.4), but
+ not in released version */
+ if (GetLastError() == ERROR_NO_MORE_FILES)
+ errno = 0;
+#endif
+ if (errno)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not read directory \"%s\": %m",
+ location)));
+ FreeDir(dirdesc);
+
+ /*
+ * Okay, try to unlink PG_VERSION and then remove the symlink.
+ */
+ subfile = palloc(strlen(location) + 11 + 1);
+ sprintf(subfile, "%s/PG_VERSION", location);
+
+ if (unlink(subfile) < 0)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not unlink file \"%s\": %m",
+ subfile)));
+
+ if (unlink(location) < 0)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not unlink symbolic link \"%s\": %m",
+ location)));
+
+ pfree(subfile);
+ pfree(location);
+
+ /*
+ * We have successfully destroyed the infrastructure ... there is
+ * now no way to roll back the DROP ... so proceed to remove the
+ * pg_tablespace tuple.
+ */
+ simple_heap_delete(rel, &tuple->t_self);
+
+ heap_endscan(scandesc);
+
+ heap_close(rel, ExclusiveLock);
+
+#else /* !HAVE_SYMLINK */
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("tablespaces are not supported on this platform")));
+#endif /* HAVE_SYMLINK */
+}
+
+
+/*
+ * write out the PG_VERSION file in the specified directory
+ */
+static void
+set_short_version(const char *path)
+{
+ char *short_version;
+ bool gotdot = false;
+ int end;
+ char *fullname;
+ FILE *version_file;
+
+ /* Construct short version string (should match initdb.c) */
+ short_version = pstrdup(PG_VERSION);
+
+ for (end = 0; short_version[end] != '\0'; end++)
+ {
+ if (short_version[end] == '.')
+ {
+ Assert(end != 0);
+ if (gotdot)
+ break;
+ else
+ gotdot = true;
+ }
+ else if (short_version[end] < '0' || short_version[end] > '9')
+ {
+ /* gone past digits and dots */
+ break;
+ }
+ }
+ Assert(end > 0 && short_version[end - 1] != '.' && gotdot);
+ short_version[end] = '\0';
+
+ /* Now write the file */
+ fullname = palloc(strlen(path) + 11 + 1);
+ sprintf(fullname, "%s/PG_VERSION", path);
+ version_file = AllocateFile(fullname, PG_BINARY_W);
+ if (version_file == NULL)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not write to file \"%s\": %m",
+ fullname)));
+ fprintf(version_file, "%s\n", short_version);
+ if (FreeFile(version_file))
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not write to file \"%s\": %m",
+ fullname)));
+
+ pfree(fullname);
+ pfree(short_version);
+}
+
+/*
+ * Check if a directory is empty.
+ */
+static bool
+directory_is_empty(const char *path)
+{
+ DIR *dirdesc;
+ struct dirent *de;
+
+ dirdesc = AllocateDir(path);
+ if (dirdesc == NULL)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not open directory \"%s\": %m",
+ path)));
+
+ errno = 0;
+ while ((de = readdir(dirdesc)) != NULL)
+ {
+ if (strcmp(de->d_name, ".") == 0 ||
+ strcmp(de->d_name, "..") == 0)
+ {
+ errno = 0;
+ continue;
+ }
+ FreeDir(dirdesc);
+ return false;
+ }
+#ifdef WIN32
+ /* This fix is in mingw cvs (runtime/mingwex/dirent.c rev 1.4), but
+ not in released version */
+ if (GetLastError() == ERROR_NO_MORE_FILES)
+ errno = 0;
+#endif
+ if (errno)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not read directory \"%s\": %m",
+ path)));
+ FreeDir(dirdesc);
+ return true;
+}
+
+/*
+ * get_tablespace_oid - given a tablespace name, look up the OID
+ *
+ * Returns InvalidOid if tablespace name not found.
+ */
+Oid
+get_tablespace_oid(const char *tablespacename)
+{
+ Oid result;
+ Relation rel;
+ HeapScanDesc scandesc;
+ HeapTuple tuple;
+ ScanKeyData entry[1];
+
+ /* Search pg_tablespace */
+ rel = heap_openr(TableSpaceRelationName, AccessShareLock);
+
+ ScanKeyInit(&entry[0],
+ Anum_pg_tablespace_spcname,
+ BTEqualStrategyNumber, F_NAMEEQ,
+ CStringGetDatum(tablespacename));
+ scandesc = heap_beginscan(rel, SnapshotNow, 1, entry);
+ tuple = heap_getnext(scandesc, ForwardScanDirection);
+
+ if (HeapTupleIsValid(tuple))
+ result = HeapTupleGetOid(tuple);
+ else
+ result = InvalidOid;
+
+ heap_endscan(scandesc);
+ heap_close(rel, AccessShareLock);
+
+ return result;
+}
+
+/*
+ * get_tablespace_name - given a tablespace OID, look up the name
+ *
+ * Returns a palloc'd string, or NULL if no such tablespace.
+ */
+char *
+get_tablespace_name(Oid spc_oid)
+{
+ char *result;
+ Relation rel;
+ HeapScanDesc scandesc;
+ HeapTuple tuple;
+ ScanKeyData entry[1];
+
+ /* Search pg_tablespace */
+ rel = heap_openr(TableSpaceRelationName, AccessShareLock);
+
+ ScanKeyInit(&entry[0],
+ ObjectIdAttributeNumber,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(spc_oid));
+ scandesc = heap_beginscan(rel, SnapshotNow, 1, entry);
+ tuple = heap_getnext(scandesc, ForwardScanDirection);
+
+ /* We assume that there can be at most one matching tuple */
+ if (HeapTupleIsValid(tuple))
+ result = pstrdup(NameStr(((Form_pg_tablespace) GETSTRUCT(tuple))->spcname));
+ else
+ result = NULL;
+
+ heap_endscan(scandesc);
+ heap_close(rel, AccessShareLock);
+
+ return result;
+}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.59 2004/06/10 17:55:56 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.60 2004/06/18 06:13:23 tgl Exp $
*
* DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the
errmsg("composite type must have at least one attribute")));
/*
- * now create the parameters for keys/inheritance etc. All of them are
- * nil...
+ * now set the parameters for keys/inheritance etc. All of these
+ * are uninteresting for composite types...
*/
createStmt->relation = (RangeVar *) typevar;
createStmt->tableElts = coldeflist;
createStmt->constraints = NIL;
createStmt->hasoids = MUST_NOT_HAVE_OIDS;
createStmt->oncommit = ONCOMMIT_NOOP;
+ createStmt->tablespacename = NULL;
/*
* finally create the relation...
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/view.c,v 1.82 2004/05/26 04:41:13 neilc Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/view.c,v 1.83 2004/06/18 06:13:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
else
{
/*
- * now create the parameters for keys/inheritance etc. All of them
- * are nil...
+ * now set the parameters for keys/inheritance etc. All of these
+ * are uninteresting for views...
*/
createStmt->relation = (RangeVar *) relation;
createStmt->tableElts = attrList;
createStmt->constraints = NIL;
createStmt->hasoids = MUST_NOT_HAVE_OIDS;
createStmt->oncommit = ONCOMMIT_NOOP;
+ createStmt->tablespacename = NULL;
/*
* finally create the relation (this will error out if there's an
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.233 2004/05/30 23:40:26 neilc Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.234 2004/06/18 06:13:26 tgl Exp $
*
*-------------------------------------------------------------------------
*/
intoRelationId = heap_create_with_catalog(intoName,
namespaceId,
+ InvalidOid,
tupdesc,
RELKIND_RELATION,
false,
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.285 2004/06/09 19:08:15 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.286 2004/06/18 06:13:28 tgl Exp $
*
*-------------------------------------------------------------------------
*/
COPY_NODE_FIELD(constraints);
COPY_SCALAR_FIELD(hasoids);
COPY_SCALAR_FIELD(oncommit);
+ COPY_STRING_FIELD(tablespacename);
return newnode;
}
COPY_STRING_FIELD(idxname);
COPY_NODE_FIELD(relation);
COPY_STRING_FIELD(accessMethod);
+ COPY_STRING_FIELD(tableSpace);
COPY_NODE_FIELD(indexParams);
COPY_NODE_FIELD(whereClause);
COPY_NODE_FIELD(rangetable);
COPY_NODE_FIELD(sequence);
COPY_NODE_FIELD(options);
+ COPY_STRING_FIELD(tablespacename);
return newnode;
}
return newnode;
}
+static CreateTableSpaceStmt *
+_copyCreateTableSpaceStmt(CreateTableSpaceStmt *from)
+{
+ CreateTableSpaceStmt *newnode = makeNode(CreateTableSpaceStmt);
+
+ COPY_STRING_FIELD(tablespacename);
+ COPY_STRING_FIELD(owner);
+ COPY_STRING_FIELD(location);
+
+ return newnode;
+}
+
+static DropTableSpaceStmt *
+_copyDropTableSpaceStmt(DropTableSpaceStmt *from)
+{
+ DropTableSpaceStmt *newnode = makeNode(DropTableSpaceStmt);
+
+ COPY_STRING_FIELD(tablespacename);
+
+ return newnode;
+}
+
static CreateTrigStmt *
_copyCreateTrigStmt(CreateTrigStmt *from)
{
COPY_STRING_FIELD(schemaname);
COPY_STRING_FIELD(authid);
+ COPY_STRING_FIELD(tablespacename);
COPY_NODE_FIELD(schemaElts);
return newnode;
case T_VariableResetStmt:
retval = _copyVariableResetStmt(from);
break;
+ case T_CreateTableSpaceStmt:
+ retval = _copyCreateTableSpaceStmt(from);
+ break;
+ case T_DropTableSpaceStmt:
+ retval = _copyDropTableSpaceStmt(from);
+ break;
case T_CreateTrigStmt:
retval = _copyCreateTrigStmt(from);
break;
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.224 2004/06/09 19:08:15 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.225 2004/06/18 06:13:28 tgl Exp $
*
*-------------------------------------------------------------------------
*/
COMPARE_NODE_FIELD(constraints);
COMPARE_SCALAR_FIELD(hasoids);
COMPARE_SCALAR_FIELD(oncommit);
+ COMPARE_STRING_FIELD(tablespacename);
return true;
}
COMPARE_STRING_FIELD(idxname);
COMPARE_NODE_FIELD(relation);
COMPARE_STRING_FIELD(accessMethod);
+ COMPARE_STRING_FIELD(tableSpace);
COMPARE_NODE_FIELD(indexParams);
COMPARE_NODE_FIELD(whereClause);
COMPARE_NODE_FIELD(rangetable);
{
COMPARE_NODE_FIELD(sequence);
COMPARE_NODE_FIELD(options);
+ COMPARE_STRING_FIELD(tablespacename);
return true;
}
return true;
}
+static bool
+_equalCreateTableSpaceStmt(CreateTableSpaceStmt *a, CreateTableSpaceStmt *b)
+{
+ COMPARE_STRING_FIELD(tablespacename);
+ COMPARE_STRING_FIELD(owner);
+ COMPARE_STRING_FIELD(location);
+
+ return true;
+}
+
+static bool
+_equalDropTableSpaceStmt(DropTableSpaceStmt *a, DropTableSpaceStmt *b)
+{
+ COMPARE_STRING_FIELD(tablespacename);
+
+ return true;
+}
+
static bool
_equalCreateTrigStmt(CreateTrigStmt *a, CreateTrigStmt *b)
{
{
COMPARE_STRING_FIELD(schemaname);
COMPARE_STRING_FIELD(authid);
+ COMPARE_STRING_FIELD(tablespacename);
COMPARE_NODE_FIELD(schemaElts);
return true;
case T_VariableResetStmt:
retval = _equalVariableResetStmt(a, b);
break;
+ case T_CreateTableSpaceStmt:
+ retval = _equalCreateTableSpaceStmt(a, b);
+ break;
+ case T_DropTableSpaceStmt:
+ retval = _equalDropTableSpaceStmt(a, b);
+ break;
case T_CreateTrigStmt:
retval = _equalCreateTrigStmt(a, b);
break;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.239 2004/06/09 19:08:15 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.240 2004/06/18 06:13:28 tgl Exp $
*
* NOTES
* Every node type that can appear in stored rules' parsetrees *must*
static void
_outCreateStmt(StringInfo str, CreateStmt *node)
{
- WRITE_NODE_TYPE("CREATE");
+ WRITE_NODE_TYPE("CREATESTMT");
WRITE_NODE_FIELD(relation);
WRITE_NODE_FIELD(tableElts);
WRITE_NODE_FIELD(constraints);
WRITE_ENUM_FIELD(hasoids, ContainsOids);
WRITE_ENUM_FIELD(oncommit, OnCommitAction);
+ WRITE_STRING_FIELD(tablespacename);
}
static void
_outIndexStmt(StringInfo str, IndexStmt *node)
{
- WRITE_NODE_TYPE("INDEX");
+ WRITE_NODE_TYPE("INDEXSTMT");
WRITE_STRING_FIELD(idxname);
WRITE_NODE_FIELD(relation);
WRITE_STRING_FIELD(accessMethod);
+ WRITE_STRING_FIELD(tableSpace);
WRITE_NODE_FIELD(indexParams);
WRITE_NODE_FIELD(whereClause);
WRITE_NODE_FIELD(rangetable);
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.305 2004/06/10 17:55:58 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.306 2004/06/18 06:13:31 tgl Exp $
*
*-------------------------------------------------------------------------
*/
seqstmt = makeNode(CreateSeqStmt);
seqstmt->sequence = makeRangeVar(snamespace, sname);
seqstmt->options = NIL;
+ seqstmt->tablespacename = NULL;
cxt->blist = lappend(cxt->blist, seqstmt);
index->relation = cxt->relation;
index->accessMethod = DEFAULT_INDEX_TYPE;
+ index->tableSpace = NULL;
index->indexParams = NIL;
index->whereClause = NULL;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.461 2004/06/09 19:08:17 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.462 2004/06/18 06:13:31 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt
ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
CreateDomainStmt CreateGroupStmt CreateOpClassStmt CreatePLangStmt
- CreateSchemaStmt CreateSeqStmt CreateStmt
+ CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
CreateAssertStmt CreateTrigStmt CreateUserStmt
CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt
DropGroupStmt DropOpClassStmt DropPLangStmt DropStmt
DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt
- DropUserStmt DropdbStmt ExplainStmt FetchStmt
+ DropUserStmt DropdbStmt DropTableSpaceStmt ExplainStmt FetchStmt
GrantStmt IndexStmt InsertStmt ListenStmt LoadStmt
LockStmt NotifyStmt ExplainableStmt PreparableStmt
CreateFunctionStmt ReindexStmt RemoveAggrStmt
%type constraints_set_list
%type constraints_set_mode
+%type OptTableSpace OptTableSpaceOwner
/*
ORDER OUT_P OUTER_P OVERLAPS OVERLAY OWNER
PARTIAL PASSWORD PATH_P PENDANT PLACING POSITION
- PRECISION PRESERVE PREPARE PRIMARY
+ PRECISION PRESERVE PREPARE PRIMARY
PRIOR PRIVILEGES PROCEDURAL PROCEDURE
QUOTE
SHOW SIMILAR SIMPLE SMALLINT SOME STABLE START STATEMENT
STATISTICS STDIN STDOUT STORAGE STRICT_P SUBSTRING SYSID
- TABLE TEMP TEMPLATE TEMPORARY THEN TIME TIMESTAMP
+ TABLE TABLESPACE TEMP TEMPLATE TEMPORARY THEN TIME TIMESTAMP
TO TOAST TRAILING TRANSACTION TREAT TRIGGER TRIM TRUE_P
TRUNCATE TRUSTED TYPE_P
| CreateSchemaStmt
| CreateSeqStmt
| CreateStmt
+ | CreateTableSpaceStmt
| CreateTrigStmt
| CreateUserStmt
| CreatedbStmt
| DropPLangStmt
| DropRuleStmt
| DropStmt
+ | DropTableSpaceStmt
| DropTrigStmt
| DropUserStmt
| DropdbStmt
*****************************************************************************/
CreateSchemaStmt:
- CREATE SCHEMA OptSchemaName AUTHORIZATION UserId OptSchemaEltList
+ CREATE SCHEMA OptSchemaName AUTHORIZATION UserId OptTableSpace OptSchemaEltList
{
CreateSchemaStmt *n = makeNode(CreateSchemaStmt);
/* One can omit the schema name or the authorization id. */
else
n->schemaname = $5;
n->authid = $5;
- n->schemaElts = $6;
+ n->tablespacename = $6;
+ n->schemaElts = $7;
$$ = (Node *)n;
}
- | CREATE SCHEMA ColId OptSchemaEltList
+ | CREATE SCHEMA ColId OptTableSpace OptSchemaEltList
{
CreateSchemaStmt *n = makeNode(CreateSchemaStmt);
/* ...but not both */
n->schemaname = $3;
n->authid = NULL;
- n->schemaElts = $4;
+ n->tablespacename = $4;
+ n->schemaElts = $5;
$$ = (Node *)n;
}
;
n->name = $3;
$$ = (Node *)n;
}
- /* ALTER TABLE SET WITHOUT CLUSTER */
+ /* ALTER TABLE SET WITHOUT CLUSTER */
| SET WITHOUT CLUSTER
{
AlterTableCmd *n = makeNode(AlterTableCmd);
*****************************************************************************/
CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
- OptInherit OptWithOids OnCommitOption
+ OptInherit OptWithOids OnCommitOption OptTableSpace
{
CreateStmt *n = makeNode(CreateStmt);
$4->istemp = $2;
n->constraints = NIL;
n->hasoids = $9;
n->oncommit = $10;
+ n->tablespacename = $11;
$$ = (Node *)n;
}
| CREATE OptTemp TABLE qualified_name OF qualified_name
- '(' OptTableElementList ')' OptWithOids OnCommitOption
+ '(' OptTableElementList ')' OptWithOids OnCommitOption OptTableSpace
{
/* SQL99 CREATE TABLE OF (cols) seems to be satisfied
* by our inheritance capabilities. Let's try it...
n->constraints = NIL;
n->hasoids = $10;
n->oncommit = $11;
+ n->tablespacename = $12;
$$ = (Node *)n;
}
;
| /*EMPTY*/ { $$ = ONCOMMIT_NOOP; }
;
+OptTableSpace: TABLESPACE name { $$ = $2; }
+ | /*EMPTY*/ { $$ = NULL; }
+ ;
+
/*
* Note: CREATE TABLE ... AS SELECT ... is just another spelling for
*****************************************************************************/
CreateSeqStmt:
- CREATE OptTemp SEQUENCE qualified_name OptSeqList
+ CREATE OptTemp SEQUENCE qualified_name OptSeqList OptTableSpace
{
CreateSeqStmt *n = makeNode(CreateSeqStmt);
$4->istemp = $2;
n->sequence = $4;
n->options = $5;
+ n->tablespacename = $6;
$$ = (Node *)n;
}
;
| /*EMPTY*/ {}
;
+/*****************************************************************************
+ *
+ * QUERY:
+ * CREATE TABLESPACE tablespace LOCATION '/path/to/tablespace/'
+ *
+ *****************************************************************************/
+
+CreateTableSpaceStmt: CREATE TABLESPACE name OptTableSpaceOwner LOCATION Sconst
+ {
+ CreateTableSpaceStmt *n = makeNode(CreateTableSpaceStmt);
+ n->tablespacename = $3;
+ n->owner = $4;
+ n->location = $6;
+ $$ = (Node *) n;
+ }
+ ;
+
+OptTableSpaceOwner: OWNER name { $$ = $2; }
+ | /*EMPTY */ { $$ = NULL; }
+ ;
+
+/*****************************************************************************
+ *
+ * QUERY :
+ *
+ * No need for drop behaviour as we cannot implement dependencies for
+ * objects in other databases; we can only support RESTRICT.
+ *
+ ****************************************************************************/
+
+DropTableSpaceStmt: DROP TABLESPACE name
+ {
+ DropTableSpaceStmt *n = makeNode(DropTableSpaceStmt);
+ n->tablespacename = $3;
+ $$ = (Node *) n;
+ }
+ ;
+
/*****************************************************************************
*
* QUERIES :
n->objargs = NIL;
n->comment = $7;
$$ = (Node *) n;
- }
+ }
;
comment_type:
n->objs = $2;
$$ = n;
}
+ | TABLESPACE name_list
+ {
+ PrivTarget *n = makeNode(PrivTarget);
+ n->objtype = ACL_OBJECT_TABLESPACE;
+ n->objs = $2;
+ $$ = n;
+ }
;
* QUERY:
* create index on
* [ using
] "(" ( [ using ] )+ ")"
+ * [
tablespace ] [ where
]
*
+ * Note: we cannot put TABLESPACE clause after WHERE clause unless we are
+ * willing to make TABLESPACE a fully reserved word.
*****************************************************************************/
IndexStmt: CREATE index_opt_unique INDEX index_name ON qualified_name
- access_method_clause '(' index_params ')' where_clause
+ access_method_clause '(' index_params ')' OptTableSpace where_clause
{
IndexStmt *n = makeNode(IndexStmt);
n->unique = $2;
n->relation = $6;
n->accessMethod = $7;
n->indexParams = $9;
- n->whereClause = $11;
+ n->tableSpace = $11;
+ n->whereClause = $12;
$$ = (Node *)n;
}
;
;
createdb_opt_item:
- LOCATION opt_equal Sconst
+ TABLESPACE opt_equal name
+ {
+ $$ = makeDefElem("tablespace", (Node *)makeString($3));
+ }
+ | TABLESPACE opt_equal DEFAULT
+ {
+ $$ = makeDefElem("tablespace", NULL);
+ }
+ | LOCATION opt_equal Sconst
{
$$ = makeDefElem("location", (Node *)makeString($3));
}
{ $$ = list_make1(makeString("!~~*")); }
/* cannot put SIMILAR TO here, because SIMILAR TO is a hack.
* the regular expression is preprocessed by a function (similar_escape),
- * and the ~ operator for posix regular expressions is used.
+ * and the ~ operator for posix regular expressions is used.
* x SIMILAR TO y -> x ~ similar_escape(y)
* this transformation is made on the fly by the parser upwards.
* however the SubLink structure which handles any/some/all stuff
* COALESCE(a,b,...)
* same as CASE WHEN a IS NOT NULL THEN a WHEN b IS NOT NULL THEN b ... END
* - thomas 1998-11-09
- *
+ *
* NULLIF and COALESCE have become first class nodes to
* prevent double evaluation of arguments.
* - Kris Jurka 2003-02-11
| STORAGE
| SYSID
| STRICT_P
+ | TABLESPACE
| TEMP
| TEMPLATE
| TEMPORARY
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.149 2004/04/21 00:34:18 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.150 2004/06/18 06:13:31 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{"substring", SUBSTRING},
{"sysid", SYSID},
{"table", TABLE},
+ {"tablespace", TABLESPACE},
{"temp", TEMP},
{"template", TEMPLATE},
{"temporary", TEMPORARY},
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/storage/buffer/bufmgr.c,v 1.170 2004/06/11 16:43:23 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/storage/buffer/bufmgr.c,v 1.171 2004/06/18 06:13:33 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*/
dirty_buffers = (BufferDesc **) palloc(NBuffers * sizeof(BufferDesc *));
buftags = (BufferTag *) palloc(NBuffers * sizeof(BufferTag));
-
+
LWLockAcquire(BufMgrLock, LW_EXCLUSIVE);
num_buffer_dirty = StrategyDirtyBufferList(dirty_buffers, buftags,
NBuffers);
if (isCommit)
elog(WARNING,
"buffer refcount leak: [%03d] "
- "(rel=%u/%u, blockNum=%u, flags=0x%x, refcount=%u %d)",
+ "(rel=%u/%u/%u, blockNum=%u, flags=0x%x, refcount=%u %d)",
i,
- buf->tag.rnode.tblNode, buf->tag.rnode.relNode,
+ buf->tag.rnode.spcNode, buf->tag.rnode.dbNode,
+ buf->tag.rnode.relNode,
buf->tag.blockNum, buf->flags,
buf->refcount, PrivateRefCount[i]);
{
/* the sole pin should be ours */
if (bufHdr->refcount != 1 || PrivateRefCount[i - 1] == 0)
- elog(FATAL, "block %u of %u/%u is still referenced (private %d, global %u)",
+ elog(FATAL, "block %u of %u/%u/%u is still referenced (private %d, global %u)",
bufHdr->tag.blockNum,
- bufHdr->tag.rnode.tblNode,
+ bufHdr->tag.rnode.spcNode,
+ bufHdr->tag.rnode.dbNode,
bufHdr->tag.rnode.relNode,
PrivateRefCount[i - 1], bufHdr->refcount);
/* Make sure it will be released */
{
bufHdr = &BufferDescriptors[i - 1];
recheck:
-
- /*
- * We know that currently database OID is tblNode but this
- * probably will be changed in future and this func will be used
- * to drop tablespace buffers.
- */
- if (bufHdr->tag.rnode.tblNode == dbid)
+ if (bufHdr->tag.rnode.dbNode == dbid)
{
/*
* If there is I/O in progress, better wait till it's done;
for (i = 0; i < NBuffers; ++i, ++buf)
{
elog(LOG,
- "[%02d] (freeNext=%d, freePrev=%d, rel=%u/%u, "
+ "[%02d] (freeNext=%d, freePrev=%d, rel=%u/%u/%u, "
"blockNum=%u, flags=0x%x, refcount=%u %d)",
i, buf->freeNext, buf->freePrev,
- buf->tag.rnode.tblNode, buf->tag.rnode.relNode,
+ buf->tag.rnode.spcNode, buf->tag.rnode.dbNode,
+ buf->tag.rnode.relNode,
buf->tag.blockNum, buf->flags,
buf->refcount, PrivateRefCount[i]);
}
/* interactive backend */
for (i = 0; i < NBuffers; ++i, ++buf)
{
- printf("[%-2d] (%u/%u, %u) flags=0x%x, refcount=%u %d)\n",
- i, buf->tag.rnode.tblNode, buf->tag.rnode.relNode,
- buf->tag.blockNum,
+ printf("[%-2d] (%u/%u/%u, %u) flags=0x%x, refcount=%u %d)\n",
+ i, buf->tag.rnode.spcNode, buf->tag.rnode.dbNode,
+ buf->tag.rnode.relNode, buf->tag.blockNum,
buf->flags, buf->refcount, PrivateRefCount[i]);
}
}
{
if (PrivateRefCount[i] > 0)
elog(WARNING,
- "[%02d] (freeNext=%d, freePrev=%d, rel=%u/%u, "
+ "[%02d] (freeNext=%d, freePrev=%d, rel=%u/%u/%u, "
"blockNum=%u, flags=0x%x, refcount=%u %d)",
i, buf->freeNext, buf->freePrev,
- buf->tag.rnode.tblNode, buf->tag.rnode.relNode,
+ buf->tag.rnode.spcNode, buf->tag.rnode.dbNode,
+ buf->tag.rnode.relNode,
buf->tag.blockNum, buf->flags,
buf->refcount, PrivateRefCount[i]);
}
BufferDesc *buf = &BufferDescriptors[buffer - 1];
fprintf(stderr,
- "PIN(Incr) %d rel = %u/%u, blockNum = %u, "
+ "PIN(Incr) %d rel = %u/%u/%u, blockNum = %u, "
"refcount = %d, file: %s, line: %d\n",
buffer,
- buf->tag.rnode.tblNode, buf->tag.rnode.relNode,
- buf->tag.blockNum,
+ buf->tag.rnode.spcNode, buf->tag.rnode.dbNode,
+ buf->tag.rnode.relNode, buf->tag.blockNum,
PrivateRefCount[buffer - 1], file, line);
}
}
BufferDesc *buf = &BufferDescriptors[buffer - 1];
fprintf(stderr,
- "UNPIN(Rel) %d rel = %u/%u, blockNum = %u, "
+ "UNPIN(Rel) %d rel = %u/%u/%u, blockNum = %u, "
"refcount = %d, file: %s, line: %d\n",
buffer,
- buf->tag.rnode.tblNode, buf->tag.rnode.relNode,
- buf->tag.blockNum,
+ buf->tag.rnode.spcNode, buf->tag.rnode.dbNode,
+ buf->tag.rnode.relNode, buf->tag.blockNum,
PrivateRefCount[buffer - 1], file, line);
}
}
BufferDesc *buf = &BufferDescriptors[buffer - 1];
fprintf(stderr,
- "UNPIN(Rel&Rd) %d rel = %u/%u, blockNum = %u, "
+ "UNPIN(Rel&Rd) %d rel = %u/%u/%u, blockNum = %u, "
"refcount = %d, file: %s, line: %d\n",
buffer,
- buf->tag.rnode.tblNode, buf->tag.rnode.relNode,
- buf->tag.blockNum,
+ buf->tag.rnode.spcNode, buf->tag.rnode.dbNode,
+ buf->tag.rnode.relNode, buf->tag.blockNum,
PrivateRefCount[buffer - 1], file, line);
}
if (ShowPinTrace && BufferIsLocal(buffer) && is_userbuffer(buffer))
BufferDesc *buf = &BufferDescriptors[b - 1];
fprintf(stderr,
- "PIN(Rel&Rd) %d rel = %u/%u, blockNum = %u, "
+ "PIN(Rel&Rd) %d rel = %u/%u/%u, blockNum = %u, "
"refcount = %d, file: %s, line: %d\n",
b,
- buf->tag.rnode.tblNode, buf->tag.rnode.relNode,
- buf->tag.blockNum,
+ buf->tag.rnode.spcNode, buf->tag.rnode.dbNode,
+ buf->tag.rnode.relNode, buf->tag.blockNum,
PrivateRefCount[b - 1], file, line);
}
return b;
{
ereport(WARNING,
(errcode(ERRCODE_IO_ERROR),
- errmsg("could not write block %u of %u/%u",
+ errmsg("could not write block %u of %u/%u/%u",
buf->tag.blockNum,
- buf->tag.rnode.tblNode,
+ buf->tag.rnode.spcNode,
+ buf->tag.rnode.dbNode,
buf->tag.rnode.relNode),
errdetail("Multiple failures --- write error may be permanent.")));
}
BufferDesc *bufHdr = (BufferDesc *) arg;
if (bufHdr != NULL)
- errcontext("writing block %u of relation %u/%u",
+ errcontext("writing block %u of relation %u/%u/%u",
bufHdr->tag.blockNum,
- bufHdr->tag.rnode.tblNode, bufHdr->tag.rnode.relNode);
+ bufHdr->tag.rnode.spcNode,
+ bufHdr->tag.rnode.dbNode,
+ bufHdr->tag.rnode.relNode);
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/storage/buffer/localbuf.c,v 1.55 2004/05/31 20:31:33 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/storage/buffer/localbuf.c,v 1.56 2004/06/18 06:13:33 tgl Exp $
*
*-------------------------------------------------------------------------
*/
if (isCommit)
elog(WARNING,
- "local buffer leak: [%03d] (rel=%u/%u, blockNum=%u, flags=0x%x, refcount=%u %d)",
+ "local buffer leak: [%03d] (rel=%u/%u/%u, blockNum=%u, flags=0x%x, refcount=%u %d)",
i,
- buf->tag.rnode.tblNode, buf->tag.rnode.relNode,
- buf->tag.blockNum, buf->flags,
+ buf->tag.rnode.spcNode, buf->tag.rnode.dbNode,
+ buf->tag.rnode.relNode, buf->tag.blockNum, buf->flags,
buf->refcount, LocalRefCount[i]);
LocalRefCount[i] = 0;
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/storage/freespace/freespace.c,v 1.31 2004/06/05 19:48:08 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/storage/freespace/freespace.c,v 1.32 2004/06/18 06:13:34 tgl Exp $
*
*
* NOTES:
*
* This is called during DROP DATABASE. As above, might as well reclaim
* map space sooner instead of later.
- *
- * XXX when we implement tablespaces, target Oid will need to be tablespace
- * ID not database ID.
*/
void
FreeSpaceMapForgetDatabase(Oid dbid)
for (fsmrel = FreeSpaceMap->usageList; fsmrel; fsmrel = nextrel)
{
nextrel = fsmrel->nextUsage; /* in case we delete it */
- if (fsmrel->key.tblNode == dbid)
+ if (fsmrel->key.dbNode == dbid)
delete_fsm_rel(fsmrel);
}
LWLockRelease(FreeSpaceLock);
for (fsmrel = FreeSpaceMap->usageList; fsmrel; fsmrel = fsmrel->nextUsage)
{
relNum++;
- fprintf(stderr, "Map %d: rel %u/%u isIndex %d avgRequest %u lastPageCount %d nextPage %d\nMap= ",
- relNum, fsmrel->key.tblNode, fsmrel->key.relNode,
+ fprintf(stderr, "Map %d: rel %u/%u/%u isIndex %d avgRequest %u lastPageCount %d nextPage %d\nMap= ",
+ relNum,
+ fsmrel->key.spcNode, fsmrel->key.dbNode, fsmrel->key.relNode,
(int) fsmrel->isIndex, fsmrel->avgRequest,
fsmrel->lastPageCount, fsmrel->nextPage);
if (fsmrel->isIndex)
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/storage/smgr/md.c,v 1.107 2004/06/02 17:28:18 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/storage/smgr/md.c,v 1.108 2004/06/18 06:13:37 tgl Exp $
*
*-------------------------------------------------------------------------
*/
return NULL;
ereport(ERROR,
(errcode_for_file_access(),
- errmsg("could not open relation %u/%u: %m",
- reln->smgr_rnode.tblNode,
+ errmsg("could not open relation %u/%u/%u: %m",
+ reln->smgr_rnode.spcNode,
+ reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode)));
}
}
{
ereport(LOG,
(errcode_for_file_access(),
- errmsg("could not fsync segment %u of relation %u/%u: %m",
+ errmsg("could not fsync segment %u of relation %u/%u/%u: %m",
entry->segno,
- entry->rnode.tblNode,
+ entry->rnode.spcNode,
+ entry->rnode.dbNode,
entry->rnode.relNode)));
return false;
}
return NULL;
ereport(ERROR,
(errcode_for_file_access(),
- errmsg("could not open segment %u of relation %u/%u (target block %u): %m",
+ errmsg("could not open segment %u of relation %u/%u/%u (target block %u): %m",
nextsegno,
- reln->smgr_rnode.tblNode,
+ reln->smgr_rnode.spcNode,
+ reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode,
blkno)));
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/storage/smgr/smgr.c,v 1.73 2004/06/02 17:28:18 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/storage/smgr/smgr.c,v 1.74 2004/06/18 06:13:37 tgl Exp $
*
*-------------------------------------------------------------------------
*/
if (! (*(smgrsw[reln->smgr_which].smgr_close)) (reln))
ereport(ERROR,
(errcode_for_file_access(),
- errmsg("could not close relation %u/%u: %m",
- reln->smgr_rnode.tblNode,
+ errmsg("could not close relation %u/%u/%u: %m",
+ reln->smgr_rnode.spcNode,
+ reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode)));
if (hash_search(SMgrRelationHash,
if (! (*(smgrsw[reln->smgr_which].smgr_create)) (reln, isRedo))
ereport(ERROR,
(errcode_for_file_access(),
- errmsg("could not create relation %u/%u: %m",
- reln->smgr_rnode.tblNode,
+ errmsg("could not create relation %u/%u/%u: %m",
+ reln->smgr_rnode.spcNode,
+ reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode)));
if (isRedo)
if (! (*(smgrsw[which].smgr_unlink)) (rnode, isRedo))
ereport(WARNING,
(errcode_for_file_access(),
- errmsg("could not unlink relation %u/%u: %m",
- rnode.tblNode,
+ errmsg("could not unlink relation %u/%u/%u: %m",
+ rnode.spcNode,
+ rnode.dbNode,
rnode.relNode)));
}
isTemp))
ereport(ERROR,
(errcode_for_file_access(),
- errmsg("could not extend relation %u/%u: %m",
- reln->smgr_rnode.tblNode,
+ errmsg("could not extend relation %u/%u/%u: %m",
+ reln->smgr_rnode.spcNode,
+ reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode),
errhint("Check free disk space.")));
}
if (! (*(smgrsw[reln->smgr_which].smgr_read)) (reln, blocknum, buffer))
ereport(ERROR,
(errcode_for_file_access(),
- errmsg("could not read block %u of relation %u/%u: %m",
+ errmsg("could not read block %u of relation %u/%u/%u: %m",
blocknum,
- reln->smgr_rnode.tblNode,
+ reln->smgr_rnode.spcNode,
+ reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode)));
}
isTemp))
ereport(ERROR,
(errcode_for_file_access(),
- errmsg("could not write block %u of relation %u/%u: %m",
+ errmsg("could not write block %u of relation %u/%u/%u: %m",
blocknum,
- reln->smgr_rnode.tblNode,
+ reln->smgr_rnode.spcNode,
+ reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode)));
}
if (nblocks == InvalidBlockNumber)
ereport(ERROR,
(errcode_for_file_access(),
- errmsg("could not count blocks of relation %u/%u: %m",
- reln->smgr_rnode.tblNode,
+ errmsg("could not count blocks of relation %u/%u/%u: %m",
+ reln->smgr_rnode.spcNode,
+ reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode)));
return nblocks;
if (newblks == InvalidBlockNumber)
ereport(ERROR,
(errcode_for_file_access(),
- errmsg("could not truncate relation %u/%u to %u blocks: %m",
- reln->smgr_rnode.tblNode,
+ errmsg("could not truncate relation %u/%u/%u to %u blocks: %m",
+ reln->smgr_rnode.spcNode,
+ reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode,
nblocks)));
if (! (*(smgrsw[reln->smgr_which].smgr_immedsync)) (reln))
ereport(ERROR,
(errcode_for_file_access(),
- errmsg("could not sync relation %u/%u: %m",
- reln->smgr_rnode.tblNode,
+ errmsg("could not sync relation %u/%u/%u: %m",
+ reln->smgr_rnode.spcNode,
+ reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode)));
}
if (newblks == InvalidBlockNumber)
ereport(WARNING,
(errcode_for_file_access(),
- errmsg("could not truncate relation %u/%u to %u blocks: %m",
- reln->smgr_rnode.tblNode,
+ errmsg("could not truncate relation %u/%u/%u to %u blocks: %m",
+ reln->smgr_rnode.spcNode,
+ reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode,
xlrec->blkno)));
}
{
xl_smgr_create *xlrec = (xl_smgr_create *) rec;
- sprintf(buf + strlen(buf), "file create: %u/%u",
- xlrec->rnode.tblNode, xlrec->rnode.relNode);
+ sprintf(buf + strlen(buf), "file create: %u/%u/%u",
+ xlrec->rnode.spcNode, xlrec->rnode.dbNode,
+ xlrec->rnode.relNode);
}
else if (info == XLOG_SMGR_TRUNCATE)
{
xl_smgr_truncate *xlrec = (xl_smgr_truncate *) rec;
- sprintf(buf + strlen(buf), "file truncate: %u/%u to %u blocks",
- xlrec->rnode.tblNode, xlrec->rnode.relNode,
- xlrec->blkno);
+ sprintf(buf + strlen(buf), "file truncate: %u/%u/%u to %u blocks",
+ xlrec->rnode.spcNode, xlrec->rnode.dbNode,
+ xlrec->rnode.relNode, xlrec->blkno);
}
else
strcat(buf, "UNKNOWN");
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.218 2004/05/29 22:48:20 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.219 2004/06/18 06:13:38 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "commands/schemacmds.h"
#include "commands/sequence.h"
#include "commands/tablecmds.h"
+#include "commands/tablespace.h"
#include "commands/trigger.h"
#include "commands/typecmds.h"
#include "commands/user.h"
case T_CreateSchemaStmt:
case T_CreateSeqStmt:
case T_CreateStmt:
+ case T_CreateTableSpaceStmt:
case T_CreateTrigStmt:
case T_CompositeTypeStmt:
case T_CreateUserStmt:
case T_DropCastStmt:
case T_DropStmt:
case T_DropdbStmt:
+ case T_DropTableSpaceStmt:
case T_RemoveFuncStmt:
case T_DropGroupStmt:
case T_DropPLangStmt:
}
break;
+ case T_CreateTableSpaceStmt:
+ CreateTableSpace((CreateTableSpaceStmt *) parsetree);
+ break;
+
+ case T_DropTableSpaceStmt:
+ DropTableSpace((DropTableSpaceStmt *) parsetree);
+ break;
+
case T_DropStmt:
{
DropStmt *stmt = (DropStmt *) parsetree;
DefineIndex(stmt->relation, /* relation */
stmt->idxname, /* index name */
stmt->accessMethod, /* am name */
+ stmt->tableSpace,
stmt->indexParams, /* parameters */
(Expr *) stmt->whereClause,
stmt->rangetable,
tag = "CREATE TABLE";
break;
+ case T_CreateTableSpaceStmt:
+ tag = "CREATE TABLESPACE";
+ break;
+
+ case T_DropTableSpaceStmt:
+ tag = "DROP TABLESPACE";
+ break;
+
case T_DropStmt:
switch (((DropStmt *) parsetree)->removeType)
{
case OBJECT_SCHEMA:
tag = "ALTER SCHEMA";
break;
+ case OBJECT_TABLESPACE:
+ tag = "ALTER TABLESPACE";
+ break;
case OBJECT_TRIGGER:
tag = "ALTER TRIGGER";
break;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.105 2004/06/01 21:49:22 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.106 2004/06/18 06:13:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
world_default = ACL_NO_RIGHTS;
owner_default = ACL_ALL_RIGHTS_NAMESPACE;
break;
+ case ACL_OBJECT_TABLESPACE:
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_ALL_RIGHTS_TABLESPACE;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
world_default = ACL_NO_RIGHTS; /* keep compiler quiet */
* back to source text
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.172 2004/06/16 01:26:47 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.173 2004/06/18 06:13:49 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
#include "catalog/pg_operator.h"
#include "catalog/pg_shadow.h"
#include "catalog/pg_trigger.h"
+#include "commands/tablespace.h"
#include "executor/spi.h"
#include "lib/stringinfo.h"
#include "nodes/makefuncs.h"
{
appendStringInfoChar(&buf, ')');
+ /*
+ * If the index is in a different tablespace from its parent,
+ * tell about that
+ */
+ if (OidIsValid(idxrelrec->reltablespace) &&
+ idxrelrec->reltablespace != get_rel_tablespace(indrelid))
+ {
+ char *spcname = get_tablespace_name(idxrelrec->reltablespace);
+
+ if (spcname) /* just paranoia... */
+ {
+ appendStringInfo(&buf, " TABLESPACE %s",
+ quote_identifier(spcname));
+ pfree(spcname);
+ }
+ }
+
/*
* If it's a partial index, decompile and append the predicate
*/
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/cache/inval.c,v 1.61 2004/05/06 16:10:57 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/cache/inval.c,v 1.62 2004/06/18 06:13:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/* We assume dbId need not be checked because it will never change */
/* relfilenode fields must be checked to support reassignment */
ProcessMessageList(hdr->rclist,
- if (msg->rc.relId == relId &&
+ if (msg->rc.relId == relId &&
RelFileNodeEquals(msg->rc.physId, physId)) return);
/* OK, add the item */
databaseId = InvalidOid;
else
databaseId = MyDatabaseId;
- rnode.tblNode = databaseId; /* XXX change for tablespaces */
+ if (classtup->reltablespace)
+ rnode.spcNode = classtup->reltablespace;
+ else
+ rnode.spcNode = MyDatabaseTableSpace;
+ rnode.dbNode = databaseId;
rnode.relNode = classtup->relfilenode;
/*
* Note: during a pg_class row update that assigns a new relfilenode
- * value, we will be called on both the old and new tuples, and thus
- * will broadcast invalidation messages showing both the old and new
- * relfilenode values. This ensures that other backends will close
- * smgr references to the old relfilenode file.
+ * or reltablespace value, we will be called on both the old and new
+ * tuples, and thus will broadcast invalidation messages showing both
+ * the old and new RelFileNode values. This ensures that other
+ * backends will close smgr references to the old file.
*/
}
else if (tupleRelId == RelOid_pg_attribute)
*/
databaseId = MyDatabaseId;
/* We assume no smgr cache flush is needed, either */
- rnode.tblNode = InvalidOid;
+ rnode.spcNode = InvalidOid;
+ rnode.dbNode = InvalidOid;
rnode.relNode = InvalidOid;
}
else
databaseId = InvalidOid;
else
databaseId = MyDatabaseId;
- rnode.tblNode = databaseId; /* XXX change for tablespaces */
+ if (classtup->reltablespace)
+ rnode.spcNode = classtup->reltablespace;
+ else
+ rnode.spcNode = MyDatabaseTableSpace;
+ rnode.dbNode = databaseId;
rnode.relNode = classtup->relfilenode;
RegisterRelcacheInvalidation(databaseId, relationId, rnode);
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.113 2004/06/06 00:41:27 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.114 2004/06/18 06:13:52 tgl Exp $
*
* NOTES
* Eventually, the index information should go through here, too.
return InvalidOid;
}
+/*
+ * get_rel_tablespace
+ * Returns the pg_tablespace OID associated with a given relation.
+ *
+ * Note: failure return is InvalidOid, which cannot be distinguished from
+ * "default tablespace for this database", but that seems OK.
+ */
+Oid
+get_rel_tablespace(Oid relid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache(RELOID,
+ ObjectIdGetDatum(relid),
+ 0, 0, 0);
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp);
+ Oid result;
+
+ result = reltup->reltablespace;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
/*
* get_rel_type_id
*
return NULL;
}
+/*
+ * get_namespace_tablespace
+ * Returns the default tablespace of a given namespace
+ *
+ * Note: failure return is InvalidOid, which cannot be distinguished from
+ * "default tablespace for this database", but that seems OK.
+ */
+Oid
+get_namespace_tablespace(Oid nspid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache(NAMESPACEOID,
+ ObjectIdGetDatum(nspid),
+ 0, 0, 0);
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_namespace nsptup = (Form_pg_namespace) GETSTRUCT(tp);
+ Oid result;
+
+ result = nsptup->nsptablespace;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
/* ---------- PG_SHADOW CACHE ---------- */
/*
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.204 2004/05/30 23:40:37 neilc Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.205 2004/06/18 06:13:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
Relation relation);
static Relation RelationBuildDesc(RelationBuildDescInfo buildinfo,
Relation oldrelation);
+static void RelationInitPhysicalAddr(Relation relation);
static void AttrDefaultFetch(Relation relation);
static void CheckConstraintFetch(Relation relation);
static List *insert_ordered_oid(List *list, Oid datum);
*/
RelationInitLockInfo(relation); /* see lmgr.c */
- if (relation->rd_rel->relisshared)
- relation->rd_node.tblNode = InvalidOid;
- else
- relation->rd_node.tblNode = MyDatabaseId;
- relation->rd_node.relNode = relation->rd_rel->relfilenode;
+ /*
+ * initialize physical addressing information for the relation
+ */
+ RelationInitPhysicalAddr(relation);
/* make sure relation is marked as having no open file yet */
relation->rd_smgr = NULL;
return relation;
}
+/*
+ * Initialize the physical addressing info (RelFileNode) for a relcache entry
+ */
+static void
+RelationInitPhysicalAddr(Relation relation)
+{
+ if (relation->rd_rel->reltablespace)
+ relation->rd_node.spcNode = relation->rd_rel->reltablespace;
+ else
+ relation->rd_node.spcNode = MyDatabaseTableSpace;
+ if (relation->rd_rel->relisshared)
+ relation->rd_node.dbNode = InvalidOid;
+ else
+ relation->rd_node.dbNode = MyDatabaseId;
+ relation->rd_node.relNode = relation->rd_rel->relfilenode;
+}
+
/*
* Initialize index-access-method support data for an index relation
*/
* initialize relation id from info in att array (my, this is ugly)
*/
RelationGetRelid(relation) = relation->rd_att->attrs[0]->attrelid;
+ relation->rd_rel->relfilenode = RelationGetRelid(relation);
/*
- * initialize the relation's lock manager and RelFileNode information
+ * initialize the relation lock manager information
*/
RelationInitLockInfo(relation); /* see lmgr.c */
- if (relation->rd_rel->relisshared)
- relation->rd_node.tblNode = InvalidOid;
- else
- relation->rd_node.tblNode = MyDatabaseId;
- relation->rd_node.relNode =
- relation->rd_rel->relfilenode = RelationGetRelid(relation);
+ /*
+ * initialize physical addressing information for the relation
+ */
+ RelationInitPhysicalAddr(relation);
/*
* initialize the rel-has-index flag, using hardwired knowledge
relation->rd_id);
relp = (Form_pg_class) GETSTRUCT(pg_class_tuple);
memcpy((char *) relation->rd_rel, (char *) relp, CLASS_TUPLE_SIZE);
- relation->rd_node.relNode = relp->relfilenode;
+ /* Now we can recalculate physical address */
+ RelationInitPhysicalAddr(relation);
heap_freetuple(pg_class_tuple);
relation->rd_targblock = InvalidBlockNumber;
/* Okay, now it's valid again */
RelationBuildLocalRelation(const char *relname,
Oid relnamespace,
TupleDesc tupDesc,
- Oid relid, Oid dbid,
- RelFileNode rnode,
+ Oid relid,
+ Oid reltablespace,
+ bool shared_relation,
bool nailit)
{
Relation rel;
/*
* Insert relation physical and logical identifiers (OIDs) into the
- * right places.
+ * right places. Note that the physical ID (relfilenode) is initially
+ * the same as the logical ID (OID).
*/
- rel->rd_rel->relisshared = (dbid == InvalidOid);
+ rel->rd_rel->relisshared = shared_relation;
RelationGetRelid(rel) = relid;
for (i = 0; i < natts; i++)
rel->rd_att->attrs[i]->attrelid = relid;
- rel->rd_node = rnode;
- rel->rd_rel->relfilenode = rnode.relNode;
+ rel->rd_rel->relfilenode = relid;
+ rel->rd_rel->reltablespace = reltablespace;
RelationInitLockInfo(rel); /* see lmgr.c */
+ RelationInitPhysicalAddr(rel);
+
/*
* Okay to insert into the relcache hash tables.
*/
MemSet(&rel->pgstat_info, 0, sizeof(rel->pgstat_info));
/*
- * Make sure database ID is correct. This is needed in case the
- * pg_internal.init file was copied from some other database by
- * CREATE DATABASE.
+ * Recompute lock and physical addressing info. This is needed in
+ * case the pg_internal.init file was copied from some other database
+ * by CREATE DATABASE.
*/
- if (rel->rd_rel->relisshared)
- rel->rd_node.tblNode = InvalidOid;
- else
- rel->rd_node.tblNode = MyDatabaseId;
-
RelationInitLockInfo(rel);
+ RelationInitPhysicalAddr(rel);
}
/*
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/init/globals.c,v 1.90 2004/05/30 17:58:12 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/init/globals.c,v 1.91 2004/06/18 06:13:54 tgl Exp $
*
* NOTES
* Globals used all over the place should be declared here and not
char *DatabasePath = NULL;
Oid MyDatabaseId = InvalidOid;
+Oid MyDatabaseTableSpace = InvalidOid;
+
pid_t PostmasterPid = 0;
/*
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/init/miscinit.c,v 1.126 2004/05/30 23:40:38 neilc Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/init/miscinit.c,v 1.127 2004/06/18 06:13:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
SetDataDir(const char *dir)
{
char *new;
- int newlen;
AssertArg(dir);
* Strip any trailing slash. Not strictly necessary, but avoids
* generating funny-looking paths to individual files.
*/
- newlen = strlen(new);
- if (newlen > 1 && (new[newlen - 1] == '/'
-#ifdef WIN32
- || new[newlen - 1] == '\\'
-#endif
- ))
- new[newlen - 1] = '\0';
+ canonicalize_path(new);
if (DataDir)
free(DataDir);
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/init/postinit.c,v 1.133 2004/05/29 22:48:21 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/init/postinit.c,v 1.134 2004/06/18 06:13:54 tgl Exp $
*
*
*-------------------------------------------------------------------------
#include "catalog/namespace.h"
#include "catalog/pg_database.h"
#include "catalog/pg_shadow.h"
+#include "catalog/pg_tablespace.h"
#include "commands/trigger.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
if (bootstrap)
{
MyDatabaseId = TemplateDbOid;
- SetDatabasePath(GetDatabasePath(MyDatabaseId));
+ MyDatabaseTableSpace = DEFAULTTABLESPACE_OID;
+ SetDatabasePath(GetDatabasePath(MyDatabaseId, MyDatabaseTableSpace));
}
else
{
- char *fullpath,
- datpath[MAXPGPATH];
+ char *fullpath;
/*
* Formerly we validated DataDir here, but now that's done
*/
/*
- * Find oid and path of the database we're about to open. Since
- * we're not yet up and running we have to use the hackish
+ * Find oid and tablespace of the database we're about to open.
+ * Since we're not yet up and running we have to use the hackish
* GetRawDatabaseInfo.
*/
- GetRawDatabaseInfo(dbname, &MyDatabaseId, datpath);
+ GetRawDatabaseInfo(dbname, &MyDatabaseId, &MyDatabaseTableSpace);
if (!OidIsValid(MyDatabaseId))
ereport(FATAL,
errmsg("database \"%s\" does not exist",
dbname)));
- fullpath = GetDatabasePath(MyDatabaseId);
+ fullpath = GetDatabasePath(MyDatabaseId, MyDatabaseTableSpace);
/* Verify the database path */
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/misc/database.c,v 1.60 2004/01/22 20:57:39 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/misc/database.c,v 1.61 2004/06/18 06:13:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "catalog/catname.h"
#include "catalog/catalog.h"
#include "catalog/pg_database.h"
+#include "catalog/pg_tablespace.h"
#include "miscadmin.h"
#include "utils/syscache.h"
/* --------------------------------
- * GetRawDatabaseInfo() -- Find the OID and path of the database.
+ * GetRawDatabaseInfo() -- Find the OID and tablespace of the database.
*
- * The database's oid forms half of the unique key for the system
- * caches and lock tables. We therefore want it initialized before
- * we open any relations, since opening relations puts things in the
- * cache. To get around this problem, this code opens and scans the
+ * We need both the OID and the default tablespace in order to find
+ * the database's system catalogs. Moreover the database's OID forms
+ * half of the unique key for the system caches and lock tables, so
+ * we must have it before we can use any of the cache mechanisms.
+ * To get around these problems, this code opens and scans the
* pg_database relation by hand.
*
* This code knows way more than it should about the layout of
* --------------------------------
*/
void
-GetRawDatabaseInfo(const char *name, Oid *db_id, char *path)
+GetRawDatabaseInfo(const char *name, Oid *db_id, Oid *db_tablespace)
{
int dbfd;
int nbytes;
- int pathlen;
HeapTupleData tup;
+ Form_pg_database tup_db;
Page pg;
char *dbfname;
- Form_pg_database tup_db;
RelFileNode rnode;
- rnode.tblNode = 0;
+ /* hard-wired path to pg_database */
+ rnode.spcNode = GLOBALTABLESPACE_OID;
+ rnode.dbNode = 0;
rnode.relNode = RelOid_pg_database;
+
dbfname = relpath(rnode);
if ((dbfd = open(dbfname, O_RDONLY | PG_BINARY, 0)) < 0)
* committed and dead tuples to be marked with correct states.
*
* XXX wouldn't it be better to let new backends read the
- * database OID from a flat file, handled the same way we
+ * database info from a flat file, handled the same way we
* handle the password relation?
*/
if (!PhonyHeapTupleSatisfiesNow(tup.t_data))
if (strcmp(name, NameStr(tup_db->datname)) == 0)
{
- /* Found it; extract the OID and the database path. */
+ /* Found it; extract the db's OID and tablespace. */
*db_id = HeapTupleGetOid(&tup);
- pathlen = VARSIZE(&(tup_db->datpath)) - VARHDRSZ;
- if (pathlen < 0)
- pathlen = 0; /* pure paranoia */
- if (pathlen >= MAXPGPATH)
- pathlen = MAXPGPATH - 1; /* more paranoia */
- strncpy(path, VARDATA(&(tup_db->datpath)), pathlen);
- path[pathlen] = '\0';
+ *db_tablespace = tup_db->dattablespace;
goto done;
}
}
/* failed to find it... */
*db_id = InvalidOid;
- *path = '\0';
+ *db_tablespace = InvalidOid;
done:
close(dbfd);
* Portions Copyright (c) 1994, Regents of the University of California
* Portions taken from FreeBSD.
*
- * $PostgreSQL: pgsql/src/bin/initdb/initdb.c,v 1.37 2004/06/10 22:26:20 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/initdb/initdb.c,v 1.38 2004/06/18 06:13:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
char *pgdenv; /* PGDATA value got from sent to
* environment */
char *subdirs[] =
- {"global", "pg_xlog", "pg_clog", "base", "base/1"};
+ {"global", "pg_xlog", "pg_clog", "base", "base/1", "pg_tablespaces"};
progname = get_progname(argv[0]);
set_pglocale_pgservice(argv[0], "initdb");
/* Bootstrap template1 */
bootstrap_template1(short_version);
- /* Make the per-database PGVERSION for template1 only after init'ing it */
+ /* Make the per-database PG_VERSION for template1 only after init'ing it */
set_short_version(short_version, "base/1");
/* Create the stuff we don't need to use bootstrap mode for */
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/bin/pg_dump/dumputils.c,v 1.12 2004/03/23 22:06:08 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/dumputils.c,v 1.13 2004/06/18 06:14:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*
* Convert a string value to a dollar quoted literal and append it to
- * the given buffer. If the dqprefix parameter is not NULL then the
+ * the given buffer. If the dqprefix parameter is not NULL then the
* dollar quote delimiter will begin with that (after the opening $).
*
* No escaping is done at all on str, in compliance with the rules
if (dqprefix)
appendPQExpBuffer(delimBuf, dqprefix);
- /*
+ /*
* Make sure we choose a delimiter which (without the trailing $)
* is not present in the string being quoted. We don't check with the
* trailing $ because a string ending in $foo must not be quoted with
* otherwise use standard quoting.
*/
void
-appendStringLiteralDQOpt(PQExpBuffer buf, const char *str,
+appendStringLiteralDQOpt(PQExpBuffer buf, const char *str,
bool escapeAll, const char *dqprefix)
{
if (strchr(str, '\'') == NULL && strchr(str, '\\') == NULL)
CONVERT_PRIV('C', "CREATE");
CONVERT_PRIV('T', "TEMPORARY");
}
+ else if (strcmp(type, "TABLESPACE") == 0)
+ CONVERT_PRIV('C', "CREATE");
else
abort();
appendPQExpBufferChar(output, *input++);
else
{
- /* Otherwise, it's a quoted username */
+ /* Otherwise, it's a quoted username */
input++;
/* Loop until we come across an unescaped quote */
while (!(*input == '"' && *(input + 1) != '"'))
* by PostgreSQL
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.374 2004/06/07 20:35:57 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.375 2004/06/18 06:14:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
i_oid,
i_dba,
i_encoding,
- i_datpath;
+ i_tablespace;
CatalogId dbCatId;
DumpId dbDumpId;
const char *datname,
*dba,
*encoding,
- *datpath;
+ *tablespace;
datname = PQdb(g_conn);
selectSourceSchema("pg_catalog");
/* Get the database owner and parameters from pg_database */
- if (g_fout->remoteVersion >= 70100)
+ if (g_fout->remoteVersion >= 70500)
{
appendPQExpBuffer(dbQry, "SELECT tableoid, oid, "
"(SELECT usename FROM pg_user WHERE usesysid = datdba) as dba, "
"pg_encoding_to_char(encoding) as encoding, "
- "datpath "
+ "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) as tablespace "
+ "FROM pg_database "
+ "WHERE datname = ");
+ appendStringLiteral(dbQry, datname, true);
+ }
+ else if (g_fout->remoteVersion >= 70100)
+ {
+ appendPQExpBuffer(dbQry, "SELECT tableoid, oid, "
+ "(SELECT usename FROM pg_user WHERE usesysid = datdba) as dba, "
+ "pg_encoding_to_char(encoding) as encoding, "
+ "NULL as tablespace "
"FROM pg_database "
"WHERE datname = ");
appendStringLiteral(dbQry, datname, true);
}
else
{
- /*
- * In 7.0, datpath is either the same as datname, or the user-given
- * location with "/" and the datname appended. We must strip this
- * junk off to produce a correct LOCATION value.
- */
appendPQExpBuffer(dbQry, "SELECT "
"(SELECT oid FROM pg_class WHERE relname = 'pg_database') AS tableoid, "
"oid, "
"(SELECT usename FROM pg_user WHERE usesysid = datdba) as dba, "
"pg_encoding_to_char(encoding) as encoding, "
- "CASE WHEN length(datpath) > length(datname) THEN "
- "substr(datpath,1,length(datpath)-length(datname)-1) "
- "ELSE '' END as datpath "
+ "NULL as tablespace "
"FROM pg_database "
"WHERE datname = ");
appendStringLiteral(dbQry, datname, true);
i_oid = PQfnumber(res, "oid");
i_dba = PQfnumber(res, "dba");
i_encoding = PQfnumber(res, "encoding");
- i_datpath = PQfnumber(res, "datpath");
+ i_tablespace = PQfnumber(res, "tablespace");
dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid));
dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid));
dba = PQgetvalue(res, 0, i_dba);
encoding = PQgetvalue(res, 0, i_encoding);
- datpath = PQgetvalue(res, 0, i_datpath);
+ tablespace = PQgetvalue(res, 0, i_tablespace);
appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0",
fmtId(datname));
- if (strlen(datpath) > 0)
- {
- appendPQExpBuffer(creaQry, " LOCATION = ");
- appendStringLiteral(creaQry, datpath, true);
- }
if (strlen(encoding) > 0)
{
appendPQExpBuffer(creaQry, " ENCODING = ");
appendStringLiteral(creaQry, encoding, true);
}
+ if (strlen(tablespace) > 0 && strcmp(tablespace, "default") != 0)
+ {
+ appendPQExpBuffer(creaQry, " TABLESPACE = %s", fmtId(tablespace));
+ }
appendPQExpBuffer(creaQry, ";\n");
appendPQExpBuffer(delQry, "DROP DATABASE %s;\n",
if (strftime(buf, 256, "%Y-%m-%d %H:%M:%S %Z", localtime(&now)) != 0)
{
PQExpBuffer qry = createPQExpBuffer();
-
+
appendPQExpBuffer(qry, "-- ");
appendPQExpBuffer(qry, msg);
appendPQExpBuffer(qry, " ");
int i_oid;
int i_nspname;
int i_usename;
+ int i_nsptablespace;
int i_nspacl;
/*
nsinfo[0].dobj.name = strdup("");
nsinfo[0].usename = strdup("");
nsinfo[0].nspacl = strdup("");
+ nsinfo[0].nsptablespace = strdup("");
selectDumpableNamespace(&nsinfo[0]);
nsinfo[1].dobj.name = strdup("pg_catalog");
nsinfo[1].usename = strdup("");
nsinfo[1].nspacl = strdup("");
+ nsinfo[0].nsptablespace = strdup("");
selectDumpableNamespace(&nsinfo[1]);
* we fetch all namespaces including system ones, so that every object
* we read in can be linked to a containing namespace.
*/
- appendPQExpBuffer(query, "SELECT tableoid, oid, nspname, "
- "(select usename from pg_user where nspowner = usesysid) as usename, "
- "nspacl "
- "FROM pg_namespace");
+ if (g_fout->remoteVersion >= 70500)
+ {
+ appendPQExpBuffer(query, "SELECT tableoid, oid, nspname, "
+ "(select usename from pg_user where nspowner = usesysid) as usename, "
+ "nspacl, "
+ "(SELECT spcname FROM pg_tablespace t WHERE t.oid = nsptablespace) AS nsptablespace "
+ "FROM pg_namespace");
+ }
+ else
+ {
+ appendPQExpBuffer(query, "SELECT tableoid, oid, nspname, "
+ "(select usename from pg_user where nspowner = usesysid) as usename, "
+ "nspacl, NULL AS nsptablespace "
+ "FROM pg_namespace");
+ }
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
i_nspname = PQfnumber(res, "nspname");
i_usename = PQfnumber(res, "usename");
i_nspacl = PQfnumber(res, "nspacl");
+ i_nsptablespace = PQfnumber(res, "nsptablespace");
for (i = 0; i < ntups; i++)
{
nsinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_nspname));
nsinfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
nsinfo[i].nspacl = strdup(PQgetvalue(res, i, i_nspacl));
+ nsinfo[i].nsptablespace = strdup(PQgetvalue(res, i, i_nsptablespace));
/* Decide whether to dump this namespace */
selectDumpableNamespace(&nsinfo[i]);
int i_relhasoids;
int i_owning_tab;
int i_owning_col;
+ int i_reltablespace;
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
* columns, etc.
*/
- if (g_fout->remoteVersion >= 70300)
+ if (g_fout->remoteVersion >= 70500)
{
/*
* Left join to pick up dependency info linking sequences to their
"relchecks, reltriggers, "
"relhasindex, relhasrules, relhasoids, "
"d.refobjid as owning_tab, "
- "d.refobjsubid as owning_col "
+ "d.refobjsubid as owning_col, "
+ "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace "
+ "from pg_class c "
+ "left join pg_depend d on "
+ "(c.relkind = '%c' and "
+ "d.classid = c.tableoid and d.objid = c.oid and "
+ "d.objsubid = 0 and "
+ "d.refclassid = c.tableoid and d.deptype = 'i') "
+ "where relkind in ('%c', '%c', '%c') "
+ "order by c.oid",
+ RELKIND_SEQUENCE,
+ RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW);
+ }
+ else if (g_fout->remoteVersion >= 70300)
+ {
+ /*
+ * Left join to pick up dependency info linking sequences to their
+ * serial column, if any
+ */
+ appendPQExpBuffer(query,
+ "SELECT c.tableoid, c.oid, relname, "
+ "relacl, relkind, relnamespace, "
+ "(select usename from pg_user where relowner = usesysid) as usename, "
+ "relchecks, reltriggers, "
+ "relhasindex, relhasrules, relhasoids, "
+ "d.refobjid as owning_tab, "
+ "d.refobjsubid as owning_col, "
+ "NULL as reltablespace "
"from pg_class c "
"left join pg_depend d on "
"(c.relkind = '%c' and "
"relchecks, reltriggers, "
"relhasindex, relhasrules, relhasoids, "
"NULL::oid as owning_tab, "
- "NULL::int4 as owning_col "
+ "NULL::int4 as owning_col, "
+ "NULL as reltablespace "
"from pg_class "
"where relkind in ('%c', '%c', '%c') "
"order by oid",
"relhasindex, relhasrules, "
"'t'::bool as relhasoids, "
"NULL::oid as owning_tab, "
- "NULL::int4 as owning_col "
+ "NULL::int4 as owning_col, "
+ "NULL as reltablespace "
"from pg_class "
"where relkind in ('%c', '%c', '%c') "
"order by oid",
"relhasindex, relhasrules, "
"'t'::bool as relhasoids, "
"NULL::oid as owning_tab, "
- "NULL::int4 as owning_col "
+ "NULL::int4 as owning_col, "
+ "NULL as reltablespace "
"from pg_class c "
"where relkind in ('%c', '%c') "
"order by oid",
i_relhasoids = PQfnumber(res, "relhasoids");
i_owning_tab = PQfnumber(res, "owning_tab");
i_owning_col = PQfnumber(res, "owning_col");
+ i_reltablespace = PQfnumber(res, "reltablespace");
for (i = 0; i < ntups; i++)
{
tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
}
+ tblinfo[i].reltablespace = strdup(PQgetvalue(res, i, i_reltablespace));
/* other fields were zeroed above */
indxinfo[j].indextable = tbinfo;
indxinfo[j].indexdef = strdup(PQgetvalue(res, j, i_indexdef));
indxinfo[j].indnkeys = atoi(PQgetvalue(res, j, i_indnkeys));
+
/*
* In pre-7.4 releases, indkeys may contain more entries than
* indnkeys says (since indnkeys will be 1 for a functional
constrinfo[j].conindex = indxinfo[j].dobj.dumpId;
constrinfo[j].coninherited = false;
constrinfo[j].separate = true;
-
+
indxinfo[j].indexconstraint = constrinfo[j].dobj.dumpId;
/* If pre-7.3 DB, better make sure table comes first */
{
appendPQExpBuffer(delq, "DROP SCHEMA %s;\n", qnspname);
- appendPQExpBuffer(q, "CREATE SCHEMA %s AUTHORIZATION %s;\n",
+ appendPQExpBuffer(q, "CREATE SCHEMA %s AUTHORIZATION %s",
qnspname, fmtId(nspinfo->usename));
+ /* Add tablespace qualifier, if not default */
+ if (strlen(nspinfo->nsptablespace) != 0)
+ appendPQExpBuffer(q, " TABLESPACE %s",
+ fmtId(nspinfo->nsptablespace));
+
+ appendPQExpBuffer(q, ";\n");
+
ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId,
nspinfo->dobj.name,
NULL, "",
if (strcmp(prosrc, "-") != 0)
{
appendPQExpBuffer(asPart, ", ");
- /*
+ /*
* where we have bin, use dollar quoting if allowed and src
* contains quote or backslash; else use regular quoting.
*/
* Skip cast if function isn't from pg_ and that namespace is
* not dumped.
*/
- if (funcInfo &&
+ if (funcInfo &&
strncmp(funcInfo->dobj.namespace->dobj.name, "pg_", 3) != 0 &&
!funcInfo->dobj.namespace->dump)
return;
fmtId(convinfo->dobj.name));
appendPQExpBuffer(q, "CREATE %sCONVERSION %s FOR ",
- (condefault) ? "DEFAULT " : "",
+ (condefault) ? "DEFAULT " : "",
fmtId(convinfo->dobj.name));
appendStringLiteral(q, conforencoding, true);
appendPQExpBuffer(q, " TO ");
appendStringLiteral(q, contoencoding, true);
/* regproc is automatically quoted in 7.3 and above */
appendPQExpBuffer(q, " FROM %s;\n", conproc);
-
+
ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId,
convinfo->dobj.name,
convinfo->dobj.namespace->dobj.name, convinfo->usename,
appendPQExpBuffer(q, ")");
}
+ /* Output tablespace clause if necessary */
+ if (strlen(tbinfo->reltablespace) != 0 &&
+ strcmp(tbinfo->reltablespace,
+ tbinfo->dobj.namespace->nsptablespace) != 0)
+ {
+ appendPQExpBuffer(q, " TABLESPACE %s",
+ fmtId(tbinfo->reltablespace));
+ }
+
appendPQExpBuffer(q, ";\n");
/* Loop dumping statistics and storage statements */
appendPQExpBuffer(query, " NO MINVALUE\n");
appendPQExpBuffer(query,
- " CACHE %s%s;\n",
+ " CACHE %s%s",
cache, (cycled ? "\n CYCLE" : ""));
+ /* Output tablespace clause if necessary */
+ if (strlen(tbinfo->reltablespace) != 0 &&
+ strcmp(tbinfo->reltablespace,
+ tbinfo->dobj.namespace->nsptablespace) != 0)
+ {
+ appendPQExpBuffer(query, " TABLESPACE %s",
+ fmtId(tbinfo->reltablespace));
+ }
+
+ appendPQExpBuffer(query, ";\n");
+
ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
tbinfo->dobj.name,
tbinfo->dobj.namespace->dobj.name, tbinfo->usename,
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.h,v 1.108 2004/03/03 21:28:55 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.h,v 1.109 2004/06/18 06:14:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
DumpableObject dobj;
char *usename; /* name of owner, or empty string */
char *nspacl;
+ char *nsptablespace; /* default tablespace */
bool dump; /* true if need to dump definition */
} NamespaceInfo;
char *usename; /* name of owner, or empty string */
char *relacl;
char relkind;
+ char *reltablespace; /* relation tablespace */
bool hasindex; /* does it have any indexes? */
bool hasrules; /* does it have any rules? */
bool hasoids; /* does it have OIDs? */
* Portions Copyright (c) 1994, Regents of the University of California
*
*
- * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dumpall.c,v 1.41 2004/06/10 16:35:17 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dumpall.c,v 1.42 2004/06/18 06:14:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
static void dumpUsers(PGconn *conn);
static void dumpGroups(PGconn *conn);
+static void dumpTablespaces(PGconn *conn);
static void dumpCreateDB(PGconn *conn);
static void dumpDatabaseConfig(PGconn *conn, const char *dbname);
static void dumpUserConfig(PGconn *conn, const char *username);
{
dumpUsers(conn);
dumpGroups(conn);
+ if (server_version >= 70500)
+ dumpTablespaces(conn);
}
if (!globals_only)
printf("\n\n");
}
+/*
+ * Dump tablespaces.
+ */
+static void
+dumpTablespaces(PGconn *conn)
+{
+ PGresult *res;
+ int i;
+
+ printf("--\n-- Tablespaces\n--\n\n");
+
+ /*
+ * Get all tablespaces except for the system default and global
+ * tablespaces
+ */
+ res = executeQuery(conn, "SELECT spcname, "
+ "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, "
+ "spclocation, spcacl "
+ "FROM pg_catalog.pg_tablespace "
+ "WHERE spcname NOT IN ('default', 'global')");
+
+ for (i = 0; i < PQntuples(res); i++)
+ {
+ PQExpBuffer buf = createPQExpBuffer();
+ char *spcname = PQgetvalue(res, i, 0);
+ char *spcowner = PQgetvalue(res, i, 1);
+ char *spclocation = PQgetvalue(res, i, 2);
+ char *spcacl = PQgetvalue(res, i, 3);
+ char *fspcname;
+
+ /* needed for buildACLCommands() */
+ fspcname = strdup(fmtId(spcname));
+
+ if (output_clean)
+ appendPQExpBuffer(buf, "DROP TABLESPACE %s;\n", fspcname);
+
+ appendPQExpBuffer(buf, "CREATE TABLESPACE %s", fspcname);
+ appendPQExpBuffer(buf, " OWNER %s", fmtId(spcowner));
+
+ appendPQExpBuffer(buf, " LOCATION ");
+ appendStringLiteral(buf, spclocation, true);
+ appendPQExpBuffer(buf, ";\n");
+
+ if (!skip_acls &&
+ !buildACLCommands(fspcname, "TABLESPACE", spcacl, spcowner,
+ server_version, buf))
+ {
+ fprintf(stderr, _("%s: could not parse ACL list (%s) for tablespace \"%s\"\n"),
+ progname, spcacl, fspcname);
+ PQfinish(conn);
+ exit(1);
+ }
+
+ printf("%s", buf->data);
+ free(fspcname);
+ destroyPQExpBuffer(buf);
+ }
+
+ PQclear(res);
+ printf("\n\n");
+}
/*
* Dump commands to create each database.
printf("--\n-- Database creation\n--\n\n");
- if (server_version >= 70300)
+ if (server_version >= 70500)
res = executeQuery(conn,
"SELECT datname, "
"coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), "
"pg_encoding_to_char(d.encoding), "
- "datistemplate, datpath, datacl "
+ "datistemplate, datacl, "
+ "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace "
+ "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) "
+ "WHERE datallowconn ORDER BY 1");
+ else if (server_version >= 70300)
+ res = executeQuery(conn,
+ "SELECT datname, "
+ "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), "
+ "pg_encoding_to_char(d.encoding), "
+ "datistemplate, datacl, "
+ "'default' AS dattablespace "
"FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) "
"WHERE datallowconn ORDER BY 1");
else if (server_version >= 70100)
"(select usename from pg_shadow where usesysid=datdba), "
"(select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), "
"pg_encoding_to_char(d.encoding), "
- "datistemplate, datpath, '' as datacl "
+ "datistemplate, '' as datacl, "
+ "'default' AS dattablespace "
"FROM pg_database d "
"WHERE datallowconn ORDER BY 1");
else
{
/*
- * In 7.0, datpath is either the same as datname, or the user-given
- * location with "/" and the datname appended. We must strip this
- * junk off to produce a correct LOCATION value.
- *
* Note: 7.0 fails to cope with sub-select in COALESCE, so just
* deal with getting a NULL by not printing any OWNER clause.
*/
"(select usename from pg_shadow where usesysid=datdba), "
"pg_encoding_to_char(d.encoding), "
"'f' as datistemplate, "
- "CASE WHEN length(datpath) > length(datname) THEN "
- "substr(datpath,1,length(datpath)-length(datname)-1) "
- "ELSE '' END as datpath, "
- "'' as datacl "
+ "'' as datacl, "
+ "'default' AS dattablespace "
"FROM pg_database d "
"ORDER BY 1");
}
char *dbowner = PQgetvalue(res, i, 1);
char *dbencoding = PQgetvalue(res, i, 2);
char *dbistemplate = PQgetvalue(res, i, 3);
- char *dbpath = PQgetvalue(res, i, 4);
- char *dbacl = PQgetvalue(res, i, 5);
+ char *dbacl = PQgetvalue(res, i, 4);
+ char *dbtablespace = PQgetvalue(res, i, 5);
char *fdbname;
if (strcmp(dbname, "template1") == 0)
appendPQExpBuffer(buf, "DROP DATABASE %s;\n", fdbname);
appendPQExpBuffer(buf, "CREATE DATABASE %s", fdbname);
+
+ appendPQExpBuffer(buf, " WITH TEMPLATE = template0");
+
if (strlen(dbowner) != 0)
- appendPQExpBuffer(buf, " WITH OWNER = %s",
+ appendPQExpBuffer(buf, " OWNER = %s",
fmtId(dbowner));
- appendPQExpBuffer(buf, " TEMPLATE = template0");
-
- if (strlen(dbpath) != 0)
- {
- appendPQExpBuffer(buf, " LOCATION = ");
- appendStringLiteral(buf, dbpath, true);
- }
appendPQExpBuffer(buf, " ENCODING = ");
appendStringLiteral(buf, dbencoding, true);
+ /* Output tablespace if it isn't default */
+ if (strcmp(dbtablespace, "default") != 0)
+ appendPQExpBuffer(buf, " TABLESPACE = %s",
+ fmtId(dbtablespace));
+
appendPQExpBuffer(buf, ";\n");
if (strcmp(dbistemplate, "t") == 0)
*
* Copyright (c) 2000-2003, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.116 2004/05/07 00:24:58 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.117 2004/06/18 06:14:04 tgl Exp $
*/
#include "postgres_fe.h"
#include "command.h"
case 'a':
success = describeAggregates(pattern, show_verbose);
break;
+ case 'b':
+ success = describeTablespaces(pattern);
+ break;
case 'c':
success = listConversions(pattern);
break;
*
* Copyright (c) 2000-2003, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.98 2004/05/07 00:24:58 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.99 2004/06/18 06:14:04 tgl Exp $
*/
#include "postgres_fe.h"
#include "describe.h"
return true;
}
+/* \db
+ * Takes an optional regexp to select particular tablespaces
+ */
+bool
+describeTablespaces(const char *pattern)
+{
+ PQExpBufferData buf;
+ PGresult *res;
+ printQueryOpt myopt = pset.popt;
+
+ initPQExpBuffer(&buf);
+
+ printfPQExpBuffer(&buf,
+ "SELECT spcname AS \"%s\",\n"
+ " pg_catalog.pg_get_userbyid(spcowner) AS \"%s\",\n"
+ " spclocation AS \"%s\"\n"
+ "FROM pg_catalog.pg_tablespace\n",
+ _("Name"), _("Owner"), _("Location"));
+
+ processNamePattern(&buf, pattern, false, false,
+ NULL, "spcname", NULL,
+ NULL);
+
+ appendPQExpBuffer(&buf, "ORDER BY 1;");
+
+ res = PSQLexec(buf.data, false);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ myopt.nullPrint = NULL;
+ myopt.title = _("List of tablespaces");
+
+ printQuery(res, &myopt, pset.queryFout);
+
+ PQclear(res);
+ return true;
+}
+
/* \df
* Takes an optional regexp to select particular functions
printfPQExpBuffer(&buf,
"SELECT n.nspname as \"%s\",\n"
" c.relname as \"%s\",\n"
- " CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'S' THEN '%s' END as \"%s\",\n"
+ " CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'S' THEN '%s' END as \"%s\",\n"
" c.relacl as \"%s\"\n"
"FROM pg_catalog.pg_class c\n"
" LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
*
* Copyright (c) 2000-2003, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/bin/psql/describe.h,v 1.23 2003/12/01 22:21:54 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/describe.h,v 1.24 2004/06/18 06:14:04 tgl Exp $
*/
#ifndef DESCRIBE_H
#define DESCRIBE_H
/* \da */
bool describeAggregates(const char *pattern, bool verbose);
+/* \db */
+bool describeTablespaces(const char *pattern);
+
/* \df */
bool describeFunctions(const char *pattern, bool verbose);
*
* Copyright (c) 2000-2003, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/bin/psql/help.c,v 1.87 2004/05/07 00:24:58 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/help.c,v 1.88 2004/06/18 06:14:04 tgl Exp $
*/
#include "postgres_fe.h"
#include "common.h"
fprintf(output, _(" \\d{t|i|s|v|S} [PATTERN] (add \"+\" for more detail)\n"
" list tables/indexes/sequences/views/system tables\n"));
fprintf(output, _(" \\da [PATTERN] list aggregate functions\n"));
+ fprintf(output, _(" \\db [PATTERN] list tablespaces\n"));
fprintf(output, _(" \\dc [PATTERN] list conversions\n"));
fprintf(output, _(" \\dC list casts\n"));
fprintf(output, _(" \\dd [PATTERN] show comment for object\n"));
size_t len;
int nl_count = 0;
char *ch;
-
+
/* don't care about trailing spaces */
len = strlen(topic);
while (topic[len - 1] == ' ')
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/catalog.h,v 1.27 2003/11/29 22:40:58 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catalog.h,v 1.28 2004/06/18 06:14:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern char *relpath(RelFileNode rnode);
-extern char *GetDatabasePath(Oid tblNode);
+extern char *GetDatabasePath(Oid dbNode, Oid spcNode);
extern bool IsSystemRelation(Relation relation);
extern bool IsToastRelation(Relation relation);
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/catname.h,v 1.31 2003/11/29 22:40:58 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catname.h,v 1.32 2004/06/18 06:14:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#define RewriteRelationName "pg_rewrite"
#define ShadowRelationName "pg_shadow"
#define StatisticRelationName "pg_statistic"
+#define TableSpaceRelationName "pg_tablespace"
#define TypeRelationName "pg_type"
#define VersionRelationName "pg_version"
#define AttrDefaultRelationName "pg_attrdef"
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.236 2004/06/16 01:26:49 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.237 2004/06/18 06:14:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200406151
+#define CATALOG_VERSION_NO 200406171
#endif
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/heap.h,v 1.66 2004/05/05 04:48:47 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/heap.h,v 1.67 2004/06/18 06:14:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern Relation heap_create(const char *relname,
Oid relnamespace,
+ Oid reltablespace,
TupleDesc tupDesc,
bool shared_relation,
bool storage_create,
extern Oid heap_create_with_catalog(const char *relname,
Oid relnamespace,
+ Oid reltablespace,
TupleDesc tupdesc,
char relkind,
bool shared_relation,
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/index.h,v 1.56 2004/05/08 00:34:49 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/index.h,v 1.57 2004/06/18 06:14:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
const char *indexRelationName,
IndexInfo *indexInfo,
Oid accessMethodObjectId,
+ Oid tableSpaceId,
Oid *classObjectId,
bool primary,
bool isconstraint,
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/indexing.h,v 1.81 2003/11/29 22:40:58 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/indexing.h,v 1.82 2004/06/18 06:14:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#define ShadowNameIndex "pg_shadow_usename_index"
#define ShadowSysidIndex "pg_shadow_usesysid_index"
#define StatisticRelidAttnumIndex "pg_statistic_relid_att_index"
+#define TablespaceNameIndex "pg_tablespace_spcname_index"
+#define TablespaceOidIndex "pg_tablespace_oid_index"
#define TriggerConstrNameIndex "pg_trigger_tgconstrname_index"
#define TriggerConstrRelidIndex "pg_trigger_tgconstrrelid_index"
#define TriggerRelidNameIndex "pg_trigger_tgrelid_tgname_index"
DECLARE_UNIQUE_INDEX(pg_shadow_usename_index on pg_shadow using btree(usename name_ops));
DECLARE_UNIQUE_INDEX(pg_shadow_usesysid_index on pg_shadow using btree(usesysid int4_ops));
DECLARE_UNIQUE_INDEX(pg_statistic_relid_att_index on pg_statistic using btree(starelid oid_ops, staattnum int2_ops));
+DECLARE_UNIQUE_INDEX(pg_tablespace_oid_index on pg_tablespace using btree(oid oid_ops));
+DECLARE_UNIQUE_INDEX(pg_tablespace_spcname_index on pg_tablespace using btree(spcname name_ops));
/* This following index is not used for a cache and is not unique */
DECLARE_INDEX(pg_trigger_tgconstrname_index on pg_trigger using btree(tgconstrname name_ops));
/* This following index is not used for a cache and is not unique */
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/pg_attribute.h,v 1.109 2004/04/01 21:28:45 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_attribute.h,v 1.110 2004/06/18 06:14:06 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
*/
/* ----------------
- * pg_type schema
+ * pg_type
* ----------------
*/
#define Schema_pg_type \
DATA(insert ( 1262 datlastsysoid 26 -1 4 6 0 -1 -1 t p i t f f t 0));
DATA(insert ( 1262 datvacuumxid 28 -1 4 7 0 -1 -1 t p i t f f t 0));
DATA(insert ( 1262 datfrozenxid 28 -1 4 8 0 -1 -1 t p i t f f t 0));
-/* do not mark datpath as toastable; GetRawDatabaseInfo won't cope */
-DATA(insert ( 1262 datpath 25 -1 -1 9 0 -1 -1 f p i t f f t 0));
+DATA(insert ( 1262 dattablespace 26 -1 4 9 0 -1 -1 t p i t f f t 0));
DATA(insert ( 1262 datconfig 1009 -1 -1 10 1 -1 -1 f x i f f f t 0));
DATA(insert ( 1262 datacl 1034 -1 -1 11 1 -1 -1 f x i f f f t 0));
DATA(insert ( 1262 ctid 27 0 6 -1 0 -1 -1 f p s t f f t 0));
{ 1259, {"relowner"}, 23, -1, 4, 4, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
{ 1259, {"relam"}, 26, -1, 4, 5, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
{ 1259, {"relfilenode"}, 26, -1, 4, 6, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1259, {"relpages"}, 23, -1, 4, 7, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1259, {"reltuples"}, 700, -1, 4, 8, 0, -1, -1, false, 'p', 'i', true, false, false, true, 0 }, \
-{ 1259, {"reltoastrelid"}, 26, -1, 4, 9, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1259, {"reltoastidxid"}, 26, -1, 4, 10, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1259, {"relhasindex"}, 16, -1, 1, 11, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1259, {"relisshared"}, 16, -1, 1, 12, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1259, {"relkind"}, 18, -1, 1, 13, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1259, {"relnatts"}, 21, -1, 2, 14, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
-{ 1259, {"relchecks"}, 21, -1, 2, 15, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
-{ 1259, {"reltriggers"}, 21, -1, 2, 16, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
-{ 1259, {"relukeys"}, 21, -1, 2, 17, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
-{ 1259, {"relfkeys"}, 21, -1, 2, 18, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
-{ 1259, {"relrefs"}, 21, -1, 2, 19, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
-{ 1259, {"relhasoids"}, 16, -1, 1, 20, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1259, {"relhaspkey"}, 16, -1, 1, 21, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1259, {"relhasrules"}, 16, -1, 1, 22, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1259, {"relhassubclass"},16, -1, 1, 23, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1259, {"relacl"}, 1034, -1, -1, 24, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0 }
+{ 1259, {"reltablespace"}, 26, -1, 4, 7, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1259, {"relpages"}, 23, -1, 4, 8, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1259, {"reltuples"}, 700, -1, 4, 9, 0, -1, -1, false, 'p', 'i', true, false, false, true, 0 }, \
+{ 1259, {"reltoastrelid"}, 26, -1, 4, 10, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1259, {"reltoastidxid"}, 26, -1, 4, 11, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1259, {"relhasindex"}, 16, -1, 1, 12, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1259, {"relisshared"}, 16, -1, 1, 13, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1259, {"relkind"}, 18, -1, 1, 14, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1259, {"relnatts"}, 21, -1, 2, 15, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
+{ 1259, {"relchecks"}, 21, -1, 2, 16, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
+{ 1259, {"reltriggers"}, 21, -1, 2, 17, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
+{ 1259, {"relukeys"}, 21, -1, 2, 18, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
+{ 1259, {"relfkeys"}, 21, -1, 2, 19, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
+{ 1259, {"relrefs"}, 21, -1, 2, 20, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
+{ 1259, {"relhasoids"}, 16, -1, 1, 21, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1259, {"relhaspkey"}, 16, -1, 1, 22, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1259, {"relhasrules"}, 16, -1, 1, 23, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1259, {"relhassubclass"},16, -1, 1, 24, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1259, {"relacl"}, 1034, -1, -1, 25, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0 }
DATA(insert ( 1259 relname 19 -1 NAMEDATALEN 1 0 -1 -1 f p i t f f t 0));
DATA(insert ( 1259 relnamespace 26 -1 4 2 0 -1 -1 t p i t f f t 0));
DATA(insert ( 1259 relowner 23 -1 4 4 0 -1 -1 t p i t f f t 0));
DATA(insert ( 1259 relam 26 -1 4 5 0 -1 -1 t p i t f f t 0));
DATA(insert ( 1259 relfilenode 26 -1 4 6 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1259 relpages 23 -1 4 7 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1259 reltuples 700 -1 4 8 0 -1 -1 f p i t f f t 0));
-DATA(insert ( 1259 reltoastrelid 26 -1 4 9 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1259 reltoastidxid 26 -1 4 10 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1259 relhasindex 16 -1 1 11 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1259 relisshared 16 -1 1 12 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1259 relkind 18 -1 1 13 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1259 relnatts 21 -1 2 14 0 -1 -1 t p s t f f t 0));
-DATA(insert ( 1259 relchecks 21 -1 2 15 0 -1 -1 t p s t f f t 0));
-DATA(insert ( 1259 reltriggers 21 -1 2 16 0 -1 -1 t p s t f f t 0));
-DATA(insert ( 1259 relukeys 21 -1 2 17 0 -1 -1 t p s t f f t 0));
-DATA(insert ( 1259 relfkeys 21 -1 2 18 0 -1 -1 t p s t f f t 0));
-DATA(insert ( 1259 relrefs 21 -1 2 19 0 -1 -1 t p s t f f t 0));
-DATA(insert ( 1259 relhasoids 16 -1 1 20 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1259 relhaspkey 16 -1 1 21 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1259 relhasrules 16 -1 1 22 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1259 relhassubclass 16 -1 1 23 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1259 relacl 1034 -1 -1 24 1 -1 -1 f x i f f f t 0));
+DATA(insert ( 1259 reltablespace 26 -1 4 7 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1259 relpages 23 -1 4 8 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1259 reltuples 700 -1 4 9 0 -1 -1 f p i t f f t 0));
+DATA(insert ( 1259 reltoastrelid 26 -1 4 10 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1259 reltoastidxid 26 -1 4 11 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1259 relhasindex 16 -1 1 12 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1259 relisshared 16 -1 1 13 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1259 relkind 18 -1 1 14 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1259 relnatts 21 -1 2 15 0 -1 -1 t p s t f f t 0));
+DATA(insert ( 1259 relchecks 21 -1 2 16 0 -1 -1 t p s t f f t 0));
+DATA(insert ( 1259 reltriggers 21 -1 2 17 0 -1 -1 t p s t f f t 0));
+DATA(insert ( 1259 relukeys 21 -1 2 18 0 -1 -1 t p s t f f t 0));
+DATA(insert ( 1259 relfkeys 21 -1 2 19 0 -1 -1 t p s t f f t 0));
+DATA(insert ( 1259 relrefs 21 -1 2 20 0 -1 -1 t p s t f f t 0));
+DATA(insert ( 1259 relhasoids 16 -1 1 21 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1259 relhaspkey 16 -1 1 22 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1259 relhasrules 16 -1 1 23 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1259 relhassubclass 16 -1 1 24 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1259 relacl 1034 -1 -1 25 1 -1 -1 f x i f f f t 0));
DATA(insert ( 1259 ctid 27 0 6 -1 0 -1 -1 f p s t f f t 0));
DATA(insert ( 1259 oid 26 0 4 -2 0 -1 -1 t p i t f f t 0));
DATA(insert ( 1259 xmin 28 0 4 -3 0 -1 -1 t p i t f f t 0));
DATA(insert ( 1259 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0));
DATA(insert ( 1259 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0));
+/* ----------------
+ * pg_tablespace
+ * ----------------
+ */
+
+DATA(insert ( 1213 spcname 19 -1 NAMEDATALEN 1 0 -1 -1 f p i t f f t 0));
+DATA(insert ( 1213 spcowner 23 -1 4 2 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1213 spclocation 25 -1 -1 3 0 -1 -1 f x i t f f t 0));
+DATA(insert ( 1213 spcacl 1034 -1 -1 4 1 -1 -1 f x i f f f t 0));
+DATA(insert ( 1213 ctid 27 0 6 -1 0 -1 -1 f p s t f f t 0));
+DATA(insert ( 1213 oid 26 0 4 -2 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1213 xmin 28 0 4 -3 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1213 cmin 29 0 4 -4 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1213 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1213 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1213 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0));
+
/* ----------------
* pg_xactlock - this is not a real relation, but is a placeholder
* to allow a relation OID to be used for transaction
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/pg_class.h,v 1.81 2004/04/01 21:28:45 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_class.h,v 1.82 2004/06/18 06:14:06 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
int4 relowner; /* class owner */
Oid relam; /* index access method; 0 if not an index */
Oid relfilenode; /* identifier of physical storage file */
+ Oid reltablespace; /* identifier of table space for relation */
int4 relpages; /* # of blocks (not always up-to-date) */
float4 reltuples; /* # of tuples (not always up-to-date) */
Oid reltoastrelid; /* OID of toast table; 0 if none */
* relacl field. This is a kluge.
* ----------------
*/
-#define Natts_pg_class_fixed 23
-#define Natts_pg_class 24
+#define Natts_pg_class_fixed 24
+#define Natts_pg_class 25
#define Anum_pg_class_relname 1
#define Anum_pg_class_relnamespace 2
#define Anum_pg_class_reltype 3
#define Anum_pg_class_relowner 4
#define Anum_pg_class_relam 5
#define Anum_pg_class_relfilenode 6
-#define Anum_pg_class_relpages 7
-#define Anum_pg_class_reltuples 8
-#define Anum_pg_class_reltoastrelid 9
-#define Anum_pg_class_reltoastidxid 10
-#define Anum_pg_class_relhasindex 11
-#define Anum_pg_class_relisshared 12
-#define Anum_pg_class_relkind 13
-#define Anum_pg_class_relnatts 14
-#define Anum_pg_class_relchecks 15
-#define Anum_pg_class_reltriggers 16
-#define Anum_pg_class_relukeys 17
-#define Anum_pg_class_relfkeys 18
-#define Anum_pg_class_relrefs 19
-#define Anum_pg_class_relhasoids 20
-#define Anum_pg_class_relhaspkey 21
-#define Anum_pg_class_relhasrules 22
-#define Anum_pg_class_relhassubclass 23
-#define Anum_pg_class_relacl 24
+#define Anum_pg_class_reltablespace 7
+#define Anum_pg_class_relpages 8
+#define Anum_pg_class_reltuples 9
+#define Anum_pg_class_reltoastrelid 10
+#define Anum_pg_class_reltoastidxid 11
+#define Anum_pg_class_relhasindex 12
+#define Anum_pg_class_relisshared 13
+#define Anum_pg_class_relkind 14
+#define Anum_pg_class_relnatts 15
+#define Anum_pg_class_relchecks 16
+#define Anum_pg_class_reltriggers 17
+#define Anum_pg_class_relukeys 18
+#define Anum_pg_class_relfkeys 19
+#define Anum_pg_class_relrefs 20
+#define Anum_pg_class_relhasoids 21
+#define Anum_pg_class_relhaspkey 22
+#define Anum_pg_class_relhasrules 23
+#define Anum_pg_class_relhassubclass 24
+#define Anum_pg_class_relacl 25
/* ----------------
* initial contents of pg_class
* ----------------
*/
-DATA(insert OID = 1247 ( pg_type PGNSP 71 PGUID 0 1247 0 0 0 0 f f r 23 0 0 0 0 0 t f f f _null_ ));
+DATA(insert OID = 1247 ( pg_type PGNSP 71 PGUID 0 1247 0 0 0 0 0 f f r 23 0 0 0 0 0 t f f f _null_ ));
DESCR("");
-DATA(insert OID = 1249 ( pg_attribute PGNSP 75 PGUID 0 1249 0 0 0 0 f f r 17 0 0 0 0 0 f f f f _null_ ));
+DATA(insert OID = 1249 ( pg_attribute PGNSP 75 PGUID 0 1249 0 0 0 0 0 f f r 17 0 0 0 0 0 f f f f _null_ ));
DESCR("");
-DATA(insert OID = 1255 ( pg_proc PGNSP 81 PGUID 0 1255 0 0 0 0 f f r 16 0 0 0 0 0 t f f f _null_ ));
+DATA(insert OID = 1255 ( pg_proc PGNSP 81 PGUID 0 1255 0 0 0 0 0 f f r 16 0 0 0 0 0 t f f f _null_ ));
DESCR("");
-DATA(insert OID = 1259 ( pg_class PGNSP 83 PGUID 0 1259 0 0 0 0 f f r 24 0 0 0 0 0 t f f f _null_ ));
+DATA(insert OID = 1259 ( pg_class PGNSP 83 PGUID 0 1259 0 0 0 0 0 f f r 25 0 0 0 0 0 t f f f _null_ ));
DESCR("");
-DATA(insert OID = 1260 ( pg_shadow PGNSP 86 PGUID 0 1260 0 0 0 0 f t r 8 0 0 0 0 0 f f f f _null_ ));
+DATA(insert OID = 1260 ( pg_shadow PGNSP 86 PGUID 0 1260 1664 0 0 0 0 f t r 8 0 0 0 0 0 f f f f _null_ ));
DESCR("");
-DATA(insert OID = 1261 ( pg_group PGNSP 87 PGUID 0 1261 0 0 0 0 f t r 3 0 0 0 0 0 f f f f _null_ ));
+DATA(insert OID = 1261 ( pg_group PGNSP 87 PGUID 0 1261 1664 0 0 0 0 f t r 3 0 0 0 0 0 f f f f _null_ ));
DESCR("");
-DATA(insert OID = 1262 ( pg_database PGNSP 88 PGUID 0 1262 0 0 0 0 f t r 11 0 0 0 0 0 t f f f _null_ ));
+DATA(insert OID = 1262 ( pg_database PGNSP 88 PGUID 0 1262 1664 0 0 0 0 f t r 11 0 0 0 0 0 t f f f _null_ ));
DESCR("");
-DATA(insert OID = 376 ( pg_xactlock PGNSP 0 PGUID 0 0 0 0 0 0 f t s 1 0 0 0 0 0 f f f f _null_ ));
+DATA(insert OID = 1213 ( pg_tablespace PGNSP 90 PGUID 0 1213 1664 0 0 0 0 f t r 4 0 0 0 0 0 t f f f _null_ ));
+DESCR("");
+DATA(insert OID = 376 ( pg_xactlock PGNSP 0 PGUID 0 0 1664 0 0 0 0 f t s 1 0 0 0 0 0 f f f f _null_ ));
DESCR("");
#define RelOid_pg_type 1247
#define RelOid_pg_shadow 1260
#define RelOid_pg_group 1261
#define RelOid_pg_database 1262
+#define RelOid_pg_tablespace 1213
/* Xact lock pseudo-table */
#define XactLockTableId 376
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/pg_database.h,v 1.31 2004/02/10 01:55:26 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_database.h,v 1.32 2004/06/18 06:14:06 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
Oid datlastsysoid; /* highest OID to consider a system OID */
TransactionId datvacuumxid; /* all XIDs before this are vacuumed */
TransactionId datfrozenxid; /* all XIDs before this are frozen */
- text datpath; /* default database location (VAR LENGTH) */
+ Oid dattablespace; /* default table space for this DB */
text datconfig[1]; /* database-specific GUC (VAR LENGTH) */
aclitem datacl[1]; /* access permissions (VAR LENGTH) */
} FormData_pg_database;
#define Anum_pg_database_datlastsysoid 6
#define Anum_pg_database_datvacuumxid 7
#define Anum_pg_database_datfrozenxid 8
-#define Anum_pg_database_datpath 9
+#define Anum_pg_database_dattablespace 9
#define Anum_pg_database_datconfig 10
#define Anum_pg_database_datacl 11
-DATA(insert OID = 1 ( template1 PGUID ENCODING t t 0 0 0 "" _null_ _null_ ));
+DATA(insert OID = 1 ( template1 PGUID ENCODING t t 0 0 0 1663 _null_ _null_ ));
DESCR("Default template database");
#define TemplateDbOid 1
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/pg_namespace.h,v 1.11 2003/11/29 22:40:58 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_namespace.h,v 1.12 2004/06/18 06:14:06 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
{
NameData nspname;
int4 nspowner;
+ Oid nsptablespace; /* default table space for name space */
aclitem nspacl[1]; /* VARIABLE LENGTH FIELD */
} FormData_pg_namespace;
* ----------------
*/
-#define Natts_pg_namespace 3
+#define Natts_pg_namespace 4
#define Anum_pg_namespace_nspname 1
#define Anum_pg_namespace_nspowner 2
-#define Anum_pg_namespace_nspacl 3
+#define Anum_pg_namespace_nsptablespace 3
+#define Anum_pg_namespace_nspacl 4
/* ----------------
* ---------------
*/
-DATA(insert OID = 11 ( "pg_catalog" PGUID _null_ ));
+DATA(insert OID = 11 ( "pg_catalog" PGUID 0 _null_ ));
DESCR("System catalog schema");
#define PG_CATALOG_NAMESPACE 11
-DATA(insert OID = 99 ( "pg_toast" PGUID _null_ ));
+DATA(insert OID = 99 ( "pg_toast" PGUID 0 _null_ ));
DESCR("Reserved schema for TOAST tables");
#define PG_TOAST_NAMESPACE 99
-DATA(insert OID = 2200 ( "public" PGUID _null_ ));
+DATA(insert OID = 2200 ( "public" PGUID 0 _null_ ));
DESCR("Standard public schema");
#define PG_PUBLIC_NAMESPACE 2200
/*
* prototypes for functions in pg_namespace.c
*/
-extern Oid NamespaceCreate(const char *nspName, int32 ownerSysId);
+extern Oid NamespaceCreate(const char *nspName, int32 ownerSysId,
+ Oid nspTablespace);
#endif /* PG_NAMESPACE_H */
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * pg_tablespace.h
+ * definition of the system "tablespace" relation (pg_tablespace)
+ * along with the relation's initial contents.
+ *
+ *
+ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $PostgreSQL: pgsql/src/include/catalog/pg_tablespace.h,v 1.1 2004/06/18 06:14:06 tgl Exp $
+ *
+ * NOTES
+ * the genbki.sh script reads this file and generates .bki
+ * information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_TABLESPACE_H
+#define PG_TABLESPACE_H
+
+/* ----------------
+ * postgres.h contains the system type definitions and the
+ * CATALOG(), BOOTSTRAP and DATA() sugar words so this file
+ * can be read by both genbki.sh and the C compiler.
+ * ----------------
+ */
+
+/* ----------------
+ * pg_tablespace definition. cpp turns this into
+ * typedef struct FormData_pg_tablespace
+ * ----------------
+ */
+CATALOG(pg_tablespace) BOOTSTRAP BKI_SHARED_RELATION
+{
+ NameData spcname; /* tablespace name */
+ int4 spcowner; /* sysid of owner */
+ text spclocation; /* physical location (VAR LENGTH) */
+ aclitem spcacl[1]; /* access permissions (VAR LENGTH) */
+} FormData_pg_tablespace;
+
+/* ----------------
+ * Form_pg_tablespace corresponds to a pointer to a tuple with
+ * the format of pg_tablespace relation.
+ * ----------------
+ */
+typedef FormData_pg_tablespace *Form_pg_tablespace;
+
+/* ----------------
+ * compiler constants for pg_tablespace
+ * ----------------
+ */
+
+#define Natts_pg_tablespace 4
+#define Anum_pg_tablespace_spcname 1
+#define Anum_pg_tablespace_spcowner 2
+#define Anum_pg_tablespace_spclocation 3
+#define Anum_pg_tablespace_spcacl 4
+
+DATA(insert OID = 1663 ( default PGUID "" _null_ ));
+DATA(insert OID = 1664 ( global PGUID "" _null_ ));
+
+#define DEFAULTTABLESPACE_OID 1663
+#define GLOBALTABLESPACE_OID 1664
+
+#endif /* PG_TABLESPACE_H */
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.153 2004/06/06 19:07:01 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.154 2004/06/18 06:14:06 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
DESCR("array of INDEX_MAX_KEYS oids, used in system tables");
#define OIDVECTOROID 30
-DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c t \054 1247 0 record_in record_out record_recv record_send - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 75 ( pg_attribute PGNSP PGUID -1 f c t \054 1249 0 record_in record_out record_recv record_send - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 81 ( pg_proc PGNSP PGUID -1 f c t \054 1255 0 record_in record_out record_recv record_send - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 83 ( pg_class PGNSP PGUID -1 f c t \054 1259 0 record_in record_out record_recv record_send - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 86 ( pg_shadow PGNSP PGUID -1 f c t \054 1260 0 record_in record_out record_recv record_send - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 87 ( pg_group PGNSP PGUID -1 f c t \054 1261 0 record_in record_out record_recv record_send - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 88 ( pg_database PGNSP PGUID -1 f c t \054 1262 0 record_in record_out record_recv record_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c t \054 1247 0 record_in record_out record_recv record_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 75 ( pg_attribute PGNSP PGUID -1 f c t \054 1249 0 record_in record_out record_recv record_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 81 ( pg_proc PGNSP PGUID -1 f c t \054 1255 0 record_in record_out record_recv record_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 83 ( pg_class PGNSP PGUID -1 f c t \054 1259 0 record_in record_out record_recv record_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 86 ( pg_shadow PGNSP PGUID -1 f c t \054 1260 0 record_in record_out record_recv record_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 87 ( pg_group PGNSP PGUID -1 f c t \054 1261 0 record_in record_out record_recv record_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 88 ( pg_database PGNSP PGUID -1 f c t \054 1262 0 record_in record_out record_recv record_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 90 ( pg_tablespace PGNSP PGUID -1 f c t \054 1213 0 record_in record_out record_recv record_send - d x f 0 -1 0 _null_ _null_ ));
/* OIDS 100 - 199 */
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.57 2004/06/10 17:55:59 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.58 2004/06/18 06:14:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern void DefineIndex(RangeVar *heapRelation,
char *indexRelationName,
char *accessMethodName,
+ char *tableSpaceName,
List *attributeList,
Expr *predicate,
List *rangetable,
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * tablespace.h
+ * prototypes for tablespace.c.
+ *
+ *
+ * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $PostgreSQL: pgsql/src/include/commands/tablespace.h,v 1.1 2004/06/18 06:14:08 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef TABLESPACE_H
+#define TABLESPACE_H
+
+#include "nodes/parsenodes.h"
+
+extern void CreateTableSpace(CreateTableSpaceStmt *stmt);
+
+extern void DropTableSpace(DropTableSpaceStmt *stmt);
+
+extern void TablespaceCreateDbspace(Oid spcNode, Oid dbNode);
+
+extern Oid get_tablespace_oid(const char *tablespacename);
+
+extern char *get_tablespace_name(Oid spc_oid);
+
+#endif /* TABLESPACE_H */
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/miscadmin.h,v 1.162 2004/05/29 22:48:22 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/miscadmin.h,v 1.163 2004/06/18 06:14:10 tgl Exp $
*
* NOTES
* some of the information in this file should be moved to other files.
*/
extern DLLIMPORT Oid MyDatabaseId;
+extern DLLIMPORT Oid MyDatabaseTableSpace;
+
/*
* Date/Time Configuration
*
extern char *DatabasePath;
/* in utils/misc/database.c */
-extern void GetRawDatabaseInfo(const char *name, Oid *db_id, char *path);
+extern void GetRawDatabaseInfo(const char *name, Oid *db_id, Oid *db_tablespace);
/* now in utils/init/miscinit.c */
extern void SetDatabasePath(const char *path);
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.157 2004/06/09 19:08:18 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.158 2004/06/18 06:14:11 tgl Exp $
*
*-------------------------------------------------------------------------
*/
T_ExecuteStmt,
T_DeallocateStmt,
T_DeclareCursorStmt,
+ T_CreateTableSpaceStmt,
+ T_DropTableSpaceStmt,
T_AlterDbOwnerStmt,
T_A_Expr = 800,
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.258 2004/06/09 19:08:18 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.259 2004/06/18 06:14:11 tgl Exp $
*
*-------------------------------------------------------------------------
*/
OBJECT_SCHEMA,
OBJECT_SEQUENCE,
OBJECT_TABLE,
+ OBJECT_TABLESPACE,
OBJECT_TRIGGER,
OBJECT_TYPE,
OBJECT_USER,
NodeTag type;
char *schemaname; /* the name of the schema to create */
char *authid; /* the owner of the created schema */
+ char *tablespacename; /* default tablespace for schema, or NULL */
List *schemaElts; /* schema components (list of parsenodes) */
} CreateSchemaStmt;
ACL_OBJECT_DATABASE, /* database */
ACL_OBJECT_FUNCTION, /* function */
ACL_OBJECT_LANGUAGE, /* procedural language */
- ACL_OBJECT_NAMESPACE /* namespace */
+ ACL_OBJECT_NAMESPACE, /* namespace */
+ ACL_OBJECT_TABLESPACE /* tablespace */
} GrantObjectType;
typedef struct GrantStmt
List *constraints; /* constraints (list of Constraint nodes) */
ContainsOids hasoids; /* should it have OIDs? */
OnCommitAction oncommit; /* what do we do at COMMIT? */
+ char *tablespacename; /* table space to use, or NULL */
} CreateStmt;
/* ----------
bool skip_validation; /* skip validation of existing rows? */
} FkConstraint;
+
+/* ----------------------
+ * Create/Drop Table Space Statements
+ * ----------------------
+ */
+
+typedef struct CreateTableSpaceStmt
+{
+ NodeTag type;
+ char *tablespacename;
+ char *owner;
+ char *location;
+} CreateTableSpaceStmt;
+
+typedef struct DropTableSpaceStmt
+{
+ NodeTag type;
+ char *tablespacename;
+} DropTableSpaceStmt;
+
/* ----------------------
* Create/Drop TRIGGER Statements
* ----------------------
NodeTag type;
RangeVar *sequence; /* the sequence to create */
List *options;
+ char *tablespacename; /* tablespace, or NULL for default */
} CreateSeqStmt;
typedef struct AlterSeqStmt
char *idxname; /* name of new index, or NULL for default */
RangeVar *relation; /* relation to build index on */
char *accessMethod; /* name of access method (eg. btree) */
+ char *tableSpace; /* tablespace, or NULL to use parent's */
List *indexParams; /* a list of IndexElem */
Node *whereClause; /* qualification (partial-index predicate) */
List *rangetable; /* range table for qual and/or
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/storage/buf_internals.h,v 1.70 2004/04/21 18:06:29 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/storage/buf_internals.h,v 1.71 2004/06/18 06:14:13 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#define CLEAR_BUFFERTAG(a) \
( \
- (a).rnode.tblNode = InvalidOid, \
+ (a).rnode.spcNode = InvalidOid, \
+ (a).rnode.dbNode = InvalidOid, \
(a).rnode.relNode = InvalidOid, \
(a).blockNum = InvalidBlockNumber \
)
+/*-------------------------------------------------------------------------
+ *
+ * relfilenode.h
+ * Physical access information for relations.
+ *
+ *
+ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $PostgreSQL: pgsql/src/include/storage/relfilenode.h,v 1.9 2004/06/18 06:14:13 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
#ifndef RELFILENODE_H
#define RELFILENODE_H
/*
- * This is all what we need to know to find relation file.
- * tblNode is identificator of tablespace and because of
- * currently our tablespaces are equal to databases this is
- * database OID. relNode is currently relation OID on creation
- * but may be changed later if required. relNode is stored in
- * pg_class.relfilenode.
+ * RelFileNode must provide all that we need to know to physically access
+ * a relation.
+ *
+ * spcNode identifies the tablespace of the relation. It corresponds to
+ * pg_tablespace.oid.
+ *
+ * dbNode identifies the database of the relation. It is zero for
+ * "shared" relations (those common to all databases of a cluster).
+ * Nonzero dbNode values correspond to pg_database.oid.
+ *
+ * relNode identifies the specific relation. relNode corresponds to
+ * pg_class.relfilenode (NOT pg_class.oid, because we need to be able
+ * to assign new physical files to relations in some situations).
+ * Notice that relNode is only unique within a particular database.
+ *
+ * Note: spcNode must be GLOBALTABLESPACE_OID if and only if dbNode is
+ * zero. We support shared relations only in the "global" tablespace.
+ *
+ * Note: in pg_class we allow reltablespace == 0 to denote that the
+ * relation is stored in its database's "default" tablespace (as
+ * identified by pg_database.dattablespace). However this shorthand
+ * is NOT allowed in RelFileNode structs --- the real tablespace ID
+ * must be supplied when setting spcNode.
*/
typedef struct RelFileNode
{
- Oid tblNode; /* tablespace */
+ Oid spcNode; /* tablespace */
+ Oid dbNode; /* database */
Oid relNode; /* relation */
} RelFileNode;
+/*
+ * Note: RelFileNodeEquals compares relNode first since that is most likely
+ * to be different in two unequal RelFileNodes. It is probably redundant
+ * to compare spcNode if the other two fields are found equal, but do it
+ * anyway to be sure.
+ */
#define RelFileNodeEquals(node1, node2) \
((node1).relNode == (node2).relNode && \
- (node1).tblNode == (node2).tblNode)
+ (node1).dbNode == (node2).dbNode && \
+ (node1).spcNode == (node2).spcNode)
-#endif /* RELFILENODE_H */
+#endif /* RELFILENODE_H */
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.70 2004/06/01 21:49:22 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.71 2004/06/18 06:14:21 tgl Exp $
*
* NOTES
* An ACL array is simply an array of AclItems, representing the union
#define ACL_ALL_RIGHTS_FUNCTION (ACL_EXECUTE)
#define ACL_ALL_RIGHTS_LANGUAGE (ACL_USAGE)
#define ACL_ALL_RIGHTS_NAMESPACE (ACL_USAGE|ACL_CREATE)
+#define ACL_ALL_RIGHTS_TABLESPACE (ACL_CREATE)
/* operation codes for pg_*_aclmask */
typedef enum
ACL_KIND_NAMESPACE, /* pg_namespace */
ACL_KIND_OPCLASS, /* pg_opclass */
ACL_KIND_CONVERSION, /* pg_conversion */
+ ACL_KIND_TABLESPACE, /* pg_tablespace */
MAX_ACL_KIND /* MUST BE LAST */
} AclObjectKind;
AclMode mask, AclMaskHow how);
extern AclMode pg_namespace_aclmask(Oid nsp_oid, AclId userid,
AclMode mask, AclMaskHow how);
+extern AclMode pg_tablespace_aclmask(Oid spc_oid, AclId userid,
+ AclMode mask, AclMaskHow how);
extern AclResult pg_class_aclcheck(Oid table_oid, AclId userid, AclMode mode);
extern AclResult pg_database_aclcheck(Oid db_oid, AclId userid, AclMode mode);
extern AclResult pg_proc_aclcheck(Oid proc_oid, AclId userid, AclMode mode);
extern AclResult pg_language_aclcheck(Oid lang_oid, AclId userid, AclMode mode);
extern AclResult pg_namespace_aclcheck(Oid nsp_oid, AclId userid, AclMode mode);
+extern AclResult pg_tablespace_aclcheck(Oid spc_oid, AclId userid, AclMode mode);
extern void aclcheck_error(AclResult aclerr, AclObjectKind objectkind,
const char *objectname);
extern bool pg_oper_ownercheck(Oid oper_oid, AclId userid);
extern bool pg_proc_ownercheck(Oid proc_oid, AclId userid);
extern bool pg_namespace_ownercheck(Oid nsp_oid, AclId userid);
+extern bool pg_tablespace_ownercheck(Oid spc_oid, AclId userid);
extern bool pg_opclass_ownercheck(Oid opc_oid, AclId userid);
extern bool pg_database_ownercheck(Oid db_oid, AclId userid);
extern bool pg_conversion_ownercheck(Oid conv_oid, AclId userid);
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/utils/lsyscache.h,v 1.87 2004/06/06 00:41:28 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/lsyscache.h,v 1.88 2004/06/18 06:14:21 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern Oid get_system_catalog_relid(const char *catname);
extern char *get_rel_name(Oid relid);
extern Oid get_rel_namespace(Oid relid);
+extern Oid get_rel_tablespace(Oid relid);
extern Oid get_rel_type_id(Oid relid);
extern char get_rel_relkind(Oid relid);
extern bool get_typisdefined(Oid typid);
Datum *values, int nvalues,
float4 *numbers, int nnumbers);
extern char *get_namespace_name(Oid nspid);
+extern Oid get_namespace_tablespace(Oid nspid);
extern int32 get_usesysid(const char *username);
#define is_array_type(typid) (get_element_type(typid) != InvalidOid)
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/utils/relcache.h,v 1.39 2004/02/10 01:55:27 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/relcache.h,v 1.40 2004/06/18 06:14:21 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern Relation RelationBuildLocalRelation(const char *relname,
Oid relnamespace,
TupleDesc tupDesc,
- Oid relid, Oid dbid,
- RelFileNode rnode,
+ Oid relid,
+ Oid reltablespace,
+ bool shared_relation,
bool nailit);
/*
#
#
# IDENTIFICATION
-# $PostgreSQL: pgsql/src/test/regress/GNUmakefile,v 1.46 2004/03/03 04:22:47 momjian Exp $
+# $PostgreSQL: pgsql/src/test/regress/GNUmakefile,v 1.47 2004/06/18 06:14:25 tgl Exp $
#
#-------------------------------------------------------------------------
# Build test input and expected files
-file_list := copy create_function_1 create_function_2 misc constraints
+file_list := copy create_function_1 create_function_2 misc constraints tablespace
input_files := $(foreach file, $(file_list), sql/$(file).sql)
output_files := $(foreach file, $(file_list), expected/$(file).out)
abs_builddir := $(shell pwd -W)
endif
+testtablespace := $(abs_builddir)/testtablespace
+
define sed-command
sed -e 's,@abs_srcdir@,$(abs_srcdir),g' \
-e 's,@abs_builddir@,$(abs_builddir),g' \
+ -e 's,@testtablespace@,$(testtablespace),g' \
-e 's/@DLSUFFIX@/$(DLSUFFIX)/g' $< >$@
endef
##
check: all
+ -rm -rf ./testtablespace
+ mkdir ./testtablespace
$(SHELL) ./pg_regress --temp-install --top-builddir=$(top_builddir) --schedule=$(srcdir)/parallel_schedule --multibyte=$(MULTIBYTE) $(MAXCONNOPT)
installcheck: all
+ -rm -rf ./testtablespace
+ mkdir ./testtablespace
$(SHELL) ./pg_regress --schedule=$(srcdir)/serial_schedule --multibyte=$(MULTIBYTE)
$(MAKE) -C $(contribdir)/spi clean
rm -f $(output_files) $(input_files) $(DLOBJS) regress.o pg_regress
# things created by various check targets
+ rm -rf testtablespace
rm -rf results tmp_check log
rm -f regression.diffs regression.out regress.out run_check.out
ifeq ($(PORTNAME), cygwin)
pg_rewrite | t
pg_shadow | t
pg_statistic | t
+ pg_tablespace | t
pg_trigger | t
pg_type | t
road | t
shighway | t
tenk1 | t
tenk2 | t
-(52 rows)
+(53 rows)
--
-- another sanity check: every system catalog that has OIDs should have
--- /dev/null
+-- create a tablespace we can use
+CREATE TABLESPACE testspace LOCATION '@testtablespace@';
+
+-- create a schema in the tablespace
+CREATE SCHEMA testschema TABLESPACE testspace;
+
+-- sanity check
+SELECT nspname, spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_namespace n
+ where n.nsptablespace = t.oid and n.nspname = 'testschema';
+
+-- try a table
+CREATE TABLE testschema.foo (i int);
+SELECT relname, spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c
+ where c.reltablespace = t.oid AND c.relname = 'foo';
+
+INSERT INTO testschema.foo VALUES(1);
+INSERT INTO testschema.foo VALUES(2);
+
+-- index
+CREATE INDEX foo_idx on testschema.foo(i);
+SELECT relname, spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c
+ where c.reltablespace = t.oid AND c.relname = 'foo_idx';
+
+-- Will fail with bad path
+CREATE TABLESPACE badspace LOCATION '/no/such/location';
+
+-- No such tablespace
+CREATE TABLE bar (i int) TABLESPACE nosuchspace;
+
+-- Fail, not empty
+DROP TABLESPACE testspace;
+
+DROP SCHEMA testschema CASCADE;
+
+-- Should succeed
+DROP TABLESPACE testspace;
--- /dev/null
+-- create a tablespace we can use
+CREATE TABLESPACE testspace LOCATION '@testtablespace@';
+-- create a schema in the tablespace
+CREATE SCHEMA testschema TABLESPACE testspace;
+-- sanity check
+SELECT nspname, spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_namespace n
+ where n.nsptablespace = t.oid and n.nspname = 'testschema';
+ nspname | spcname
+------------+-----------
+ testschema | testspace
+(1 row)
+
+-- try a table
+CREATE TABLE testschema.foo (i int);
+SELECT relname, spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c
+ where c.reltablespace = t.oid AND c.relname = 'foo';
+ relname | spcname
+---------+-----------
+ foo | testspace
+(1 row)
+
+INSERT INTO testschema.foo VALUES(1);
+INSERT INTO testschema.foo VALUES(2);
+-- index
+CREATE INDEX foo_idx on testschema.foo(i);
+SELECT relname, spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c
+ where c.reltablespace = t.oid AND c.relname = 'foo_idx';
+ relname | spcname
+---------+-----------
+ foo_idx | testspace
+(1 row)
+
+-- Will fail with bad path
+CREATE TABLESPACE badspace LOCATION '/no/such/location';
+ERROR: could not set permissions on directory "/no/such/location": No such file or directory
+-- No such tablespace
+CREATE TABLE bar (i int) TABLESPACE nosuchspace;
+ERROR: tablespace "nosuchspace" does not exist
+-- Fail, not empty
+DROP TABLESPACE testspace;
+ERROR: tablespace "testspace" is not empty
+DROP SCHEMA testschema CASCADE;
+NOTICE: drop cascades to table testschema.foo
+-- Should succeed
+DROP TABLESPACE testspace;
# Depends on point, lseg, box, path, polygon and circle
test: geometry
# Depends on interval, timetz, timestamp, timestamptz, reltime and abstime
-test: horology
+test: horology
# ----------
# These four each depend on the previous one
# run stats by itself because its delay may be insufficient under heavy load
test: stats
+
+# run tablespace by itself
+test: tablespace
-# $PostgreSQL: pgsql/src/test/regress/serial_schedule,v 1.25 2004/06/06 21:20:46 tgl Exp $
+# $PostgreSQL: pgsql/src/test/regress/serial_schedule,v 1.26 2004/06/18 06:14:25 tgl Exp $
# This should probably be in an order similar to parallel_schedule.
test: boolean
test: char
test: polymorphism
test: rowtypes
test: stats
+test: tablespace