Extensions, this time with a patch

Started by Dimitri Fontaineover 15 years ago128 messages
#1Dimitri Fontaine
dimitri@2ndQuadrant.fr
2 attachment(s)

Hi,

Please find attached a WIP patch for extension's pg_dump support in
PostgreSQL, following design decisions that we've outlined earlier at
this year's and last year's PGCon developer meetings.

What's in the patch?

An extension is a new SQL object with a catalog and two commands to
manage them (reserved to superuser):

CREATE EXTENSION <extension> ;
DROP EXTENSION [IF EXISTS] <extension> [ RESTRICT | CASCADE ];

The first command (create) will parse the "control" file from a fixed
place (`pg_control --sharedir`/contrib/<extension>.control) and insert
an entry in the pg_extension catalog. That gives us an Oid which we can
use in pg_depend. Once we have it, the command will execute the SQL
script at same/place/<extension>.sql and recordDependencyOn lots of
objects created here (not all of them, because it does not appear
necessary to).

The drop command will happily remove the extension's object and all its
"direct" dependencies. That's what's been installed by the script. If
there exists some object not created by the script and that depends on
the extension, CASCADE is needed (e.g. create table foo(kv hstore);).

With that tracking in place, pg_dump is now able to issue a single
command per extension, the CREATE command. All dependent objects are
filtered out of the dump in the pg_dump queries. There's a nasty corner
case here with schema, see pg_dump support.

Rough support for a new \dx command in psql is implemented, too.

PGXS has been adapted so that it produces automatically the control file
should it be missing, using $(MAJORVERSION) as the contrib's
version. That means that right after installing contrib, it's possible
to 'create extension <any of them>' automatically (thanks to a 2 lines
file).

Open Items :

- cfparser

To parse the control/registry file, I've piggybacked on the recovery
configuration file parsing. The function to parse each line was not
exported from xlog, I've made a new src/backend/utils/misc/cfparser.c
file and placed parseRecoveryCommandFileLine() in there.

Please find attached a separate patch implementing just that, in case
you want to review/apply that on its own. The main patch do contains
the change too.

- User Documentation. Where in the manual do I write it?

- Naming of the "control" file, <extension>.{control,registry,install}

Currently, inspired by debian/control, the file is called .control,
but maybe we want to choose something that the user can guess about
the purpose before reading the fine manual. I don't like .install
because that's not what it is. .registry?

- Handling of custom_variable_classes

The attached patch has a column to store the information but makes no
use whatsoever of it, the goal would be to append classes from the
extension's registry file and possibly setup the default values
there.

As I don't think this part has been agreed before, I send the patch
without the code for that, even if I suspect it would be a rather
short addition, and very worthwile too.

- User data tables

An extension can install plain relations, and that's fine. The
problem is when the data in there are changed after installing.
Because the name of the game here is to exclude the table from the
dumps, of course the data will not be in there.

The solution would be to offer extension's author a way to 'flag'
some tables as worthy of dumping, I think marking the dependency as
DEPENDENCY_NORMAL rather then DEPENDENCY_INTERNAL will do the trick:

SELECT pg_extension_flag_dump(oid);

- Extension Upgrading

Should this be done by means of 'create extension' or some other
command, like 'alter extension foo upgrade'? The command would run
the SQL script again, which would be responsible for any steps the
extension author might find necessary to run.

- Bugs to fix

There's at least one where drop extension leaves things behind,
although it uses performDeletion(). The dependencies are fine enough
so that the leftover objects are not part of the dump done before to
drop, though. I didn't investigate it at all, this mail is in the
"discuss early" tradition of the project.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

Attachments:

extension.v0.patchtext/x-patchDownload
*** a/src/backend/access/transam/xlog.c
--- b/src/backend/access/transam/xlog.c
***************
*** 55,60 ****
--- 55,61 ----
  #include "utils/guc.h"
  #include "utils/ps_status.h"
  #include "utils/relmapper.h"
+ #include "utils/cfparser.h"
  #include "pg_trace.h"
  
  
***************
*** 5018,5117 **** str_time(pg_time_t tnow)
  }
  
  /*
-  * Parse one line from recovery.conf. 'cmdline' is the raw line from the
-  * file. If the line is parsed successfully, returns true, false indicates
-  * syntax error. On success, *key_p and *value_p are set to the parameter
-  * name and value on the line, respectively. If the line is an empty line,
-  * consisting entirely of whitespace and comments, function returns true
-  * and *keyp_p and *value_p are set to NULL.
-  *
-  * The pointers returned in *key_p and *value_p point to an internal buffer
-  * that is valid only until the next call of parseRecoveryCommandFile().
-  */
- static bool
- parseRecoveryCommandFileLine(char *cmdline, char **key_p, char **value_p)
- {
- 	char	   *ptr;
- 	char	   *bufp;
- 	char	   *key;
- 	char	   *value;
- 	static char *buf = NULL;
- 
- 	*key_p = *value_p = NULL;
- 
- 	/*
- 	 * Allocate the buffer on first use. It's used to hold both the parameter
- 	 * name and value.
- 	 */
- 	if (buf == NULL)
- 		buf = malloc(MAXPGPATH + 1);
- 	bufp = buf;
- 
- 	/* Skip any whitespace at the beginning of line */
- 	for (ptr = cmdline; *ptr; ptr++)
- 	{
- 		if (!isspace((unsigned char) *ptr))
- 			break;
- 	}
- 	/* Ignore empty lines */
- 	if (*ptr == '\0' || *ptr == '#')
- 		return true;
- 
- 	/* Read the parameter name */
- 	key = bufp;
- 	while (*ptr && !isspace((unsigned char) *ptr) &&
- 		   *ptr != '=' && *ptr != '\'')
- 		*(bufp++) = *(ptr++);
- 	*(bufp++) = '\0';
- 
- 	/* Skip to the beginning quote of the parameter value */
- 	ptr = strchr(ptr, '\'');
- 	if (!ptr)
- 		return false;
- 	ptr++;
- 
- 	/* Read the parameter value to *bufp. Collapse any '' escapes as we go. */
- 	value = bufp;
- 	for (;;)
- 	{
- 		if (*ptr == '\'')
- 		{
- 			ptr++;
- 			if (*ptr == '\'')
- 				*(bufp++) = '\'';
- 			else
- 			{
- 				/* end of parameter */
- 				*bufp = '\0';
- 				break;
- 			}
- 		}
- 		else if (*ptr == '\0')
- 			return false;		/* unterminated quoted string */
- 		else
- 			*(bufp++) = *ptr;
- 
- 		ptr++;
- 	}
- 	*(bufp++) = '\0';
- 
- 	/* Check that there's no garbage after the value */
- 	while (*ptr)
- 	{
- 		if (*ptr == '#')
- 			break;
- 		if (!isspace((unsigned char) *ptr))
- 			return false;
- 		ptr++;
- 	}
- 
- 	/* Success! */
- 	*key_p = key;
- 	*value_p = value;
- 	return true;
- }
- 
- /*
   * See if there is a recovery command file (recovery.conf), and if so
   * read in parameters for archive recovery and XLOG streaming.
   *
--- 5019,5024 ----
*** a/src/backend/catalog/Makefile
--- b/src/backend/catalog/Makefile
***************
*** 39,44 **** POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
--- 39,45 ----
  	pg_ts_parser.h pg_ts_template.h \
  	pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
  	pg_default_acl.h pg_seclabel.h \
+ 	pg_extension.h \
  	toasting.h indexing.h \
      )
  
*** a/src/backend/catalog/dependency.c
--- b/src/backend/catalog/dependency.c
***************
*** 34,39 ****
--- 34,40 ----
  #include "catalog/pg_database.h"
  #include "catalog/pg_default_acl.h"
  #include "catalog/pg_depend.h"
+ #include "catalog/pg_extension.h"
  #include "catalog/pg_foreign_data_wrapper.h"
  #include "catalog/pg_foreign_server.h"
  #include "catalog/pg_language.h"
***************
*** 55,60 ****
--- 56,62 ----
  #include "commands/comment.h"
  #include "commands/dbcommands.h"
  #include "commands/defrem.h"
+ #include "commands/extension.h"
  #include "commands/proclang.h"
  #include "commands/schemacmds.h"
  #include "commands/seclabel.h"
***************
*** 152,158 **** static const Oid object_classes[MAX_OCLASS] = {
  	ForeignDataWrapperRelationId,		/* OCLASS_FDW */
  	ForeignServerRelationId,	/* OCLASS_FOREIGN_SERVER */
  	UserMappingRelationId,		/* OCLASS_USER_MAPPING */
! 	DefaultAclRelationId		/* OCLASS_DEFACL */
  };
  
  
--- 154,161 ----
  	ForeignDataWrapperRelationId,		/* OCLASS_FDW */
  	ForeignServerRelationId,	/* OCLASS_FOREIGN_SERVER */
  	UserMappingRelationId,		/* OCLASS_USER_MAPPING */
! 	DefaultAclRelationId,		/* OCLASS_DEFACL */
! 	ExtensionRelationId 		/* OCLASS_EXTENSION */
  };
  
  
***************
*** 1148,1153 **** doDeletion(const ObjectAddress *object)
--- 1151,1160 ----
  			RemoveUserMappingById(object->objectId);
  			break;
  
+ 		case OCLASS_EXTENSION:
+ 			RemoveExtensionById(object->objectId);
+ 			break;
+ 
  		case OCLASS_DEFACL:
  			RemoveDefaultACLById(object->objectId);
  			break;
***************
*** 2082,2087 **** getObjectClass(const ObjectAddress *object)
--- 2089,2098 ----
  		case DefaultAclRelationId:
  			Assert(object->objectSubId == 0);
  			return OCLASS_DEFACL;
+ 
+ 		case ExtensionRelationId:
+ 			Assert(object->objectSubId == 0);
+ 			return OCLASS_EXTENSION;
  	}
  
  	/* shouldn't get here */
***************
*** 2563,2568 **** getObjectDescription(const ObjectAddress *object)
--- 2574,2591 ----
  				break;
  			}
  
+ 		case OCLASS_EXTENSION:
+ 			{
+ 				char	   *extension;
+ 
+ 				extension = get_extension_name(object->objectId);
+ 				if (!extension)
+ 					elog(ERROR, "cache lookup failed for extension %u",
+ 						 object->objectId);
+ 				appendStringInfo(&buffer, _("extension %s"), extension);
+ 				break;
+ 			}
+ 
  		case OCLASS_TBLSPACE:
  			{
  				char	   *tblspace;
*** a/src/backend/catalog/heap.c
--- b/src/backend/catalog/heap.c
***************
*** 49,54 ****
--- 49,55 ----
  #include "catalog/pg_type.h"
  #include "catalog/pg_type_fn.h"
  #include "catalog/storage.h"
+ #include "commands/extension.h"
  #include "commands/tablecmds.h"
  #include "commands/typecmds.h"
  #include "miscadmin.h"
***************
*** 1139,1145 **** heap_create_with_catalog(const char *relname,
  	/*
  	 * Make a dependency link to force the relation to be deleted if its
  	 * namespace is.  Also make a dependency link to its owner, as well as
! 	 * dependencies for any roles mentioned in the default ACL.
  	 *
  	 * For composite types, these dependencies are tracked for the pg_type
  	 * entry, so we needn't record them here.  Likewise, TOAST tables don't
--- 1140,1147 ----
  	/*
  	 * Make a dependency link to force the relation to be deleted if its
  	 * namespace is.  Also make a dependency link to its owner, as well as
! 	 * dependencies for any roles mentioned in the default ACL. Record a
! 	 * dependancy to the extension we're part of if any.
  	 *
  	 * For composite types, these dependencies are tracked for the pg_type
  	 * entry, so we needn't record them here.  Likewise, TOAST tables don't
***************
*** 1186,1191 **** heap_create_with_catalog(const char *relname,
--- 1188,1198 ----
  								  0, NULL,
  								  nnewmembers, newmembers);
  		}
+ 
+ 		if (create_extension != NULL )
+ 		{
+ 			recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ 		}
  	}
  
  	/*
*** a/src/backend/catalog/objectaddress.c
--- b/src/backend/catalog/objectaddress.c
***************
*** 28,33 ****
--- 28,34 ----
  #include "catalog/pg_constraint.h"
  #include "catalog/pg_conversion.h"
  #include "catalog/pg_database.h"
+ #include "catalog/pg_extension.h"
  #include "catalog/pg_language.h"
  #include "catalog/pg_largeobject.h"
  #include "catalog/pg_largeobject_metadata.h"
***************
*** 47,52 ****
--- 48,54 ----
  #include "commands/dbcommands.h"
  #include "commands/defrem.h"
  #include "commands/proclang.h"
+ #include "commands/extension.h"
  #include "commands/tablespace.h"
  #include "commands/trigger.h"
  #include "nodes/makefuncs.h"
***************
*** 128,133 **** get_object_address(ObjectType objtype, List *objname, List *objargs,
--- 130,136 ----
  			address = get_object_address_relobject(objtype, objname, &relation);
  			break;
  		case OBJECT_DATABASE:
+ 		case OBJECT_EXTENSION:
  		case OBJECT_TABLESPACE:
  		case OBJECT_ROLE:
  		case OBJECT_SCHEMA:
***************
*** 266,271 **** get_object_address_unqualified(ObjectType objtype, List *qualname)
--- 269,277 ----
  			case OBJECT_DATABASE:
  				msg = gettext_noop("database name cannot be qualified");
  				break;
+ 			case OBJECT_EXTENSION:
+ 				msg = gettext_noop("extension name cannot be qualified");
+ 				break;
  			case OBJECT_TABLESPACE:
  				msg = gettext_noop("tablespace name cannot be qualified");
  				break;
***************
*** 298,303 **** get_object_address_unqualified(ObjectType objtype, List *qualname)
--- 304,314 ----
  			address.objectId = get_database_oid(name, false);
  			address.objectSubId = 0;
  			break;
+ 		case OBJECT_EXTENSION:
+ 			address.classId = ExtensionRelationId;
+ 			address.objectId = get_extension_oid(name, false);
+ 			address.objectSubId = 0;
+ 			break;
  		case OBJECT_TABLESPACE:
  			address.classId = TableSpaceRelationId;
  			address.objectId = get_tablespace_oid(name, false);
***************
*** 635,640 **** object_exists(ObjectAddress address)
--- 646,654 ----
  		case TSConfigRelationId:
  			cache = TSCONFIGOID;
  			break;
+ 		case ExtensionRelationId:
+ 			indexoid = ExtensionOidIndexId;
+ 			break;
  		default:
  			elog(ERROR, "unrecognized classid: %u", address.classId);
  	}
*** a/src/backend/catalog/pg_aggregate.c
--- b/src/backend/catalog/pg_aggregate.c
***************
*** 32,37 ****
--- 32,38 ----
  #include "utils/lsyscache.h"
  #include "utils/rel.h"
  #include "utils/syscache.h"
+ #include "commands/extension.h"
  
  
  static Oid lookup_agg_function(List *fnName, int nargs, Oid *input_types,
***************
*** 293,298 **** AggregateCreate(const char *aggName,
--- 294,305 ----
  		referenced.objectSubId = 0;
  		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
  	}
+ 
+ 	/* Depends on currently installed extension, if any */
+ 	if (create_extension != NULL )
+ 	{
+ 		recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ 	}
  }
  
  /*
*** a/src/backend/catalog/pg_conversion.c
--- b/src/backend/catalog/pg_conversion.c
***************
*** 30,35 ****
--- 30,36 ----
  #include "utils/rel.h"
  #include "utils/syscache.h"
  #include "utils/tqual.h"
+ #include "commands/extension.h"
  
  /*
   * ConversionCreate
***************
*** 131,136 **** ConversionCreate(const char *conname, Oid connamespace,
--- 132,143 ----
  	recordDependencyOnOwner(ConversionRelationId, HeapTupleGetOid(tup),
  							conowner);
  
+ 	/* Depends on currently installed extension, if any */
+ 	if (create_extension != NULL )
+ 	{
+ 		recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ 	}
+ 
  	heap_freetuple(tup);
  	heap_close(rel, RowExclusiveLock);
  
*** a/src/backend/catalog/pg_namespace.c
--- b/src/backend/catalog/pg_namespace.c
***************
*** 21,26 ****
--- 21,27 ----
  #include "utils/builtins.h"
  #include "utils/rel.h"
  #include "utils/syscache.h"
+ #include "commands/extension.h"
  
  
  /* ----------------
***************
*** 75,79 **** NamespaceCreate(const char *nspName, Oid ownerId)
--- 76,92 ----
  	/* Record dependency on owner */
  	recordDependencyOnOwner(NamespaceRelationId, nspoid, ownerId);
  
+ 	/* Record dependency on extension, if we're in a CREATE EXTENSION */
+ 	if (create_extension != NULL )
+ 	{
+ 		ObjectAddress myself;
+ 
+ 		myself.classId = NamespaceRelationId;
+ 		myself.objectId = nspoid;
+ 		myself.objectSubId = 0;
+ 
+ 		recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ 	}
+ 
  	return nspoid;
  }
*** a/src/backend/catalog/pg_operator.c
--- b/src/backend/catalog/pg_operator.c
***************
*** 33,38 ****
--- 33,39 ----
  #include "utils/lsyscache.h"
  #include "utils/rel.h"
  #include "utils/syscache.h"
+ #include "commands/extension.h"
  
  
  static Oid OperatorGet(const char *operatorName,
***************
*** 846,849 **** makeOperatorDependencies(HeapTuple tuple)
--- 847,856 ----
  	/* Dependency on owner */
  	recordDependencyOnOwner(OperatorRelationId, HeapTupleGetOid(tuple),
  							oper->oprowner);
+ 
+ 	/* Dependency on extension */
+ 	if (create_extension != NULL )
+ 	{
+ 		recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ 	}
  }
*** a/src/backend/catalog/pg_proc.c
--- b/src/backend/catalog/pg_proc.c
***************
*** 35,40 ****
--- 35,41 ----
  #include "utils/builtins.h"
  #include "utils/lsyscache.h"
  #include "utils/syscache.h"
+ #include "commands/extension.h"
  
  
  Datum		fmgr_internal_validator(PG_FUNCTION_ARGS);
***************
*** 614,619 **** ProcedureCreate(const char *procedureName,
--- 615,626 ----
  							  nnewmembers, newmembers);
  	}
  
+ 	/* dependency on extension */
+ 	if (create_extension != NULL )
+ 	{
+ 		recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ 	}
+ 
  	heap_freetuple(tup);
  
  	heap_close(rel, RowExclusiveLock);
*** a/src/backend/catalog/pg_type.c
--- b/src/backend/catalog/pg_type.c
***************
*** 31,36 ****
--- 31,38 ----
  #include "utils/lsyscache.h"
  #include "utils/rel.h"
  #include "utils/syscache.h"
+ #include "commands/extension.h"
+ 
  
  Oid			binary_upgrade_next_pg_type_oid = InvalidOid;
  
***************
*** 625,630 **** GenerateTypeDependencies(Oid typeNamespace,
--- 627,638 ----
  	/* Normal dependency on the default expression. */
  	if (defaultExpr)
  		recordDependencyOnExpr(&myself, defaultExpr, NIL, DEPENDENCY_NORMAL);
+ 
+ 	/* dependency on extension */
+ 	if (create_extension != NULL )
+ 	{
+ 		recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ 	}
  }
  
  /*
*** a/src/backend/commands/Makefile
--- b/src/backend/commands/Makefile
***************
*** 14,20 **** include $(top_builddir)/src/Makefile.global
  
  OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o  \
  	constraint.o conversioncmds.o copy.o \
! 	dbcommands.o define.o discard.o explain.o foreigncmds.o functioncmds.o \
  	indexcmds.o lockcmds.o operatorcmds.o opclasscmds.o \
  	portalcmds.o prepare.o proclang.o \
  	schemacmds.o seclabel.o sequence.o tablecmds.o tablespace.o trigger.o \
--- 14,21 ----
  
  OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o  \
  	constraint.o conversioncmds.o copy.o \
! 	dbcommands.o define.o discard.o \
! 	extension.o explain.o foreigncmds.o functioncmds.o \
  	indexcmds.o lockcmds.o operatorcmds.o opclasscmds.o \
  	portalcmds.o prepare.o proclang.o \
  	schemacmds.o seclabel.o sequence.o tablecmds.o tablespace.o trigger.o \
*** a/src/backend/commands/comment.c
--- b/src/backend/commands/comment.c
***************
*** 142,147 **** CommentObject(CommentStmt *stmt)
--- 142,153 ----
  						(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  					 errmsg("must be superuser to comment on procedural language")));
  			break;
+ 		case OBJECT_EXTENSION:
+ 			if (!superuser())
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 					 errmsg("must be superuser to comment on extension")));
+ 			break;
  		case OBJECT_OPCLASS:
  			if (!pg_opclass_ownercheck(address.objectId, GetUserId()))
  				aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS,
*** /dev/null
--- b/src/backend/commands/extension.c
***************
*** 0 ****
--- 1,456 ----
+ /*-------------------------------------------------------------------------
+  *
+  * extension.c
+  *	  Commands to manipulate extensions
+  *
+  * Extensions in PostgreSQL allow user defined behavior plugged in at
+  * runtime. They will typically create SQL objects that you want to avoid
+  * dumping, issuing a single CREATE EXTENSION foo; command instead.
+  *
+  * All we need internally to manage an extension is an OID so that the
+  * dependant objects can get associated with it. An extension is created by
+  * populating the pg_extension catalog from a "control" file, containing
+  * some parameters.
+  *
+  * The extension control file format is the most simple name = value, we
+  * don't need anything more there. The SQL file to execute commands from is
+  * hardcoded to `pg_config --sharedir`/<extension>.install.sql.
+  *
+  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  *
+  * IDENTIFICATION
+  *	  src/backend/commands/extension.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include <unistd.h>
+ #include <dirent.h>
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ 
+ #include "nodes/parsenodes.h"
+ #include "miscadmin.h"
+ #include "catalog/pg_extension.h"
+ #include "commands/extension.h"
+ #include "storage/fd.h"
+ #include "utils/builtins.h"
+ #include "utils/cfparser.h"
+ #include "utils/rel.h"
+ #include "access/heapam.h"
+ #include "catalog/indexing.h"
+ #include "catalog/dependency.h"
+ #include "utils/memutils.h"
+ #include "utils/syscache.h"
+ #include "utils/tqual.h"
+ #include "utils/builtins.h"
+ #include "utils/fmgroids.h"
+ #include "access/sysattr.h"
+ 
+ /*
+  * See commands/extension.h for details.
+  */
+ ObjectAddress *create_extension = NULL;
+ 
+ char *
+ get_extension_install_filename(const char *extname)
+ {
+ 	char		sharepath[MAXPGPATH];
+ 	char	   *result;
+ 
+ 	get_share_path(my_exec_path, sharepath);
+ 	result = palloc(MAXPGPATH);
+ 	snprintf(result, MAXPGPATH, "%s/contrib/%s.sql", sharepath, extname);
+ 
+ 	return result;
+ }
+ 
+ char *
+ get_extension_control_filename(const char *extname)
+ {
+ 	char		sharepath[MAXPGPATH];
+ 	char	   *result;
+ 
+ 	get_share_path(my_exec_path, sharepath);
+ 	result = palloc(MAXPGPATH);
+ 	snprintf(result, MAXPGPATH, "%s/contrib/%s.control", sharepath, extname);
+ 
+ 	return result;
+ }
+ 
+ /*
+  * The control file is supposed to be very short, half a dozen lines, and
+  * reading it is only allowed to superuser, so we don't even try to minimize
+  * memory allocation risks here.
+  *
+  * Also note that only happens in CREATE EXTENSION.
+  */
+ ExtensionControlFile *
+ read_extension_control_file(const char *filename)
+ {
+ 	int64       fsize = -1;
+ 	struct stat fst;
+ 	FILE	   *file;
+ 	char		line[MAXPGPATH];
+ 	bool		syntaxError = false;
+ 	ExtensionControlFile *control =
+ 		(ExtensionControlFile *)palloc(sizeof(ExtensionControlFile));
+ 
+ 	/*
+ 	 * Stat the file to get its size, then read its content into memory so
+ 	 * that we can "parse" it.
+ 	 */
+ 	if (stat(filename, &fst) < 0)
+ 		ereport(ERROR,
+ 				(errcode_for_file_access(),
+ 				 errmsg("could not stat file \"%s\": %m", filename)));
+ 
+ 	fsize = Int64GetDatum((int64) fst.st_size);
+ 
+ 	if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
+ 		ereport(ERROR,
+ 				(errcode_for_file_access(),
+ 				 errmsg("could not open file \"%s\" for reading: %m",
+ 						filename)));
+ 
+ 	if (ferror(file))
+ 		ereport(ERROR,
+ 				(errcode_for_file_access(),
+ 				 errmsg("could not read file \"%s\": %m", filename)));
+ 
+ 	/*
+ 	 * Prepare our ExtensionControlFile structure with default values, then
+ 	 * "parse" the control file.
+ 	 *
+ 	 * The parsing uses directly the code for parsing Recovery Commands as
+ 	 * we're abusing the exact same format here.
+ 	 */
+ 	control->name = NULL;
+ 	control->version = NULL;
+ 	control->custom_class = NULL;
+ 
+ 	while (fgets(line, sizeof(line), file) != NULL)
+ 	{
+ 		char	   *tok1;
+ 		char	   *tok2;
+ 
+ 		if (!parseRecoveryCommandFileLine(line, &tok1, &tok2))
+ 		{
+ 			syntaxError = true;
+ 			break;
+ 		}
+ 		if (tok1 == NULL)
+ 			continue;
+ 
+ 		if (strcmp(tok1, "name") == 0)
+ 		{
+ 			control->name = pstrdup(tok2);
+ 		}
+ 		if (strcmp(tok1, "version") == 0)
+ 		{
+ 			control->version = pstrdup(tok2);
+ 		}
+ 		if (strcmp(tok1, "custom_class") == 0)
+ 		{
+ 			control->custom_class = pstrdup(tok2);
+ 		}
+ 	}
+ 
+ 	if( control->name == NULL )
+ 		ereport(FATAL,
+ 				(errmsg("Missing a name parameter in file: %s", filename)));
+ 
+ 	if (syntaxError)
+ 		ereport(FATAL,
+ 				(errmsg("syntax error in extension control file: %s", line),
+ 				 errhint("Lines should have the format parameter = 'value'.")));
+ 
+ 	FreeFile(file);
+ 	return control;
+ }
+ 
+ /*
+  * Create an extension
+  *
+  * Only superusers can create an extension.
+  */
+ void
+ CreateExtension(CreateExtensionStmt *stmt)
+ {
+ 	ExtensionControlFile *control;
+ 	Relation	rel;
+ 	Datum		values[Natts_pg_extension];
+ 	bool		nulls[Natts_pg_extension];
+ 	HeapTuple	tuple;
+ 	Oid			extensionoid;
+ 	text       *filename;
+ 
+ 	/* Must be super user */
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 				 errmsg("permission denied to create extension \"%s\"",
+ 						stmt->extname),
+ 				 errhint("Must be superuser to create an extension.")));
+ 
+ 	if( InvalidOid != get_extension_oid(stmt->extname, true) )
+ 	{
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_DUPLICATE_OBJECT),
+ 				 errmsg("extension \"%s\" already exists", stmt->extname)));
+ 	}
+ 
+ 	control = read_extension_control_file(
+ 		get_extension_control_filename(stmt->extname));
+ 
+ 	/*
+ 	 * Insert tuple into pg_extension.
+ 	 *
+ 	 * We use ExclusiveLock here so that there's only CREATE EXTENSION
+ 	 * possible at any given time. It's necessary because of the way the
+ 	 * global variable create_extension is handled, and it seems like a
+ 	 * restriction that's easy to live with.
+ 	 */
+ 	rel = heap_open(ExtensionRelationId, ExclusiveLock);
+ 
+ 	memset(values, 0, sizeof(values));
+ 	MemSet(nulls, false, sizeof(nulls));
+ 
+ 	values[Anum_pg_extension_extname - 1] =
+ 		DirectFunctionCall1(namein, CStringGetDatum(control->name));
+ 
+ 	if( control->version == NULL )
+ 		nulls[Anum_pg_extension_extversion - 1] = true;
+ 	else
+ 		values[Anum_pg_extension_extversion - 1] =
+ 			CStringGetTextDatum(control->version);
+ 
+ 	if( control->custom_class == NULL )
+ 		nulls[Anum_pg_extension_custom_class - 1] = true;
+ 	else
+ 		values[Anum_pg_extension_custom_class - 1] =
+ 			CStringGetTextDatum(control->custom_class);
+ 
+ 	tuple = heap_form_tuple(rel->rd_att, values, nulls);
+ 	extensionoid = simple_heap_insert(rel, tuple);
+ 	CatalogUpdateIndexes(rel, tuple);
+ 	heap_freetuple(tuple);
+ 	heap_close(rel, ExclusiveLock);
+ 
+ 	/*
+ 	 * Set the global create_extension so that any object created in the
+ 	 * script has a dependancy recorded towards the extension here.
+ 	 */
+ 	create_extension = (ObjectAddress *)palloc(sizeof(ObjectAddress));
+ 	create_extension->classId = ExtensionRelationId;
+ 	create_extension->objectId = extensionoid;
+ 	create_extension->objectSubId = 0;
+ 
+ 	elog(NOTICE,
+ 		 "Installing extension '%s' from '%s'",
+ 		 stmt->extname,
+ 		 get_extension_install_filename(stmt->extname));
+ 
+ 	filename = cstring_to_text(get_extension_install_filename(stmt->extname));
+ 	DirectFunctionCall1(pg_execute_from_file, PointerGetDatum(filename));
+ 
+ 	/* reset the current_extension dependancy tracker */
+ 	pfree(create_extension);
+ 	create_extension = NULL;
+ 
+ 	return;
+ }
+ 
+ /*
+  * Drop an extension
+  */
+ void
+ DropExtension(DropExtensionStmt *stmt)
+ {
+ 	char	   *extname = stmt->extname;
+ 	Relation	rel;
+ 	HeapScanDesc scandesc;
+ 	HeapTuple	tuple;
+ 	ScanKeyData entry[1];
+ 	Oid			extensionoid;
+ 	ObjectAddress object;
+ 
+ 	/* Must be super user */
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 				 errmsg("permission denied to drop extension \"%s\"",
+ 						stmt->extname),
+ 				 errhint("Must be superuser to drop an extension.")));
+ 
+ 	/*
+ 	 * Find the target tuple
+ 	 */
+ 	rel = heap_open(ExtensionRelationId, RowExclusiveLock);
+ 
+ 	ScanKeyInit(&entry[0],
+ 				Anum_pg_extension_extname,
+ 				BTEqualStrategyNumber, F_NAMEEQ,
+ 				CStringGetDatum(extname));
+ 	scandesc = heap_beginscan(rel, SnapshotNow, 1, entry);
+ 	tuple = heap_getnext(scandesc, ForwardScanDirection);
+ 
+ 	if (!HeapTupleIsValid(tuple))
+ 	{
+ 		if (!stmt->missing_ok)
+ 		{
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_UNDEFINED_OBJECT),
+ 					 errmsg("extension \"%s\" does not exist", extname)));
+ 		}
+ 		else
+ 		{
+ 			ereport(NOTICE,
+ 					(errmsg("extension \"%s\" does not exist, skipping",
+ 							extname)));
+ 			/* XXX I assume I need one or both of these next two calls */
+ 			heap_endscan(scandesc);
+ 			heap_close(rel, RowExclusiveLock);
+ 		}
+ 		return;
+ 	}
+ 	extensionoid = HeapTupleGetOid(tuple);
+ 	heap_endscan(scandesc);
+ 	heap_close(rel, RowExclusiveLock);
+ 
+ 	/*
+ 	 * Do the deletion
+ 	 */
+ 	object.classId = ExtensionRelationId;
+ 	object.objectId = extensionoid;
+ 	object.objectSubId = 0;
+ 
+ 	performDeletion(&object, stmt->behavior);
+ 
+ 	return;
+ }
+ 
+ /*
+  * get_extension_oid - given an extension name, look up the OID
+  *
+  * If missing_ok is false, throw an error if extension name not found.  If
+  * true, just return InvalidOid.
+  */
+ Oid
+ get_extension_oid(const char *extname, bool missing_ok)
+ {
+ 	Oid			result;
+ 	Relation	rel;
+ 	HeapScanDesc scandesc;
+ 	HeapTuple	tuple;
+ 	ScanKeyData entry[1];
+ 
+ 	rel = heap_open(ExtensionRelationId, AccessShareLock);
+ 
+ 	ScanKeyInit(&entry[0],
+ 				Anum_pg_extension_extname,
+ 				BTEqualStrategyNumber, F_NAMEEQ,
+ 				CStringGetDatum(extname));
+ 	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 = HeapTupleGetOid(tuple);
+ 	else
+ 		result = InvalidOid;
+ 
+ 	heap_endscan(scandesc);
+ 	heap_close(rel, AccessShareLock);
+ 
+ 	if (!OidIsValid(result) && !missing_ok)
+         ereport(ERROR,
+                 (errcode(ERRCODE_UNDEFINED_OBJECT),
+                  errmsg("extension \"%s\" does not exist",
+                         extname)));
+ 
+ 	return result;
+ }
+ 
+ /*
+  * get_extension_name - given an extension OID, look up the name
+  *
+  * Returns a palloc'd string, or NULL if no such tablespace.
+  */
+ char *
+ get_extension_name(Oid ext_oid)
+ {
+ 	char	   *result;
+ 	Relation	rel;
+ 	HeapScanDesc scandesc;
+ 	HeapTuple	tuple;
+ 	ScanKeyData entry[1];
+ 
+ 	/*
+ 	 * Search pg_extension.  We use a heapscan here even though there is an
+ 	 * index on oid, on the theory that pg_extension will usually have just a
+ 	 * few entries and so an indexed lookup is a waste of effort.
+ 	 */
+ 	rel = heap_open(ExtensionRelationId, AccessShareLock);
+ 
+ 	ScanKeyInit(&entry[0],
+ 				ObjectIdAttributeNumber,
+ 				BTEqualStrategyNumber, F_OIDEQ,
+ 				ObjectIdGetDatum(ext_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_extension) GETSTRUCT(tuple))->extname));
+ 	else
+ 		result = NULL;
+ 
+ 	heap_endscan(scandesc);
+ 	heap_close(rel, AccessShareLock);
+ 
+ 	return result;
+ }
+ 
+ /*
+  * Drop extension by OID.  This is called to clean up dependencies.
+  */
+ char *
+ RemoveExtensionById(Oid extId)
+ {
+ 	char	   *result;
+ 	Relation	rel;
+ 	HeapScanDesc scandesc;
+ 	HeapTuple	tuple;
+ 	ScanKeyData entry[1];
+ 
+ 	/*
+ 	 * Search pg_extension.  We use a heapscan here even though there is an
+ 	 * index on oid, on the theory that pg_extension will usually have just a
+ 	 * few entries and so an indexed lookup is a waste of effort.
+ 	 */
+ 	rel = heap_open(ExtensionRelationId, AccessShareLock);
+ 
+ 	ScanKeyInit(&entry[0],
+ 				ObjectIdAttributeNumber,
+ 				BTEqualStrategyNumber, F_OIDEQ,
+ 				ObjectIdGetDatum(extId));
+ 	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_extension) GETSTRUCT(tuple))->extname));
+ 	else
+ 		result = NULL;
+ 
+ 	simple_heap_delete(rel, &tuple->t_self);
+ 
+ 	heap_endscan(scandesc);
+ 	heap_close(rel, AccessShareLock);
+ 
+ 	return result;
+ }
*** a/src/backend/commands/foreigncmds.c
--- b/src/backend/commands/foreigncmds.c
***************
*** 32,37 ****
--- 32,38 ----
  #include "utils/lsyscache.h"
  #include "utils/rel.h"
  #include "utils/syscache.h"
+ #include "commands/extension.h"
  
  
  /*
***************
*** 411,416 **** CreateForeignDataWrapper(CreateFdwStmt *stmt)
--- 412,423 ----
  		referenced.objectId = fdwvalidator;
  		referenced.objectSubId = 0;
  		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ 
+ 		/* dependency on extension */
+ 		if (create_extension != NULL )
+ 		{
+ 			recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ 		}
  	}
  
  	recordDependencyOnOwner(ForeignDataWrapperRelationId, fdwId, ownerId);
***************
*** 696,701 **** CreateForeignServer(CreateForeignServerStmt *stmt)
--- 703,714 ----
  
  	recordDependencyOnOwner(ForeignServerRelationId, srvId, ownerId);
  
+ 	/* dependency on extension */
+ 	if (create_extension != NULL )
+ 	{
+ 		recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ 	}
+ 
  	heap_close(rel, NoLock);
  }
  
***************
*** 967,972 **** CreateUserMapping(CreateUserMappingStmt *stmt)
--- 980,991 ----
  		/* Record the mapped user dependency */
  		recordDependencyOnOwner(UserMappingRelationId, umId, useId);
  
+ 	/* dependency on extension */
+ 	if (create_extension != NULL )
+ 	{
+ 		recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ 	}
+ 
  	heap_close(rel, NoLock);
  }
  
*** a/src/backend/commands/functioncmds.c
--- b/src/backend/commands/functioncmds.c
***************
*** 61,67 ****
  #include "utils/rel.h"
  #include "utils/syscache.h"
  #include "utils/tqual.h"
! 
  
  static void AlterFunctionOwner_internal(Relation rel, HeapTuple tup,
  							Oid newOwnerId);
--- 61,67 ----
  #include "utils/rel.h"
  #include "utils/syscache.h"
  #include "utils/tqual.h"
! #include "commands/extension.h"
  
  static void AlterFunctionOwner_internal(Relation rel, HeapTuple tup,
  							Oid newOwnerId);
***************
*** 1744,1749 **** CreateCast(CreateCastStmt *stmt)
--- 1744,1754 ----
  		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
  	}
  
+ 	if (create_extension != NULL )
+ 	{
+ 		recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ 	}
+ 
  	heap_freetuple(tuple);
  
  	heap_close(relation, RowExclusiveLock);
*** a/src/backend/commands/opclasscmds.c
--- b/src/backend/commands/opclasscmds.c
***************
*** 42,47 ****
--- 42,48 ----
  #include "utils/rel.h"
  #include "utils/syscache.h"
  #include "utils/tqual.h"
+ #include "commands/extension.h"
  
  
  /*
***************
*** 306,311 **** CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid)
--- 307,318 ----
  	/* dependency on owner */
  	recordDependencyOnOwner(OperatorFamilyRelationId, opfamilyoid, GetUserId());
  
+ 	/* dependency on extension */
+ 	if (create_extension != NULL )
+ 	{
+ 		recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ 	}
+ 
  	heap_close(rel, RowExclusiveLock);
  
  	return opfamilyoid;
***************
*** 693,698 **** DefineOpClass(CreateOpClassStmt *stmt)
--- 700,711 ----
  	/* dependency on owner */
  	recordDependencyOnOwner(OperatorClassRelationId, opclassoid, GetUserId());
  
+ 	/* dependency on extension */
+ 	if (create_extension != NULL )
+ 	{
+ 		recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ 	}
+ 
  	heap_close(rel, RowExclusiveLock);
  }
  
*** a/src/backend/commands/proclang.c
--- b/src/backend/commands/proclang.c
***************
*** 36,41 ****
--- 36,42 ----
  #include "utils/rel.h"
  #include "utils/syscache.h"
  #include "utils/tqual.h"
+ #include "commands/extension.h"
  
  
  typedef struct
***************
*** 425,430 **** create_proc_lang(const char *languageName, bool replace,
--- 426,437 ----
  		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
  	}
  
+ 	/* dependency on extension */
+ 	if (create_extension != NULL )
+ 	{
+ 		recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ 	}
+ 
  	heap_close(rel, RowExclusiveLock);
  }
  
*** a/src/backend/commands/tsearchcmds.c
--- b/src/backend/commands/tsearchcmds.c
***************
*** 46,51 ****
--- 46,52 ----
  #include "utils/rel.h"
  #include "utils/syscache.h"
  #include "utils/tqual.h"
+ #include "commands/extension.h"
  
  
  static void MakeConfigurationMapping(AlterTSConfigurationStmt *stmt,
***************
*** 154,159 **** makeParserDependencies(HeapTuple tuple)
--- 155,166 ----
  		referenced.objectId = prs->prsheadline;
  		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
  	}
+ 
+ 	/* dependency on extension */
+ 	if (create_extension != NULL )
+ 	{
+ 		recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ 	}
  }
  
  /*
***************
*** 423,428 **** makeDictionaryDependencies(HeapTuple tuple)
--- 430,441 ----
  	referenced.objectId = dict->dicttemplate;
  	referenced.objectSubId = 0;
  	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ 
+ 	/* dependency on extension, if in create extension */
+ 	if (create_extension != NULL )
+ 	{
+ 		recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ 	}
  }
  
  /*
***************
*** 965,970 **** makeTSTemplateDependencies(HeapTuple tuple)
--- 978,989 ----
  		referenced.objectId = tmpl->tmplinit;
  		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
  	}
+ 
+ 	/* dependency on extension, if in create extension */
+ 	if (create_extension != NULL )
+ 	{
+ 		recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ 	}
  }
  
  /*
***************
*** 1289,1294 **** makeConfigurationDependencies(HeapTuple tuple, bool removeOld,
--- 1308,1319 ----
  	/* Record 'em (this includes duplicate elimination) */
  	record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
  
+ 	/* dependencies on extension being created, if any */
+ 	if (create_extension != NULL )
+ 	{
+ 		recordDependencyOn(&myself, create_extension, DEPENDENCY_INTERNAL);
+ 	}
+ 
  	free_object_addresses(addrs);
  }
  
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 3087,3092 **** _copyDiscardStmt(DiscardStmt *from)
--- 3087,3114 ----
  	return newnode;
  }
  
+ static CreateExtensionStmt *
+ _copyCreateExtensionStmt(CreateExtensionStmt *from)
+ {
+ 	CreateExtensionStmt *newnode = makeNode(CreateExtensionStmt);
+ 
+ 	COPY_STRING_FIELD(extname);
+ 
+ 	return newnode;
+ }
+ 
+ static DropExtensionStmt *
+ _copyDropExtensionStmt(DropExtensionStmt *from)
+ {
+ 	DropExtensionStmt *newnode = makeNode(DropExtensionStmt);
+ 
+ 	COPY_STRING_FIELD(extname);
+ 	COPY_SCALAR_FIELD(missing_ok);
+ 	COPY_SCALAR_FIELD(behavior);
+ 
+ 	return newnode;
+ }
+ 
  static CreateTableSpaceStmt *
  _copyCreateTableSpaceStmt(CreateTableSpaceStmt *from)
  {
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
***************
*** 1587,1592 **** _equalDropTableSpaceStmt(DropTableSpaceStmt *a, DropTableSpaceStmt *b)
--- 1587,1610 ----
  }
  
  static bool
+ _equalCreateExtensionStmt(CreateExtensionStmt *a, CreateExtensionStmt *b)
+ {
+ 	COMPARE_STRING_FIELD(extname);
+ 
+ 	return true;
+ }
+ 
+ static bool
+ _equalDropExtensionStmt(DropExtensionStmt *a, DropExtensionStmt *b)
+ {
+ 	COMPARE_STRING_FIELD(extname);
+ 	COMPARE_SCALAR_FIELD(missing_ok);
+ 	COMPARE_SCALAR_FIELD(behavior);
+ 
+ 	return true;
+ }
+ 
+ static bool
  _equalAlterTableSpaceOptionsStmt(AlterTableSpaceOptionsStmt *a,
  								 AlterTableSpaceOptionsStmt *b)
  {
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 190,196 **** static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
  		AlterDefaultPrivilegesStmt DefACLAction
  		AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt
  		ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
! 		CreateDomainStmt CreateGroupStmt CreateOpClassStmt
  		CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
  		CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
  		CreateFdwStmt CreateForeignServerStmt CreateAssertStmt CreateTrigStmt
--- 190,196 ----
  		AlterDefaultPrivilegesStmt DefACLAction
  		AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt
  		ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
! 		CreateDomainStmt CreateExtensionStmt CreateGroupStmt CreateOpClassStmt
  		CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
  		CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
  		CreateFdwStmt CreateForeignServerStmt CreateAssertStmt CreateTrigStmt
***************
*** 198,204 **** static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
  		CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
  		DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt
  		DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt DropRoleStmt
! 		DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt
  		DropForeignServerStmt DropUserMappingStmt ExplainStmt FetchStmt
  		GrantStmt GrantRoleStmt IndexStmt InsertStmt ListenStmt LoadStmt
  		LockStmt NotifyStmt ExplainableStmt PreparableStmt
--- 198,204 ----
  		CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
  		DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt
  		DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt DropRoleStmt
! 		DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt DropExtensionStmt
  		DropForeignServerStmt DropUserMappingStmt ExplainStmt FetchStmt
  		GrantStmt GrantRoleStmt IndexStmt InsertStmt ListenStmt LoadStmt
  		LockStmt NotifyStmt ExplainableStmt PreparableStmt
***************
*** 484,490 **** static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
  	DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P DOUBLE_P DROP
  
  	EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EXCEPT
! 	EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN EXTERNAL EXTRACT
  
  	FALSE_P FAMILY FETCH FIRST_P FLOAT_P FOLLOWING FOR FORCE FOREIGN FORWARD
  	FREEZE FROM FULL FUNCTION FUNCTIONS
--- 484,490 ----
  	DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P DOUBLE_P DROP
  
  	EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EXCEPT
! 	EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN EXTENSION EXTERNAL EXTRACT
  
  	FALSE_P FAMILY FETCH FIRST_P FLOAT_P FOLLOWING FOR FORCE FOREIGN FORWARD
  	FREEZE FROM FULL FUNCTION FUNCTIONS
***************
*** 680,685 **** stmt :
--- 680,686 ----
  			| CreateCastStmt
  			| CreateConversionStmt
  			| CreateDomainStmt
+ 			| CreateExtensionStmt
  			| CreateFdwStmt
  			| CreateForeignServerStmt
  			| CreateFunctionStmt
***************
*** 705,710 **** stmt :
--- 706,712 ----
  			| DoStmt
  			| DropAssertStmt
  			| DropCastStmt
+ 			| DropExtensionStmt
  			| DropFdwStmt
  			| DropForeignServerStmt
  			| DropGroupStmt
***************
*** 3081,3086 **** opt_procedural:
--- 3083,3128 ----
  /*****************************************************************************
   *
   * 		QUERY:
+  *             CREATE EXTENSION extension
+  *
+  *****************************************************************************/
+ 
+ CreateExtensionStmt: CREATE EXTENSION name
+ 				{
+ 					CreateExtensionStmt *n = makeNode(CreateExtensionStmt);
+ 					n->extname = $3;
+ 					$$ = (Node *) n;
+ 				}
+ 		;
+ 
+ /*****************************************************************************
+  *
+  * 		QUERY :
+  *				DROP EXTENSION <extension> [ RESTRICT | CASCADE ]
+  *
+  ****************************************************************************/
+ 
+ DropExtensionStmt: DROP EXTENSION name opt_drop_behavior
+ 				{
+ 					DropExtensionStmt *n = makeNode(DropExtensionStmt);
+ 					n->extname = $3;
+ 					n->missing_ok = false;
+ 					n->behavior = $4;
+ 					$$ = (Node *) n;
+ 				}
+ 				|  DROP EXTENSION IF_P EXISTS name opt_drop_behavior
+                 {
+ 					DropExtensionStmt *n = makeNode(DropExtensionStmt);
+ 					n->extname = $5;
+ 					n->missing_ok = true;
+ 					n->behavior = $6;
+ 					$$ = (Node *) n;
+ 				}
+ 		;
+ 
+ /*****************************************************************************
+  *
+  * 		QUERY:
   *             CREATE TABLESPACE tablespace LOCATION '/path/to/tablespace/'
   *
   *****************************************************************************/
***************
*** 4184,4190 **** opt_restart_seqs:
   *
   *	COMMENT ON [ [ DATABASE | DOMAIN | INDEX | SEQUENCE | TABLE | TYPE | VIEW |
   *				   CONVERSION | LANGUAGE | OPERATOR CLASS | LARGE OBJECT |
!  *				   CAST | COLUMN | SCHEMA | TABLESPACE | ROLE |
   *				   TEXT SEARCH PARSER | TEXT SEARCH DICTIONARY |
   *				   TEXT SEARCH TEMPLATE |
   *				   TEXT SEARCH CONFIGURATION ] <objname> |
--- 4226,4232 ----
   *
   *	COMMENT ON [ [ DATABASE | DOMAIN | INDEX | SEQUENCE | TABLE | TYPE | VIEW |
   *				   CONVERSION | LANGUAGE | OPERATOR CLASS | LARGE OBJECT |
!  *				   CAST | COLUMN | SCHEMA | TABLESPACE | EXTENSION | ROLE |
   *				   TEXT SEARCH PARSER | TEXT SEARCH DICTIONARY |
   *				   TEXT SEARCH TEMPLATE |
   *				   TEXT SEARCH CONFIGURATION ] <objname> |
***************
*** 4363,4368 **** comment_type:
--- 4405,4411 ----
  			| VIEW								{ $$ = OBJECT_VIEW; }
  			| CONVERSION_P						{ $$ = OBJECT_CONVERSION; }
  			| TABLESPACE						{ $$ = OBJECT_TABLESPACE; }
+ 			| EXTENSION 						{ $$ = OBJECT_EXTENSION; }
  			| ROLE								{ $$ = OBJECT_ROLE; }
  		;
  
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
***************
*** 32,37 ****
--- 32,38 ----
  #include "commands/defrem.h"
  #include "commands/discard.h"
  #include "commands/explain.h"
+ #include "commands/extension.h"
  #include "commands/lockcmds.h"
  #include "commands/portalcmds.h"
  #include "commands/prepare.h"
***************
*** 175,180 **** check_xact_readonly(Node *parsetree)
--- 176,182 ----
  		case T_CreateConversionStmt:
  		case T_CreatedbStmt:
  		case T_CreateDomainStmt:
+ 		case T_CreateExtensionStmt:
  		case T_CreateFunctionStmt:
  		case T_CreateRoleStmt:
  		case T_IndexStmt:
***************
*** 194,199 **** check_xact_readonly(Node *parsetree)
--- 196,202 ----
  		case T_DropCastStmt:
  		case T_DropStmt:
  		case T_DropdbStmt:
+ 		case T_DropExtensionStmt:
  		case T_DropTableSpaceStmt:
  		case T_RemoveFuncStmt:
  		case T_DropRoleStmt:
***************
*** 557,562 **** standard_ProcessUtility(Node *parsetree,
--- 560,573 ----
  			}
  			break;
  
+ 		case T_CreateExtensionStmt:
+ 			CreateExtension((CreateExtensionStmt *) parsetree);
+ 			break;
+ 
+ 		case T_DropExtensionStmt:
+ 			DropExtension((DropExtensionStmt *) parsetree);
+ 			break;
+ 
  		case T_CreateTableSpaceStmt:
  			PreventTransactionChain(isTopLevel, "CREATE TABLESPACE");
  			CreateTableSpace((CreateTableSpaceStmt *) parsetree);
***************
*** 1498,1503 **** CreateCommandTag(Node *parsetree)
--- 1509,1522 ----
  			tag = "CREATE TABLE";
  			break;
  
+ 		case T_CreateExtensionStmt:
+ 			tag = "CREATE EXTENSION";
+ 			break;
+ 
+ 		case T_DropExtensionStmt:
+ 			tag = "DROP EXTENSION";
+ 			break;
+ 
  		case T_CreateTableSpaceStmt:
  			tag = "CREATE TABLESPACE";
  			break;
***************
*** 2292,2297 **** GetCommandLogLevel(Node *parsetree)
--- 2311,2324 ----
  			lev = LOGSTMT_DDL;
  			break;
  
+ 		case T_CreateExtensionStmt:
+ 			lev = LOGSTMT_DDL;
+ 			break;
+ 
+ 		case T_DropExtensionStmt:
+ 			lev = LOGSTMT_DDL;
+ 			break;
+ 
  		case T_CreateTableSpaceStmt:
  			lev = LOGSTMT_DDL;
  			break;
*** a/src/backend/utils/adt/genfile.c
--- b/src/backend/utils/adt/genfile.c
***************
*** 7,12 ****
--- 7,13 ----
   * Copyright (c) 2004-2010, PostgreSQL Global Development Group
   *
   * Author: Andreas Pflug <pgadmin@pse-consulting.de>
+  *         Dimitri Fontaine <dimitri@2ndQuadrant.fr>
   *
   * IDENTIFICATION
   *	  src/backend/utils/adt/genfile.c
***************
*** 30,35 ****
--- 31,46 ----
  #include "utils/memutils.h"
  #include "utils/timestamp.h"
  
+ #include "tcop/pquery.h"
+ #include "tcop/tcopprot.h"
+ #include "tcop/utility.h"
+ #include "access/transam.h"
+ #include "access/xact.h"
+ #include "utils/resowner.h"
+ #include "utils/snapmgr.h"
+ #include "parser/analyze.h"
+ #include "access/printtup.h"
+ 
  typedef struct
  {
  	char	   *location;
***************
*** 264,266 **** pg_ls_dir(PG_FUNCTION_ARGS)
--- 275,459 ----
  
  	SRF_RETURN_DONE(funcctx);
  }
+ 
+ /*
+  * Read a file then execute the SQL commands it contains.
+  */
+ Datum
+ pg_execute_from_file(PG_FUNCTION_ARGS)
+ {
+ 	text	   *filename_t = PG_GETARG_TEXT_P(0);
+ 	char	   *filename;
+ 	FILE       *file;
+ 	int64       fsize = -1, nbytes;
+ 	struct stat fst;
+ 	char       *query_string = NULL;
+ 
+ 	CommandDest dest = DestNone;
+ 	MemoryContext oldcontext;
+ 	List	   *parsetree_list;
+ 	ListCell   *parsetree_item;
+ 	bool		save_log_statement_stats = log_statement_stats;
+ 	bool		was_logged = false;
+ 	bool		isTopLevel;
+ 	char		msec_str[32];
+ 
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 				 (errmsg("must be superuser to get file information"))));
+ 
+ 	/*
+ 	 * Only superuser can call pg_execute_from_file, and CREATE EXTENSION
+ 	 * uses that too. Don't double check the PATH. Also note that
+ 	 * extension's install files are not in $PGDATA but `pg_config
+ 	 * --sharedir`.
+ 	 */
+ 	filename = text_to_cstring(filename_t);
+ 
+ 	if (stat(filename, &fst) < 0)
+ 		ereport(ERROR,
+ 				(errcode_for_file_access(),
+ 				 errmsg("could not stat file \"%s\": %m", filename)));
+ 
+ 	fsize = Int64GetDatum((int64) fst.st_size);
+ 
+ 	if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
+ 		ereport(ERROR,
+ 				(errcode_for_file_access(),
+ 				 errmsg("could not open file \"%s\" for reading: %m",
+ 						filename)));
+ 
+ 	if (ferror(file))
+ 		ereport(ERROR,
+ 				(errcode_for_file_access(),
+ 				 errmsg("could not read file \"%s\": %m", filename)));
+ 
+ 	query_string = (char *)palloc((fsize+1)*sizeof(char));
+ 	memset(query_string, 0, fsize+1);
+ 	nbytes = fread(query_string, 1, (size_t) fsize, file);
+ 	pg_verifymbstr(query_string, nbytes, false);
+ 	FreeFile(file);
+ 
+ 	/*
+ 	elog(NOTICE, "pg_execute_from_file('%s') read %d/%d bytes:", filename, nbytes, fsize);
+ 	elog(NOTICE, "%s", query_string);
+ 	 */
+ 
+ 	/*
+ 	 * Code pasted from postgres.c:exec_simple_query, main differences are:
+ 	 * - don't override unnamed portal, name it after filename instead
+ 	 * - don't start nor finish a transaction
+ 	 * - don't set stats or tracing markers
+ 	 */
+ 	oldcontext = MemoryContextSwitchTo(MessageContext);
+ 	parsetree_list = pg_parse_query(query_string);
+ 	MemoryContextSwitchTo(oldcontext);
+ 
+ 	isTopLevel = false;
+ 
+ 	foreach(parsetree_item, parsetree_list)
+ 	{
+ 		Node	   *parsetree = (Node *) lfirst(parsetree_item);
+ 		bool		snapshot_set = false;
+ 		const char *commandTag;
+ 		char		completionTag[COMPLETION_TAG_BUFSIZE];
+ 		List	   *querytree_list,
+ 				   *plantree_list;
+ 		Portal		portal;
+ 		DestReceiver *receiver;
+ 		int16		format = 0; /* TEXT */
+ 
+ 		commandTag = CreateCommandTag(parsetree);
+ 
+ 		/* If we got a cancel signal in parsing or prior command, quit */
+ 		CHECK_FOR_INTERRUPTS();
+ 
+ 		/*
+ 		 * Set up a snapshot if parse analysis/planning will need one.
+ 		 */
+ 		if (analyze_requires_snapshot(parsetree))
+ 		{
+ 			PushActiveSnapshot(GetTransactionSnapshot());
+ 			snapshot_set = true;
+ 		}
+ 
+ 		/*
+ 		 * OK to analyze, rewrite, and plan this query.
+ 		 *
+ 		 * Switch to appropriate context for constructing querytrees (again,
+ 		 * these must outlive the execution context).
+ 		 */
+ 		oldcontext = MemoryContextSwitchTo(MessageContext);
+ 
+ 		querytree_list = pg_analyze_and_rewrite(parsetree, query_string,
+ 												NULL, 0);
+ 
+ 		plantree_list = pg_plan_queries(querytree_list, 0, NULL);
+ 
+ 		/* Done with the snapshot used for parsing/planning */
+ 		if (snapshot_set)
+ 			PopActiveSnapshot();
+ 
+ 		/* If we got a cancel signal in analysis or planning, quit */
+ 		CHECK_FOR_INTERRUPTS();
+ 
+ 		/*
+ 		 * Create a portal to run the query or queries in. Name if after the
+ 		 * given filename. If there already is one, silently drop it.
+ 		 */
+ 		portal = CreatePortal(filename, true, true);
+ 		/* Don't display the portal in pg_cursors */
+ 		portal->visible = false;
+ 
+ 		/*
+ 		 * We don't have to copy anything into the portal, because everything
+ 		 * we are passing here is in MessageContext, which will outlive the
+ 		 * portal anyway.
+ 		 */
+ 		PortalDefineQuery(portal,
+ 						  NULL,
+ 						  query_string,
+ 						  commandTag,
+ 						  plantree_list,
+ 						  NULL);
+ 
+ 		/*
+ 		 * Start the portal.  No parameters here.
+ 		 */
+ 		PortalStart(portal, NULL, InvalidSnapshot);
+ 		PortalSetResultFormat(portal, 1, &format);
+ 
+ 		/*
+ 		 * Now we can create the destination receiver object.
+ 		 */
+ 		receiver = CreateDestReceiver(dest);
+ 		if (dest == DestRemote)
+ 			SetRemoteDestReceiverParams(receiver, portal);
+ 
+ 		/*
+ 		 * Switch back to transaction context for execution.
+ 		 */
+ 		MemoryContextSwitchTo(oldcontext);
+ 
+ 		/*
+ 		 * Run the portal to completion, and then drop it (and the receiver).
+ 		 */
+ 		(void) PortalRun(portal,
+ 						 FETCH_ALL,
+ 						 isTopLevel,
+ 						 receiver,
+ 						 receiver,
+ 						 completionTag);
+ 
+ 		(*receiver->rDestroy) (receiver);
+ 
+ 		PortalDrop(portal, false);
+ 
+ 		if (!IsA(parsetree, TransactionStmt) && lnext(parsetree_item) != NULL)
+ 		{
+ 			CommandCounterIncrement();
+ 		}
+ 	}
+ 	PG_RETURN_VOID();
+ }
*** a/src/backend/utils/misc/Makefile
--- b/src/backend/utils/misc/Makefile
***************
*** 15,21 **** include $(top_builddir)/src/Makefile.global
  override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
  
  OBJS = guc.o help_config.o pg_rusage.o ps_status.o superuser.o tzparser.o \
!        rbtree.o
  
  # This location might depend on the installation directories. Therefore
  # we can't subsitute it into pg_config.h.
--- 15,21 ----
  override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
  
  OBJS = guc.o help_config.o pg_rusage.o ps_status.o superuser.o tzparser.o \
!        rbtree.o cfparser.o
  
  # This location might depend on the installation directories. Therefore
  # we can't subsitute it into pg_config.h.
*** /dev/null
--- b/src/backend/utils/misc/cfparser.c
***************
*** 0 ****
--- 1,112 ----
+ /*-------------------------------------------------------------------------
+  *
+  * cfparser.c
+  *	  Function for parsing RecoveryCommandFile lines
+  *
+  * This very simple file format (varible = value) is now also used in the
+  * extension control file format
+  *
+  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * IDENTIFICATION
+  *	  src/backend/utils/misc/cfparser.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ 
+ #include "postgres.h"
+ 
+ /*
+  * Parse one line from recovery.conf. 'cmdline' is the raw line from the
+  * file. If the line is parsed successfully, returns true, false indicates
+  * syntax error. On success, *key_p and *value_p are set to the parameter
+  * name and value on the line, respectively. If the line is an empty line,
+  * consisting entirely of whitespace and comments, function returns true
+  * and *keyp_p and *value_p are set to NULL.
+  *
+  * The pointers returned in *key_p and *value_p point to an internal buffer
+  * that is valid only until the next call of parseRecoveryCommandFile().
+  */
+ bool
+ parseRecoveryCommandFileLine(char *cmdline, char **key_p, char **value_p)
+ {
+ 	char	   *ptr;
+ 	char	   *bufp;
+ 	char	   *key;
+ 	char	   *value;
+ 	static char *buf = NULL;
+ 
+ 	*key_p = *value_p = NULL;
+ 
+ 	/*
+ 	 * Allocate the buffer on first use. It's used to hold both the parameter
+ 	 * name and value.
+ 	 */
+ 	if (buf == NULL)
+ 		buf = malloc(MAXPGPATH + 1);
+ 	bufp = buf;
+ 
+ 	/* Skip any whitespace at the beginning of line */
+ 	for (ptr = cmdline; *ptr; ptr++)
+ 	{
+ 		if (!isspace((unsigned char) *ptr))
+ 			break;
+ 	}
+ 	/* Ignore empty lines */
+ 	if (*ptr == '\0' || *ptr == '#')
+ 		return true;
+ 
+ 	/* Read the parameter name */
+ 	key = bufp;
+ 	while (*ptr && !isspace((unsigned char) *ptr) &&
+ 		   *ptr != '=' && *ptr != '\'')
+ 		*(bufp++) = *(ptr++);
+ 	*(bufp++) = '\0';
+ 
+ 	/* Skip to the beginning quote of the parameter value */
+ 	ptr = strchr(ptr, '\'');
+ 	if (!ptr)
+ 		return false;
+ 	ptr++;
+ 
+ 	/* Read the parameter value to *bufp. Collapse any '' escapes as we go. */
+ 	value = bufp;
+ 	for (;;)
+ 	{
+ 		if (*ptr == '\'')
+ 		{
+ 			ptr++;
+ 			if (*ptr == '\'')
+ 				*(bufp++) = '\'';
+ 			else
+ 			{
+ 				/* end of parameter */
+ 				*bufp = '\0';
+ 				break;
+ 			}
+ 		}
+ 		else if (*ptr == '\0')
+ 			return false;		/* unterminated quoted string */
+ 		else
+ 			*(bufp++) = *ptr;
+ 
+ 		ptr++;
+ 	}
+ 	*(bufp++) = '\0';
+ 
+ 	/* Check that there's no garbage after the value */
+ 	while (*ptr)
+ 	{
+ 		if (*ptr == '#')
+ 			break;
+ 		if (!isspace((unsigned char) *ptr))
+ 			return false;
+ 		ptr++;
+ 	}
+ 
+ 	/* Success! */
+ 	*key_p = key;
+ 	*value_p = value;
+ 	return true;
+ }
*** a/src/bin/pg_dump/common.c
--- b/src/bin/pg_dump/common.c
***************
*** 78,83 **** static int	strInArray(const char *pattern, char **arr, int arr_size);
--- 78,84 ----
  TableInfo *
  getSchemaData(int *numTablesPtr)
  {
+ 	ExtensionInfo *extinfo;
  	NamespaceInfo *nsinfo;
  	AggInfo    *agginfo;
  	InhInfo    *inhinfo;
***************
*** 94,99 **** getSchemaData(int *numTablesPtr)
--- 95,101 ----
  	FdwInfo    *fdwinfo;
  	ForeignServerInfo *srvinfo;
  	DefaultACLInfo *daclinfo;
+ 	int			numExtensions;
  	int			numNamespaces;
  	int			numAggregates;
  	int			numInherits;
***************
*** 112,117 **** getSchemaData(int *numTablesPtr)
--- 114,123 ----
  	int			numDefaultACLs;
  
  	if (g_verbose)
+ 		write_msg(NULL, "reading extensions\n");
+ 	extinfo = getExtensions(&numExtensions);
+ 
+ 	if (g_verbose)
  		write_msg(NULL, "reading schemas\n");
  	nsinfo = getNamespaces(&numNamespaces);
  
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
***************
*** 157,162 **** static int	findSecLabels(Archive *fout, Oid classoid, Oid objoid,
--- 157,163 ----
  						  SecLabelItem **items);
  static int	collectSecLabels(Archive *fout, SecLabelItem **items);
  static void dumpDumpableObject(Archive *fout, DumpableObject *dobj);
+ static void dumpExtension(Archive *fout, ExtensionInfo *extinfo);
  static void dumpNamespace(Archive *fout, NamespaceInfo *nspinfo);
  static void dumpType(Archive *fout, TypeInfo *tyinfo);
  static void dumpBaseType(Archive *fout, TypeInfo *tyinfo);
***************
*** 2376,2381 **** binary_upgrade_set_relfilenodes(PQExpBuffer upgrade_buffer, Oid pg_class_oid,
--- 2377,2456 ----
  }
  
  /*
+  * getExtensions:
+  *	  read all extensions in the system catalogs and return them in the
+  * ExtensionInfo* structure
+  *
+  *	numExtensions is set to the number of extensions read in
+  */
+ ExtensionInfo *
+ getExtensions(int *numExtensions)
+ {
+ 	PGresult   *res;
+ 	int			ntups;
+ 	int			i;
+ 	PQExpBuffer query;
+ 	ExtensionInfo *extinfo = NULL;
+ 	int			i_tableoid;
+ 	int			i_oid;
+ 	int			i_extname;
+ 	int			i_extversion;
+ 	int			i_extcclass;
+ 
+ 	/*
+ 	 * Before 9.1, there are no extensions.
+ 	 */
+ 	if (g_fout->remoteVersion < 90100)
+ 	{
+ 		*numExtensions = 0;
+ 		return extinfo;
+ 	}
+ 
+ 	query = createPQExpBuffer();
+ 
+ 	/* Make sure we are in proper schema */
+ 	selectSourceSchema("pg_catalog");
+ 
+ 	/*
+ 	 * 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, extname, extversion, custom_class "
+ 					  "FROM pg_extension");
+ 
+ 	res = PQexec(g_conn, query->data);
+ 	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+ 
+ 	ntups = PQntuples(res);
+ 
+ 	extinfo = (ExtensionInfo *) malloc(ntups * sizeof(ExtensionInfo));
+ 
+ 	i_tableoid = PQfnumber(res, "tableoid");
+ 	i_oid = PQfnumber(res, "oid");
+ 	i_extname = PQfnumber(res, "extname");
+ 	i_extversion = PQfnumber(res, "extversion");
+ 	i_extcclass = PQfnumber(res, "custom_class");
+ 
+ 	for (i = 0; i < ntups; i++)
+ 	{
+ 		extinfo[i].dobj.objType = DO_EXTENSION;
+ 		extinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+ 		extinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ 		AssignDumpId(&extinfo[i].dobj);
+ 		extinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_extname));
+ 		extinfo[i].extversion = strdup(PQgetvalue(res, i, i_extversion));
+ 		extinfo[i].custom_class = strdup(PQgetvalue(res, i, i_extcclass));
+ 	}
+ 
+ 	PQclear(res);
+ 	destroyPQExpBuffer(query);
+ 
+ 	*numExtensions = ntups;
+ 	return extinfo;
+ }
+ 
+ /*
   * getNamespaces:
   *	  read all namespaces in the system catalogs and return them in the
   * NamespaceInfo* structure
***************
*** 2439,2448 **** getNamespaces(int *numNamespaces)
  	 * 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, "
! 					  "(%s nspowner) AS rolname, "
! 					  "nspacl FROM pg_namespace",
! 					  username_subquery);
  
  	res = PQexec(g_conn, query->data);
  	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
--- 2514,2569 ----
  	 * we fetch all namespaces including system ones, so that every object we
  	 * read in can be linked to a containing namespace.
  	 */
! 	if (g_fout->remoteVersion >= 90100)
! 	{
! 		/*
! 		 * So we want the namespaces, but we want to filter out any
! 		 * namespace created by an extension's script. That's unless the
! 		 * user went over his head and created objects into the extension's
! 		 * schema: we now want the schema not to be filtered out to avoid:
! 		 *
! 		 *   pg_dump: schema with OID 77869 does not exist
! 		 */
! 		appendPQExpBuffer(query, "SELECT n.tableoid, n.oid, n.nspname, "
! 						  "(%s nspowner) AS rolname, "
! 						  "n.nspacl "
! 						  "  FROM pg_namespace n "
! 						  " WHERE n.nspname != 'information_schema' "
! 						  "   AND CASE WHEN (SELECT count(*) "
! 						  "                    FROM pg_depend "
! 						  "                   WHERE refobjid = n.oid and deptype != 'p') > 0 "
! 						  "            THEN EXISTS( "
! 						  "WITH RECURSIVE depends AS ( "
! 						  " select n.oid as nsp, objid, refobjid, array[refobjid] as deps "
! 						  "   from pg_depend "
! 						  "  where refobjid = n.oid and deptype != 'p' "
! 						  " UNION ALL "
! 						  " select p.nsp, p.objid, d.refobjid, deps || d.refobjid "
! 						  "   from pg_depend d JOIN depends p ON d.objid = p.objid "
! 						  "  where d.deptype != 'p' and not d.refobjid = any(deps) "
! 						  ") "
! 						  "  SELECT nsp, objid, array_agg(distinct refobjid) "
! 						  "    FROM depends "
! 						  "GROUP BY nsp, objid "
! 						  "  HAVING NOT array_agg(distinct refobjid) && array(select oid from pg_extension) "
! 						  ") "
! 						  "            ELSE true "
! 						  "        END "
! 						  "UNION ALL "
! 						  "SELECT n.tableoid, n.oid, n.nspname,  "
! 						  "(%s nspowner) AS rolname, "
! 						  "nspacl FROM pg_namespace n "
! 						  "WHERE n.nspname = 'information_schema'",
! 						  username_subquery,
! 						  username_subquery);
! 	}
! 	else
! 	{
! 		appendPQExpBuffer(query, "SELECT tableoid, oid, nspname, "
! 						  "(%s nspowner) AS rolname, "
! 						  "nspacl FROM pg_namespace",
! 						  username_subquery);
! 	}
  
  	res = PQexec(g_conn, query->data);
  	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
***************
*** 2559,2565 **** getTypes(int *numTypes)
  	 * we include even the built-in types because those may be used as array
  	 * elements by user-defined types
  	 *
! 	 * we filter out the built-in types when we dump out the types
  	 *
  	 * same approach for undefined (shell) types and array types
  	 *
--- 2680,2687 ----
  	 * we include even the built-in types because those may be used as array
  	 * elements by user-defined types
  	 *
! 	 * we filter out the built-in types when we dump out the types, and from
! 	 * 9.1 we also filter out types that depend on an extension
  	 *
  	 * same approach for undefined (shell) types and array types
  	 *
***************
*** 2574,2580 **** getTypes(int *numTypes)
  	/* Make sure we are in proper schema */
  	selectSourceSchema("pg_catalog");
  
! 	if (g_fout->remoteVersion >= 80300)
  	{
  		appendPQExpBuffer(query, "SELECT tableoid, oid, typname, "
  						  "typnamespace, "
--- 2696,2721 ----
  	/* Make sure we are in proper schema */
  	selectSourceSchema("pg_catalog");
  
! 	if (g_fout->remoteVersion >= 90100)
! 	{
! 		appendPQExpBuffer(query, "SELECT t.tableoid, t.oid, t.typname, "
! 						  "t.typnamespace, "
! 						  "(%s typowner) AS rolname, "
! 						  "t.typinput::oid AS typinput, "
! 						  "t.typoutput::oid AS typoutput, t.typelem, t.typrelid, "
! 						  "CASE WHEN t.typrelid = 0 THEN ' '::\"char\" "
! 						  "ELSE (SELECT relkind FROM pg_class WHERE oid = t.typrelid) END AS typrelkind, "
! 						  "t.typtype, t.typisdefined, "
! 						  "t.typname[0] = '_' AND t.typelem != 0 AND "
! 						  "(SELECT typarray FROM pg_type te WHERE oid = t.typelem) = t.oid AS isarray "
! 						  "FROM pg_type t "
! 						  "LEFT JOIN pg_depend d ON d.objid = t.oid and d.deptype = 'i' "
! 						  "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! 						  "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! 						  "WHERE x.oid IS NULL",
! 						  username_subquery);
! 	}
! 	else if (g_fout->remoteVersion >= 80300)
  	{
  		appendPQExpBuffer(query, "SELECT tableoid, oid, typname, "
  						  "typnamespace, "
***************
*** 2806,2812 **** getOperators(int *numOprs)
  	/* Make sure we are in proper schema */
  	selectSourceSchema("pg_catalog");
  
! 	if (g_fout->remoteVersion >= 70300)
  	{
  		appendPQExpBuffer(query, "SELECT tableoid, oid, oprname, "
  						  "oprnamespace, "
--- 2947,2966 ----
  	/* Make sure we are in proper schema */
  	selectSourceSchema("pg_catalog");
  
! 	if (g_fout->remoteVersion >= 90100)
! 	{
! 		appendPQExpBuffer(query, "SELECT o.tableoid, o.oid, o.oprname, "
! 						  "o.oprnamespace, "
! 						  "(%s oprowner) AS rolname, "
! 						  "o.oprcode::oid AS oprcode "
! 						  "FROM pg_operator o "
! 						  "LEFT JOIN pg_depend d ON d.objid = o.oid and d.deptype = 'i' "
! 						  "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! 						  "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! 						  "WHERE x.oid IS NULL",
! 						  username_subquery);
! 	}
! 	else if (g_fout->remoteVersion >= 70300)
  	{
  		appendPQExpBuffer(query, "SELECT tableoid, oid, oprname, "
  						  "oprnamespace, "
***************
*** 2985,2991 **** getOpclasses(int *numOpclasses)
  	/* Make sure we are in proper schema */
  	selectSourceSchema("pg_catalog");
  
! 	if (g_fout->remoteVersion >= 70300)
  	{
  		appendPQExpBuffer(query, "SELECT tableoid, oid, opcname, "
  						  "opcnamespace, "
--- 3139,3157 ----
  	/* Make sure we are in proper schema */
  	selectSourceSchema("pg_catalog");
  
! 	if (g_fout->remoteVersion >= 90100)
! 	{
! 		appendPQExpBuffer(query, "SELECT o.tableoid, o.oid, o.opcname, "
! 						  "o.opcnamespace, "
! 						  "(%s opcowner) AS rolname "
! 						  "FROM pg_opclass o "
! 						  "LEFT JOIN pg_depend d ON d.objid = o.oid and d.deptype = 'i' "
! 						  "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! 						  "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! 						  "WHERE x.oid IS NULL",
! 						  username_subquery);
! 	}
! 	else if (g_fout->remoteVersion >= 70300)
  	{
  		appendPQExpBuffer(query, "SELECT tableoid, oid, opcname, "
  						  "opcnamespace, "
***************
*** 3091,3101 **** getOpfamilies(int *numOpfamilies)
  	/* Make sure we are in proper schema */
  	selectSourceSchema("pg_catalog");
  
! 	appendPQExpBuffer(query, "SELECT tableoid, oid, opfname, "
! 					  "opfnamespace, "
! 					  "(%s opfowner) AS rolname "
! 					  "FROM pg_opfamily",
! 					  username_subquery);
  
  	res = PQexec(g_conn, query->data);
  	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
--- 3257,3282 ----
  	/* Make sure we are in proper schema */
  	selectSourceSchema("pg_catalog");
  
! 	if (g_fout->remoteVersion >= 90100)
! 	{
! 		appendPQExpBuffer(query, "SELECT o.tableoid, o.oid, o.opfname, "
! 						  "o.opfnamespace, "
! 						  "(%s opfowner) AS rolname "
! 						  "FROM pg_opfamily o "
! 						  "LEFT JOIN pg_depend d ON d.objid = o.oid and d.deptype = 'i' "
! 						  "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! 						  "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! 						  "WHERE x.oid IS NULL",
! 						  username_subquery);
! 	}
! 	else
! 	{
! 		appendPQExpBuffer(query, "SELECT tableoid, oid, opfname, "
! 						  "opfnamespace, "
! 						  "(%s opfowner) AS rolname "
! 						  "FROM pg_opfamily",
! 						  username_subquery);
! 	}
  
  	res = PQexec(g_conn, query->data);
  	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
***************
*** 3169,3175 **** getAggregates(int *numAggs)
  
  	/* find all user-defined aggregates */
  
! 	if (g_fout->remoteVersion >= 80200)
  	{
  		appendPQExpBuffer(query, "SELECT tableoid, oid, proname AS aggname, "
  						  "pronamespace AS aggnamespace, "
--- 3350,3372 ----
  
  	/* find all user-defined aggregates */
  
! 	if (g_fout->remoteVersion >= 90100)
! 	{
! 		appendPQExpBuffer(query, "SELECT a.tableoid, a.oid, a.proname AS aggname, "
! 						  "a.pronamespace AS aggnamespace, "
! 						  "a.pronargs, a.proargtypes, "
! 						  "(%s proowner) AS rolname, "
! 						  "a.proacl AS aggacl "
! 						  "FROM pg_proc a "
! 						  "LEFT JOIN pg_depend d ON d.objid = a.oid and d.deptype = 'i' "
! 						  "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! 						  "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! 						  "WHERE x.oid IS NULL AND proisagg "
! 						  "AND pronamespace != "
! 			   "(SELECT oid FROM pg_namespace WHERE nspname = 'pg_catalog')",
! 						  username_subquery);
! 	}
! 	else if (g_fout->remoteVersion >= 80200)
  	{
  		appendPQExpBuffer(query, "SELECT tableoid, oid, proname AS aggname, "
  						  "pronamespace AS aggnamespace, "
***************
*** 3315,3321 **** getFuncs(int *numFuncs)
  
  	/* find all user-defined funcs */
  
! 	if (g_fout->remoteVersion >= 70300)
  	{
  		appendPQExpBuffer(query,
  						  "SELECT tableoid, oid, proname, prolang, "
--- 3512,3535 ----
  
  	/* find all user-defined funcs */
  
! 	if (g_fout->remoteVersion >= 90100)
! 	{
! 		appendPQExpBuffer(query,
! 						  "SELECT f.tableoid, f.oid, f.proname, f.prolang, "
! 						  "f.pronargs, f.proargtypes, f.prorettype, f.proacl, "
! 						  "f.pronamespace, "
! 						  "(%s proowner) AS rolname "
! 						  "FROM pg_proc f "
! 						  "LEFT JOIN pg_depend d ON d.objid = f.oid and d.deptype = 'i' "
! 						  "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! 						  "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! 						  "WHERE x.oid IS NULL AND NOT proisagg "
! 						  "AND pronamespace != "
! 						  "(SELECT oid FROM pg_namespace "
! 						  "WHERE nspname = 'pg_catalog')",
! 						  username_subquery);
! 	}
! 	else if (g_fout->remoteVersion >= 70300)
  	{
  		appendPQExpBuffer(query,
  						  "SELECT tableoid, oid, proname, prolang, "
***************
*** 3477,3483 **** getTables(int *numTables)
  	 * we cannot correctly identify inherited columns, owned sequences, etc.
  	 */
  
! 	if (g_fout->remoteVersion >= 90000)
  	{
  		/*
  		 * Left join to pick up dependency info linking sequences to their
--- 3691,3734 ----
  	 * we cannot correctly identify inherited columns, owned sequences, etc.
  	 */
  
! 	if (g_fout->remoteVersion >= 90100)
! 	{
! 		/*
! 		 * Left join to pick up dependency info linking sequences to their
! 		 * owning column, if any (note this dependency is AUTO as of 8.2)
! 		 */
! 		appendPQExpBuffer(query,
! 						  "SELECT c.tableoid, c.oid, c.relname, "
! 						  "c.relacl, c.relkind, c.relnamespace, "
! 						  "(%s c.relowner) AS rolname, "
! 						  "c.relchecks, c.relhastriggers, "
! 						  "c.relhasindex, c.relhasrules, c.relhasoids, "
! 						  "c.relfrozenxid, "
! 						  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
! 						  "d.refobjid AS owning_tab, "
! 						  "d.refobjsubid AS owning_col, "
! 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
! 						"array_to_string(c.reloptions, ', ') AS reloptions, "
! 						  "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
! 						  "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 = 'a') "
! 						  "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) "
! 						  "LEFT JOIN pg_depend dx ON dx.objid = c.oid and dx.deptype = 'i' "
! 						  "and dx.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! 						  "LEFT JOIN pg_extension x ON dx.refobjid = x.oid "
! 						  "WHERE x.oid IS NULL "
! 						  "AND c.relkind in ('%c', '%c', '%c', '%c') "
! 						  "ORDER BY c.oid",
! 						  username_subquery,
! 						  RELKIND_SEQUENCE,
! 						  RELKIND_RELATION, RELKIND_SEQUENCE,
! 						  RELKIND_VIEW, RELKIND_COMPOSITE_TYPE);
! 	}
! 	else if (g_fout->remoteVersion >= 90000)
  	{
  		/*
  		 * Left join to pick up dependency info linking sequences to their
***************
*** 4838,4844 **** getProcLangs(int *numProcLangs)
  	/* Make sure we are in proper schema */
  	selectSourceSchema("pg_catalog");
  
! 	if (g_fout->remoteVersion >= 90000)
  	{
  		/* pg_language has a laninline column */
  		appendPQExpBuffer(query, "SELECT tableoid, oid, "
--- 5089,5110 ----
  	/* Make sure we are in proper schema */
  	selectSourceSchema("pg_catalog");
  
! 	if (g_fout->remoteVersion >= 90100)
! 	{
! 		/* pg_language has a laninline column */
! 		appendPQExpBuffer(query, "SELECT l.tableoid, l.oid, "
! 						  "l.lanname, l.lanpltrusted, l.lanplcallfoid, "
! 						  "l.laninline, l.lanvalidator, l.lanacl, "
! 						  "(%s lanowner) AS lanowner "
! 						  "FROM pg_language l "
! 						  "LEFT JOIN pg_depend d ON d.objid = l.oid and d.deptype = 'i' "
! 						  "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! 						  "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! 						  "WHERE x.oid IS NULL and lanispl "
! 						  "ORDER BY oid",
! 						  username_subquery);
! 	}
! 	else if (g_fout->remoteVersion >= 90000)
  	{
  		/* pg_language has a laninline column */
  		appendPQExpBuffer(query, "SELECT tableoid, oid, "
***************
*** 4992,4998 **** getCasts(int *numCasts)
  	/* Make sure we are in proper schema */
  	selectSourceSchema("pg_catalog");
  
! 	if (g_fout->remoteVersion >= 80400)
  	{
  		appendPQExpBuffer(query, "SELECT tableoid, oid, "
  						  "castsource, casttarget, castfunc, castcontext, "
--- 5258,5276 ----
  	/* Make sure we are in proper schema */
  	selectSourceSchema("pg_catalog");
  
! 	if (g_fout->remoteVersion >= 90100)
! 	{
! 		appendPQExpBuffer(query, "SELECT c.tableoid, c.oid, "
! 						  "c.castsource, c.casttarget, c.castfunc, c.castcontext, "
! 						  "c.castmethod "
! 						  "FROM pg_cast c "
! 						  "LEFT JOIN pg_depend d ON d.objid = c.oid and d.deptype = 'i' "
! 						  "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! 						  "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! 						  "WHERE x.oid IS NULL "
! 						  "ORDER BY 3,4");
! 	}
! 	else if (g_fout->remoteVersion >= 80400)
  	{
  		appendPQExpBuffer(query, "SELECT tableoid, oid, "
  						  "castsource, casttarget, castfunc, castcontext, "
***************
*** 5586,5595 **** getTSParsers(int *numTSParsers)
  	/* Make sure we are in proper schema */
  	selectSourceSchema("pg_catalog");
  
! 	appendPQExpBuffer(query, "SELECT tableoid, oid, prsname, prsnamespace, "
! 					  "prsstart::oid, prstoken::oid, "
! 					  "prsend::oid, prsheadline::oid, prslextype::oid "
! 					  "FROM pg_ts_parser");
  
  	res = PQexec(g_conn, query->data);
  	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
--- 5864,5887 ----
  	/* Make sure we are in proper schema */
  	selectSourceSchema("pg_catalog");
  
! 	if (g_fout->remoteVersion >= 90100)
! 	{
! 		appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, p.prsname, p.prsnamespace, "
! 						  "p.prsstart::oid, p.prstoken::oid, "
! 						  "p.prsend::oid, p.prsheadline::oid, p.prslextype::oid "
! 						  "FROM pg_ts_parser p "
! 						  "LEFT JOIN pg_depend d ON d.objid = p.oid and d.deptype = 'i' "
! 						  "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! 						  "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! 						  "WHERE x.oid IS NULL");
! 	}
! 	else
! 	{
! 		appendPQExpBuffer(query, "SELECT tableoid, oid, prsname, prsnamespace, "
! 						  "prsstart::oid, prstoken::oid, "
! 						  "prsend::oid, prsheadline::oid, prslextype::oid "
! 						  "FROM pg_ts_parser");
! 	}
  
  	res = PQexec(g_conn, query->data);
  	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
***************
*** 5668,5678 **** getTSDictionaries(int *numTSDicts)
  	/* Make sure we are in proper schema */
  	selectSourceSchema("pg_catalog");
  
! 	appendPQExpBuffer(query, "SELECT tableoid, oid, dictname, "
! 					  "dictnamespace, (%s dictowner) AS rolname, "
! 					  "dicttemplate, dictinitoption "
! 					  "FROM pg_ts_dict",
! 					  username_subquery);
  
  	res = PQexec(g_conn, query->data);
  	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
--- 5960,5985 ----
  	/* Make sure we are in proper schema */
  	selectSourceSchema("pg_catalog");
  
! 	if (g_fout->remoteVersion >= 90100)
! 	{
! 		appendPQExpBuffer(query, "SELECT tsd.tableoid, tsd.oid, tsd.dictname, "
! 						  "tsd.dictnamespace, (%s dictowner) AS rolname, "
! 						  "tsd.dicttemplate, tsd.dictinitoption "
! 						  "FROM pg_ts_dict tsd "
! 						  "LEFT JOIN pg_depend d ON d.objid = tsd.oid and d.deptype = 'i' "
! 						  "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! 						  "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! 						  "WHERE x.oid IS NULL",
! 						  username_subquery);
! 	}
! 	else
! 	{
! 		appendPQExpBuffer(query, "SELECT tableoid, oid, dictname, "
! 						  "dictnamespace, (%s dictowner) AS rolname, "
! 						  "dicttemplate, dictinitoption "
! 						  "FROM pg_ts_dict",
! 						  username_subquery);
! 	}
  
  	res = PQexec(g_conn, query->data);
  	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
***************
*** 5749,5757 **** getTSTemplates(int *numTSTemplates)
  	/* Make sure we are in proper schema */
  	selectSourceSchema("pg_catalog");
  
! 	appendPQExpBuffer(query, "SELECT tableoid, oid, tmplname, "
! 					  "tmplnamespace, tmplinit::oid, tmpllexize::oid "
! 					  "FROM pg_ts_template");
  
  	res = PQexec(g_conn, query->data);
  	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
--- 6056,6077 ----
  	/* Make sure we are in proper schema */
  	selectSourceSchema("pg_catalog");
  
! 	if (g_fout->remoteVersion >= 90100)
! 	{
! 		appendPQExpBuffer(query, "SELECT t.tableoid, t.oid, t.tmplname, "
! 						  "t.tmplnamespace, t.tmplinit::oid, t.tmpllexize::oid "
! 						  "FROM pg_ts_template t "
! 						  "LEFT JOIN pg_depend d ON d.objid = t.oid and d.deptype = 'i' "
! 						  "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! 						  "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! 						  "WHERE x.oid IS NULL");
! 	}
! 	else
! 	{
! 		appendPQExpBuffer(query, "SELECT tableoid, oid, tmplname, "
! 						  "tmplnamespace, tmplinit::oid, tmpllexize::oid "
! 						  "FROM pg_ts_template");
! 	}
  
  	res = PQexec(g_conn, query->data);
  	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
***************
*** 5823,5832 **** getTSConfigurations(int *numTSConfigs)
  	/* Make sure we are in proper schema */
  	selectSourceSchema("pg_catalog");
  
! 	appendPQExpBuffer(query, "SELECT tableoid, oid, cfgname, "
! 					  "cfgnamespace, (%s cfgowner) AS rolname, cfgparser "
! 					  "FROM pg_ts_config",
! 					  username_subquery);
  
  	res = PQexec(g_conn, query->data);
  	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
--- 6143,6166 ----
  	/* Make sure we are in proper schema */
  	selectSourceSchema("pg_catalog");
  
! 	if (g_fout->remoteVersion >= 90100)
! 	{
! 		appendPQExpBuffer(query, "SELECT c.tableoid, c.oid, c.cfgname, "
! 						  "c.cfgnamespace, (%s cfgowner) AS rolname, c.cfgparser "
! 						  "FROM pg_ts_config c "
! 						  "LEFT JOIN pg_depend d ON d.objid = c.oid and d.deptype = 'i' "
! 						  "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! 						  "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! 						  "WHERE x.oid IS NULL",
! 						  username_subquery);
! 	}
! 	else
! 	{
! 		appendPQExpBuffer(query, "SELECT tableoid, oid, cfgname, "
! 						  "cfgnamespace, (%s cfgowner) AS rolname, cfgparser "
! 						  "FROM pg_ts_config",
! 						  username_subquery);
! 	}
  
  	res = PQexec(g_conn, query->data);
  	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
***************
*** 5899,5911 **** getForeignDataWrappers(int *numForeignDataWrappers)
  	/* Make sure we are in proper schema */
  	selectSourceSchema("pg_catalog");
  
! 	appendPQExpBuffer(query, "SELECT tableoid, oid, fdwname, "
! 		"(%s fdwowner) AS rolname, fdwvalidator::pg_catalog.regproc, fdwacl,"
! 					  "array_to_string(ARRAY("
! 		 "		SELECT option_name || ' ' || quote_literal(option_value) "
! 	   "		FROM pg_options_to_table(fdwoptions)), ', ') AS fdwoptions "
! 					  "FROM pg_foreign_data_wrapper",
! 					  username_subquery);
  
  	res = PQexec(g_conn, query->data);
  	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
--- 6233,6262 ----
  	/* Make sure we are in proper schema */
  	selectSourceSchema("pg_catalog");
  
! 	if (g_fout->remoteVersion >= 90100)
! 	{
! 		appendPQExpBuffer(query, "SELECT fdw.tableoid, fdw.oid, fdw.fdwname, "
! 						  "(%s fdwowner) AS rolname, fdw.fdwvalidator::pg_catalog.regproc, fdwacl,"
! 						  "array_to_string(ARRAY("
! 						  "		SELECT option_name || ' ' || quote_literal(option_value) "
! 						  "		FROM pg_options_to_table(fdw.fdwoptions)), ', ') AS fdwoptions "
! 						  "FROM pg_foreign_data_wrapper fdw "
! 						  "LEFT JOIN pg_depend d ON d.objid = fdw.oid and d.deptype = 'i' "
! 						  "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! 						  "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! 						  "WHERE x.oid IS NULL",
! 						  username_subquery);
! 	}
! 	else
! 	{
! 		appendPQExpBuffer(query, "SELECT tableoid, oid, fdwname, "
! 						  "(%s fdwowner) AS rolname, fdwvalidator::pg_catalog.regproc, fdwacl,"
! 						  "array_to_string(ARRAY("
! 						  "		SELECT option_name || ' ' || quote_literal(option_value) "
! 						  "		FROM pg_options_to_table(fdwoptions)), ', ') AS fdwoptions "
! 						  "FROM pg_foreign_data_wrapper",
! 						  username_subquery);
! 	}
  
  	res = PQexec(g_conn, query->data);
  	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
***************
*** 5983,5996 **** getForeignServers(int *numForeignServers)
  	/* Make sure we are in proper schema */
  	selectSourceSchema("pg_catalog");
  
! 	appendPQExpBuffer(query, "SELECT tableoid, oid, srvname, "
! 					  "(%s srvowner) AS rolname, "
! 					  "srvfdw, srvtype, srvversion, srvacl,"
! 					  "array_to_string(ARRAY("
! 		 "		SELECT option_name || ' ' || quote_literal(option_value) "
! 	   "		FROM pg_options_to_table(srvoptions)), ', ') AS srvoptions "
! 					  "FROM pg_foreign_server",
! 					  username_subquery);
  
  	res = PQexec(g_conn, query->data);
  	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
--- 6334,6365 ----
  	/* Make sure we are in proper schema */
  	selectSourceSchema("pg_catalog");
  
! 	if (g_fout->remoteVersion >= 90100)
! 	{
! 		appendPQExpBuffer(query, "SELECT fs.tableoid, fs.oid, fs.srvname, "
! 						  "(%s srvowner) AS rolname, "
! 						  "fs.srvfdw, fs.srvtype, fs.srvversion, fs.srvacl,"
! 						  "array_to_string(ARRAY("
! 						  "		SELECT option_name || ' ' || quote_literal(option_value) "
! 						  "		FROM pg_options_to_table(fs.srvoptions)), ', ') AS srvoptions "
! 						  "FROM pg_foreign_server fs "
! 						  "LEFT JOIN pg_depend d ON d.objid = fs.oid and d.deptype = 'i' "
! 						  "and d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
! 						  "LEFT JOIN pg_extension x ON d.refobjid = x.oid "
! 						  "WHERE x.oid IS NULL",
! 						  username_subquery);
! 	}
! 	else
! 	{
! 		appendPQExpBuffer(query, "SELECT tableoid, oid, srvname, "
! 						  "(%s srvowner) AS rolname, "
! 						  "srvfdw, srvtype, srvversion, srvacl,"
! 						  "array_to_string(ARRAY("
! 						  "		SELECT option_name || ' ' || quote_literal(option_value) "
! 						  "		FROM pg_options_to_table(srvoptions)), ', ') AS srvoptions "
! 						  "FROM pg_foreign_server",
! 						  username_subquery);
! 	}
  
  	res = PQexec(g_conn, query->data);
  	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
***************
*** 6467,6472 **** dumpDumpableObject(Archive *fout, DumpableObject *dobj)
--- 6836,6844 ----
  {
  	switch (dobj->objType)
  	{
+ 		case DO_EXTENSION:
+ 			dumpExtension(fout, (ExtensionInfo *) dobj);
+ 			break;
  		case DO_NAMESPACE:
  			dumpNamespace(fout, (NamespaceInfo *) dobj);
  			break;
***************
*** 6563,6568 **** dumpDumpableObject(Archive *fout, DumpableObject *dobj)
--- 6935,6986 ----
  }
  
  /*
+  * dumpExtension
+  *	  writes out to fout the queries to recreate an extension
+  */
+ static void
+ dumpExtension(Archive *fout, ExtensionInfo *extinfo)
+ {
+ 	PQExpBuffer q;
+ 	PQExpBuffer delq;
+ 	char	   *qextname;
+ 
+ 	/* Skip if not to be dumped */
+ 	if (!extinfo->dobj.dump || dataOnly)
+ 		return;
+ 
+ 	q = createPQExpBuffer();
+ 	delq = createPQExpBuffer();
+ 
+ 	qextname = strdup(fmtId(extinfo->dobj.name));
+ 
+ 	appendPQExpBuffer(delq, "DROP EXTENSION %s;\n", qextname);
+ 
+ 	appendPQExpBuffer(q, "CREATE EXTENSION %s;\n", qextname);
+ 
+ 	ArchiveEntry(fout, extinfo->dobj.catId, extinfo->dobj.dumpId,
+ 				 extinfo->dobj.name,
+ 				 NULL, NULL,
+ 				 "",
+ 				 false, "EXTENSION", SECTION_PRE_DATA,
+ 				 q->data, delq->data, NULL,
+ 				 extinfo->dobj.dependencies, extinfo->dobj.nDeps,
+ 				 NULL, NULL);
+ 
+ 	/* Dump Extension Comments */
+ 	resetPQExpBuffer(q);
+ 	appendPQExpBuffer(q, "EXTENSION %s", qextname);
+ 	dumpComment(fout, q->data,
+ 				NULL, "",
+ 				extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
+ 
+ 	free(qextname);
+ 
+ 	destroyPQExpBuffer(q);
+ 	destroyPQExpBuffer(delq);
+ }
+ 
+ /*
   * dumpNamespace
   *	  writes out to fout the queries to recreate a user-defined namespace
   */
*** a/src/bin/pg_dump/pg_dump.h
--- b/src/bin/pg_dump/pg_dump.h
***************
*** 88,93 **** typedef struct SimpleStringList
--- 88,94 ----
  typedef enum
  {
  	/* When modifying this enum, update priority tables in pg_dump_sort.c! */
+ 	DO_EXTENSION,
  	DO_NAMESPACE,
  	DO_TYPE,
  	DO_SHELL_TYPE,
***************
*** 132,137 **** typedef struct _dumpableObject
--- 133,146 ----
  	int			allocDeps;		/* allocated size of dependencies[] */
  } DumpableObject;
  
+ typedef struct _extensionInfo
+ {
+ 	DumpableObject dobj;
+ 	char	   *extname;
+ 	char	   *extversion;
+ 	char	   *custom_class;
+ } ExtensionInfo;
+ 
  typedef struct _namespaceInfo
  {
  	DumpableObject dobj;
***************
*** 511,516 **** extern void sortDumpableObjectsByTypeOid(DumpableObject **objs, int numObjs);
--- 520,526 ----
  /*
   * version specific routines
   */
+ extern ExtensionInfo *getExtensions(int *numExtensions);
  extern NamespaceInfo *getNamespaces(int *numNamespaces);
  extern TypeInfo *getTypes(int *numTypes);
  extern FuncInfo *getFuncs(int *numFuncs);
*** a/src/bin/pg_dump/pg_dump_sort.c
--- b/src/bin/pg_dump/pg_dump_sort.c
***************
*** 28,33 **** static const char *modulename = gettext_noop("sorter");
--- 28,34 ----
   */
  static const int oldObjectTypePriority[] =
  {
+ 	1,							/* DO_EXTENSION */
  	1,							/* DO_NAMESPACE */
  	2,							/* DO_TYPE */
  	2,							/* DO_SHELL_TYPE */
***************
*** 65,70 **** static const int oldObjectTypePriority[] =
--- 66,72 ----
   */
  static const int newObjectTypePriority[] =
  {
+ 	1,							/* DO_EXTENSION */
  	1,							/* DO_NAMESPACE */
  	3,							/* DO_TYPE */
  	3,							/* DO_SHELL_TYPE */
***************
*** 1018,1023 **** describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
--- 1020,1030 ----
  {
  	switch (obj->objType)
  	{
+ 		case DO_EXTENSION:
+ 			snprintf(buf, bufsize,
+ 					 "EXTENSION %s  (ID %d OID %u)",
+ 					 obj->name, obj->dumpId, obj->catId.oid);
+ 			return;
  		case DO_NAMESPACE:
  			snprintf(buf, bufsize,
  					 "SCHEMA %s  (ID %d OID %u)",
*** a/src/bin/psql/command.c
--- b/src/bin/psql/command.c
***************
*** 488,493 **** exec_command(const char *cmd,
--- 488,496 ----
  						break;
  				}
  				break;
+ 			case 'x':          /* Extensions */
+ 				success = listExtensions(pattern, show_verbose);
+ 				break;
  			default:
  				status = PSQL_CMD_UNKNOWN;
  		}
*** a/src/bin/psql/describe.c
--- b/src/bin/psql/describe.c
***************
*** 2720,2725 **** listSchemas(const char *pattern, bool verbose)
--- 2720,2770 ----
  	return true;
  }
  
+ /*
+  * \dx
+  *
+  * Describes extensions
+  */
+ bool
+ listExtensions(const char *pattern, bool verbose)
+ {
+ 	PQExpBufferData buf;
+ 	PGresult   *res;
+ 	printQueryOpt myopt = pset.popt;
+ 
+ 	initPQExpBuffer(&buf);
+ 	printfPQExpBuffer(&buf,
+ 					  "SELECT x.extname AS \"%s\", x.extversion AS \"%s\", "
+                       "x.custom_class AS \"%s\", d.description as \"%s\" "
+ 					  " FROM pg_catalog.pg_extension x \n"
+ 					  " LEFT JOIN pg_description d on x.oid = d.objoid "
+ 					  " and d.classoid = 3696",
+ 					  gettext_noop("Name"),
+ 					  gettext_noop("Version"),
+ 					  gettext_noop("Custom Class"),
+ 					  gettext_noop("Description"));
+ 
+ 	processSQLNamePattern(pset.db, &buf, pattern, false, false,
+ 						  NULL, "x.extname", 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 extensions");
+ 	myopt.translate_header = true;
+ 
+ 	printQuery(res, &myopt, pset.queryFout, pset.logfile);
+ 
+ 	PQclear(res);
+ 	return true;
+ }
+ 
  
  /*
   * \dFp
*** a/src/bin/psql/describe.h
--- b/src/bin/psql/describe.h
***************
*** 54,59 **** extern bool listTSDictionaries(const char *pattern, bool verbose);
--- 54,62 ----
  /* \dFt */
  extern bool listTSTemplates(const char *pattern, bool verbose);
  
+ /* \dx */
+ bool listExtensions(const char *pattern, bool verbose);
+ 
  /* \l */
  extern bool listAllDbs(bool verbose);
  
*** a/src/bin/psql/help.c
--- b/src/bin/psql/help.c
***************
*** 219,224 **** slashUsage(unsigned short int pager)
--- 219,225 ----
  	fprintf(output, _("  \\dT[S+] [PATTERN]      list data types\n"));
  	fprintf(output, _("  \\du[+]  [PATTERN]      list roles (users)\n"));
  	fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
+ 	fprintf(output, _("  \\dx     [PATTERN]      list extensions\n"));
  	fprintf(output, _("  \\l[+]                  list all databases\n"));
  	fprintf(output, _("  \\sf[+] FUNCNAME        show a function's definition\n"));
  	fprintf(output, _("  \\z      [PATTERN]      same as \\dp\n"));
*** a/src/include/catalog/dependency.h
--- b/src/include/catalog/dependency.h
***************
*** 138,143 **** typedef enum ObjectClass
--- 138,144 ----
  	OCLASS_FOREIGN_SERVER,		/* pg_foreign_server */
  	OCLASS_USER_MAPPING,		/* pg_user_mapping */
  	OCLASS_DEFACL,				/* pg_default_acl */
+ 	OCLASS_EXTENSION,           /* pg_extension */
  	MAX_OCLASS					/* MUST BE LAST */
  } ObjectClass;
  
*** a/src/include/catalog/indexing.h
--- b/src/include/catalog/indexing.h
***************
*** 284,289 **** DECLARE_UNIQUE_INDEX(pg_db_role_setting_databaseid_rol_index, 2965, on pg_db_rol
--- 284,295 ----
  DECLARE_UNIQUE_INDEX(pg_seclabel_object_index, 3597, on pg_seclabel using btree(objoid oid_ops, classoid oid_ops, objsubid int4_ops, provider text_ops));
  #define SecLabelObjectIndexId				3597
  
+ DECLARE_UNIQUE_INDEX(pg_extension_oid_index, 3695, on pg_extension using btree(oid oid_ops));
+ #define ExtensionOidIndexId 3695
+ 
+ DECLARE_UNIQUE_INDEX(pg_extension_name_index, 3697, on pg_extension using btree(extname name_ops));
+ #define ExtensionNameIndexId 3697
+ 
  /* last step of initialization script: build the indexes declared above */
  BUILD_INDICES
  
*** /dev/null
--- b/src/include/catalog/pg_extension.h
***************
*** 0 ****
--- 1,61 ----
+ /*-------------------------------------------------------------------------
+  *
+  * pg_extension.h
+  *	  definition of the system "extension" relation (pg_extension)
+  *
+  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/include/catalog/pg_extension.h
+  *
+  * NOTES
+  *	  the genbki.pl script reads this file and generates .bki
+  *	  information from the DATA() statements.
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef PG_EXTENSION_H
+ #define PG_EXTENSION_H
+ 
+ #include "catalog/genbki.h"
+ 
+ /* ----------------
+  *		pg_extension definition.  cpp turns this into
+  *		typedef struct FormData_pg_extension
+  * ----------------
+  */
+ #define ExtensionRelationId	3696
+ 
+ CATALOG(pg_extension,3696)
+ {
+ 	NameData	extname;	  /* name of the extension */
+ 	text		extversion;	  /* version "name" of the extension */
+ 	text        custom_class; /* custom class used by the extension */
+ } FormData_pg_extension;
+ 
+ /* ----------------
+  *		Form_pg_extension corresponds to a pointer to a tuple with
+  *		the format of pg_extension relation.
+  * ----------------
+  */
+ typedef FormData_pg_extension *Form_pg_extension;
+ 
+ /* ----------------
+  *		compiler constants for pg_extension
+  * ----------------
+  */
+ 
+ #define Natts_pg_extension					3
+ #define Anum_pg_extension_extname			1
+ #define Anum_pg_extension_extversion		2
+ #define Anum_pg_extension_custom_class		3
+ 
+ /* ----------------
+  *		initial contents of pg_extension
+  * ----------------
+  */
+ 
+ /* btree
+ DATA(insert ("PostgreSQL" PG_VERSION _null_));
+ */
+ #endif   /* PG_EXTENSION_H */
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 3386,3399 **** DESCR("reload configuration files");
  DATA(insert OID = 2622 ( pg_rotate_logfile		PGNSP PGUID 12 1 0 0 f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ pg_rotate_logfile _null_ _null_ _null_ ));
  DESCR("rotate log file");
  
! DATA(insert OID = 2623 ( pg_stat_file		PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2249 "25" "{25,20,1184,1184,1184,1184,16}" "{i,o,o,o,o,o,o}" "{filename,size,access,modification,change,creation,isdir}" _null_ pg_stat_file _null_ _null_ _null_ ));
  DESCR("return file information");
! DATA(insert OID = 2624 ( pg_read_file		PGNSP PGUID 12 1 0 0 f f f t f v 3 0 25 "25 20 20" _null_ _null_ _null_ _null_ pg_read_file _null_ _null_ _null_ ));
  DESCR("read text from a file");
! DATA(insert OID = 2625 ( pg_ls_dir			PGNSP PGUID 12 1 1000 0 f f f t t v 1 0 25 "25" _null_ _null_ _null_ _null_ pg_ls_dir _null_ _null_ _null_ ));
  DESCR("list all files in a directory");
! DATA(insert OID = 2626 ( pg_sleep			PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2278 "701" _null_ _null_ _null_ _null_ pg_sleep _null_ _null_ _null_ ));
  DESCR("sleep for the specified time in seconds");
  
  DATA(insert OID = 2971 (  text				PGNSP PGUID 12 1 0 0 f f f t f i 1 0 25 "16" _null_ _null_ _null_ _null_ booltext _null_ _null_ _null_ ));
  DESCR("convert boolean to text");
--- 3386,3401 ----
  DATA(insert OID = 2622 ( pg_rotate_logfile		PGNSP PGUID 12 1 0 0 f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ pg_rotate_logfile _null_ _null_ _null_ ));
  DESCR("rotate log file");
  
! DATA(insert OID = 2623 ( pg_stat_file			PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2249 "25" "{25,20,1184,1184,1184,1184,16}" "{i,o,o,o,o,o,o}" "{filename,size,access,modification,change,creation,isdir}" _null_ pg_stat_file _null_ _null_ _null_ ));
  DESCR("return file information");
! DATA(insert OID = 2624 ( pg_read_file			PGNSP PGUID 12 1 0 0 f f f t f v 3 0 25 "25 20 20" _null_ _null_ _null_ _null_ pg_read_file _null_ _null_ _null_ ));
  DESCR("read text from a file");
! DATA(insert OID = 2625 ( pg_ls_dir				PGNSP PGUID 12 1 1000 0 f f f t t v 1 0 25 "25" _null_ _null_ _null_ _null_ pg_ls_dir _null_ _null_ _null_ ));
  DESCR("list all files in a directory");
! DATA(insert OID = 2626 ( pg_sleep				PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2278 "701" _null_ _null_ _null_ _null_ pg_sleep _null_ _null_ _null_ ));
  DESCR("sleep for the specified time in seconds");
+ DATA(insert OID = 3627 ( pg_execute_from_file	PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2278 "25" _null_ _null_ _null_ _null_ pg_execute_from_file _null_ _null_ _null_ ));
+ DESCR("execute queries read from a file");
  
  DATA(insert OID = 2971 (  text				PGNSP PGUID 12 1 0 0 f f f t f i 1 0 25 "16" _null_ _null_ _null_ _null_ booltext _null_ _null_ _null_ ));
  DESCR("convert boolean to text");
*** a/src/include/catalog/toasting.h
--- b/src/include/catalog/toasting.h
***************
*** 43,48 **** extern void BootstrapToastTable(char *relName,
--- 43,49 ----
  DECLARE_TOAST(pg_attrdef, 2830, 2831);
  DECLARE_TOAST(pg_constraint, 2832, 2833);
  DECLARE_TOAST(pg_description, 2834, 2835);
+ DECLARE_TOAST(pg_extension, 3698, 3699);
  DECLARE_TOAST(pg_proc, 2836, 2837);
  DECLARE_TOAST(pg_rewrite, 2838, 2839);
  DECLARE_TOAST(pg_seclabel, 3598, 3599);
*** /dev/null
--- b/src/include/commands/extension.h
***************
*** 0 ****
--- 1,49 ----
+ /*-------------------------------------------------------------------------
+  *
+  * extension.h
+  *		Extension management commands (create/drop extension).
+  *
+  *
+  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/include/commands/extension.h
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef EXTENSION_H
+ #define EXTENSION_H
+ 
+ #include "catalog/objectaddress.h"
+ 
+ /*
+  * create_extension is only set when running a CREATE EXTENSION command, it
+  * allows to register the (INTERNAL) dependancies between the pg_extension
+  * row and the SQL objects created by its installation script.
+  *
+  * For that to work out, all CREATE commands have been modified so that they
+  * will inquire about create_extension and call recordDependencyOn() when
+  * it's set.
+  */
+ extern ObjectAddress *create_extension;
+ 
+ typedef struct ExtensionControlFile
+ {
+ 	char *name;	        /* name of the extension */
+ 	char *version;	    /* version "name" of the extension */
+ 	char *custom_class; /* custom class used by the extension */
+ } ExtensionControlFile;
+ 
+ char *get_extension_install_filename(const char *extname);
+ char *get_extension_control_filename(const char *extname);
+ char * extension_parse_control_line(const char *content, ExtensionControlFile *control);
+ ExtensionControlFile *read_extension_control_file(const char *filename);
+ extern void CreateExtension(CreateExtensionStmt *stmt);
+ extern void DropExtension(DropExtensionStmt *stmt);
+ 
+ Oid get_extension_oid(const char *extname, bool missing_ok);
+ char * get_extension_name(Oid ext_oid);
+ char * RemoveExtensionById(Oid extId);
+ 
+ 
+ #endif   /* EXTENSION_H */
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
***************
*** 348,353 **** typedef enum NodeTag
--- 348,355 ----
  	T_DropUserMappingStmt,
  	T_AlterTableSpaceOptionsStmt,
  	T_SecLabelStmt,
+ 	T_CreateExtensionStmt,
+ 	T_DropExtensionStmt,
  
  	/*
  	 * TAGS FOR PARSE TREE NODES (parsenodes.h)
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 1053,1058 **** typedef enum ObjectType
--- 1053,1059 ----
  	OBJECT_CONVERSION,
  	OBJECT_DATABASE,
  	OBJECT_DOMAIN,
+ 	OBJECT_EXTENSION,
  	OBJECT_FDW,
  	OBJECT_FOREIGN_SERVER,
  	OBJECT_FUNCTION,
***************
*** 1479,1484 **** typedef struct Constraint
--- 1480,1504 ----
  } Constraint;
  
  /* ----------------------
+  *		Create/Drop Extension Statements
+  * ----------------------
+  */
+ 
+ typedef struct CreateExtensionStmt
+ {
+ 	NodeTag		type;
+ 	char	   *extname;
+ } CreateExtensionStmt;
+ 
+ typedef struct DropExtensionStmt
+ {
+ 	NodeTag		 type;
+ 	char	    *extname;
+ 	bool		 missing_ok;	/* skip error if missing? */
+ 	DropBehavior behavior;		/* drop behavior - cascade/restrict */
+ } DropExtensionStmt;
+ 
+ /* ----------------------
   *		Create/Drop Table Space Statements
   * ----------------------
   */
*** a/src/include/parser/kwlist.h
--- b/src/include/parser/kwlist.h
***************
*** 150,155 **** PG_KEYWORD("exclusive", EXCLUSIVE, UNRESERVED_KEYWORD)
--- 150,156 ----
  PG_KEYWORD("execute", EXECUTE, UNRESERVED_KEYWORD)
  PG_KEYWORD("exists", EXISTS, COL_NAME_KEYWORD)
  PG_KEYWORD("explain", EXPLAIN, UNRESERVED_KEYWORD)
+ PG_KEYWORD("extension", EXTENSION, UNRESERVED_KEYWORD)
  PG_KEYWORD("external", EXTERNAL, UNRESERVED_KEYWORD)
  PG_KEYWORD("extract", EXTRACT, COL_NAME_KEYWORD)
  PG_KEYWORD("false", FALSE_P, RESERVED_KEYWORD)
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
***************
*** 442,447 **** extern Datum pg_relation_filepath(PG_FUNCTION_ARGS);
--- 442,448 ----
  extern Datum pg_stat_file(PG_FUNCTION_ARGS);
  extern Datum pg_read_file(PG_FUNCTION_ARGS);
  extern Datum pg_ls_dir(PG_FUNCTION_ARGS);
+ extern Datum pg_execute_from_file(PG_FUNCTION_ARGS);
  
  /* misc.c */
  extern Datum current_database(PG_FUNCTION_ARGS);
*** /dev/null
--- b/src/include/utils/cfparser.h
***************
*** 0 ****
--- 1,18 ----
+ /*-------------------------------------------------------------------------
+  *
+  * cfparser.h
+  *	  Function for parsing RecoveryCommandFile lines
+  *
+  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/include/utils/cfparser.h
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef CFPARSER_H
+ #define CFPARSER_H
+ 
+ bool parseRecoveryCommandFileLine(char *cmdline, char **key_p, char **value_p);
+ 
+ #endif   /* CFPARSER_H */
*** a/src/makefiles/pgxs.mk
--- b/src/makefiles/pgxs.mk
***************
*** 93,102 **** include $(top_srcdir)/src/Makefile.shlib
  all: all-lib
  endif # MODULE_big
  
  
! install: all installdirs
! ifneq (,$(DATA)$(DATA_built))
! 	@for file in $(addprefix $(srcdir)/, $(DATA)) $(DATA_built); do \
  	  echo "$(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/$(datamoduledir)'"; \
  	  $(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/$(datamoduledir)'; \
  	done
--- 93,116 ----
  all: all-lib
  endif # MODULE_big
  
+ # create extension support
+ ifndef CONTROL
+ ifdef MODULE_big
+ CONTROL = $(MODULE_big).control
+ EXTENSION = $(MODULE_big)
+ else
+ CONTROL = $(MODULES).control
+ EXTENSION = $(MODULES)
+ endif
+ endif
  
! $(CONTROL):
! 	touch .control
! 	test ! -f $@ && echo "name = '$(EXTENSION)' \nversion = '$(MAJORVERSION)'" > $@
! 
! install: all installdirs $(CONTROL)
! ifneq (,$(DATA)$(DATA_built)$(CONTROL))
! 	@for file in $(addprefix $(srcdir)/, $(DATA)) $(DATA_built) $(CONTROL); do \
  	  echo "$(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/$(datamoduledir)'"; \
  	  $(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/$(datamoduledir)'; \
  	done
***************
*** 221,226 **** ifeq ($(PORTNAME), win)
--- 235,241 ----
  	rm -f regress.def
  endif
  endif # REGRESS
+ 	if [ -f .control ]; then rm -f .control $(CONTROL); fi
  
  ifdef MODULE_big
  clean: clean-lib
cfparser.v0.patchtext/x-patchDownload
*** a/src/backend/access/transam/xlog.c
--- b/src/backend/access/transam/xlog.c
***************
*** 55,60 ****
--- 55,61 ----
  #include "utils/guc.h"
  #include "utils/ps_status.h"
  #include "utils/relmapper.h"
+ #include "utils/cfparser.h"
  #include "pg_trace.h"
  
  
***************
*** 5018,5117 **** str_time(pg_time_t tnow)
  }
  
  /*
-  * Parse one line from recovery.conf. 'cmdline' is the raw line from the
-  * file. If the line is parsed successfully, returns true, false indicates
-  * syntax error. On success, *key_p and *value_p are set to the parameter
-  * name and value on the line, respectively. If the line is an empty line,
-  * consisting entirely of whitespace and comments, function returns true
-  * and *keyp_p and *value_p are set to NULL.
-  *
-  * The pointers returned in *key_p and *value_p point to an internal buffer
-  * that is valid only until the next call of parseRecoveryCommandFile().
-  */
- static bool
- parseRecoveryCommandFileLine(char *cmdline, char **key_p, char **value_p)
- {
- 	char	   *ptr;
- 	char	   *bufp;
- 	char	   *key;
- 	char	   *value;
- 	static char *buf = NULL;
- 
- 	*key_p = *value_p = NULL;
- 
- 	/*
- 	 * Allocate the buffer on first use. It's used to hold both the parameter
- 	 * name and value.
- 	 */
- 	if (buf == NULL)
- 		buf = malloc(MAXPGPATH + 1);
- 	bufp = buf;
- 
- 	/* Skip any whitespace at the beginning of line */
- 	for (ptr = cmdline; *ptr; ptr++)
- 	{
- 		if (!isspace((unsigned char) *ptr))
- 			break;
- 	}
- 	/* Ignore empty lines */
- 	if (*ptr == '\0' || *ptr == '#')
- 		return true;
- 
- 	/* Read the parameter name */
- 	key = bufp;
- 	while (*ptr && !isspace((unsigned char) *ptr) &&
- 		   *ptr != '=' && *ptr != '\'')
- 		*(bufp++) = *(ptr++);
- 	*(bufp++) = '\0';
- 
- 	/* Skip to the beginning quote of the parameter value */
- 	ptr = strchr(ptr, '\'');
- 	if (!ptr)
- 		return false;
- 	ptr++;
- 
- 	/* Read the parameter value to *bufp. Collapse any '' escapes as we go. */
- 	value = bufp;
- 	for (;;)
- 	{
- 		if (*ptr == '\'')
- 		{
- 			ptr++;
- 			if (*ptr == '\'')
- 				*(bufp++) = '\'';
- 			else
- 			{
- 				/* end of parameter */
- 				*bufp = '\0';
- 				break;
- 			}
- 		}
- 		else if (*ptr == '\0')
- 			return false;		/* unterminated quoted string */
- 		else
- 			*(bufp++) = *ptr;
- 
- 		ptr++;
- 	}
- 	*(bufp++) = '\0';
- 
- 	/* Check that there's no garbage after the value */
- 	while (*ptr)
- 	{
- 		if (*ptr == '#')
- 			break;
- 		if (!isspace((unsigned char) *ptr))
- 			return false;
- 		ptr++;
- 	}
- 
- 	/* Success! */
- 	*key_p = key;
- 	*value_p = value;
- 	return true;
- }
- 
- /*
   * See if there is a recovery command file (recovery.conf), and if so
   * read in parameters for archive recovery and XLOG streaming.
   *
--- 5019,5024 ----
*** a/src/backend/utils/misc/Makefile
--- b/src/backend/utils/misc/Makefile
***************
*** 15,21 **** include $(top_builddir)/src/Makefile.global
  override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
  
  OBJS = guc.o help_config.o pg_rusage.o ps_status.o superuser.o tzparser.o \
!        rbtree.o
  
  # This location might depend on the installation directories. Therefore
  # we can't subsitute it into pg_config.h.
--- 15,21 ----
  override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
  
  OBJS = guc.o help_config.o pg_rusage.o ps_status.o superuser.o tzparser.o \
!        rbtree.o cfparser.o
  
  # This location might depend on the installation directories. Therefore
  # we can't subsitute it into pg_config.h.
*** /dev/null
--- b/src/backend/utils/misc/cfparser.c
***************
*** 0 ****
--- 1,112 ----
+ /*-------------------------------------------------------------------------
+  *
+  * cfparser.c
+  *	  Function for parsing RecoveryCommandFile lines
+  *
+  * This very simple file format (varible = value) is now also used in the
+  * extension control file format
+  *
+  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * IDENTIFICATION
+  *	  src/backend/utils/misc/cfparser.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ 
+ #include "postgres.h"
+ 
+ /*
+  * Parse one line from recovery.conf. 'cmdline' is the raw line from the
+  * file. If the line is parsed successfully, returns true, false indicates
+  * syntax error. On success, *key_p and *value_p are set to the parameter
+  * name and value on the line, respectively. If the line is an empty line,
+  * consisting entirely of whitespace and comments, function returns true
+  * and *keyp_p and *value_p are set to NULL.
+  *
+  * The pointers returned in *key_p and *value_p point to an internal buffer
+  * that is valid only until the next call of parseRecoveryCommandFile().
+  */
+ bool
+ parseRecoveryCommandFileLine(char *cmdline, char **key_p, char **value_p)
+ {
+ 	char	   *ptr;
+ 	char	   *bufp;
+ 	char	   *key;
+ 	char	   *value;
+ 	static char *buf = NULL;
+ 
+ 	*key_p = *value_p = NULL;
+ 
+ 	/*
+ 	 * Allocate the buffer on first use. It's used to hold both the parameter
+ 	 * name and value.
+ 	 */
+ 	if (buf == NULL)
+ 		buf = malloc(MAXPGPATH + 1);
+ 	bufp = buf;
+ 
+ 	/* Skip any whitespace at the beginning of line */
+ 	for (ptr = cmdline; *ptr; ptr++)
+ 	{
+ 		if (!isspace((unsigned char) *ptr))
+ 			break;
+ 	}
+ 	/* Ignore empty lines */
+ 	if (*ptr == '\0' || *ptr == '#')
+ 		return true;
+ 
+ 	/* Read the parameter name */
+ 	key = bufp;
+ 	while (*ptr && !isspace((unsigned char) *ptr) &&
+ 		   *ptr != '=' && *ptr != '\'')
+ 		*(bufp++) = *(ptr++);
+ 	*(bufp++) = '\0';
+ 
+ 	/* Skip to the beginning quote of the parameter value */
+ 	ptr = strchr(ptr, '\'');
+ 	if (!ptr)
+ 		return false;
+ 	ptr++;
+ 
+ 	/* Read the parameter value to *bufp. Collapse any '' escapes as we go. */
+ 	value = bufp;
+ 	for (;;)
+ 	{
+ 		if (*ptr == '\'')
+ 		{
+ 			ptr++;
+ 			if (*ptr == '\'')
+ 				*(bufp++) = '\'';
+ 			else
+ 			{
+ 				/* end of parameter */
+ 				*bufp = '\0';
+ 				break;
+ 			}
+ 		}
+ 		else if (*ptr == '\0')
+ 			return false;		/* unterminated quoted string */
+ 		else
+ 			*(bufp++) = *ptr;
+ 
+ 		ptr++;
+ 	}
+ 	*(bufp++) = '\0';
+ 
+ 	/* Check that there's no garbage after the value */
+ 	while (*ptr)
+ 	{
+ 		if (*ptr == '#')
+ 			break;
+ 		if (!isspace((unsigned char) *ptr))
+ 			return false;
+ 		ptr++;
+ 	}
+ 
+ 	/* Success! */
+ 	*key_p = key;
+ 	*value_p = value;
+ 	return true;
+ }
*** /dev/null
--- b/src/include/utils/cfparser.h
***************
*** 0 ****
--- 1,18 ----
+ /*-------------------------------------------------------------------------
+  *
+  * cfparser.h
+  *	  Function for parsing RecoveryCommandFile lines
+  *
+  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/include/utils/cfparser.h
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef CFPARSER_H
+ #define CFPARSER_H
+ 
+ bool parseRecoveryCommandFileLine(char *cmdline, char **key_p, char **value_p);
+ 
+ #endif   /* CFPARSER_H */
#2David Fetter
david@fetter.org
In reply to: Dimitri Fontaine (#1)
Re: Extensions, this time with a patch

On Tue, Oct 12, 2010 at 08:57:09PM +0200, Dimitri Fontaine wrote:

Hi,

Please find attached a WIP patch for extension's pg_dump support in
PostgreSQL, following design decisions that we've outlined earlier at
this year's and last year's PGCon developer meetings.

What's in the patch?

An extension is a new SQL object with a catalog and two commands to
manage them (reserved to superuser):

CREATE EXTENSION <extension>�;
DROP EXTENSION [IF EXISTS] <extension> [ RESTRICT | CASCADE ];

Kudos!

- User Documentation. Where in the manual do I write it?

Parts belong in Server Administration, others in Server Programming.

- Extension Upgrading

Should this be done by means of 'create extension' or some other
command, like 'alter extension foo upgrade'? The command would
run the SQL script again, which would be responsible for any
steps the extension author might find necessary to run.

As people will want to up- or downgrade extensions to a particular
version, this should probably be something more like ALTER EXTENSION
... SET VERSION [version number | LATEST | PREVIOUS ]... or something
like that.

Cheers,
David.
--
David Fetter <david@fetter.org> http://fetter.org/
Phone: +1 415 235 3778 AIM: dfetter666 Yahoo!: dfetter
Skype: davidfetter XMPP: david.fetter@gmail.com
iCal: webcal://www.tripit.com/feed/ical/people/david74/tripit.ics

Remember to vote!
Consider donating to Postgres: http://www.postgresql.org/about/donate

#3Alvaro Herrera
alvherre@commandprompt.com
In reply to: David Fetter (#2)
Re: Extensions, this time with a patch

Excerpts from David Fetter's message of mié oct 13 11:27:56 -0300 2010:

On Tue, Oct 12, 2010 at 08:57:09PM +0200, Dimitri Fontaine wrote:

- Extension Upgrading

Should this be done by means of 'create extension' or some other
command, like 'alter extension foo upgrade'? The command would
run the SQL script again, which would be responsible for any
steps the extension author might find necessary to run.

As people will want to up- or downgrade extensions to a particular
version, this should probably be something more like ALTER EXTENSION
... SET VERSION [version number | LATEST | PREVIOUS ]... or something
like that.

Does this mean that the control file should contain a version number in
the filename? Otherwise, I don't see how you'd have more than one
version of the control file.

Also, if upgrading is necessary, there will need to be one "upgrade"
control file that says how to upgrade from version N to N+1.

I don't think we should really support the downgrade case. It has the
potential to get too messy -- and for what gain?

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#4Tom Lane
tgl@sss.pgh.pa.us
In reply to: Alvaro Herrera (#3)
Re: Extensions, this time with a patch

Alvaro Herrera <alvherre@commandprompt.com> writes:

Also, if upgrading is necessary, there will need to be one "upgrade"
control file that says how to upgrade from version N to N+1.

I don't think we should really support the downgrade case. It has the
potential to get too messy -- and for what gain?

I think we could leave that to the extension author to decide. Basically,
what is needed is a script file that says how to replace version M by
version N. If the author cares to supply a script for going from
M to M-1, it's no extra logic in the extension manager to be able to
apply that one. In many cases it wouldn't be very practical to do,
but then you just don't offer the script.

regards, tom lane

#5David Fetter
david@fetter.org
In reply to: Alvaro Herrera (#3)
Re: Extensions, this time with a patch

On Wed, Oct 13, 2010 at 11:36:02AM -0300, Alvaro Herrera wrote:

Excerpts from David Fetter's message of mi� oct 13 11:27:56 -0300 2010:

On Tue, Oct 12, 2010 at 08:57:09PM +0200, Dimitri Fontaine wrote:

- Extension Upgrading

Should this be done by means of 'create extension' or some other
command, like 'alter extension foo upgrade'? The command would
run the SQL script again, which would be responsible for any
steps the extension author might find necessary to run.

As people will want to up- or downgrade extensions to a particular
version, this should probably be something more like ALTER EXTENSION
... SET VERSION [version number | LATEST | PREVIOUS ]... or something
like that.

Does this mean that the control file should contain a version number in
the filename? Otherwise, I don't see how you'd have more than one
version of the control file.

Excellent idea!

Also, if upgrading is necessary, there will need to be one "upgrade"
control file that says how to upgrade from version N to N+1.

This, too, is an excellent idea.

I don't think we should really support the downgrade case. It has
the potential to get too messy -- and for what gain?

I think there should be something extension authors should be able to
provide for the downgrade case, given that an upgrade could cause a
system-wide failure.

Unfortunately, the extensions not provided with a downgrade option are
the ones likeliest to need it. Authors who write and test downgrade
code are much less likely to have their upgrades cause such failures
in the first place, but there's not much to do about that.

Cheers,
David.
--
David Fetter <david@fetter.org> http://fetter.org/
Phone: +1 415 235 3778 AIM: dfetter666 Yahoo!: dfetter
Skype: davidfetter XMPP: david.fetter@gmail.com
iCal: webcal://www.tripit.com/feed/ical/people/david74/tripit.ics

Remember to vote!
Consider donating to Postgres: http://www.postgresql.org/about/donate

#6Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Alvaro Herrera (#3)
Re: Extensions, this time with a patch

Alvaro Herrera <alvherre@commandprompt.com> writes:

Does this mean that the control file should contain a version number in
the filename? Otherwise, I don't see how you'd have more than one
version of the control file.

Also, if upgrading is necessary, there will need to be one "upgrade"
control file that says how to upgrade from version N to N+1.

I like both ideas. I'd like to propose that we get back to this part of
the feature later, after the first patch is in. After all, the main goal
is to support dump&restore of extensions. Let's do that first.

If some of you are interested, the development happens here:
http://git.postgresql.org/gitweb?p=postgresql-extension.git;a=shortlog;h=refs/heads/extension

I've written some basic documentation in the "Extending SQL"
chapter (and command man pages too). There's not so much to tell about
the new features, as the goal is for it to fix something quite basic.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#7Alvaro Herrera
alvherre@commandprompt.com
In reply to: Dimitri Fontaine (#6)
Re: Extensions, this time with a patch

Excerpts from Dimitri Fontaine's message of mié oct 13 18:11:21 -0300 2010:

I like both ideas. I'd like to propose that we get back to this part of
the feature later, after the first patch is in. After all, the main goal
is to support dump&restore of extensions. Let's do that first.

Okay. I looked at the code and I have to admit that it seems awkward to
have pg_dump left-joining everything against pg_depend and checking for
NULLs. I wondered if there was a simpler way to go about it, perhaps
using EXCEPT? No specific proposal though.

If some of you are interested, the development happens here:
http://git.postgresql.org/gitweb?p=postgresql-extension.git;a=shortlog;h=refs/heads/extension

Thanks. I managed to retrieve into an already-checked-out copy of HEAD
and it worked pretty well:
git remote add extensions git://git.postgresql.org/git/postgresql-extension.git
git fetch extensions extension:extension

then I could run "git diff master...extension" and see the complete
diff. Of course, I can also see each commit individually. Or
"git checkout extension".

Maybe it would be worthwhile to split the parts that parse a file and
execute from a file, and submit separately. It is obviously
self-contained and serves a useful purpose on its own. It also forces
you to think harder about renaming the parse function :-)

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#8Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Alvaro Herrera (#7)
Re: Extensions, this time with a patch

Alvaro Herrera <alvherre@commandprompt.com> writes:

Okay. I looked at the code and I have to admit that it seems awkward to
have pg_dump left-joining everything against pg_depend and checking for
NULLs. I wondered if there was a simpler way to go about it, perhaps
using EXCEPT? No specific proposal though.

Thanks for your time and review!

The LEFT JOIN WHERE right IS NULL is the first thing that I though
about, if it looks ugly I'll rework the queries, ok.

Maybe it would be worthwhile to split the parts that parse a file and
execute from a file, and submit separately. It is obviously
self-contained and serves a useful purpose on its own. It also forces
you to think harder about renaming the parse function :-)

The cfparser code is extracted from xlog.c and I left it as-is, so the
function is till named parseRecoveryCommandFileLine. If cfparser has a
file name is ok, I could rename the function cfParseOneLine?

I'll send separate patches for that and pg_execute_from_file() then.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#9Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Dimitri Fontaine (#1)
1 attachment(s)
Re: Extensions, this time with a patch

Dimitri Fontaine <dimitri@2ndQuadrant.fr> writes:

Open Items :

- cfparser

Still in attached v1 patch, but will repost separately, as proposed by
Álvaro.

- User Documentation. Where in the manual do I write it?

Chapter 35. Extending SQL looked like a good choice, there it is. Needs
to get expanded with latest additions.

- Naming of the "control" file,
<extension>.{control,registry,install}

Issue still to discuss. I'm happy with the current .control name.

- Handling of custom_variable_classes

This version of the patch address the point. The problem I wanted to
solve is that 'create extension' has no chance to edit the user
configuration, such as custom_variable_classes. Also, in the debian
world it is a policy violation for a package to modify another package's
configuration file.

So I wanted the extension mechanism to be able to append some classes to
the custom_variable_classes without touching the configuration
file. That's why pg_extension has this custom_class column.

The v1 patch, attached, rework this GUC so that it's SUSET. Invalid
variables are now kept aside while processing the configuration, so that
it's possible to scan them again when custom_variable_classes changes,
and to make them available.

Based on this change, postinit now will scan the pg_extension catalog
for the connected database and add all classes defined there. So that if
an extension register its own class, and the user defines some variables
in the configuration file, it's not necessary for him to edit the global
custom_variable_classes any more.

Also, at CREATE EXTENSION time, should an extension specific classes be
given, custom_variable_classes is changed on the fly, and default values
from the control file can get loaded too.

Current code is to be read as a proposal, open to discussion of course:
it seemed to me preferable to write it (having not heard back from my
earlier mail) as an opening.

- User data tables

An extension can install plain relations, and that's fine. The
problem is when the data in there are changed after installing.
Because the name of the game here is to exclude the table from the
dumps, of course the data will not be in there.

The solution would be to offer extension's author a way to 'flag'
some tables as worthy of dumping, I think marking the dependency as
DEPENDENCY_NORMAL rather then DEPENDENCY_INTERNAL will do the trick:

SELECT pg_extension_flag_dump(oid);

That's in the attached patch too.

- Extension Upgrading

That was a mistake to raise this subject this early, it seems wise to
defer that to a later patch. Much remains to be designed here.

- Bugs to fix

There's at least one where drop extension leaves things behind,
although it uses performDeletion(). The dependencies are fine enough
so that the leftover objects are not part of the dump done before to
drop, though.

That well seems to me to be an existing bug that I'm just putting in the
light. The reading of comments from findDependentObjects() makes me
think that following nested DEPENDENCY_INTERNAL levels is broken, but I
didn't have time to figure out how exactly, nor to try to fix.

So, v1 patch attached, and repository available too:

http://git.postgresql.org/gitweb?p=postgresql-extension.git;a=shortlog;h=refs/heads/extension

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

Attachments:

extension.v1.patch.gzapplication/octet-streamDownload
�Az�Lextension.v1.patch�\{W�H������`@����8�1�]\��;�l���mt�%�O&�}������23��9��RWWWUWW������
f�l�jE!�6w[�c�=�������b���������v���CF/�?�8�c�y�;���34O��#n�m|�n���9���i��D����;������I;��913]�����w<|����1gi�����%���5��_}�����������Tc�^I^���6��O������\��,H�TNf2�����Y�c�O��D�?g����b~:�����)�o�lIvN�������]7]]D�����@��}96�p$@�K{��]���:^D1�S��M��@�d����L��\�kIl��$2h�7���eRk�"S���G����
~�\��W��Z �|�
��_��5"�?�|�\����<Y�~q�����]O<e!w��G���2�G�f��,pM����,=��7�
G-�K��jWj�����+�]'�S}��	[�7�Lrr��uaR\3�NV�~��}U����Ib�n&f�5e������e�L����<�q�G�<Z$�3��7s!����W�����������l&H���/�I�k��t��Mw��>����h�a��/&A��1�
N��`���B����
���@w`5���[y��'V���P�D�"�3�
.T2��i��U���HQ�;	#!�H�&���	��<����,,�:����v�p�+�����6
z�q\��a6nl�M�����$�V�x�����<{��K����� �K�5��`Q�#8Q����W�D!��?(��n�e��
uo��������9*��(�7��<��E~Z\�.Z�0'���~�h����v��W���x!7���I�"�WE��J�H���r�336o����
�m�V���c�a���Mu�,v�.X��������!wf
��� 8yL�0y4�����,%F����y��6��T�*/9�3�'/9��;%/�C?��H&$6-���yH�������������q�w ������b��EA�%~��9�(t-zk����%nm��\���!��q(��&a-��L������Y�b��nB�0L$�Sxa?�n	��B?�������i'��h1�.������^2?�?ne)��Y}��-vaz���q6�9�����X-M.%'n����q��BRKJ$a�G���S�3B�(��$��r_J�
�
L���N%$����,���T$<(7`4?��H�}������=wo���o����3j�O�R;v�5���
� ��u�s,!J�1TK
����4	�FBB]���F���j�`�H���Y�[0��<
���r
]���#QW6�����l ��I�?�^�JR<:�j/�vj�"�k�V��c���b]d� ���g��N?����)xX2����JW1�)(���k�ZQ�����A��'f�_P�01y�Wx/�/�M-�.o��2��f�T�P
n7q1�vb�\��K9{/f�:���I"tE"��F��j��
�f��������%�B��#��rY�(\M�!WNS��b�Y����H�����0�h"$��E���e������r������/����f�c2��l���U�E�#��e��]��B]���}�����@^�pN���R������%q#'�@;���U���FZ����-��5.��v�F�.�(W@�v�T�j�2h�L�Y������������K�����K�&��h�B�j����X��]�b���e����(�W�<;�O6|������\	2�{�����`�|�N����;���h0�
�7��?{��!O��(�������	1'���V��BU�����c`�/W�24j����0�L 8�>}q���{%�'
^��$�SJ=K90W�r w�3���~��4sa����
�����4Y����DQvJY����&%xQeh��^����F��9[���5��'��_<��$���D�H��������t;0[���z�Z*��X�������tJ�G'������s��<9Qy��6����\�?��2��gj�
����"�������M�������K5��E��<�������
�������vO�u�`�(�^��o�~$��pF�'-���Z�K��J}��]���ow�����S��H{[�'+�+mr}��^���������9�5-���i�iw+M/2����?kZr~���<��3�Zj��me���r���$v��5K���jECM`/����9��\@@�f������D��9�M@pQz���>�l{���8_��p;s����w������g���2���qP�Bg� CnAP
X��6��5��q�.�@��G��,��\6h���������s1�ta�qz�,L���{i���8��`&.�dW��`��{��T����y|9���}�.0(x)x\Az"W�:J��D��������A,�-��$R�_/v`V��`����FRW ����SZ�"@�`�:�?^\4�\�A���GEm�R\}�2"j��3]v�L��2�u@Q����'��(1y�$���q$�/6�w0��u!]�E�w,v��mk{\�-x��mH�2�x*�OR�u��	����W 8l1�������bF���b��N�������'����pV�v=���a�V���ZS'��0���0^����Sp���>��]�CWJ.�o��+�$����"���y��x������w��d�u�
�'�����@�������C�������x���^r�������I������ �A��Fkd���V^�.�B��[���I��R�����A[i����~��>l���~c���k��X��4]G����&��:K��Y���k�\
��q�#�W ���K?�I�6p���SC��F�P��,�8�M�/�sy-P�I4���&���]�cB�#e�W(�n2��G����r���@.ZlM��]�"N���xd��;"����oJ�D?���RGG���Y�N�e�������&�9�'>I^�e=SM1!&r��F��J�YW���9�X�����Kc&z��g�M8�I�	�U����s��%3��'<���{��+_�b���-V.��M��<�����t�<{��\���r~����R��X�s-&g�$;���D&.���==�7���8�j�P��!�9�,��������[�&,7S�����9�07a�gMb /w�����B��-l(&���{���X��DRy����t���������'�Q����i��?�G���F��p�F��3�� n�����
"�h�0+��S.�9�� �*���j�V'��nY����+� 
t�[��&�d9���&�4x�H��[�P�fD�r�EejQIV9#w��ct����b�����B����XB���
�@3X-���{�\Mi-�+R��9	Y���T�M^RmI�]P����c=	��|^2�j�7��B�B�j	"���M�Q�Bdn]m������;PI������
�;�&T���?N�����gX��pI���w��mh�W��wg���u������h0<������d�����\3��@?y���^�c�Wl�Lxv�r�>z����^�B���H��T����,g�K*jTk
�iA%#f:qV��[���9��[Q�b��]�<����z�
�1q�S��T�����4���>��f�������N����~$�%t�/����HI�7�$����q������<Wa�7�������HZe����������������"�<����I�*����`=�qA�qrn�l[�,�PN�#Q*�cT8����Cl��H'���%�K�u
o-F�0�^���������A���A�3L���&�m�13_#����$�d5��1����'(�$�V��)����-��FW#�G�O���<UC�������������0�y��R��7��E}�`����-Z��	�����,W��n�^��{��t�O�����[�R��%����Rh%	��� ���|�[
��db	��������*/�tkc���"T����4���CP��?A�M�Mo�2�D*!��z��Eo5�j�!��|����i
�����(s�2l�/���upJ��8�|��(.9Q�������~���7�t��"���SJ���`a�!��k��@Q��N+���D��:&z�9X��Y�G�����!���~�K���D'M$�X��8���y_H��9�9������N�
vs���+;���:r��)�O���$������c������G�R���#����Y�^�]�U��^��[ 2����l�����{�y�Q��-��
|V{�<��~`�2�}R�;Z�,��+��z���;5X���������e��&������w!w��y4�d;�)����A��K��Y->v�n4��Td�9"���%����E�����$��k<�j�WQ����o��h��n������Of�_��R��'	���\��|�G-��x2�n��=m�M��1�6����0��!6SZ��#�E$��.�����%?�&��L <�7�t���k�
�r+����m�����nzo{��
��
�;A��������E��q�������u��;��:[N���t����%vET]B�.$Q���%��s���cp����AcU-Yq0d�����4�eUY6�k,�Y;N��E5m�r�e�Q=R�(��S�|B����]�kw�c��;e�o��E.5-������@	��&3T��_K\}SgB�2bP��irf�_Y����n.Y���.Kz��<�Y�E]f��/*d�r��������*�����*��,\(������N��q3�������"�=A���w����Y�$�W-��:�x��C|�N�fj	P���;���<�Eq����P��
s�.���6`s6��B���a������ ��A��$7ZDd��H�
z��l������gQ�@]��J7D�z��nx����:l8^���k����H�`O�O04��a��C�B���N�V�+~vu�X�y_g�sp��j���.�����
&���XI���.��G�2�F{>��<��������>�!�'�8GY�������������g�i�5�
�<Xg7�sU���67��
�9w��"������
���n���Y��D����� ���_B:�1�o�I��%�0�������JQf�������Q��r��7�=6�a�#��_����x�7���3�����m�t�4��|�6q����d�����w���}q&�.����E�e���W���[�����CT?�JK�-�E���y:���00��F:�����}X������[����XGxuz 5\V���N��2�`Ek%��K��(p+X�h����Z3]�/"j�~o/E|k �J��5��neT�� ���\RXZP����d#
����XJ�}� ���,b�*R�=�U���f�jm��A����)��'^Y��XOz�A%M�s���	>�_��p��y��'����|N�%�	]��)o\�O��x����6����u)]�ct��E���N�����Tw���te�'
����m[��i<l,�G:m|�%M�?��]
_���9����q�i��$	f�i�	~M`�QE.>��?��S��;�S���p�=`�H���a�^�Atwg:�A���-��~&�?���o��h��'[��^�b[�iZ�]/���o���H���/�}Z��L�:���[�Z��)�O;�h�x4g�k�����
���u:����o��WYLw�+�O����/�L����L���w�QeT�����(�C
����x���	������sX�&&y�j�|�Q�(%_�.�G#�G��zG�1%��o�[3��I�H2������������������b>�CdE�(LT��}��'�, �g�����,k-[�Q�����~���H*���|���mD�����=������m#����)���F�����=�,�:���e��m���%��FU���v���< AI��M��ne��s0����i�� ��� @�i���
�{ut�iu���������I�������7Nn*�*���A!��k$����.AW������dd�{�T��U��)u���j��<y�l2�f��<���9�TI�4;��<E��#�����r��c3E���fZ���#*�#��J�u�����'M��w���3z�����=g� '�e���8<����
�������~���C������T���]&�t6��'Fu}c@QV��D�O��25�Z��o�(���,Y��)�����B��%���Y+��}4w���|l�5������8"� ��2cA�1��g��z�?��k�8���v��K��'��
��:�h�|�����N�g��a��04����D��1�D3�*#�pk���D��6
#�������2��*y�x*�9T���hl��%��/-�"+&�z.}�����
����,�vr�h��`�'��-�|��)[
aW���.��;����X��5@�C>�����ax%�W�F���=�5����u���������S�6N�*���;O�
���c��@���)�(��_���>�
Er���W�����|�[x	����F�mv����%m�\�6�B�����b�q��D
6���&a�j�/��z-��� A��f�z�Z%~nu��'�3E_���e�i������?��q���K��8[�F��i\f��9N���O��O�DZF����Z�I��\|1�����L��s��XQ��A�[{��j]�5tpt���eH���9+�����������A��R<�z7�GP��>�^�(|����Oo���??����r���C���J��}�*���n�Y+�	#�����u2��p�
+\���'V�Lt��/$��F�dO�Q����l����<w�z�R�;k�8���9�Cd�����6�����s4D���W �a<��se��0�@��qK�MWv�|��y�n��_��$�9��Q�M���:��	�[<VB!�f���[�� �2��e�P��-��Fq"%da�B)�>�r;[*�n3�u~Z��M�Y?��xEK�r�
�6�!�
6�z�0�"��)$�s����pN����9	?w��DDn4t��4�D������BVM�����v��`���DjY�H�D�vG�rb���U�a����!�*
���
@X�,]:eK%Y�C,�]�����%�(���>h����s�	��O���1�0��(��vp�A
�+�J�P��K\K�*@]�As�Z*p	�o��u~���s����wQ�f�F>�#��ej��<_-�;����`;$����#�Yj42��bHD����y����HJ���[eS
�]��(K]rX
�M��83��^������<�dL���%�(��9x8{d�H'P�R�"xj?�q=}!e%�,d/T��;����6��Pa/'
$����"T�r���gNg������F�H���H��R�2/R��+l��4����M9�����w�6c�-�N8�j���r�~��Z�o��' R�/z�6���<��Z��������Ze�Ms���B����5��pu��]��s��p���KQa#�/��7�<�'c�u������(@=\�7�9�$j�"9��dUW_YY�s�=���A$VJ1_��R#Ec�8��m���.��V��]5:c���[�;m#��L�	��3��:9�� DR������.��
T{������]�(7���un��8���H����1<F��@��B*8&��#�U��6�|w7�8����%0����H�������J@o!��n���X��7Z�2Y�q\��V�(�	kZ���IH7���W_�S.Lz&����e�
��DR��!�(b0F1b=F��������}� e��3���b=B��n�����.�����jI�*�/qyF?��wq0<�'5�B����A���q��6��U��y`�A:�]/��$K~�������u�0+<<�kX����+�_��CL,���y�c���%V���]xgF��2����i�6j��$�b�E x&	���j���3��e:3�Ebn����Y
�d��P�9�j� �9����\�����Qd���I�pH���a�+0e8��.�.|X�|��������~���}i�R*�7�/�Ag�A�JA����B*lQ:����O2u�BX�2���E�����\p��R�g=p%��G�h��hN���p�����1����<�f���I0zs��TD����������������gn2�������pt/�)	_����=��
f$�L"
"qJ{��H�)�iPH���-�{�l�|#�����>��a4�{}�Zk����PO�uDj�;��p	��� ������	K�|�R,�'b��6�3�#Z��JX':�X��o9r[�&O�;�����D���o�= Cdz�v�����h�`�)54�iD�}-����~������cRc�������_��X��r�*�v����c��/T��S_:HX����R���E`X1��dt`�X�Q����jk�B�>�� �l������)b4{��Z�FM��_��N�2����b�%�%��8e0�r����'�<�*�a��KeC�T�r��{���*��4��I3��Y���������/��s�M�	u?����A���suN����[��J$<�y�����\Rc������]dH�R
F��KA��u�=�;����~��9�aQQ1*�S��@����p0�:�7��/	���7���<U`3�ct�n������d�|�Z|�I��2�I3	ES�.c6	���7d��BV�Z����%hqB�I���O�����U.�+PO4I����i��Qy�GaZQ*�
�Z�'��$�B����\�a%Z�����.M�u9Y5<
���p�;���N�����bxTs/6�W
��������O��x@�%M���a�4�;�+���V���F�'�G����	J��f���g���?}z�Z��W����B����J���F��.��R��c����e����u��������#��'�r(r������G�� �v��uS��"�����u�RyCr�
R�p��M�-c�M;C���/�C�X���[v��J��i(w��k�gN� =x��x
��X+e�.��*-��#l��*�*���4�J�4h�K�r�L�MO�vp����v���a�a��!�3s�r�2Z�w�j��������i����UK��v�HO�Q
���:��K_:�W�'h	����c.���a�W12�.�>6rj
}�8���������n
�sg�Q���-������W�s�C!v����ya\�"U"K�Qgs��5�R~�I�4���5� m�P|�QhG�1��7�Q��F��N�j(��$�,	�+t����9���A����������:���5���6�Z���
J:T<��z���������Ve�i�V���������9��3��L��X�3&z��
<��^Is��fcW����Z}����c��.���U��SpT_b��Z����P��c/�[����0<H�\A	�,D{�JHD�Otf0�F�Y��)��E ~%_��uf�1x���� ��M�'��Y��,����ee�lG&
���b����P�~&9�]�^�hG��7������o�ta��b������.8^I�*�/[I�Y�$I���J>[`CWr������sF����LN��
:��<2Y��<zlk}�9�4����3K���$�`�{������"�����3(���I>�d���w��d����5#K�[�|��`,���#�sL��?��k���X�I�����Cs���B[<���G|��� J���l������K���i�+^����J�U���6�I�~s:C�i��{�������X���7�Cu��k����T�=��c�4�xy�|��^[:���S��N�G���a^r�#:��yW3�������,��q����9_������w��������]���j�����1����k�]Dy�6^��nXY�s=AFM���fy�$�=P�Q���^=���6�!0v��r�����V��iyC�
�+.��N��U]xdh`_Yd���X���iB��uM����v��+Gl���s6K���h6�4"�)�"*������
�:'�:��_��{�����,�?�!��Z��X�N�	I~2-)�I�@qH��~�'o7�/c'�j��������.lF�����ZUWR�{�R�?y��������U���i������e,��)��h�M���A�.d�R_:����u�Z�PO���kz�k�R�Cr
j�&��br8�|LRbH����V�	���i���mv��f7�Fz9e��i�1��2�;;�h�Y�wd�A�A��/���m���)�=Z�����dP�79����p��`'Z�`]�;�3y�6StIRR�E�/����\R�g�Ke)��7[e����?M]Dsm������K4�Bwp6���Q@�588H�+����)���������������0������H{h�U�����z�E� )A^���XGs�n!��b�������m�`��r���G�~���/31%���vT��f���Z�V5�hP(0������g��i�	H?���`����@�w�
�&��bk���hr�[��U� �A��d���/O����n1�v������#�!�dM��#4GS�����Ml\;8��z�[�H����|!�}z��i�P�F���
������#�aP�h��jPv�FzS����i�^��<"^�?6X���O�c�x�,�Hh7�jm����6�JK�(�;Cute���:.a%
,�:���
'h4��AA#q����n�Z�������q�[�m�d!�4~#��y\��9�mYpS2�����!��R�R��;�g:�9V��O|���n�I��\Tp�^���y�9NZI����!�{���-[
bq���>!��8ly�<�VF�������
�`nx���&s�O7�{���VM��:#D������J�M���-e-DH�1�K�;����o[g�4��7�k��dSz ����Hk{������q5�'*>����2�C���y|9CW3�����H�iPN��	��� J��hz�����'?<7������������;C^j���%��wM�����!�V����)��
�%�:]�u
�$�+��B�O]d�e�] ��w��u���pV��u9���Jg�e���g-"�����@�g�>�6�F97�H����6�B��.G�P��d�P���{��<�j��z�j��_�����{l�����'�����^�<��d,���G�_�h_^]���>;;�hO���Eu4CW���h��r�p��b��z����tt�3vK����]>6�V��m56�dy�����N��~�������0������,�����������y����;����W��������f���q!e�H�7w�O7�!�F:���e��wv����	j#{��$���
o����e�����S��#g�.�����1}&�_c-J)�~x>I�K��{;���I������D��2
����*j1��Jdad�@��$�;�����<>R��G�Dy�����[[|����'i%�Z��u�z���L�O*��((I�@�_6��Zj3l��
gt�$�R<����3���O�;�f�:g��G�8�K�N,e
�a��L��"�#>��t����v�e+����������B��k3~��v,��e�����J����oxHa���-
���~��
`��f��{}h��5�g���C��%%F)�����A�,?>A�~e4���m��,r	p����2S�O3[0�G:=�V&����O��4����^|���K��m8�??4�0���qf1%�{�r�w	t�_�o��m�_;�p�����������"=�4N���X�����c?�,AJ�	Dm/��!{���b�����k% @�O_�g�����������������������������������e�R�r�*���y�{��vF
�=}��n�����J�{�^������E��-�>_��l4PY�7���f}���u-�
���t9����F�8�P�U�G��4�2�L�4����o����/�KX��:�x����i�F�47u�/Ms�����V59���X�-������/ytSv��������c�WbV�gjR��5�������c��^S�����<i2�,!���m��4�m�����^^tN�{G���a�$�����	�s8��i^�<��)9�;&T(��t��V��y!_v�%%J�O
�J_�-[�|	a.��������� �hX��� 30pjP�qn��G���	��k��a�;�
�M~�XQ��;\z@������8{y��h/����Q��-���>���|�t*C�9srn�����(a�������f��1��usN�������k�w����W����rrh��
�d���������I� UA%�����#d�'mA���I��G;����q�&7O�6['Mb��,�f��-�E�)���MY�{�����t����������7��R\cNFyFc�n���R�oC��]ZK*H3��p�������]J�h������6Y0�P��e��j���l/y0D\��k�3]-�d����{C�y������y�k����Y�D�B�p��ho�_
�0�p@r���2U���X
|���~Y-��U��~\�F���o��M�o��j��[5�����
��P5h^a�9�+}?
���n�8�eS���&��P~�s�E�����FP]GOO�4��]��7��P?%-`������.,t�?����������7>9���z��q��6�>i���[�yq�/;��Kqx�o]�����e�sJ_�d���sye�n����n[�Oes 2��I$|�����}����K����av�}~�8�N��	������y����+2���u���	���Q"�/����QF]5_vN~G�K�Q����8:9k����������������v���~���.���o.�^�#�$(����JI�fj���@����7�{�����0���r�������\��T�/���6���������S�0O�r��]I)q����v����n����5W��_�6�jh<��9��?t���9Y�N���������i��y(M���Yoh�������A�O��1{��������G(��O�v��Aq,������[���N�Chk7?t����������x\����<S�vty2�2�<���E�h�H{,~m�T����R�����Zr�
��@��G�D� T=���/�K��Xw8w�&f%+��|���C���V�w~��#<v�)�l�����v����j�y�|RX~�$�A�.����7��"e��3N�-���Eo=�����k�������Gf��8X��������Kr�����������&L�	<��%3_:�����o�
p9m�;�����������9^x���U��P����yz|�<��g�mJ{'Z'�n��]��M<2�hv�r��\��V��o�/��s�������v��yl�y�����:�C�3,�x�~	�����a�G���^���?��!���*lA�&4�M��U ��O���������x��J��6U��lm[��eX�!yE0+�o����o��]����sW���Q)��J����]1��]U�[�\"���*��}I����:Z����z(W��=���� ��8o���f�J�����e��%FQ�5f����w~N)������^}O����_�����=���G�i(S���e������X��x�	V�h�|i�S�����x��8�+��9(Q�ny����Bg�kl�c�W���
PO����(�������������]o{��UQ�`�%�$�����l�lx�������	2�-[{�]m���3��Q��l=��s@�
��}]��s{( �k=V�c��^��$�9~�-�~%L.��	��TM&o���$��+%���H�(������76-����M	�oo�C���bH� ���W���2�-��d�]S�e�&���h�)����t����ox����
%�D7��n@76�u87�.`e��Q��w/_��O�
��f���,�s*�l7����`Biy]<���S���_�63V�nD)'�����������Q�����=�s��C\�h�� ]��PjR*���`R�8�f7��0'�=M��h?a��� xak��q���8�&���W�1�'���������
�F��C��;G����]���mn�S���)�c�Rb��Q�O�1��7��H�y��^��4�R�K�����j=%B;.y��z��2hO���6����M��4���V��O��������U��a��f�,-K����������Mo��a�PDT����}�)�{q�����+ha�O�J�E+j�0?L
��P�F��<})���=1�K����g�T�e
(��9
V�,L��
��Y�\EW�@3s6�u@D4�9���;[*lf��f��=1��O9i�
/��A�T���"��q�\�3I�����~?�&�J8����sG�A���K���Y�C�7
��]+���a��4X���D7=z@b&~�lm��F�;?�z9�T���E�`�2N�>������VX���,@G���c�~��p�	�a�`��b�p;�g<����<��$���2�R��(����2P
���q����&��$�8��O6`Fo��7h&������1ji(M���9��l���C����_3����Q3~���l3����;O|H_�K, f�!`���U�������p�8����r����vfw�&U��[�MYK���5��j��xf�y^@=�����wQ�.l��s�Hb�)F����S��|H��9��d��9��U����=�WS���[f��KU�BLn����r��S�@S��#����q8�_C�\MnV�������A0���1�:�01O����`������j���L	x��r�,��by����T�S)�k�\O�O��<��1��/�h��"2F_��1 �l���"y|Kg�?�4��l 1���Z!>����-pi&������	|��~�:�3E�>����{�������������xD��=c�T��v��u��?}���)b%�[�_O��1|MP��Os�bi4�$��&��0���zL������k�]����N9����O��c]6�^u;����&3����'�iN`���������
�?CF�"��� ��7~�������p�)�I����J6(QPL�*,�3��l7@�|<���pIx��Sac`�����S��{1LU@��7��?{Gg��squ~����p.��6�b�=�4��D���'!p�+JjO�RU~��l"��$e�^�P`{�T��<�`N���-��*�a�e[P?��br���^���?������������������&�h��~��(���WLm�!�}�<5F�YP�s�j�3���R��an�&S�O�[mp���6�3%Q+�SLC�&673S^a�'�����v�$T��'Wr{���1��0����8�)b6#y�y�� C�G�-���������^R���a���8h9�Pa��IM���4�g��@H�I)#�:�j8���"�p'������S�0��HC��o��iptl>�2���	�����s+Hz�W'�������7!�d��/l�S�u) 4�n�����>����96�-K��V�eTK��(LL}2�*h<������
a�2��jG��p����cH�E��t��aeQ%	����=$Dv�O�M�o�I��U� T���X��\�`��YPE5~��-�vK�<��@��(�Gt���u����kx8�.0�ms��$���B��2��=T�R��y�V�9Fw�t��x������Y�A�iG��'���1}�+"���E�5����4?f�x!�CF02.��)RN���',��S����$Q���vm�����P����jx�}d�d�+�Sz�"�������k��
���,�r��y��iW&��VS��hD��s��r����~��9�L_QT)f�b�������KY�����(���9��|9��zs���R�Q���"�_�I`��1r����VI�hje[��t��U-�
"�(���)O�`����O������[�����OQ���u{����������D>�)E���7�97�J��I�u�d�40�W-y��W?���@u9��D:���t@��1���|�T������*����w6�0��c�<LK��.����2f�R�QI��p��0�R����6��k��u~~t�<�����i��}Q�*X�Ro��!?���0��Y���1�J�[��������R���Z���{�+_
�����������jw�_��F)���.�d�a��Ej���2�D�5�`��8���@A�{v��)���T'��C����\��D�O:�?T��<@!���7���{>����)�Fv�V�����W�~��	�1�'|�wd�7Q"1��@vQRxT�	�N�M���L`��C)�W��0K	���XN0�$*�I:���Mm��o6f����_�na���;��]��������L�*�&!�0�����l�fVM�r�I��j%tv������T�B����>9qH�2&.?�c�5�5��S��5\bb��;��^c"�R��P��`�TNa3�{2�h"�*bc0�E���]��I���T�5q6Q-x�����7%��1������K�H����5�<WG�CI�	�����1��x
HAe��8	�2����g@�J�96�$�<Q)���9in�������d5kS"LAg�$o5X� +v��B�3T�L@��q����)T��G���x�����A5*���W��(+z@���T�Q��%�_L�6Mc���2��@K�jL���;%w5sw���3�����^]/���S��E�h��:��oA����y�\)�f>E��d�y�c��y������!�U P�1����)EK2��%d��Bj#XcB{y�gE�
m��z�����I>_w�$l�6� �	�!�"������l��`��l�%��S����Ok������U��Vb�ui�`�A�
�3tkdJj�����`	����kXO��iM��1�����Ta�Q���Y~�$����Y�	��91%�I�2������/��L����M�ew	4�����`\��jp��5%���X[A���x�����&G��`J={�C�l�������RVp%��5Y\����U05����t���r���X��U��d��E=�*��]���f
��D+�������Z(#	+5 ~_�A-iw��!G&n����Luj!���VFm8(K��������O�GrT�+rn������AAXyJ,�v�(������J����+���x�W�=J���V�f*]�H/���A��L���d�LT?P�u+ui)�.g��C��c�;J
���2	���N �B��
%jJ�.�y#R?�L#��D7�:c��J���555PN��0�'{�N*�rz��U2I0O�����_�P��{�&���o���H�Rj?2��=�l���$�Q��h�6�X���T��P�_�D*�l*oHM���l�R���W-N#�'�����RIA�M9�m0aK>B�=�:�3D+��Y�0~yd��F��d6��.U{q$HR�]u��� B���n�t�aj���}�>�y�&5ULg��;�kJ��[�0��h���V���@��}ED��7%-�6��B���U�����uE�K�jt�����$0��$fd3"Pg����w[����)�f�����?�n>�N�V(�V/����VK��(��{��o�m�}����|�E�A���t>��%,e�����H���,�X����oy���������,�ol��F���q�S�K4B_�������eG�%���	��� ��q��h1�"�8�l���������v���:�d,T�?I:F�(&���R4Xe��	7�<~'�����u3�����c{����{�r��*�f�q�	�"p�Y&z�p>�Ke_Q) ,T>0���&�A����Y;O��F�����Bvh���Q����ix��}+L_�0�������?d�m/�b�����
�[c�Ad�� �F�$������n\�$��]S�b��K�	_�=�ct�1Mo��}���*g��B/���!<u
a��`_>��4�Z*�M��� ����|
�����L���u4�x�v�d� p�.�PH�yp����67-�)$����F�@��XBe�{���7!�`RV�{��v��nv�������������r��������S������������/�a��Q0�Io�I���QM�I$s�m�qH��#<�8Qh�^6������V��^6����}���S�+��� )�e�Z����0�p���]�L�Y�M��c�d�>GV'��C6��d�,MY��{�DT *"#���{����r�7�1��F�^�&?�,��\�TM��bf&Vt����O_�^5/�H0y����<���������l�n4�}��q�|����W�EZ*�����4yC�/t���^�z%.N
>�,���9�������$]zH@���tyb���\Y�N���������9��' !�TL�hZY&<��,i��Dw���%����,�����;�t/{���+���|{uv�f�O�A��C��R��1#�J��A��o��������}4�_B�m,�C���x`T����:�(z=��t2`zx�rr4��e6oF��'���8Fk7!��t"�M0	bx��1J��,X=��b����R	��{B��QK]���7tY�C��|Q�x��Y�*
��7���~�D���s��tg9�����?���/���c%�i �2��m��Fv�.�_���C ;�$�P���6�)�����Y��:;V~AT�P���
{�f�BH�����z����q�e��+����"�R�=�}�7�	�\�JRo�@�e�����to���t����q�Q)����#�C
0��)�B�zS�$U)T�5;��4����Ec�KY��sD���X��>��x�.�X�_��� -�����c{E����0��O
�
��I��A���/�:��)���8����~�x"J�{�_��M�1k����<f����/�����;���Uj����]���/<%���6��wX�4R�u����U��s�cw�i�"��fz�K1�����]�oj`|�����@����-}?Um��:pa�����kI(T��&��I�fR�O�$��@��
���jK�,�C���%+���������u	�@��VX���|���+:9=�#�R1
��l4��>&ct�4�<���o����cA����-������1��G����I"	�3gB�����M���4������`�E��DP���Sv`I���@I��GXfI���d�[����'�x����*��;�4*���kH����cZB��o���5b�|����?�
"�`olZ4�E���]��\�
�~�wP��������:Y��+�P����������-�=1����k!4��%�����>e�*S\S�z|T��|�5�D6�������jd����"��Qi��8��GZ����D���,T��R��:\ -�iK�@��G������=�]3e[#�H��g�[�)u���x?����C����~���WooVo�����6�d��&�Jw�G���@"Ta��
���D�qg�}#3����������]�[-���� J�����t�>����1�Kf���u877F��O,<<�n;
����,E�������M�_�C�43���R��Jz�&�Mc��B�u�W�*�~y��nnD�&K��>kW����nv\*����Q��|��(t�������k�x�?m�����7��o���i�G;���2?�.��'&����Y��p��-��f����wt7��`�W+�SZl��"�&���V����
����s�?�t3��CH�T0{����*�;����$���LGBk'�:��!	�+
�6�<w��'W�4���'�A�������'l��������h�1�������!���g^�"�kKs"^�|{�<�u��n��I1������T���Z'�)����Rr�T[����$@2-��M����{�YCy�M�p�%��T���XW�h%���a0b��@�I�	s�q0_�S�*cM��������P�����|�E�K��K��e��`3���'���\N���4��R%�h0�V�M�(�
>�y�KRV#JX	�����-,>������/`k�=�
��;�L�4�u�58��k�g9� � ��e��J���N>g����}o{C
&���g��i�@UiE1�G4�L)������~"���rR�RQ1�	�
"���	��'Yz�bv�R���]��^������<�#w4���{�!o�����LG��u9/�����o��p���=���b�=6�?A;v�v��5|��D|`��t]�,��Z�0�VP��f�^��m�������s����������k�7cw�DZ�H	�����]����9�a
�~T���`�|�����_�������OH����z�G�J�-R�*
���TD�LZ4V~tcb�N�5
b4��bbS�����U��BZ�����<��8%��YG����h�Ka�R����8"C.�$����
��*9
EH��w��C�"V�2"��`�+�h�����d��{`0`
�������w���6�����$U`�B3"2�!n��r��P��u(B�Y���J��P�9�����g�z����av��Q-��K)��E�/���	�
�g���]����2>�D_��b��7���=�����qnj?e��(�#���*��(����f���g��b�L�����~J�A����H�/lW�p�[o0OI����iw�z�<������.��3i"?gS����R���EN����ZJ���#:�n��3�c�#2�{��$�i����k��(�2�D���R�M����!��������~Nn���[�J��p�8��m�����:��~��;�����G��o��C����~7��#~���W�RIC����s
��sF��i�9�#Fjw�`�^�����[^������ii��(�H��Q"��Q,�zP�G��:�����4Y���p�i��Wx���Fob^o	�Sv��H���A�z�~V�i��]'������;.��W7�$����n�?���QRi��&d���3d$�$A�E����2@6i�O"7�Z����'c5�"�Q���)�xQ�Y����=g�0�����U
P�L�X)���j8��W1�F��LK������3����V��O�%+;M�7ww�����G
'~|��Mob`X�^�(�h4�=���v�5�Q�����CT!��4�@�^ck[_�e�R^Z��@�-bo�������K��
���"A�[��Q��E|,5h3i��}Y��-#LOf�l��N�*5�(���(��TE�=����"�FJ��t653�jB�b�>+?���0��%]�����\��g2�^�q�������fb�V�T�[�h@�9��"��1��|����Q��0Xv���S�8�x7�7p���Rm@�b�|�_%��T�b��8���D$]6	�����gZC�K�\-j���a��H�'���0�td0NG2/SJ
m�!V�
C�$.��	�5+~|e}�
�^L(� 60��Q�-�j�}��O�x�>$J�����5V�6�.�^Z�\V/��aW+�n"#VS�F_e�����/�oT�z�Q�G.������	�9�����u����>��<�"������E��6_���0�����L0x�����8h�9��%�<��R��YR����|i����(n�d�Vy�H��y3JH�d[�#��]r$��3#���,l���f�O#�gP����B����
�nlq;FMRX!��T��5X��\)�9{�+���f���X�na;���E��E-O�������H�F����"���s	y�g|�����Ox� >����-�<�����6����Lb����6xr6�P�[����H���w�~�����E���e�/(�\U4�"�F�R�|$)����������?�]+l�zp!��I����y��m�1�����y�\�4����:zA3������T_���B��P��Je�?�W�u��`> 
id�J�q8M�k�T�����l�R��������+���2��,����j���5F^�������U���:�p�(��fTkBHA�@5@��g�C����g�*l�,%d5vkX�u&�l�Lj�Oq-��h��&������\���i_���)r��8���_�7,�������
����Y��W���/�S#�����ab�����Kp#$�( 7�|�V/�Fys�8�����e�b���s���h��.��WmP9A��

���h}<A���=�V�Q��B�Iah����h�y�*^��{���=�i�=���j��i�����l����:�dS]jj������4pf�����#����J�J��P%bB���)����4��A����d-�t�'�u�����77�K�S�P���+Yo�/�����?����|�9=�g��{��+~_�����KCb�T����x��.�`�(���������!O��T�M���v�c�K�e�����m��`!�������0~��.oc4�m���L3U'j�&y�p5�F��������%����f*�y�+���g�������d��!I�x��&a?�W2UF\���;��y��4�gc����m��KFS�-�r�M�S_<U��n�$����%!�3!�~����`2++
,�S��(*�J���F�"��G��L�������	����O��k��p!���3��)�'�������w��e1�~C���l�z�$��hT�
�C�j�Qec����D��.��H�d���ku�v����(��v�:��)E|P�te�������V,���\yJe���M�����CO��:&���U��h+�b����k\���s�����j�iM�1fl����s�p���Wj#�Lg���l(��J�$sE��$�A�5F8v��L����z��5��O�h����]�X6%�A��p�j���_f�X}��^%v�G*����C��������:����HZ��FV������~��V����4(���	�0��V�v3�����%/�4c�/i&p�C����@A�P�cs���F%���������[�����.���>�y�Ir��=d��d�(Z{g��l�9�z�=�McX�S�R�o;�����P����QM��fl �~^F���"JMm` tM}�o7a�$����!��8�	��6oov�rC&����3�`8�~����M�@!6�[uo�����S�~�������(���������_�����Y��?���(�v�z�����'<,����y��x=���Z�l�?����6F���?[�4,!J��Di�4Q����*}TE{�:~�U�T��u��S�����
����(�f!P?OV������Y���{@+h	`5�o�	"��PV�f�6�eL#��������o��&��gjn�����7�l�J�m���"�����d�C�)0���L���Y#� LBvi�&[�G�Ua(����M����.6���U?���O���p@�m��1��9�Z�k!�B��ew����9���/#r��Z�mno�.E�2�e)���~h��aM��3��jF��Af�6���q��>y(	��~�y��MsH����g��!=h;�`�ck���eh�������NR����mn)�S�$����q����z��1;��t5��|)��h6��~@4��h0�	Y`i��\booX��1��T�+��HL��k1���V�m#�;�M�^U
@�%�����1�v7wrW���Y�w�� �C�{�����2�ab5�����^�GC�WkT��u���AZ�5�'\|='2I���N.#�T`G�H�Hd��~"��%����r3e��hV?a&���8�5���s�����u�i<��P*I7�D��E�.�����x5@��>%��>Y%=<7}LX]�]\E "�J>-�n#4��}U�7{bv��6i$�T�� ^����C�x��gA9��4���Q^��<��d$i%5����U�/���^�O]>��K�6P5��1�e�xc<�e�m���\v��k�Y��^Z�mt�4/4o0~�x�� � ��yN:u�����K@�O������v!o��bN��wR`F���^���D�3�������T�7��h���sz�����j����w���i���J�����J��^��u;�������8�Dw&q��G[{[[����'�e�~R�s��yx "�6����h�&FO3��S\�"{�����6�-l���OFg�����4��cT�
G��M3�@_��4�%i0��1$���"�I��;x�.�=dU�E�����E�`�`�n���(� @9��Jh�BJ������?'���ox���T��(����5��K;fi��8
������a���=�M��������m7v?#� ���,H�,X�h�r�(�1��hP���(��D��� "n���m�>P9�g��3v�������o8t��8	cA��S1
����E%%��&��a1L���D������r�U��9{2��4m��i2�����?�ZV�oo�32a��lZ�����)-U-����������9V�V������_�����,������{K���og�����m���$�0���~lZk��O�*`0E[-�T�3J/�hA�,����On�����c�O��k{����w?#_�d`z%����X^�H0^=`����B�'��v�6���
O�o��w��������s��'k�����m�n�d�R�������B�SO���r��'��<W�'�����b��8_������l���n�����������V�D%k�����he�b��N���>b����s�a���Q��x����w��M�����yO�<��`	������S���v�;����_+.��\.�v5��9��?U��1�%��[p��tyF��U7�r9�F��.F�zX��O��w���%%<s�<�w�yG�S*���&}��4/.�?T��X]YQ��$H��%Z��	2��e��7
� �GY����p�x|e%�H��{�IVp������;��!/V��w���i�
�M8;��������_�K���R4�=��,����3)k@�E���n������.$r����Ca��p����0����?�9#P���?��;���CR��jz(kJM�1|����'u��tH�=�{�'�h��	�����l�|F���<����[��%�����>!"��R�]�#��T	&
nY�,�O�������S�=���A���A�����9������l�J��;f4c��f�;{[������� T���2/�4�\�~XIZ8�����.f�1R��\�������FO�/���f�}�Fdv�4�h.;�nT�:A���������&����^Ir2*7��MT���
��H�Ip�;d�Jfdp�_���N�3��2�
��/2��#���#��Lz�}N��0���
Fb�G�[���b,z������jd�R
�R�lk�5?�/Y$��f8N;��=�,��;��4����\h$_&�(���cNX�u�n^���/W�=I�{���x)~t�����\�8C��CI��z����}8i`�z�0�.Pj��;�h���M]�E�D���rn���%$Q{���4q�L���/Ce�����Q(i�Zpg<q/���e 2#[�K��'(�qk�9Wh��HYb,��[XW
���J��#U�E'U����;��jj}Az���Eo��xo������g@�2	�����(�7q�	Y�y,����B�j�l^|����� �s��0�"��M8�iF�{f�(o�d/����$q�<����<���S����ON�g2���W��u����3�)us��g'���H)�p�<#�
�IIu0��.�;���Y��=w����A�����	�Xm�f�G����Q?73����3ND���<��j;��{���Kj=p���s�����$_�#�s*���1:�r�i�@?��\5�:�����%�����a���J��z���;�f���Usl���dF�,�kI|t�,�]�e]Y��'rg����;T��i�7���v��y�����7��ie�d{Y
��h�;�j'����sI,`=W)��Kt:4B�+r�5����k��75�����z������;�nc&�����f)6
��F}��o4$������� ����3�f�@�z6�}
_(}<��
�����
�h�@�(���3I��K��%O�X!*��c �}TW�r�+��U���#K2"�+�W���J&����o�/��Q��<H��}���������n��=oK�(Q�d�����c�����y�
qr���d	$���kYR�u#���+0�~?�,�#�?�<�S$��Hn�;��]�o�`���%�Wp��Ha�����zy��:����w�*���
��5�:��.`0|H�)�kP�zB�Q4:O=Ln����ri�����y�h�����,=�8��K���Z����O�K��(���������������l���}���d��������&��6�Y��;G���5%6����/��@�xkfo5�c�c����v�V��>9	#��'���E9K��y���P�tN���N�[��'��e
�Q���4J.����cP��y|Jy�K_�R��KK�,����K�f�X�}���=
��q��U!<\{��Th��r1�QAOR�K�j����w��g���\�I�%k,d�8a|c7���S��i���j���6��F���f�w~����^e%/;���	-K��$A[�z31Z�]P�V��5�_Q-���=�H������p����4�������	�=G��$�v.	uK��[���%81B��svZL������42Y��2�S� 3�_�uI��g$��V�`���=��htx�T��;�����K�q\�}����K������	*�	f�FY��zb���<�����
%g���B��O���O~?�7//��?3��y�Z��K��dcN+��g+q�z���<�7��t8o��N��Iyo����l�.�����G�`�J��5�N�J�����iYM��J����)'&=S�?6��p��3�%��l"�?�[m]��Y4��-��������Y�~I�\A����`��h��u��v{Gg���i���x���T����(KB�����l��wN�u�����rY��G���'%(:��
�a_foe�L�%,v�������{\K�f/�������g�J�d� G���Ps0���ar+�������m�EX���W�^����}'����MS���9���y��a�]C���}MQ��H���Q�s-�F7��wU����%�`�)��%?���)F�S\�~�c�����G���	��%n���/���n�?��x):8���G�GD�4����5��}
�)�YdfB���O��p B��	�xt��
cw�0�M������1�]}��0lH�Sdx�2���W��I�(W���4��,&��#�z�}��U��'�i��jiy��\�|7�V����a���3������[n<�
3#�)J�~p�U]xC2��%����	Nw+�����m**������<ml�7<(��7q��8E���7��h�q�������EpCFr�W��$����`���NB��K�>=��E�������am:�X��	_D �K�U�@5(��)�9�3���o�*��8�g-���.:
�1��8?�N��7�F*��J2����������WV���aVM��t*�����:���#�jzf[�8�������@+Q��a��'g�
{�Z+!��`�+�d�h	"�p�IJ����.��WQ��U�(�UV�u��M�>E8O������� {�h���U��'�S���w��	�&��������X-��1�g���Em����w�@<.q����x
�2f������>�t:�*��'ni�(�^�*�������I$YYi�-ib�>���I������C�u$W��S ��	_E�/��{x�����\w8d���0����,�Ng.��qg���67�5�m]TV1����
OQ��	���D}�s���[!��I������i�fu��
Q�3|C�)���~�w������O�ew1��4���
4xm,��&���#�' �������X������I������X ����{�
�w�I����}�'�{�[rxd����d���p�C^$R%��������p���
���p�D�l#TD�m,^Y��R3����x_/m���y�#P�8F_��0'�����e]0tnw�qk]�#����)%����G��(�+K#���X���/>5����
����>�X�d��:��c*��u��``�DB�����@=���Y�����J�S�a�����nm����A��&t%��3�t�;��h��D6w�<����N��c�z���X�B���{�&'�hHB�~}��mT���������a��%�W�	r�P�Th@����s`�^Gz!&�-�i��#��h���m���gcs>T��� �����N6���������z8���05K��|6S[r���k�,�����a,k��%�u����J��&5N������:�#�G��q�&B)|��} $Rv���mO�,������b,R�����	hX���:��$���%V�DM�����,�	���~^yy��^�Jd�DhO.n�g]�R� ib?��T�A���������m�x���R	U�{���n������u�����"{�Y�����K�=����gs�l���?HM���q���J�/��oHo*��E�
��q4�*W
��g��v���!v����
3��� ��"����:�3���"���x6��^�(8�HXx"L�5����n7��I�U���Er��*��}�������)j��8A�=��%��$r��m
S���M���#�Ps	-�D����� d�t4���o��g@,�d�������f8�e�%^B��"q���}=�xp�/��&�*��
S8G�G/�$�|���%��H%kN/�7a+��q4:��E���X+���x��7�*���jsi:��
s�\����� 5�r�=%�YV7����r��X�r6X��cti�t����s6�~P�d�[��T~��!����e�]f	��p"W�RPI�x��=`%�p�F��l�LNB��
���V��2�6��3G���n:N��>5����Y�r���g!^��=���q���w/zm���!6L�rn��E0��d����3PE3���r~�f~��N��~�N����r&6�P���aU����.{�+`�_�Q���7M�b���s���Fe4����#��7�U�tN_�H��y�Gg��y�m���v[���.*�E�����3K���*�������c��Z	�������}�?����T^�x�S���nv�������)�H>�����������9�����\PK|��v����������9v�����W����X�%�d$�0>++�����;jW���/��We���L�-3��}w�����&�rq=�BW�w�(��gh	��x�����/�����N���	���������G�)f�����1��b�ty��]~�+�-
KF�_��7M�Y�����}�q��j���3_������R-s~��g����.Q?�$ ��A�}����y�����E�6��**����c�D�.j����,i%_	��P�N������	9
�W��`Q��$�^��*)K!OV��W$�!�d�����X����\</2��
G��n�w�) �}qc���d��R���Q(��{l5���]K�q��@�I�g��8����V`��\���]���Vt|�|;�6�U_8���������U�I�lA�����4�~�oY�sC�Y���&��FJ��-<�J�=��q0�i�2xC���N�0����o��i�T0�����i'Q\2w�wa�oIjH�E�Z��������e����w�B<���)�2�����Uc�VuG������� 	��q�b��@1"���M�SX�?@��:"���R�X�I�r���S���yE�����}o����7U����Y����!s3�������b���(r����M�r~������������d<
�����=�m����j^h}@��O��`�&��	�����G����bT�K~��TI�\�}�rY�j�e�6����j[JR� V-�:?DR_!-��
��������,����f��0�b�F�~�����h����o6�;�fc?w"���[y;��d���{�c�"}��	�f�Q�z�x�����Sf����%�<�g��_Y�
������eq�
��"
��7���=.Q�g�@�hp�LE6CK9�����fK�c7l���sk�@?=k�����u)��9^���C8�PA�g?��F� �3+vL#$���
C�����V��������Oo�&��k����Px�k�M)������m�T��^��j��(��q���~<��<��/��N���������H)�����&�j8	~��
n�*��Z��������QT���o���'d����?�U?��C��H�~Q���yrB��A-�A�v���s@A��W���V������� ��~�_�B�R`��Fj�
�������Ku��^^����m��D��a6��jM`Tl���\�����HwQ]����	I�������i4��
�>�[<O����_}%�TV����k����������9���"XG���&���1�X�|_|5��P�����{��fh_T��..Q��zh5!�a7���V���c\�8��{7@V����hc���$�+(�??C�t"�����0d�T�B�7���������yRz�
#10Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Alvaro Herrera (#7)
2 attachment(s)
Re: Extensions, this time with a patch

Alvaro Herrera <alvherre@commandprompt.com> writes:

Maybe it would be worthwhile to split the parts that parse a file and
execute from a file, and submit separately. It is obviously
self-contained and serves a useful purpose on its own. It also forces
you to think harder about renaming the parse function :-)

So, you will find two new branches for those purposes at the repository,
named cfparser and pg_execute_from_file:

http://git.postgresql.org/gitweb?p=postgresql-extension.git;a=summary

Please find attached the patches extracted from those branches. Note
that currently, the main "extension" branch still contains the
modifications, I intend to merge/rebase that against the master after
the commits.

I've also merged the master repository into my feature branch, and git
just did it all by itself. I like it when the tools are helping! :)

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

Attachments:

cfparser.v1.patchtext/x-patchDownload
*** a/src/backend/access/transam/xlog.c
--- b/src/backend/access/transam/xlog.c
***************
*** 55,60 ****
--- 55,61 ----
  #include "utils/guc.h"
  #include "utils/ps_status.h"
  #include "utils/relmapper.h"
+ #include "utils/cfparser.h"
  #include "pg_trace.h"
  
  
***************
*** 5018,5117 **** str_time(pg_time_t tnow)
  }
  
  /*
-  * Parse one line from recovery.conf. 'cmdline' is the raw line from the
-  * file. If the line is parsed successfully, returns true, false indicates
-  * syntax error. On success, *key_p and *value_p are set to the parameter
-  * name and value on the line, respectively. If the line is an empty line,
-  * consisting entirely of whitespace and comments, function returns true
-  * and *keyp_p and *value_p are set to NULL.
-  *
-  * The pointers returned in *key_p and *value_p point to an internal buffer
-  * that is valid only until the next call of parseRecoveryCommandFile().
-  */
- static bool
- parseRecoveryCommandFileLine(char *cmdline, char **key_p, char **value_p)
- {
- 	char	   *ptr;
- 	char	   *bufp;
- 	char	   *key;
- 	char	   *value;
- 	static char *buf = NULL;
- 
- 	*key_p = *value_p = NULL;
- 
- 	/*
- 	 * Allocate the buffer on first use. It's used to hold both the parameter
- 	 * name and value.
- 	 */
- 	if (buf == NULL)
- 		buf = malloc(MAXPGPATH + 1);
- 	bufp = buf;
- 
- 	/* Skip any whitespace at the beginning of line */
- 	for (ptr = cmdline; *ptr; ptr++)
- 	{
- 		if (!isspace((unsigned char) *ptr))
- 			break;
- 	}
- 	/* Ignore empty lines */
- 	if (*ptr == '\0' || *ptr == '#')
- 		return true;
- 
- 	/* Read the parameter name */
- 	key = bufp;
- 	while (*ptr && !isspace((unsigned char) *ptr) &&
- 		   *ptr != '=' && *ptr != '\'')
- 		*(bufp++) = *(ptr++);
- 	*(bufp++) = '\0';
- 
- 	/* Skip to the beginning quote of the parameter value */
- 	ptr = strchr(ptr, '\'');
- 	if (!ptr)
- 		return false;
- 	ptr++;
- 
- 	/* Read the parameter value to *bufp. Collapse any '' escapes as we go. */
- 	value = bufp;
- 	for (;;)
- 	{
- 		if (*ptr == '\'')
- 		{
- 			ptr++;
- 			if (*ptr == '\'')
- 				*(bufp++) = '\'';
- 			else
- 			{
- 				/* end of parameter */
- 				*bufp = '\0';
- 				break;
- 			}
- 		}
- 		else if (*ptr == '\0')
- 			return false;		/* unterminated quoted string */
- 		else
- 			*(bufp++) = *ptr;
- 
- 		ptr++;
- 	}
- 	*(bufp++) = '\0';
- 
- 	/* Check that there's no garbage after the value */
- 	while (*ptr)
- 	{
- 		if (*ptr == '#')
- 			break;
- 		if (!isspace((unsigned char) *ptr))
- 			return false;
- 		ptr++;
- 	}
- 
- 	/* Success! */
- 	*key_p = key;
- 	*value_p = value;
- 	return true;
- }
- 
- /*
   * See if there is a recovery command file (recovery.conf), and if so
   * read in parameters for archive recovery and XLOG streaming.
   *
--- 5019,5024 ----
***************
*** 5147,5153 **** readRecoveryCommandFile(void)
  		char	   *tok1;
  		char	   *tok2;
  
! 		if (!parseRecoveryCommandFileLine(cmdline, &tok1, &tok2))
  		{
  			syntaxError = true;
  			break;
--- 5054,5060 ----
  		char	   *tok1;
  		char	   *tok2;
  
! 		if (!cfParseOneLine(cmdline, &tok1, &tok2))
  		{
  			syntaxError = true;
  			break;
*** a/src/backend/utils/misc/Makefile
--- b/src/backend/utils/misc/Makefile
***************
*** 15,21 **** include $(top_builddir)/src/Makefile.global
  override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
  
  OBJS = guc.o help_config.o pg_rusage.o ps_status.o superuser.o tzparser.o \
!        rbtree.o
  
  # This location might depend on the installation directories. Therefore
  # we can't subsitute it into pg_config.h.
--- 15,21 ----
  override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
  
  OBJS = guc.o help_config.o pg_rusage.o ps_status.o superuser.o tzparser.o \
!        rbtree.o cfparser.o
  
  # This location might depend on the installation directories. Therefore
  # we can't subsitute it into pg_config.h.
*** /dev/null
--- b/src/backend/utils/misc/cfparser.c
***************
*** 0 ****
--- 1,112 ----
+ /*-------------------------------------------------------------------------
+  *
+  * cfparser.c
+  *	  Function for parsing RecoveryCommandFile lines
+  *
+  * This very simple file format (varible = value) is now also used in the
+  * extension control file format
+  *
+  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * IDENTIFICATION
+  *	  src/backend/utils/misc/cfparser.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ 
+ #include "postgres.h"
+ 
+ /*
+  * Parse one line from recovery.conf. 'cmdline' is the raw line from the
+  * file. If the line is parsed successfully, returns true, false indicates
+  * syntax error. On success, *key_p and *value_p are set to the parameter
+  * name and value on the line, respectively. If the line is an empty line,
+  * consisting entirely of whitespace and comments, function returns true
+  * and *keyp_p and *value_p are set to NULL.
+  *
+  * The pointers returned in *key_p and *value_p point to an internal buffer
+  * that is valid only until the next call of parseRecoveryCommandFile().
+  */
+ bool
+ cfParseOneLine(char *cmdline, char **key_p, char **value_p)
+ {
+ 	char	   *ptr;
+ 	char	   *bufp;
+ 	char	   *key;
+ 	char	   *value;
+ 	static char *buf = NULL;
+ 
+ 	*key_p = *value_p = NULL;
+ 
+ 	/*
+ 	 * Allocate the buffer on first use. It's used to hold both the parameter
+ 	 * name and value.
+ 	 */
+ 	if (buf == NULL)
+ 		buf = malloc(MAXPGPATH + 1);
+ 	bufp = buf;
+ 
+ 	/* Skip any whitespace at the beginning of line */
+ 	for (ptr = cmdline; *ptr; ptr++)
+ 	{
+ 		if (!isspace((unsigned char) *ptr))
+ 			break;
+ 	}
+ 	/* Ignore empty lines */
+ 	if (*ptr == '\0' || *ptr == '#')
+ 		return true;
+ 
+ 	/* Read the parameter name */
+ 	key = bufp;
+ 	while (*ptr && !isspace((unsigned char) *ptr) &&
+ 		   *ptr != '=' && *ptr != '\'')
+ 		*(bufp++) = *(ptr++);
+ 	*(bufp++) = '\0';
+ 
+ 	/* Skip to the beginning quote of the parameter value */
+ 	ptr = strchr(ptr, '\'');
+ 	if (!ptr)
+ 		return false;
+ 	ptr++;
+ 
+ 	/* Read the parameter value to *bufp. Collapse any '' escapes as we go. */
+ 	value = bufp;
+ 	for (;;)
+ 	{
+ 		if (*ptr == '\'')
+ 		{
+ 			ptr++;
+ 			if (*ptr == '\'')
+ 				*(bufp++) = '\'';
+ 			else
+ 			{
+ 				/* end of parameter */
+ 				*bufp = '\0';
+ 				break;
+ 			}
+ 		}
+ 		else if (*ptr == '\0')
+ 			return false;		/* unterminated quoted string */
+ 		else
+ 			*(bufp++) = *ptr;
+ 
+ 		ptr++;
+ 	}
+ 	*(bufp++) = '\0';
+ 
+ 	/* Check that there's no garbage after the value */
+ 	while (*ptr)
+ 	{
+ 		if (*ptr == '#')
+ 			break;
+ 		if (!isspace((unsigned char) *ptr))
+ 			return false;
+ 		ptr++;
+ 	}
+ 
+ 	/* Success! */
+ 	*key_p = key;
+ 	*value_p = value;
+ 	return true;
+ }
*** /dev/null
--- b/src/include/utils/cfparser.h
***************
*** 0 ****
--- 1,18 ----
+ /*-------------------------------------------------------------------------
+  *
+  * cfparser.h
+  *	  Function for parsing RecoveryCommandFile lines
+  *
+  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/include/utils/cfparser.h
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef CFPARSER_H
+ #define CFPARSER_H
+ 
+ bool cfParseOneLine(char *cmdline, char **key_p, char **value_p);
+ 
+ #endif   /* CFPARSER_H */
pg_execute_from_file.v0.patchtext/x-patchDownload
*** a/src/backend/utils/adt/genfile.c
--- b/src/backend/utils/adt/genfile.c
***************
*** 7,12 ****
--- 7,13 ----
   * Copyright (c) 2004-2010, PostgreSQL Global Development Group
   *
   * Author: Andreas Pflug <pgadmin@pse-consulting.de>
+  *         Dimitri Fontaine <dimitri@2ndQuadrant.fr>
   *
   * IDENTIFICATION
   *	  src/backend/utils/adt/genfile.c
***************
*** 30,35 ****
--- 31,46 ----
  #include "utils/memutils.h"
  #include "utils/timestamp.h"
  
+ #include "tcop/pquery.h"
+ #include "tcop/tcopprot.h"
+ #include "tcop/utility.h"
+ #include "access/transam.h"
+ #include "access/xact.h"
+ #include "utils/resowner.h"
+ #include "utils/snapmgr.h"
+ #include "parser/analyze.h"
+ #include "access/printtup.h"
+ 
  typedef struct
  {
  	char	   *location;
***************
*** 264,266 **** pg_ls_dir(PG_FUNCTION_ARGS)
--- 275,459 ----
  
  	SRF_RETURN_DONE(funcctx);
  }
+ 
+ /*
+  * Read a file then execute the SQL commands it contains.
+  */
+ Datum
+ pg_execute_from_file(PG_FUNCTION_ARGS)
+ {
+ 	text	   *filename_t = PG_GETARG_TEXT_P(0);
+ 	char	   *filename;
+ 	FILE       *file;
+ 	int64       fsize = -1, nbytes;
+ 	struct stat fst;
+ 	char       *query_string = NULL;
+ 
+ 	CommandDest dest = DestNone;
+ 	MemoryContext oldcontext;
+ 	List	   *parsetree_list;
+ 	ListCell   *parsetree_item;
+ 	bool		save_log_statement_stats = log_statement_stats;
+ 	bool		was_logged = false;
+ 	bool		isTopLevel;
+ 	char		msec_str[32];
+ 
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 				 (errmsg("must be superuser to get file information"))));
+ 
+ 	/*
+ 	 * Only superuser can call pg_execute_from_file, and CREATE EXTENSION
+ 	 * uses that too. Don't double check the PATH. Also note that
+ 	 * extension's install files are not in $PGDATA but `pg_config
+ 	 * --sharedir`.
+ 	 */
+ 	filename = text_to_cstring(filename_t);
+ 
+ 	if (stat(filename, &fst) < 0)
+ 		ereport(ERROR,
+ 				(errcode_for_file_access(),
+ 				 errmsg("could not stat file \"%s\": %m", filename)));
+ 
+ 	fsize = Int64GetDatum((int64) fst.st_size);
+ 
+ 	if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
+ 		ereport(ERROR,
+ 				(errcode_for_file_access(),
+ 				 errmsg("could not open file \"%s\" for reading: %m",
+ 						filename)));
+ 
+ 	if (ferror(file))
+ 		ereport(ERROR,
+ 				(errcode_for_file_access(),
+ 				 errmsg("could not read file \"%s\": %m", filename)));
+ 
+ 	query_string = (char *)palloc((fsize+1)*sizeof(char));
+ 	memset(query_string, 0, fsize+1);
+ 	nbytes = fread(query_string, 1, (size_t) fsize, file);
+ 	pg_verifymbstr(query_string, nbytes, false);
+ 	FreeFile(file);
+ 
+ 	/*
+ 	elog(NOTICE, "pg_execute_from_file('%s') read %d/%d bytes:", filename, nbytes, fsize);
+ 	elog(NOTICE, "%s", query_string);
+ 	 */
+ 
+ 	/*
+ 	 * Code pasted from postgres.c:exec_simple_query, main differences are:
+ 	 * - don't override unnamed portal, name it after filename instead
+ 	 * - don't start nor finish a transaction
+ 	 * - don't set stats or tracing markers
+ 	 */
+ 	oldcontext = MemoryContextSwitchTo(MessageContext);
+ 	parsetree_list = pg_parse_query(query_string);
+ 	MemoryContextSwitchTo(oldcontext);
+ 
+ 	isTopLevel = false;
+ 
+ 	foreach(parsetree_item, parsetree_list)
+ 	{
+ 		Node	   *parsetree = (Node *) lfirst(parsetree_item);
+ 		bool		snapshot_set = false;
+ 		const char *commandTag;
+ 		char		completionTag[COMPLETION_TAG_BUFSIZE];
+ 		List	   *querytree_list,
+ 				   *plantree_list;
+ 		Portal		portal;
+ 		DestReceiver *receiver;
+ 		int16		format = 0; /* TEXT */
+ 
+ 		commandTag = CreateCommandTag(parsetree);
+ 
+ 		/* If we got a cancel signal in parsing or prior command, quit */
+ 		CHECK_FOR_INTERRUPTS();
+ 
+ 		/*
+ 		 * Set up a snapshot if parse analysis/planning will need one.
+ 		 */
+ 		if (analyze_requires_snapshot(parsetree))
+ 		{
+ 			PushActiveSnapshot(GetTransactionSnapshot());
+ 			snapshot_set = true;
+ 		}
+ 
+ 		/*
+ 		 * OK to analyze, rewrite, and plan this query.
+ 		 *
+ 		 * Switch to appropriate context for constructing querytrees (again,
+ 		 * these must outlive the execution context).
+ 		 */
+ 		oldcontext = MemoryContextSwitchTo(MessageContext);
+ 
+ 		querytree_list = pg_analyze_and_rewrite(parsetree, query_string,
+ 												NULL, 0);
+ 
+ 		plantree_list = pg_plan_queries(querytree_list, 0, NULL);
+ 
+ 		/* Done with the snapshot used for parsing/planning */
+ 		if (snapshot_set)
+ 			PopActiveSnapshot();
+ 
+ 		/* If we got a cancel signal in analysis or planning, quit */
+ 		CHECK_FOR_INTERRUPTS();
+ 
+ 		/*
+ 		 * Create a portal to run the query or queries in. Name if after the
+ 		 * given filename. If there already is one, silently drop it.
+ 		 */
+ 		portal = CreatePortal(filename, true, true);
+ 		/* Don't display the portal in pg_cursors */
+ 		portal->visible = false;
+ 
+ 		/*
+ 		 * We don't have to copy anything into the portal, because everything
+ 		 * we are passing here is in MessageContext, which will outlive the
+ 		 * portal anyway.
+ 		 */
+ 		PortalDefineQuery(portal,
+ 						  NULL,
+ 						  query_string,
+ 						  commandTag,
+ 						  plantree_list,
+ 						  NULL);
+ 
+ 		/*
+ 		 * Start the portal.  No parameters here.
+ 		 */
+ 		PortalStart(portal, NULL, InvalidSnapshot);
+ 		PortalSetResultFormat(portal, 1, &format);
+ 
+ 		/*
+ 		 * Now we can create the destination receiver object.
+ 		 */
+ 		receiver = CreateDestReceiver(dest);
+ 		if (dest == DestRemote)
+ 			SetRemoteDestReceiverParams(receiver, portal);
+ 
+ 		/*
+ 		 * Switch back to transaction context for execution.
+ 		 */
+ 		MemoryContextSwitchTo(oldcontext);
+ 
+ 		/*
+ 		 * Run the portal to completion, and then drop it (and the receiver).
+ 		 */
+ 		(void) PortalRun(portal,
+ 						 FETCH_ALL,
+ 						 isTopLevel,
+ 						 receiver,
+ 						 receiver,
+ 						 completionTag);
+ 
+ 		(*receiver->rDestroy) (receiver);
+ 
+ 		PortalDrop(portal, false);
+ 
+ 		if (!IsA(parsetree, TransactionStmt) && lnext(parsetree_item) != NULL)
+ 		{
+ 			CommandCounterIncrement();
+ 		}
+ 	}
+ 	PG_RETURN_VOID();
+ }
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 3386,3399 **** DESCR("reload configuration files");
  DATA(insert OID = 2622 ( pg_rotate_logfile		PGNSP PGUID 12 1 0 0 f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ pg_rotate_logfile _null_ _null_ _null_ ));
  DESCR("rotate log file");
  
! DATA(insert OID = 2623 ( pg_stat_file		PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2249 "25" "{25,20,1184,1184,1184,1184,16}" "{i,o,o,o,o,o,o}" "{filename,size,access,modification,change,creation,isdir}" _null_ pg_stat_file _null_ _null_ _null_ ));
  DESCR("return file information");
! DATA(insert OID = 2624 ( pg_read_file		PGNSP PGUID 12 1 0 0 f f f t f v 3 0 25 "25 20 20" _null_ _null_ _null_ _null_ pg_read_file _null_ _null_ _null_ ));
  DESCR("read text from a file");
! DATA(insert OID = 2625 ( pg_ls_dir			PGNSP PGUID 12 1 1000 0 f f f t t v 1 0 25 "25" _null_ _null_ _null_ _null_ pg_ls_dir _null_ _null_ _null_ ));
  DESCR("list all files in a directory");
! DATA(insert OID = 2626 ( pg_sleep			PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2278 "701" _null_ _null_ _null_ _null_ pg_sleep _null_ _null_ _null_ ));
  DESCR("sleep for the specified time in seconds");
  
  DATA(insert OID = 2971 (  text				PGNSP PGUID 12 1 0 0 f f f t f i 1 0 25 "16" _null_ _null_ _null_ _null_ booltext _null_ _null_ _null_ ));
  DESCR("convert boolean to text");
--- 3386,3401 ----
  DATA(insert OID = 2622 ( pg_rotate_logfile		PGNSP PGUID 12 1 0 0 f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ pg_rotate_logfile _null_ _null_ _null_ ));
  DESCR("rotate log file");
  
! DATA(insert OID = 2623 ( pg_stat_file			PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2249 "25" "{25,20,1184,1184,1184,1184,16}" "{i,o,o,o,o,o,o}" "{filename,size,access,modification,change,creation,isdir}" _null_ pg_stat_file _null_ _null_ _null_ ));
  DESCR("return file information");
! DATA(insert OID = 2624 ( pg_read_file			PGNSP PGUID 12 1 0 0 f f f t f v 3 0 25 "25 20 20" _null_ _null_ _null_ _null_ pg_read_file _null_ _null_ _null_ ));
  DESCR("read text from a file");
! DATA(insert OID = 2625 ( pg_ls_dir				PGNSP PGUID 12 1 1000 0 f f f t t v 1 0 25 "25" _null_ _null_ _null_ _null_ pg_ls_dir _null_ _null_ _null_ ));
  DESCR("list all files in a directory");
! DATA(insert OID = 2626 ( pg_sleep				PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2278 "701" _null_ _null_ _null_ _null_ pg_sleep _null_ _null_ _null_ ));
  DESCR("sleep for the specified time in seconds");
+ DATA(insert OID = 3627 ( pg_execute_from_file	PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2278 "25" _null_ _null_ _null_ _null_ pg_execute_from_file _null_ _null_ _null_ ));
+ DESCR("execute queries read from a file");
  
  DATA(insert OID = 2971 (  text				PGNSP PGUID 12 1 0 0 f f f t f i 1 0 25 "16" _null_ _null_ _null_ _null_ booltext _null_ _null_ _null_ ));
  DESCR("convert boolean to text");
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
***************
*** 442,447 **** extern Datum pg_relation_filepath(PG_FUNCTION_ARGS);
--- 442,448 ----
  extern Datum pg_stat_file(PG_FUNCTION_ARGS);
  extern Datum pg_read_file(PG_FUNCTION_ARGS);
  extern Datum pg_ls_dir(PG_FUNCTION_ARGS);
+ extern Datum pg_execute_from_file(PG_FUNCTION_ARGS);
  
  /* misc.c */
  extern Datum current_database(PG_FUNCTION_ARGS);
#11Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Dimitri Fontaine (#10)
2 attachment(s)
Re: Extensions, this time with a patch

Dimitri Fontaine <dimitri@2ndQuadrant.fr> writes:

So, you will find two new branches for those purposes at the repository,
named cfparser and pg_execute_from_file:

http://git.postgresql.org/gitweb?p=postgresql-extension.git;a=summary

I updated the pg_execute_from_file branch with documentation for the
function, and added documentation (catalog, functions, custom classes)
to the main feature branch too.

The cfparser patch didn't change, the current version is still v1.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

Attachments:

extension.v2.patch.gzapplication/octet-streamDownload
pg_execute_from_file.v1.patchtext/x-patchDownload
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
***************
*** 13895,13900 **** postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
--- 13895,13907 ----
         <entry><type>record</type></entry>
         <entry>Return information about a file</entry>
        </row>
+       <row>
+        <entry>
+         <literal><function>pg_execute_from_file(<parameter>filename</> <type>text</>)</function></literal>
+        </entry>
+        <entry><type>void</type></entry>
+        <entry>Executes the <acronym>SQL</> commands contained in a file</entry>
+       </row>
       </tbody>
      </tgroup>
     </table>
***************
*** 13933,13938 **** SELECT (pg_stat_file('filename')).modification;
--- 13940,13954 ----
  </programlisting>
     </para>
  
+    <indexterm>
+     <primary>pg_execute_from_file</primary>
+    </indexterm>
+    <para>
+     <function>pg_execute_from_file</> makes the server
+     execute <acronym>SQL</> commands to be found in a file. This function is
+     reserved to superusers.
+    </para>
+ 
     <para>
      The functions shown in <xref linkend="functions-advisory-locks"> manage
      advisory locks.  For details about proper use of these functions, see
***************
*** 13955,13960 **** SELECT (pg_stat_file('filename')).modification;
--- 13971,13977 ----
         <entry><type>void</type></entry>
         <entry>Obtain exclusive advisory lock</entry>
        </row>
+ 
        <row>
         <entry>
          <literal><function>pg_advisory_lock(<parameter>key1</> <type>int</>, <parameter>key2</> <type>int</>)</function></literal>
*** a/src/backend/utils/adt/genfile.c
--- b/src/backend/utils/adt/genfile.c
***************
*** 7,12 ****
--- 7,13 ----
   * Copyright (c) 2004-2010, PostgreSQL Global Development Group
   *
   * Author: Andreas Pflug <pgadmin@pse-consulting.de>
+  *         Dimitri Fontaine <dimitri@2ndQuadrant.fr>
   *
   * IDENTIFICATION
   *	  src/backend/utils/adt/genfile.c
***************
*** 30,35 ****
--- 31,46 ----
  #include "utils/memutils.h"
  #include "utils/timestamp.h"
  
+ #include "tcop/pquery.h"
+ #include "tcop/tcopprot.h"
+ #include "tcop/utility.h"
+ #include "access/transam.h"
+ #include "access/xact.h"
+ #include "utils/resowner.h"
+ #include "utils/snapmgr.h"
+ #include "parser/analyze.h"
+ #include "access/printtup.h"
+ 
  typedef struct
  {
  	char	   *location;
***************
*** 264,266 **** pg_ls_dir(PG_FUNCTION_ARGS)
--- 275,459 ----
  
  	SRF_RETURN_DONE(funcctx);
  }
+ 
+ /*
+  * Read a file then execute the SQL commands it contains.
+  */
+ Datum
+ pg_execute_from_file(PG_FUNCTION_ARGS)
+ {
+ 	text	   *filename_t = PG_GETARG_TEXT_P(0);
+ 	char	   *filename;
+ 	FILE       *file;
+ 	int64       fsize = -1, nbytes;
+ 	struct stat fst;
+ 	char       *query_string = NULL;
+ 
+ 	CommandDest dest = DestNone;
+ 	MemoryContext oldcontext;
+ 	List	   *parsetree_list;
+ 	ListCell   *parsetree_item;
+ 	bool		save_log_statement_stats = log_statement_stats;
+ 	bool		was_logged = false;
+ 	bool		isTopLevel;
+ 	char		msec_str[32];
+ 
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 				 (errmsg("must be superuser to get file information"))));
+ 
+ 	/*
+ 	 * Only superuser can call pg_execute_from_file, and CREATE EXTENSION
+ 	 * uses that too. Don't double check the PATH. Also note that
+ 	 * extension's install files are not in $PGDATA but `pg_config
+ 	 * --sharedir`.
+ 	 */
+ 	filename = text_to_cstring(filename_t);
+ 
+ 	if (stat(filename, &fst) < 0)
+ 		ereport(ERROR,
+ 				(errcode_for_file_access(),
+ 				 errmsg("could not stat file \"%s\": %m", filename)));
+ 
+ 	fsize = Int64GetDatum((int64) fst.st_size);
+ 
+ 	if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
+ 		ereport(ERROR,
+ 				(errcode_for_file_access(),
+ 				 errmsg("could not open file \"%s\" for reading: %m",
+ 						filename)));
+ 
+ 	if (ferror(file))
+ 		ereport(ERROR,
+ 				(errcode_for_file_access(),
+ 				 errmsg("could not read file \"%s\": %m", filename)));
+ 
+ 	query_string = (char *)palloc((fsize+1)*sizeof(char));
+ 	memset(query_string, 0, fsize+1);
+ 	nbytes = fread(query_string, 1, (size_t) fsize, file);
+ 	pg_verifymbstr(query_string, nbytes, false);
+ 	FreeFile(file);
+ 
+ 	/*
+ 	elog(NOTICE, "pg_execute_from_file('%s') read %d/%d bytes:", filename, nbytes, fsize);
+ 	elog(NOTICE, "%s", query_string);
+ 	 */
+ 
+ 	/*
+ 	 * Code pasted from postgres.c:exec_simple_query, main differences are:
+ 	 * - don't override unnamed portal, name it after filename instead
+ 	 * - don't start nor finish a transaction
+ 	 * - don't set stats or tracing markers
+ 	 */
+ 	oldcontext = MemoryContextSwitchTo(MessageContext);
+ 	parsetree_list = pg_parse_query(query_string);
+ 	MemoryContextSwitchTo(oldcontext);
+ 
+ 	isTopLevel = false;
+ 
+ 	foreach(parsetree_item, parsetree_list)
+ 	{
+ 		Node	   *parsetree = (Node *) lfirst(parsetree_item);
+ 		bool		snapshot_set = false;
+ 		const char *commandTag;
+ 		char		completionTag[COMPLETION_TAG_BUFSIZE];
+ 		List	   *querytree_list,
+ 				   *plantree_list;
+ 		Portal		portal;
+ 		DestReceiver *receiver;
+ 		int16		format = 0; /* TEXT */
+ 
+ 		commandTag = CreateCommandTag(parsetree);
+ 
+ 		/* If we got a cancel signal in parsing or prior command, quit */
+ 		CHECK_FOR_INTERRUPTS();
+ 
+ 		/*
+ 		 * Set up a snapshot if parse analysis/planning will need one.
+ 		 */
+ 		if (analyze_requires_snapshot(parsetree))
+ 		{
+ 			PushActiveSnapshot(GetTransactionSnapshot());
+ 			snapshot_set = true;
+ 		}
+ 
+ 		/*
+ 		 * OK to analyze, rewrite, and plan this query.
+ 		 *
+ 		 * Switch to appropriate context for constructing querytrees (again,
+ 		 * these must outlive the execution context).
+ 		 */
+ 		oldcontext = MemoryContextSwitchTo(MessageContext);
+ 
+ 		querytree_list = pg_analyze_and_rewrite(parsetree, query_string,
+ 												NULL, 0);
+ 
+ 		plantree_list = pg_plan_queries(querytree_list, 0, NULL);
+ 
+ 		/* Done with the snapshot used for parsing/planning */
+ 		if (snapshot_set)
+ 			PopActiveSnapshot();
+ 
+ 		/* If we got a cancel signal in analysis or planning, quit */
+ 		CHECK_FOR_INTERRUPTS();
+ 
+ 		/*
+ 		 * Create a portal to run the query or queries in. Name if after the
+ 		 * given filename. If there already is one, silently drop it.
+ 		 */
+ 		portal = CreatePortal(filename, true, true);
+ 		/* Don't display the portal in pg_cursors */
+ 		portal->visible = false;
+ 
+ 		/*
+ 		 * We don't have to copy anything into the portal, because everything
+ 		 * we are passing here is in MessageContext, which will outlive the
+ 		 * portal anyway.
+ 		 */
+ 		PortalDefineQuery(portal,
+ 						  NULL,
+ 						  query_string,
+ 						  commandTag,
+ 						  plantree_list,
+ 						  NULL);
+ 
+ 		/*
+ 		 * Start the portal.  No parameters here.
+ 		 */
+ 		PortalStart(portal, NULL, InvalidSnapshot);
+ 		PortalSetResultFormat(portal, 1, &format);
+ 
+ 		/*
+ 		 * Now we can create the destination receiver object.
+ 		 */
+ 		receiver = CreateDestReceiver(dest);
+ 		if (dest == DestRemote)
+ 			SetRemoteDestReceiverParams(receiver, portal);
+ 
+ 		/*
+ 		 * Switch back to transaction context for execution.
+ 		 */
+ 		MemoryContextSwitchTo(oldcontext);
+ 
+ 		/*
+ 		 * Run the portal to completion, and then drop it (and the receiver).
+ 		 */
+ 		(void) PortalRun(portal,
+ 						 FETCH_ALL,
+ 						 isTopLevel,
+ 						 receiver,
+ 						 receiver,
+ 						 completionTag);
+ 
+ 		(*receiver->rDestroy) (receiver);
+ 
+ 		PortalDrop(portal, false);
+ 
+ 		if (!IsA(parsetree, TransactionStmt) && lnext(parsetree_item) != NULL)
+ 		{
+ 			CommandCounterIncrement();
+ 		}
+ 	}
+ 	PG_RETURN_VOID();
+ }
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 3386,3399 **** DESCR("reload configuration files");
  DATA(insert OID = 2622 ( pg_rotate_logfile		PGNSP PGUID 12 1 0 0 f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ pg_rotate_logfile _null_ _null_ _null_ ));
  DESCR("rotate log file");
  
! DATA(insert OID = 2623 ( pg_stat_file		PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2249 "25" "{25,20,1184,1184,1184,1184,16}" "{i,o,o,o,o,o,o}" "{filename,size,access,modification,change,creation,isdir}" _null_ pg_stat_file _null_ _null_ _null_ ));
  DESCR("return file information");
! DATA(insert OID = 2624 ( pg_read_file		PGNSP PGUID 12 1 0 0 f f f t f v 3 0 25 "25 20 20" _null_ _null_ _null_ _null_ pg_read_file _null_ _null_ _null_ ));
  DESCR("read text from a file");
! DATA(insert OID = 2625 ( pg_ls_dir			PGNSP PGUID 12 1 1000 0 f f f t t v 1 0 25 "25" _null_ _null_ _null_ _null_ pg_ls_dir _null_ _null_ _null_ ));
  DESCR("list all files in a directory");
! DATA(insert OID = 2626 ( pg_sleep			PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2278 "701" _null_ _null_ _null_ _null_ pg_sleep _null_ _null_ _null_ ));
  DESCR("sleep for the specified time in seconds");
  
  DATA(insert OID = 2971 (  text				PGNSP PGUID 12 1 0 0 f f f t f i 1 0 25 "16" _null_ _null_ _null_ _null_ booltext _null_ _null_ _null_ ));
  DESCR("convert boolean to text");
--- 3386,3401 ----
  DATA(insert OID = 2622 ( pg_rotate_logfile		PGNSP PGUID 12 1 0 0 f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ pg_rotate_logfile _null_ _null_ _null_ ));
  DESCR("rotate log file");
  
! DATA(insert OID = 2623 ( pg_stat_file			PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2249 "25" "{25,20,1184,1184,1184,1184,16}" "{i,o,o,o,o,o,o}" "{filename,size,access,modification,change,creation,isdir}" _null_ pg_stat_file _null_ _null_ _null_ ));
  DESCR("return file information");
! DATA(insert OID = 2624 ( pg_read_file			PGNSP PGUID 12 1 0 0 f f f t f v 3 0 25 "25 20 20" _null_ _null_ _null_ _null_ pg_read_file _null_ _null_ _null_ ));
  DESCR("read text from a file");
! DATA(insert OID = 2625 ( pg_ls_dir				PGNSP PGUID 12 1 1000 0 f f f t t v 1 0 25 "25" _null_ _null_ _null_ _null_ pg_ls_dir _null_ _null_ _null_ ));
  DESCR("list all files in a directory");
! DATA(insert OID = 2626 ( pg_sleep				PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2278 "701" _null_ _null_ _null_ _null_ pg_sleep _null_ _null_ _null_ ));
  DESCR("sleep for the specified time in seconds");
+ DATA(insert OID = 3627 ( pg_execute_from_file	PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2278 "25" _null_ _null_ _null_ _null_ pg_execute_from_file _null_ _null_ _null_ ));
+ DESCR("execute queries read from a file");
  
  DATA(insert OID = 2971 (  text				PGNSP PGUID 12 1 0 0 f f f t f i 1 0 25 "16" _null_ _null_ _null_ _null_ booltext _null_ _null_ _null_ ));
  DESCR("convert boolean to text");
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
***************
*** 442,447 **** extern Datum pg_relation_filepath(PG_FUNCTION_ARGS);
--- 442,448 ----
  extern Datum pg_stat_file(PG_FUNCTION_ARGS);
  extern Datum pg_read_file(PG_FUNCTION_ARGS);
  extern Datum pg_ls_dir(PG_FUNCTION_ARGS);
+ extern Datum pg_execute_from_file(PG_FUNCTION_ARGS);
  
  /* misc.c */
  extern Datum current_database(PG_FUNCTION_ARGS);
#12Alvaro Herrera
alvherre@commandprompt.com
In reply to: Dimitri Fontaine (#11)
Re: Extensions, this time with a patch

Excerpts from Dimitri Fontaine's message of vie oct 15 16:15:23 -0300 2010:

Dimitri Fontaine <dimitri@2ndQuadrant.fr> writes:

So, you will find two new branches for those purposes at the repository,
named cfparser and pg_execute_from_file:

http://git.postgresql.org/gitweb?p=postgresql-extension.git;a=summary

I updated the pg_execute_from_file branch with documentation for the
function, and added documentation (catalog, functions, custom classes)
to the main feature branch too.

The cfparser patch didn't change, the current version is still v1.

Hmm, did you try "make install" in contrib? It fails for me in intagg:

make[1]: Entering directory `/home/alvherre/Code/CVS/pgsql/build/HEAD/contrib/intagg'
/bin/mkdir -p '/pgsql/install/HEAD/share/contrib'
touch .control
test ! -f .control && echo "name = '' \nversion = '9.1'" > .control
make[1]: *** [.control] Error 1
make[1]: *** Deleting file `.control'
make[1]: Leaving directory `/home/alvherre/Code/CVS/pgsql/build/HEAD/contrib/intagg'
make: *** [install] Error 2

I also note that the .control file generation is not working as intended
for me -- the \n ends up verbatim in the generated files, not as a
newline. It's probably easier to call echo two times.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#13Alvaro Herrera
alvherre@commandprompt.com
In reply to: Alvaro Herrera (#12)
Re: Extensions, this time with a patch

Excerpts from Alvaro Herrera's message of sáb oct 16 00:30:41 -0300 2010:

Hmm, did you try "make install" in contrib? It fails for me in intagg:

make[1]: Entering directory `/home/alvherre/Code/CVS/pgsql/build/HEAD/contrib/intagg'
/bin/mkdir -p '/pgsql/install/HEAD/share/contrib'
touch .control
test ! -f .control && echo "name = '' \nversion = '9.1'" > .control
make[1]: *** [.control] Error 1
make[1]: *** Deleting file `.control'
make[1]: Leaving directory
`/home/alvherre/Code/CVS/pgsql/build/HEAD/contrib/intagg'
make: *** [install] Error 2

Oh, I see what's going on here ... you have this bit in pgxs.mk:

# create extension support
ifndef CONTROL
ifdef MODULE_big
CONTROL = $(MODULE_big).control
EXTENSION = $(MODULE_big)
else
CONTROL = $(MODULES).control
EXTENSION = $(MODULES)
endif
endif

The reason it fails for intagg is that it doesn't define MODULE_big, so
it takes the other path. But since MODULES is not defined either, this
ends up defining CONTROL as ".control"; and then "test" returns a
failure because a file with the same name has been created in the
previous line.

I don't think the MODULES bit works either; see the spi contrib for an
example. What it ends up doing is probably not what you want.

Maybe what you should be doing here is that modules should provide
another definition, say EXTENSION, and they have to explicitely define
it in their Makefile (maybe require EXTENSION_VERSION too or something
like that). I think the idea that modules should continue to work as
extensions without any modification is doomed.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#14Alvaro Herrera
alvherre@commandprompt.com
In reply to: Dimitri Fontaine (#11)
Re: Extensions, this time with a patch

Excerpts from Dimitri Fontaine's message of vie oct 15 16:15:23 -0300 2010:

I updated the pg_execute_from_file branch with documentation for the
function, and added documentation (catalog, functions, custom classes)
to the main feature branch too.

Hmm. To be honest I don't like the direction that pg_execute_from_file
has taken. (Now that I look, it's been like this since inception). I
have two problems with it: one is that it is #including half the world
into genfile.c. This already smells trouble in itself. I got worried
when I saw the CommandDest declaration. Really, I think having the guts
of postgres.c into that file is not a good idea from a modularisation
point of view.

The other problem is that it's slurping the whole file and executing it
as a single query. This is really two problems: one is that you
shouldn't be trusting that the file is going to be small enough to be
read that way. The other one is that I don't think it's a good idea to
execute it in a fell swoop; seems to be it would be better to split it
into queries, and rewrite/parse/plan/execute them one by one.

I think a better way to go about this is to have another entry point in
postgres.c that executes a query the way you want; and somehow read the
file in small chunks, find where each query ends, and execute them one
by one. (To be honest, I have no idea how to find out where each query
ends. psql knows how to do it, but I'm not sure how trustworthy it is.)

As far as #include lines go, please keep them in alphabetical order. As
a matter of style we always have "postgres.h" alone, then a blank line,
then system includes, another blank, then the rest of the includes.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#15Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Alvaro Herrera (#13)
Re: Extensions, this time with a patch

Alvaro Herrera <alvherre@commandprompt.com> writes:

Maybe what you should be doing here is that modules should provide
another definition, say EXTENSION, and they have to explicitely define
it in their Makefile (maybe require EXTENSION_VERSION too or something
like that). I think the idea that modules should continue to work as
extensions without any modification is doomed.

In fact there's ifndef CONTROL that protects the black magic failing
part, so that we could edit any contrib's Makefile to give the
information we're trying to guess. I just had another try at it that
seems to work much better, based on DATA and DATA_built:

# create extension support
ifndef CONTROL
ifdef DATA_built
EXTENSION = $(basename $(notdir $(firstword $(DATA_built))))
else ifdef DATA
EXTENSION = $(basename $(notdir $(firstword $(DATA))))
endif
ifdef EXTENSION
CONTROL = $(EXTENSION).control
endif
endif

Also, I've switched to using echo twice as you recommended, that's much
better too.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#16Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Alvaro Herrera (#14)
Re: Extensions, this time with a patch

Alvaro Herrera <alvherre@commandprompt.com> writes:

Hmm. To be honest I don't like the direction that pg_execute_from_file
has taken. (Now that I look, it's been like this since inception). I
have two problems with it: one is that it is #including half the world
into genfile.c. This already smells trouble in itself. I got worried
when I saw the CommandDest declaration. Really, I think having the guts
of postgres.c into that file is not a good idea from a modularisation
point of view.

Understood. The thinking back when I worked on that part was to minimize
the diff and remain localized, which is known to be a bad idea... I'll
rework that part as soon as we agree on the other one:

The other problem is that it's slurping the whole file and executing it
as a single query. This is really two problems: one is that you
shouldn't be trusting that the file is going to be small enough to be
read that way. The other one is that I don't think it's a good idea to
execute it in a fell swoop; seems to be it would be better to split it
into queries, and rewrite/parse/plan/execute them one by one.

I think a better way to go about this is to have another entry point in
postgres.c that executes a query the way you want; and somehow read the
file in small chunks, find where each query ends, and execute them one
by one. (To be honest, I have no idea how to find out where each query
ends. psql knows how to do it, but I'm not sure how trustworthy it
is.)

Well, that's the reason why it's done this way now, relying on a multiple
queries portal. The only trustworthy way to split the queries apart in
the SQL install script would be to rely on gram.y, and I didn't find the
API to explicitly loop over each query parsed.

Given some advice, I'll rework that part too. The good news is that it's
well separated from the rest of the extension's work.

We need a way to do the same as \i in psql, but from the backend, and we
won't be returning anything from there, so we don't need to handle more
than one portal definition in a single fe/be communication.

As far as #include lines go, please keep them in alphabetical order. As
a matter of style we always have "postgres.h" alone, then a blank line,
then system includes, another blank, then the rest of the includes.

Will do.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#17Tom Lane
tgl@sss.pgh.pa.us
In reply to: Dimitri Fontaine (#16)
Re: Extensions, this time with a patch

Dimitri Fontaine <dimitri@2ndQuadrant.fr> writes:

Alvaro Herrera <alvherre@commandprompt.com> writes:

The other problem is that it's slurping the whole file and executing it
as a single query.

Given some advice, I'll rework that part too. The good news is that it's
well separated from the rest of the extension's work.

I think that's something that could be left for later, if not never.
I find it hard to imagine extension modules with more than a few
thousand commands (the largest one in contrib is isn, with about 500).
No modern machine is going to have the slightest difficulty with that.
If it ever does get to be a problem in practice, we could rework the
implementation at that time.

regards, tom lane

#18Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Tom Lane (#17)
Re: Extensions, this time with a patch

Tom Lane <tgl@sss.pgh.pa.us> writes:

I think that's something that could be left for later, if not never.

That's very great news. I'm left with moving the bulk of the code away
from genfile.c and into postgres.c, and have the former be a user
callable shell around the later, I suppose. Right?

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#19Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Dimitri Fontaine (#18)
1 attachment(s)
Re: Extensions, this time with a patch

Dimitri Fontaine <dimitri@2ndQuadrant.fr> writes:

That's very great news. I'm left with moving the bulk of the code away
from genfile.c and into postgres.c, and have the former be a user
callable shell around the later, I suppose. Right?

Here it is, looks much better this way.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

Attachments:

pg_execute_from_file.v2.patchtext/x-patchDownload
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
***************
*** 13895,13900 **** postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
--- 13895,13907 ----
         <entry><type>record</type></entry>
         <entry>Return information about a file</entry>
        </row>
+       <row>
+        <entry>
+         <literal><function>pg_execute_from_file(<parameter>filename</> <type>text</>)</function></literal>
+        </entry>
+        <entry><type>void</type></entry>
+        <entry>Executes the <acronym>SQL</> commands contained in a file</entry>
+       </row>
       </tbody>
      </tgroup>
     </table>
***************
*** 13933,13938 **** SELECT (pg_stat_file('filename')).modification;
--- 13940,13954 ----
  </programlisting>
     </para>
  
+    <indexterm>
+     <primary>pg_execute_from_file</primary>
+    </indexterm>
+    <para>
+     <function>pg_execute_from_file</> makes the server
+     execute <acronym>SQL</> commands to be found in a file. This function is
+     reserved to superusers.
+    </para>
+ 
     <para>
      The functions shown in <xref linkend="functions-advisory-locks"> manage
      advisory locks.  For details about proper use of these functions, see
***************
*** 13955,13960 **** SELECT (pg_stat_file('filename')).modification;
--- 13971,13977 ----
         <entry><type>void</type></entry>
         <entry>Obtain exclusive advisory lock</entry>
        </row>
+ 
        <row>
         <entry>
          <literal><function>pg_advisory_lock(<parameter>key1</> <type>int</>, <parameter>key2</> <type>int</>)</function></literal>
*** a/src/backend/tcop/postgres.c
--- b/src/backend/tcop/postgres.c
***************
*** 73,79 ****
  #include "utils/snapmgr.h"
  #include "mb/pg_wchar.h"
  
- 
  extern char *optarg;
  extern int	optind;
  
--- 73,78 ----
***************
*** 1139,1144 **** exec_simple_query(const char *query_string)
--- 1138,1277 ----
  }
  
  /*
+  * exec_multiple_queries
+  *
+  * Execute all queries found in the given query_string. Main use is
+  * pg_execute_from_file() function in backend/utils/adt/genfile.c
+  *
+  * Main differences from exec_simple_query are:
+  * - don't override unnamed portal
+  * - don't start nor finish a transaction
+  * - don't set stats or tracing markers
+  */
+ void
+ exec_multiple_queries(const char *portal_name, const char *query_string)
+ {
+ 	CommandDest dest = DestNone;
+ 	MemoryContext oldcontext;
+ 	List	   *parsetree_list;
+ 	ListCell   *parsetree_item;
+ 	bool		save_log_statement_stats = log_statement_stats;
+ 	bool		was_logged = false;
+ 	bool		isTopLevel;
+ 	char		msec_str[32];
+ 
+ 	oldcontext = MemoryContextSwitchTo(MessageContext);
+ 	parsetree_list = pg_parse_query(query_string);
+ 	MemoryContextSwitchTo(oldcontext);
+ 
+ 	isTopLevel = false;
+ 
+ 	foreach(parsetree_item, parsetree_list)
+ 	{
+ 		Node	   *parsetree = (Node *) lfirst(parsetree_item);
+ 		bool		snapshot_set = false;
+ 		const char *commandTag;
+ 		char		completionTag[COMPLETION_TAG_BUFSIZE];
+ 		List	   *querytree_list,
+ 				   *plantree_list;
+ 		Portal		portal;
+ 		DestReceiver *receiver;
+ 		int16		format = 0; /* TEXT */
+ 
+ 		commandTag = CreateCommandTag(parsetree);
+ 
+ 		/* If we got a cancel signal in parsing or prior command, quit */
+ 		CHECK_FOR_INTERRUPTS();
+ 
+ 		/*
+ 		 * Set up a snapshot if parse analysis/planning will need one.
+ 		 */
+ 		if (analyze_requires_snapshot(parsetree))
+ 		{
+ 			PushActiveSnapshot(GetTransactionSnapshot());
+ 			snapshot_set = true;
+ 		}
+ 
+ 		/*
+ 		 * OK to analyze, rewrite, and plan this query.
+ 		 *
+ 		 * Switch to appropriate context for constructing querytrees (again,
+ 		 * these must outlive the execution context).
+ 		 */
+ 		oldcontext = MemoryContextSwitchTo(MessageContext);
+ 
+ 		querytree_list = pg_analyze_and_rewrite(parsetree, query_string,
+ 												NULL, 0);
+ 
+ 		plantree_list = pg_plan_queries(querytree_list, 0, NULL);
+ 
+ 		/* Done with the snapshot used for parsing/planning */
+ 		if (snapshot_set)
+ 			PopActiveSnapshot();
+ 
+ 		/* If we got a cancel signal in analysis or planning, quit */
+ 		CHECK_FOR_INTERRUPTS();
+ 
+ 		/*
+ 		 * Create a portal to run the query or queries in. If there already
+ 		 * is one with given portal_name, silently drop it.
+ 		 */
+ 		portal = CreatePortal(portal_name, true, true);
+ 		/* Don't display the portal in pg_cursors */
+ 		portal->visible = false;
+ 
+ 		/*
+ 		 * We don't have to copy anything into the portal, because everything
+ 		 * we are passing here is in MessageContext, which will outlive the
+ 		 * portal anyway.
+ 		 */
+ 		PortalDefineQuery(portal,
+ 						  NULL,
+ 						  query_string,
+ 						  commandTag,
+ 						  plantree_list,
+ 						  NULL);
+ 
+ 		/*
+ 		 * Start the portal.  No parameters here.
+ 		 */
+ 		PortalStart(portal, NULL, InvalidSnapshot);
+ 		PortalSetResultFormat(portal, 1, &format);
+ 
+ 		/*
+ 		 * Now we can create the destination receiver object.
+ 		 */
+ 		receiver = CreateDestReceiver(dest);
+ 		if (dest == DestRemote)
+ 			SetRemoteDestReceiverParams(receiver, portal);
+ 
+ 		/*
+ 		 * Switch back to transaction context for execution.
+ 		 */
+ 		MemoryContextSwitchTo(oldcontext);
+ 
+ 		/*
+ 		 * Run the portal to completion, and then drop it (and the receiver).
+ 		 */
+ 		(void) PortalRun(portal,
+ 						 FETCH_ALL,
+ 						 isTopLevel,
+ 						 receiver,
+ 						 receiver,
+ 						 completionTag);
+ 
+ 		(*receiver->rDestroy) (receiver);
+ 
+ 		PortalDrop(portal, false);
+ 
+ 		if (!IsA(parsetree, TransactionStmt) && lnext(parsetree_item) != NULL)
+ 		{
+ 			CommandCounterIncrement();
+ 		}
+ 	}
+ }
+ 
+ /*
   * exec_parse_message
   *
   * Execute a "Parse" protocol message.
*** a/src/backend/utils/adt/genfile.c
--- b/src/backend/utils/adt/genfile.c
***************
*** 7,12 ****
--- 7,13 ----
   * Copyright (c) 2004-2010, PostgreSQL Global Development Group
   *
   * Author: Andreas Pflug <pgadmin@pse-consulting.de>
+  *         Dimitri Fontaine <dimitri@2ndQuadrant.fr>
   *
   * IDENTIFICATION
   *	  src/backend/utils/adt/genfile.c
***************
*** 264,266 **** pg_ls_dir(PG_FUNCTION_ARGS)
--- 265,321 ----
  
  	SRF_RETURN_DONE(funcctx);
  }
+ 
+ /*
+  * Read a file then execute the SQL commands it contains.
+  */
+ Datum
+ pg_execute_from_file(PG_FUNCTION_ARGS)
+ {
+ 	text	   *filename_t = PG_GETARG_TEXT_P(0);
+ 	char	   *filename;
+ 	FILE       *file;
+ 	int64       fsize = -1, nbytes;
+ 	struct stat fst;
+ 	char       *query_string = NULL;
+ 
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 				 (errmsg("must be superuser to get file information"))));
+ 
+ 	/*
+ 	 * Only superuser can call pg_execute_from_file, and CREATE EXTENSION
+ 	 * uses that too. Don't double check the PATH. Also note that
+ 	 * extension's install files are not in $PGDATA but `pg_config
+ 	 * --sharedir`.
+ 	 */
+ 	filename = text_to_cstring(filename_t);
+ 
+ 	if (stat(filename, &fst) < 0)
+ 		ereport(ERROR,
+ 				(errcode_for_file_access(),
+ 				 errmsg("could not stat file \"%s\": %m", filename)));
+ 
+ 	fsize = Int64GetDatum((int64) fst.st_size);
+ 
+ 	if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
+ 		ereport(ERROR,
+ 				(errcode_for_file_access(),
+ 				 errmsg("could not open file \"%s\" for reading: %m",
+ 						filename)));
+ 
+ 	if (ferror(file))
+ 		ereport(ERROR,
+ 				(errcode_for_file_access(),
+ 				 errmsg("could not read file \"%s\": %m", filename)));
+ 
+ 	query_string = (char *)palloc((fsize+1)*sizeof(char));
+ 	memset(query_string, 0, fsize+1);
+ 	nbytes = fread(query_string, 1, (size_t) fsize, file);
+ 	pg_verifymbstr(query_string, nbytes, false);
+ 	FreeFile(file);
+ 
+ 	exec_multiple_queries(filename, query_string);
+ 	PG_RETURN_VOID();
+ }
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 3386,3399 **** DESCR("reload configuration files");
  DATA(insert OID = 2622 ( pg_rotate_logfile		PGNSP PGUID 12 1 0 0 f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ pg_rotate_logfile _null_ _null_ _null_ ));
  DESCR("rotate log file");
  
! DATA(insert OID = 2623 ( pg_stat_file		PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2249 "25" "{25,20,1184,1184,1184,1184,16}" "{i,o,o,o,o,o,o}" "{filename,size,access,modification,change,creation,isdir}" _null_ pg_stat_file _null_ _null_ _null_ ));
  DESCR("return file information");
! DATA(insert OID = 2624 ( pg_read_file		PGNSP PGUID 12 1 0 0 f f f t f v 3 0 25 "25 20 20" _null_ _null_ _null_ _null_ pg_read_file _null_ _null_ _null_ ));
  DESCR("read text from a file");
! DATA(insert OID = 2625 ( pg_ls_dir			PGNSP PGUID 12 1 1000 0 f f f t t v 1 0 25 "25" _null_ _null_ _null_ _null_ pg_ls_dir _null_ _null_ _null_ ));
  DESCR("list all files in a directory");
! DATA(insert OID = 2626 ( pg_sleep			PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2278 "701" _null_ _null_ _null_ _null_ pg_sleep _null_ _null_ _null_ ));
  DESCR("sleep for the specified time in seconds");
  
  DATA(insert OID = 2971 (  text				PGNSP PGUID 12 1 0 0 f f f t f i 1 0 25 "16" _null_ _null_ _null_ _null_ booltext _null_ _null_ _null_ ));
  DESCR("convert boolean to text");
--- 3386,3401 ----
  DATA(insert OID = 2622 ( pg_rotate_logfile		PGNSP PGUID 12 1 0 0 f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ pg_rotate_logfile _null_ _null_ _null_ ));
  DESCR("rotate log file");
  
! DATA(insert OID = 2623 ( pg_stat_file			PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2249 "25" "{25,20,1184,1184,1184,1184,16}" "{i,o,o,o,o,o,o}" "{filename,size,access,modification,change,creation,isdir}" _null_ pg_stat_file _null_ _null_ _null_ ));
  DESCR("return file information");
! DATA(insert OID = 2624 ( pg_read_file			PGNSP PGUID 12 1 0 0 f f f t f v 3 0 25 "25 20 20" _null_ _null_ _null_ _null_ pg_read_file _null_ _null_ _null_ ));
  DESCR("read text from a file");
! DATA(insert OID = 2625 ( pg_ls_dir				PGNSP PGUID 12 1 1000 0 f f f t t v 1 0 25 "25" _null_ _null_ _null_ _null_ pg_ls_dir _null_ _null_ _null_ ));
  DESCR("list all files in a directory");
! DATA(insert OID = 2626 ( pg_sleep				PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2278 "701" _null_ _null_ _null_ _null_ pg_sleep _null_ _null_ _null_ ));
  DESCR("sleep for the specified time in seconds");
+ DATA(insert OID = 3627 ( pg_execute_from_file	PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2278 "25" _null_ _null_ _null_ _null_ pg_execute_from_file _null_ _null_ _null_ ));
+ DESCR("execute queries read from a file");
  
  DATA(insert OID = 2971 (  text				PGNSP PGUID 12 1 0 0 f f f t f i 1 0 25 "16" _null_ _null_ _null_ _null_ booltext _null_ _null_ _null_ ));
  DESCR("convert boolean to text");
*** a/src/include/postgres.h
--- b/src/include/postgres.h
***************
*** 687,690 **** extern int ExceptionalCondition(const char *conditionName,
--- 687,693 ----
  					 const char *errorType,
  					 const char *fileName, int lineNumber);
  
+ extern void exec_multiple_queries(const char *portal_name,
+ 								  const char *query_string);
+ 
  #endif   /* POSTGRES_H */
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
***************
*** 442,447 **** extern Datum pg_relation_filepath(PG_FUNCTION_ARGS);
--- 442,448 ----
  extern Datum pg_stat_file(PG_FUNCTION_ARGS);
  extern Datum pg_read_file(PG_FUNCTION_ARGS);
  extern Datum pg_ls_dir(PG_FUNCTION_ARGS);
+ extern Datum pg_execute_from_file(PG_FUNCTION_ARGS);
  
  /* misc.c */
  extern Datum current_database(PG_FUNCTION_ARGS);
#20Tom Lane
tgl@sss.pgh.pa.us
In reply to: Dimitri Fontaine (#18)
Re: Extensions, this time with a patch

Dimitri Fontaine <dimitri@2ndQuadrant.fr> writes:

Tom Lane <tgl@sss.pgh.pa.us> writes:

I think that's something that could be left for later, if not never.

That's very great news. I'm left with moving the bulk of the code away
from genfile.c and into postgres.c, and have the former be a user
callable shell around the later, I suppose. Right?

Umm ... I fail to see why an extensions patch should be touching
postgres.c at all, let alone injecting a large amount of code there.
Whatever you're doing there probably requires some rethinking.

regards, tom lane

#21Alvaro Herrera
alvherre@commandprompt.com
In reply to: Tom Lane (#20)
Re: Extensions, this time with a patch

Excerpts from Tom Lane's message of sáb oct 16 19:52:27 -0300 2010:

Dimitri Fontaine <dimitri@2ndQuadrant.fr> writes:

Tom Lane <tgl@sss.pgh.pa.us> writes:

I think that's something that could be left for later, if not never.

That's very great news. I'm left with moving the bulk of the code away
from genfile.c and into postgres.c, and have the former be a user
callable shell around the later, I suppose. Right?

Umm ... I fail to see why an extensions patch should be touching
postgres.c at all, let alone injecting a large amount of code there.
Whatever you're doing there probably requires some rethinking.

Hm, it was me that led him in that direction. The original patch was
just copying a bunch of code from postgres.c into genfile.c, which
struck me as a worse proposition.

The intent here is to execute some code from the file directly inside
the server.

Eh, I realize now that the right way to go about this is to use SPI.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#22Tom Lane
tgl@sss.pgh.pa.us
In reply to: Alvaro Herrera (#21)
Re: Extensions, this time with a patch

Alvaro Herrera <alvherre@commandprompt.com> writes:

The intent here is to execute some code from the file directly inside
the server.

Eh, I realize now that the right way to go about this is to use SPI.

Yeah, that would be one way to go about it. But IMO postgres.c should
be solely concerned with interactions with the client.

regards, tom lane

#23Alvaro Herrera
alvherre@commandprompt.com
In reply to: Tom Lane (#22)
Re: Extensions, this time with a patch

Excerpts from Tom Lane's message of sáb oct 16 23:32:49 -0300 2010:

Alvaro Herrera <alvherre@commandprompt.com> writes:

The intent here is to execute some code from the file directly inside
the server.

Eh, I realize now that the right way to go about this is to use SPI.

Yeah, that would be one way to go about it. But IMO postgres.c should
be solely concerned with interactions with the client.

Duly noted.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#24David Fetter
david@fetter.org
In reply to: Alvaro Herrera (#23)
Re: Extensions, this time with a patch

On Sun, Oct 17, 2010 at 12:09:51AM -0300, Alvaro Herrera wrote:

Excerpts from Tom Lane's message of s�b oct 16 23:32:49 -0300 2010:

Alvaro Herrera <alvherre@commandprompt.com> writes:

The intent here is to execute some code from the file directly inside
the server.

Eh, I realize now that the right way to go about this is to use SPI.

Yeah, that would be one way to go about it. But IMO postgres.c should
be solely concerned with interactions with the client.

Duly noted.

Should this be noted in a README? Source code comments? I'm thinking
if Alvaro didn't know it, it's not clear enough from context.

Cheers,
David.
--
David Fetter <david@fetter.org> http://fetter.org/
Phone: +1 415 235 3778 AIM: dfetter666 Yahoo!: dfetter
Skype: davidfetter XMPP: david.fetter@gmail.com
iCal: webcal://www.tripit.com/feed/ical/people/david74/tripit.ics

Remember to vote!
Consider donating to Postgres: http://www.postgresql.org/about/donate

#25Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Tom Lane (#22)
1 attachment(s)
Re: Extensions, this time with a patch

Tom Lane <tgl@sss.pgh.pa.us> writes:

Alvaro Herrera <alvherre@commandprompt.com> writes:

Eh, I realize now that the right way to go about this is to use SPI.

Yeah, that would be one way to go about it. But IMO postgres.c should
be solely concerned with interactions with the client.

I didn't notice it's "possible" to use SPI from within the backend core
code, and now see precedent in xml.c where the user can give a query
string. I've used SPI_execute() in the new (attached) version of the
patch, that's not touching postgres.c at all anymore.

The bulk of it is now short enough to be inlined in the mail, and if you
have more comments I guess they'll be directed at this portion of the
patch, so let's make it easy:

/*
* We abuse some internal knowledge from spi.h here. As we don't know
* which queries are going to get executed, we don't know what to expect
* as an OK return code from SPI_execute(). We assume that
* SPI_OK_CONNECT, SPI_OK_FINISH and SPI_OK_FETCH are quite improbable,
* though, and the errors are negatives. So a valid return code is
* considered to be SPI_OK_UTILITY or anything from there.
*/
SPI_connect();
if (SPI_execute(query_string, false, 0) < SPI_OK_UTILITY)
ereport(ERROR,
(errcode(ERRCODE_DATA_EXCEPTION),
errmsg("File '%s' contains invalid query", filename)));
SPI_finish();

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

Attachments:

pg_execute_from_file.v3.patchtext/x-patchDownload
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
***************
*** 13895,13900 **** postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
--- 13895,13907 ----
         <entry><type>record</type></entry>
         <entry>Return information about a file</entry>
        </row>
+       <row>
+        <entry>
+         <literal><function>pg_execute_from_file(<parameter>filename</> <type>text</>)</function></literal>
+        </entry>
+        <entry><type>void</type></entry>
+        <entry>Executes the <acronym>SQL</> commands contained in a file</entry>
+       </row>
       </tbody>
      </tgroup>
     </table>
***************
*** 13933,13938 **** SELECT (pg_stat_file('filename')).modification;
--- 13940,13954 ----
  </programlisting>
     </para>
  
+    <indexterm>
+     <primary>pg_execute_from_file</primary>
+    </indexterm>
+    <para>
+     <function>pg_execute_from_file</> makes the server
+     execute <acronym>SQL</> commands to be found in a file. This function is
+     reserved to superusers.
+    </para>
+ 
     <para>
      The functions shown in <xref linkend="functions-advisory-locks"> manage
      advisory locks.  For details about proper use of these functions, see
***************
*** 13955,13960 **** SELECT (pg_stat_file('filename')).modification;
--- 13971,13977 ----
         <entry><type>void</type></entry>
         <entry>Obtain exclusive advisory lock</entry>
        </row>
+ 
        <row>
         <entry>
          <literal><function>pg_advisory_lock(<parameter>key1</> <type>int</>, <parameter>key2</> <type>int</>)</function></literal>
*** a/src/backend/utils/adt/genfile.c
--- b/src/backend/utils/adt/genfile.c
***************
*** 7,12 ****
--- 7,13 ----
   * Copyright (c) 2004-2010, PostgreSQL Global Development Group
   *
   * Author: Andreas Pflug <pgadmin@pse-consulting.de>
+  *         Dimitri Fontaine <dimitri@2ndQuadrant.fr>
   *
   * IDENTIFICATION
   *	  src/backend/utils/adt/genfile.c
***************
*** 21,26 ****
--- 22,28 ----
  #include <dirent.h>
  
  #include "catalog/pg_type.h"
+ #include "executor/spi.h"
  #include "funcapi.h"
  #include "mb/pg_wchar.h"
  #include "miscadmin.h"
***************
*** 264,266 **** pg_ls_dir(PG_FUNCTION_ARGS)
--- 266,336 ----
  
  	SRF_RETURN_DONE(funcctx);
  }
+ 
+ /*
+  * Read a file then execute the SQL commands it contains.
+  */
+ Datum
+ pg_execute_from_file(PG_FUNCTION_ARGS)
+ {
+ 	text	   *filename_t = PG_GETARG_TEXT_P(0);
+ 	char	   *filename;
+ 	FILE       *file;
+ 	int64       fsize = -1, nbytes;
+ 	struct stat fst;
+ 	char       *query_string = NULL;
+ 
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 				 (errmsg("must be superuser to get file information"))));
+ 
+ 	/*
+ 	 * Only superuser can call pg_execute_from_file, and CREATE EXTENSION
+ 	 * uses that too. Don't double check the PATH. Also note that
+ 	 * extension's install files are not in $PGDATA but `pg_config
+ 	 * --sharedir`.
+ 	 */
+ 	filename = text_to_cstring(filename_t);
+ 
+ 	if (stat(filename, &fst) < 0)
+ 		ereport(ERROR,
+ 				(errcode_for_file_access(),
+ 				 errmsg("could not stat file \"%s\": %m", filename)));
+ 
+ 	fsize = Int64GetDatum((int64) fst.st_size);
+ 
+ 	if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
+ 		ereport(ERROR,
+ 				(errcode_for_file_access(),
+ 				 errmsg("could not open file \"%s\" for reading: %m",
+ 						filename)));
+ 
+ 	if (ferror(file))
+ 		ereport(ERROR,
+ 				(errcode_for_file_access(),
+ 				 errmsg("could not read file \"%s\": %m", filename)));
+ 
+ 	query_string = (char *)palloc((fsize+1)*sizeof(char));
+ 	memset(query_string, 0, fsize+1);
+ 	nbytes = fread(query_string, 1, (size_t) fsize, file);
+ 	pg_verifymbstr(query_string, nbytes, false);
+ 	FreeFile(file);
+ 
+ 	/*
+ 	 * We abuse some internal knowledge from spi.h here. As we don't know
+ 	 * which queries are going to get executed, we don't know what to expect
+ 	 * as an OK return code from SPI_execute().  We assume that
+ 	 * SPI_OK_CONNECT, SPI_OK_FINISH and SPI_OK_FETCH are quite improbable,
+ 	 * though, and the errors are negatives.  So a valid return code is
+ 	 * considered to be SPI_OK_UTILITY or anything from there.
+ 	 */
+ 	SPI_connect();
+ 	if (SPI_execute(query_string, false, 0) < SPI_OK_UTILITY)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_DATA_EXCEPTION),
+ 				 errmsg("File '%s' contains invalid query", filename)));
+ 	SPI_finish();
+ 
+ 	PG_RETURN_VOID();
+ }
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 3386,3399 **** DESCR("reload configuration files");
  DATA(insert OID = 2622 ( pg_rotate_logfile		PGNSP PGUID 12 1 0 0 f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ pg_rotate_logfile _null_ _null_ _null_ ));
  DESCR("rotate log file");
  
! DATA(insert OID = 2623 ( pg_stat_file		PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2249 "25" "{25,20,1184,1184,1184,1184,16}" "{i,o,o,o,o,o,o}" "{filename,size,access,modification,change,creation,isdir}" _null_ pg_stat_file _null_ _null_ _null_ ));
  DESCR("return file information");
! DATA(insert OID = 2624 ( pg_read_file		PGNSP PGUID 12 1 0 0 f f f t f v 3 0 25 "25 20 20" _null_ _null_ _null_ _null_ pg_read_file _null_ _null_ _null_ ));
  DESCR("read text from a file");
! DATA(insert OID = 2625 ( pg_ls_dir			PGNSP PGUID 12 1 1000 0 f f f t t v 1 0 25 "25" _null_ _null_ _null_ _null_ pg_ls_dir _null_ _null_ _null_ ));
  DESCR("list all files in a directory");
! DATA(insert OID = 2626 ( pg_sleep			PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2278 "701" _null_ _null_ _null_ _null_ pg_sleep _null_ _null_ _null_ ));
  DESCR("sleep for the specified time in seconds");
  
  DATA(insert OID = 2971 (  text				PGNSP PGUID 12 1 0 0 f f f t f i 1 0 25 "16" _null_ _null_ _null_ _null_ booltext _null_ _null_ _null_ ));
  DESCR("convert boolean to text");
--- 3386,3401 ----
  DATA(insert OID = 2622 ( pg_rotate_logfile		PGNSP PGUID 12 1 0 0 f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ pg_rotate_logfile _null_ _null_ _null_ ));
  DESCR("rotate log file");
  
! DATA(insert OID = 2623 ( pg_stat_file			PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2249 "25" "{25,20,1184,1184,1184,1184,16}" "{i,o,o,o,o,o,o}" "{filename,size,access,modification,change,creation,isdir}" _null_ pg_stat_file _null_ _null_ _null_ ));
  DESCR("return file information");
! DATA(insert OID = 2624 ( pg_read_file			PGNSP PGUID 12 1 0 0 f f f t f v 3 0 25 "25 20 20" _null_ _null_ _null_ _null_ pg_read_file _null_ _null_ _null_ ));
  DESCR("read text from a file");
! DATA(insert OID = 2625 ( pg_ls_dir				PGNSP PGUID 12 1 1000 0 f f f t t v 1 0 25 "25" _null_ _null_ _null_ _null_ pg_ls_dir _null_ _null_ _null_ ));
  DESCR("list all files in a directory");
! DATA(insert OID = 2626 ( pg_sleep				PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2278 "701" _null_ _null_ _null_ _null_ pg_sleep _null_ _null_ _null_ ));
  DESCR("sleep for the specified time in seconds");
+ DATA(insert OID = 3627 ( pg_execute_from_file	PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2278 "25" _null_ _null_ _null_ _null_ pg_execute_from_file _null_ _null_ _null_ ));
+ DESCR("execute queries read from a file");
  
  DATA(insert OID = 2971 (  text				PGNSP PGUID 12 1 0 0 f f f t f i 1 0 25 "16" _null_ _null_ _null_ _null_ booltext _null_ _null_ _null_ ));
  DESCR("convert boolean to text");
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
***************
*** 442,447 **** extern Datum pg_relation_filepath(PG_FUNCTION_ARGS);
--- 442,448 ----
  extern Datum pg_stat_file(PG_FUNCTION_ARGS);
  extern Datum pg_read_file(PG_FUNCTION_ARGS);
  extern Datum pg_ls_dir(PG_FUNCTION_ARGS);
+ extern Datum pg_execute_from_file(PG_FUNCTION_ARGS);
  
  /* misc.c */
  extern Datum current_database(PG_FUNCTION_ARGS);
#26Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Dimitri Fontaine (#9)
1 attachment(s)
Re: Extensions, this time with a patch

Dimitri Fontaine <dimitri@2ndQuadrant.fr> writes:

- Bugs to fix

There's at least one where drop extension leaves things behind,
although it uses performDeletion(). The dependencies are fine enough
so that the leftover objects are not part of the dump done before to
drop, though.

That well seems to me to be an existing bug that I'm just putting in the
light. The reading of comments from findDependentObjects() makes me
think that following nested DEPENDENCY_INTERNAL levels is broken, but I
didn't have time to figure out how exactly, nor to try to fix.

http://git.postgresql.org/gitweb?p=postgresql-extension.git;a=shortlog;h=refs/heads/extension

Here's another version of the patch, v3. Changes:

- mentioned bug is fixed, that indeed was a shortcoming in following
dependencies, due to a new way of using DEPENDENCY_INTERNAL in the
extension code.

- system view pg_extensions, using a function of the same name, lists
all available and installed extensions. That should make the life of
pgAdmin developers easier, among other users :)

- pg_dump now issues CREATE EXTENSION foo WITH NO DATA; variant, and
extension install script can use pg_extension_with_user_data() which
returns true only when they have to create user data objects (often,
fact table with pre-loaded data that the user is free to change)

- pgxs.mk now will use given $(EXTENSION), $(EXTVERSION) and
$(EXTCOMMENT) variables to produce the control file, or use an
existing one. When $(EXTENSION) is not given, it will try to guess it
from $(DATA) and $(DATA_built).

- more documentation, I think it's all covered now

- merges of the cfparser and pg_execute_from_file latest versions, so
that the patch is self-contained and you can test it should you want
to. I intend to merge from the official branch (pgmaster here) then
produce another version of the patch without that code once it's
separately committed, as that's the outlined plan so far.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

Attachments:

extension.v3.patch.gzapplication/octet-streamDownload
��-�Lextension.v3.patch�<�s�6�?��v�/Y���8V4�������v�}�v4	I<S�J�Vt���ow����n����EX�.��������"�%b��x�\'q�h,������..���������a�9a��0d���#O��0�:-��������h��{������.���P�Q���N���I���������wg�A�������X?��g<e��B�t��������Ule�$3'v��	gj��L$Q���QO��KH�0J�<?0��@4���u>�|�?��!0�^{6a�JpO?
l�E4Y��rg(��
���,
�i(�����8Jg���n�P3�'������<{`&��K����������w7|�c�\��8����YB���tZ*@�F3�{y��x+>�|x]�RHt��uxu �+�����5�}�&,�`^F����c[fW� �aO���r6���Y4�
��Z�9������`7�O@��d�� �Ma3Nn����%�6�4A2q���yXF����`�g�Y�n{�����+��}pb��.��z���rM��1P=:-������������'��ZY=�iI��R�����8���iw;����f�)"5`�j@�M�Se��N��G'mc��N��C��~f�,��|-����<�|��6���2Z�:�q@�#w�zU&B��!�J����0���X�����k��R{��_�Z�\�����q�p�,�q���q�������n�}��%Y��_�j�NS�h�'x�a�XR���:��F��p�4�������g;buo���x_��RG`��2�`K�R��5�k,?�������%/����������#P���� ��0����d���/^��h:����Z���!�i����(0nEk����������G���#��dsp��	^�������eEw�G8c���*������8�w�4��fO
��P`��/�5��xh�60@��0XT
�d�Y9K��*�#���2$�Mm����������������=�/�D���a�X��s�:M��>��A����J�e.�4�)���,�u3��.�Q�s�d��b�,0�azB��P
��&��;�G�b�����h�����'B��H�F'4ro����89(�T�y�h���g���� p�����)
a���k��&�$��QPH���S%j�����AN����?��������]���������S����
�lS��;��`Wr�Z�-��.��#G�+��,���G�l�B
W����z7����)mh1<S E:����7�����O����9�X��9�c���u�?�	�|�X��z�B�21�R�Mj�U-�����\y�H���3��![�H�$T	�MA��wDs��dQu�������)���[,��
6����� ��l��A�"�FP�2�3���M�k.<�Q{�������8)	t�&�tf\����ZH�P�F3"T�A�I8"����!oI�e>3��(��������W��FZ$*��hb�PD��,����!��Q{�Q�����V����>`��^�fk=N
�K�{X2>�J�����RJzK�a�?W��C�A���	m����h!��aK��VE
6Le�+�^��#5���
�
]�I8�e�,��]���G��n���`�&YBGS+9�Z�}�����=/h�ZK�xS�5Fi���,d�t����/�g<���L%�4D��C2��T�������GN$�c3@	�,
|7�m�C�2[:�A����WW��`�I#jcT�9DF'Nt :t��t&������@���n������<��*��}���u�}p�&6��\�����?����6{s���>�c��a�F��6�6�l ����:%,��\n�����b�����$���y���������|��{*S�&��'�
7#���<��lCu�Y��.�#��V����Y��.�������@��'�D���
e��V`�N1����G��G�U����.�J�j����$��k[[�i��#P��)-3'�������-38Dc`:i�p�-�B����T-�#�HQo-�
d���.���C�J���k$u�(JCk���k�t��UlK�N�
�8SJ�&H(ES�NE�j�o�w�+�.ZJe��>���/K����h2���N��
���:�H)���6��R�j�)ox@i��z��.K	s�R�T����N��.h�r��~�N+P[���B��U���j4Lm=���29��!��"*��lQ�������[��#>;1��~������3%iu�@������y�sC@R�@]b��H�-��%��\��~����z���{���GQl8V���3�;<t!2f�2N��k�� ���az$
��X6���F�/o���X�kt�'��Q�s$)M�`�{�y�v"�$�2��."�b*��>�!�7G�u�l��	y�����J����s`#�o�yI��
�$�E��P�����EaOH.�<~��[DY
���
���;�����#��}���!�	�:M�����o!�����
�b����m�1��M6D���8m7�0�sJf_�7,:����b,��UD�������]&���-��[6�_f�avu�(�-I\����xH�U��pA�^Eqs?�~��9�_(�~~��l�=�G�WQ�
}?;�S��Z�7`��N`sL\��<���tq���R�{w���,�����X�]�YJx�'x8$L�f��_�������������R���~�_��*�.7"�wj7KU���o����LI&���pu1����
�����^��+�/��_����������7v��=��v�_D�b7��{��F������2z[�u�@4�����B@:�5a�E��������0�;n/��"�b��!���g7}��������%Wz��u��NO3�ild�m�c�����;�;�8���(:I����;UV��5���"�Eh�a����TH+c�������7t�Z��cp;�MY������0�Oj�E�.��E��W7{Gx��ka��.�b���t�~p4E�`�����SjB��Ps.S7�{Y���$+�0����i��-9��?P�Z�r���*)ys�8�{!)ys�0���h���$��r�9	)^"!4�����:���9�(\&�a�l��%�@�rQ@c�\���T����<��
��V�Tn�J���%���N���i3�Y���yh��qL���������N;�:�����t�2�J:��
�����A2�S>w���c��>DA�N��;-�������4z�l�}�3{�niN�7�&�T,�4�tv�?�T{R�$�����x	���tM5%8�!���P�=C��������E���c����[,�h&|����g|(9���q
df�>�b�H��p��o�X�����g��(����Od��b.�W�����9�I�(.W��sN��w!� ���u�$V�&����O-���f!S��%��\�@�a&�QF���J+�S�,A������fUX�����|G9
���R=.�Q1N�	@�t�A�D������~�������YG���U�5�g��\m�g�4�oE�����Siv��kE�(��hj�)&�t�Q�e�r5�]�id�C+Z`��TJQ[4��n�<��;����G"��l��"w��"Ws,�vN�N$�T*�ZE\I2U&�T�E
|�h�����<&H�Ao��z���������M�*	�[0@@)�h�~2���,���y�0���1U�U� Q�� N��R���U(��rr0��`����`��D�s�Y��U����/S�b�e�P.��v	�����w�_U�a�W�k*�|9�>�b�eU�0���,s�\��)�z�-���Sq��_/Q	]��"Q������bE_V�;�X0���b(`�=�������[f��JI����P�����T�i+�*���b�*�O�T�o���$N���(���%��sy2,f`����8�Qzf@����{x���������_�nz��w��G�����Y����`7����������e'�`�c���&�tEZ;,��<�m�c]��QJ��o.����;�����>�C����Z�<Y7�G�x@1���R���P&Y����)\���=���
\�I�i��������1������h�������,��f�b�3)�%u��2&<}'}!�
�P�0�B2�;r�?y�{�g��|���r��yI��� K+�'�u�Gx�6��!�B�:Ci�����;|a]{�*/�N�_�J�EJ��g�N�H+�e{���;������?]kh�{��Rn��^n�dP~mf�������}�����8��xh�~_<�;�����B+���sv������OF
�Up�o����������o,7�}�h���4���N��"=��HS�;
���Sb��m�
XY\3�
�i9.�k%�
g��������~�'�G�c+���� �[?t���l=M�@������W��	:�OE���Syr�wJ���I!���g�"J��J�k�i������H���"�;��F�-����J�f�8)����6R�x����Q�m�S_nP�(���[���%P��BzK���5Lb���b!��`&�S,~�{�h��$�	L^����h
�}��e����>��KI$��i�R�"w���U��#����a��%����y��!�O�>Vu���|6��2�I_l��7�h	�HjfK��+�M�.���5����=$Hy]��-�Q]��!��a:i��ZB�����p�Q2;�j�2=����F	��4>���eKb��Q�}�a%.<������{�l[	V��GIB����X����a�(��Y��n�f���o `������H���������;���{�8k��^D.ea�%1Ek��`
S<������V5���dR�����6e+�t
�M�R���X[�hO1��n~��|���w��a�-���A��bu,:�����^C>��P}���_N�u~��n����T���?;;��
��
�������`"��h���|m����S�u1�6X;MX�o������{��?X����(��*����A����0]f��? j�S}�=[N��y� �W���
�=���p�����P�6%�hF�RV\%������FI�)C�Th�(�L���I�s5$"����{�e�wO������S�R#��������	N����p����9g������,f�H��D�,���|gP[���V�������<�K�B�(�Y�I4��4z=�r��&�U���b�S���.������!]������u�����(EGo#�|D��&���wbN5�l��C�\��uZ\[x�-P�2z��LYy��D�@�~i�_)�2]������B/h�4nQ*���X���������r3��l���#��'8�������]l7�������?^}�������o�
l�4���UQ���_�'w$��������sk��%�}�����M������*k{�=�����4����_����;�sfKQytTgA��"���-�
_�r��k����s�������34'��C�V�W�wn����������P�n�����g���~a���h&��g35~�_����y���0������y����TY%G��y�R�Q�9$(����7�� ��!���1�5.������M$��e��S�]s��2���V�������{�N�CRu�M!�����X�Q+�uV�a�@�W���zIk;��
�����+�����T`S�P����g}>����.���Y��,�\$����=j�	#��li�7�Got8-C�v����ow�����c���7�������Od]x
�
���������!���vo���M�����������#��'��k���>���/.?� P��R�{n`��C���{g������@��Mv����F5��;K]����l��y�T��C��Z�F���
K�d~S	=�</��m�K��|��!V�D��O�Y�<Zb�����y�����w�������i(�e|]�4�%�,G@���X^�m��NH��S�k?q6q(su��m6����d=kF)z�>S*K����Ve:���0��d�?q�_X�	~&�C�`�C���"�\��&a�	?&�#lUVo�qd;%���Wc����]9����{_���0�7��P8m���`�k��L��	P��o����e���\I�{����6�id���I��
���g��u_���p���(��.d+���Y�~`�/��v0���b�C���H�Knl���f;����~�	�����k6������%��a�Z�����X��ArWA�"X����	�K�e��%;1����_�N���4��j��P�x &����d�t�g�	iqZ��]w��t����Rsy��s.�_���U)��h%��0:������(]�g���m��2�;��]gX�ZO%�����P4AR�+�9��v�����n���`h����ss��f�?Zf���$�
~����lm�*66���F}c��3	[;�|y��8���;�d��Pc��5��t�>@�{q��.���DG��G����d�KB8�����n��k7���I�K����������[���2?�4���++���[l��Q)Q���Uy��Si�������P�E�fSo��>�����l�WUW�������$��LFQ�1K�}�WY5ZKV�~��������X��:������"�0d#�c���`\��\�Y
��I����#�u.��:�9�&���J�\��^�#y?�����\�F�"���|�� �Mq�U������$���.�V�O+���Et+�H)d����:�],hC�Y�
FG��=�b5M�.�h~Aa��mS���	�&	��\�H��b���G����(�#��"����@d8�2�<���^�:�~�V�8F�����l���p�j������ ���`�G��j���I�{.��p�Bye���[�����&xg�2�|;wJO�!��I:�x���g������
��W����`������Ec#��6|R�����$�e�#X���P��;o���-*��A�/���W�@o������.	�#������K�A~���9��\}������NZb�oVO�us��@[�C�d�*����S
��'�SM�gi��*6a�R=4��(_%7jn�m���k�,�^�����O�P	��������y
���B�qIJ�U���@z����F1��a�_|@p��0�3�}��H��7�7M�I�j������Tt����(�V�V{�����z��ju��
����o�T9j$����������]�M�yY2Gs)��eo6�e���0�YW,�S�{�4	~��a�$W����7��ieU��"�A���p���������\/�'��]���[�W����C�������s�����������"���"G@mK/
�u�������C�6�C0���F�l��������1~��������Z���H�q�����P�-i��:3�aB4�W6��F�S\�t�[����oo�����h2
�
B
�v��'����'���M,�Z���dn�I8��c���R�*#�������	�6g_�p��n/�U�v��5X��*�P�=���t*�~s�P���2#�qr�������_�������/�f�e8���X`49&pC��	]�T�I8����%H��"bl�E�'2�����cMC7��ldV+�X~���h��Ye��=>9{C�.�����x�%M���9<�|�����\L�3y�@��o�5�\6(�@l�tB����2��n�A�R`Z|���sV�V��WI��:n@��a�#|f�`._�*
�75�w��J����������Rh����U�)�����*��,f$��� �Y��^���q��5�g�m[�
<��x�s�Y�]@�.�&d����{��g3?��
�9��ar��h���Y��� R�*\1�)�E6$(�%����>����1l��)*'������'?$��4(Bx&l�G����w=?B�� �]�5����s�0��b��������=��&] i����ZW�����%�u���������C<��:��,���^�	�ZC�����;����[/	���$O����H"'K����i�1Nb�g�X-�����Z�l5_���uS�
yuwL��{�= ����R'�)�$`�f7�l����1���������R��1T����f6��$m6rc�vd+h}~���#�u|XahP0�3�a������N���:J
|�����XF��5@v����RA�fv�:z�TpU���,JcE�H���W��T�����������NC(��r�T�����=�J!x��%"�c��J!�/re�����T�#++<IF���D���^���cU�C
�f�z�M�xA��|�<}���~��M�A+z3��DH���R���s��$���B#����>!��dLt�m���9Y��zf���a,2m������Ep��+�6�����k�,��	�~c�����2z�r�S8WS��4������w���;VsuPs�dz��9���d�|j� ��~B:���V�2RO�o�4�1���������t+T(V!����;��g�N�~�j�&Sya�4<A�[KR�{��r<���{hT��Eg�(��������
�?7F�D���u����v���w7�H����:0�s���=�Giws����Ng\�}N��n���*2�	���4VlS��/��?�g���SY�R�	�[��aO�_������A(p++�����,�z���[^9}�;�8�?��k�����5���v]@��C�������]&X��o�����jv���i?/x��m/�p��G��Y�R�	�u�i���O�z�(A�r8��w@^^��a�C����X��|������TS�	��	"�yae"Z�v�mA�vc*��H��&�s�f�eI��8fl�|�m��j�L�e�fT�����[8�����r�O���D�	i����n��
���40M�����S(���������������1�"��,�(����*���j�5�e��v���d�������������>Fh�����qL�C�����5U�72t��E-�Hi�=�E�A
��z�����c;�7�P��W�~CN�B
��7�P�A�6jW���?������Rku��/��c���!-���_���M�q*�SNj�����u
��y����4��	���������M�!|�s?�dE�NL��b�+�J*5y��)���oVD'��X����'1��b��[�i6�����E^f-�#���t`S���oh2�s]|*�]%���s��������@��dG�/��>9��MoR�I��Ukl5mr��� ���?9h����8<��w���������Q�u;c0��MrUYU�uI�a�1���F�}�	�j�Z��Q���*Ue$.N���B��g�~��d�����|{��S��:��#'L��#>����i�O�n#�a�*^�++�����(�R����]{�o@T@W?��|�-���s|�������@����0�{Y��gO�>����1G����7���p:Cgy#��Q��]����M�si���0KC9�(c9J�Rn'��I7��!�+����Z�&���*��
�N�vO�S#��p��x/t(TG����ja�>6���^�;Y��K�	�O:f�x�r��3@�?���F������.��l�Z�����4�=QM:�zP%
\�����Wi�C��a�0�����0'nYGK;�����f�6��n6��#>��L��y����G~���R�s��p{9B�3Y���4����x�8�bC�+�G�����0��g��������bs~���r���[�:�������66��	������%4���{����5��pfJ�z1	�O)��>��E�$�g#�6;�8�w��P���B��xXl��bN��0�����of�0I�����S������S�Y��$q�@y ?�����W�0�3�;������i��E���z��|���x��_�/���|G��Q&��'�,�3�-W���4�?fkf��B��-�!���4=
��M!������(��;���D�,��	n�����q�����X����+��E4��GU����aAZ�B��S��4"�7UG����>�6���{��N���DN�r�[!��/����B���xg�T�b����o�z��������
k�P��h}�8�Lc��G.�{����~��<9��v
�h6�����\!=����B161�����$)2��1i2�P
d�r����Z,u�_�	U_���#��^�$����7�3��2�5�$��I���e�X�;�Taw��}6�C���!{g�d�����Z"qKy�$)p��.����t<B7��W���vn��0����������!nI3�����c�-�9[��
�&�U�g��0q���R�0y������$��!`����&/l��
�2=9�E�G:���l.�$2��*��h�����+�8�2	������sr^�)�z���g�E��,T�����}�X{48\����h��D=2
�Y��*	[�������AJZixE�r�9j�c��X�����1[OB8�����)����w���n4r���V�K�P�9��`<d?U�p`�b�6���������*���h02��zY�E��&<"���O*t���0���|N���#�[�F�-
Z"�!��%m`
��J�XE6��j��d%�����U�o�\N��na�8���	-��������&18��A������f�@H��(�� j�
e���*�2�����Te
i����u�U��u���Y��E>~�9�a�9G�X��|���1o���ek#��A�R;���TR�a�(��ct0�"$��*�ViJA��r^�T���3����[������,���:�	�(-)��VV�KNu�.�}�\xrEP�n������������y���tg9�p�Q�S����PU�UC��r�^r�	�~2�R��4����0"u�=]�	���c3�i��'��ZY�eQ��_��z�_��:���Y����B�'g�I,���J
"W���6z��l
��3��0
H��v���s��v������zv!��u�8�G�t�F�|{��]��� <r���4G�E���$�O�����*�S���V
��Rz�$��)�_y�n�O<(����`BU�6����AdJN�>eoPB�@"�t�suQ+�D(�HL<�(����(FI���[8�8��,A���y��r1	��!�����]�[�
!��{��.�A�����������D]���WV��8B�E"������Y�-���.!z�f��� ���������5� .�F2�ne��|�I�D��u�w){�����&=D�8b��v�r�p"������a�� �����N^��)��n��'�	G��r��t.��s�q����O�;�m@+�e�����w~B_�{S��`��2��+���.�:7�UVS��@�dO��N4�\L�?�=��h*!�g�1��`<LMYJ�k���W(������k%���Eo��)�e�6��quOU��P�������������MH<#f����\��^	j��tf�����1����H��="�8�L���j2-�����k��q�����8����^�a��p���y_��A���[%�mi��V^�}3�����~�U����A�5�FrJ=Qh!���IJ��p�����7�d�������E���n���75��U��E6��a�zO�<1��iOo�`����Q"�CG�%�����{��<�0o ��
c{[aE�1gU���4�>�S�����W�Dz=�)��/c���A����@�?��Yc���L}L�3��Iw�����E�7<�Fh��������p-�	�X
����$Q��uo��9/������r%��X�I��#�����IpG����������h��J�C>_+�>%��F�9��������_qr6���		'C���i��SKV��/
�� ���4�{�O��&n���T4Q� �h�(�+&��dQC]��D�xd1��`3���|��M�����N�~J>�j�U6����X8�V)[�"�{Ul����	S� ��d-�@��5�({e�2�HC�H��A��tm��O����f�$Ka��j�h����7��xr�`�.	e?���(����������IAc�@h������ig
�sP���	�iHa!\�dy��tH�K��`�3�W-!�$��/��EEE T'LY/Z}\WXfb�$���Gr�e.}Un8��H��06tSb��������[�n�������]?�E{�ZVY�����$`U��64�9�{G��k����Z����
^�iE�7������`��o��s��h^��C]Z���d��2�pr��\�%Bc���]�T��76�c\��~<��%1$tsI��,�������D��67\��A)m:���nj#Wf��e<�Ak�7�y�
���������DP
��KF]��Y�b�)����R����9�9rEj]�5b -�S��Y��V���}+��<�`���C�Qp��l`i3d�	
y����z�b������Vr�Mci9�s�b/�xA|����x��X;e*��l��q�v����0F�Sbh�Z���i����L
U�����X��Z�A��.�������L��ve��#�h������i����U���h�O�1���E*bz\-�:�Gh)���Ixq]�s���Dw��C�k	rb
����|f�n�ALn�3��EDw&	���Y�i�Zd�L�Z�����C�\�$�����
0VI��y�������)�$@����4� ��D���f&����j!ru�����bM�>I�Kl�
�P`�GN�+� ��d1F���L:}��6g��������A�F��Mv�{��-?_�*��>�pu��N|O.
��	t_�DhZ�cI��9�*��2E��������g.��+����n�C���U��SpT_b��[����P��cf�f��b�q� h��			���d��gB����P0 �Z���^���b(�%����KN�����3)������b�����N`���8��H��$�g�Cd&Al�#���N�8)�����/]�H'9�bA�^4���)���Xo��[��/�[�cD8)/e�O�B�]J�����#T��Uy�������|q���|��'jf�[������l;q���G=;/A[B&��<[`&�"�.�z��d�{��S4KW3�!iM�8r/�zH�V�5�7��C{E8=\�{�F$���Q�S1H_�d-�'i�q���A��SDS�{�y������`>�Ccp'X �M�o����#�o���Z�a)?�����q!6r�C�Ys!�c��y�a~�-D��CV^�9	���/�Xx�
#�{W��X �<n�igq���}��#&��`\�c�C�p��<�P&7z��{��^������-��8>hv��y>o�7�6-!No5���L�\#�$��6xA?��)����w������A?���k�P��A�"��p��/��H��h��9���e"�dK�aS����;���{
��D/��zI�N	h��(�L��$���� �H���4�NK�Xl
�W����QA�l����0K?0��'fg'��t�e'�#1�{���[j��Fys5�Q4�k4"�[��yKE��Ii�3�'��
�����gvwe^��%��2���(�	��d�"��@i�w#F�������2rb�N@;������f�*�'
��u��d��[��>��W�����rWT�g���UY�a]&�89sE���aQG���8n���KLA	���z�[��e���)(+F�q���v6�=Q�!d�X����S�>8����a.`����QHi���)�$�Te
+�C_:w���,j�~������V�GG�-
�����Q�F�(R�0�w���p~�`7Zzo��G!Lz���wp��?-~����R���������E8����.��������M�P�4`n�d�����,8��o��o�ac�������S;�)_y�Sk*�}�D/���q8�*�Q	�U-}&ua��������7���b�=@�@4���`$n�4�xE�
���I(1�0���F��
KE%V��3��C��;������/����=������O1�0S.��k
&�=�.�O��bFe�2�i
g~'���X]�qb�9[����kvY&��~�����9��b�d���
���?+Q�t�h8��F��KB�C�@���������g��W''G�R���aa����h�67�,�k&a�
f7L6��Y�<���Nb�,�����\�&���p�j,�1���n$_��>��f�:�����0�9L<�
��M�j^+���AM��
����	q�)�������)������%�yYn|m�����qpE/uk��7��Fu����G�zZ/�1���?!x�i
��U��`ux�wae���2T������:���S��`�.��@��,�R�qd#���Q"F�8���D�������O?�*!SYN�\>*�qR@�YTn��!���fm;�����hI���#��c}H��mh/v���\��n��b�6�+>'�����
��W_jT�vI�.!i�sLyl�r����#r�nmb���(�"��r�g<#��'
"r��yO��/!Z���ZP�Gc�F?�tl��,�w�/-L�A7p�8�$�����b�\!����0-=}{��A�m"[��zxD~���l|���{�k >�����~`uP.��V���E�4��X'��R�R>���"m�����[^��g.=[W��O��:�J�V�m��X�T���?��~���]_��1
��;���u���P�����g$�+Zb�o�7��y��;�PfP?��E=���&��^9����������L��T�c�KI������?*e�o��nM��P}�G8=��$�4MB���7l'�h�s�=����iC���h&e�MB�F�8`xqg^������1t'���H��������J��!�C	YB��9J���kFlc�\��'�V�,��QF����N�L���o��z�c����%Y|
b�F�A���z����tRN��}�f!�D#�'������C�zO��mV�AA�!�o�����hzc,���K��h\X�^��;��u��[GG���
��Rk����g�NR+H�hb���D&����>���^�[Yt@8<=6X��yEi6(f
�����<�H3O_����D�qH�<����a���f����+[b�^��%�m$�+���UP��R����e8(-TU���i=C:K��0BY������6�D�y�n6�F�,l���#�adLr
�@Wv�x�.���KoY�g�:��	G���'���P�MeIT�|�l&�\<�s.,+#>#������7��������.�A\�
�h���x@!W�m[5q��D��^�
���^7J-����q[yl���{,�@f����s���1^�v��!�^5����e#�����.p���U�A%#�$�H�)"Gt4����������'Z�X�s$�b�[�q�|�9[@���x�PY�����|�!���U��$����I�:W!W)�Bg[���������c�*1-�FY��{8��,J�
�j^p��u)_��8k�-�@����Z�K ��V2����;���T2���4�������\#h.As�rB*�{F�,��]h\`��*��E�3-M��@��9�-�����5�I����e73���"_����H��8�q(V'��A-�t=u7B�x�,���1��o<��&k��O$��#���~��Y��q�#�?6��?�`y��0���Y�{���q�����"B�c �TE;�.�@d=�!3�r� \�h����������bf��������\�
��c�m���F�d��o���f}[���r��D��b������6�m���7Fn4�;`���i�=���/���moA���Y�yZ���B�H���2�r��H������0e���+��3�����Qw�k7��g[j>�o��F���oI���1���;����ocC�v�pM2���zo{���i�K������o�Rx�_���
o����gO(Rt��N7C��M���-��?s;f7�;	�{Q������Q�]��������X�+O%�H���)�^m=�J���W�fz�$��D�����('���>u�v�Qr}�<���~�i:~�ln��k�<���$�d)?����y�}��d�o�l�����+A{��+/��]m��6v���:I )
���^`���Ssc�o��{�dz��	�w<�#���[4�W�]������I���%�v�������6��;U���k�D������B��k3��v-8�T�*�D2&ZF*5���]��Jsz2��H�jC�[�<����0D=��C����=��o�Zz.*1J9��6p6/_>A��a0E	��7��5^�����Z��*A�q�����	�n�<�-����4!���w�]���������3�A�
{�p��YL�G�-D��w�w
�����M[Bia���@��"�	��-����H�0���/�l�C��x �^�L�Z`�\z�hR�����c�����k(�����/���E�
�T4�/��m���o�w�'�I1�g�K{�[���F�K�]e�x��vv����E'#���������:_�N��{~���������m��_�m�d<����������I��f-�A�bt�d��x����Z���p��N�F������g�Qf��o����oz�X��2��?G�6�����y���a�4�y�6'�D�;�����b'�X�o���s�����:��W1>{O�A�4���4�([�������vF�������;�.�@,��%d������QN��������g�����N���b�������������L�C5sm(3w��R�.�r����x��q�����-QR�$�LaA<����rY���m�]�W���);"�����U�M��m ��%��*��FY|�=����������L���E�Jz�p�C]:�_f'���i��=��R������R�.����.���P���'sc�A�0�r �I���6��x�J^�A9������M����{'l#��*T&w�do��F����ikM�����	b���h.��C�������y��h~4�v�=����8�
xb�����`�Tv����gd��(N�u���0[f�	�L��Q<�a$I7H����?�Yh%�%���t�����e^��lj|7�a��%���4S�	:=��������o������6�0�P��2���8�5��C! �~Mh��e���+;�>��~cc�w<�<����`����Y�D��p�G��
B� ����>�'�f������@�}�Z2V�.]Y'�r6��V����|�;��j��K���P5h�����=���n�u�����'54�Be�'=G���[9��C�����q���;�K�~�������������.�'_��;El��������D:�*�:��w���^�{��i�uv��;�����	�����u���V��>\H���S�{�[��y��n�kKs�w�����qz�>�O���
���N��~�}z�0��.�	������y�����{�����#��#[��Z��2����8l����;l�������������������o��v��1�����:k��?xsv��;Dq�����,��=S�������3Yl�:uw����RN�s6?��}���<Uc?�&b]|�=l��/�cW1���aQ�^�qJ)r����w����ll���5W��o�6���X��9���t���9Y�N�1�n��_�E�����p����*a��R2-�|��!��z�_0��/�g�xna��B��LN�#G`�c0�LQ���3������n�{^ y��rR�!��u�rO/��-#��T��#�4��fz��6����Bol}x&�9{'�Pn���������KT��������gms����<
X���U����{����7����$��.�b���
5"H[�J�V�����!?��!�Y|��N6����*�x�lR���g���90�9���\�Yt]��d�E���3t�|�F�����2��������7�{��B��t�#��<����D��<�����W- ��%�|����mX��}|G+��q��v�?x���me���m��@�_�Q���E�5�<9m����~�?ju���^p�fh���D�K�����
��������:����wv�1�F�ak�h���Y�}�u��YYZ���h��9M�;�/�x����.���%Ta��.�Xhn������LW�y"���[4���E��zh�������U4-��Y����V�������`�S�~��U<{oT���U%{���MQ�������F'�E�)�U��[M���3�W
�;��)W��}����0��M�f*{������
�5�r�G�wdVV`J2��EH@A�_n��-���,14_�C�H�?��r���#�el��
��d�O`��w=/`���E��=ggb��MO+������Y��<�y��w�����wu����FquL��y����Z�u�q�Jn�k������-�k1��v�����cM���Sd����^~A��ek{���1����KX�w��d�ns2�\&a����e�!�����-XI*lgs�V��P<�~����;=�����N����09��G�n�{�"o����Jd-WJ���H� ������6��sc���U	�om�A��)�H�W���1����m�[�7���l��e�&��i�)����t-s�����_?�D�5>��h���h��~��0	+��=����u��
L����V�����wq#����9��\��gW�CJ8iW)���72��53R������2�z��������fBL��8����9���d4�($(3�Q�Yz����d�J���xv�}3��o��M��)�c,��Um���V�M���wMR@������Wc2�~����6�_]v���v�d��>f�u�	aP�p����K���M{6�"�6O���P��\b�[�L(�Bb*�b
�p��
b�r�>��Be���
���n��Y������7���x>�A~�f*��O��O���,
�
P��t���x"q��oYX���4� �D+���*�Y�t.�~|5#�K#o_����xv9���EOa�q�������-��`;����z��:���9O*p��m��������#� m�v�o6��~y/�1a�w��^hL�b���b���������4��!q����U��V�sd�{�*��>u�4==����lx{S��$�0�d�\���X",������������f���i��8�V�E	���eu1(
����y�,�$�44KI-�FQ�;�`��h��6�BN[���8��$J9)�4`fPI:�WNe��������kR�`(���1�_Zx�4�����c�7�&# a�dI�\����@������c����������9��1P�>����,�r��Y����c�T~���$�ak4#4�>3��UEa��ju�P���#��3�HQ^�������s�g1���4��C����/��s.n�*�x�6h�^U����D!��ef�LU�B�qP5�C��D�L���k�)��%��\B�\Mn�
���4 ����tUItd�A0O�8^�H��yN����$������n�������*��	��G��*C5B!xK��c���o*�/�3�{�Q��R�\[F�%��
��
�����s_}?�w��RSO�����,��Lc �/%�6�3�|]�	)�����
�� �at#�$)x�5�0�fTnN~�[pq�9����
�'w)e�R�v�tXZ��"I�E��k,�
	o���}�%D�=�F�_<Y\q��dM��B�4�������I�HxO;��[�kR��)/�]{��!~����9������������r�aR���H}����T�{��-��w��������CTX]J�1R�~���l�������~�Uk�o������n��}��8:7��ZC#��v�Ju�7J����+����0����)����r�&�&����D<�F��G��^"�������Kf
$��������<C�c�����K�}R��((!�U%EM50C�!���j�vE�>>z�)R������Q�u�{Dc����@�
M��U��O^�w������x*�|S"1����(�%�(���s�+{"���Pg�?�%J�j�pk�]G��!��d}�T\�,��f91�"�5��Fq@-�R�:����2	S$���S�h��v]� ����g�:�`�^�y�p�l��a
����CmdB��_WR+���>��a�`f�EI�f
3|�w��bLp�5�������eJL���?�*�L.�Z��ULI���
�Nf�~#X���3�D�

�i��{{���-hj���_A�co#�\LB����{�?�$��I�m9�z��.����m�a�e-�.%r= zk���
����i�X��	�j�����0���j��[���cB�^�W��Cy�v�)������w~�Q�r����M�eZr*%w@�|`r�\�j�����zS"���� �0�
����J��b�
���
c�q(	��������1w������,	gB�+��`��T,{H�H���gNZ�F�`6�9�A�fJ�^Dd~"M�Qs-�V������H���x����X�3���s]ab.��hX�x
���8P�L�#X6a��T�,��&.����-#��O�'����Y�s'P3'��%�6[0����?N����P4
c /��������u��K"��h�J��h�&g���a��M����Q&��{����@�c�������o�%gV�tW�19b@��XnL��{�����_����U��$a��l�$f0�U�R��3[���:WL�������[o���5�_�����XS�s-Ui�$��B�s����:5���~���?
(�q�1<���kXO�iM��wn
���T�%��8��.	�����Y���91��I�6al����y ��|��7�,����_=�	�E���'�USL�
���a(�)�H�p�dT\�Xl�/r��m�Z;1(�C[)+��[��5)�E"W��05�G��$0�"�F�����p�7����gU�6V���!�����wr(:�{6Td���l!~[A�"�U��������%�9��RJX�c���Q�\��O}�r�Q����#���
�89C���\'�'O�(Y@������m��xO1�a�]��g��H%�`	�(�<2�*;nz��%Q��I��}��?W�u+uNb��������`���nW&�m	�R��[��P"H�yH�$RC���r,�����	�4�:J=�����r�b)V*�
�����>�l�=��'������?�7-�
�dh#O"�����a��}�cC�jVV��f�i�&���f, =�Dy5�S�c$����p;1E�lJ�v^X�R���k��������l�����rt�[%�g/�J�`��?�]���+�)�sc���Fi��HfS��RA2"�E�}N
�"
,u#}�P���9<���<J�ZS�tq)`
J^}I���hc��T$g���VP��<���,��%mf����$��U���^�����w/��'�� �#�K�!��NQ���r����U%���R���������{�dl���D�,@n���	�����7]r.���'/1�������'���h!7v����Y����@�Rm�X���E�U@���O6�bE��i�9����%��"�)��K�Q�"O�a�h�F2�E��]j
��F3�S.'����L�n��w���$�sfE�#
>b
P9��d���7�4~'����Nu3��\H<�QME�����_W�F�V`v�N	���,���l7%50���*�j#l�C7�������+>�Pw$��m��`<���Ic��5�1M���]�?K#^�i��N�9���5��(�V����������9�ap9�������t�c
�/���B�On'I��ZN�IP_wT����`L�GW=�L~�&��M(�2�}����!gJ*+��� �$���A�����3a�@n00��_���)�Y�_�����>���g�(��8�\��/����-��&����f���Q:���I��=�����
���N��:�}�3
���$��gW���@�^r����a�T��&��M��9�o��	�w�l��M!)��|�W	0yv+UK"���;M�=�t��A��
�����o%p�US�.��8��y�C����E��o{o[gT$���hB*5�|y20��6?�� ��Mk7*j\��������"mI����ME4��B��Pa���n4|��k����	�!���"�'������bT���?�3�����_t�O��N���+7!YlM�hZY�5q:��h/2�3&��V�����V���=�u�O/��P?�����-��j��wJ�S|3r��^����������?�����22��B�f\%n#�Q�=Hg�����l����D�R�J��a�5�hU�	�\���QS�2Dl�U0	bx��	J��",8��"1��Sy���s�^����Z������"u����c���W!�yo��A���0�>�~��@p���!�����fp��.�(�D���yL�=�_"q|���Wo�4�oh�T&d;a��iD��,P����+� (T�
�1Y�}���|<���d���j.�t��Bg�U�rE=E\�>{"����o\�JP���dxE�a�I��yik:e���8��>����k����,�����u���z�Q�����;�����`^�leEo��jzv<|�m2��� +�A��f=Q�Y����m�`R?+�*������gL���Jr�TtW(�
�E���a�u�����e�y�4�0���Ex�������hM��C���@?��(#��`-�aO�`�:����Z$�A%����JL�������a��C|a���	����^���E���{E`z�j�i���8T�]��d��Z�j7���5�i&{x�%���p�;�@}p���,���E��"&&"eT���(�,,�nE����g�3��3��rd�Q9����ft�o(�<�d�g��o����aA����#���3�bL���t��d4Q�#��$��q�j�h�(!�8@�v�G�6A�Afm����G�)4�Qh���Y0�1�~���y�D]�p��_e�
k��dU���M�J��9Y�'��,�nn��1����`��������M�U@3XtP��K[���\�W<O�	��^�r��cDEu�N�FJ�N%�>�4��w>������8��7��"%�B`PC�Y_���[��4��6F����:?h;�-���+�)�C�=-<(>(�-�+np_��{�+�������ReBT.�ko0Z�cc:O4�
�)3 =�&�1�F03��[�)��fsc�yY��?t�������{�[�{*��G����8J��(������W-y�EXpj���v&�a
Y�8��6|��%��.�J��B�=�a��F�o��
}���m���%���u�7�Y��fY���p{�m7`}v$��E']����mZ��
z��a��X��YDn��4�|���{�r���Vz0�M���}�.&�+#���T���
��~����tq#�+���[�W�" ����^��6� �������.R��%������|����&����^���Y=�� ��"�&4c�V���7HkR�JId��i��P�G���8��X�O`���M���R��;��o�Ix��2�1p��=�K@jW
�mf-���YC���e����S`#��4*�f�	�)������60�y���u�2>$�x��a\�e�Q���E��s�i���������G�^�	�r��(#�K��myy�cu<-o��z{u�av'�csI���� 37�vt�F�@�x�Jd��`�$3�K��\���`>���UF�<���"���%'0��bE�&_b��+��fhb����W��K�hn�a�m��W	#���|��(w%�����<�%1������F���-l>������/�h�=�
��;�L�4�}������5� c�����y|�{���_qwo�����dHa��<i!�*�(�����)�QQk0d��xb�������+_1�	�
,���{:�����*f�(�������f~�����K�0rg������D���oe2�G.�<��$S8g��dB�c������T(��)�	Z}k�D���F?���6%
h�/$�	Sj�?l��=���YooX{�`����6w����]3�/j�n����qkC�s�>:y���&	%���}G���G\v����*�zbWw��a 	z��>ly�H[��*
���r2�8/L�a��:��N*2�4��nL�"����R
�Q����M������&����
���m$yQ��������T�G6FU(�����w��)F4B�F+}�b�
�q]�G�"^�}��(B��p�K4���2DZ��c�5m��ZF��.dajz]��B���Y.$��z���^(~������	����??�?�p�S�u��6��w�"�-��K)S�E�/���
f�g|���Y����<�0�V��b��GI<5j���\���CO�c& 7���{SU��PJRw�Mt�>�[� 3%U��)	-<g$H��=�q/�`d���fJ�n�uL�{�k�}���6����I�9�
���,�ht���5�*.����zPT��d�mw������8�T&9Mc����<\P��u!sL��+(����������+��3�����5?w�����b�/(Nto��o��0+��[��hx����N�����~-yT[�G�n���%o\!��^V[��z9r�a1b�����1MJ�f�~��s�5��7�z#3?�e����(�@n��Htna,A�
��eU���VI~����(���zT[�c��3z�����<�+�E���l����k5���8u���Tf��q�v���^L��t��Q�2'��a�6���w�����������0�&�P��&�Z��O�����\0��2�/j�$8����sr���f��xA^�o����d1�s-���s(z�m�������i�:>���]i�W�$X��������7���5�����lz��A���`L�0s}������+��
�oC	�pB��iX�v����v.����Z���
������qti��%�1�� A�[H�Q��M|"����q_�yGf�c��8����-J�
<�1��uVQr�����xZ"�p^:����F5W���(���P*���������KZ��3IV�<��.�2)���W��A8c!���U���8��-wt��F}c� �������������
��pj�l+����U���@ iGS����`��0��|�UV� W�Rdhn��w	�'�K=�2��	��)!������|%�p���HI�X��W������\eb����ia���V��#L3������}8�:$6�X5��T�&/�-	��O������
� (�a_���2�/�����8���������#�s�7���
?�U}d�yvD*��SU�~�m�d3���#E}�a�xW�+�8��9��%��|�R��,);�.,/��|T�������J�,0FYI�lkx����9G�<81�[9����k���>���/<����,1PK[���D�VH)t����K��������c���-hB
:Z����vx���O�G)O���
\�i�P����.�"��"�{r�gt�s�`C��B��
O�[$�y�;�tmln�j�����=m��l�P���8�-�H���w�z���������u�/��IW�V���q��;{
���d�Y<T��>�]*h��zp!��>�i��bckg����l6���y���i)�g� "
����Z��`c�.B�f*����N������b%Hq8M�k�D�bY�GJ���$��e�H�K�+J��T	Gn���Y�&����)R����S�F���y.����%"{�\�Q�y�0��U�?��f{����Q���j�����Lj�����O�-y����YO����&�"?|�>kg�S�;#/E�g��o�k`N�66p�Udf�h6I+@���~��$����<L�{��t����@�G>]�z/�����q���b��$�g����n�m['������j�?�G���}V+��������042.]����s/����ut���F���d��Z6p���e<[4����'�c�dSL_;��K'��
k�i���X��8���*�+U�B���jk&c���y\��7Z�'k��=��k�5�p�uU��� ��(�Z"������S��?�����k�����O�����1���>#����+�h��O�8�0�E���%P���8�WB:��\����
�Aq�}�����\�2XHG=�r�.5�?��r��[�nK���I`&�Di�$/�fa������z��4)�5S���w�(^���_�r�O�U��$�)�4�M�A0��d�����7���y��4�fb����f��KFSB�~=����_<U��n'����9!d�3&�����f��X%3�lO���4����4Q���;��(s_Ysq[bK� Y�P��-�����J>�q��y�/M����p�	YO�q���������\�qU$�&��
��.�8����_��p�����I����f����,&������T��9����[Fr�����n�M�����}o���&2���<%�����?�
�?z�y>u�\
z"��c������9�.Wx��r�6��t�>�8
���0�\Q~"��3(�`�c���X���G��Y����i�~Z�+���1��.RM0����/���D��H��kZ8tw���+[�7�)u��F���6�������wS>�j�D��Aq��X���"�	����������y1��}I3������
������D�7�))X����`�>���.��tI3�z���P�M�"���HD@�'����;C�'�����;��,�4v,�<�1��&�S{��_��L���l����e�^/����
��W��C$P�E�F�����p
�a�v��T(7dB�<C����!��0D��Y�����/CJN�`>�0D����������e�����A��5;���M8����^G��5��0��Gs��k���g�@m6�0�Gf`��"�Q	R�GJ���������*h/�����/@���c���N&��l�5�GI5���y�z���}]/����l|�����v�v� R��BY)�\jnIL#������h�o��~M��gj�����W��
Eh[�yx����&c��`��&����Fk��Z#�,@��
���M�y��U<C0��5���������*�����;P}�%e�&@��}[���=z���,�k�l����qr��Z�5��;K����}YL�����4��=���`Thfd��)��G�@����cn��zF�F=���A��{�[2�F@.C�������j6wv����:E%!����
=6��G����P/�j0Z5�2Rz���f��S���'d���Zp���a-H��(���F����i8x����i��6y'��)����[R3N���`;���J��b�!��NQ��r8$���Ip+�1&V����8?A�z4�vk��R6��:0P��&��Eg�sB���ut�p���H8*D�E"M^�	;����k�����g�Y}��8+����`�a���<"d��K��4��Q(�$�'�;Qd�������t��()��*	����c���LqI��d*tZ*n#4��}U��=1�Xe��4��^
r��%^��U��}=������z-�`�k����}6�IZI�~�����U�e���.�m�%�]{Q5��5��`��1���6;ET.��5�,WWo�S�����@?o<jM�� �yN�tJ�����K���r#���B���D�|���@�d;��{�{���;9;h��
-�����g���u�z���������ma�(��o;����'oNO���v����{�8����L����6w775y��I�� �(�����o����$���_���7�xB��Mnq����*�b[�[������z�I/%i�����q
�fb�>N�i<K�`��cH�QIE���;�e��EfIx�*���j�k0�7S7�K0u�sV9��Kp�BL����w=N$�����W�S�Wi�����d���I�1K6�i�x|�5������Q���$U��U��
����98��`�D�Z�����z��	��hXB��1P�����/�D����o��S8�g��3v�������g8tO�q�����b0�%�JJ�M���T0����+	�g���1s�d�G��*��l���
�����{�}��_�	���f����v�V���U�����6V7�����Z��:7���N��fO������~��-=������������k�����c�Z�6Xy�)�j���������.�H�L��4��1.-���P0���L����*|��J���P2����%���=����m�z��l�nc��;����m�����#O���z���l���v6�����m��\M���}.x;��1�z*;�_q��^��p�������^��t������������i�F\s�S�oP�t�J��������������4�(}�n�����0����Q��x����w��U��C�>�'���
'X��80�gE�n�7������-���'��e����=���_�s�ADe�F~�\p?_��3g�
�\�%���q]
9�����SG�[sGII	��O��3�h|J%P�i���aN�����
��VWV��%1ds�V:k��2d��i��)���x�h<���y������$+8��1-{����`���Cp���v}^�B�	�Bc�r4�%7������@CW^
�����"X�"�
�Y;���v�\�X��F�����>E.�_2t>d~	���.0C{{��[�3.py����I���^,�C	�Sj�
���NF�?�{������{<�M��n�fs��nnA��Qb^�IM�-���@H�OT��?��}���_?W�I�[<��fy�k<��-�	���z/t����@�p�oon����;"����f���L|{wS;���Wrb�
�{�R���9��'����9�����q��,9F����8���0������v����~�Fdv�4i4�M7��:�nk�����X��&����nIr2k1Tn2
���T#`� d��8�,	v�T�0����>&	��f�+d�_$�R?F���/Fn/Iz�}N��0���
�Fb�G���A������d2����T���'�Z�a����K�I���n���b�#K���m��N<8;9�4�{_'/(���cNX{���:o[�=�Jp|���[%��6�'i|'�n��I�)�J����_q\i�������>\;0x=jb�6|����{8p]���p��g��� �OKH����i���e
c��~t�q�����	4��1�%�
�s�u�5=�������
�&eY�0�na�]��J3-��Wy�,���&���Q	&VS���^�k����ww�=q�F����L^7����.�G�����Z$�*��f���0��M4Gw��<L�����C��4#X�;��(����DqZ<�2/;rB7/*�]����w��#y����l����s�4��������_��iI�H����� �
�Jv0�:2�'������-w�I6�� ��P�F�:D���	3���L�������K�g��lb��j��s
A��&"�c<�����v+��;$|N�a%O<AoSN:�������k����'uU��d���1�>8Z��u5����0(n�T��D3�U���%�����E�u�gy1=����#T!w�`�i�
7���q��!`��T��D����m��,���f4��a5����C�q\�SAp��U���>�
t;8B���c�������M�M�:[g��(�gAt����X�����\�f�:4������_�h���9��� G���3�f�M�r6�s
(�<��
8������O�'��L�����
�%�d�T-���*��c(�?��Y9���w�($ �2�����y�V��<5���w�7-��rg��g����a���/������"���I��y�7�(��o�I�
�r�S�diIh�>�=�2$=3�>+�f6�2s���������%�����.�[#rzY�<b3�t�?���9�]�����c����
��j�ub�������$��`qjb-���e�=���&�`������,x��u��4�����dx���Y�����-��*2�hu����,I6���(u9����]�����;���Y����3���f
�����?�T���V�N~.�CM�C���Y��2����!"��D���W=Cf[0�
��g�#������%w{S
���oU����4��L�������0C��U#q2�p	GiG��50���,C1j&�D���y�2tU
�����5���|����a��	~��,�6�����X��f�E!�5�d�P��\6����x��TZ)L�k&9���}��d6�"T����U�c��z�Ud��|��!ZJ��I���=�m
b��%��\;�>�Z><��M�2G�8��I�-�#����y�|�sq��\�fQ�6��=�s l�h+���u	���o72f��2�S� 3�_�uA���3{+<64v�5\&�b�����S��%o�7W}�o4$�	@Xr}�����l�9������h�)<����ZYz���6*k����������������������xq^$��\S?1�����Gh��J�`�2#���
�/��0�U#Y/o��p5������t�?�
F�����iqC�[:�8�GZS5�����X�)�&AS�_6����!�Kp��D�i��\z���:�[�-�^c3'�B����p"����	���Q������;��{��������p�t��
I�(�{�:=����z7p����*~�>l���B�n����e�VJf�-���-�'������R�����������s%e2�@���
�������S����O����QLi#���d�#���0u�x��c1X*F�V���,���FQ�s�W
���|jl�GV_�Y���Yc�}�2��Y��N`S���zr/`�{
U��Y��$'��F	��P���S��>�h���Y���~��+�]P���g��
X�=iF!��g�o���{��;k���������^A���!��C���O!�p��{������cFB]C�A����-��H���`��T�6���QU��%x/S�s�M����)�TD�����u���H��!RD�#����By���#�VIg�p��a�-��>�=�_Mb{O�Lv��Yd�KH
ES2j����� {6����P>�U>�����hBf�b�4�"1K�N�J�@���9e��v���
��i���*Y������:���"y��x�)�9�n����Og��o�2��{�P?s��(��KW
#��u��Vu�UR�Kgmi�h��3�.���]^]�^ePt�����Q���D�^�p9��qt��{��(��}G����6}�,�"m���b�����}�c�V'a��[�>>9�M������wam:�����2!�B�JP������n$��|���V+U�r>�Z���n:^���0��w�:��{����*I��U�\���J��9{\Y��sY5�L�rs�t�#�����={�����oN�4�h�L�p%`�m�>,���k�}|�%L�M����Z!\B�H����/���fX��������`^eE�Yj2l��-��/�\���^���
kY58"t2�0�}�>�`��n�/�l
��)����X�q~VK
_�l��{��'�!.�� �����Y��GL����K8��X���4����J�t�\}Q9������&dp�s�@.;�bm	�����m���ZGtN��$����U������a�oh>�@��#�,lc�8�2���R�Ywn��m����ddw���������	&���i�9KTv|���^�-�U�DX��r���{
���r��W�;|��)�{O���������O�ew1��4��(W4xm�~���Mo����Fcs�[ml�z��ll���������(���`O��"�R�*>g�Zv�y+�LP�{����Uc]���Ywbi��e�t��l���5���4q]�pU�|�X�����f�I�}��%�����1I��}}c��@�6v��u����%���Dt�����AM�R��n���.�A0]Y(wv�������S�K�����@/l���7��^�:T�c*{;u�������z���g�<=���Y�����J����XS?02���k�����7�+At_8���Q����in7vx"����I��c�������B���[W��:����,__&w[�lm��k�"oG(����+�n=r�Q�Th@�������:�o�^�CN�r�z9���M�[��h�_��p2�n{��%V'��qzm�Ki>��<����!&�x���L,\�����@����-�_��J��@yP��t�b��ks�F0�4��h���H�Q�����+���J8:����S�%�R��%�x��?v�;���D�uk�LX4�dy����%&lX>���-��Q��:����.C�����OKt�n.�1���H��PT3V���cg	I���	�������vVsx���Hdr��&������(J�4�O��:�m��Mp����B�j��5<�'��9���i�������u�7��$>��!�T,���Mw�af�D�6����g�p&yD}�.��s��q��m���.��AEv�wIsJgE��]��'J���6�L��O�����2
����c���"����U�x<��4�\5��_���'���CXB����?IX����IP����V26��w�`8������rM?M�x6�,�}���'k�{a������8B]e/���9FK��Q�����%�
�,S�"M� |� �Md�$��D�lS�h�����>�f�Q��##9��2
����Iy<!�DR�:���nC�N~�����YZ\B4�Bo�(f�'�J���
S�k�ZO� e�H�xK�'�^(c����e(��H��h���� C)i�+���`����*���:1B�X����d�=\,:��1S?��?����}�1����y\��^>�����i���"�������)=uu�=�����G���
RS�����Tc�O�+��������S2���������U���PY�h�6V���0�������s/����c����s��Y�E��2�:)H
����������E�����#TV��f�DY�$�����o�3�1�~��s���
�~j-�����j��he�
4�\��M��5K/zg���A6L�?���&z�M��]g��f��9U�B=�$Z�;)t��S��U��[�c�c�u��R���T�����{8�
�/�����5�!r���'��(�L���:>r���)���s�K�����wxr�����m������O���*t�e����3K���U�������7���v�h�'ni\4Khx������'���������ju����7��1��<�������W�p�|sqL�J��7w>�r;>�k��g�����[Y�
��fa�9��
��3�|#r@W���U�E�w~�E0��"�l�R�M�@~,��7W������1x�AG�2���y��1���@��*Wx��U�f�/����yO1����c���!�Ja����^a3	/x�����lg>v�:�����[��;On����6XE�z�����?~89;@944��
W����.���m��8>k��9PE�������vK$��v���%��+������w}o���<?�+L�p����"g/��J�"��K�e���n,���Z�Z��|X����x^��[G�v�����(�s�h����B��j��on�X���~
(�5���DD0�[!�^��uW������h������V4t�g����z�lA_���8 �Am`yqC�Y������FJn�M��J�{�"����M-�������i�h�v��\���Q��Mp�=�
gq�%s'|���,f�XR�����������xwE�H7M4�>�w3:s���r0b������;K������x)�T^�4�,
�����3�x_������p��(�3KVh=�"���<�G�6+��D�_���zS��t��b�L���X8��q1l6��Y��
l��)^	����1��(�Q�]��`�2
��z.�)���2����������a��m�����%�2�	o/]��)A$R�)��E�3�<�6P��d�-4s������e�
�P��.k���(r��i������=v�8:W���0����L��P<��l��m�m�L{
$��IL��q4!P"'�*N�_1*%��kh`+����D.k���O�m�s�Zm���%lU�����!�����O�����YP�t�FI�����7��^���`Zr������w��I���YE��o���^��c���a�w7�8vE��P:�	VFa�{�g���q
����s{N�����*�\��0<���w9KY�L�l2�1�}(i6���D(i�ZF�)3������e&���n�D�[�0J��H�!��Q����y�\~B��PP�� ���C�����S���������.���~����d�lz�!���3�I�����3�!X!��*i4�A�����i��\��KW<~����W�xoN.�]�WL���GJ�E��h7�W�I��W����q����SU
��_Hx�u#�
V�Q�>������zV����O$����u��~U���utD��E-�A�v�=Q5�9�>X[}�Z��v��a4a9�4n�e��Fvd�3�M�'����:9>?;9�h�K��I�\(����oa��'Iy�Ue�h�Ue���F��^i������U���jJf-�������D���)���M��O���nUO�lLV�j����\FFU�KP�UEjU�#��
Q��{��(�x����@k���>l#
3<�������{�Z�
�u�����@�
�D�[3g�0�o�RSP���Z�^BwteY�O��i����
*%�jP*A��Ki���<`��l��T�`��%��O|Q��#\��*�'g�(��hS)�P6�.1#���8�"��o�bTs��@�]�!����?�@��x��~l��(d���d�-��3y������"Qb
#27Itagaki Takahiro
itagaki.takahiro@gmail.com
In reply to: Dimitri Fontaine (#26)
Re: Extensions, this time with a patch

On Mon, Oct 18, 2010 at 8:37 PM, Dimitri Fontaine
<dimitri@2ndquadrant.fr> wrote:

Here's another version of the patch, v3. Changes:

CREATE EXTENSION is interesting feature!
I just compiled it and tested it via SQL commands. Here is a quick report.

* There are some compiler warnings. You might be missing something in
copyfuncs and equalfuncs.
----
copyfuncs.c:3119: warning: ‘_copyCreateExtensionStmt’ defined but not used
copyfuncs.c:3130: warning: ‘_copyDropExtensionStmt’ defined but not used
equalfuncs.c:1593: warning: ‘_equalCreateExtensionStmt’ defined but not used
equalfuncs.c:1602: warning: ‘_equalDropExtensionStmt’ defined but not used
postinit.c: In function ‘CheckMyDatabase’:
postinit.c:341: warning: implicit declaration of function ‘ExtensionSetCVC’
----

* There might be some bugs in pg_dump:
----
postgres=# CREATE EXTENSION dblink;
NOTICE: Installing extension 'dblink' from
'$PGHOME/share/contrib/dblink.sql', with user data
CREATE EXTENSION
postgres=# \q
$ pg_dump
pg_dump: schema with OID 2200 does not exist, but is needed for object 16411
----

* The example in the doc "CREATE EXTENSION hstore" dumps surprising
warning messages,
We would be better to avoid such messages, though it's not an issue
for EXTENSION.
----
WARNING: => is deprecated as an operator name
DETAIL: This name may be disallowed altogether in future versions of
PostgreSQL.
CONTEXT: SQL statement "/* contrib/hstore/hstore.sql.in */
(followed by dumped script)
----

* Docs sql-createextension.html has two odd links:
----
See Also
DROP EXTENSION, Table 9-61, Appendix F
----

--
Itagaki Takahiro

#28Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Itagaki Takahiro (#27)
Re: Extensions, this time with a patch

Itagaki Takahiro <itagaki.takahiro@gmail.com> writes:

CREATE EXTENSION is interesting feature!
I just compiled it and tested it via SQL commands. Here is a quick
report.

Thanks for you time and interest!

* There are some compiler warnings. You might be missing something in
copyfuncs and equalfuncs.
----
copyfuncs.c:3119: warning: ‘_copyCreateExtensionStmt’ defined but not used
copyfuncs.c:3130: warning: ‘_copyDropExtensionStmt’ defined but not used
equalfuncs.c:1593: warning: ‘_equalCreateExtensionStmt’ defined but not used
equalfuncs.c:1602: warning: ‘_equalDropExtensionStmt’ defined but not used
postinit.c: In function ‘CheckMyDatabase’:
postinit.c:341: warning: implicit declaration of function ‘ExtensionSetCVC’
----

Ouch, sorry about that, I didn't spot them. Will fix and post a v4 patch soon.

* There might be some bugs in pg_dump:
----
postgres=# CREATE EXTENSION dblink;
NOTICE: Installing extension 'dblink' from
'$PGHOME/share/contrib/dblink.sql', with user data
CREATE EXTENSION
postgres=# \q
$ pg_dump
pg_dump: schema with OID 2200 does not exist, but is needed for object 16411
----

I've hit that sometime but though that were tied to the dependency bug
fixed in the v3 patch. I can reproduce here, will fix too.

* The example in the doc "CREATE EXTENSION hstore" dumps surprising
warning messages,
We would be better to avoid such messages, though it's not an issue
for EXTENSION.
----
WARNING: => is deprecated as an operator name
DETAIL: This name may be disallowed altogether in future versions of
PostgreSQL.
CONTEXT: SQL statement "/* contrib/hstore/hstore.sql.in */
(followed by dumped script)
----

I don't have a dumped script here, that maybe depends on verbosity
options?

* Docs sql-createextension.html has two odd links:
----
See Also
DROP EXTENSION, Table 9-61, Appendix F
----

I didn't know if using xref would do, but if you find that odd, I will
replace with linkend and a custom label there.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#29Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Itagaki Takahiro (#27)
1 attachment(s)
Re: Extensions, this time with a patch

Itagaki Takahiro <itagaki.takahiro@gmail.com> writes:

* There are some compiler warnings. You might be missing something in
copyfuncs and equalfuncs.

Fixed in v4, attached.

* There might be some bugs in pg_dump:
pg_dump: schema with OID 2200 does not exist, but is needed for object
16411

Fixed in v4, attached.

The problem was with the query used in pg_dump to filter out relations
that are part of an extension, in getTables(). A composite type will
create an underlying relation of relkind 'c', but there was no direct
dependency to the extension, thus the filter failed to bypass it.

It's fixed by adding a direct internal dependency between the relation
and the extension, as that's so much easier than doing a recursive scan
of pg_depend in pg_dump SQL queries.

I will try to find out if other cases are forgotten like this one.

Note that as a result you need to drop extension dblink then create it
again so that you benefit from the fix. Also note that drop extension is
recursively following the dependencies so is not concerned by this bug.

* The example in the doc "CREATE EXTENSION hstore" dumps surprising
warning messages,
We would be better to avoid such messages, though it's not an issue
for EXTENSION.
----
WARNING: => is deprecated as an operator name
DETAIL: This name may be disallowed altogether in future versions of
PostgreSQL.
CONTEXT: SQL statement "/* contrib/hstore/hstore.sql.in */
(followed by dumped script)
----

I didn't realise that using SPI would mean dumping the all script in
case of even NOTICEs. May be we want to protect against that in the
CREATE EXTENSION case, but I didn't have a look at how to do it. Do we
want CREATE EXTENSION to be more quiet than SPI usually is?

* Docs sql-createextension.html has two odd links:
----
See Also
DROP EXTENSION, Table 9-61, Appendix F
----

Fixed in v4, attached.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

Attachments:

extension.v4.patch.gzapplication/octet-streamDownload
#30Robert Haas
robertmhaas@gmail.com
In reply to: Dimitri Fontaine (#29)
Re: Extensions, this time with a patch

On Oct 19, 2010, at 8:33 AM, Dimitri Fontaine <dimitri@2ndQuadrant.fr> wrote:

I didn't realise that using SPI would mean dumping the all script in
case of even NOTICEs. May be we want to protect against that in the
CREATE EXTENSION case, but I didn't have a look at how to do it. Do we
want CREATE EXTENSION to be more quiet than SPI usually is?

I don't see why. I think the real action item here is to remove => from hstore.

...Robert

#31Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Robert Haas (#30)
Re: Extensions, this time with a patch

Robert Haas <robertmhaas@gmail.com> writes:

I don't see why. I think the real action item here is to remove =>
from hstore.

As input, consider that lots of extensions will create types that are
only a shell at the moment of the CREATE TYPE, and for each of those
types you will see the (potentially > 1000 lines long) whole SQL script
dumped on the screen.

In the following script, I've filtered out the scripts, but it's written
out for each NOTICE message that you see:

dim ~/dev/PostgreSQL/postgresql-extension psql -c "create extension citext;" 2>&1 | egrep 'NOTICE|ERROR'
NOTICE: Installing extension 'citext' from '/Users/dim/pgsql/exts/share/contrib/citext.sql', with user data
NOTICE: return type citext is only a shell
NOTICE: argument type citext is only a shell
NOTICE: return type citext is only a shell
NOTICE: argument type citext is only a shell
dim ~/dev/PostgreSQL/postgresql-extension psql -c "create extension cube;" 2>&1 | egrep 'NOTICE|ERROR'
NOTICE: Installing extension 'cube' from '/Users/dim/pgsql/exts/share/contrib/cube.sql', with user data
NOTICE: type "cube" is not yet defined
NOTICE: return type cube is only a shell
NOTICE: return type cube is only a shell
NOTICE: argument type cube is only a shell
dim ~/dev/PostgreSQL/postgresql-extension psql -c "create extension earthdistance;" 2>&1 | egrep 'NOTICE|ERROR'
NOTICE: Installing extension 'earthdistance' from '/Users/dim/pgsql/exts/share/contrib/earthdistance.sql', with user data
dim ~/dev/PostgreSQL/postgresql-extension psql -c "create extension fuzzystrmatch;" 2>&1 | egrep 'NOTICE|ERROR'
NOTICE: Installing extension 'fuzzystrmatch' from '/Users/dim/pgsql/exts/share/contrib/fuzzystrmatch.sql', with user data
dim ~/dev/PostgreSQL/postgresql-extension psql -c "create extension hstore;" 2>&1 | egrep 'NOTICE|ERROR'
NOTICE: Installing extension 'hstore' from '/Users/dim/pgsql/exts/share/contrib/hstore.sql', with user data
NOTICE: return type hstore is only a shell
NOTICE: argument type hstore is only a shell
NOTICE: return type hstore is only a shell
NOTICE: argument type hstore is only a shell
NOTICE: return type ghstore is only a shell
NOTICE: argument type ghstore is only a shell
dim ~/dev/PostgreSQL/postgresql-extension psql -c "create extension isn;" 2>&1 | egrep 'NOTICE|ERROR'
NOTICE: Installing extension 'isn' from '/Users/dim/pgsql/exts/share/contrib/isn.sql', with user data
NOTICE: type "ean13" is not yet defined
NOTICE: argument type ean13 is only a shell
NOTICE: type "isbn13" is not yet defined
NOTICE: argument type isbn13 is only a shell
NOTICE: type "ismn13" is not yet defined
NOTICE: argument type ismn13 is only a shell
NOTICE: type "issn13" is not yet defined
NOTICE: argument type issn13 is only a shell
NOTICE: type "isbn" is not yet defined
NOTICE: argument type isbn is only a shell
NOTICE: type "ismn" is not yet defined
NOTICE: argument type ismn is only a shell
NOTICE: type "issn" is not yet defined
NOTICE: argument type issn is only a shell
NOTICE: type "upc" is not yet defined
NOTICE: argument type upc is only a shell
dim ~/dev/PostgreSQL/postgresql-extension psql -c "create extension ltree;" 2>&1 | egrep 'NOTICE|ERROR'
NOTICE: Installing extension 'ltree' from '/Users/dim/pgsql/exts/share/contrib/ltree.sql', with user data
NOTICE: type "ltree" is not yet defined
NOTICE: argument type ltree is only a shell
NOTICE: type "lquery" is not yet defined
NOTICE: argument type lquery is only a shell
NOTICE: type "ltxtquery" is not yet defined
NOTICE: argument type ltxtquery is only a shell
NOTICE: type "ltree_gist" is not yet defined
NOTICE: argument type ltree_gist is only a shell

Just saying,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

PS: Oh, a repalloc() bug now. Will fix later in the afternoon, \dx or
select * from pg_extensions(); crashes with more than 10 extensions
installed in the v4 patch. That's what I get for doing that on a
Saturday evening.

#32Robert Haas
robertmhaas@gmail.com
In reply to: Dimitri Fontaine (#31)
Re: Extensions, this time with a patch

On Tue, Oct 19, 2010 at 9:07 AM, Dimitri Fontaine
<dimitri@2ndquadrant.fr> wrote:

Robert Haas <robertmhaas@gmail.com> writes:

I don't see why.  I think the real action item here is to remove =>
from hstore.

As input, consider that lots of extensions will create types that are
only a shell at the moment of the CREATE TYPE, and for each of those
types you will see the (potentially > 1000 lines long) whole SQL script
dumped on the screen.

In the following script, I've filtered out the scripts, but it's written
out for each NOTICE message that you see:

dim ~/dev/PostgreSQL/postgresql-extension psql -c "create extension citext;" 2>&1 | egrep 'NOTICE|ERROR'
NOTICE:  Installing extension 'citext' from '/Users/dim/pgsql/exts/share/contrib/citext.sql', with user data
NOTICE:  return type citext is only a shell
NOTICE:  argument type citext is only a shell
NOTICE:  return type citext is only a shell
NOTICE:  argument type citext is only a shell

Well, perhaps it's reasonable to suppress NOTICE messages then. That
particular message could perhaps be quieted, but we probably don't
want to do that with every NOTICE that might occur (e.g. those that
you might get on table creation for sequences, indices, etc.).

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#33Tom Lane
tgl@sss.pgh.pa.us
In reply to: Dimitri Fontaine (#31)
Re: Extensions, this time with a patch

Dimitri Fontaine <dimitri@2ndQuadrant.fr> writes:

Robert Haas <robertmhaas@gmail.com> writes:

I don't see why. I think the real action item here is to remove =>
from hstore.

As input, consider that lots of extensions will create types that are
only a shell at the moment of the CREATE TYPE, and for each of those
types you will see the (potentially > 1000 lines long) whole SQL script
dumped on the screen.

Only if the script is intentionally noisy. The right fix here is
probably to bump up the min message level while running the script.

regards, tom lane

#34Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Tom Lane (#33)
Re: Extensions, this time with a patch

Tom Lane <tgl@sss.pgh.pa.us> writes:

Only if the script is intentionally noisy. The right fix here is
probably to bump up the min message level while running the script.

You mean doing that from the SQL script itself (using SET) or in the
pg_execute_from_file() code? My guess is the former, but...

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#35Tom Lane
tgl@sss.pgh.pa.us
In reply to: Dimitri Fontaine (#34)
Re: Extensions, this time with a patch

Dimitri Fontaine <dimitri@2ndQuadrant.fr> writes:

Tom Lane <tgl@sss.pgh.pa.us> writes:

Only if the script is intentionally noisy. The right fix here is
probably to bump up the min message level while running the script.

You mean doing that from the SQL script itself (using SET) or in the
pg_execute_from_file() code? My guess is the former, but...

You could argue that either way I guess. The script knows what it
needs, but OTOH just about every extension there is will probably
be generating useless NOTICEs unless something is done, so maybe
the extension management code should take care of it for them.

regards, tom lane

#36Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Tom Lane (#35)
1 attachment(s)
Re: Extensions, this time with a patch

Tom Lane <tgl@sss.pgh.pa.us> writes:

You could argue that either way I guess. The script knows what it
needs, but OTOH just about every extension there is will probably
be generating useless NOTICEs unless something is done, so maybe
the extension management code should take care of it for them.

Either way is the key here too, so please find attached a revised (v5)
patch which will force log_min_messages and client_min_messages to
WARNING while the script is run.

v5 also contains the \dx bug fix about repalloc.

Please note that I didn't touch any contrib yet, so that hstore will
still dump its full script here:

dim=# create extension isn;
NOTICE: Installing extension 'isn' from '/Users/dim/pgsql/exts/share/contrib/isn.sql', with user data
CREATE EXTENSION
dim=# create extension hstore;
NOTICE: Installing extension 'hstore' from '/Users/dim/pgsql/exts/share/contrib/hstore.sql', with user data
WARNING: => is deprecated as an operator name
DETAIL: This name may be disallowed altogether in future versions of PostgreSQL.
CONTEXT: SQL statement "/* contrib/hstore/hstore.sql.in */

The script follows here. Maybe 9.1 is when to deprecate => as an
operator name in the hstore official extension? :)

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

Of course the git repo is still maintained for those prefering it:
http://git.postgresql.org/gitweb?p=postgresql-extension.git;a=shortlog;h=refs/heads/extension

Attachments:

extension.v5.patch.gzapplication/octet-streamDownload
#37Alvaro Herrera
alvherre@commandprompt.com
In reply to: Dimitri Fontaine (#25)
Re: Extensions, this time with a patch

Excerpts from Dimitri Fontaine's message of dom oct 17 16:30:47 -0300 2010:

The bulk of it is now short enough to be inlined in the mail, and if you
have more comments I guess they'll be directed at this portion of the
patch, so let's make it easy:

/*
* We abuse some internal knowledge from spi.h here. As we don't know
* which queries are going to get executed, we don't know what to expect
* as an OK return code from SPI_execute(). We assume that
* SPI_OK_CONNECT, SPI_OK_FINISH and SPI_OK_FETCH are quite improbable,
* though, and the errors are negatives. So a valid return code is
* considered to be SPI_OK_UTILITY or anything from there.
*/
SPI_connect();
if (SPI_execute(query_string, false, 0) < SPI_OK_UTILITY)
ereport(ERROR,
(errcode(ERRCODE_DATA_EXCEPTION),
errmsg("File '%s' contains invalid query", filename)));
SPI_finish();

SPI_OK_FETCH is indeed improbable -- it's not used anywhere in the SPI
routines, and hasn't been for ages. SPI_OK_CONNECT and SPI_OK_FINNISH
are only issued by SPI_connect and SPI_finish, so presumably they can't
be returned by a script.

The error message wording needs some work; maybe
errmsg("file \"%s\" could not be executed", filename)

SPI_execute sets the query as errcontext, which is good; but apparently
there is no error position, which is bad. I'm not sure how feasible is
to fix this. (Not this patch's responsibility anyway.)

I happened to notice that there are several pieces of code that are
calling SPI_connect and SPI_finish without checking the return value,
notably the FTS code and xml.c.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#38Alvaro Herrera
alvherre@commandprompt.com
In reply to: Dimitri Fontaine (#11)
Re: Extensions, this time with a patch

Excerpts from Dimitri Fontaine's message of vie oct 15 16:15:23 -0300 2010:

The cfparser patch didn't change, the current version is still v1.

Hmm, this needs some cleanup; the comments still talk about the old
function name; and about just the recovery.conf file.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#39Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Alvaro Herrera (#37)
1 attachment(s)
Re: Extensions, this time with a patch

Alvaro Herrera <alvherre@commandprompt.com> writes:

The error message wording needs some work; maybe
errmsg("file \"%s\" could not be executed", filename)

[...]

I happened to notice that there are several pieces of code that are
calling SPI_connect and SPI_finish without checking the return value,
notably the FTS code and xml.c.

Please find attached pg_execute_from_file.v4.patch with fixes. I've used
the usual error messages for SPI_connect() and finish this time.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

Attachments:

pg_execute_from_file.v4.patchtext/x-patchDownload
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
***************
*** 13897,13902 **** postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup());
--- 13897,13909 ----
         <entry><type>record</type></entry>
         <entry>Return information about a file</entry>
        </row>
+       <row>
+        <entry>
+         <literal><function>pg_execute_from_file(<parameter>filename</> <type>text</>)</function></literal>
+        </entry>
+        <entry><type>void</type></entry>
+        <entry>Executes the <acronym>SQL</> commands contained in a file</entry>
+       </row>
       </tbody>
      </tgroup>
     </table>
***************
*** 13935,13940 **** SELECT (pg_stat_file('filename')).modification;
--- 13942,13956 ----
  </programlisting>
     </para>
  
+    <indexterm>
+     <primary>pg_execute_from_file</primary>
+    </indexterm>
+    <para>
+     <function>pg_execute_from_file</> makes the server
+     execute <acronym>SQL</> commands to be found in a file. This function is
+     reserved to superusers.
+    </para>
+ 
     <para>
      The functions shown in <xref linkend="functions-advisory-locks"> manage
      advisory locks.  For details about proper use of these functions, see
***************
*** 13957,13962 **** SELECT (pg_stat_file('filename')).modification;
--- 13973,13979 ----
         <entry><type>void</type></entry>
         <entry>Obtain exclusive advisory lock</entry>
        </row>
+ 
        <row>
         <entry>
          <literal><function>pg_advisory_lock(<parameter>key1</> <type>int</>, <parameter>key2</> <type>int</>)</function></literal>
*** a/src/backend/utils/adt/genfile.c
--- b/src/backend/utils/adt/genfile.c
***************
*** 7,12 ****
--- 7,13 ----
   * Copyright (c) 2004-2010, PostgreSQL Global Development Group
   *
   * Author: Andreas Pflug <pgadmin@pse-consulting.de>
+  *         Dimitri Fontaine <dimitri@2ndQuadrant.fr>
   *
   * IDENTIFICATION
   *	  src/backend/utils/adt/genfile.c
***************
*** 21,26 ****
--- 22,28 ----
  #include <dirent.h>
  
  #include "catalog/pg_type.h"
+ #include "executor/spi.h"
  #include "funcapi.h"
  #include "mb/pg_wchar.h"
  #include "miscadmin.h"
***************
*** 264,266 **** pg_ls_dir(PG_FUNCTION_ARGS)
--- 266,340 ----
  
  	SRF_RETURN_DONE(funcctx);
  }
+ 
+ /*
+  * Read a file then execute the SQL commands it contains.
+  */
+ Datum
+ pg_execute_from_file(PG_FUNCTION_ARGS)
+ {
+ 	text	   *filename_t = PG_GETARG_TEXT_P(0);
+ 	char	   *filename;
+ 	FILE       *file;
+ 	int64       fsize = -1, nbytes;
+ 	struct stat fst;
+ 	char       *query_string = NULL;
+ 
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 				 (errmsg("must be superuser to get file information"))));
+ 
+ 	/*
+ 	 * Only superuser can call pg_execute_from_file, and CREATE EXTENSION
+ 	 * uses that too. Don't double check the PATH. Also note that
+ 	 * extension's install files are not in $PGDATA but `pg_config
+ 	 * --sharedir`.
+ 	 */
+ 	filename = text_to_cstring(filename_t);
+ 
+ 	if (stat(filename, &fst) < 0)
+ 		ereport(ERROR,
+ 				(errcode_for_file_access(),
+ 				 errmsg("could not stat file \"%s\": %m", filename)));
+ 
+ 	fsize = Int64GetDatum((int64) fst.st_size);
+ 
+ 	if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
+ 		ereport(ERROR,
+ 				(errcode_for_file_access(),
+ 				 errmsg("could not open file \"%s\" for reading: %m",
+ 						filename)));
+ 
+ 	if (ferror(file))
+ 		ereport(ERROR,
+ 				(errcode_for_file_access(),
+ 				 errmsg("could not read file \"%s\": %m", filename)));
+ 
+ 	query_string = (char *)palloc((fsize+1)*sizeof(char));
+ 	memset(query_string, 0, fsize+1);
+ 	nbytes = fread(query_string, 1, (size_t) fsize, file);
+ 	pg_verifymbstr(query_string, nbytes, false);
+ 	FreeFile(file);
+ 
+ 	/*
+ 	 * We abuse some internal knowledge from spi.h here. As we don't know
+ 	 * which queries are going to get executed, we don't know what to expect
+ 	 * as an OK return code from SPI_execute().  We assume that
+ 	 * SPI_OK_CONNECT, SPI_OK_FINISH and SPI_OK_FETCH are quite improbable,
+ 	 * though, and the errors are negatives.  So a valid return code is
+ 	 * considered to be SPI_OK_UTILITY or anything from there.
+ 	 */
+ 	if (SPI_connect() != SPI_OK_CONNECT)
+ 		elog(ERROR, "SPI_connect failed");
+ 
+ 	if (SPI_execute(query_string, false, 0) < SPI_OK_UTILITY)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_DATA_EXCEPTION),
+ 				 errmsg("File '%s' could not be executed", filename)));
+ 
+ 	if (SPI_finish() != SPI_OK_FINISH)
+ 		elog(ERROR, "SPI_finish failed");
+ 
+ 	PG_RETURN_VOID();
+ }
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 3386,3399 **** DESCR("reload configuration files");
  DATA(insert OID = 2622 ( pg_rotate_logfile		PGNSP PGUID 12 1 0 0 f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ pg_rotate_logfile _null_ _null_ _null_ ));
  DESCR("rotate log file");
  
! DATA(insert OID = 2623 ( pg_stat_file		PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2249 "25" "{25,20,1184,1184,1184,1184,16}" "{i,o,o,o,o,o,o}" "{filename,size,access,modification,change,creation,isdir}" _null_ pg_stat_file _null_ _null_ _null_ ));
  DESCR("return file information");
! DATA(insert OID = 2624 ( pg_read_file		PGNSP PGUID 12 1 0 0 f f f t f v 3 0 25 "25 20 20" _null_ _null_ _null_ _null_ pg_read_file _null_ _null_ _null_ ));
  DESCR("read text from a file");
! DATA(insert OID = 2625 ( pg_ls_dir			PGNSP PGUID 12 1 1000 0 f f f t t v 1 0 25 "25" _null_ _null_ _null_ _null_ pg_ls_dir _null_ _null_ _null_ ));
  DESCR("list all files in a directory");
! DATA(insert OID = 2626 ( pg_sleep			PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2278 "701" _null_ _null_ _null_ _null_ pg_sleep _null_ _null_ _null_ ));
  DESCR("sleep for the specified time in seconds");
  
  DATA(insert OID = 2971 (  text				PGNSP PGUID 12 1 0 0 f f f t f i 1 0 25 "16" _null_ _null_ _null_ _null_ booltext _null_ _null_ _null_ ));
  DESCR("convert boolean to text");
--- 3386,3401 ----
  DATA(insert OID = 2622 ( pg_rotate_logfile		PGNSP PGUID 12 1 0 0 f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ pg_rotate_logfile _null_ _null_ _null_ ));
  DESCR("rotate log file");
  
! DATA(insert OID = 2623 ( pg_stat_file			PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2249 "25" "{25,20,1184,1184,1184,1184,16}" "{i,o,o,o,o,o,o}" "{filename,size,access,modification,change,creation,isdir}" _null_ pg_stat_file _null_ _null_ _null_ ));
  DESCR("return file information");
! DATA(insert OID = 2624 ( pg_read_file			PGNSP PGUID 12 1 0 0 f f f t f v 3 0 25 "25 20 20" _null_ _null_ _null_ _null_ pg_read_file _null_ _null_ _null_ ));
  DESCR("read text from a file");
! DATA(insert OID = 2625 ( pg_ls_dir				PGNSP PGUID 12 1 1000 0 f f f t t v 1 0 25 "25" _null_ _null_ _null_ _null_ pg_ls_dir _null_ _null_ _null_ ));
  DESCR("list all files in a directory");
! DATA(insert OID = 2626 ( pg_sleep				PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2278 "701" _null_ _null_ _null_ _null_ pg_sleep _null_ _null_ _null_ ));
  DESCR("sleep for the specified time in seconds");
+ DATA(insert OID = 3627 ( pg_execute_from_file	PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2278 "25" _null_ _null_ _null_ _null_ pg_execute_from_file _null_ _null_ _null_ ));
+ DESCR("execute queries read from a file");
  
  DATA(insert OID = 2971 (  text				PGNSP PGUID 12 1 0 0 f f f t f i 1 0 25 "16" _null_ _null_ _null_ _null_ booltext _null_ _null_ _null_ ));
  DESCR("convert boolean to text");
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
***************
*** 442,447 **** extern Datum pg_relation_filepath(PG_FUNCTION_ARGS);
--- 442,448 ----
  extern Datum pg_stat_file(PG_FUNCTION_ARGS);
  extern Datum pg_read_file(PG_FUNCTION_ARGS);
  extern Datum pg_ls_dir(PG_FUNCTION_ARGS);
+ extern Datum pg_execute_from_file(PG_FUNCTION_ARGS);
  
  /* misc.c */
  extern Datum current_database(PG_FUNCTION_ARGS);
#40Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Alvaro Herrera (#38)
1 attachment(s)
Re: Extensions, this time with a patch

Alvaro Herrera <alvherre@commandprompt.com> writes:

Hmm, this needs some cleanup; the comments still talk about the old
function name; and about just the recovery.conf file.

Ah yes, thinking it's an easy patch is not helping. Please find attached
a revised version of it.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

Attachments:

cfparser.v2.patchtext/x-patchDownload
*** a/src/backend/access/transam/xlog.c
--- b/src/backend/access/transam/xlog.c
***************
*** 55,60 ****
--- 55,61 ----
  #include "utils/guc.h"
  #include "utils/ps_status.h"
  #include "utils/relmapper.h"
+ #include "utils/cfparser.h"
  #include "pg_trace.h"
  
  
***************
*** 5018,5117 **** str_time(pg_time_t tnow)
  }
  
  /*
-  * Parse one line from recovery.conf. 'cmdline' is the raw line from the
-  * file. If the line is parsed successfully, returns true, false indicates
-  * syntax error. On success, *key_p and *value_p are set to the parameter
-  * name and value on the line, respectively. If the line is an empty line,
-  * consisting entirely of whitespace and comments, function returns true
-  * and *keyp_p and *value_p are set to NULL.
-  *
-  * The pointers returned in *key_p and *value_p point to an internal buffer
-  * that is valid only until the next call of parseRecoveryCommandFile().
-  */
- static bool
- parseRecoveryCommandFileLine(char *cmdline, char **key_p, char **value_p)
- {
- 	char	   *ptr;
- 	char	   *bufp;
- 	char	   *key;
- 	char	   *value;
- 	static char *buf = NULL;
- 
- 	*key_p = *value_p = NULL;
- 
- 	/*
- 	 * Allocate the buffer on first use. It's used to hold both the parameter
- 	 * name and value.
- 	 */
- 	if (buf == NULL)
- 		buf = malloc(MAXPGPATH + 1);
- 	bufp = buf;
- 
- 	/* Skip any whitespace at the beginning of line */
- 	for (ptr = cmdline; *ptr; ptr++)
- 	{
- 		if (!isspace((unsigned char) *ptr))
- 			break;
- 	}
- 	/* Ignore empty lines */
- 	if (*ptr == '\0' || *ptr == '#')
- 		return true;
- 
- 	/* Read the parameter name */
- 	key = bufp;
- 	while (*ptr && !isspace((unsigned char) *ptr) &&
- 		   *ptr != '=' && *ptr != '\'')
- 		*(bufp++) = *(ptr++);
- 	*(bufp++) = '\0';
- 
- 	/* Skip to the beginning quote of the parameter value */
- 	ptr = strchr(ptr, '\'');
- 	if (!ptr)
- 		return false;
- 	ptr++;
- 
- 	/* Read the parameter value to *bufp. Collapse any '' escapes as we go. */
- 	value = bufp;
- 	for (;;)
- 	{
- 		if (*ptr == '\'')
- 		{
- 			ptr++;
- 			if (*ptr == '\'')
- 				*(bufp++) = '\'';
- 			else
- 			{
- 				/* end of parameter */
- 				*bufp = '\0';
- 				break;
- 			}
- 		}
- 		else if (*ptr == '\0')
- 			return false;		/* unterminated quoted string */
- 		else
- 			*(bufp++) = *ptr;
- 
- 		ptr++;
- 	}
- 	*(bufp++) = '\0';
- 
- 	/* Check that there's no garbage after the value */
- 	while (*ptr)
- 	{
- 		if (*ptr == '#')
- 			break;
- 		if (!isspace((unsigned char) *ptr))
- 			return false;
- 		ptr++;
- 	}
- 
- 	/* Success! */
- 	*key_p = key;
- 	*value_p = value;
- 	return true;
- }
- 
- /*
   * See if there is a recovery command file (recovery.conf), and if so
   * read in parameters for archive recovery and XLOG streaming.
   *
--- 5019,5024 ----
***************
*** 5147,5153 **** readRecoveryCommandFile(void)
  		char	   *tok1;
  		char	   *tok2;
  
! 		if (!parseRecoveryCommandFileLine(cmdline, &tok1, &tok2))
  		{
  			syntaxError = true;
  			break;
--- 5054,5060 ----
  		char	   *tok1;
  		char	   *tok2;
  
! 		if (!cfParseOneLine(cmdline, &tok1, &tok2))
  		{
  			syntaxError = true;
  			break;
*** a/src/backend/utils/misc/Makefile
--- b/src/backend/utils/misc/Makefile
***************
*** 15,21 **** include $(top_builddir)/src/Makefile.global
  override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
  
  OBJS = guc.o help_config.o pg_rusage.o ps_status.o superuser.o tzparser.o \
!        rbtree.o
  
  # This location might depend on the installation directories. Therefore
  # we can't subsitute it into pg_config.h.
--- 15,21 ----
  override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
  
  OBJS = guc.o help_config.o pg_rusage.o ps_status.o superuser.o tzparser.o \
!        rbtree.o cfparser.o
  
  # This location might depend on the installation directories. Therefore
  # we can't subsitute it into pg_config.h.
*** /dev/null
--- b/src/backend/utils/misc/cfparser.c
***************
*** 0 ****
--- 1,114 ----
+ /*-------------------------------------------------------------------------
+  *
+  * cfparser.c
+  *	  Function for parsing RecoveryCommandFile and Extension Control files
+  *
+  * This very simple file format (line oriented, variable = 'value') is used
+  * as the recovery.conf and the extension control file format.
+  *
+  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * IDENTIFICATION
+  *	  src/backend/utils/misc/cfparser.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ 
+ #include "postgres.h"
+ 
+ /*
+  * Parse one line from recovery.conf or an extension control file.
+  *
+  * 'cmdline' is the raw line from the file. If the line is parsed
+  * successfully, returns true, false indicates syntax error. On success,
+  * *key_p and *value_p are set to the parameter name and value on the line,
+  * respectively. If the line is an empty line, consisting entirely of
+  * whitespace and comments, function returns true and *keyp_p and *value_p
+  * are set to NULL.
+  *
+  * The pointers returned in *key_p and *value_p point to an internal buffer
+  * that is valid only until the next call of cfParseOneLine().
+  */
+ bool
+ cfParseOneLine(char *cmdline, char **key_p, char **value_p)
+ {
+ 	char	   *ptr;
+ 	char	   *bufp;
+ 	char	   *key;
+ 	char	   *value;
+ 	static char *buf = NULL;
+ 
+ 	*key_p = *value_p = NULL;
+ 
+ 	/*
+ 	 * Allocate the buffer on first use. It's used to hold both the parameter
+ 	 * name and value.
+ 	 */
+ 	if (buf == NULL)
+ 		buf = malloc(MAXPGPATH + 1);
+ 	bufp = buf;
+ 
+ 	/* Skip any whitespace at the beginning of line */
+ 	for (ptr = cmdline; *ptr; ptr++)
+ 	{
+ 		if (!isspace((unsigned char) *ptr))
+ 			break;
+ 	}
+ 	/* Ignore empty lines */
+ 	if (*ptr == '\0' || *ptr == '#')
+ 		return true;
+ 
+ 	/* Read the parameter name */
+ 	key = bufp;
+ 	while (*ptr && !isspace((unsigned char) *ptr) &&
+ 		   *ptr != '=' && *ptr != '\'')
+ 		*(bufp++) = *(ptr++);
+ 	*(bufp++) = '\0';
+ 
+ 	/* Skip to the beginning quote of the parameter value */
+ 	ptr = strchr(ptr, '\'');
+ 	if (!ptr)
+ 		return false;
+ 	ptr++;
+ 
+ 	/* Read the parameter value to *bufp. Collapse any '' escapes as we go. */
+ 	value = bufp;
+ 	for (;;)
+ 	{
+ 		if (*ptr == '\'')
+ 		{
+ 			ptr++;
+ 			if (*ptr == '\'')
+ 				*(bufp++) = '\'';
+ 			else
+ 			{
+ 				/* end of parameter */
+ 				*bufp = '\0';
+ 				break;
+ 			}
+ 		}
+ 		else if (*ptr == '\0')
+ 			return false;		/* unterminated quoted string */
+ 		else
+ 			*(bufp++) = *ptr;
+ 
+ 		ptr++;
+ 	}
+ 	*(bufp++) = '\0';
+ 
+ 	/* Check that there's no garbage after the value */
+ 	while (*ptr)
+ 	{
+ 		if (*ptr == '#')
+ 			break;
+ 		if (!isspace((unsigned char) *ptr))
+ 			return false;
+ 		ptr++;
+ 	}
+ 
+ 	/* Success! */
+ 	*key_p = key;
+ 	*value_p = value;
+ 	return true;
+ }
*** /dev/null
--- b/src/include/utils/cfparser.h
***************
*** 0 ****
--- 1,18 ----
+ /*-------------------------------------------------------------------------
+  *
+  * cfparser.h
+  *	  Function for parsing RecoveryCommandFile lines
+  *
+  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/include/utils/cfparser.h
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef CFPARSER_H
+ #define CFPARSER_H
+ 
+ bool cfParseOneLine(char *cmdline, char **key_p, char **value_p);
+ 
+ #endif   /* CFPARSER_H */
#41Robert Haas
robertmhaas@gmail.com
In reply to: Dimitri Fontaine (#36)
Re: Extensions, this time with a patch

On Tue, Oct 19, 2010 at 12:09 PM, Dimitri Fontaine
<dimitri@2ndquadrant.fr> wrote:

Tom Lane <tgl@sss.pgh.pa.us> writes:

You could argue that either way I guess.  The script knows what it
needs, but OTOH just about every extension there is will probably
be generating useless NOTICEs unless something is done, so maybe
the extension management code should take care of it for them.

Either way is the key here too, so please find attached a revised (v5)
patch which will force log_min_messages and client_min_messages to
WARNING while the script is run.

It seems good to do this in the normal case, but (1) if
client_min_messages was already set higher than WARNING, we probably
should not lower it and (2) we might want to have a way to lower it
for troubleshooting purposes.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#42Tom Lane
tgl@sss.pgh.pa.us
In reply to: Robert Haas (#41)
Re: Extensions, this time with a patch

Robert Haas <robertmhaas@gmail.com> writes:

On Tue, Oct 19, 2010 at 12:09 PM, Dimitri Fontaine
<dimitri@2ndquadrant.fr> wrote:

Either way is the key here too, so please find attached a revised (v5)
patch which will force log_min_messages and client_min_messages to
WARNING while the script is run.

It seems good to do this in the normal case, but (1) if
client_min_messages was already set higher than WARNING, we probably
should not lower it and (2) we might want to have a way to lower it
for troubleshooting purposes.

I think the standard way of troubleshooting would be to run the
extension's script by hand, no? So while I agree with (1),
I'm not sure we need to sweat about (2).

regards, tom lane

#43Alvaro Herrera
alvherre@commandprompt.com
In reply to: Dimitri Fontaine (#36)
Re: Extensions, this time with a patch

Excerpts from Dimitri Fontaine's message of mar oct 19 13:09:47 -0300 2010:

Tom Lane <tgl@sss.pgh.pa.us> writes:

You could argue that either way I guess. The script knows what it
needs, but OTOH just about every extension there is will probably
be generating useless NOTICEs unless something is done, so maybe
the extension management code should take care of it for them.

Either way is the key here too, so please find attached a revised (v5)
patch which will force log_min_messages and client_min_messages to
WARNING while the script is run.

I think you should pstrdup the return value from GetConfigOption.
(As a matter of style, I'd name the local vars differently, so that they
don't show up when you search the code for the global vars; perhaps
"old_cmsgs" and "old_lmsgs" would do, for example.)

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#44Itagaki Takahiro
itagaki.takahiro@gmail.com
In reply to: Dimitri Fontaine (#29)
Re: Extensions, this time with a patch

On Tue, Oct 19, 2010 at 9:33 PM, Dimitri Fontaine
<dimitri@2ndquadrant.fr> wrote:

Fixed in v4, attached.

It works as expected in basic functionality, so I pick up edge case issues.
Some of them might be a problem for the patch; They might come from our
non-standardized module design.

==== Source code ====
* It still has a warning.
backend/utils/init/postinit.c needs #include "commands/extension.h".

* It has duplicated oids.
$ ./duplicate_oids
3627
3695
3696
3697
3698
3699

==== Tests for each contrib module ====
* Some modules dumps noisy NOTICE or WARNING messages.
We need to fix btree_gist, chkpass, cube, hstore, isn, ltree, pg_trgm,
and seg. CREATE EXTENSION commands for them dumps the contents of installer
script as CONTEXT. We can add SET client_min_messages in each script, but
there is an issue to use SET command in scripts, discussed in below.

* Modules that should be added to shared_preload_libraries.
auto_explain, dummy_seclabel, passwordcheck, and pg_stat_statements are
designed to be preloaded with shared_preload_libraries. If we support
modifying GUC variables vis SQL, we could add some hooks to update conf files.

* Modules that only have *.sql, but not *.sql.in.
There are no *.control files for intagg and intarray, maybe because they
don't have *.sql.in. But we should support them, too.

* Hyphen (-) in module name
'uuid-ossp' has a hyphen in the name. Do we need double-quotes to install
such extensions? (CREATE EXTENSION "uuid-ossp" works.)
Also, custom_variable_classes doesn't support hyphens; only [A-Za-z0-9_]
are accepted for now, but will we also support hyphens?

* xml2 module has a different extension name from the directory name.
The directory name is 'xml2', but the extension name is 'pgxml'.
I'm not sure whether we should change the names. Or, merging xml2 module
into core and deprecating xml2 might be the best solution.

* spi module has multiple *.so, but only one *.control.
'spi' module generates multiple libraries: 'autoinc', 'insert_username',
'moddatetime', 'refint', and 'timetravel'. But it has only autoinc.control.
We might need control files for each library.

==== CREATE EXTENSION command ====
* Environment could be modified by the installer script.
=# SHOW search_path; => "$user",public
=# CREATE EXTENSION dblink;
=# SHOW search_path; => public
because almost all of the modules have SET search_path in the scripts:
-- Adjust this setting to control where the objects get created.
SET search_path = public;

Is is an intended behavior? Personally, I want the installer to run in sandbox.
One idea is to rewrite module scripts to use BEGIN - SET LOCAL - COMMIT,
but we cannot execute CREATE EXTENSION in transaction if do so.

* Non-ASCII characters in script
User-defined module could have non-ascii characters in their install script.
They often have "SET client_encoding" at the beginning, and it works as
expected if executed by psql. However, it raises encoding errors if executed
by CREATE EXTENSION because they are executed as one multi-command.

Are there any solution to solve the issue? I think it's a bit difficult
because encodings in database, script, and client might be different.
If encodings in script and client are different, the server might
need to handle two different client encodings in the same time.

--
Itagaki Takahiro

#45Alvaro Herrera
alvherre@commandprompt.com
In reply to: Itagaki Takahiro (#44)
Re: Extensions, this time with a patch

Excerpts from Itagaki Takahiro's message of mié oct 20 00:19:44 -0300 2010:

* xml2 module has a different extension name from the directory name.
The directory name is 'xml2', but the extension name is 'pgxml'.
I'm not sure whether we should change the names. Or, merging xml2 module
into core and deprecating xml2 might be the best solution.

Lets rename the directory. We used to have a problem with that in CVS,
in Git it's no longer an issue.

Merging xml2 into core would be nice, but it's been attempted many
times and it's obvious that it's going to require a lot of work.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#46Itagaki Takahiro
itagaki.takahiro@gmail.com
In reply to: Alvaro Herrera (#45)
Re: Extensions, this time with a patch

On Wed, Oct 20, 2010 at 12:58 PM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

* xml2 module has a different extension name from the directory name.
The directory name is 'xml2', but the extension name is 'pgxml'.

Lets rename the directory.

Hmmm, but we call it 'xml2' in the doc. There is no 'pgxml' at all in it.
http://developer.postgresql.org/pgdocs/postgres/xml2.html

However, I don't think we can change the module name because pg_upgrade
will fail if the module (.so) name was changed. So, it might be the
point of compromise to keep two names until we deprecate it completely.

--
Itagaki Takahiro

#47Itagaki Takahiro
itagaki.takahiro@gmail.com
In reply to: Itagaki Takahiro (#44)
Re: Extensions, this time with a patch

Sorry. I missed v5 patch and other new ones.
Some of the issues might have been already fixed in the new version.

On Wed, Oct 20, 2010 at 12:19 PM, Itagaki Takahiro
<itagaki.takahiro@gmail.com> wrote:

On Tue, Oct 19, 2010 at 9:33 PM, Dimitri Fontaine

Fixed in v4, attached.

==== Source code ====
* It still has a warning.
* It has duplicated oids.

==== Tests for each contrib module ====
* Some modules dumps noisy NOTICE or WARNING messages.
* Modules that should be added to shared_preload_libraries.
* Modules that only have *.sql, but not *.sql.in.
* Hyphen (-) in module name
* xml2 module has a different extension name from the directory name.
* spi module has multiple *.so, but only one *.control.

==== CREATE EXTENSION command ====
* Environment could be modified by the installer script.
* Non-ASCII characters in script

--
Itagaki Takahiro

#48Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Tom Lane (#42)
Re: Extensions, this time with a patch

Tom Lane <tgl@sss.pgh.pa.us> writes:

Robert Haas <robertmhaas@gmail.com> writes:

It seems good to do this in the normal case, but (1) if
client_min_messages was already set higher than WARNING, we probably
should not lower it and (2) we might want to have a way to lower it
for troubleshooting purposes.

I think the standard way of troubleshooting would be to run the
extension's script by hand, no? So while I agree with (1),
I'm not sure we need to sweat about (2).

Will go and fix (1), but like Tom I think (2) is handled by the
extension's author running pg_execute_from_file() rather than CREATE
EXTENSION for debugging purposes: then the settings are not changed.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#49Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Itagaki Takahiro (#44)
1 attachment(s)
Re: Extensions, this time with a patch

Itagaki Takahiro <itagaki.takahiro@gmail.com> writes:

Fixed in v4, attached.

It works as expected in basic functionality, so I pick up edge case issues.
Some of them might be a problem for the patch; They might come from our
non-standardized module design.

I see you noticed v5 later in another mail, but still pick this one to
answer. Detailed answer follow, in summary I attach a v6 of the patch
with fixes for most of your comments (and the ones from Robert and
Álvaro too).

==== Source code ====
* It still has a warning.
backend/utils/init/postinit.c needs #include "commands/extension.h".

Fixed in v6, attached.

* It has duplicated oids.
$ ./duplicate_oids

I'm discovering this tool, thanks for pointing it out. Fixed in v6, attached.

* Some modules dumps noisy NOTICE or WARNING messages.
We need to fix btree_gist, chkpass, cube, hstore, isn, ltree, pg_trgm,
and seg. CREATE EXTENSION commands for them dumps the contents of installer
script as CONTEXT. We can add SET client_min_messages in each script, but
there is an issue to use SET command in scripts, discussed in below.

In v6 patch, should client_min_messages or log_min_messages be lower
than WARNING, they get set to WARNING for the script install context. We
still dump the extension's script at each WARNING, but you can set your
client_min_messages (and log_min_messages) to ERROR before hand.

That's following comments from Robert Haas and also includes rework
asked by Álvaro Herrera.

* Modules that should be added to shared_preload_libraries.
auto_explain, dummy_seclabel, passwordcheck, and pg_stat_statements are
designed to be preloaded with shared_preload_libraries. If we support
modifying GUC variables vis SQL, we could add some hooks to update
conf files.

For this to work as custom_variable_classes, we need to have the setting
effects apply at two times: when loading the extension, and when
connecting to a database.

Now do we want to be able to have s_p_l SUSET and to change it
dynamically at connection time depending on what extensions are
installed? That sounds a lot like local_preload_libraries now.

Even then, it does not seem like local_preload_libraries is loaded after
you have access to the catalogs, but maybe we could have a second run of
process_local_preload_libraries().

All in all, it seems like custom_variable_classes is a GUC for extension
authors to deal with and it was practical to drive the implementation
there, but I'm less convinced of what we can do for the libs, because of
the backend init timing.

What I think we should do is to not produce a .control file for
extensions that have no .sql script. I've made that happen in the
attached version of the patch, v6.

* Modules that only have *.sql, but not *.sql.in.
There are no *.control files for intagg and intarray, maybe because they
don't have *.sql.in. But we should support them, too.

Well in fact there is, but the .sql names are different from the contrib
directory names:

dim=# create extension int_aggregate;
NOTICE: Installing extension 'int_aggregate' from '/Users/dim/pgsql/exts/share/contrib/int_aggregate.sql', with user data
CREATE EXTENSION
dim=# select * from pg_extension_objects('int_aggregate');
class | classid | objid | objdesc
--------------+---------+-------+------------------------------------------
pg_extension | 3996 | 16855 | extension int_aggregate
pg_proc | 1255 | 16856 | function int_agg_state(internal,integer)
pg_proc | 1255 | 16857 | function int_agg_final_array(internal)
pg_proc | 1255 | 16858 | function int_array_aggregate(integer)
pg_proc | 1255 | 16859 | function int_array_enum(integer[])
(5 rows)

dim=# create extension _int;
NOTICE: Installing extension '_int' from '/Users/dim/pgsql/exts/share/contrib/_int.sql', with user data
CREATE EXTENSION
dim=# select count(*) from pg_extension_objects('_int');
count
-------
111
(1 row)

Note: this new function will be mostly useful to help extension authors
find their existing object ids at upgrade time, but is nice for users
too.

* Hyphen (-) in module name
'uuid-ossp' has a hyphen in the name. Do we need double-quotes to install
such extensions? (CREATE EXTENSION "uuid-ossp" works.)
Also, custom_variable_classes doesn't support hyphens; only [A-Za-z0-9_]
are accepted for now, but will we also support hyphens?

Well I think requiring to quote extension names containing hyphens is a
good idea... that's consistent with any other SQL object name, isn't it?

* xml2 module has a different extension name from the directory name.
The directory name is 'xml2', but the extension name is 'pgxml'.
I'm not sure whether we should change the names. Or, merging xml2 module
into core and deprecating xml2 might be the best solution.

Same with intagg and intarray. Do we want to fix all this?

There's \dx+ in the patch so that you can list available extensions, or
you can SELECT * FROM pg_extensions; too.

* spi module has multiple *.so, but only one *.control.
'spi' module generates multiple libraries: 'autoinc', 'insert_username',
'moddatetime', 'refint', and 'timetravel'. But it has only autoinc.control.
We might need control files for each library.

Yes I think the best answer here will be to edit the
contrib/spi/Makefile and to manually provide a list of the control files
wanted. I've done that in the attached patch:

+CONTROL = $(addsuffix .control, $(MODULES))
+EXTVERSION = $(MAJORVERSION)

Now we properly have one extension per MODULES in contrib/spi.

==== CREATE EXTENSION command ====
* Environment could be modified by the installer script.
=# SHOW search_path; => "$user",public
=# CREATE EXTENSION dblink;
=# SHOW search_path; => public
because almost all of the modules have SET search_path in the scripts:
-- Adjust this setting to control where the objects get created.
SET search_path = public;

Is is an intended behavior? Personally, I want the installer to run in sandbox.
One idea is to rewrite module scripts to use BEGIN - SET LOCAL - COMMIT,
but we cannot execute CREATE EXTENSION in transaction if do so.

Using SPI to execute the extension's script already means that it can
not contain explicit BEGIN and COMMIT commands. Now, is it possible to
force a Reset of all GUCs after script's execution?

* Non-ASCII characters in script
User-defined module could have non-ascii characters in their install script.
They often have "SET client_encoding" at the beginning, and it works as
expected if executed by psql. However, it raises encoding errors if executed
by CREATE EXTENSION because they are executed as one multi-command.

Are there any solution to solve the issue? I think it's a bit difficult
because encodings in database, script, and client might be different.
If encodings in script and client are different, the server might
need to handle two different client encodings in the same time.

No idea here, at least yet.

Thanks again for your detailed reviewing!

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

Attachments:

extension.v6.patch.gzapplication/octet-streamDownload
����Lextension.v6.patch�<kS�H���_�!��el�+a0�b�d�7	\`v����K���Y��%����{���Z/�I���
��}�����H������Q�&��#b�����#?�?loo�a����������1��y{����?0���������9Y����P�$d�o�L9�F���<��w+�)��i�<� �����������y"��/�-~ZpG-������n���g�8t��]}����XM$GAy���w������L>����������Y:���B�=��Q��-$j�p!�(d)�8��n4�r�5�R���<����k����<��fF�������%X'��N����H���i�q��	��h�����ur�=<�o�?G�4����Z����M�}I70������U��v<��_R"��}�z����i��)
��f���:r0L�s�������+
��uX/N����K��]	�c ���N�(w 9�[
c&���
��:)<���!�5T�D��[�Y�H�~x�C`"=�l�P���S��k���"K��X�%����EA6
E��0u�DYZ���}�h�?����X/�f���@����a�^G�.?�����n��'<t�hq����qJ���:*@�F��=���b��F>�>p)$:��Mx�R �/�����
�/�LXX�<C�W���[g�� �iO���r5���Y4�
`t����$z��R��Oz�I��_�\7��8��#�w"�D��	�7|���{�p��A��[���p�Lfi�z����?��m���������.V�0,�h
�������+���s<a+�m�	����enG�����j�?,���mp���,q��'��3���0�	5��'���s^G������n��������]��,��}-���\|>+x�r��$��8��s�.sB��E�%7[
}�js���t� p_������$�<^�Z'����A��������{t�:�}���*��X�N$�D��B��e�
��Hc9Lb"� �hN��Q�7K�s�hn������X>Z��.��w��6X���P6Tv����������b/�r����^�H��m�qG�`~q�!A�`
;x1�(|Q�e��m����@���!I03d6���ZrR3��/?�1�
�|��f�������(�N�g���P+�"U�|�	�(��f�8�3ra�T~�n��2�����(�u���Y�CVA��`�.�(Dw���L�K�����ako�*k���;.�D�j�a�X���:KS��>��A�����b.�4-���,�M+�aGSq�6�(�)���ccyB��0
��S&�m{��D�|���������?�'B��H�F'���BQ�j��`0RIg`�r
��(�v��T�!���E��4��qr`���l��@GA!SVF;�H��i[s��8%��F�����������gmi������
�l]T�;��`[r��hb�]��#��C�<{$y��!��q��6���|zsq~y�����)�	������.�;x�O,�X`H�%��:�<Y��S,c��`
���}Q��$��7))�������\Py�H���3O,���4NB���~A4#��`�  ��{>g'9��'���%n1�����O'���.fq%A��9#��&���|���M�k.\�Y{��UA��S��:H��r��
n����d@�7�|4#B�)_K&!���)�\��yK�-�ib��..C/R��]/i������$C%KO �R�M��F��#��G��1@�����"TA��"��
[�qjHZ,u�Ad|4�l�%e=e�j��~B�--�
��&Li���J� ���upK��VE-6�d�+�R^������Y�]�.7��V5."GU�����_���j?X�IV��'��*����c�d���!��K���S9�4��,t�Z��A����;.�g<��i�
A"n�!�PO^���t��#'��cq@)k�kv�<�Pq���y�p"�!��
��,�6F[�@�ct�T'�C���bQu���7,�u�'r�1�������wGo[��#5�&�@�xx���^|�8�c�����'��DcT��A4��Z������$f}c����j���'�n�x���<���Y���C�Z-v�YZ���LY�H+�T6��|��n�����������}fg���^���Y�������������O�����
��dVb�V��\L�������L����h�����Zy�z8��������F{y��2rJ����n���s`s1q���t�8��_����I%��#�H�n-�
d����C����Rjl���MEYhI�-�&c}mB`["t2l���J�
IB%�26#�YH�y1�����)���������,�G'c���{��t��hP�(7QE:H�n-��fJ�k~Ny�*������vQI�\����\2Uv)x�p�bU���*��mZ���V
\�m��=��ai�1���)��5� �(����� ��X�1��f��~���g'fY�4D�����gJ��*�Xc�	�����Iu4�,�����
:6��\S!���E���Z���;f�RY�q��S?����q��_7��}���D�?����@�����,�(3R��g��,#c��b��R�}�����q�5������x.��.$u�8����s�Y�l	�XY.a'�da���3������Fgt�0�����2�e�Q��~l�d��*�I&�0DDS� ��8Cv���P���w�CbC]*�DD���K���\���j("��l��vK�u�G���5�lF�I�+"S��|��&�i�o���?��^9����a�GZ��O�6�H��O?�-��VMX�U�SK�Qa�H�q�<�v����c��T"6i*�L���
��e5�����k��-�E��,-�e���e�mf_�
���5�7q����Qfz������ @��f~���!��N���Td	"�4�:#��v=��#�9 =n�t����Xsl�V"���������}��O|�J�#�6�
�i�o�\O����mv~�������p��^�g�v���gZ\��������)��������= �4k�rd)��7�tc�{�/������]|>Z�/����Rf������/�a��<���9�B$��T�V�v[��(���?a����MK���G��/����Kh����6��i�/��Y�?k��lgC��%d�v��.�i��l�����j��r\+PE�(��~���Z���b���u\����a����I���=�"J��A���UL�Cg�4������t0�m��i
�$7�X��v������[*��}��,WW�*�������x����\~��h��H����2v�F����>��@|U�[����TYA:�)�����ZT�	��:8�B<�0���z2!�,�z�aQY��#�}������-[H��)��&��\Y��(�-P������|4p�3���!J��u�$�N�m����)���L�������lS��g8M��}����d�nF]'o38mk����8����@���Y�%�ZG;/�%�[y��A�KT��I|�XGpDACJ�hM���<�I?�iA;JiX�4I� P�^��X������e�`�?t�,h47��V��N�n��f���,	y�]�3"H�pN���}��mF�t����m3��Yf88��>���dm�z&\|.���}��0����:��8��O�0�c����3|a����Ap�0�Bf����su�!�L}a�H�3.��8���s�Ng�DB>c��Y3� b-L���X����[�X�1kM;�-�a_��{�/*����Y�����e��w���?(sb��p�^Q����AU������^����K4 �S|QW��=��N�l�r�~!�����������E&"�Z�Z�oQqZ(�D;?!�(K��
����>"A��hC���&T���74��z�(;
��R�	#.�Q�\��	@�tGE�D�����E�,�k�o����M*�^2����B����3T�������J��+h�%Q:&���IP�����0S�k�Ki�7�Y����NV��ZC������c�����b /���H��\g=(��|C�Zf��i��<���F�NY��LUj"S�DU�1����l��
x1�=�����p�����~��
U�� G�b��R��.�����g��<eX���1�mB�=���t�8�ZMV��=�\M�����E�C^)/V�+gu�h��5t�F/y�Kb�ma�WM�]�����:�r�5+._No.�Y|Y�0Lt��C�3�?�oc���@�K~��T���{�����QH����;Y�C��5�S������0���C\+��&.�4��B�B�peFW�����t'`���@~� q�
�l���%3��To�|�{�X�S����������:8�?<��A�>�I�4�z�<&1���'6��PLk�(=3�)��|��=\^���BZR6\���~����y4�9;�=;=�`��������������e%8(��Yr1��E
��&=&����a�
:8@w�c���5�/����F{�8���tQeb�<��AO���YE%�P��TJ�9d4�n)9Uc�b�qQ{:I�I.I�4���q�%��&Dqx������D|2 u��59g%���6M�"&<}'}%�JU�n�!���,���,���+���& �
�/������}��!{2]7|D'���� �J�6C����m��X��Vs�������]��liv������b$mo��g�G������oD^��>7���.��I-:Z�G��o�����A/^�[��?�O���aN�.���f}�$������s�y��i�q��ww�v��}��~���c�T<�:�����O�45��${X3%���}/�$�2�"�����{��4qB�L;�M�U�}|\���A��Jm��|����A�q���~ :��mOVk���2Q�8��T�~����cw{R����x<@E�~���v��Z��[y�)�d���a#����~��O	��2���qQj���V�@b�,�5�Q���S�QC&~���Y�1�%P�E�K-�s��&2��R�y�>k�d�!��I@7�m\H`�U[�a����b��|>����I�B��z31����:���(\����I��CDE��
���yy�q*��mI`��O����1�M��P^F+���[y��M�E$5�r�m.�`�G~Hg'�|��-4P�ZGS��0�F#��p*�1�����(Y�
�4S/'�Gr�Q�q&��{�����u�_�~�e�iWM3>/���g������$�\)*6����+x{��f����oIq�@*� `tKa)�����������I���s�8+��� �\���]b*���O�fxt��]���$
 }�d3YACW**��w��+���
����D{��Zw������O�~f[��A4"C`���f��B�A����~��~�������t�
����%����E��l�W�� ���,T-g��
��!1_���'$�Tx]�C�
�N����	[�mg���3���$@�hWY����L
��,���Ef��Y�\���b2`����\N�p���mM������O�N��e���)+KI��\,�gQj>��$
�$J�	,�;Ip��D�Xs�bo����������{���Hn�6~,pb�I����s���q6��
+9�b6���qIur1k��g9j+MWJ�\[S�9�')XHe=#�0�&BQ�F�c���+R]���<VI=:������R��$���g��sT*�=���#t6���9 J8����N2��%}���k+���$�7����w}E�J�h��N�����bm�-����P��N��qY�I�O7��y���?�Q���:�_� 8�}$��Fs��l7��4@q���>���3�c-F��:�G���]�B�Fr���������s�x����J�����{\����~x����+k�#��v7�2��Wd\vA���sfkQy�T���k�tG�^�/�V5�P�--�|����L�������n�>�\�����������1~������a�O6h%���Z��/UL���
��b��&/Sp�x�7~S�j;�h�~"�����LHPCs ��3p�@���;p�<�1[4&�&�yi�PD�Tm�A��6��{�q7��6��V+��V�k_����j��!��U��q�Cln,����:k���F ��{t�i��@Y��eR_�k
������T������<��>��K?nf�'������qd	��|�
;1���������A�` ��7�O+��Z��dL.��=��tw�$l'�g�� u��N�:��pZ����F� ,X��e{+�Ve]�����Ywh9����>�!q{8FLw�$�ya�����"���a�w�NI,���1mGv���8�JD Ju��P| H>���y�~�4��e��
�;Ou&T��u'��}��.�0����r��[VE}�����i
xL@�h���1�uMo��[r��/�6#v��/wr��W@��O��rw'!�)�0:L_�W4��{z�M��&<����
=X���A�A����q�9=o�N�+������4T�M"��a�h��	�x99=
Mp5jc���u�8��RS����J���T�����!���:�h�����1"�lCf�����,���|�D�`�}��$���Q����%�W����$}������2�S�j�G��2�$�2.L%��hvk����N$0#NR�n��������Y
w��,i�O���!����K�����WT�	A'Hm��@����XE	X��N����t��nZ(��p�����f-�n�h)#KR�,C����C���������q���	(!���R`������-+W�|�C�U�t����������I���-R����L��q	�;�gg��#���_�����q�4�����{�����z���EN4��J��-���VY�^/��,�!�O��������Jc		0�T=v=BC�P�������j��5O�'��4��9*nE>�`5��"�6�/�>�X+�kbY�zw��z���������frJ���3�k���b�B�&nO�0|4x����������m��0�]���j���%!�H�^�p
GC��F�_�l���W������v�������Uq���}(��]�+m��^Q��I-M�8�����C��)P�#p�T6�5�R�a�>
�;�SoYj3<�pT�C�
1YK���u/�l�I8�;5��+�
)�D���(�����
�V5�,����@�DPt�6��Y1���'Q�"�>U������������s�J�w._���5�;h*�~�y�y�]]t
��� 8'��{u�����S	d���P;AR�+�9��v�����i�	���*�������u�x��zS�����%�\Xh^�}���X[���������������7���k{v�P���,������C��Y��]<�wa6� �k��8��hmJ�$����]���z�v2��Xz.����Un��N�u��2 �+�����J�"��X3�8�N�t�DYpj
���
��������y�b�j����l��h(9m�_�V]uEv����&Fk4�K�X�Ni�i-Y-�uS��'Z9���xu�>k�7<E�������s(8?����pw�n���{
��M��Wz����(Y�]�vvl�L8�����>.e�{{1�����v���yw�?AI�����	)8����0����\��D����KwR���lH~DH��]�@]�G��;
��1���t�s��k�@S�R-T������-G���'�N�F+pM�g��\�,o������H�IDv�C7~�a���-x�H�6��$����!=ms����v���#�J�!G ^�e�y�y�	����K��^�<�k����O���=�#����j����K�~���o�U�a��>���>�D&3�u�DpH��a<�K��hZ);s����G,�)?�}D�$x������2MU��������VD�����0jt��Phy3!�!����)���MH��(�����j�*��{�sM����:�P�D�;�5$(d��p��x|���a�z�nu�)3^���0N��)(�,���lf��9���W�J��9�h���� �
Y�S��&=��/��NEhn�D�%�� ��;��KW����t�qt
h�*�������t�����eG$������5-�z����wI����
��!y�\M������ugP��B�gQ }�������cp��XE����4���`��z?��b������c�/�\��Z���f�[����t��>/
���}�(��1�|�9�`kD�t�X({u���$4K�O�cc��.}5,�"
dl�~�#���!����-���Y�Gh�7D�c�P�<�B�O���orYki�mKL���D��`		W��-`\��:o��ur��?}}v�n]4;�<k���"��\���F��M����������=�ks��~%���1�����v�,V��e�U~�����j������R��}2;�����&b��&S:��H�((���\7
��[�������R�j��5���s�	�e ���o?��������P$
u��C`�r������$��1�����]���^f��
��=I[�-]�#���mHis���(�(�����YHY�����Z$���g������e��!�C
�ov}t�F������nB3B_��\}�m���NN/�+�D&����������T*)�4
������c<0�>�	�R���ODoI���������9��N;>�qt1�I��J��W�G�v�G������d����(�$�1	�f��/���Ds�$�`!��L��^��NPw�y���\��������6�H��6�3!�������.U��������B���Cp� �Yl#� ��>CsV,.2�����S#���3��P��-
���HR�f�����"�(�8��B��+�E"���[}��3J{"Lw���OJ�����\ 9��������E#��j���&�q��JP������w�������o�����%�������Fhp���?g]�N]�w[����J�O�E���5�0&���x\Z5y��u���)�U��XV���$�+����L������u�����������]����O�_����^��C�K�`_z[��*1��]�x�Z��vwj�F�{��e��^�lf���67�'s�d�i�mt~���m	Y4T���`��l��T��
!+��y��6������^�����uN+���X����ut�:(��b��#3_n
g���f��GF����FHz]�0'��(�Ic�2{PuU1Y�A���'�5ZU��j2�������>���"��ar������_������1C�
+�p.&�k���`tB��"�]�T�q4���rX��� "���^�}"�S�
tzb���.��uG7:�]R��G�r��f�O
��V�%�����/�F��5�E���%<��w�Jz�x=�o:P_���x)0�c��$k"���UK�J���N�H�#|f�`._�j
���
��w��nNJ�WF�-p��E�(�B���l\6�C�-�Q����F��J���x��S�%�o�<g
��@�1�
��q,�dS�O�n�)�"�V�3?�'%�s�%�f�	���k;�cHZ�}jP�j�Mb��:3�z�Q�k�s��6&��h��Cj�b�<���d��d��e���+|�059�n�G�$�+�f�y42��Q�/F��y���
$���{��E���?������_� ���9++S4O(��<�k9�'P����N%!z��EN��5|2��(���$�����
� ���������X�=�����?���a���w���l
 	X��m�n\���Cu�R���#0f&C��B
�
GXe��5��k�1P;2����O�D��G6>4jV
Bapl�+J�G�qrO
ULB,#�� [�T!������QJ8�2�X,��Y�-2|�J�7eu��h_�_�_��#D�R�)'�C�,����2�4��������X7��A�^m��O�����)�,�}6O�8<w;��dlyg�uH,��g�>^�z;A;O_N;|?�F7�a�xi"$�f�}��������h�(�r�F�D����^]��'E_�F��d��\��m����b����;�#��A���&��-G�
_���9�O�\�)L	������p3���Q�x��Q2>1��V-1�v�pXR�8x-����B�H=�J(P�1����������8��P��\���w~�����v���z�U��=o��?�������V^��_tf�r^����e������1��2�4S�@��9mo����`gC�^	��Gil�@E�����`��d�\�CN��n��y:-�����p<�lS�������g���SY�R������'�_������A(p++���IG�nt���tv�9�<����i�������`K[�f�?����X������f�U�J�� ����5Le?��I;�S���������\x$���(e��X
�}�_�b��E�
��F��}G(v�l�h[���X��V
`�.���p�,D^8�2-c�'iA�Vm*��H�$�j.��:3��x�S"k�%o���\ �w��
�:i���O>S�_N��!d;���.�&s 9W��Z�TMC�������9�7�, ��f_3E�J�t@a�cHN�������-Q*C�/Tc��N��;�����L[�-��y�E#����*��8��^#Q{V��'{%�\V&1)
�G��a++���3���vN�y1re�{���Tj���}������mT���Uw(���c` ��!�Wt(�K���u�K����Q�"N�w:�J�8��c��/�<XZD^����h�IF�18��#������!�����64�(_�WR��L��	@���~�"z�v��D��>���s�����i��?�(Xo��Aq�dD�/b��Mqr��Z�~��O��U���s��^����h�$*M�Mj�[��P�o���T=N����b��z�\+��	�A����A�����ak��<���������GM� `�����������h(cV:�p�j��r��0���3=����O<�ESm������gc�v�����
�g��'
u�A��0���a��R�4!�g�~��~�,��VV��!�Pd�Rc�x����������?i�
�u�pW��)p�@fao�t��u���������������M�ie����h<�(p�d�To�[<�Iw8���"t��������	:\��%�C2n�����R��i��T��x���~Xg{L��(^&� �}�#�v?'�����������w�Z
�j�h���
�wD�.]Z��9����i2&�6���CQt��{����X�,��i�f�����D{R����P����$�[���"h;��
�`���
8�N�"e9-�p$K�}sXg��_���m�$tfk'�'�}Z��H�n��bf����r�@����l������=�����H���k��5Mm�?��1#YE3���������E
���������n=��W�����R�#��������5��8�Z�z9���RP�}�a�'��kG�m��s�h*m�HA��8�4�i���w:��I�'u^}3E��_�y�z�v�h��~
L�e%�(����%�>,i7�O�����%4��N'o��(2�vg�K�������d������(�L�-��)��awe�93�����);����=��\)��|��(��;����X���
o�����q�,-���9�7��-��������z�W��O(:~N��6���b���]N)]�M�.-����
&��p���X�]�Z���a��d�����

��Bh_�`��M���s�
�Z��=P��x{�����4���X�;�����h<��}P�r�2���u���f�uT�vl�c-X	��:x�Q�
W���N�[�g_���]
�A����� �/�P�����g�����|
�Y�!�Kry>�oNg�!���@�b8��Eoph����������f{(��;��3�e��"��H S�[I��.pOn��
�����i��X.��9*;�����7�x*n�p���A�"�aBS�P����~
��x�M�8��$J�&����x�P2C��P,��r�4IjS<DlKB3���^#[)���������M��9H�N���M��`��r�	�l��0���/����[�s���jF������Y�)R������C� %�bV[�M�xl�����������%��'bfjj�^����/T������B����u7��!���a�����s�H*&V��H����|1���7R��#���@�7��/�#���0��Qc�e�X���9�r��(�E���<$��d�T�Vi���g�gq'K"����7j��d^���a8u���%#3"1�A�`�18�A���\}}�� $�9�Z����D@YF��$�J������9@-�`��=�y�u^�N������6-���#Z��s�����x�F���7�b��A�'A�R;`��Lr�c�P��ct0�"$��*�ViJC��r^Q�����$�Ol�"�������N�a|&Z�ju�f:6��.b.<�"��7�J���t�s���2�=�b�:����8+��)�R�M(k�����dy���	�~���)��lZoN����IF���i�I�&Z�hd����M���������q�:l5�;�&&��8=O��b���� p��Q����=C�����\+T�}(��������?�uA�s[���[g���buq_od�^������9��V	��d"PPs���Uk���J����U���$�{�G����@�����7LD��'�d�5����N[uJ��.�6��� G"VL�}Qk�D(�HL<���lq(WFm��iZ8 *W�%��^KJ��r�lz�6D�����64����f��=�x�� �����}Lm~��	�G�sye���#��HD'&'!�n?EulK
�Nh���m�M?Z4W�Z�:YSq4�t+����H�&�w�yC���f=�wk�t\���e�*���r�D�+.)������LCz:>=�����p��������"��l7/����;=9�h�7�d�j��uqJ_��1c����O*��Q�]��0E�2C]eU�1�$
������@��H�3��i���1y�c�:��4�PJ�{���
E����Y+���,(�U����W�@���JW7����F����k��,�%xqH�.�f�u@�	�Lgn�Y���`�.��q�T^�c�����&��&���_�5"-�R7��$��~�U���k4�����l���r��������
v;�Y�N��'>��$1&j�\-9���k�5L�7U�C�iB�.\ $)	�C,�q�B���$���\Z���9^\���=p�p"������x�/������z���3sH{���w0dS�X�C���%���9�t>7h�7���Q")�Iz[aE�!'�����=���R�����*�b��������Nvx5����B9Pb��2z���F�8=q���0�����S{���Q+��UL��;$�p��_�0���88�M���
?QK��Yqt���JeJ�.�0�&��<-7U�G��=����{���X��+�|�	���a@#��F���s[��/?�4���1	'#���4�����TR�|<�^���1�W�O����%�-$7��$9Z(w���I�Q�����=��A��F%��tG]�3@H��t��n�q��:Yr9�*�KL	|�,\�V)Z�<�{�o����	S� �.���8@�D��({e���JC�H��A�tm�O����f1$Kn��j�h�������xr�`�����1�qo�L�����<�m����Z��9�z���b
��9�iRX%YB^��hR��g5��eUK�q	���eQBHwO�6���������\K�&��B�$\���*p��|>�VQ���6K���f?�[6��q�GO���>������X����D	���c�j��44N2����A�j����Z����-^������?�s'�
e_��0�+1������1w��Zs��I��C��b2�8�[�
�^��p��"���>�/�!1*�OJ�g!���Nqi���OL�h��%��A����nj#Sf�������2o�6;rI>MOns1��F�6��d��m���g9N��K�6#��'r�	r>��>��k�0&FY�(Id���V�u(�}�9�<�`T������79a#M� �!�NQ�c������k�8�jR)�����S?W+����������$�S�2��Vaa_-SP�c�<-����Q�%�|���h�<"
.�z$�GCvS��0����iL?�|��5�f��U�����*_�57��1��O$��(b�@@��'��q��cdy��^�s$������J�8-|/��Hf\���V����->��m��Kv�:����xh��Z�������*����o��Yi+bK���dbGk���lS���LF�\<9��cO=	��"+�Vt���Qz�q�������(D�����E,s���������F��.J�4�0%��U��I�L��
�qUI"�Gu �!
7P��
K��?��,�� 
F�_�������$�|����>��U
��%�QOg��{>1�Q#�$y�~��xLVg��y�IW�3gGm.�Rvx���U2KM U��S�T_b)y��I��C�b��]����`x��
���'"$���:Tc���K
�CQ��r������8T���F���ll�����^W���f��;�@y�)���)K~`m�Pj	N�h���L��h������c��)�D���H�'�fN��7,�f�����
����_�6_�	��#��J�B���Vh��R�E���ucL���r]\m�D�c':&�7o����y�'�����Q��E�1��uQ��m	��&��SeY�p�S���U�>���x6]��?pF=Va�<k��K�}U��Z,(���1�}'!d�t5��%Z~Mb��-�.��I���`��%����	D%@��I4%�2m7
"�#��H��|����m	������o��b� !e(KHr���i�����z�o��W0I���9��4����a���1k����q~�:9���5��8����N%�25��C�������Vu�haq���Y��J�U����l[����=�.;��:-��������������L�2��sn�G�$�����qCC�Y��<�Xv��,Z��-��fg� �1��JE�;��A3A��f��H��b�����]\N��E/�}:'xq�(X��PR�<7w��Q
.�>�p�����D�9OZ�&@����%��S�)���NI)����k������,fa��T�T��|����0h�I�u�M�r�i��qv5`��"�v��W�$�V��o�-[�t��)�v�IN
�.O�����AV4G��C6�#i���.*�L$}#Ji����p�1�QV����?���B����k�Q�E��P*!�CA���.&���t�{e�pav	,6�c�}�G��{� }q���X�cq�P��Jz��l���+���$�0������G��V����P^x�������RqXB�������\����'G�!��I�I|GM��� kV�9�g�~EA#b`�A�9	�(?�$�q��{2/���z��F�l�j�.��G]��k�?����$��J���%��1�mw��8��%'��.�3��a��)�N��	�u���<42L������J����K������������n#u�,���>�"��`#�=id�E=�
��`��Fz"�.,�(f�['8�"GZ��P���-9���S��JA$%H)h���t���Uv5��g�7r�(���~�4���R�hI �����UID����M��)�Q��;t$4�q�N�C���n2%AM8�m�`��!L�g�1Ei���?.Y����RR>�^�����'���p�A����G�g���'��U�:������2�����5xxu��k���7��7�Hc����������3A��5����'�����0��$CT��F�h��� ��,��W����c3h�"���2���� �86��������$�$����hkkT�PyC�)JS}r�0C�^�/��\$��s0�R��n�����P�4�����X���g1d��I�����N�
�����#�#��-���)St��Mg��Q�C��������eM���<nd!sE�����e��C�y�����������@�W~8l"lQ�DqV����Fr�1%�a�<Pb f3�O��t�0�M��"���.�#��Z�VNg��3d��n$���.���6���S��x:N�0�������h��	���@4ztnP�mO���&��;oN&�L(9}�H�m�A_���P�R6��v�t���IxM/Mk������I�MHRzG�i5W�F�6�����5(>(V�c�B���5�����?�Z��#��80�H�9�jf1K]�{kS�� ������	���]@��Q0�r�C{vja����������>W��/�|~�P�����=���E�g����!VhI���#��cI�2�mh/v��9���[��b�mp�+g'+;����z��K�*�.��H�S���S�\8���<���af�34)�cF��3������F��\'�����[��PR!�'f-(���Fi����_���blr�h�
��#��uYN���U������"-�17y���H�u�D�� ��'���3���>:n"+�i�8tk���^�R�PQ�&|k��j?}	+P��,�����^���iw��a�*��LC�%l~��f��H�������'���u}�BUk{�w��i�[������A�* JB���)�Ta;�k����Y��9�8�S�,�9o����y����B���;�[������wEK����,��a�gi��y�4��$��m�w���d���q�#.���a��Xr������Nt�"�>����RAV8ed�H8E�c=�#��9�'t�r�v���tIO,���h6�#R66��6�457�q��$65l������tWTc�)c@���rb/��7�y1�O����n���Wnw��:c��i�T���f\��3&1�����t{!{������2FI1?��xh����$�N������o]=��i���
B
)�G�H��f�@�$g�$@h�<J�	��������y�b�q|\r�m^S G�h����|,��!���q8\���Rb*���e�0���[%9��;(�J����E\�f���?� �����xlq��aG���v�q��J��x43527��)I�*�h��ee"�������|Z�8f�6kN4�T��l~x��9��>a7N��O�,l����t���ev�0>v�x��L��Ko��3��	g���'�]!��v�������xQ�K���V|��H�h�q@�<T1�_,

��
�hE�P�@���m�j��)��?���r�����+"Z���r��
%j	9�l�!��l����gp�EK��J;k�Bk7/.�d���R��`Qnz Yn����9���=��
�o�fk�w�c{�M�S��3�������LqqL)�:�����} ���[�H0I��o�W�W��}���*k&�n��������N�^e�^�+���A����Sj/����T�����	\N��9�J�d:����?�gti�QAm�jG�u��C3-�����x/HZ���H�N��h���:�S�,gZ�T*w�n%|?&q��6p|(�N�TA����f+l� ��!y�&�	hCA���%�*�T���K���y�P�7*sE��B$��!�>W�������d��eBwfO�n�+XisD��Sd���C�x��W���	PW�b:1:�@�?�(�z/.�4}pz��M���D�#���
3�����Nl��FI$��%j4��������v����
)��8�J�RL�/d=.:Z�������/��z��s]��'��.N{�|~l��G�a:i���yD9$?��L7
M~+F�	���6��B��#��<��r�s�Q�}�#�T^���u\9�.	�
��t������v*�I\��W?.qi�JX�L��z���1,���������KM4���L(����V��7�����7�XX8a
vK�!�q���'�b�����:�Y����G�r��~=���<���_@����w�N��R�9��)��qq}���
IZ������B�sc���~���8w�=��E5'���E|n��tA_�z-�o�,��uG���&V�yUQ�F�t�F�L���?{��F�lT�$38Q
�<K���~���'����M�Q�5'M8Y�`<����7�����\���s�>���/���tTzt{�F��zd+�����O� ���c��>;��R��hi��'�BjP�Z�8p*E��o��SL�]���kq��(78�x��n�n��U�����;������n��������%F����M&���U6/���Y�3����'���U^�x�nQ)�4�R�nm��5w�1/��.y�rO�������V�z���M�������5�e����?w;f��;	�5�����JO���j�%8���*N����g���8
��TmgG����f*Zs�"S@�z����Ss�px����c�����o�����u�~7�������:��������a���S��F����s��1���\���$��������M?����z5u�������^x����7�o�x�_	vQ����d��O���LK���6�lf�G�t��=Pt
���D���	` ���*/�V�T����jZ�,h���J�`kw��� D#��}���7�����u ���f������9����E�7f�vG��Q�S�{4���qx1�Z���:����(�
�f�Q_g��-��vp����`��Oll�IH����0���������o��k���,]���[�����3�A�
{�}rt������[�<�h��Y�i�M �6���4���tr����R�!Lpmn���Er��$y�~�6�1(��'�'�)P�Koju@j;v�8Zwwr�x�����/y?B�<���6i�[8-��������w���������f���nw���5s�wiw{'���]t2���������Y��K��jmg7��v7�^�����0�+�8������w�j�����"�YoI����D�g�eIk5�n�[��hDQu�����A6�����h����>�)tvR
[��M��.�����if�0_���K{����<�GI����������&0\59�,��;���Wr>����*lW��;x���Qx���o@oxM=z^������[B���m�=���?=�g�}q�:9����&���}��8n����K��zafn,Yd����z��W�P��9{�{f�+���u��(�6�[��4o�@�D���~[�W��Q�2~%Omx��f�6k�(�����m(�i�+r\]tp!��>gA��S��}���Z���-/�z@��V��
������rn��������L�rH��p=�������H�/Bp�B~�|w��M�pT��a�G�q7P��W�|��������!J���1t�}~e������ys�)�K!�LjA����S�X�z�y�)T����M����j�+������y%��QTmXG�%(��#�	V�?�����Ptg#����;��{�}�^}Z*gi���R�y�U�/@���3���ylDN��g���m�������8����c�7(��k[;����h��nn$.�!eE��"4����d���p,��I�.��! <m�78'
�u���0.�G��Y<�b�2�>�����-��3��G��#I��T�����n#q�h������9�o��S�h���wG��]�tT�f�z������ev*�{7�m�)�v���i�p���E�z��/�mo�`����7'���4j��������7�����_	P���I�?�$�D�I4
�#F����v�u@�j�	������>0{A�R-9+E���}9�
C�����M�o���v�[=�<���d��{�t�M���C
����wc�8�t��(����(��|2s�M�����7��`�����hp�vI3��g�c��
.`�1������<��o��A��tw��C"-�Di��S���W���~�o��������:8���/_SD����:��R���L�������y�n���4�<�?=z?�����y����k���a@�����|:�<�'��j��v�
>k�_R�3\���`(����yc�BFs���@�����������M���i����~�������&�n��N������y���5��e~���|h��@�L��%�@{��b{mv����M}`��=g/z��W+�e����x$���w��a��7����^O�aQe^�X!r�^������n�o��Zj��
��R��}jV��}��W��������w
,�v��(OI��g��������Qm�
������w����e����Sq0�'v�*~� �?`��(���:~��g,��1���3!.'|E��xi�@[b�e�T}db{a���������
��oC�l=yF�9C��^��to�������� ��\���l�K���&��E��l�����lj^�2�3���:Z���T�`�`��:I|R����v�Cv��C����)�l���O|�p��]>?���
��3>�`���g
��m�u|���{v��oy6������,��������l����0*xw�A�N�����)'���C����oBI����A�����u9��w��S����������A�`�?=y�<'��M7N�.GX���y�����|��9��|9��WN�6a������J{���������7u~�q�F�Yk�h���y�y�u��YYX���h��9M�[G����?�o���E�	U�P����L��WYq�l>��������-��$y^M'�@��X�F��z���e�z�_a%^���#��f�;g����S�.���}��~�T�]|_�����?|���m!��x�����WO�)������&�m�0��f�L���E�Z/zJ��[��,*0&��"$� �J��fP��������C$��c|2��H�1����q{j�r:*��<_��C��+{�P_����Z��Z�ze�A���Auw�������k^+��=���>�5_���!����E�P��v�|����ln��k����!{�$�|���vd�[������~O���ZN;�yS������GVO���2�������rZ��	f]�^L�����7��(�����n���������K�3��zA|cw'�n��Sd�=.������j�|b�������ms�%s��~�fJ[i�d�7�3E���j9oP�����%�a���k�n�B���jP��E��0|�8>=j_����*{�y��.nd��]0�<9������u8��x^��W�+��5K���%�I�:��o���+�������zu=Pgq2����X�h_u��w2S�6�9���lzO�����J��`8�V������h�_�$|��/it]��/�b���6�N"u�02V������������}�sT��N����[�-���U�C�V5�F��
G3���d��C~p`�g�k"l��	ru�Q���V�C���+$�p��/��	��+n��$��u���������A��er5�p������>x�D�������i�	������`:��>=h�C�X�����m`R�����i
D"�5U]��ayV)�����]�����7�P1�6g���k	'��SXa�%���.}�{3��$�����?���'���{��
�emK������;��XG[�U
��M7Fq��
��e1�j��P�>�z��(��0!����x+)H�F;h5�!N#�����������O!k�9������9�^��)h�^��h4�������b�����Tr�Hw��B���A��)��;�o���m0w�"'�~*��I�u���m]������`�%���YJ�iW�����3�����r������a`O9/�4�&y�y�W�&��������g0����{��"-<}�`���qs{���B! u�q���U����q��m���{Y}�0��������T�On0j������B�5!�J�e��J2�`��<hN�n�������:������<G������H /
��	���7����w�S��q-��s��W�
zR-{��I7�����P!�8���!e
F����\�G�5���
�o��|�&7�
O�a\
 ������I|����t��C�eG�<g������O2�bCw7Q�&��:��n�k��������	,mu1�:��N"���i��4�*�9����VgF�B�G)�Is�"��['������2}���<�=E��	�W�2�2Jb�d����
aSPTx
��]��0���"���%��N/���}�./Z���b����~JIyu��|�B�-����b��'*��$9)j��0a4�tvg9��$A����k
�w��Y����L
/��u����

����� E�Mj%x��������{�y-��1������0�s�l9/��7,O�_7=�jK���6�$�N���$d(�����V]�p��0�������
�~z.����6���F���kJ���N��������i�����X;�6
c�v`���d��+��*)��4�)Y���{���~�Uc�����M�;��A��qy|�z�6�N��:59�)��7��u+��A��g��\�y=Rx����
$����1��������9����=��+����d��a�<����.���
j�2�����m�e�UW�fu�k�����w�7����7���9���
��[	�`UhJ�-3�s�����w���p�
�BL�7�	e���I��Y�
L���/�D-&�L�P�W����\&��-I�$j�sa���F�%r�(#tRKw��n�dv�DSd��)��4tp��"�Z\I��puTo�_�u�p���{��]����m�[�]
�UA��3?�T?�}�3�',�;t�z�K��e�*���P�a3�D�;��T�w�
J� ��>f>R�0J�����1�����ke���Y�%�,g^��p0'��RO6���.��rc��c%�Hf���[�.hj#�U���	�p��]�"�jN)��>�s0�Q�uF���>)��f���iaDl��23X�y��u3�S6?�HN4hz�D-�Aw��z��f
��@�{�4�O��j���+(J Jf��a�����0��t�u8�}
C���~4��g���NG�r�M=~�w���	`�P���]�5�N���m��(���G�D�	�)%���pyn�;T�i~���W? �!B��p�v�
�H��"�����N�Ggf"G�N9�J�l��b��i��={��
R:��e�f`��5� � F�R��`fC�.�	 H�G�7 �:�����`g�L��rt�A��&����O'iA �y�~-�Pc"�#o
�jf�z�P/�2��FZ]h1x���(V��.(������71f���7i������i�����}��������M=Q"m����	N�~�)l��S>�{!��^x�h
�@	���^���+���I&i�WQ���z6B�5��*����v���e\���g�G)q���z�����5��o�|����DuIy�K�$���un��W��F�P�������~�y��kX�|�qM��wn���T�%��8��.	�������rk�91��I�6a���	��@���G�.�5���_=�	�E���+�j�$$�Z[S��i�r ]��s�@jo/:v�������JQ���j������:a��@���9p&&���94z?V���b�������Y6^�vu���V���D;�E���52��81�Y��@��H��lOB@B�X]w'W@���g����s�vF8wQ�>�9���G?_�_��.��������E�ed��yJ����)���o5����H�z4���L:�
XKY�P. bB�`��T$D�������um�J��"N���-JH���u����D��]�we%R"r��V����&*
��$Fr#�����=�@L,�iM�r���{(G}�����^�k��3�0��z��'�w�������B�C����9���A��u�<��5j��5���	��Q���U�A�6X��a*f�$���1n'f���;���5�{f7�&-��:��'I�=/8q����J��N<�T����|N��/�`�������Nc�F2�^��8$I�.��j-MQ#m�R�%���}8}���=L*��)�R
�qa{�1m��l�u
FdZAK��6]!2���5jj�d�0U<�!���w�����O
D�{��.��9y!qZ�
Af�\�r�����.d}X��'�c��qh�r���'[Zy��D#��TK��0} � �|G�)��s��R�#��)X�n"&�Y���rm����9���~�h|�����h�a=�eK=�fx<��M�1G�L���|m�aV��3#�b�����X ����f�fb�F����MH�I�+�EZ#�n}{y&h,b��
������T
���`D�
!��J.G0�3������9* ��W��U��E�	g��
����a@�����A+��++��~���a���g�*u���5��hP����3��v02n�y���V�5�1�o��}�;��(���%z�����o�QhZ����Se3����;�~x5��t���������x��P���I������B�_y;���S0&����^&���P��.�P��{D������G�7m%1Y:�Af��>�0� ?8����v�w�����\��G���>���q� 
'�a8�������/^ML�!�=�6C88$�D1���l�������.�2�H#�b5�Y'�\��.G����Ij���"���`4��<���S�O�s���j��m��;��Q7�=�(r	��XQu��RHE�[�\Z~'���k9{�����(�~R/$�6U��.FJ3S�}�����4O�t�4��H8zM����������d���i���Q�z��-6���H��U$��2^�4QJg]��#@�jP`)�^��N
f��Q�=�8���!�����H;�Q&�~]-�����W���8}�9%����P�d�7��qiH�D��L��Q�(��(�-�je<Q���q�}�i��]�G�y����ES�`�:h�y�����<f��%��[Kc������;@�u�.����B�u2@�6,��b��t��0����8ks$b5F�U�����D��H0��0U{��zfq��Q8�����L�{6�������L��	�D������� $���(5>D�H��}o���xo��^w���B1�>��������!�����=fp.�/�(�D����+���u��8��'oU�??p�7�tR(2�H��iD��,P����+?#(�$jY�
c���2�P:�m����\����j����
Mq�HOf���*����7�O%���c@�2��a?����
y��t����q�/a}(��W,���	jr0=r~����u�#�_��.x_�����R�yY���Y��>��y�Lm0��� k���/�'�����w���9L�gQ9_�yP�����0DIN4�J���aQ���EX��*8�U~�{^4���;cy�b~�@���hM��Ce5������_���.\����S X0JRv����oPFI���T%���������^�O}�����-��K�^���y�V�U��tm���=���"����\���f���&8��b�o	��D��Z��
�jCX��A���c�2��G[�P�N����A��Y�������v4Z ����xF��6�~��
2������%p��X0U���x��'��	��g�i%��"�"�?N'��]����_N���$D�q��6A��u)��������h����Y0�3�y��,}��g8�/�e��c~�,�Q|c`%i����c�B�X����m;�����`����������f���|�(]:E)��~���>A���W�<FT�t�%h���T��C������#��>nn�#��)�T�AUd}�
n�.�AE�1��f|T��|����>Y�����3��I�A�l�Cq��:��E\�i��BkZ�	P�h��el�p(&bL�A�U��/�a��G����a��GbF�g�BM���������>-������v6a�t���=8l>��&<Jw�{>$�UK��Nm��(vL!+��sWk/��H�w�{W�d��i���Y�i1]�s��m�c0������Q�{�n��|6�������l���-iH7-:��2��8��A�0l�`��#1��L����O�XA�w�,W�\o��������&v9b�[���Rk������sy�������+N�_��ww�A}cs��4�y,�|�"Y'�6Ye'.�����!d�pX�2����k��1��@��[d�����
|�i-FjB[)��$��)h74��gKwhe3)��O�`B���:-e������h�"��
-"t�rHk8��������z�����pzo=02O��B���>a�!�`�s��
�f���#l���HPW��0n��y������q�:l5�;��Y�b��#��/��j~��3FJ��m)�W9
���I�Lo��)����$�i�B��m;���e���g%2le8d���%�Q�	w��p>����%M�~��l0��8��L�<{�-����q3�
�������Ii�n�c����W	#���%�|��P���&G�<�%1�i����FU��6��`t���K8��O����F�4�}�����37� c��3��y|��d7�7����n���.�I���d	�y�B*P�RQ���
�+G�A��~���A;iY�*^1�	�r,��(
�P���q�5Jq?w�q�;�Vb�y����s#��^��K4�m�V$������e
���W&t8������w�������qFdm
k���/lW����Brp0�hJ� ��������0�����~��iaw�;������@5cw��T;C��������Y�!E9�HvW"?��\�'��������h�����{�oH���h��G�=���e�
y'r��$�i#���n%L��3'h7$r���M�R
�Qe���M��
5��'S'b�Kb�2������E�_�#2��N��8��0�BA�U�`d,����1lB5Z���l0��J=��:�OF�x�}^��=\�����c��e�2��4AM]�+�V��9�E�1V����T��aj/��{&��>C���������h�m��w%E4��^.�M����F�|Rm0��~�'L��Y�?�����"F��J�El�%������Spjj=�3a��|�?��"��B�����D�������2�QR����I8��sN�yj_���7���I����kw�{��v��z�~��5j =���(=���:P|��|�o���r����� ���:�r�S�,D�����R��l:���
��5�`$GZ2�D���R�M����!
���6�[��<��s�j�n:�����Dw7���	4�r��3��w��Tn1u2yg^K����1��w{C~�:WH�dV��V��Y��sX�	d�>����8M
"�WkA����5�V7�j�������e)
1Z�U<�;K�AC�Y�w��U�G�lE�l8��Qz�Z�TX4�75����)]!-����
SO�k9���9u���T���PP�A�I;�&Jx�a��{�R��w!`�@�"!�70��-��_�,1��I7����Z���������La��� ����!��^�Gc���[� �������fl��L~�����l9�2.l��~�����������n.Y�k�\��
j�m������l|=��)�]g)N`��=���~��p�B���P�Q�l:V������iTkV>@����{�c����p�]M�%�	��xL&$H����\�Om�c����n��J�wd���5���
-QjP�I�h\�`-�<;�E���y�����4���Qr��~sB��68*!�L�a���~�>��4��=���(�b���J5����8����;�O_N�[` �pw@���W���=����S�8�zw��Za\��nW���|�^%��T���x���tl#6����*�=�j^��
0�2���|��GW�t<"�2-��a1c�;��$.0�W�-~���4���L�X��>-���*��nc.1
(��_�A�����Xc�i������]/�}�1HK@�APt��:������>(�
��8���7�����c�s�w���
?�U�$���="���)�@?��1_Js���#E}
a�xW�+�8��y��%l��l)y�������ny>����N���������QV8�
^��w��/N�f��p`Z�����i��J�_��u�8��-n�i�A+�Z���L�������Av�3���-hB
zZI������K3�}�R��9+p�O'�}5`�!]{A���Ms����t�s�E���)����O�X�
�%v�]����cx����C��!��������#}U����6����Wu�����|���IY5�j��F��PiTn�c��(T�O(�2�����zp!�W�����~�����Xm}6�UL��|�i�����3��f1k���F��`c�.F��T�i����o*��G#7��P��I4����L����4B�+�H���H�K��)J��T	�M�]Kt;L6=�QS8z=+!����#��\��ID���4�[SJ���R���P����U�(YJH5vkXxtF{xF��?@K� :B9D��*��(S��o��M�=��s��tx�k��U���6��U�����hZ�&[<��G&����ab�����{����Z��8��ZY�T���]�h9�u)]�2��7�/���7M�a���g
*���hE�
�V+�������$742.\����3/O���q|���B�Wd���8�����g���W�}�:1K6�����t��Y���������9%V\�:%�DDU[s����'���{��M�Pp��i:��>��^�w����>K��f�<%��K�z{t~zy�^��i<[�������:9�����G��$����7��������1p
\�-��zQuK���{�C�e���9��<���l����Q��r��B:�,��Rc���)/�����6%�#�ZQ'J�FY�p��-�wh���o�9��II�������x={�=�\�?�V����ig���}��20p���NP��;�5��-�� �����m����,�zf��_<U��n'���i8!d�-D��G�:����H����QT����e�8A��=Ru������-�%H�,E��\}n��R%oi\O�d���@�2n;�eB����$F�\��H�ZJn���,
�C6�
���8����_��p�%����TI����n�����.�I��������stY��ZFrk]y
y���:�n�S���	�\_Vu�*O	��+�]�O��B���k�O]<W���V��3c�b4���3�+�^t�R�h<�>�8
���0�LQ~"��3('���j��V��G��YS@�����?��+���1��.RC0����/m�e"�x�R�7-��;J��-������Y#n�LI�uz�����c>�z�D�O�����2}�E���j7�L��q������I�/S�p�C����@����V3����0%���[�����=
�C���V�4s�!^�g���hzI�M"�>L���U��'
����e������4���rjwk����������cg��y^����"LMm`�p�}��1�E*~0r�����S����	�B�!r���98��_ ��y������F5�W7w�x�+8������E'�O���_��n�G�:9DsV{���h�N�~�G��Ox�����,`����X�l�?+j���Q>�����Hi0)
�FJ�{��J���`?�.~��z��T��|���
���
���y�zH����
�����}�����v��� R��B�T�\�oJL'�������N�A;������=��Es��\'�6|�m���"����
���!�>�tGu����
h�^�Q+�.-o��}�BP�`�k:E�i�vbk�O*�����?Pu�%Y�s*[�E��-t�^�4/K��3�� �a�]t��f����R��!c_yt�'
*����>�98����}��S�'�����>�4��z�2�mG�~��y?�Z�����=�^T����7��)*	9�~����c��y���	;�K��VM����p8��y@0�Tb0�Y`��^�\�l���c>S��1jlc5�zo�l,�V�m��;�M�^U@���	��-�	�]������)�~�� �C����4�Q�j2Q���ST���S���������Z{�3	}t='4I�XGg
!I*����B�Z$��M7��c��,���L��dF��W����`����Qy���l�y�����,
����$t'�LS<���AQ5@��>�?�Jx~�����*.	#��
�6���}_���'n�l�7�%,@���\�a�����y���2��FPF�6�a�+���=�}6���4u�)��e�/������.�m�%�����k���Z^c������"*go�^E?��5[��Fgn�\�1����&�K0��yN�tZ�?����X!���g&F�������?�8y��I��;��{�������4����VeyC�����['�v�����~���60MT�T�7����������v������Y�A$�w�{��a�����
C�`z�c �,�c����mv=�d�a��4rSC�'���������2+�����j��������R���Cz+q�JV�i&��x8���i�7�1$���"�I�R�����<���
�������Z���
�%�:�9�����p!&�H|���?/�������T��Uj}��57>J:����p4�s
L��	L�?���?I�N��j[��fm�2�p>v!���ZT���d�#��m8���4jE=���%��q����
��J���n���
67w���Yw�����y���'��E%��&�a�b*8���#��)	��r�U���>��7��d6��i�V�lk�=����/��i�`�qE����)mU%�����5����xv�R���
V�#������'�'Xtvpx?������`skk'����qP�AD����Z�6X����j���������.��4����[���,C+B�X��2�����a���/�W(���BI�b>x��<�W�c���h��������wT�I����C�������s����mno����������8�<�\���������0~���{����2�*�{����[��;����������q�lO%�A��-*����>�Wm����9G�3@v;�:���������
.���
��
���N:�<�\g�T8�����?+:t���lU��l�g���8�\.�w��y������O"*6�����3�����9����r.��������z�}.8u��sw������{���8����T��.~��4���,q	����
/�� �K��YSd����QgM�IwX�2������++�#��/��&Y�y�m�k�c{��oV��s���y�
�:�
��/�������o�C]y!V��`Q�|,@�v�e���2�����y�}�}�\B�d��T��%���;���neoq�������rw'�w����b��J��Rl@L2��Y��8P��s���6�������`�����{�'�7�K!�?Q}D�(�R���#�\&
nY�,�O���������G ��������2��{|kck;�����f4c��f�[;������� �������E��(x7	SIZ�s
�tR4�;�%�I��r5	�o9�n�����>k�7��Y:K�4���fUN�������k�,�nvawg� 9Yj1tn2
���T`� b��I�Y�!Su�0'�|��$t&��,�!��|J�	,�vr{I����h�v�6(�;����n%2E��X�����}������8��
k~.��m$�n3������8l�n+�p�����2@��N�(���cNX{����h��+�Jpr����d����^�==x
&�}��TD:��O������[?Z]5]�v`�f�0�6�m��9;ovp����o�z����3��>-����x�� �,kx-\�����3�Z
��M`�����-�m �#�����%8�Y7���7�f��d�����S*���_��MJ!M�6�!�'�`�j���\�7r��xQ��N�+��Ht@���I����0liq��Y�y,nD��B���<���`��q?�s��(�"����8��$�aQ��z�$R2�NO���W[�eGN��E��K�����y|,O��U���Z����`/N�Z�U{MKE�7�g��7�+e���s�����~b�����&������C����:}M� ��n
H��MGF@����8+uv`#������WRE0��'#N���n%yu���i�/e����m�I�1|��t5��0���������^4�|��V"��J��^�7O�fh�����6�kA�t�,�`1e}�Y���9Y�q�.���������p�g�PH!:tFq<.�b�l<Kqw��@�d���I��>�8��� 8G/t~�j���N��E������~�����b���6�^�,��Y�l���(��#��^����Cu��T�k pe�&�U�!���N��&5����K���:���z}��'=�MJFp�����%��L�)�D�&��K����-G�2�N��/6��pZ����jL�����m�n�G�9���>����g=�����lhr����/�g�w+����V:���[�pJ�����a���s�!����Y���0�^/���C�?�<�N)*���L�wi?5�>����^�#6#�A�����������ON�?��`<�������[/���o��
A8�&�%5����2I�	ar���N�9��{�~b���m�lu �K�4������Ofq�W��E����$������t<U��1�~���;T�����E)f��Y�
��<���Wt��W�N~.�}E�C���Y}�3���N"DX�x���{�
��` .�zoSGx���z����=O(�p��7:ipa�}��z#SQ�<�95fHv��$NF��>�(����FF@Yl3�fN4>��CW���(�o�Nw�#N�}EgYNs���Y���xkV�rI�a�$c����i�0�[?��Ja�^7�9����WF�����M�����t��T����W:��d�����(A���6���%��\;�>�Z<��M�2�������)�#����)F>���8�f.������n�s l�h+���u	��l�,����2�����/���b���>�
�

�k�WI)��g�o���{���U�
j5	d��\&���4andvo��'�m�'r[Q-��m �������?�/~h?�I�p���h������EBJ�5�#��9��~�F��Lb,�f�����w��y�wz$O�xO5|
�s�pC�N��Z����2W@<-n(`K��'�Hk���XR]�u9���$h����&Q�r�W`,MX��Lk���{fc�����b�0;i!
�T�T�?0
 g�sj�8ot�g������?;��7�c��_�u���j�����1`�����c�a�> G�J�l�P��� B�^���d�)����{i�z�%h�"%C��M�~G���3H(�5^x�_��0R�d��9�� ��h�aH�Yr���o�<������X�!��!%�1�\j������0��C���0F�v8
+JB���Q����:��O#���<}oC��
Me����$��1iMN�
�_��FD$O�<�
5��;x@�a��k"�|M��\I��xaZ�!�>)�^�����:�9�f_�0,^3��Q�����g���R���;����%\�?X��
�G����Q��q���~}}�����Qt�>��p(	#'����	n�FmLp�a��"L >���L�-o+R������p�o�u�hJOf'�l*��CY��SR��q'�4(���kJ��J�(�x	w*J��t�E���>&�M���Z�NJD_HC�l���Tl���d�l�
�J0'r8��gn����|--�N���������_��T7��{�GB���-@������q�����7[G'�v��M�<���265,2�*�P���qv�:92�
���`Y��4����������r)i� *%7IY�p����p--�{}��P��
�]hm���n����zP���K�'a/��;��:]�����"7�Rb�����FP�a�,���3n�a�h6{
��DS�R?W������f(e��]���*�@I8�6��XQ^;�h��> ���<�i/���������vP[��vkc(����:���}J��I������������j�zJ�wJ#��j
������u���;�Uf���BC����v�v�4��q���8�fS��� z�J�/���Bo��u����C0�(���cRf�8�������}���mKc���il.M�������B~=�����M�����I���&\���Z�:�A��N1Z�f=�>>�H�����f�y�M
Q�@��,�#	a+�,�g\�oK���[�:�����,�ho���)"��������G@���P<Br.x�����a�;h����78��i�����$vw��X����0��z(FN !��X&�xd8g��=�n�j��u�`�
rb�7���x���F����b)�s�u�'�V�$/l���\��BF��.[�8��~����{�r�9�����Y��W}�%l�i�~�G�����Y^W
Ch�USx�^���������!~b?�a~3U% 2���[Ok���J$��I���X
�+�p�w`���G�x6^��F���k����^�"��O�a��E]gdKl�-|rz�,K�@oWo��x(P��z?a�e�DWC!�1
T���:Z�S�|�$����`�TfN���*��O��H��G4�������i�o���{A�$��^.Y��UmU��=������,`�{��B�, �X�d$p���������S<�,Z8x\	Xa���K|z��_�b�{E���.!D$��iI��~J�^����h��*��U�$s��E�B�6A'��������%�[�WX+U��<$�x�'���������l��R	�q7$<'��mI������{���z���I���1��	+tS�|�����Iw�1���D���"
��R%::P������JmnI2���K �����*6�%��:��h|" �U��Ws�If	�;|C�y�{:	i����e���"m������Z������*�1��l���A�;a�!w��$k�m�j���6�UwD��rvt�>�Y]B9`�p����7���{uK�������Z�cjPO�*�rM�7�N���y�x�t�9>�jW��6W������\�@6gm��� �����lP�T�Kf�\F=Z��y��K�{���������!/�$�����}��f���!�
����R�u��UQ5���xgu�K� �.��fk����b����g���uwS��������.1n���s�����������9�����xei���Q�������f�=����(^6�DAvtK�`��4�<S����T'�M�n�����gc|�#�*a��E`Mm���pc��W�o��#�D��c�/�}���g"���6Om�fp�q��0�%6$�~�����I��B@���/���`7����"o!�����k�)r���T�A����{�^�"�P��
��.��,G��v�{��k���r���]��������Lo����LY�\���6�L��$�K�0_����hS��k!}%�u���5���b�Fpl/�>��w�hH��]J��M�Vy�u�e�ED��-��E��z�j��>[��f���KDK�7~�����v����ZzM�u�.t�eE�4o)���@�r>Yq#-T�Z��:Xb^d!���%�"�]m����`�sf���)�tX���E�M�����Xb��E'���^�Z$����a_���S�L��������U<Di�""���bV
uN���YB:$�Buw��=�~|����-��9��X��:��~��d:��/�:��-���$��O/a{����q]�6�(��N��~8��u�]��)H�I7\��j���/����Tl�~o�~f
[�+B����������-���/5	������������ty;�{W��s�9�j�<{�6g���jsr���	
��^�zA����g�I<�������R����OP���(K�P\��Uo	F�bP�-4��iZ��q�kxw�9�
���G�=�
'36`��\$Y�@ESn
-�����T�~%m�VNy��3��,�5#���	�E���9��h�'H�U�:�I��h�����'o��- �1�~�}����7�:I,uo�{n�.�����BQ�;�����9��L���"Q�S0��3�Q���i3����u)Z������x4���C����kA���M�<EWq�X�c����j�t}&���p�h�'��2��f��.����ITa�E�J#��d����
���:!{�yV�O�Fq�]cv8T�V��u�q���!D{��znF�AOQ����2y�<6|�����v���o��'SVQ�aAn5P�����t�&h�����i�W_i�FG�R0n �y]}\e 9r�u�Nmg%(��i�F��`Y�(?�J���=p��u�Y��7����L���2Lr�s~E�������EK��=QG��\�z�v"s)k�s;�����@��'$m�+e����p0���y$���������]V�	����n#rp��o]pI�@�
��Y�@v���xu����66MF��P�;��%��^F(��������-���	���^���J�\�K�8_�U3�o�oC�������c�9��?z��<{,or�H8���v����:<=Wg��vS]�7����[U�#,�/.�[�p
S��NI�[Au}�ZhnN�H���Em>��W����e8=]�<E���F��zv���:�5�'�89��������=������Mo�������o8X���N=�n��mN	#)Ps����g��N���1���V�-,�#���1Ibhg3���:�
�'E7��k�O�@:7�D~P���=�X�N&@�����#)A��B �#x}%��!NU?RO1-l��?��W�FMh������d'Tn3	/(B�v�9��
}��%'yx��IL��������:�����:o�����Tx@#	0��p��c����z����y����.*�J�#�O�D�j��yQ�J��9�:��E;P���8���x'�j�#r���$SI���q0Yfx�=%��W]��������yc�b���o*6����Y��}�a��x�;����\��p�lll�d��)�t��1M4�0n�H;�����Q+f�1�i_}��VT��g�����\��b��#p@0=���jH8'�3�k����kq�`��X�I�������66�~�p<�y�oo�'��	�;�
o��G��l���������z)�������-��&Z�(=ZQ@5��p�S4��h��-y:O��[�0Z^F�[��ce��X���P|�Yg/Q�w�G��>�:�KC�"���ss[�'�r��0?OIrS�xn@��OG�1q�:������mx��o�a��5����by�4����Y��z�,;�����S�f8������U���.�@�l��s�JCTm}}���YR�q�!pt�<9l7S`�>s����xm4��y22C�S�N&��uNr���*���O��<�f���Ve�f�9���i�v���%Gd�w6����~���/�/�_����<���X���l��M�M��� $�vI��E�xD �%U��8�`�� ��<�������-����������y�j[%W/!�T�&�:HWH%~��
��m{����vJz�%���^Mk�]�[�kxw���U���������VP��f�Br+��1����^�6�������&L�=����i��f=����c)>��I�:'�����OH!����l���#���C��CI�����L���,�2P���3��=3��$JR��t��������P`����.\<;9t�%-��z8�^x�@����;��!�W�	9�����,B�@'d�l|�>���u�I���w���!X#������m�+S[���Jr3��`\���9�z�_y���^�\�����3�_i
#��_��_
F���������:���)Q�!�%��0*XU���{��G(�]V��{���G�
��w�����8�hr������Z�}��(����������>�i��X���_7�����mvcQ�������:=�8?=���G��.*�b�L�$).�V�S���[i�6��'��J��!Z��f?�A���:5<�0==1�r��C
�}����sy`�6�6&�E���rE����gv�r
4kvJ7���[4
F��[V���F-����8n`� Hw�� XNC@,O��J3`������z�U_a1y��h������7����
����O{8��~�K�����[M�[)�)���knQ<\�Vk�u�sk�e�R�y��LP�m�x>��A�~K�z�������3m�C��u����z���6m?���E5�,��d>�����F���5d�D�
p��V:;=�@�c9@�i	_�	7�P��x&�5�(�A��ML������������"�8?��>F�=�?t?��|?�p��
#50Robert Haas
robertmhaas@gmail.com
In reply to: Dimitri Fontaine (#49)
Re: Extensions, this time with a patch

On Wed, Oct 20, 2010 at 6:22 AM, Dimitri Fontaine
<dimitri@2ndquadrant.fr> wrote:

In v6 patch, should client_min_messages or log_min_messages be lower
than WARNING, they get set to WARNING for the script install context. We
still dump the extension's script at each WARNING, but you can set your
client_min_messages (and log_min_messages) to ERROR before hand.

I would vote for overriding client_min_messages but not log_min_messages.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#51Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Robert Haas (#50)
Re: Extensions, this time with a patch

Robert Haas <robertmhaas@gmail.com> writes:

I would vote for overriding client_min_messages but not log_min_messages.

Well it defaults to WARNING so I see your point. Then again, we're
talking about hundreds of lines (3197 lines of isn, 531 lines for
hstore) of output per message, containing a script that you're not
maintaining. Do we really want that amount of extra logging?

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#52Robert Haas
robertmhaas@gmail.com
In reply to: Dimitri Fontaine (#51)
Re: Extensions, this time with a patch

On Wed, Oct 20, 2010 at 9:33 AM, Dimitri Fontaine
<dimitri@2ndquadrant.fr> wrote:

Robert Haas <robertmhaas@gmail.com> writes:

I would vote for overriding client_min_messages but not log_min_messages.

Well it defaults to WARNING so I see your point. Then again, we're
talking about hundreds of lines (3197 lines of isn, 531 lines for
hstore) of output per message, containing a script that you're not
maintaining. Do we really want that amount of extra logging?

Well, my thought was that it makes sense to override the user's
logging preferences because, after all, if they wanted the extra
logging, they could run the script by hand. But what gets logged to
the system log is server policy, not user preference, and I'm
reluctant to think we should second-guess whatever the admin has
configured.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#53Tom Lane
tgl@sss.pgh.pa.us
In reply to: Itagaki Takahiro (#46)
Re: Extensions, this time with a patch

Itagaki Takahiro <itagaki.takahiro@gmail.com> writes:

On Wed, Oct 20, 2010 at 12:58 PM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

Lets rename the directory.

Hmmm, but we call it 'xml2' in the doc. There is no 'pgxml' at all in it.
http://developer.postgresql.org/pgdocs/postgres/xml2.html

However, I don't think we can change the module name because pg_upgrade
will fail if the module (.so) name was changed. So, it might be the
point of compromise to keep two names until we deprecate it completely.

If the extensions manager is dependent on the assumption that a module's
name matches the name of the directory it's built in, that assumption
needs to be removed anyway. There are too many use-cases where that
wouldn't hold, even if we try to force the standard contrib modules to
follow such a rule.

regards, tom lane

#54Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Tom Lane (#53)
Re: Extensions, this time with a patch

Tom Lane <tgl@sss.pgh.pa.us> writes:

If the extensions manager is dependent on the assumption that a module's
name matches the name of the directory it's built in

It is not. There's some magic for simple cases so that contrib mostly
"works" with no editing, but of course, that's only mostly.

The version Itakagi-San worked with had not a single change to the
contrib sources, I've only begun to change things there (in v6) with the
spi case, that now produces 5 extensions control files out of a single
Makefile, thanks to this single new line:

CONTROL = $(addsuffix .control, $(MODULES))

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#55Tom Lane
tgl@sss.pgh.pa.us
In reply to: Robert Haas (#50)
Re: Extensions, this time with a patch

Robert Haas <robertmhaas@gmail.com> writes:

On Wed, Oct 20, 2010 at 6:22 AM, Dimitri Fontaine
<dimitri@2ndquadrant.fr> wrote:

In v6 patch, should client_min_messages or log_min_messages be lower
than WARNING, they get set to WARNING for the script install context. We
still dump the extension's script at each WARNING, but you can set your
client_min_messages (and log_min_messages) to ERROR before hand.

I would vote for overriding client_min_messages but not log_min_messages.

Why? The problem with unreasonably bulky messages is just as
objectionable for the log.

regards, tom lane

#56Alvaro Herrera
alvherre@commandprompt.com
In reply to: Dimitri Fontaine (#49)
Re: Extensions, this time with a patch

Excerpts from Dimitri Fontaine's message of mié oct 20 07:22:53 -0300 2010:

Itagaki Takahiro <itagaki.takahiro@gmail.com> writes:

==== CREATE EXTENSION command ====
* Environment could be modified by the installer script.
=# SHOW search_path; => "$user",public
=# CREATE EXTENSION dblink;
=# SHOW search_path; => public
because almost all of the modules have SET search_path in the scripts:
-- Adjust this setting to control where the objects get created.
SET search_path = public;

Is is an intended behavior? Personally, I want the installer to run in sandbox.
One idea is to rewrite module scripts to use BEGIN - SET LOCAL - COMMIT,
but we cannot execute CREATE EXTENSION in transaction if do so.

Using SPI to execute the extension's script already means that it can
not contain explicit BEGIN and COMMIT commands. Now, is it possible to
force a Reset of all GUCs after script's execution?

Would it work to force a new transaction internally in CREATE EXTENSION,
and use the equivalent of SET LOCAL in the CREATE EXTENSION code?

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#57Tom Lane
tgl@sss.pgh.pa.us
In reply to: Dimitri Fontaine (#54)
Re: Extensions, this time with a patch

Dimitri Fontaine <dimitri@2ndQuadrant.fr> writes:

Tom Lane <tgl@sss.pgh.pa.us> writes:

If the extensions manager is dependent on the assumption that a module's
name matches the name of the directory it's built in

It is not. There's some magic for simple cases so that contrib mostly
"works" with no editing, but of course, that's only mostly.

The version Itakagi-San worked with had not a single change to the
contrib sources,

I don't think that "no changes to the makefiles" is a requirement,
or even a wish-list item, for this. I think it's perfectly reasonable
for the makefile to have to specify the module name; far better that
than that we get the name by some "magic" or other.

regards, tom lane

#58Tom Lane
tgl@sss.pgh.pa.us
In reply to: Alvaro Herrera (#56)
Re: Extensions, this time with a patch

Alvaro Herrera <alvherre@commandprompt.com> writes:

Excerpts from Dimitri Fontaine's message of mié oct 20 07:22:53 -0300 2010:

Using SPI to execute the extension's script already means that it can
not contain explicit BEGIN and COMMIT commands. Now, is it possible to
force a Reset of all GUCs after script's execution?

Would it work to force a new transaction internally in CREATE EXTENSION,

No, but maybe a savepoint?

and use the equivalent of SET LOCAL in the CREATE EXTENSION code?

I had assumed that that was how he was doing it ...

regards, tom lane

#59Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Tom Lane (#57)
Re: Extensions, this time with a patch

Tom Lane <tgl@sss.pgh.pa.us> writes:

I don't think that "no changes to the makefiles" is a requirement,
or even a wish-list item, for this. I think it's perfectly reasonable
for the makefile to have to specify the module name; far better that
than that we get the name by some "magic" or other.

It seemed easy to get a reasonable approach requiring very few edits in
contribs so I favoured that. Now, it's still entirely possible to hand
adjust. Determining the extension name automatically from DATA_built or
DATA is only done where EXTENSION has not been provided, and guessing
the CONTROL file name from the EXTENSION name only occurs when CONTROL
has not been provided.

Of course if those changes (inlined there after) are seen as a bad idea,
then I will change all contrib Makefiles to add EXTENSION, EXTVERSION
(which always is MAJORVERSION here) and CONTROL (which almost always is
EXTENSION.control).

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

# create extension support
ifndef CONTROL
ifndef EXTENSION
ifdef DATA_built
EXTENSION = $(basename $(notdir $(firstword $(DATA_built))))
else ifdef DATA
EXTENSION = $(basename $(notdir $(firstword $(DATA))))
endif # DATA_built
endif # EXTENSION
ifndef EXTVERSION
EXTVERSION = $(MAJORVERSION)
endif
ifdef EXTENSION
CONTROL = $(EXTENSION).control
endif # EXTENSION
endif # CONTROL

control:
# create .control to keep track that we created the control file(s)
@for file in $(CONTROL); do \
test -f `basename $$file .control`.sql -a ! -f $$file && touch .control || true ; \
if [ -f .control ]; then \
if [ -n "$(EXTENSION)" ]; then \
(echo "name = '$(EXTENSION)'"; echo "version = '$(EXTVERSION)'") > $$file ; \
else \
(echo "name = '`basename $$file .control`'"; echo "version = '$(EXTVERSION)'") > $$file ; \
fi ; \
if [ -n "$(EXTCOMMENT)" ]; then echo "comment = '$(EXTCOMMENT)'" >> $$file ; fi ; \
fi ; \
done

install: all installdirs control
ifneq (,$(DATA)$(DATA_built)$(CONTROL))
@for file in $(addprefix $(srcdir)/, $(DATA)) $(DATA_built) $(CONTROL); do \
echo "$(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/$(datamoduledir)'"; \
$(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/$(datamoduledir)'; \
done
endif # DATA

#60Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Tom Lane (#58)
Re: Extensions, this time with a patch

Tom Lane <tgl@sss.pgh.pa.us> writes:

and use the equivalent of SET LOCAL in the CREATE EXTENSION code?

I had assumed that that was how he was doing it ...

I'm currently doing:
SetConfigOption("client_min_messages", "warning", PGC_SUSET, PGC_S_SESSION);

And then manually reverting to what was there before the command:
SetConfigOption("client_min_messages", old_cmsgs, PGC_SUSET, PGC_S_SESSION);

The thing is that CREATE EXTENSION can be part of a transaction, so even
SET LOCAL ain't going to work here, we need to reset before continuing
the transaction. I don't know that SET LOCAL is RESET after a savepoint,
so we would still need to care about that "by hand", right?

--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#61Tom Lane
tgl@sss.pgh.pa.us
In reply to: Dimitri Fontaine (#59)
Re: Extensions, this time with a patch

Dimitri Fontaine <dimitri@2ndQuadrant.fr> writes:

Tom Lane <tgl@sss.pgh.pa.us> writes:

I don't think that "no changes to the makefiles" is a requirement,
or even a wish-list item, for this. I think it's perfectly reasonable
for the makefile to have to specify the module name; far better that
than that we get the name by some "magic" or other.

It seemed easy to get a reasonable approach requiring very few edits in
contribs so I favoured that. Now, it's still entirely possible to hand
adjust. Determining the extension name automatically from DATA_built or
DATA is only done where EXTENSION has not been provided,

That is simply a horrid idea. Just make it specify EXTENSION.

and guessing
the CONTROL file name from the EXTENSION name only occurs when CONTROL
has not been provided.

Here, on the other hand, I'm wondering why have two variables at all.
Is there any sane use-case for the control file to not be named the same
as the extension? It seems like that would accomplish little except to
sow confusion.

regards, tom lane

#62Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Tom Lane (#61)
Re: Extensions, this time with a patch

Tom Lane <tgl@sss.pgh.pa.us> writes:

That is simply a horrid idea. Just make it specify EXTENSION.

Black magic it is, will remove in v7.

Is there any sane use-case for the control file to not be named the same
as the extension? It seems like that would accomplish little except to
sow confusion.

The goal of the 3 variables EXTENSION, EXTVERSION, EXTCOMMENT is to
prepare the control file with 3 lines formatted variable = 'value'. If
you specify CONTROL instead, that should be the file name you're
providing directly.

It then grew up into being a directive to produce the right file set for
spi, but the simplest thing would be to hand prepare the files there. If
you agree with that, that's what I'll do in v7.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#63Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Tom Lane (#61)
Re: Extensions, this time with a patch

Tom Lane <tgl@sss.pgh.pa.us> writes:

That is simply a horrid idea. Just make it specify EXTENSION.

And VERSION too, finally.

So any extension

and guessing
the CONTROL file name from the EXTENSION name only occurs when CONTROL
has not been provided.

Here, on the other hand, I'm wondering why have two variables at all.
Is there any sane use-case for the control file to not be named the same
as the extension? It seems like that would accomplish little except to
sow confusion.

regards, tom lane

--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#64Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Tom Lane (#61)
1 attachment(s)
Re: Extensions, this time with a patch

Tom Lane <tgl@sss.pgh.pa.us> writes:

That is simply a horrid idea. Just make it specify EXTENSION.

And VERSION too. Sorry for previous email, fingers trying to work on
their own at the end of the day...

So, the idea is that $(EXTENSION) is a list of extensions you're
providing from the Makefile (most often, a list of one extension, but
contrib/spi is an exception here). Each extension in the list must have
a corresponding $EXTENSION.control file.

This control file contains at minimum a single line for the name of the
extension, but it's better already with a comment for users. I've been
filling them for our extensions, pasting from the documentation:

http://www.postgresql.org/docs/9/static/contrib.html

dim=# select name, version, installed as i, comment from pg_extensions;
name | version | i | comment
--------------------+----------+---+-------------------------------------------------------------------------
intarray | 9.1devel | f | one-dimensional arrays of integers: functions, operators, index support
adminpack | 9.1devel | f | Administrative functions for PostgreSQL
autoinc | 9.1devel | t | functions for autoincrementing fields
btree_gin | 9.1devel | f | GIN support for common types BTree operators
btree_gist | 9.1devel | t | GiST support for common types BTree operators
chkpass | 9.1devel | f | Store crypt()ed passwords
citext | 9.1devel | t | case-insensitive character string type
cube | 9.1devel | t | data type for representing multidimensional cubes
dblink | 9.1devel | f | connect to other PostgreSQL databases from within a database
dict_int | 9.1devel | f | example of an add-on dictionary template for full-text search
dict_xsyn | 9.1devel | f | example of an add-on dictionary template for full-text search
earthdistance | 9.1devel | t | calculating great circle distances on the surface of the Earth
fuzzystrmatch | 9.1devel | f | determine similarities and distance between strings
hstore | 9.1 | t | storing sets of key/value pairs
auto_username | 9.1devel | f | functions for tracking who changed a table
int_aggregate | 9.1devel | t | integer aggregator and an enumerator (obsolete)
isn | 9.1devel | t | data types for the international product numbering standards
lo | 9.1devel | f | managing Large Objects
ltree | 9.1devel | t | data type for hierarchical tree-like structure
moddatetime | 9.1devel | t | functions for tracking last modification time
pageinspect | 9.1devel | f | inspect the contents of database pages at a low level
pg_buffercache | 9.1devel | f | examine the shared buffer cache in real time
pg_freespacemap | 9.1devel | f | examine the free space map (FSM)
pg_stat_statements | 9.1devel | f | tracking execution statistics of all SQL statements executed
pg_trgm | 9.1devel | t | determine the similarity of text, with indexing support
pgcrypto | 9.1devel | f | cryptographic functions
pgrowlocks | 9.1devel | f | show row locking information for a specified table
pgstattuple | 9.1devel | f | obtain tuple-level statistics
refint | 9.1devel | f | functions for implementing referential integrity
seg | 9.1devel | f | data type for representing line segments, or floating point intervals
tablefunc | 9.1devel | t | various functions that return tables, including crosstab(text sql)
test_parser | 9.1devel | f | example of a custom parser for full-text search
timetravel | 9.1devel | f | functions for implementing time travel
tsearch2 | 9.1devel | f | backwards-compatible text search functionality (pre-8.3)
unaccent | 9.1devel | f | text search dictionary that removes accents
(35 rows)

(\dx+ also has the "Custom Variable Classes" column and that's too large
a paste then, so I've used the pg_extensions system view directly

Some extensions are missing here because they fail to compile on my
workstation where all the libs aren't installed --- ossp, xml2, etc
)

If you provide a $(VERSION) variable, then a line in the control file is
automatically added at make install (version = '$(VERSION)'), in order
to make life easier for extension authors.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

Dunno if erroneous previous mail did contain the attachement, here it
is.

Attachments:

extension.v7.patch.gzapplication/octet-streamDownload
#65David E. Wheeler
david@kineticode.com
In reply to: Dimitri Fontaine (#64)
Re: Extensions, this time with a patch

On Oct 20, 2010, at 3:12 PM, Dimitri Fontaine wrote:

So, the idea is that $(EXTENSION) is a list of extensions you're
providing from the Makefile (most often, a list of one extension, but
contrib/spi is an exception here). Each extension in the list must have
a corresponding $EXTENSION.control file.

This control file contains at minimum a single line for the name of the
extension, but it's better already with a comment for users. I've been
filling them for our extensions, pasting from the documentation:

Might I suggest instead a META.json file like PGXN requires? Here's a simple example:

{
"name": "pair",
"abstract": "A key/value pair data type",
"version": "0.1.0",
"maintainer": "David E. Wheeler <david@justatheory.com>",
"license": "postgresql",
}

They can have a lot more information, too. Her's the one I actually shipped with pair:

http://github.com/theory/kv-pair/blob/master/META.json

The meta spec is here:

http://github.com/theory/pgxn/wiki/PGXN-Meta-Spec

Anyway, the point is that it might be useful for us to sync on this format. I went with JSON for a few reasons:

* CPAN is switching to it (from YAML)
* It's extremely widespread
* It's useful for ac-hoc REST-style requests
* The format will likely be in 9.1.

Thoughts?

BTW, really excited that you're finally getting EXTENSION done, Dim. This is going to be *great* for PostgreSQL developers. I'll have to work it into my talk at West.

https://www.postgresqlconference.org/content/building-and-distributing-postgresql-extensions-without-learning-c

Best,

David

#66Itagaki Takahiro
itagaki.takahiro@gmail.com
In reply to: Dimitri Fontaine (#64)
Re: Extensions, this time with a patch

On Thu, Oct 21, 2010 at 7:12 AM, Dimitri Fontaine
<dimitri@2ndquadrant.fr> wrote:

This control file contains at minimum a single line for the name of the
extension, but it's better already with a comment for users. I've been
filling them for our extensions, pasting from the documentation:

       name        | version  |
--------------------+----------+
 fuzzystrmatch      | 9.1devel |
 hstore             | 9.1      |

Why does only hstore have version '9.1'? Any other modules have '9.1devel'.

If you provide a $(VERSION) variable, then a line in the control file is
automatically added at make install (version = '$(VERSION)'), in order
to make life easier for extension authors.

In v7, a line of "version = '...'" is added at "make install", and removed
at "make clean". Also, if we runs "make install" multiple times, version
lines are added repeatedly. I don't think they are good ideas; we should
not modify source codes stored in git repo when we build them.

How about having *.control.in and replace magic keywords in them at "make"?
"make install" won't modify files at all, and "make clean" just removes
*.control. It is the way we're using for *.sql.in and MODULE_PATHNAME.

Some extensions are missing here because they fail to compile on my
workstation where all the libs aren't installed --- ossp, xml2, etc

I found xml2/pgxml.control should have 'pgxml" for the name.

--
Itagaki Takahiro

#67Itagaki Takahiro
itagaki.takahiro@gmail.com
In reply to: David E. Wheeler (#65)
Re: Extensions, this time with a patch

On Thu, Oct 21, 2010 at 8:14 AM, David E. Wheeler <david@kineticode.com> wrote:

Might I suggest instead a META.json file like PGXN requires?

I think JSON is also reasonable, but one of the problem to use JSON format is
we cannot apply the extension patch until JSON patch has been applied ;-)

BTW, does anyone needs JSON formatted configuration files for other purposes?
There might be some discussions in "Standby registration" or "Configuring
synchronous replication" threads. Module control files are so simple that
they don't always require JSON format, such as nested variable. But
configuration files for replication might be more complex. If needed,
it would be reasonable to introduce a JSON reader.

--
Itagaki Takahiro

#68Alvaro Herrera
alvherre@commandprompt.com
In reply to: Itagaki Takahiro (#67)
Re: Extensions, this time with a patch

Excerpts from Itagaki Takahiro's message of jue oct 21 00:01:59 -0300 2010:

On Thu, Oct 21, 2010 at 8:14 AM, David E. Wheeler <david@kineticode.com> wrote:

Might I suggest instead a META.json file like PGXN requires?

I think JSON is also reasonable, but one of the problem to use JSON format is
we cannot apply the extension patch until JSON patch has been applied ;-)

What's wrong with sticking to Makefile syntax? Are we intending to
build a JSON parser in GNU make perchance?

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#69David E. Wheeler
david@kineticode.com
In reply to: Alvaro Herrera (#68)
Re: Extensions, this time with a patch

On Oct 20, 2010, at 9:58 PM, Alvaro Herrera wrote:

What's wrong with sticking to Makefile syntax? Are we intending to
build a JSON parser in GNU make perchance?

That metadata isn't *for* make, is it?

D

#70Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: David E. Wheeler (#65)
Re: Extensions, this time with a patch

"David E. Wheeler" <david@kineticode.com> writes:

Might I suggest instead a META.json file like PGXN requires? Here's a
simple example:

I don't see what it buys us in this very context. The main thing here to
realize is that I wrote about no code to parse the control file. I don't
think the extension patch should depend on the JSON-in-core patch.

Now, once we have JSON and before the release, I guess given a good
reason to have much more complex configuration files that don't look at
all like postgresql.conf, we could revisit.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#71Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Itagaki Takahiro (#66)
1 attachment(s)
Re: Extensions, this time with a patch

Itagaki Takahiro <itagaki.takahiro@gmail.com> writes:

Why does only hstore have version '9.1'? Any other modules have
'9.1devel'.

It's the only contrib that's not using PGXS but instead directly
includes $(top_builddir)/src/Makefile.global, and that file contains the
following:

# PostgreSQL version number
VERSION = 9.1devel
MAJORVERSION = 9.1

Then in contrib/hstore/Makefile we have VERSION = $(MAJORVERSION) and
that's what get used to build the control file.

We could decide to "fix" hstore Makefile to look more like all the other
ones in contrib, but I don't think that directly falls in the scope of
the extension's patch. I could provide a separate patch to this end,
that said.

How about having *.control.in and replace magic keywords in them at "make"?
"make install" won't modify files at all, and "make clean" just removes
*.control. It is the way we're using for *.sql.in and MODULE_PATHNAME.

Thanks a lot for that idea, it's obvious now that you say it. Maybe I
should stop working (or posting patches) that late in the evening :)

It's done in the v8 patch, as always based on the git repository:
http://git.postgresql.org/gitweb?p=postgresql-extension.git;a=shortlog;h=refs/heads/extension

As I think we're now on the right track about PGXS integration of the
feature, I went ahead and added documentation about EXTENSION and
VERSION.

I found xml2/pgxml.control should have 'pgxml" for the name.

Well really I'm not sure about this one. As Tom said there's no need for
the extension's name to be the same as its directory or even file
names. That's why there's a 'name' property in the control file, after
all.

Now it could be that we want to clean this up in contrib, but that's out
of the extension's patch scope in my mind.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

Attachments:

extension.v8.patch.gzapplication/octet-streamDownload
����Lextension.v8.patch�]ys�8��;�)��T��Hr|�p�qU'�};k9��Um�
"!�c�d������@�L���*5cS�����/�/�����GY���?��{W���+1	B�S��g������3{���a��}z�~4^a�����@~idh��������h��x��������%������?.���'�gP�l���O�N�G�`�u�>�(j����B`�&���o����/���������'_�G2��`J��[E�D�����Lo�������h���+�Ouq8�Z.� �8;=����g��
���LD2�#(��\��7��7����s!�7�W ��g��`�E�e�S�I���������{]��^�]4�(�,b4
�&��4�#q�Eo��B������!
��?J�]���?��Isx�_o�<��������xc�1�z2���:��y<e�6c�<�f<e�c��g������g���4��c���U1�`!+/+!�����$�����U��l��>\B''p��~���]��[�l���"���j�*WK�kw�@�S��5�����rFp������'�S�=�xF���|�	T��<d+�W��Eq6�<\C�X-��
�Y�����Q���.e�+���t����������������C/]&YJ�z��UkVs��S���u��4��f�+�Ew���KJH#�on	�a��8���"�5��Rm/{;/j�+�5�a�j�����2����f�����x�iYU��dK�TP�9�t����E��^��
�*���':`1��w���8��]r��
�����?��������(�� �������-����HsK�H���?V�'���&|,�L,'q��OE�
�k�B�/�,��6�S
�w\��8����\�.Zg]�����V��u��9[�?D��k�M��z�+
Y�S�Y�mE��X�8�	;�A?��aQC���svd3�3x^�Q�����[k�z������-XvZ;��kd�s��D��C��]T����<o�8���� xl�z ]��
�����������K���=�r+�Q+Z�u�����ZM8B���_������������I"{�.B!���&��b`��� �/!�����������G^������3B7G����S�0�������_����#z.�\ZV�Kr����]�� �.�'��~��L{�0�	P�	�k������������y��� ���B:-�������'�8��G9���
�i]�u�K.[]JV��3�3����
X1� � �)�2-�9��Xd7BD:�����$��M�*����v�<�
�����Z��
D���R��K��_@e'#�z~�nq����������U+ 4K��W������*(	�P�H�ITWb�}���`	�f��_��i����uD��^d���(�T-Hf��b
��B�B����8�&�M�_��%aE���4�w�K�l�L��/�<�*�R�T��e������4��6L����|�G=���E;9���A\LPQ	`��v����j�@�o���^���A�/nM��#Ldc��T����;P��N"�q�(�������(��"����)��i�.��)	�����]��g
I��v��BY�|�9��M��k:
>����Mb�k�B�����2&"����<�S��)O�����^�U��k�(N��n�o����y��+�7m�������t�T�b��eU!����sk�{�O�Ya&���0�����^�S�	�
F�iBm�ZoE��O��c)���7�O��Hx��Z�a"��Qq9�*������T���$��6+�����UZq)���~%�a��4 D����nX(�E�gS��d"R�{�F������$��$������s���`����fk�*��%bV��3�
/��&���
Jz�x*|�3j
�
K�3�d;k�D&�s�� ��YWh��;��i���vk�+��erVG�=�-��[��Q;
�����[��������e�[�#;_�[���_~���>^/p��[C��+����TYZ�r�
]nq+�VDe����%���nt1�n*�� ����yn����6��o�Nbcyc�A���'b��hu��Z��1'O�������#n�MP�
���}I�tJ-n�G�����W����� cs�%�6�s���������M9ci�g�s���8Z���$����9D)���=�H=?�����uty<�n���� Y�������LW,
�I��p�%g�yXX���,UT�U��i����^W���M{W-V���c�Wt�;�+���7�+7YU�s��ae\�d�������#�XY(\'�i����g��o����N6��l��-�[M�����b��Tc=�s������6+*�AT^Z�Wg�2��S�o9;��#E��S��b,�t�d��c�?N���U#w
�����dC:D �����k��/X��A��7X�
>	oW��'�7�{�A�{�����e"�F��@�<���
��I�`�t1'���r��l���\L&�-C�����O�t�qXn����M�3����i���c��j�����$��"!|P�f�'S�&I%2��Rc#�x��e�`7<�$��E����^/�Su�x*��
r�rX���}�Q����^m�mR���`���M�6Jz-� %���V����f�u�h
��w�6���*a�YVi7�B.3<Oy%we���R�]�$���F���7���"�g�Mv��V���P��c������;]�*c�����!:����\�����u�)C��a���^@s��~UW@u��UR��)�>F3A;@����0�_��)�@G���1p ��Pk�FM��dV��]+y�{�+^������uD�3KE��*�y�i�R ���^�H#5�����6�4���������c�=�Q�S���(�kr��gV�n�����U�F*�f�M��@��`A�(m�M�Ef�����?���������c_�t����v��+�F�����C�]T����<�����hf�!�@<��&D��7����eq������wd�
����$���O@�i<:�z9dc�q����
V����o��nT�w0�?F��{�I���������[����w4J���0�80o��R�~��*�rax3E��;Z��.e���5�q����E����5�>�ip�+���w5�������o�N>���Y'D��*�^f�������H�@�����K|��,�14�P�6��������������5J�=\�[��y�v{�hk[%��5��JQ�"����$�?��l�@���Y�K�����]�~�m�����m�xO�?i
Z���y�j�?�[5T�z7_��p[
/�9R���`����`j_���==�F�0�P�#���#�R�
�-*�Uc����������8����4��J-
oJ��������G������`�f��������HED��X�|�hN!��m
P��
��H��Mz�j8_d<�6o"�,C�f9�I�-w���}���<�e�C�i/�k(�=�7����������&�����3���m�\��\&�u*��	�����^$M�mr�mM�1���=��e��5�(�{\����i^���q���cc����|,�a^N������}(@GeE
��U�~���X�c����� �n��,�$&�gq:xrU�7�B.�"�D�mH�x�:�
�/A�xN,:����3�D��}����������>"��#h���B�b�[n��]
�y��V��Z��V�Q����?�${P��Aa�`�1����<ID����fE�;���Hw��	`tj���W��{;��zy�f���;�t2_-�r�r����8��2�Dp�5�s�.��!;.�i�$�����u�����{8��d���km�lg�e�����2��9x�{��z�
,w��5�D�����s5�����+x�f�p& R�IW�*]���4O����G�����[��!�����t�*��
��R>�5�{,?N����?�%���G�B1��WD�W�s�fLO�v^���T��R�����Z
<i���@��p3T�[��X�4�#:y��o��EC<���d7���_��]�����q��Gwp���r���K�q<��4J�B��Z�3�����-7��w>���`$!A,��e���=@Vk�������9�wWNn���1�aW���{�����X����
���s�4Vcs�`~]dt�!��FOJ�-��
L�r,[�����M�������9L(n���z��;��X	�������K�h9?����nz��30���G��)��~9(�T���I�R=�(�r;��E [�K��9u�+��L�&�D/���*+O;"O����p���J�R��L�������"mi-i�������6e5�#��}�)K���<�u��\(}H��{y��1(��{J_�3���������ZZ�H�Nh0�>Z�B��'������KSA�)�����~�g����
�mQ��,^�m�R�	���H$<!%m��x��,RB6�qjT5����$�1�������+�d���6��F���&v�o�h{*�M����:��'�H��x�5?�2o���v��UA����Tr�I����+t���Z(�Ci������I8pD�-f���|��-����e,v!M��y��P����31�dh�T�	�T���Kb�=P��xd�j������P�av.�
K�nj�K�2~"���jo���C��j�����v=J\Ae�h��J� ������T��,�{l�Py�(����$���Nt���3~����1���9�~da��I�,�+�[{���#h9P�M�{^������P�)0'�I��L��]�~�h���_��2#�"�	�����v7|1��0+8�h�&qx�jS;��P���cNL�s��#O_�����d�1����	D�`�"�U�W�a�A�&0��:��N�5y�Ee�
ys����w�B��'�e����lx|z�����>_�����a<E`�P9�����&]���"fsk�W��D��Nn�����S����^�3u�s���ZMn�Q���LY��O���$�p���{6�u����<bvd{�u��R��*�����8����"�O��d�+�R^X�Q���f7��?��|T!\=�����N��l�����
������}��`�`�����e����^������G�X��Tb�:A����V;*��s�ot�Y���+��6�H��I��,)
�����������b?N���n*�TqN�AF������EK�G�p/��S�������c���i��1C����Da�����9S�]�}�R����d��m)�rNX���\pIU���)����&��Y��uZ���Z
L���H=T�aj�.��&���
Y��(N�p�%��(ffy�`S�-���?��e�
&���<�Hk�bv���D�:��H�1d�%�u�T�����5�"�t����c��.��JG-������i��v���k��:�h��3�O�z����(J�!E���^?-�����R�|m%a{���q�bdk�;����v�0"�����~����K�A������F,��ip���0��8��Q�=���_qZf��0���U��r�s`���Qh"�9f�>�s��Tw�����b���l�����#*����Z&Z��s(�X�l��vO�u���EkD���9R�W�y�f�Lkb�So[��B�������
iCz31��F���5K���5n��J�Zr�l�
�D��c�9g M�9n�xlJU���d��#WNc�v5��A�q���kdug���um������G&��,K�k�n��7���*�S����&C4�W�O����(�+�&�owE��`�s�/���{�[V�Q��t�,n�9��lL$f�{r�;;��I�_W|q��x�v��!xA�����'?���s����p���~�|f�^������\o������j+U��1O7���^�L�����Q
��+]�1
������s�R���O@�~�_��uR�[7Z�?���;R{��;gL��:�
Q��f����\?4	R6[�� 
4�_�`KP"�P�[y?�"i~ ��Y�?d�lcC��%d�v��v�i��l��/p��6�(��U4��hg\��w,U�������6�,q��}61�i�H�jb�W��A2�$�h����B��%c&��k��n����������]�:u�����k�6�\��+�T�w��i2�B�V��u�ciM�U�(��9v�\�i���G&�@��E���G�X67vH���;O,�BeK�p���}o����z�L�>��
��p��v�r�t��.�]
�W~�Z�@���8<�CIZ�Bik�P�t#�����-@���g�2�u�g(Tf�*����0�mv_aRh�����+t����8HF�
��W�z��;'YCd�j�I����*�+'�*X�������Vl��j9���_��.�������-nNc��`4�V~MB��"��kc;mE{�Jv�#kdl�L���$��#X���L�9BU]��V-�|�AG��<��s`��/	���a��|�AE�j*r>_9t<����Z1�N� �g������Io������!����/��IVE�0�lHf�p�fL�������N��u��}�G��;�>�_L��@��Y9�DuG�3���6�g�]S�M��q�q�^}���Lva�A�Sn<���4~��:��3p"��Ny�YS�����e
���GC��G���s,wz?J�� ������K�ZV��a����V?2��#pO�g�����u�G^����P�����=i{����,a����iv�*�� ��O�\r.�`f<��5�����R�bOdPh���E�1�m5DH��h�[2i�m��#��� F�$���g_`h����Vz���q�*�!p2+)�����-:�-.��S
oy���6R�m�����?<a)��i��a�#9�u�A����&t
Z?	b���F�"f�t�s�����
��4���)JfN�}O��������� �Z\����a~��������J��Y���i��L5��@P1�~��eP�3����'��/������F*��x����$'v�{/$,����W��T����1���$��'D�dQH���r2�.��N[���8XI�/I��}�g>JZ�5YF����ZBb��p��8e���+Tv�
���+����uYvb���?��Fi1�|���+:f�6�i�nJa�e�����`����"�6Fc��0��]�E3�0��k!.��4.ht�E��H��6��C��Vx�"�,CxY-Y�$�`���wt8�b~'���r;�g'��u��g����s8H��*R��gl2G�gk�C�H��o���+��j���-	�
������y�l!�s�h4������>?k���,Nv�G�:�D�O���BL���DSwCa�������q
��\���x>���td�b<o�����\E����n�R�2���x*�<
�S�	�Oo0���d����`?����yM�����+����`�L�Zx�3<g����6E��E�����5�pT�7��4�O�����������):��f�������,�"
d�s2�G F��'i�7�V�wq���q��O�w'��A���i����C���qo�����>�����r)��|�c���v��I�S-�%�w���%z�����}��
�{��2'��t1l�����/���d���?%m�~AT�:��N��jsao�s�uX����|��I��S'���g��~�gv��}?�=��|���fu/�m�x
�����v��c��8>8��i��n�9W�o�����Q��Q>}���Q�9�0��o�[����6���
�������2!W����L�*f��{!�-#o�7S4���lLf0��#DP�����<`Sm��u���1/��P���E<���&�s�Y118R2����%�`_�v����$����vrkc�����P�����+���V��>H��P��,I�������$���b�Y%��D�SJ}��k����c/����'����"�^��lN���yW�9���6T��=��~�����4v�\1./I�o��bT��c����P���!	���!�hYr^?��!{|k�R�B`r�buj���q��tt��>@��r�H��;On2e#��Q~\�`h$O2����Cc
��o��kK'�:�a_�V���	S�xk���	"���	>T>��9o7g���D��De���V�9�8(���:M�N'�`����Z��-��\�2k+�v����{�O��Wg��0}z=�UnV/�)�����I<�e�x��{�������IP$?/2�Q������X�u���o.�Qr�7�o��S��R��)vJN�@=S����*j�w���k������wNi��)vbl�$��R�f�G-1�&4��$Y46���� B�2�����`�B�"��m|��&�1�3~��i�a3����"Q����0�q(��q
�px�<�Wn�S��qc�&������UJ_�d�2O�I���i�;in��������Pq��x�������dW���pz:v[�*(�����;s �8�����X{4���=2H��}<�8`��Z��ytO�7e@�)�b�oE5�`-KSD=���)�o2 �����
>^��x<��}S�O�%�5F�d��	�J��kY��v��tpV`�CL+0e��A�j0s��-T���7	���	j{���
?�5]\�����������EMh���������������i�� P��a�6�'!!��*;�]����Hn^�~�-J������>/��_��4��W�`���l$����e�R���\�������u=��9i�3����s�������?����k��N�3���6�0R������0�Qj`=�n��=R��%�_D�����3_Z��=���:!8�x��G��,�K�����2K�&��#4�o`�����x �z%����KFxw_����_=	���a�`��aw��Pkk*N{�qL�)w��N*2*��,6���~t�6���wvh+EW2���&�c���pj$Q�r&���V����X��u��U~�tce�S���.�9q�s���N�5�+���z #������m������b�S���N.Q+�d3n�<�(�}��EY���@.3
�|�%�2�X�s'[�����i<�����	����1n|D��<��,����������p�����p���4J�N^���[2Qt}��~u/����x.H�U7w���v�v�n��{�M������>����f����}���?�2w�+�^a��IJ������
���&�r��:����'�O6�<!1��iH�	�p��67y�NO���J���/�N��f�ei|��9�C0)SOB�����8
$�ib�� 
���d7���4F�x��
Z�Sy�����3r�C�!�6"�N�7�ri�v/�!�Y�2VR/��I�(b�N��$��m����Z�� ,�������mn�y8�NU��f8.���sJ���l�S�Y��F���������C1�"����<�l���06������X\$���������K�O�N�����(�����`�DY�</?�e�Z���a��aL��sNSB�_f����6T��p�����Q'��l�������Sslx	s%"5��~,���{����y�~�e��'9��D�;���S*m����	��������#GO�<`���
�q�\���	(MB�}&�.��n�������t�a�����W@��O�zw��1��`���X�j��7�M��&��~��oT�?�Y��y��R;'g������������*
MN&��a�h��	�9>9{Mp5jc���7�q6���Ld�`+���ic3��;\�B���yf&��k���
�����#�6.iY�I�E������7�� ���]�3H�����1�e��B��G�e�=e&\�J�����v�S��(�F��z�n������?bf)�!���@�?}h�K��nw�.�[�F�^Q�$���6"��
�b��,v�FT�����S�i���Fy\/�N�[�������,I��mt�C��7��uN����@	�����}*���hY�B���<t]�M�[�)|r�l�>�����M�]�������>.�|�M���u�+�y�~�7�u�*��W��#���}��QE]�8x���"'�g
����Vna� �W&Hpl�A�S�P`~z"a��|,!�{����G�;Tt+�`�}���l�������������"H�:��"�6�m^4���� �"���wG:_Z������t�������Z����w��� I��)���a��d]�l*��Cl������*G>7R�.�(	!Eb�p�������h������z��j����^���s]�p&( �u���|�v��R�+�vW����7�{R�+�3E�b^�����b)�8���+D��2o3�
��X�#hi��Rx��Xh@��&�</��\8\j�h���)�� H9'o��!Px���e)�%�b$����������T>������B� ���UO�����WJ�s��B�-l����n"��'�'��e���a���gd=��q/�[��z"A"a�}q�$�����+i�lXQ��N���n�|K>s��o���eF����E�K�����fh�W����
�w��a���/#o�6�f���]2To�1�����>[H� �����|f�h�������FkSr-$�.���bnm�#���YL��b�%���r#vr�����_���|��Qqe���<��v|��Be��)\��+�b_��gg'g��z��N�������$����l5�UWTagH���m�a�FWI�+@#�)�:������n���D+8V�/��������y����t�ggs�m�-���������+=x�a�W(Y�]�vvmXj8�����.e�{{>�����v���xw���$�e���DN��kr���uD�
��g���GwS\����|t�%�N�*�4AoY����b|��&�9��c������Z��S���3�[��)8i��N�F+pM�g��\�,o���������|Kx���(����l��KFb�1������
��h����h��������T:�9��
/����M�wp%^��Rg�]S��:�}*�&����vdo�X��4�\j�c�|q��r�&-�Q^F��$	�x����%�C�h���^��
���3'���q����?@�J����_O-�T�A�8w����H3���F��
(�Z�L�pH�i�n�N#ew<����wrz:]
V%3� u�	q��RB�J��sg���,2.��'T90����Vg�2�%��
�4.���2������(�fk�C.���4���3�vI�`C6��@}����-�S47YWr�f0�2�����XW�KWG{@+Uyu~c�U
�cf>4+�eG$�v�
��k�p�����@�~C��U��<e���sTD��sU��B�"Q }������cp���L�c��4{��h��z?����b����c�/�\��"Z���V�W����t%�>/
���}�(��1�|�9�`kD�t'X({u���4^i�=&���`�N���W��+�@���0R�[�.J���$���tS	r�$S@����M��y��x��MY���������c1�X����[��h=u�<�K���sp�����:ov��~�D�+E�K���'������S��V�R�vj�po`�%���8���D�[7�i�X���W�=�wj�Jb��K����t�N/�n��]��Li����QP����n�W�-���m?��V���/j��3;�,>�@����ad�������H�V�?����
��H&e��'���x��k�����{�[��G*������H�SQ�_�DO�{�_f������H�5q�t�;$���z�CBg�>>��>����%�9���f�~� ������YS������7�5.������R�x*�i6�"�!�1���xh}2*=���������:B��9m�1�s�:���g����&��+�_>cq��bWv���ck������$F���0OCI�������K0eK%�Q������s)'�s�''^�#!���#��\s��"@�
#�T����/DZ$��s��b��-������bq����:o<5<�>��%i����Z��$u>s �f�B^��HQ6�w��_a/R��u �����Q�q��IN?)�>L��&r����Z��������z�h7�9L~�0����Q������N�O��5�4���%J�w�Ek�����
�{��b��H����MW��4�o�k^at����d\Z�G�]z���\���eU��'�,X���~21D:Z�`�#��=��)�_{�97��t���Q����|������ �UbP'�r=p�Z��vwj�F�{��e���_63NGJ�������9������w�"!��j�"0�l���m��Q6�"
e���!o���v�����K��(j@N+����X���6�X�D�����B������@g����x���I����U�^�iLTf���*&k0����$�F��yTM�`����w�g��[��:L�SZ�B����22�1T���	�b���\��	�P$8���*<���i�# �z��a����H`�Y�N�
�R�4���F��Kj���V�Hm���`�	m���������SE:����^�sa��|�W2�Q}��b��]�K����%!X��83\yZ�D�I������,��|!��)@��b�v��nOJ�WF�p����0~O���t6.�!���(��em#�A%KsH<���)����o��dR�����b�8@�)&d����{�����J&%�s�%"�f�	���k;�cHZ�jP�A���b��:3�z�Q�i�s��6&��h���cj�b�<���d��d��e���+|���|7�#����[�o���aT��(��9�U����K|�v��5�c�g~|����+@��Y1gee�A��	�y���Qza���@����;���1HV9�����Z����'���^�l`�aG��]���M�r�����1�w�^u�xj�U�:�M$+5�-��+���c���	>cfR0���!xPU8
�*�v���^�����1 ��W�}J$r�<���Q�j:@��c[_Q�=����d
P�zd��aq�5�J�GR	���=�����#(����p(�A�"C�����)�����������N�<Bt.��pR\����)�J#x�*�u���M���*�K��?����g�$��s���M��w��Q���}�q6�������������m�7�6���&Bm���h�i��;+����|BN�0�e�!�W�d�I�M�Q�8��=p�@v��&����2�4���k�-�	�Qm�����"z�"����C���'����V�#ze��	Ps�t|l�9��Zb>��&���
�q�>Z�cQ��z�P��c�k#��C�;��8��P�\����wa�����v/��Z*My������w�1�x�(�����m��\��#�nY�::4n����.M���1�Ll���n���+A7u�(�����������|��ky�)��-`�0�e�pZp�'�m�0����g��s�u*�T��s+�t���k������0%ne���z������-�GM-kV7�mmMD���l��c��
���[�vm��_A���;��F�~.[�v����Os��%���HFi�Q��������R�uE�
������$'� ����vy;�;���T]�	��"�yae"Z�v�}A�vm*��H�8�j.��"�F��q
yi���m^�d���B�S!R����pt�3�������n��A��sE���Q��f���V�4ga��F��T��k�(Z�`,(�r��X�:;ycK��PD��i����n����bp�iK2q�;��h���i��s���k$jO+�Z�d�����$!�!�Hw2le��~������c;/�O�Lp�6��F6�3�!��G�Q�&�����������V��^��.-���?b����z�q*���W�qw�|����"����'�EsL2	w[���I>��b��DI`iC����z%��<@����4_��w+�mw(Od��{H1'zK�L������'�'N�!�"&�'>��u����T�^%���1�8����;��P�@��d����5.	%����N��~��)�����Z)�L00�|N�p5�/^�j��������������e0@�mz]Z��_M�!4�1+F�}�	�j�\~�Q���Je��'���6B��g�������ry����)���B�c�!'�v���{���5M������o�_*K����noH�;��88:��y� *����g��g2����`z� ��C�[&��N��b�'���������M�ie�����x�Q�l�������`�&�����R��_�I=����Yr9$�V}�1�o)]p|_�"���a������Y6SP���I��m����������d��hD}d���z����B4Zf��b�;�P�.-���OZ�nfR�u�����D����vRl,LD����������8�=���.��� �~�*Q�W%�~�/����
8�N����v�1��q�a��
|�o�������L���i�#q��E[`�����q{uJ��}��Mc�5�2�33=V���P���1)A��Pz������f����H��;|F�{���|�zeU���~R��nD.<�&�JbP�dLH��,jj����tk=��h@����z�
[2
�vd�f8�������\�SL�[<���1/L*<�����h�N����S��Gg�)0-����^�@��{]K�}X�n.���C�A�Kh�;�N
�"Qd>��
���)
��q}��������B���l�A��;d�93���������B���^��\)��|��(��;����X����o�C���q�������X��Q�S��J��]=��+t�u���G��
�d���:��7��)�b3\@�.-����
&��w���X�]�Z��0U��2�XA�
�}�[X!��/4�����Dc�9��S-���N�Z�����A_"e�zP����2��E�>O�f�*q�H���Z�&�I�W��:*V;6����\C��(��+��bl���-��oH�������|�Bi���[(M�}�b�3�����|
�Y�!�+��n��l�9�i����E��`ss����*�hl*�;z�>����K���3f2�n�yf-�@������]
����
b^��b��P2��tjc���d���l��B<�d��y
� K�0Y8r(@���!8\k����q��� }��������7f����X<���8(�i�j="1�%Upjp�����eRA`��\��&]�$_
'��tp7����/����
�z�����ip��8'�_�fdn���M�v�"�>WO��8$	R��� f�U��I�
�3����=�c6���7Xw"fzS����D&�p���nr,T��'��!��t�����$��GR1�REF����������<�����{.!K�xa_0�<�5a���H�8����qV���Ql�B��y�z+:M��+{��XEV<�<�;Y��6���U,%������tu��(����2f��9����g���U!����B(4V�%�2]%T
eL
��j�C������u�e��q���Y�l�}�9�u�;G	/LX��k���y�,�X@wE���GGP��`<e�����c��`EH��=T���������t���3)��#��(E\����u���
�L�&6��^�tl*��]�\xrE��o������J����e�{��R�
�rZ�����K�7�����v6�d=�D?t���L=��lZ�`N�5�d=#m���������c02|��
���X�~�j����q�z�j�u�MLh~r���b���� p��Q}�Y��P�?�j5�
�f����������.�~n��Bxp�q:Q�!������vm�6���)��J��%������t?�Z��U�����:Hg��$I�;=R� �)h�O�0�t���e����R�;m�)	����P2��X1U��E�1�("1�������\���i���\	��R�{-)AB����7oc��9�=�oc�
ni&����w��-�
(����wp��p}T=��WV��9B�G":19	�t��Y�-�aVZ�%D/eu3�����I��C\����j!�E�����:o������G�n�D�8b�'�?A��;�E��KJ!1�a<1����N^��i�v<ew����x��\�����`�������V2�5�7�:?�����1A�X�'K���.�a�"�3C]eU�1�$
������@��H�3��i���2`���Xu��i!O�{���E����Y+���,(�U���������M�a���������Z- i	�8$g�q���:�
�P�X�37�,vV�I�L�8D*����V^�d��s�F���ws���������r�F���T��vp�Y9x_��A��ud��i�S�������c��_����A�5����*Q�!�4�[.����!�8W!�gu�bx.��
�/.Rg����n8��przM�������z���3sH{���w�5������p�^u{$���n�����L`_c�mJ�8���
+2r�'<�Z������!�)�����(1�A�Nk�d��3^NX.��F�(�g��n���w1��S
��k���]��x�Z�=/R��!i�[������i,���mb�D�Px]-};g���{�*�)�]�q�)Lr%�/7U�G��}����{���X�O+�|�	��>-��F�9��������_~r>���1	'(��i��KK^R�|<�^���1�d��O����%�-$7���$9Z(�e=����m�{(&��W��J:W��������4�H����u��r�Uv���X8�V)Z�<�{�o����	S� �.��� YW�2�^�����P*Run�D�][���#4i�Y��`��#�|�����3<�D��KB���8�7b��Z���8�m����Z��9�z���b
��9�>
),D��,!/�`��p��}���%�����b��(!��'L�@Z�{]\WXf�t�%���[s�u.}Yn8��H>�S��pBD�%������-���8��'����!q�n�"�'�,?����1	X�������~ dP�${l��)���W�����F��O��	�B�7�-����J
/���)�}��������j<�q��PLF�~k��K�0[�]w���%1$F�I��,����)�������v;\��Aa��~���62�a�O^L�h-��o�#�������6��a��i��K������=�q�O^he��9<!�CN��I���6�X#��01����D���i]�r���S9�CF-��;�x�s�6�t
"2��<Fi�j�^�F��s�� �2�]�k9�s�b�A� �}|�~<�H��r�qt�
����j����C�i1�f-�:P��(�=���<�GC�Qp)6�#!�x0d7�
Z<���a������Go\m��YUX�H|��eYq��c>�T2M�������3�xa!�,��&hv��U��/9�t�������E2���-�:��Gh����m{~]�s��5��Co	2����g�r�QNf����#�[#������y�l=R�&�=ON���SO�51�����p�j�q�"�.o!;8��u'��v�\ddm�fa�,��5��b����A�*\�$l&Vy��8�*H"�Gu �!
7P��
K��?��,�� 
F�_�������,�|���� #��q0��\\F=�*���xF��I���+���2���f�W�3gGm.�Rvx���*��&�Uy�����'o�3��<*v�1���������p��:p���`R�j03pA�q(�PN�By�p��C�,�mt�-��F�j]�u�ii&-���T���	�����	���d��F~��$�v1I���;�9�� �&��4A
=�5sz��aq6�V�|�{+X&�
�a��|'TK��**��b�[�mJ�F;��1����uq����@���[��U6_����DN`�V[,JF�f���c�O���	�2�'��S�Y�p�K���U�>���d6]��?pF=Va�<k��K�}U�O[,(���1�}'%d�t5���Z~Mb��-�.��I���`��%����	D%@���`J�e�nD����Pl�3U���H0D
}�0��	)CYB���h�L�8��I���vx���	H������O�t�����M���Y�o�������}[^�
�s
k|���P�.S��8��/���lU����\�U@~�X
(y���U����p��g�i��4p�,����ODg�t��,=��s�!��� ��u�t����2�jL����g�f�2��n��6;��a��W*���n�	j4�<FR&k$|gf��r�-z9���9��#E�V��}HP���F\���CU��s���'@����%��S�)������Be����I��P�0��3��.<6�*.�z�x��AS����"��F`\�C��.��j���������e��N���l��x�wq|�|�:nfeAs�A�$f8��Z��r�D24"O#�X�,��������"�l�xP����������X��:X�(�L�R�1
�G�w	�����s��+���K`���}�5�o�����w	���B�S*���MRp���1H�a���9KK�$��}����"�IE��yqXB�������]��+eO�"C�!������b����Y-�U2�+
��I�E�i%��s-��y�n���
6�������w��uI~����,
J���+Q/3�S,�H��i��Tp�����4��s��A������(��u�yhd��[#m�����K���������U��n��L�q������p����h �AWL��HoB@B$���%�~��Z�HK����%��;�����DR�OA��V�K��:�����M2���CD�o����5��GK��}���yV!$!;?�6}��$F�r���"@AC�w��?�����S��Wp�M�W� ���4�(
�X0��%+�@�]jA���KaV��8u�9����{�.p��)�q�m�Q��S����(�,�M�Y��������c�cc���/>�p5���	rxD���?���.���*a@2D%Hk����L
�����x�NM;�16�/�:.���(�����c�I,>x�����c�$���mm�j��`�=Ei��OfH����P����v^
�/���;8�78��?�(�&����YG��x��g=~��v���!o������|�%V�o�������`l:��w��0"�����^���(k�_�q#�KB�}�@�/�������g���''G�����aa�b&���Don6����(!s��1��}B����4��l����;B����Kk�����w�@��j��8�
���A?&��=u�����q�9�t���Fk�H)v����s��h{B���6��ys2!eB���F�m����7w���������k��M�kziZ���~%����B���d��Vsu�a���<�����*zP�:�����Q�T��~d���:�A�,f��{om��dy���#9!w����1
U����ZA���d�,��3���%��$��� Tl��0n�@<�yQ��"���|a�Z��.����cI�2�mh/v��9���[��b�mp�+g'+;����z��K�*�.��H�S���S�\8������af�34)�cF��3��]�#��N��O��/��b�O�ZP�G'49��4���\��blr��4�tG����,�:��$2��)}��E"Z�cn��g������=�A��O�gH�#�|t�DV2��q����=W�L��hM����~�V�:5Y���m��<�u���=�a�*��LC�%l~��f��H��������'���u]���}��2�D�������;(QDIh[���������a�\��E|�����jg�y[�.����-p�7��9�B<�e=��C(�X�?�^�e�}
}���[�����X����������rv���� v�C��7������;����XE�}2�g���p��H�p�����#��9�'t�r�v���tIO,���h6�#R66��6�457�q��415l��+��A���� �fS���1����^pCo2�b�FC�a�;�{\��k���������3.)����g$Lbj
�y�������>{5�1J��!4�C3<��h%�w�7}����x��q?�m�;WPjH���D�47��&9�&BS�Q<&���jw^��������kn��9��@��cAg	<���2$^�3Pi�,+`��&�:/����A1o(m �{7�II��{X�A��G���c�Kdp�;���c��mfP����������\NI��T	E�,8.+�����'�����1K�Ys��y��������j���l:��>a��A�����7�4�`|��#�������l����?���@`�Bb�B��1���������V��<����\4�xE�<T1��/

��
���8�J�P/���j��)��?���r�����+"Z���r��
%j	��l�!��l����gp�EK�p��6���������A){c�(�H���#�uw��G���
M4�l-���xlo������>�Q:.��H���2�sH�
��b��a�����T����

��p�n�������m��<6�����nE}������ve�z���K9�>�#Uo��G~��e|N�|�L���!����.�4*#�-Am����ah�%4����� ��C��_	����#��TGx��r�uI�r�V��c�:l��2��HD���L�Mk����+���j��6��]����J��J������G�Z�U�+*'� �
�9����u��Y��	+�	��=A��U/a��q{����;p�p	���M#�c��tE?1:�@�?�(�/.�4}xr��M���D�#��i��lh�J'��Ae�$�e��5�����I��r�k;�gi���kP�X)����-l{lUdH��6d=�r��������x��=���l����5_S����&�����c��?a���&�Y�w}{�'b}�.10n1�H�9�K�e��MQ��#��`�\ �k�<<��`����I�����q���*a92-�����.���A<Kf��.z�/4��k3���.Z
�����'��cm`��5�A,��PG����(�>��`u��"n%5����[�f���y62��?�h�#�������s&�Sr���:����
IZ������B�sc���~���8w���E5'���E|n�m�`(r��wld���shg+����(]�Z:]c8K&�u�O�Y�F��m�NT�+�%ZO�0���	]���M�Y�E�5'M8Y�`<����w��#/!L��.��}~��_�a�������V#��V�g�����A�;��CvL_y��hi��'��7(m-d8��_�7��	&�.�w��8���?��N|h_������j`_�����R�Yvsw6��NuSo�#�v����w�`���5ok���b��I��.l�W2'�[T�6-��{�;��N�]j����K^�����2��nD{{U{��>�|Gv��E��t�f�`����7������-�N���(��rE/������T��� �V�Iu��,�b���������h����LEKb�QdJ#�A/�Q|wbN����2��y,u���m_ugs3�_{�=������=�Rgk���>�B�8�O>L�Pt*p�h����sN\2&�u���I )
���^`���S}c;�W�{�d��{;��������-_�W�]Ti��-Y����-9����-����4��r]'@; ��#ro=���
���/���k!��V!g�E�R=����-<��#�����7�����
 ���f�����kn�����������)<����8�|���x�K���(�
�V�Y�`��-��vp�^X|0�^��6��$�|b��N�t}���b���7M�5x�]�n�v��� ~�`����t��]n6!���"����;��0��d��PZ���2�N?��G����VT��^$GO�'/�ls�R�"�^�L�Z`�XzS�R���}�������k(&����Wx�������h����}��'��FD���|D��t�������
�K�m��[�K{;�������1����������:_�NTk�{Q���i���o�������d���Z�������K��f-�U$UW��S����%��pp;�%�G#���e�M�
�a���E;��4�1���eL)l97m"����77�����|i��.�Q�
7��%9��:_&H.V�[�p���v���n�I����Y=FUa���f����w��;����x���k�1�2�8�G 6�2@��h�(g������;��������V���D=���G�3y���;�f���Ef�����{
���9�>0�\���x��DA����(��T��rY�������+�`e�J��&�B5!��m��Q�1�7��;Pr��V0��:��B��>cA��S��}���Z���-/�@��V��
����n9���v��h�Y&��9$sZ���C�R��b��!8�P�:��@�|K0UlX��z��T���&���m�+��c4$C)w�4��.��/-�;ys�8k�=�Rb)��I-�RxJ�S�;����p���)��X-~����5��e1�����E�x;�j����N��n�q���s��w�������CK��,M0^*��@�*;�(�r���#���iq��z������>��Q���������Yxm�o�=6F�(�*����#tH�DYVSw�QB2���	9��d�n0��c@x�88jpN*;�������F��d�����K�g����T�ep�.�$m�S)2��������UL���I�s��Bu���I2;��3w��� ��{��N���ev*�{7�m�)��q���>\8Mh��"�=q����7�`����7'��-��_������j�����A�/(z��$>�M�d��d0��#F����v��u@�j�	������>0{A�����"�H��������bt��&�7_�u;���g���G��f�=�
��&������W��wc�x=��AQ�IMQF3�d���s+GI�oA�:==N���{�K�~?��UpC������F���/~���.��{�9l�'J������n�<jvN��A���������	�;�xC���F��>\H���SQ.7���Q�����\�����y�p�����!|:�o��v������������_�������Y�����������C��Pv���2�W
	t����u�w��y�#w|xt���'GG'?`7�N����&�n�^��`)���f���dO�W(��n<�C�E�g���+-1��L;h���m�n����x�s��Z�g����&#�����D���>~�z���*�Z,�
����V�S���ugc����:7~�P������}�W��������;C��~	��������Iw�LLv�(�6J���?������1���y��g^L�������9���g
��~�����)+B�x~��L��I_Q�#^Z:�V��|9U��^;��>w�`~�i�|�5��`���3��9������{k?eoo.���,��fK_�f�5Q(f�(`��gWeS�b���P����������5�6�I��E[��z,�mD�	�^����O�d�?�~�[�����yoVP���Y��#��>m�l�������kO1|��i���}�fQ�����vfw+���U���Q����K���%�H9)������
 ��)�|h6��MX��|G+�1E�?������� {���<#���:j��h���'����9�/��K������&�r�h�	s�]��V��5�4t����Tg'8�kt���6���:m���gX�y������o�v;��4��U�����������_@6����#��F�UV�"��t��'��
�E}��+I��A�I&�677v�v����Z��"����7���b���~�4T��w*�e
U�o��o�����b�:�����ot���-������Q�`z�f
|��1/��W��6k�h_�p��}���e�%�I�-�_��x~P�ye��d+��js\���y��!���1>��|���Nbm���
�j9U��z�/������=[���Uvy��k-�^y����fT��{���mG��Z��@�f��/��B�W�-a� �9i.T��]��blm�D[�5#���;������������F������<]�R�i'3oJa�zY����)�QU�I�C*��=,��<�`�����;J��q�_�����
#��#�V�3�r��t�i�7�v���F�� c�q��.����=�Ts���5 to�����~K�&Y�����|�d�7-0E���j9oP��E���%�a���k���B���jT��E��0~�8:y�>?<��U4���]��r+�`Nyr�������x�A��4A�TP
Uk�.�ouK*�:u2����R��j�Oj��H�&��zcb�����;T�����������F5f��d�L5F}�T�N���k���������8����A]W��_lu8�L'�*ad�X}��'��6��u�����r5y�t�:������ �J}�������2��h���,=v���t|M�m�:AC��x�}|{����'�
�-\���1l�-����+	�y�*cp��u����2��
p�������>x�n���������+�o#�������uF�c	����I%"���)��Tu�*��Y���#�~w=#��&���C�����f0]K9���
�� ���u���}�������
���}=�V��O���O*p��m������d bm�v5���� ��fT6tF���[�izC!�p����tc��`>���� 1U���(�8-���KF'F�Zg���B��s�3P�S�Ks���QS�*�����t{S�^a�$h���.���#Fb���LM�-Lyv��}��,�s���9���H�&�	���u1�&F�n�e�D��fi,q�IRQ������0�?C2B�i����j`��Q2�������M�I�^9�p�����I=������#gi����'��������
�3M:=����'/�=n�y�G��e����W�CR"R3P�>���
np�Kg���*��.+���e�y��p������-�u���%��=x�u��#��@^�?|;0o�fK&4���%�Zj2�<7�D�^-��I7�����P!�8���!e
F����\�G�5���2�������������@�#�{����6�1U�0�_�����y���%�d4���n�/#�u"��8�a��k+C5B!:X��b
Ru��D���>miDU*s�c'�����N��R����y���j���R�O�����l�ip;��))�(�$�LfLH�6E���h��)��1l)B�7�A*����������u�:�;�{������Wg<�g,�J��_(��?Q9,@'�IQ�N
��Fs��{8��C��@D��!�"�[����?�^~��tM�3z`	M=���h��x+�[\�\>�6������0��}���	S�0�!���q{Q}����u+ ���8*�1aCN���L@�����,.i��_��_�v��
���O.�\@P�_��U�����xC��q�^��������F������#j�0i���;a8@�L<x�uZ� %���<%+��q9}}�y�8�K��o��N�s�|��8:w�V}'
��a������2�eE������?`��\�y=Rx����
����K0�������9����=�*����d��b�<�����
{sE5Qk���4E�`��@���Y�F���/0���
������������
��u	�`UhJ�-3�s�����w��$p�
�BL�7�	e���i��Y
�
L���?�7>��ZL��\�������L��[�(I*�D������`���9�|��I-�Q�v�%��e:�"�5�r�NCWn*"���P�WG����?o�����wO1��|�5�
��
���Q�*(y�'����q����Wn^o|�T��c^9��i�2QN���K�[A)$����G�F)Q��#"�4����0w����1������k�D���������������C�#|�luoo���MmF�j�P<�N8������S��t�m4�:#��[����Y@2-|Z[4_f�R �2��ny���i�����H��t�n�wK�m�.�wN������9���40�tF�����l��LG^����0�J�G��{&�+�d�+G�����}gL������u^��nA`|��
�RI��p��N��"�P2�����v�C����zx�"TI
�`7���Dl ���>���y�0�qf&r��t���xg��W�J���9�,T��q�,#x9���[#q"bt(��f6�����dIptzWP'���85����)��@�5�c4�d��<�������h�@�����5�����B=����7����b��!]
&@�~pA�.���5�I0f2��AP�1��(]�
��eo;}��7��D�������v0��).0Ny���y��{I�-�5@%XnL\�{�������z&i�W�4e��l�Tk��U.S�8�����Z�#L�n�R�L�1���Ok���T����%���-.m�;p��a�b^�IK@M?z��OJp�!D�E���a=���5�c��1��S���zt_����$��n�/3����e��h�'�����n&�W��7�j��d�H�F6��'�
\�@����jmMa��1��HXt�TdT\�Yl�����m�k'V�fh+EW2�����S���Y��F��31	�����������u�o&���g�x����>g�[)v��d_A�w;Td�qbx��-����������F���N.��W�+�5n�<�(�}��EY���@.3
�|�%�2�X�s'[���1����	�#x�\P����4;+�R,p ���YO�f�yVX��X���r�~����"%*����k�V��qz,�m���i|Yw{�J�����Q|WV"%"Ol���l����I$�0��^Y��
�4�r`��D(w�
����r�w
)^��\C��1-�����>����e�~���2�5�<
�!/���Z���[��mY#������
�� �FUs��
Vmu��#�(��u�����f�g�v������	C���^_������[JG[%�l'K*�x��C>'���V0SN����s��i#��q�eg��r��95�����6�t)�����>�>�y��]�g)��a{�1m��l�u
FdZAK�g6]!2���5jj�d�0U<�!���w�����O
x���]��s�B��L�����"X�;����a�~�������9�N��{�dlh�Q�3���{-�g���p�|�m������J�K�,Hx+w�`����` d����m>���@�C�mF��������p��-�����xw49��2ig���M�Y
����K��S"Oo`�h�NZ�E��]5��F7!q&u�$i�T�����SAc����^P�(��i@���:O�sF�����b3:5�0|���x��r�������]D�pV���(�����T;h��`eE��O�P;1���l�C�.�!���s
Jz��uF��F��0��R�*��;��M2��tg�E�i�^��Cx�|��{�S~�l��y�v���/g��.yU�\�!S6x���
|t;i:|�r���@H����+�D���cy=���e��d����*c�w/��7���E����M[iB��EeP��1��.L�(N3�+���-'�t'���t���|����� �t���zz�/>t��x51����p�����P�3H��f�L|/�����QE�i�:����w��x�>�� Im�]$�����R@s�x�����`.0T�Z��Y|���p��
pO:�\��� VT��RQ�V*����6w��E�t�l@��:�����s	A�C�������bafj���V#�H������3*��
&���z���dE��}~A6dr�6�r��^�l�����K��U$��2^�4QJg]��#@�jT`)�Q�����b�(���ti���r�EZt���(�k����j����:�h�������G[n(B2�%���
$k*Bt���(h�eL��V�2���V���>���O/���<����yS�`�:h�y�����<f�%��W��S�X�v� �:@�tF�`��:�P�bD�vo:�`�$ogc���9��#�*�H��FTF��h
$rI���BM=�����xO����8(�����9��&1�7S�B�@%Q�c�B����^ty�C��tQ�x����<���=7����=T(f�'�y��24��{uz�����E%���������%�w���*��N���N
E��H�FD`����������BIb���W��f������l�������E�Wd�^Vh��Ez��0s�DV�P3b��>�����U#�����H�^�+��������_��P`��X4�[��`z��j�-_',(��G�>�]���W��o���d++f�T�}�9����d�w�A��s_NO������k[s���
�r:�4��������0FI�`*�+�z��|a�����*8�U~�{^4���;cy������
5�x������j�@g��h��]�6�U?��@�`�����a-������ss^%������������>���Q�	���X����<T�g*L�������C�,ZJVx�Q�v�M�[��b�O��S}"�-Q�\�),���zQ�����H���-�(E�[CQ� ��,w�WLNgZ;-�Sa]f2��~�?����E�v���8�7,��1w��&X�P�$��+]���q:!d���-�bp���'1�/�c�0!�	j
�KA�w`N
��lG���������X��Wg�}=��K�![�[;�'�r�7V��-�z>�-d�u}������
V����?��\4�E�+�@~i�R�+����?A���W�s<FT�t$h�T�T��C����~�#��>nn�#��)�T�AUd}�
n�.�A
�	bf���8��!F�1n�}�B]�:f:h����<������u����b�^����**�r��7��h�PL��N�����_�LH���)������`����"���76P�g}Z��qG��2����n������{p2�|�Mx����|H���T?A��Aq;��1��(Ng�]��c#��e�]IH�U����O��I�b�\�|a�x�`����
�f���l���_��S�v
�gG�<�nZt�.e:m����g��I0����Ed&�Mc�'J� �;P��_�������hlncv�1���\�u�5���bV�����iS��F�W�%'
��>E@�]��zT���t#M|���"�H�I�MV��>,��`�;V��L��#��9����ts���0���Z��b� �%HMh+%#��0f
�
M����Z��'�P��&�L{�i)�4!�D�F�[������C���R��l������8�����i<��[O���z�\(��'l:�,w������c6d���!	*��Y��]6�;�h�^��g�v��A1T���C��Lv��?
���r�D[*�UNr�n�C����=�6�=v��;-X2s�mG_}�����D����L2���`�i��$���ideI�G�_6��xrG��)�go�%�/��!n��a����x2)m��tL�]�*aD�����o3��`������G�$fu"M���*������<yGs��������h0�>�����37� c��3��y|��d7_0����^���!�I���d	�y�B*P�RQ���
�+G�A��~���A;iY�*^1�	�r,��)
�P���q�5Jq?�vp��[Vb�y����s#���F�K4�m�V$��*���e
��W&t8���.���w������qFdm
k���/lW����Brp0���ZA��exO��`����~��iaw��������@5cw��T;C�������yY��F���"�]���s'w��G\v�����+���y��a )z��>ly�H[��*�
���r��`>L�a� �:��0]:��8�����E��7�������5�4uk�O�N� ����eh��m#���.^Gd���LM;pdcaT��0�(��X~w�c�*�j��w-��`��zt-�ul��&�����D�{�,H��s�u�fL�Q�h��}�l0u��`Z���Yn@c5P���j ~�������e��~���������|`o��h��^.�M����F�|�6�u���	�>g��OG!z�0��Q��b��GI<5*���\���CO�c& 7W��SU��PHRw��r�}>9�A�:JJ���1	=<��������7Qp����d�x��v�����`7����wa-Z��s�0F��dd���c�>��{|D6�3�>n������)�=��Bd���,�IO�����?\PFr�u!sL��+(��4�������k��5�1��~Z���H�_8P���VT�0�fV^��L?W�;���[�G�N����,�qp������_r��R*�a��yn�#�c����a1�8N�����ZT��<`
����Z����#;-cY@�B��w��D���w����_V�ln����&[(�O�z�y���EszS�zKi���"����0�d���i;�S'O�}�U���`��������0-5�{�v��	x#�������	�t����n�E>]�����q�@Y@�v��
�,�l����K�h�6�������
�i�&*������]���C/��6�������kN�+�������������V�����Qwr����'�>��L�!�	L���m�����61�`T(����`'�mn������;~�t������I��>E����	��B���q��&>	�L���/��#����&��0hh���f��E�:�h���k�^��Hi�7����L���O%���7'��n����x��%�[��d��>��v�E�CL�U�F��Y�,e~�A�}�b��=wt��Fuc�!���4���1�����
��p�t��%G���*�UD�
�4�$c����`��0��|��V� W�Rdh�
�+@����|te0L'#2/�B��0�s�J��3zI����������A�����������)�R������)�% A���5V�6(_����%a���_1i��AC4����A�1QP��������
G���f�s~qzN�b:��C]�'�����6�G��9<e��0�K^1'Z�9R��#��~%��@1�����-%o�������-�G5P��I�W9~�P�a��0�Jg[�+���s���'�Y�,�V��,`w���c�����7�f�%tAc��q�h��
)�V��(�`A7�;��{��
���MH�@+��[�o�4����_Q�31gb���$���9�k/�<��i�����}�����C����q��ro� �������]��8������
�>�V�����b�]����d���#7v.}����nO���V�d�/5���J�r3�E�b~B�����%�����H4O�������
�j����b���sM�|�=$�rX4�H(XKw45
\�C�w	J7�"L���u}SA��>���%����M�)z���T,+�HI#����D��,���t\1��4�K�r0X/��Z��a�������Y	9�n$������8H"�7����R��>�
�Z��u�
��=6��F�RB��[���3���3����Z����!�����R�L��k�5m���I>����eV��CL����UIf�Kf�i	(�l�����
^T�����/���]Z7h�b`���ke�Bm7w���D�%�e�?k\��[�7%�q�K�)��&�#����+2#�X�����3�6�����p!8����T�8F���Qx��

z\���+v�4���y�h�}��'�c�dc�Q���K'���+�i���X9�?G�S�a���S�JD�P�5�1X{�l_��7��Gk��s=����5�p�{}]���Toj6*0Px�D���g'���������k|�:~��O������/�>#���2+�h���y���4x/
�n)��ax��t���,�#9'\�����m~s=*~]�2XHG}��X]j�=$��0����v$�@+�Di�(+.��e�m�X�
={l��4�)�y�����w�7�����h5�!�a
<�vF�^<�/YU\Q��j�u���O�����0�����������#������-�����'�l�e������P��tW�0��91�Js���A���}�G���1w�5�%�)���O�����p!\
��-�����~hBS�m��L��x<�$h��9�^K�M<��Arh��z�0��b���������Z����J����{�6J��]�I�r�t��������>@����e$�����w���#�f<uz����5aUg���p+�b�����(D���f���su��iE�83v.F���9s���E�+�1�g�g�gCA� T&�)�Od�x��$���&,ke��x��5��O�h����_�H6��A��p����_&�X|i[/9�#�z�i��e��T^�b���N�k�5�������G�6�m�����&"}�������,� ��^�~3�����sf^\n�c_���r9�A���9�C�Z�p"�����n}�csX��4���/Zm���>�xU�J
��]$w5�h�0Z{[T}2��9�z�#�%O�"O}L��	��������S'����^��S��E���������0bH�T�`���9�	� 6owN�vC&����-rpO�@��M/�!���jT�n�}��Wp�{�1D/�!�N>���?��/�A��
r�����_uo��w������?|�WyM�<�����9p��5����R��k[��X�H��)]�GJWK#%��c��U�^���z��{���w�;V>km�
�Gi��\@�<Y=x����gd-`&kum��-����7�`��-��K�-�)���ts|0��:h�[�?������h�����T���"���<�]�pq����3D��������Z�_���`��
�����������acyM��6�b�Nl��I�>�Q
��na�$K�aNe����B�������e	]wfs�>��k������U�Y
=d��b������IW�cf@���W����}��S�'_		�/��C�i��z0����A=h;�`��K��(dh��c���Q���NT��^��$������FW�=z'v��t5��|)=�pv;���`���`�#������
������c>������&j<��U��hZ1��"��6%{U=qKL'mcL���vF%�O����c�(��98B�$����^��j\���j=�R��Zy)�j�9��W�?�8Dg�sB���ut�p���H8*D�E*M�tS	;����k��dKOf�Y��+^M����sT�<�+B����JO�i�RI�|�E�)�������MG���b����Q?�,���K��$�B�M�m�&c��e�����*��M	P�� }X�5��0��)S�me�k�v�BY����g�Q�NKS���z_v��J�:|����[�����8^#�����~�^��Q9{��*�Y�����6:s���w��n�x�� /����9��i����W^b�����Q��&�� ���P'9b��<`��}���3+vrv�<CZZ��
9��Giv���^4���w���i���J�j�`K��9=i�������6D��~7z0�F?����4�
�'=��R8�I���f�I��?�A#75�xB��Mnq����*�b[�[��Vk���z�I/%i:�������a�fb�>����,��}�C"]T�1IQ
��Zv��g��T��P�_�W�Y�������SG9a����.����~��DR{{�~Y���J-��o����GI�<IX�A��0��t���T�3�{����4����m�v� ����a���xkQqV��Y�\���&���]�`$���@"a�Q�6W(�3�?�ukw;���s����I;�����b0��Z����
���4y��k�$tn��W-bf�d�G��*��l��
�����{�}��_�	���f����;Z)�S��Jv[+Ek�;{[	�n�p���F��	c�OfO���>��~��-<�������n��������&�N\k-~��`�>&o����>#������"��v<�Ncn����c����kko����w� _���z�����^�Hp^=`���v����fv�?�Q�'�o��=�x��?�s��gk����m�l�8h�\���l��s��SO������J�U�
����b��8]����mo��}�n�������T�T)���=�?����pe�f}�I�s�>d�[��Y�9������b<~������u��C�>�'���
'X��80�gE�nW7������-��U�'��e�������_�s�ADe�F~�\p?_��3gU�\�%�����:r�����^���%%%<�<5:�,���)�@���_��9�����K\B���h�Kb$���t���2d����;,I���E�����B��F�,�<��������W�oV��s���y�
�:�
��/�������o�C]y!V��`Q�|,@�v�e���2�����y�}�}�\B�d��T��%��{����mgoq�������rw��w����b��J��Rl@L2��Y��8�P��s���6�������h�����R�O+�o�Br����Q����G���"L���Y�0��_���o��@�����8?	d~�����N���#B��h�����w7�#�{�w��P�rO^H^1�\��4���<�>~R4�;�%�I��r9��o9�n�����>m4����%M�dG3�*����mm�#���	�n�����[���[������:U����@0`��I�Y���:a���
�}H:�?��������H>��	���8��$�U��`�v�6(�;����n%2E��X��'��}������8��
k~.�_l$�nsu;m�K�8l��pn+�p�����2@��I�)���cNX����8oz�e%8>Q���FAc����tr/����Ig�)�N����5�8�4���VW���.\;0x3jb�6|���5;8pS���p��g��� �O���E�1��40H7�F�� ����o�V���	4��q�%�
�s�u�==��6�����&�,Y�l7���|J���p����I��i��|��#���r��,�5zD�����8c#�]KF&����q�H�#����cq#�b`^m�y���M���{�x>H�H�f�>Ns<$�(�L=Q)d'M&�J�+�-��#'t����%Ln�<:�'����ZT���K���M������%�"�����O���2�`�9tdvO��?3K�[�R�l��A����o��u�P�_nH����<w�����>g��l���z��
A��&"��d�	Qq��V���H����,��M9�4���/����^f��y����q�\�
>��G+�������������ha�2��M�Z,�)�1XL�Px����AN�|���Gu?W����l	#D���(I��Ul��g)�.4c��9�T2����*�
��s�\��F�*����^d^�!�k�k��7��
���F`Q������h��`F������u�?t�����Q�+�7\�"?{��4�l���+>���r�3���s]��o���I������D}I?6��G�(U��}��Q^���W��������fNK��X���)=���w�7���2g���=����~���/������&���H��{�{��(��o�I��r�S����$�k����IO���������z1����`�qw�H1R�Mrgr�K�����^Vz������u�v.��r|���NXO
��u���N}+��l
��y�85���L�2I��$�'��-8f�:��d�kx�0�?���\��P��:i�
[������.1�<3�V��EI�	���R���T��'����P�g��w4���G�f�6p�}�����#����M�p�+�pb�����{���]w2@��������g��l���{�:" 
M�{�����yL)�_���8����e*���2����V���(Wp  $�������@�����
��u��U"(�_F�����E�����|_�Y�}X�#+��o��~.�5�d�P��\6����x�TZ)L��&9���}��h6�"T�����t0"%�)�"������Rr��h��A��x�v�����c���GT+��GB�W�h�w������������WK�#�W��8�f.
��[�����96B��q}Z���]���k�Y�aO���w`���i]P1ba�c�a������5�������>��7�p���M����E��2Ko.��u\��072�7@��6������M�v%������O?��l���~<m��7��f���"!%�������Vf?B#�V&	
J3�K^�������wz$��
�����]8�!M'�C�`�J\�K �7�^aK�.����
!����r�i�I�4�/���
������ci���dZ{*��S[�Cs�0���B0�6��h@�����q�<�6O�������wZ��7���~�6H6�):���v��ex}3���9"eP�e{�����	"T��9�LF�������c�s-A�)��l��;�W�AJ�����2	#KV��c��������%�:
�&�/�^��~��;$�6�$s1f���J���f�c��&(���qE�WP5Ei���RC�i���')���a����������KYk��x����)�v���/�dD$O�<�
5��;x@�a^
��(DN�r0��-p%q�_���i�p�8���-1u>s���^aX�f�1&������g���R���;�����\�?Y��
^5^wN�Z�[������U�m�`���G3%n����
4�m���	n;����On�)f+�
��66smtg�[h#�����5��J��P�?��2s�	8
�/8��|8V�J$��_���R�5�|18���	d�}�!�S����%�=�/ �mo6��,[����W�9�p�3��m�}��~�C(�� �~b$��F��z����'��q��c(urp�h�;�N������v����Y��5eljXd&U.�l�M���u��T@*�s�Ed�?l�jE"j#�Wt0�;�A����(On�Y�p�o�p--�{s�>W/�
��km���n����FT���K�'q/��;��:]�����"7�Rb�����fT�a�,���3n�q�h6{
��TS�R?W������f(e��]��Ox�4�_�`�(�
^�[TG�U������h��wko'�m���&���;��m�p�R2h�j�l:h��%�=���Z����Nh�W^�a�,�:1u�����y��������c�������{�f;0��X'��W���;T�|�0h�z�\�K��l�'��F	��P�2S�����d�����mw��-�]Pp>���4�
$2�~v
��8����O6O�5;���^4��<l����z�A
P�t��z5�1�����,��moE�g����gx��>���"}������4�������r��{����v�d��� �'&&-��&?���#$��G��h�����#�VI��p��a�-�����_Obo���E��,�����C1r	5�2��� �9;�����v;T�x,(�s�c�����=������p���U,E|F���d�@�����M�`���]�(��E��'�:h�� ��wOQN6����<K������
B9��O����3�4��J�bm�j
��]�K�mi�i�8�'��=�7SU"�������Q���D:���������(����&���I2/jj3Rg�5���X/F��O`��
�����[���6Y�;��.�*��@
�����I]
�0�(P
��h�L�I����C��R�99�V���?��#����+L��N_[:���s�r/��d�_��%�p����T����8[�V�7+����L�NF��-*m:n[�9�����3�W����y���G'�=����!6��W���
�BDpMK���S���-\E�uVI0��&�=�E��G������P+���l�^a-��yH���O�_]5N��/����z�nHxN��������>8�87����%�c8�6V�"�@��`����c�9u�d�;E��y%::P������JmnI2���K �����*6�%��:����D@����"����:xv�����+0�t|�[�2���R��L8c��vT�C�����Yi�tQ6�D� �����;��5��k5U�u�$��;��ZY9}}�>�Y]@9`�p�o�+�o
�����m��*���k9\��A=
�,�5
�8;�_���E�Yr����=�Z�ZU����"4s������D����`Od�B�"]2����r��e���������`����.f��,�(1��s�r����l���5����q]�pUT
>m,�Y��R3����h������o���`l�}uc���To�o���s�K��h��0���&p)��~���.�q<^Y(wv���Fu����%F���j ���0Q�=�%M	0xx
�`����Ta*�	���M�n�����gc|�#�*q��E`Mm���ps��W�o��#�D��c�/�}�n=0��^m�'�6G38��}R���k�xr�zrR�x��e�����a;���~my��U�f���$��$w�\e�=jPk{���W��/�hz�n���15�Q��������Z}���
F���czXbu����7���=S�<7�{���ic�x�$�	�5�
����\��@����.0���32F�����^.}FK���!�Ov)��6m�Z����]y/�<�����m��l��-���KDK�7~�����v����ZzM���]����i�R�sK�F	�|�$�FZ�����u����B�u^K�E����-��&��t37S���.�)�f�.O�u����N���^�Z$��$�a_���S�L��������e2Di�""�$�V
uN�g�YJ:$�buw��=�~|����-��9��X��:��~�$�t:����:��-���$�O/a{����q��4��(��N��~|)@���*7S�h��p����z�l�fS�M��%��)le����{/�.�������N��$&OV*�K���Z;�����=Qk�����I��A���������rV�'4�{M���J|<��'��V.;��/J���?@���,�Bq�GW�%��AI��T�z�i�"�����u��d�7�����6������r�d5#5�rkh)u'.��p�
�+i����I���.�.�L^��t'��F��<dRk��� uWE�'�Z�M&�.��U����L�@�M�]C�����������������(���i~	��{���iQ�-2E?.!s�8���6����\����(��q?HF�I2|��Y|-h�������*��z,q��P
������.���X�A�uC�L|�%>\�7�*,�(Vi0��3�C�"S�Y�P'd�8�Jr�m�5f�Cu`��[w~B�7}��f�����������3�M�o*��n�u��LY
�
�p;`������w��6A�����N���J�4:Z��9���8�u�q����U�-:���� ��L�;���/Ra(�ze�{�����lkhn���LOo���9����I�^�����`O�jE;��^���\�������X�q����2���[8)���<�����ej�?4��a��?O�v@���.~a�0��y���������V_��i2:��
�Q,�2B���"w����6��P��{�*e�wpu/��|�V����iaG�kjc������v�h�X��v1� p����������:m�������T�'���a�}�p��R�k�
;%mmG���j��99#�3���O^�w����Xt����/�����M�uLk$O�89�����W���}sqL��
�7wk�
��90,�ns�U�6l�6����9���
��S�|'�T�s�m+�������$1���TqlB������9���A����(i)�����	��@���HJ���H����L����'�����`�+L�&�Nn��VB�*���!w;����������<<}{�$f��fKA{k�U����;i�����CTx@#)0��p�������������&�C]T"�P;F���D�j�ypq^�J��9�:��y;R'G8���x'�j�!r�����MO#�8�,3<���z��.Ym��tmX��qp�x^�7^5����i��}�a��x�;���\���p�mn�x2���P:����&��`�F�������Q+f�1��P}�VT��g�k��z�lN1��8 ��vg�����)�A7�Z�*X��y�;���h���-�_7������St�o����Q$;����`���b��'�p��x�/�a��Ck��C+
��}�&�����%O��wyzW�����Vw_��3�%%������(�;��#���w���!E������-�D���0?�$��C#���D�'����ml
�Z�	{�6����7�0u��r��v�<p��b��}b�o�����{���^3�?O��	���U���{s�i j6s�9M�U���x p��?���^7�_�����\9��(^��b���P��S��<l����1��
7��}y]��P���,��7���� �[��F�������v8}}�a����s�W|��?!Ou�.?a.�<���}��� $�vI��
&��@@K���8�h�� ��2�������-������������k[%W/!`��a�u�+$�?F��j��4i���AyIu+�W}��+4Bcr
�Nz7�
�������^�����Y��Bn��0��~�������_����������`���g�P)����s�$A��P��Q�'��z�r6e�?�����q�������Y�Cfh��T��d���e2H=����+�$��~2y#�C������g'���
z�����$�>mzf����x��UG}�DN!���A�`���|:�~�Vn�:�$�.�{����5���4M�h�\��z#mU���������'��G����������5�?�Si��E6py��*E_�����O���2���Hx��-�
V����{��G)�]V^�}�E�IU��I���% N�GG������Tk����EP���������}h
���3/�E���JgW8���?����e��c�X��m��|�IW�Z��v��~2��=z���DX	2��;QuG���a���F`���Vmr��\yS^r�� �d�����0��4��t������w���a*W!>to9�N
�k�		�������o��9{�b���F�
	���G�$K=�U
9�B�����L�;j_�z��[G!e��UI�wf-��{��C}!�ugI��)�X��2/�a�H�Z��* Z��E�
�	��4�r����P�}�5\�7O�~]����U/��<�2�����FO���X�$�������������}��#B�iU�-i�������,'���g'��o�q�|.?��'/��M��u����Cm����9j���G���|>��Q�'������[���
#72Tom Lane
tgl@sss.pgh.pa.us
In reply to: Dimitri Fontaine (#71)
Re: Extensions, this time with a patch

Dimitri Fontaine <dimitri@2ndQuadrant.fr> writes:

Itagaki Takahiro <itagaki.takahiro@gmail.com> writes:

Why does only hstore have version '9.1'? Any other modules have
'9.1devel'.

It's the only contrib that's not using PGXS but instead directly
includes $(top_builddir)/src/Makefile.global,

... well, that's just a bug in hstore. *All* the contrib modules
should be using PGXS, unless they have a damn good reason not to;
which is not apparent for hstore.

regards, tom lane

#73Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Tom Lane (#72)
1 attachment(s)
Re: Extensions, this time with a patch

Tom Lane <tgl@sss.pgh.pa.us> writes:

... well, that's just a bug in hstore. *All* the contrib modules
should be using PGXS, unless they have a damn good reason not to;
which is not apparent for hstore.

Here's a patch.
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

Attachments:

hstore-pgxs.patchtext/x-patchDownload
*** a/contrib/hstore/Makefile
--- b/contrib/hstore/Makefile
***************
*** 1,9 ****
  # contrib/hstore/Makefile
  
- subdir = contrib/hstore
- top_builddir = ../..
- include $(top_builddir)/src/Makefile.global
- 
  MODULE_big = hstore
  OBJS = hstore_io.o hstore_op.o hstore_gist.o hstore_gin.o hstore_compat.o \
  	crc32.o
--- 1,5 ----
***************
*** 12,15 **** DATA_built = hstore.sql
--- 8,21 ----
  DATA = uninstall_hstore.sql
  REGRESS = hstore
  
+ ifdef USE_PGXS
+ PG_CONFIG = pg_config
+ PGXS := $(shell $(PG_CONFIG) --pgxs)
+ include $(PGXS)
+ else
+ subdir = contrib/hstore
+ top_builddir = ../..
+ include $(top_builddir)/src/Makefile.global
  include $(top_srcdir)/contrib/contrib-global.mk
+ endif
+ 
#74David E. Wheeler
david@kineticode.com
In reply to: Dimitri Fontaine (#70)
Re: Extensions, this time with a patch

On Oct 21, 2010, at 12:33 AM, Dimitri Fontaine wrote:

I don't see what it buys us in this very context. The main thing here to
realize is that I wrote about no code to parse the control file. I don't
think the extension patch should depend on the JSON-in-core patch.

Now, once we have JSON and before the release, I guess given a good
reason to have much more complex configuration files that don't look at
all like postgresql.conf, we could revisit.

Sure. The reason to do it, though, is so that extension authors can create just one metadata file, instead of two (or three, if one must also put such data into the Makefile).

Best,

David

#75Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: David E. Wheeler (#74)
Re: Extensions, this time with a patch

"David E. Wheeler" <david@kineticode.com> writes:

Sure. The reason to do it, though, is so that extension authors can create
just one metadata file, instead of two (or three, if one must also put such
data into the Makefile).

That's a good idea, but my guess is that the implementation cost of
supporting the control format in your perl infrastructure is at least an
order of magnitude lower than the cost for me to support your current
JSON file format, so I lean towards you having an automated way to fill
in the json file from the control one...

The Makefile supports $(VERSION) because chances are it's already there
(think packaging or tarball release targets). Having yet another place
where to manually maintain a version number ain't appealing.

In the latest patch, though, the only other thing you find in the
Makefile about the extension is its basename, which must be the one of
both the .control and the .sql files. And it's possible for $(EXTENSION)
to be a list of them, too, because of contrib/spi.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#76David E. Wheeler
david@kineticode.com
In reply to: Dimitri Fontaine (#75)
Re: Extensions, this time with a patch

On Oct 21, 2010, at 8:12 AM, Dimitri Fontaine wrote:

That's a good idea, but my guess is that the implementation cost of
supporting the control format in your perl infrastructure is at least an
order of magnitude lower than the cost for me to support your current
JSON file format, so I lean towards you having an automated way to fill
in the json file from the control one...

Well, it *will* be easier. Eventually. Right now, the file has to be edited by hand. Which I can tell you from experience is rather…error-prone.

Anyway, I wouldn't push for a JSON file format until a parser was just there for you to use without too much trouble.

The Makefile supports $(VERSION) because chances are it's already there
(think packaging or tarball release targets). Having yet another place
where to manually maintain a version number ain't appealing.

Be aware that PGXS sets a $(VERSION) variable already, so you'll need to use another name, I think, to guard from conflicts. This is in Makefile.global:

VERSION = 9.0.1
MAJORVERSION = 9.0

Maybe use EXTVERSION? You don't want to overwrite the core version because a makefile author could use it to change the build (pgTAP does this, for example).

In the latest patch, though, the only other thing you find in the
Makefile about the extension is its basename, which must be the one of
both the .control and the .sql files. And it's possible for $(EXTENSION)
to be a list of them, too, because of contrib/spi.

Right, that makes sense.

Best,

David

#77Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: David E. Wheeler (#76)
Re: Extensions, this time with a patch

"David E. Wheeler" <david@kineticode.com> writes:

Be aware that PGXS sets a $(VERSION) variable already, so you'll need
to use another name, I think, to guard from conflicts. This is in
Makefile.global:

Of course that's not a problem for contribs, and I used EXTVERSION in a
previous version of the patch. I guess I will get back to use
$(EXTVERSION) in the Makefile next time I have to produce a patch.

This part of the problem didn't receive much thoughts yet, and it shows
up. About the rest of the patch have been in my head for months, I
expect less problems there...

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#78Alvaro Herrera
alvherre@commandprompt.com
In reply to: Dimitri Fontaine (#77)
Re: Extensions, this time with a patch

Excerpts from Dimitri Fontaine's message of jue oct 21 12:53:18 -0300 2010:

This part of the problem didn't receive much thoughts yet, and it shows
up. About the rest of the patch have been in my head for months, I
expect less problems there...

Keep on it. You're doing a terrific job.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#79Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Alvaro Herrera (#78)
1 attachment(s)
Re: Extensions, this time with a patch

Alvaro Herrera <alvherre@commandprompt.com> writes:

Excerpts from Dimitri Fontaine's message of jue oct 21 12:53:18 -0300 2010:

This part of the problem didn't receive much thoughts yet, and it shows
up. About the rest of the patch have been in my head for months, I
expect less problems there...

Keep on it. You're doing a terrific job.

Thank you very much, such encouragements are highly appreciated :)

Of course, you what that means? Yes, another version of the patch, that
will build the control file out of the control.in at build time rather
than install time, and that's back to using EXTVERSION both in the
Makefile and in the .control.in file.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

Attachments:

extension.v9.patch.gzapplication/octet-streamDownload
��i�Lextension.v9.patch�\yw7��[����j��8q�8Z�e���r<���kv�"�>8}H�3��[U8M6)�Hv�~�D���W�j���0w���,�]�E4u�O���'>���l��*���?���i����7�Ag8�E��W��n��
�3~��C�F�@�G"J37�er��_��_�C��~t����g���j��o.O������g��=�P}�'�W�}>f������C.����go���xz5�U������� �� ������^��uZ����w�<����==����*e�G$>�����ddpX�6�Q*��#7�0�m�~�8y����^"�7�������g��q�.�4�Jx�o'���'�{���9He	��+-CJA�*�{��s	�^������5h��������������X���R\���F�F�z��'��g��)���vo�&�~�fw�HP3��'����'��������P��L�����X�O�q����'�My�^����'����fw���D�3D�qFL��M��8&���������������tBx�D�a�pZ|�>�g��h���qd��k7`B�.��l��������TK)���l��?xz{�OS7M�ax���`�wr�zxr|�?�$�(a��Kf��-��_�WFV���*�.���?j������B�j��J �	�V�>C��8���V�sO���������dW���������eV����u��OR���Z�C�.li���>�7At�qQ
�����!��y�����R���U-�;4���	{#x��V:�s�u��'��j���Q�S�'�4q�!9%�Z��U`b�Rq����
EIX���s�������'|�pPl�Z� >g��!c��-�"Z�}��Z�%��]�e�7K-��e%����9��u��������S�Y6��mE��X�8�p{����;��MP��Fd�j��}}�/B��T����i����3��K�W�4A��,��@�+�������o!A �3���M�<��'Hf�z�cL��k�a�MJ�S�����r����p)T���F{���,�9�|��l;�����;��t��V�J�����A�?pH|�\�t���	v�:���{q#oi����id��z��bE����-O��=�����'�8�+����)/��O�}!��0�i�(7�����)�
���>5��
�v�����?� g
���,����R������!���$�x+���]���j��gZ&�����"���N��I�1�v�a<�%�j���Wg�{{���^{�a�<GA<���
��A<%�%q>�:''�w|�n���w{��o��M|@�(�d\D��lj��d�O��21L�|D,dT<�1E_�1�W1NB6��S�)�#<p#_r�y�3��I�M_������B�$\��`��~k�����1���^�ta�#��\��`_��Z��U��s�xe�P6xJG-�rH��w�8��'�f`�i�2�O��H��.�����aI����I������}Mm���Yx�/W�+\�r �c+��l��K
��/�F)����9N�����.A��������i�de1�����i�����13�.��'{�fyh�w������F����p6�����������@v��#�;F\��S-�q7m�p��kP��<������t���r�jX��ktp�����,�qd���&������5���/��������I�=x�w�����?��t�66��?�L�g��d_C:���QV��6�9�N��>����u
�}�y�?��h���i�����S-|�?Jd_4^<[���w�6��W��]}6��C�f� ���<5�I�
V���P��lIfI��7qn�s��,�5�����-1��x�+%d�!��F<���/��9@���Ii���'�f%�����9�4�&	����O�@'���,�B0w�`:�/
�]l��7-���55������W$� u�HH�x������z`� 
�	�.�xy��[R�qP$D����������?Rc	C��/4g��3l�������@�t�����
��,�3$sV��ZR�r<���N������ki�F���euk�7��h����l�����DG�H|���	����)��?�GGv?�{�+���DNe#�)R8�Aa�)���\[��a$u��:S��E-��?1��a�E7J!�����
`��.��o��<�������(���4S�����z�WN[���$����	3�!D2Kz� *�@@�_X�[�Q����B��D?^!� ������������<3m��`"R=}�gb�FI
P���ka|K�N�=q�-��3�[�U�[W�~X�����M��R�C���Z��;�i��A�F�	Z��t��g�;'�o�XQw��
��e��;0��W�-7��)%E9p?%
�[R�(�o_"{�x��%��/"�2����^�%-l�4B&���'����~0�����0a0,����
���t6���Y����xI�4���\	t��t�0�WJP���L�|�L[3X1r�O�����+���Ob
_�4t@������f��LT�k&�b�xK��q�1p#�<o�0�j����7m��rI������A��M��h2����sp�dpH�a��LF�.��yDw��
�g�����J��+�%|��(�H�/�TK���2��];=/����������x�\H�`6�Kxml`"^�g��	�=��i"r���/�
"���b�3rpJ�|����$�=z�[�A�!�L���d�'=�yL��F�	��h�X�%l&�V�����j|@""/��	R�Z%��5�:��m-���E��R>��&�c���Q��@���p���������y�@��q�DB�H@���+�*C�bU�}�#��Jh�.�F�Nm^!U�i��|a��(��lRF���U��q�j�-�'w�}$�9�6�l��,�S%�V@	��g�	��6�c���obI+��U�@�4���]��CJ]}H	��C���tJ���)�����ymaf&�<�"� �����,
z��J�Q��;�<�"a;�]��}\B�5�@h`��a����E�g0��!�(m$���D���#�NVZ�X��	��l������.�O��g�����#|n���Y����w������m�G*����&m����X��Y�����{�*X����t�Q3���E%|�����+���ye��N��g8����
�<0]�����%���w�%[^��`"�E���!�Q�6vEB�iu;F���:��-s���rOV�O�9d�t�n�B6�����i�8��1V&���L�����r��P�`�,Jb�m��)7b��h,��h�F������6Rn�f���F�<a������(��g��jO-��R�v��	�P���B��`�
�>�G���Z=	�]]�a�)�y���Mc�"��Y�@�"";i�G�su��F|�t�M�!	
qR�H�t��^s�^����8e*���J�}�&�r_��
��d!���BV�Fc�r��k{%UM�-`�Rw@`\$����\��	$�^���3�I���������W_i�Z�����q�m�A�[���C3�%���|),��JJ�d���0T�[UY"�U�Q
	}��� &�Y�`�=V�8b�<��b J"<�1��F��\�X��7+_5�����Y3~��>
�tY�a}�v ����9�������^��S)"S���;e*��l����?��@j�*����J�d�J�d��t�s�3���?u@[dz����;����IY
���
V� �=uz�7��R���p-Sm`�3����:"0����	2��>�?>{{>|��U������`�8v��t�O���V�&����������/��4��kK:c��2��������MD�QA��&"c� ����������zRy 
�C�����������D�L����j}PZ)U$��k�?+^�����Rg�[��-|�n�����n�?����s����a�y�����4���6����:0���a�y/�o�	��C�O��AVye����y�����:�"���D����%����`4������� ��)��'a[�W!�UUm+��V�VbbU����k��=e*B�x��n%������,e[(���/��������TSp��v]v�c���I���4�l(2�%o�P����$�t�N�m7�M5w���U�Eg���s��nlW��m,���]v������^:�O��?�dQ�z��87����5fX�B-����3\.����$��'�VM�
�����������y�z���Rfv��?��[m���r�j]�6�������h�-u$$RY��L��U�����/c���5,��(���t]��,�},�R�}tk�'�}t�Y��<A���P��<���Y�����#rp�[UP�s"�[���b�n�5�L�/e!������r�*�.�89��8��2P#z��S0�Us��Us8J����:�|Dh�{����
����j���+*�$3Y�V���I����&VOr#M���BZ�O�G�{��h3��_F����6�z�U����.�w<���~x����������.��Z�
`DH�4Z�1)�S���)C���*���F�x�0����	��$"M%B��a�N�X{�pyI��Q
�>�����=����`e0]�����3.�3Ob�\%�<26�o���VV������d���j��
�w1�K�x!��'Y�F������j�l�2h�=L�^���������S*n�QI�r��.5�|��5(�]�����)�-��������*p�#2�����1*)��O0`��~RbW(%p���3p��Q�:��r @/����8r��zM�z63� k�E�����/o�A�xa��
��#0�����SV�#��L�Kt���� �iJ����"S��,�s#Y���v<��}
pm'�[
�k�N�M�����)<qz`g�Z��C�?��k�\{�,�^;�����k���vt��S]{�T�^;����lP�+k�P�s�^�1�]�����T�}�hh
��^Q`K'����\�bO1��aK~Y'=%���$� #|�c�No0<<?�<=�[�/�F���%�%�����c�[2Z8��>���YQQ����7c>������������WE�s���9�y��zg��zc������w��z������QQ�<����������bE�s��z������r�y�;��Z{]Y&����*�,L�v�T�L�E���Q�����2���2P�s&��S�P�N�$H�:�qCq�h#�X�(B�X���h������A�rTdV�Z"2#��D�\������2�Eb�%7oP�F�e��Iq_0�d���G���y�]qT=����
B�=#�]�FJ�M,�������,����Q��i�+�g/Y��pR���j�p!Z�Ey���x`|�1Yg%�����V�n�{�"��
���i��H����f���-N��Z�[eT^��6�7�*oR��� �j�+3k�����cA�L	�b���{�H�N{�i��#
�e�l�x6��lt�$M&��1B�1�b�e��"�����F���#����P3�w]���a��}�V�#flEt�����1�&U�q�(M*����?�.�_��Pi��BL'������yG������Z]���z��z���m�}# ��$�Fn$��A9U2L]���X9�q��,�O��rO!�[=np�31�~���4p�O���(��yd8�"0,�Vr/U4���'6�����svW�>C�4[\������:Xa�����\������<��h����<��h�����<�u���o��h��,$4����B�C^GsV��fE����9K_G3��zM]%y�Q��9�u4��BWg���(��3��k0��}^r*^�3�'ym�Y���[F��VZ���C�Zjg��DF��x��Y�]�2��w���9�h���N��������nP�A<�����|��G0�����F4'�1s�T:��#:��}��z������v76��u�W: ��5�Aw�d�5�/����w�����O�&Yf�u������{lpg���Gg$
0�4�h$c��������L�������Y��{uuuUu]������Y���-�4P
�����������Q���DGO�j��������F]�����Oi��M0����~���� 
�NZ	�>K�AE ��g����^d��x�1���`S�{|+�.|�2(�}�|��i���;�M��e��w��qv��A���N���o�S�T=Z���q������-n=nq��q����-.���8c���N�����-�����O{��/�'�]K�+���>tg�����������U}��s}���o��4���Y^I����������o�~�o�������g��<�S�����g;O�NsM�w���sG*������^�An�����C�ct�K���+�u��O;������rz�Y3�8�-�7�U���D�H��
X��:"3�hl���&��bw��l��d�BW�6V�&������b�O�p��o����&A��.�/C4L�pVr��6K���~ .���OG(F�I���]4,#^�����
�������8HyV0�������d�������E�e�i��s[Z.�6_�I�j%��W0����H��uvQOX�Ki�H���Rc����q���`bh
��1��Qx�����x2��^�(����9�������H��=����_1}��	�r���RE�{H��o>0�������zb2�f�AJ
��XG��
�)b�A�C���jJ��r�y%i���i,��]��+R�:�Q�3y���^��������V��O������*N�j�3���,����	�,��
��Lf
��q�n&s�JF(^2�E���2A����D~�Q�D�O&�������nL��C
-WH?���j2������^�d�_!�eyR���~������)�H�J����������#�����V��8Gp���X]��K~�jR�!=�W/8���h��R�\�e}1�u�gt������:\M��=�#eZ;��U��!]���>Lw�y.\�
�*��nZ��{�<����[w�s��MS��7���s��K��Z2��Y^C��s/D���%x"�M��
O�Z����kV�{������5y��`�:�;�"zW�@���0.��7�+����E����R=vG�JYD�/y�)���*_�����0���t{���#A�m�`���`��9����f�ol6�/��%g��������Tm�CuU�(���^` ����'pX���^�j����H��J_�$��)�{����H!�����y�9]�ZG�k���1[��&��iC?��xJ��5S�EQ����#�c/`�MO/Q���=�h�zM/G�I�U�Y1E'}��;	���`o!����iHa�/�oj�\S�<S�T�)�U�K�Nl��O)|F��L-�]��$��_�����A/�"����'I�������Mz���I0�^�������0�������u�5i��Rm�P�����|��#)u���'�������i�?����\FY�6�����o����p��o��?e�R*�?*�>��������X��]�k�����Z�B��&�@���9�����,��6�ZQ'��0�n�h����>�*��Z�&/=�>�%�'�@���7��[�{�a�AU���������&�rC�B������Z�W��\h�I�����g5�mW�bq�c��Ym���R���.i���V���ZUY�H��R�?��z�K�%�cY~�[u���jn���5U���XC��)���z��a�l��-[h���������_�����I��W���,����=��������e��n�����{Y�{YK�e���N�tY)U���*���j�`�@��j�m��M35�5��a���(h�������Z��J�4-}V�-��,�e�Y-���rI}� X����~E������{��_U��v��7�X����6�wh��]/�F�/���K��6������U4J[��Jj��@-UP�%�T�FQ�5�6�Fi��)��2���&R�'R�)4Rm�E]���
[5�4���~U���,��-j�I#���H%�s*������U�sM}o��[EUi5�2{ !%^T�1��1�vPI��uY-�a�h�63��T�UP������^��+��Sp�p�)�
�����
�j���KU��Q�e)�k�������nZ�n�e7m�oj�o"����5]V�����i4��������R�h��hih�R�o�o���2��JC���;�*�]RI�Q�������Y���������a~���U-x��U]b��V�(US�(U
x��x��x��,��	?��6��Z��M�K��Y�������e]RO�Qt~7�j*W����:m��u�K
u��g=�F��H��0�k���U�7wd>L�]Y�soj���������LKHMSDjJIN1$M��MK,i*��i&M-�4��iT�iT�4H�P,y�b�``�c=U�s��[��r��unv�����E6�dY��U47��f-��My�G�L]���:��@-���Vv��}�����9��AJ�h�c?��E�\6��M/�f/�~/KubA�q.��iig�k�m
�E��Y��t�yDZEc5�G+]\5�S������e��j��s(!g���Z������\�Z���d.����Z���f�j�0N�i�H��g���WnX�-H]UK�5��f�YR'~P�b�l�����y������W��j���^�*���*Z����+�i ��eUy�eA]�4Sm���)����������j�j�fV��1�nN�Zp��Z��A�.{������Q4JS�)��
�����#�j���V�f��V��3
?������n\��gM��R]�=�UR�[��[3$a�mUmcUe&*������E]f���5�G9Sl���LM��vP�>�T���R�MY����?k��^@�X@-
��	0�*��@u�Rl�tc�hW|`hWmYE5�j�����M���2��Pv>���5�Y��J��zQ��
\�x�ji!
���i��,�C����*k������Ti
�M��M�"�]���Kl84
84�ph�phf�@K��:(�e]RO��+h�2N���B�U����^������T���.K����j"�d��������%5��=���g��\�ou��w� �R*��E�C�VA�Q4�����h_�}��2��`���J�*��bT�f�t��� ����6����zM\K
\3�W�Eg����z-����Zh������Qdw��x�YO���S�^�nN���LC�	��f�j`��f�*PSl Kb�e��(H��U���{�����5���jUj��TQ��6E��lTtI5�F���zNZ�v�
��-79[
CDlp���Q����G��.n��%%�O����eUR�*F#�C]�%��j�to�TQ�(��VS7�]J�FYU�Q�>��wS0�?k����JQ�FZ�0� ��d���E�b
o
�����eUb���gX9h��6��������*^�T7���aS�.*�ML��Q��^�:��Q<��������AR����,/$�5Lq�Q��@]\3J�B.��R���_i����U�]��Z�R]��[�T���?L��Z�z�J�:����b�.(����P�/�M%4mq����iM�Vn��YS����i���uI9��R�e�V4����E����.���4TI����7����~����*)�����������T3��TY�.h�V�T����������j�j�
�n�*(���o�o}3U��*[o�����B�{��j����f#(�$ ��P���������
c�F��m�V�G
������1��q)��ij�mk��C�,5U��V���YG�>�U�
J��J)\�����lz��mKq.�Vj^-c^�4�Z&�Zi0�����~�����o�S��:�?2��ua3��e4L���]���*�ewLjF����j]��nr��gY�T���e�f�86��R��TQ�(���U�Fi5�mSw�Lu�4�m��m��63��$$�W�.���Z�.�����t�M=^+u�*�^���TQ�(kUR�JwI��-�nK�R��e�t;E���T�e�m�F�JY�/�nVS�}+�L�R8�����.�����*�eT�eT2�)�@����.����TS�\1����x55^����K���A*���j��j��9�r�����j�����W7�W���P�5,[A
�*���hx6��5�������4����FY��*l���T������]P�%�T=^+�M-s�2g�ZTT�hmS���v�(T
�P-����%�#5^I�W����6���j�o�4�������UM-�6�����i��?��R�����b�6��b�U��4��f�B������Lqi=�B=U�06����s�b��d�5���sq'�z����e�TQ�>�Z�4�ju�W�V�����������zz�5���6�j
��F�z�5�4�mS���4�c���.������p_/5�z�RH����G,��02�F�e�ik#�r�S��U����&�SO+�Zu�����]P�%�Y�:^����:l��*�����vAC�pzL�R�x��%)���*��F�^�������Wm��vM�������Qf�����3��~0�O�fK�	��H��@R��Z��U7���z���`����(����t'"�[^��"4�
��i��V#��R+U�A��0A�=I=(1UP�%��k4(h�]��JMh������7�.ca�TQ�(k�����6
X7T\�Q+��Zi����X�U3�+��Q�Y�F]�~���a�Q������T�
���u}�JM�h5�F�bc�F�U��u9���[^pI��7��7����5)q�a��
-���F�n����f�C�����5�8
�**�Kv��:��_U=UT7�j�Ta�>����&�Ds1�h��t�o�v�E<��V��a�Zg?�t��N1�������5���t�V�(5�}��F�$6�6��R���Y�����q���%��!B�b���������?�E5��U��
���������������gA��,�&*�J^���Q��V�����{]��]����Y�y�R)��zP����4��43�l�E������*E���iY5J�����65jY�D��+.�+p*�q��rE�]��=�w�(�U��*WR��VY#Y�~���+FY����m����g%UT5�:T��5��u?�.�����l'v��Qo����9�Jf^�}�SE������P�ZJ�J�.�����e4LM���S���J]_7y=v�Z^���L�i������l'v4;Zb������M��x'�D�b�&�?-�O!F�}��]�����-��P��o�j9u�ec������`H3�!���Fz���[�3�f��5*�����o�Z��9��2{^��1�����,�/����EM����E��E-
��	�Z9
���5j����e����[�V�t�o�Z5��qY��)<�oX�Uc�A��
������K{�`������V���.�+�mp$�X���
���FY-�N_#�#�(n��ta�(L��G����!��*S����T�
��Fj�
c���F���i��x�oK���Q�J���T�������Z���VB-B�t�u}���]T2������l��L��6���TQ�(k�����^�t��o�l��n�z9�eu�����n��W��5�4�V�z�����\���)��\=s���k)�u����\��L��+��TQ�(���i�Q��Ow]�z�(���D�}�B��ue���+�����#�yk�@���z�����1oG���|��2/�uy�q�Y�_��SB������m��b�hF,)��R���W����T���d7j��F�(��������b��K?a9�Dz!���F@n����f�	��*�\�<�('���WH9aC
��u��w����p
�������R?x��}�F����<�\<��(Kn����n������Ran�[JX�x�n��7A���aV� ��g<I%<{�G��pwi�@I���7{{�7m��ar5$�9epUy'E:z8�����v�!���E�?�i�=M�%u��>\���X|�'�3�f:��}�������u�Nj<Z�u������1��j.�;'�����R�����X�-'��3���ni@�/����i@�;�$�(3h^9�yr?f,R@��w����%}��z1l�������F��%��$���&�R�m(M#�������������7��Y4t��1\���7.�~�w��%s^9�x�����.U�����m��t��t���~��r`��k��w���u�t�l�x�f)L:Svx��#�4�i���>��f+��������+Yv�i��F��y��N�M��4�]8�X��~"�'|�S���C�:����a���;4�5!f���z�F�-6����?��Z	�����W�@����NZYr}�0��o���l�o�L@�kX��.�2�f���W������MD��`�xl�0`�|�K��'�??�]�F^]������:y����#l��0H�J��S��nC�]��]�A+���,�\V/&~Y)���0���X�+���6�X�)Z8��+�'���8�����-��heU��W�z�bYM/fy�2��q�,��\X�Q=�����{� ����`�������1�k�c�v����Xy�SN;���QK����c=��X�'�R�)L���'���/AwB�&��KW&l���;�{U��"X8]���]�|�d��4�a[�d
O���LU6�t��[�'�\���C�������do}�������1W�cy��'������Q'�����������n:*8����,�\�2���n]�����?<�X�e����/ ��������n�C�� ��������2fO��/{��$v"���c�y�K�'�o��N;L��`8�sv��t����?B��I��u��>����.�ay�� ���F
�q�$����0����{
��4���p�Qp�F���"a{������2�F�'��d��t�(
F9��R�I?����I�.�lP��}f�3��$:�G�(������0��%�q]������9�I�M�����>����=O��~,����;�m�tDzx�@~�a���}9r+�Ao_�5����,aj{!��p��D�{|�����?��_B����	�5�Tw`�{�s��"bg0�;��Mgv�����}(���Y_��z�)Z��-��a�]����Pc�������$��&0���tG�p��;��$��+��y������������N8��q{'��Os�'�������6t��+����#��v��54�#��6��>�8�{AN#���/���H�d��	�0��&��&q[�,Fr���A��]�	��:q��?�'F���GAv��Z���~����3�+��"���eO8z�~�3T�'0o?�OF��� ^���{:-t��j3�`O�8B�%`;k*[�?P"��=�l�?�g�mqqk�5��	#��}��x�����>����v��g��:����	����f6������G2�=��s�c����G���~U�0���CXD������u�w�ez��x����e���}����I���:�{���g��h��w���50)��#S�	\��(�s��� �a�������@���V�c�,��������R,�M,�H�/��<4�p���M�K�
��L���iX�_p�
):�����&.a>���]��E�d%!�5�z��:��'/�������\f���k��M#��Qph�*��WL��d8����*A<K�gh������j��
'}�,o���cqt�f���n0e$S��,�a=vwb�Ms�����X�?�����w��$:�T�_�XK�I'���Xc����#�(���d0��*)���>��T��
9�A<���t�WA�f�!n]����JO�R������dR�E<�R��Z�*Y���;�g}iH���(��=��������7���gG��w'���a�gt���>�f�:
��� M��x�$��M�C���d�%z"�����@O����#!���u�B�|���n��3�x	��'�9T�O��N��������_����
c�=vO�]���������g.������h���n���6��L;&KQ6���A�����.~������U�[?R��o�!yz��;~�)T �?���j$�x�q�o��$��sVq.N�bd�kR%LWX��4W�Y��(|�m�b�	�\�S�	��u������!k�Qq����B@qC8k��6���a���� "zH$���Ohm���qi�������������1 �6��0�WP��}�N��������/A��V-A����=��K
]?���:t�bw����J~<8�u���#�t%���(�6g��L+�;���#��d�?qt�M.@Eu8��IP��IDD.L'������������L��"=�����5�T��`�J�	�\���|6	�
</��������]��Z�~�S����+�����d/u������'c��M�$��f�}�	����3�5���,��~b8�Lr d�tg���}���^Haz���T�_����d2�;���������wp�,l�v���w �T�9\l�� �f�����:������f�\�T��@H�]$KB#��18�s>��p��!m�V��$�2����f^~��:,�{>�s!g*������ )�Rd,*,>\9������b�2"�5���gK�s�����M%{��a��0��$h�1O1���^��7C������D=��<��f��E��R��S�3e���e����9�FI�.�8����Ha~��'�(���9����E%����5�ss�)�-����,vXb�V����}�hS ���$������$�9S�^Y��\�����E���Ly����J��D2a�q(�*�a����4�(�nc����2��*�e���Qb
_�O:``���<��q�m��z����=^��L�f�M��qf��k��@����q�G_��q�S���{}"~=
��DNYI�E�FuZ�z����d�FAx��FxD�W�5(��2��/��{�=z���a]�g�����{�X�IAQ<��M@��d�4��v�z�m��#���>�T\�$8�\$phB1�DW�+v��h��`bx����[�<M_`�q����������}��;�:���	�|V���NJ�6��HA�����A$$z[[���t����������_~�����c��'P�����a����23����"��<�4�8�U4���G�m����4V8C	�!"��%���$0E�O�+��9Az�4p]���bV�^�n���H�qW/a<��o��Q�5��S��ad���@*n7��p,��'�$�J!qK���^8"�Bp��n��J;Fq<>�}	z&f$�pF��]2�% ����=�����y��l�P�r#������o���]D�]	u �W��H��=���C�1���r�j����5%e�-�jH�h�����&�1�������U�'���I���r�N���\�����.1�sE��~	V�gb��Y
���Y f~�s�u�u������1���
����&j�y)^��U,����[�c>��?��p��0c_�?�xtr~�v>�u���X:(�,	Z�.�����6~8A92��,��7��^=��C@P4��E����)90���Q����\���dGg�e_��M	�0t61�����[ Nf�Q�����
 �P���(R�l�8��o,E�_��
J�;:��i@re���F�z�������(�&}���X���_:���5�A\2E>�gl���3�E��{���)�v��r��r��m��?p�.c�&l2�S�(���x�zxMR_�X$c�����xi$�N'���^,���<�)�Mumm��Qh�I���*fO���P^n	;|}y����������H���������m6�]1�2N8�W���["��zH��(���q��3���J�kv�%�����lt
�k��v�����8~w�����=&{T��T����{z�{��[�`���5��������4�,��mabVn6T:K���V�D�V�����kC^h���+�������E�dNu,�a[H^��m=���&C!w�H&��2sV{�mXw$������I>���|��mM��K���Z�B{�Qw�+?���?7�����'��w?�5O�h����k���5/��9f�-��F�y���FO���VP�0��h�:n�~��P��\��R�y�Zc���=��%5C��;2:����g�����+�0t�L�d7a�|��<$��H����\zE���q��������1I���<��EGl�I��^tJro�)�������2%��w��\1�"����!���(��uO"4%��`Z�%�R�Ll��wi ������/����:�(��7"�����4S�y�GA�pDY'�mh��H�?��g�������2�����$~�`�&��a�(�qVx�U�U $u-�m�<����N��:�_�|[F��gK���i�4_�@g����v2u`US�8/ua9$�G���)
e)�i��>���$LDi"�:Mb���hUhU��s������23'�M�"�-q����j����L��u�Ip5���2��]0jJ,��8v��������r��jQ	���4�Y���P�bc�����8���9�<@��.G�z�mT3��diNn|x4>�6��=��XQ�[��0���]�?�|��������<v�������_�O\
,��k���������)9=rK�(�I������c��-��43[����cB�trZ)���h>7.�q�a��tL��H(�\/^��|��&�':�YK^�,��V�w�^x���=CC���Gu������Jl"�c���N�����m����o2m,����h�������;��2���d���p[548�&��`t�X}�p�R�� ���n(��O<�x,8�\6��}z2���hf����H���m�^H�W��m�?f���>�)F�Lo��p��|�f7����� �z� Jz�~�����4��Z��&S	��jJ��-d���^�RTAX7}
r\?�$���	��}�}�<������Z-������Dh�����2r����|�o�s�V��>����x�Z S��.3��P?������I:��,8��rZi��
.>������7��7�7�h������3u�&���z�����E>q)NI�le��"��{�VsGR��(g����k@�s�	�O��f�J�����`�}���u�����x�W���G�!�������]���1�pxqq|��?�v���1>tp�:�=�cn�G3V9WM}�������
�b��h�u�"�J���s�Zl����1F��Y�2S��9����0�����Nx
S;�cz��X�)��U?�����6�@%�*�����1R�����!�F�x2�,��d1:����&_���Ug�������FA��G��*U�����
^�
�wJ������N�"�+�����_������XtO%�yV�����EL
%����An}mULM�����/J�D���3��<������hN�)V���W�R�R��y���g�G�V�m)"��0�s~�0y��y�1�� �R��,e�$d�� @�5\LA�.zv��T%��+00~����v=k���7�k��W�g�b��<�B������aR�!�~9`���6��x���O9�����n���=���P���%��k��W+���7�<\�8��\�j�
b��n�.�4=-�������i��8�r<r�
��_�M���mMgR!�h�����C���|+p�4��S�
���X�^��<L0@���b����W���E�7r���H]A �6Hj�@P�#���+W�E�^�{��%/��L�Zc}s�+D5�o������������'�Bl�O4�$�7m7����?�m)L���,Lx�HaAX!�����j	�&��i*���S��"rU�cV��)r���t�g�o��b/1�B���m�|"��6�#����U���S�2�J�E�����k��WA����pc6K�[au�&�N����P<~�:�u����\/�b�3��ny���r�'d�Z�{gX�>���	��3��3��m��h��.^ �t�KDz����zv�k�Io�����*=vr�vu$k^��c@����g����C ���e������S��1�
�������%���)��\����I�]���(�����S�'����Ux���{!�c����~����p	�*��![[����1���M$�	$H���;�D�y��nq�R8�$�~_x�"��b��[� ��+c�2�^�y�R�we�el��X)n�6��<�7L��')��H����l3�2�������������'������R)[/�V�G)�������*����y
�Y��J��W�kT��Z}�O��{j����;����Z*y%���fI�G/`/�	�p���]�g+���r^��PP8/}�G�}��x�M~X&�����������tv,y�b��`�p�i	�t5��?�&An"%��1���2��z�JMU�T�;��
����*���}qy~r��W~ss��i�\�����:
�T"�������u?Ln
`��XCR��L
]�T�#0����w������;�<�A��or�z������Q�-\��?;=�<<������12Q��4���=��������k4#8��:��\}^��7�����&�g�:<}���]�R5�=|<|{l�2�{mzO��ki���+����k;w���F�0w9�k��M�M�N��(JM�>Vz�+��D�6������$*L�D;<�n������m�`V^�<q�)�z�-7?�f<~���"��H@+1��XY0o�/_�:'F�0uu���!)w
e!�{�V�N��<G*�w>;�)���d��(Nq����@#�H�L�M�FW%�k��k!�k�}���Yo:��I�f����mA�vdm:E�F��s
Z��W*m��������d�R1`����Ta��ysO�Z0��eF{���
��Q<����4�x/�5g8
�k��O����'����`��N�en�r����d<�0$
x��bmx��w"W���*�
!��r����"t|��h���]���QB�F�|�_t��'�����^�,�]���������d��m���zAgr-t�WPIu�K�W=����_o�$�<�������kLY�?�E�T�o?�z~�v�p�h�����s6T��\)&~�
��'�=�z��:4����\F�;�7���CT[��q(������������'^./_s[S����&���q�#�lS8�����{������l� k�Z

�`��'@#����G�D���H�@�+61�*������cT+{�ZsS�h7���v��y���]h������e"����M�f<���{�%&�^��� F��RJ���NN��mc4���[FX�V��P[
���C���)}	-�<�(0X�O+�����X'��a��oy��?
��0���7X���D���Dc�Ndj�#�\p�i�=Q��?Tr.��H��Wj�~G�y�}�L����L��2�.��P��W.G��(���a4n����(���.w���:��W.U7�R[�,��.��F�xe����b_��-	S�w�Px�p"Y��������b�������wK��Kw"V���+��k����:r"�����P�%�S���r��;"��N?�~J��D%���r�D�Z�+�~W �'M��� ��C�{�Nd�W�r�w%�\p���9�$�����s���� �6W�
U�*J�������P�f�+�J���<`
{A4vb�����I�%��9��J��;"���|q"���w�H�%;������;"��q�'N�$���!�s�n?��W)WG�y���L!F����em���p�����`�lSHu�{�q x�D���[^����<
t(��i���.Ly����7�a�,��<��W���<x&��!�>�i��pQ������EQpK��������<��8�m3�^�+V�O���`���_��0��U�;"o�[u4�u(&����})�����r������PS)a��c���#i�O�J���~��[$���;�L
��������������jd��8<���n�;�8P���^��<.�p��.\��d�<H�����pG����B��#���;�k�8��]i����=<��];O�{N����]r�����������}rg��>�jc{�FA�k5�]�������(���f�n�|;={������v���o��������A'9hU�JkSo�jz������o�)���-Ww<&�m��19[�O>���8�;�����!
A�6���=D�c�M8��H��6���`���:��L�W_��_�;Wz��n���� ����K������/����/��E����,?��p�������I81\B���<
\�0��]?g�����_"�om�`m���1.�c\C�p������x����SD,��3W��1k��$���������W�� /���}_t$hB����W���BjNL^��}�����p00���uw&,�fvG������0.�c�P��pT�����L
fv�����S��|��fO�h���@��������MN�Hj��8�t_1jG��?P^6"��������K@�I4
����3�M�"v'��+�������?���Uax�V���&�&���35�I\Oh�U��`|��t��s�1"o�qLt:�&%��s*��S�F>zW��H@�q'������yK}�j]w����{��6tm�Z����g��z��5]7��i6u�l�~yW�����\(N����17dp�D8g\���4j2��x,�Qd��;��C�<���rj���*G$����r�!P�����UlY�����/Yo���G/�h,����'�y�SaWq���C��#��)~%�3����HOW��(��S�Do�9+C���e�9g��/?gIw� PDw�u+��*�gs
�U��
��f����k��==gys�6i�v[n���3=k����qM��*��t��ss�����5y}'�a��m�����Zz���9s��_}�i�`�����9U���S��tx~zr����/�Y���O�>�-`HS�Y��h!���?pS^bh+�?���%{u��8~�!���h�6 ��Q ;��:���������h�QhW�(�LBX$^��H�������5n��88��	�+��:@Hp6�	C,�N:�����x�};��N�xHi��i�O���7�Y<��b�1���f�5~�c�U.��d�9��;c�E�������j���Ej]�?���������Yg��5���/-�����{d��vz�e��+���Qc��������i�NA-f��O�"�t�x���An��z�P=�y=�����S	5����6f�:��Y��\k��~���B�9��92~���l��t��YX�����_	������/�k�k��4���j5�e1���l����^�n%<���B�)y4G����Ld0�s�������L�� x�:@Z���1f�.�f����V�>a
���}#[+�O��������������!���i��i[��LT���k������g'���~�x���XfG4��d'���&��K����#^@��x4~*7��}�L�V�L��_�c=���-����0�)�DM-��	1��u����l%`q$��A����n���d0���Q��3Dx���������8����,h�>�$�������;i�aM^c]��������in�S=��d@�l�������=��`�d�o)�������C�Zt6������(�w������z"�����{R0LzA����e<�V�o6C��\������t�t��2Lq���&p��r{�,��x����k}|�����
'��[�h8��RYh
&#���������
�g����YO�qyp����df$3%�b�Gb�f��G�WSf�@��WUF��������Fv�� 6(��M��p<'�B��5���Z�}�Y�D|&2M\���M�:	�O��M���9���@��1�V:�)>��;bb�$���dW���H�'�������Y���H�����������Qq�0lO��5���j^�W���wg?��p���/�:n\��(�,�����\F�s�+@���@����LP��@�z$����_@����MF��(t�4�����.^������n��
2W�&VR&z���y�hn�>?>�}~������qo�u�u���$y��i�P��Vsx���Mh��B�;5&�h ���K<<�DM���W!���fZqd���F(�}	k%k���$�d���JXC�N#rEOdu�����)zV��=�F�r��z4,��<�#S]!�Ld�������B�Z*������)����`��p�sL�_����w*�\t������D�[���GC#P
�=(Y
��mA�� ���<b}�����s��P|��("U�
�Ub@�*��l!�	����z����6�bC�j0�
���>��~������u�i1�xk����Q�<}Ms_�,���^MR\��>��|��Bk�k�3����@*��=9}sFW$�N�
XRT�������exU�r���43���i�L�k�_�������mB ���$5?:�9+	;n������:��9��#h\hY/{2/�����a&l�������p��f"��8IA��D��8|��j�,�>�8>@.L��a�=�Dx������EN=;:��A��qR���M��uY�.�����'3��G�$_ab����Y������?<@�=�B!�t���x�O��O/������%��$�$��,�����Q�11�Ku���{�A ���W�����U�J�Ae�M?=�<~���wl2D����<zDeTd�p2��IZ�" �ta�0H��'��%x�� ��=�
������<��4�������y?8e\�l�.�G���������0��1��G>���8@�9^^Jk��n��J��[�l���H�~z5��0}N�����Qm���Q�|I��������s������j�>����z�Hnr���,-��W}2���S���_�P�G��&�Ig�����+�u����=�6W3�/z#���M��k
����(�1�#?J���N?v��������?a�����EF���,aE��2���T���������\�U����H�|����^��<? {�jE��t9�'f(=�����4����)�l����+�X)|���7�Y�|=����-��z�Cu�2�aS&����1�E��6�"�	9L��|t�>�8:9��+$�� �{2���)�@���M�Y���4��JMl��s� `����,8/f��5��L��aO�@����R�%t��52��4�R�QlE`�m���� �W��@�U�&�Yku�8\vsCi����27D�x�hx�f����PmGw4��)4�C- ���I�%�.�&���X8�.�P!-�����p��z#fkQ�V@\�'��� 2�d6��
.��MXb���5��+Z��������0^T-��
\B�����(�s���O�i����
����)���
�.M�H��YqQ��9���H�n0$�S��h	NK�t�p�� �}T%%��GUQ��JU��M������]���Gp'���d��9���+�^i������/^��`=�Q�������p����H�&�&�1q�'.��UIU��v�$A���=&�j�g.��Mt�;��K��E�C�{�Rz�W�
����L`}�Bt@+�1>�l�����������@qT]�m�k$����Q�#������{F4Z������#kM��s2��V@6��]�W���A����������Ju�T/�k�$��O��%��P1I�3����P�'�#�t�z�<H����=!��Z	F$�^�~pOKu��`��}�^j8����1X��Q�n'P�1��a"���9h������Yt�������9����E���+_�vg�5$��),<����W.��P>���� �_��
��c|���?�ReR	h�
�za/�A��1_���$��s;�@��O>!}�sv��Y���
/>��Sl�?�<,�����ub���d�*��~m�t���?��]�"�U�Jr�||�2�,��Lcl��h���%A�rx:��;:�����Scf�����!�QxB?�;N��8b�B�c�7a����<�������'�7�/�ul���W��;�xD!�/��0�B��q��G�a�����
��t(�H�	�K�%�k�F�9:�5HV��{�������JPd�+�s(j�Ox|�����$Z|Zv����
�
�et��6t�b&�P�V���e����O������������H(2�T0Tr��N^��75��|��=7���/��>
�a�V��r��zC��^��yB����4���*������XN V_����*�A�;>�89;�+���
���f:-`�w����&����9�i��1��8m0������� f���7��iL��7��0 b�����<e���ABxV|�����W7%
�5g��Z��M���"��]-�V������!���:f����FOGL���sE�Y#�v:_)�g|���w�&\�q����87����������������G�
�@�w#�����T��|��0u��Kt�x�2d�|t�%2O�N�4���/4��u����:�(��7p�������@�^:y�j
-���r��������h����_�d:��B���u��u������y���'+N��E����f���O�6�R���v��?��/��_'���lt��-�<#J����%@,D��A�d0�[��\�o��=_�s<
G�g�����?d'o���O../D�
�z��!8G�KCk8?�Y�T��_�_�(V`�{=]������aS�|<}}	�S��@��-{�B���&�]a�BL��{vr��[I�\~<?�`��t����S?Dk������.S���������*[5{��8��
�����]=��W�5e�7�?l��>�p��Om<�����Sp��y����=
hO������K���������s��g��V��,�e��>������g���C�s�y�l,�f���AF����e��79��Y����z ��Ka��y�����.l6dA�*����!]B�y�*>omR?w��n�����=un2�_A5��l#��U����iYH.Z���]6Z�l������0�oy�+Uk��Y���'[?<�&��oh�=�	�(=�a�gig+����f���~�������Q;,�/�;��������wA ��VD?�U#�����5��o����J�2���'O����e��b�<rh��Eub?���%ZX|��7H�;2�I<6��7���+��>���&G?
�<��?�4e`6e
9up����zv���wG�H_HG�U0�(G=�pa"���<�9)���Za������Kvtrqy"Y�B��v�7�g�D������t���r|~�>��:\�*����O�2NL�qO��
C2�X��q�~?TK��{���Qr��W�R����/p��9D|D��[8�;	���bf�'�7�6]��h,|%�������dS���
�Ef����2����3�yO�[�����~w'���HG&�Fl<������.r"�_=�^���;�����&��H�� 47p\n>n���Wj5���-Nu"Nq"�6���H�1#�b����.�/���H?G���SQ*�#O�F�,����(�?��~�����(A1$5����>R	����:Gn�'����%��E�� ��Dt��{h�,X �
$N���F����j��`���(���Sv�D�����W.7�Bn`pd��]+��n7�O�,�������0'#�=Cd�J�YaUp00�����$�Q84
�E���x��X�2� �W�?��
IF�)4<�0�/o��G�������������0�2��f��E�����%�[*�L����A%/�2������UB�����e�������,xM���@����l�``1VV�3�����`�������Lr.�^3�k�c����e��y�f!��(���Y"�V�]��]�e(��I���$��]��8r����'����dG"�9�g�-6���,�/Q��Z	 #y�$���YA�ox�q�R<|F��" �9�~��`������Z�+�Z�b�� i��W�~�b����������%��
�H��:��D���/��-15'ZN���! ���w�I�#'������u��X����#����4������c��$����&�{0P���A��d�e�����ZM�!D;i�Z`kf���!�oG�v8Z��^[����q/��/O��&��J���a���~r[�o-u��V����"��R�63�p�1nT�<� ���p�����RN�C��[�|�#t-�PE��#���q�u6��@z�6��@U��\}c���}��L�f*��&���rO2�����\�'�4�I�5��J�:G�{G��I����cQjU��c6�%�v���>�P]����I��ap~���'0����;w�����3�C�=�:�������d������e7��9b����1EV U�4h��s4;��0U�+�
�gL�qxe�U��>{�����?�GR�6��{S�E�aS	���W�P�5��5Q��h���b�8���2��29�'L��@�Fk���^�hEX\�T�;N�<��k����>w&CX������G���h:2
����<�����m���|���4I�a��'���� �Eo�8�2^�������@��^XG=�2��w��2:U��3*�Q��\���^��(D�#�����p������N��N�2�������O[�w����5�S����������W��Q4�����P��������\Xc���B�|j�{dl
fY��RI6�v8��pq�3y��E���D-�Nr\B�}&�D������m
�O�1L�7*�TRf�+n���:|F�E-	������*����G�@� �]u�Q���!��v�/��U�_p����")Ia��p���8�����{��c���Y{����\�6��������6W�J������ur��)N��|���+�R�M���.�����F���1�!/��F�,��-�Oh0\H�`b�A�Gb����5�l�q%�����s0I@�)�"~.��9X�FsJ��q�t��M�����[{�������J���M���V����Q�s� gi��w�DrfB�JqsT��m��b���~���r>�2(����
��X	�$�R��1k#!����N�<L0������s���O�&��=C2!�n��y��GY3�,3��8��g8iu{
<Ke��L�QW�B�`�R����[�
n���?n�~�����z
5a����h�BD������2�VH^8�Pa;/����w�+�
�&�m��F��G��x�a��-�4��+]����*�=w��=��(����DB�Z��nh07�r�#��%�*i��)�R&>`�s��6Z
]�F+M^x��:��vz���6��&��a��a�n��Q�c96�c�j
r�Y�X�����|�?�a�m�8��������q��]�����I�Zum=��#7��'eTLC���
�L���J�tg�,y��&q<��c70M)F�7wI����)	���%	y�~��`J^�����U�em?�0��cy����As�&�e��x��(������>���4�^��\���)
�1�t�M�������������^���<�i�XJ&�72�%#�_�H���0�Z�P?$���	{����1��5#��g��������$]���u�gJ��A��,�Y�+-x���K���pC'o:����f�����t�8c
f��5���Ol{;���"'�~��������������co
�]��im^B�7�lg�dN'^�x�'y"���I;�8o��I��5Kf�a����d7_jPM1p1(lf�k_�;�r�+W��x�[+f"�{n������bD�����!-z���`����������-�6��U��;~s��>^���wvr�j��9��1�r��=/�����b�G#��jus�d����W<}z�]0J�Sk$��|�+��%�|���F�,�������{@�?���.~���/x=��r`%D�b���F��]��W������1�s�x����;M�.�C�Y*|������?
��M"8������L�k��(�;����S�������7v��o��E�~�^�eu�H�Q�����������!�ag������1"�u�M#���7w������y�{7e���@z�f������^������NG�7�(��#�eg����V�M9�pl�y�=Z��dA��c�
����Of�[���e���*^�X2�:!���&��Q89b��^�l-[}(!H�rU]��2�X�`��/�a�{���~�@i���-8)�/y`����lP�������V���^�Q4=��c_I��Y�7�E��|�����l��O�;C�#��I��
��UzR��
�q�T��>�@�����_��*����s�7�T`oj�U�Fx��=?����������
���Tk�Si�p�[Tb�ZL�g�6�R}�W���o�5�s�W�ql=�-��x�s?���C4GeG�]isc�vh#TFQ���=e�v�_�����s�)rW���?�Q�bk�c/^�f����,����1Uw.vtl��B��<������l��vQ!���\�`������R���%Jd0�J��h9�?|J5{��e�j��a�G�{�+'�k�-�l��6j�l���|��&����'��R�E������Y��8p	��Q�@Mc+��W-
�gB�Md��b�m�%1���Q��-���#���"����u����������+�E/�^���X����.�Zi�
!@V����%��TX�a%w�G��>���j��v���#10�����r�=��-'z�n���6Z���?���j+
Hh������Mm�.��;J������C?3n�p��u�������R�-%�v��d+����	a��sVK
�Z5���b�3������B��Q.�����;L��+�,=b��!�d�;�C���&��R�;�|����4�[��<\|$C�M������x��y�������Os���N��Kf��<��"��u����v�S��W���|�X<��b�!7j��h1��d1�p��z(t�4�b\�EDb:_2�t�WE/�b�Sn���V��
#t������}��u���<����}�s���@�c�������,���$�}6���3���W+�L&�f����r�N	���B���6F ����85IF�3�8�H��L2e9�������g��yoK��Is�����;��?eE�	;&�O>�9���N�T�����pP�0)�<���|XWc��5������4��)1��E����kB��p|E��'D��q87Vi<�)S���N���W��x�A�����&�,��5������#�PrF����$,�pD�����(���G�\�B_����0
x��c/�YT���
?]�����5��FS���~�P����=}�M��*��L�~�6�����!���RN�<b��>&�FO�����-x9��E�R�����?����`��@�� �:D2��X]�Q��h���
Q��uW�~��WE��:����������'?c�p�g;������o{*����	�Wx��zl����P�W�J�?�����*^�j^K�
��+2�8����o?^�o���*q��b��+3-�����2�e�
G��x2!|������1�#%em}i��������,6�M�c�����6�[w�3+v�_������ ��j�{;	�nn��b\�_������{�B0�>�����o����������>��H�G0Rb��(��;l��L�,�C���'	e�DU>���W,6�����;�Q��b%������+07A(�~��$[�F�ZZ8�����a"Q_����C�%�������)����)w���q�4���C^.�e�m,/��n�?����M�*�<]�m]?B���M"���HA,�u�����moW&��J7�68q@��]A�W�w)�.',�@�������u��>��6|������m+:�I�0��Bc8P�^���#��^��1/-�)brd~�(�tb$��E��W��I��K�����J
�4=N��$L�c~oiZ�����T�y�rB�y�FmM<`�m��`���dO]4�^�����X�������Z�`���������Y��Nvv\�X����[������l��t��V����sn��x2�fH�#���������ak����|U��L�*�S���0�HbGb�h�f�x:K��f������������MG��
�C�Y�������@w������B4�HA���3n��6�~pL��RV�8F����'��q��S%�y6��"��FtQY8��������32�m_��(�|�/�<�o<
�n��l�:��*���R��fr��'G��GA��7,\�
8��l�?�#h�%7��������w��f��$A�>�>������-_��,�8{�,vuY��r�u�3s\6�][����L_Z��^��Z�������������������p�� ������A��r����+�Z�_Xv��{]�tS9�S�K]U�R
�V���xI9�Y�T�4l��r�]kx���$7Fo/��]������.V�B�o�&���;m��'���p����,��l�c�H"a�1
Q0���M�.������Y;:�
m��`�Ql�
L�?�H"���04�a�k�f��G��e��vJa0������Lw�|�-�����k���4�>�e��3tU��r�D�*_�`"g���`NG'���R���|���e<~�(H�� >�XZ����d�ud���}���������S9T'wY������-�25������9F����{�N��6�bs����we{w���#V�=��g.O�N/~-0E,4���`E �M+�4N�y}\2�*�[��d:�	�1���;ESS�P;��~������exu�����9�����@�8�{�=7D�g��
Q��Y�uct�qV������}6�K�8g�j�+WE��n@���'�l�������f~�e���q��r�]����p��A�/�`��wf����0a��������S���d?�������$y�Rqz�R�?�����d���i���+M�O�����y����=���W�)/��	������4����'�50H��G_f��xQ���hG�aO�-qC!#^y���a����5�2c�kC�*oZ|��Q�U�x��F�������Y���@�Z"(���jZ��)�1O���E��f	X
1u�yOpz�iE���u2�t�m���!	1�s4��R�Gn)�~#L[n���A�/�� ��a��i����0�pG��3\ah����Kc[����1� ��h�j���?�����n�Y�S\�Iq�(��S��G��Cm�O)sa��k�l[��M��������S��hXR2��<����5�/�L������'�oQT�-���4�����y���"�#�e����fy����Vp9��\�*�����HL0!G~c�kT��4�
����R��o���W
.���*D�t{���g:�b8+5	F��r<��KX�n0E��"��.�R-��������Snu���
��	w���W��WF0����W\��/������������[j�W�s��k�j	�g��]ys�\��!����D�;s�'�xsQ%��o�n�T��]`L��|����M�{�0b8\��'�����w�R_&F�y��>�aO����n�-���u8u��-1�Lc���Pa��r���<}����~��:W��V,��e7�=A�T+��Z\# �'���9���������K$[	c���"*���:����S]���.�^]\���J���d�(����Q�Q�$I]>��"��o����w�>OE�~gQ���*�N|��SQ�����1���q���8��ww�\s��<�*��u����bI���y;N�Q	�����;FR�������G�K������.Yt�7��+m���7*�L�����\7��Uf�S��������szu�;%e8%�6R;"(��:�w18!k�H����F�~����ac��]~ugaN����Z�-_a@�I� ���P�_}{�c�
�����������)������0����
��^�:60c'�]nz���I�FP<�.������D]Rg�����������c1������}�z
������rz�7���YWn�������0i��dP�
����/����7�4F'��8fL�'NMs��6��vm�.����\���(_�W?�J�f-w{����}<S/`/�~tMO�7��0v8�^����m0[�E���?
�O�xC}g��"�3���m�`��u��''r_�\�m���D�$���d8U��f�E�u�������M���I�?����� �"�L�#m/J#q
v����@7Z�@��
�MI?��&H�<U��$�Sk����"��
�%�=������y�
e�����,���-�0�U�|�v��"@I^��g!�F�����c�z�;��2ZxB
���U���;a?���s{5O]�*�:wG��6h�����'��#��\{'��o�-�[����R��c�e�>8�1t'
��~ +Y��g��~�>#�QAk����Bef�����?i�E���KO�E���&�x�����r�`*�K�5���&���C[<qP=�y�6�o�$A� �QQ`@�H������M=�u9��U��L�>��tyN�����@q���x����%>�^
�5�������R*���N�8n������GR��G�B~�w���M*b��M�T���t��N.��YQ����cO�������������2w���O�5F5`'���?��z�i��"w�W��2���7L�����2�F��.��-���1�����e\��X����'r��������
3nS�*�S��\��Y]�&��T�,z%�w:��9�@��1f�����*�����?� 	�N%A���/���g%��e3e7K��|R���
��&J�)v���y"����`<FPQn���5gX<���i(T�h�>{�����xvy�
@�
��~hSc�M,�?^��?L���}g������1 A~���7����}Sd���I�'''#�V<2�o[R}������0-h��#t{�0�A�3��%1�5=X��5���^xg�K���@#��������K��bZ�6�8�h~i1�V�qD��7��
i����gd<�jp'22���_4�xCx��|����
e.���O9�N�o�����{�����^�0�c������Tk��W��cK�<�����0�f����i��6D>H�����W�D�{���� G����
���Q�v��vb����l�w~���6���7|/�*�Wwmi�F1���h�4a�
Y���g�����R���@��|VmQ5e�+�k~�~i(xI�/	���0�o���vq*��J��l�
���f2���O�lt�����;�_Wpy�%(�]���',�y������L���n��u��L4��2\��G��o���]w��Q�v����3e
mH��V����b��Q8�#r�n3�p@�1��OA0�w0-���	e����R�]c���r��Gqv)�2������p1�����M$�|�Lh���/N
*���1���.y>�<v�}�U�/�	��;���=�k����v'� ��	�I%��x��f{5�h�~O�;�z8{�(-��q�i�p�e����Y(�b�x��k`	�[@.LS��?��w����d�������\�&Ct�� H
	�n���KV��#.��F�-��J����+y����\����������K��A�d�*W�S��|�
=U�����lZ&*@�/�+6�b���E@�3��>���#`���Ob�����^���������;ySrV����'�]���a}��6������~��x���r���]��E����������U�S��zLw�����vl�fu+y%������V/�D�R=5�
'9����r3����$R��+��\op�z�_���q��Hb�#���^��6����0�2������$)���8�x�9L��K
���LM}�� -a�&7�[8lp�����!���������V�/��_����N���z�f+KTg�2�d�]R6y�J��		� G�y�q�(���dT�3�l*:�K��7�Dlb2�@����,��$���4�^����������g^�q�����"�����`��Z�?^��>{����%��"4?9���g���=������%[������.�����wg?��h}|�������B���i�?��N�A��}������oy�y^;��]0(2F	���d2$��`���(|N\����MNx�l��I�<�qQ|�"���KV+$G�
����'b����)�������h>|Y��iWn?�q�����t~w��h��\��:_'�)lFl���G��H����/��������2���
�N���S�c7�j�N�h}u���0���cD�T�xn�)�~L{����fs�
3������'�����llS��'��
��\�y������b��=%|���Ox��9?;�<>=�*=�Z�����1�~?)����H:����+���G�u�/�}�:y��6��o`��?)8�h��M�N(��61\��uq�\x~�������{
��{:�V�T)��s��Phc���a&;9�{�a��4+�{�,��T7�<���b_lC�\�wM��o}�[��8~x�	T�~����M���I���{ �t�N����&��OK��K�9�C�a�LEA4]�FZ\g�6��g����O�c��3��$�,)��.�|B�2
3���d!=A�esX�%��"��M�0U�p��������.����������"z�����5 {�{�06.[8��W/r���0�&\�)�C*�<��D���+�������}�z��y����K@Ky��W�"\Y"��m�R��jz��}��V��?�����v�`Z&��-Z~�W(��x�k1=_f�Q�{u�(���0j����Y�)�G%����o����W:I:��J��'���~�Q���Ki=w_�e"zV5���&s�[a�z����_���M��_'@��FG�x���b$������!J����N��������5�K���dG����-Je�M����Gs�@`�%�tq���5M�'�179�$���o�/�����h�����QA����}��\���Xt_��c��W�W�c�����5!;�r��x�w�J@j��E�	�ei�b[nn�!��3�~����1P'�H3��=u�D'N��6�u�s���IR0�]d��9 c�C�#�4��F
kL���������5�����%�os+
nFi�H@�>?aT��S��3����xg�<UPf��U��
x������o�H+���@����)���T~�r�g�[=��n_�$>�C��O�M�ZZL. ���^k:����`S#��"����2WJ	�j���������/���d��A<�~7Hd���R��V���~��~U>��Z*Z\��DE����;�����������
��*�:68�������v����������]=������S5�kuc�]���)U�W���dru~���]���,2M�I��f��T��Rf�qGf�;U�Q����BQ�t0\G�C�m�h��w��;	����F�vm����g70������J�"����4?��:�<�!z��kN�'��$}��N��	��%�9`r��`z"YZh=���S�Q5W�C�����?��,���{�v^�n��������2iv<�R���s)e6�����I)s�5��\�b�O�"������0���ybN��s����6�Z]���W�\�|$�wfkK��k>�r�]i��ZD9�4
�����_�?���;]�q�,>�������/�4��6����W�7S&��>��0����k� z��&� �~��n.�O�K�bq/o=�{�<�W��:�x�h
E�dk}i�������eO��������~�l%���wG��Suo�g�ho������%,����3�61�8��w��G���u��6���F�
b��UR�#W��c�������9W{t��!�,�`��������<��q[��=������=(���z`��Y���{�*w��h)����5MOg�&��<�S]|4^1�{_�M�������q�i2�8Z]����V`���RtT�L���qG2�oN��f�k���/���t���{��-��Q��fxR����j���
_�R��=V���G�)S������!q����9��������#*��FN_c�%�)����9�0�`�[�OW�;
dX�t��i��'�G��r�m5�;�/����Oo�w�]n���W.�s�:��0�|J)G�?[?���O�b��M�(��.��!�8�y?�@R���}a�g��0)�p��m��{��:�j�H��9�l$���G������"�������2a������U�Cx����(��Y+KYL����jx�J�N-�n�]|}��AG"�G�	�!%�.pJ��?K�.F8��i/���`�����?��(0��A#�,j�H�$@�5]�<f��"EIm���������~�9���q���sk����fp:�Z>s�����	�'�@?�|\�����'�x)��!�
K('���/�L������J�x��aL��w��G^
����rIr��5�������t��_��Wn�r��dq&�5�1l��^:{m(���N�����,t'#`���;A�$�
1��0sX�M���>l\���y��=:kF�%��r�b��������!��R�P����T�Am�M~V���f�<�-�8�����#������9�8���R<�'��<WF&������
��0������f�+�f��
��]����b(�3?SR���`�����Oifh�ia�me�i���Ds��������`�Of��n>���]YD9=w�,������.S���6G��
��|S��G}��v1 7�k�MC����k���d:��1,�����9B�O����(�mx�/�N2���B�`��Q2w-�Lg�&��6���!,��
l70��0dV]�y�f3ei�n���5�5�/����w���H�tt�s�"�g�"�6|��H����l�[k3��� 5�>�j����o�����J�]ysk�$��������)����g��}.j�6��f�G������h
����z�f���s_�H�2�-��q��x��N�c����B��s���Wsq+%)n��p����1��{�����RIq��*���Dl�9[2<!������^���y���dal/��.O��d�����>��s���8�0�����������T~�Y�;b� u
�n.
��i��n��
n��g���g!_�M��b�Z6�1!��m�w���1N����	/F�����O���\:�P��-�����!{�������a�u��@���D~���<B�'�co�h�G(����������t��6��b	���e�d���M��dx��!c'�PC�f����oY�/��T��^�sL���/��H�F�Y�W=���N��$�t���{GY)5�7�k�lD^����dz[l��L88�l$`�,�~���Q������m�4���n)�3FO�]����.s�=#��s����H|�$k�����r��[d�#;e~,7
����]*����W���e^)Q���/���QO���Lb�a�NgsD�F��8�g�+�����?U��/uma��'��M�O�;��G���!�`����:r�6b&v�Mb`�)�~x�������8�!1���;F���[�iN��pnu���/�%~��98��*�tOy}�����m?{�+l�B��F������1���1�,$��F����DIxQ
Xb��x�-!���Y��U�X�_M�&$�l3S�Y�Ts�r�"#�SLi~9�$���&�����N�U~����"@|s���c��g�K�U������Y_[��<�m3��������;���|�t>>t����!��ex�%��9�C5U?8���x<�J��E��y;�%6�S��.�aN�?i_f��+��`��?w������h��_B���������&>������M1������K�@��_H�����>_���3	��8�L�� �3�?/TS���g�"3�Zr�������r
cv����U���G��w$��xs�Og��� $i��^�d����4m��ps�9N���j��X
��k^�<MC�L�v�����;m�����9��:Z�m���]�i�_��%��F����m��c�����c�c��j��#�u����x2��T%����q5��.V���6�2������c��j+�)2�����������h&�� �h��
��V|�@w������^4N�&�KS��%�1����u���bN]����������� �6��g%�"�o��D��2�0vL�&hi��|���^EQ��%�_�����Q�c��������:����N����\a.+F�S����
���dv4���1��{�{��>nK�u��,<no�c>��[�����s���|��W��GG}�������g�;>�89;����v��yR08{����x}�impz�z�J��UD������
xFNa���3��\�RM;���`�����u@�a*��F�~pc�h�Fl8,��X�tFox25��{�e0�2���.��G1rW�����d2"=�S���
N��������+����CfFr6P_�e�����O��lD�������Y�R��1���
��W���vk�}��%�l��'������/�� 6��_3]g�U�JR����$�sv~t|�~��^�f��\�z�N��K2Fy�����cs��y<�^ELALE��ERf>RD(9�=��_�Uo<����r<��(t;�Y�
�`\�������6�&�f���J����
}Sy�/rfUE(@U=��;u�����R�$��_��-v@��;���?q9A�~AM��J<���/���&��i�i��X;����pY�tW&
�n�����	s�c����>bv
H��������
:����k�M�|"L�d�X��g�_�E���g%k���r��-��E��D�����/=v|�O�'������KrvVxU`�a�zO�pl	�h;�J:c�!��J��2�������hO�JO�)I)x����&�j5�F��d���@�������-k�2�5^o�/�����y[��9�q�����Y�J���I1�k�i|KN�|��g@�D�,��R�i�B�gS��RB)ds�M������C��J���S�XS1���N��#�A��-��e�{,������r�_A��������jvH���g%]L�g���n�T<����S�)����
�Ld�S�h�S�V��tf !�K�,����k�����f���J���������+xZ(�}6�j�g���k>2W���9������_1��/����b�]N�>z�US�g�N1���32���H��l�dY?��*z*b�We_R�H��R!�%"@p�q@��/��E�'���������:���{yH,�GK0���h���I��\
�z4�5���~{8��RTU�l�z����R��l��!�^�?���{����2]�R�Bk�--F�,FJ���c� i���9Kh��+���AWe���h�o�y+��:��q��rp*FnK����-uj
3�U�����-� �1�1����J�rJu�]�-�++
������#��~p�2I6�z@ �1*K����AQ<VI��a�t�(�VY���E��JX9����������
�p�s �I�J^3%+uN�r�o����lx�R�J�����������!3�v��}��Ib�n������+_������"��M*o�}���E��\������x���K����;�s�+����_�bi�(��cY���l�rQ3u6�Z��
	������*n��FW�K��J�1�T����������y����q����������}@T�=����
������Q��I�4�:�����!��Wb����x���tA����&������P����Q����f������{R89}s��
��	"�h!G�A-�K�s�[�d�{sX�+��x����B��vA��w1����~HvH��������C��x�Y3R4Z3�u���%���B�+M���	����<����,x�F'�i��M�V���1��,W�r��Yo��}�dN�,���}}27����n3dn%
�-����%I�7D�r]��y�W���d3��C���tyx~��C�������������VN���h���Gy}������E<�f���78�M���#�d��6���K�U��gm�]jY/������"���0dO�D<R6�SG�L�xB�Oa�O'"i��#e��a����E��b��1:fX����P�q'7�h����T#C�gl����3���oa.j���G%�[�RB��uOF���u�����D�S���i�p���Q�z�v��*�A�k@eWxE�v"\<�C���B+���|}vzz������}����|��~���%T6�+X�&��%"�������O��#<��DK���:C�6v�{D� m��)d_
��W3c��C��i�D�Y�h���0����*�?4������^����c%���a�aj`UK��tF<����������,���?�m��mm����S��Q�����2r�H�<�k��g������N�y�cx(a2�
���� rG���������Bs��+��\=�I@O<���S5��3MUx�~���y� ���p�?�=?�h���9"l��������GX>�Ex�����
�D�${Ys��-�_.!K%f���@��;
��#�n����d�>�0��`0����CV����W�j�
�)�|x@��,p:��R�����p������
 q1�����	Mz�-+�Jj�R�A�+d?��a���p�������Mv�i9|zKB�_2td��I�j'�/�}d�1�1���a�z���I@�Rk�C����0�V�i�������lk����$�{��;�G'�_�BeZ�bVX��r[�;�L�+U��e��0a@6F!����P]z���m �1�p��8�t��N��)��"��X���������s�p�HPW=CV�������(��Z/5e$~5�Ip���
8;�tn�$B�d�����%���[�A���A�J���}���]�� 2z�ms��+�q8fi:�!�XV�s�(�E� �+������^�V��g��?E��\qi�
�������|"�	��z���W %�������.�/�����w�	��I1,#���b�,pKR�Wa3iEA���	�:�������m�
�������x�]Wv���z�K	�uB4U�|��-���9���o#"��(G�RMS��`{���R�S�o���k �m�d^������f���f���Oa&����M���
�/a"��y0���/�p_�[B���e�]��W�n��HJ{��/oT$�G���D��>�1^��@D������-�Q2��6�q�`7�g�O�E�gTMt���M�;>���fv`H�����y�����xvy���!}����6;���_<D��f��T�wL�;�+������7�8<�{@�tU��*K$�����n#��W�����H>��Q��+9�����ms�r)�<�*�U?���L�=H�D�yu��7�*���P����!��i$��"Y�L�J�( �S��������-��B�`���.�x/e^�9uvq���.��.O�����
v�����K�����)���IoC898S�s�Z�����A�7�s��u%��$��/��d��v�x������M�(Y��V�b
������$�d���^0�ec��7w�"o*�qJT�Q�2$}�,Zk�v,���RR�
[��a�������q`v�S�~NV�p��X�&���"ov�Y����YhmdG��.H����X�0�Y��|G)g�)�faR#�]��W��;����t�v�r�F.��c94����3��j��iyVix�ZQrN+]D�6�$�h��l�`@?����sM9���F��DN������:}j�������c�Dqs��W�����i�@X������������+
b�9�5�r���sr�9����"$H������`8�{#���v5��=��n�$���������l�)f6v��
��`�/.�ON���q|FH6m���'R�����d�Y�Fl��e���������|CM�+�Es�B������`E\*���u����dNt�w�5!�Z0�-�!S,D'Yj��rKXe������[�Y�t[�Ko�L$��y�������u��\�W<(.�O���9�Xl����9��d��A��n������W����:��1w2��W)����@IO�(6FN��'h�v�v��^$�au�~&��B���uIF�x���<;0O���]H[,��[
�R4=70w����\�;X�x��4D^n*�-"�zV8�\�3���i�0��pP.&���NF��'�oel�Wy��O�c.�����DR�DsM������J��T����o0�m�eEI������u�j�<�	���0!-�9c4�N�-���O�N�l1 ��~	�����V�uX���%f~�����3�����Y����@5�{���Jw"�Y�)�Pc�;��)7I~^x�eb�T�$����*��l����(�y7����:W��n�0k�����=�/�
9�v4���($��c���(���w�iQ'S6�A#�k�������������;�������M�~&	1&z1�h&�7��0Qe�]�VG2}A�!���02���$�QI`���t ��/���*9o��'3�M0w�X����K7z���nL�1����������=Tgj��i�*)il����d���lg2�I2����/�%��l�d�����h�X5����1�CB�#�1aDp��Jv���Q*�I��u?����m��k� �H4�1=*��0#��7Vei���2��S�:Gz�������#�`fb-ksy?�R�l�+�}����s��XRN��g^����(2z�3R����F�J��b^:����I�� Z�{��C�w�����w�L;q���Cg8�Wb�3�n��.g�����������9=FK�iP;�����>tK����k=��G�X�����
������"R���p�BD�&b)�d����\�v�������	�%t��nxM&�_D��5��5Re�LD�t��Mv������T�4�g'G��rx~CCG%+�8�
���G���TUDA����N2�G����J]z�Zp���C�&#�����X� ��a��M�[Ed/�V��a��\R�af��c
���N�������L�Z��$�N]�o��=!����w�b$�+���\��d����D���������/�A��n^�3C�������x8��0��.I�O�Q[��<�����'O8F{���(����X������]|}y7��3=�B*��z0S�����S����@g������T�AP"��M(/2U�s��;#��\��������n�����U��(�/q���s�|�
��?f� ��9���V��v�wbn�	���K4[? �dW��H{������/!�@P���F#wn��F�4�s���6C��.nA���0�+�A
�b0��n�M���[r����'h�	&�0�%��V+�����H�;�w�G+��u<��� .��pwW��%���.���O"�KaZ���!���o�ch�!���-�#�0�������+�r���q8�q&�<���1���������Kj������OZ����;%���\���b`~E�����M�dB/���mr�����$��]�}�w�����Z^C�n���tM�\?����
J��W��u��Yy��������nG�80F�w�c%m]���A������;�Dn~���c��M&��!w~�����iQt�Bf4�+�nv3��VL�m8��0������/�?��I���:q���b8��Td���(�?=�&$��������i�ew���+��=c3�[nU7����������u�i5m�������m�W�����N�g�������t&�2�������3��@����g*�VI����_�B��������U�J������	�T3E8��?'A0H���O��-_���s��{��{��������`jr��|�Ydn� `�a�M�?�`���>�(��!w��Y'�Q�0"� 0��,�_�x��^���p��������x~��S�2��L���Y��@��	o+1��hm��w���R��R7����������^�.d�e�f������7h����{ �E\�F���@tD�YSj
|����Yy<�K3o�SF�V�U���[�������W.V���2+�����19KU�>�
o^
o�V�\a�nL<#����.|Th��k4*tN{�!��$�0�%,���`��U$*��Z�����-T��b
��p�}����*hM�Z�
��b5��b�_�1|����.N�^���$�4�#T������%�
;?�O�^������v,��f�;�9[��^�=*S���4��;��\��f�:�zc�:��J�'I�%~h���Z�v����������_~����fX�4~
�"�cR�ey����3���NG1�����_+�����Wbl���A��Sb.VBtb��d�f�O�#b+����:��%�����r���S��'i]��(4=����[��0I��z\�?���?���
��%dRL/�i�5Z8��E���	Z�����A0���}9��&�l"�V�W:��:�5iW�#�U�R|m�M�X��*]�����Z�`\d����A7W���5Bo��#02�d;���\g���D�R{?
���*X2��{���di����|����u����:����f)����S�mv���:���\�����=�3��+�@�i��z�~D"�����g���#��7���|eoir�h��O�L�P���y����D^�pZ�����3:3 �W8��|������{?�8{���'����x��
E�p��O��LL�YC�p�B���m�}� ����V!�)a#��c�tG������5�]hd������������aVq���{y}����l�e?n�����w��S�Pr���hutrqyr
?���\��c�l���������ZF|�����i��~93z��fuc'�?��.�B��a#^V����p��>A������w�?��^��e��'#������J>|��n!���onu�6bC"|�y�s��t�4oR`��
����c����/t�t�k@7K%�L�Oc����ylA�rd������2�����\���d������5���jx�,�������Q]��B����b�*��2��P����F���Zf��Ez��O�F�f�2�>�����UL��$.���s`��L��o[AA&���n�z{B	�V�V��<2��BL�6���� Z^��;�&�"�����x�Z�M�����c�w�h�4����EW��*�$��PH� l�������1~y��F��=a?�m��d@����:��@�f��I�I$:��>�j��6�����������L�� ����%��9��Q<����s��	���������M����)�����Q!J�"�}A"�b�gj��}��xv��T-���B�<���V���Z_8	.N����hn+s��Q��u!��@(�@�te����3t���s���h��%�~�<\�8M��^k�w�
0���R�9�\��R)fE]�*�����6.��m�H��(Y���g�@�'�}]�����q�V,�(�������	����'�2�(-�V8���T��J^�i�&�v����A�)�+^��Qf����1�4������J�2�6���~oVT}��,�rm6 ���(���$��
��������`'I�1s������_x���:�Um@X=���}2t��"�w�A�_l�?�A���Y��K&�Q�{R�d����*������d�R���}�
�5m�C�dKu`\�������X-�R��z�wAv� [jB6[A�'����bY!%T��0��A�����8S%��T��"��f�U�R�-51�u��I�by��K��������L�q/�k�T�����������t�?����:���P��1FAg��q��� ���9����|�[r6e�N�{Tlz��kC�� �� ��7n��TK�^�u�����|G���oZ��N���P�]pw�H[n�����#�
{r8�75�Y����[b\[����������/6�e��<�:%K����#b������eF� 2��<�0=7��]���.|w�,�..ndMg���Q��"�����@�J��o�_����O� �Ah���-�����
�L��o�<�	S^�T����������O1���T"6:_���pg���e���I��Vc����H7��?>��R��xqr�|��,���D���������lr{S�:W�z��T�Rb\�}�����l�R���-���5�R-��8~��zi^����*)m& �C���;���T'�vN��@�����wg����z4��I���F�|��U*�o�-�w��� ���U���
H��9����J����^��D��n��)dO���' }�
qG����{�u���J���m��6��'�����f���bnF�	mo��a�nYKh��~�@���o�
�i���
t�;s�����i~�UpoCX���8����}��hy�f�>hM�G1�z��V�A�s���_6�Zs�����P�eTp�5��>} [qO��u7��Z[�x��f�������^-�c�����������:U��k�2�S���D�����O��l��e)���Zu)���\�����K��������'7��mA|��{�����G����I�-���/�/�05^���c���k���Ku�k�q|��xpp��hz��&���U
��~
����)Z�h,��$��z���WWf������$&�*$���V��1�oT��a�D�* {���OLR^<"hY!_9�U*
�������k�����h>k�L����*�~��R?~-�~pu��z����;�����L��)t�ko7�v�d�Y���5\8S��v<�P-�G���	�f�;9Z�7��>����.�C��}}4B�['���u)E���}���8{�f��KU\� �=|��#�=��k��Z��K>W�O���:��i����~����g.�k�Xjm��vQ����n��iQ>H�oa*��(�{wLOIU7~'�<G��9�0�8�l|v�����i�oh�������V�^���=$(}U�3V��Z��A<�����'��A�\�j���O�?�?��u�V+o�<-�+��<����Wf�Z������� � ����:\������o�!A�3EK��e;�_����t��rp�6PjUku�L������4�4k���t�K�_���C;
8�y������h��Z�����C���)n���^lx����F��I
�������C��c|�<�*����$��$������w)������P��S���BWe�w�dU=���{��u) 
5lJ��~w@��~��d3�S�m��Q�^,{�R���>�y�*�y�`����g^�w��t�����t�uP�oXk����m�u��!Y�xZ/W`C���;���a�<\��Wo�6r�2�K����S"�,�v��~S ���>�� 	GPU��R����B�_�`Rt�I���_a���0��]�0�t�,15������y�*M��@�`4����h<�{�X$!CIm[[O����Wp�;���
��+l�Vz��F�|��R$I=��C���+<��Z�<�����{�6VM���X��=K�o�RS(5y�����u.����^�?�_�8��I����wlX.<���:o�&��Vm7��9���bEF��h9
��Qd���"e5p��VN��Y.2Z:
owt7�O����4����r��l��
��������k�c6��Xr�������_��>u���U���Q����}������hx=<�G��W/���o�O�����o� 
�/�/./���l8�|
��6��l��P<D�����_�^��������������v'���e;>W����{x�����c���/�P��g�oN�r��y^�����O�I��h��@%q�FN�sKY���#���L��?��~�v�����TT%�g����]rJ�������w�pG�����������Eqz�v�vb�kj�7I`�vq��3�a���	�xl���1!>��0��_p��?� �j ��
}	��lnm}&�G�>�
?i]�#U��_����^�O���(��72	�%IT���������������1��3��I�?n����R�fe��O�T���n���X�)��O3�7�o�q�S�O��5�����p�:���]lm���M�����m>��M�v��������D������$��_�����_�g�Ar������+^I�f$?cK%��E���aJn��a!���D������g�0�"��}�Y��#V-qU�������	aD�-5&1�_
���|�`5��4F�.,�R_S8w���Q�A?�`h)��dv�����:m�"�J�^�����u�DL��,0�����w���gw��BW�{���8B��F�����R;�3� IC�P2z���cU�-���s-�0�C*H ��0��y���{M���WR�a�m��d���2��([�h����O��p����p��{<��-B[��z���*���6x
3.��8:{}��|�p$��������]����t�|���8������C�d���.��z
�I�2�q��*���1�����oD�0@�D�Y5����^H�c+����e1V47�V|Ia�b��e������^�"rfN[F
�VY��$�(�}��M�~{���\W����b��]+3���.Tk`_��t9{"�.�4������6�?L]K��_��'K�����F�n��hZ���j���?��4"���|D?�#��K���N��T�L"�����k�dB����l�]��0^r��2����/)d0���w�,���l������x��@-�	�_� �����
�E6<)�k�P5�.�F��
��yw���
��������A������]���|���!�~-M%�"!�����?
�I�^���	�r����'=$�Q����������H�K{�����EW�v�+	�E&��DH��i]L�p�0�t�7CTL�k[������I����[����
��	p�	�Q�f~�����x�)�~��H#+�I�B6���-t���S�YE��
��R��6�����8��h3�A� �|���Y��O`&����`yX�-�����+�f�Vd��@NTn�}��`����P�n�k����o���Ch���_Bo�����vI�<��p�~��L���q��z98���~��~�eR��?�E8��������9j���7�?��5�_�1������H��0���P��"H)���/�������gz�..>�?~<9j������^��H!;0�B~K��u#�*��������&���	�E�&���/�~�e�9�N79{;}�	X��{7�����c38A���Xh�����ZYSbadi1$��/�]���^�}���������2)�_bS*8_����i���O�����it��W��c��@���b���� ��o��J��8��W/��B�l�����Oye�����J����;������Q8���^~�>U=�A�1�E��B����1e��-���E[q(��U]�v{�e��NA@����0dVyw�`���Dt��$�,�r�$Xf��{�s��M�G�d��A���U	h�~�=�{1�o�� 7��z���N�]�
s�����$y5�npC����xjL�#�����{w��-�D��W���a~���|�x������9������<����/��"+@ne��MP-	H_x�y��=��C|T���p��>�&��\.�q�{Z0m�l'a�!����w�v�V�{�b��Y^�/�:������jB���B�d������
e����������O�
z�3�
�6��E'^KMG���t8�?� ��P�8���a0��;#��ml�
�wB^\�V����%}y�ZMt�l�=s�t}M�({_>���u��v�w��N�!���9B�n}���y �\��]����N�u�wj����$%mN���\�
�R���KU�k��Zu�^n���b7l^��%�p�b�F�0nF#Y���o�g[����rZ#�����#�qc���B2?�P�n�y�E�9n��e���p37?N���[~}7y��[�Z@��OFi�S�*c9&&`2/�7��X���������I]����#�fp�W��/��������"�D@>�-0�j�u0w��-�v�hd4_�V��3��,G��O7V3|��!k�#�z�
��s=��,	D]��2��,��)��������r���PF��o��8U��W�K��8Gq����a��F���_S����+h.IL��rG���OX��{��T��k���@���{C������ z��;��;i����>��m��*Z!5�?uo����\��E|��#@�7����,p������/=8\ys?� ��}�t��s�	�����`d�����8�d���?�;n_�����)�<cgo�<7��9����|e�=��;�A���TAD���!��#��9S4��Zp���:KoT���x�O�c�B�*�QW0����EqT��M����d���@���u������<�gg����9C?)�0����ci:���$�=!7m��~L��J�������k�G=h�=soS~���A�_��~bt��X~�����~�`���	�9s���ito����'c�/��I�uy�cK��Cmd�',rc
:��������N��__������t��1���@���D�1������.�R|�?�!�}��iM��������t@�����������D|��*�F�~;xJ��OR3��$��G�+�)P��"�q�6�a�k=���L`&�>?���
��6z�u6g�}��\�d2���7�8c�����V�	9-R.r���#����SI��g;M�+u�R1���C�h�5���E��o���R�|�p��q��b�3�G�������Qy#iuT���Se�
�5�>��!>[�y"��7<�����_��Q�
^� 6��"����4��icLQ~*��H�����1K��'�����D��!>�#X/��R�/�7�h|�R	*�D/�g�iG��$J6(HhY���l����C���l���X��l7����pH[�o��0��( ����S����`��
R!������1t��m\	��u��������C�!7[I��������:��&�2T!_�@+e�
���,V�%�E�G�uH4
�XD�{��h7u���-����y+�U}
��K���I�1��������p|#�,��G�B��c�L�������@75F�k�
��F������m�����P!�{K��Jb_����0Zh�<�8���H�L���G��
�71��L��<K��))Q���3�pbQ�IE5���a�=�J������{s��x��PfgC��s�f���2��`���+ �X��� J�%K�#���	��&������A�o�B�#��=���Xg��C��3���y\M���A���/���������y��0<��8��8(�G����@����Q���#��T~o����kM?>+�{��0�������MC�@({�C��+T�1�>��8� 6�d����N�l|:��"Y5J�+�G��"E�����������z�~'�${�eu�O��a�N��<"�8O]���T���W���2���v_���p@O��h}������_]�����p��m��]��C�����9M}�(��$�V����s�����O��}~jk����Jv�I���F���x��8��u�^I�y6�������������m�`����m�AK�����\X
P�i[����}�'W.���UA���xF�����s��H����LwH�3�j�+U�5e��������_�e��l��A\9CI���X�@���u�JL���{�i"�v���&ECIB�qz'�|\0"S�!!#Mi?&��9�x'���R�r�wQ��e�?�����o�{�e���]�44�[� 	�����t�]���y����7��!�N3H�
pZ��H�*�D`��������i���jpe!��$�h��
�<gQ�70�J�IU!��8��'����3�;5�f/����D)����nl�`&0-���tZ�7Ad3�;��H����,��x�qdN�y�	�DA�Ry���%3j	�EV>[~qW�bu���]�zp����gl3�O��n(���]6C�{&YJ��L�:���$�~��<���b��*����WB��v~WFu�xp��B���%���f
������!Z��)�Il
�@'��?��(
p�a}%����'S�!L\�*I<@
B"�uw��Atw�;�����2�^�n��N�;�*=	J��8�%��6��:y�����4�_-����J{��(8S�L����	+?��/�<��dsn���{��H���g���a$�,[���SS��Q�:7�(%J������8��Q�$�3ri5���Pf\nN��B������G����sn�}���?�X��s�	��Yys8���>p�b��DE�;���HE���G��8&=#���>.s"%9 <a��p'�s��V�Db����_����]��/����Fr�v��|!
�-�O��3����?�	������)�{��M�Z��_�Oip�T�".������Z�����6�0���a����.���>�ac������S��O�`�����?�E6;7�������^�gjzlW�zO>O����E�~������=LJ���GA�4!.!i7�~�D���o�����DfC�h������<�v79w,��1I�T���#�q
F/G�Dn�#��&C��^ ���p�%�Y��Eec��9L������F�?����,e<�K"�Hc��Bu���K�4j��
*�j�A*�Fs�Z���J�<���/�n0�^z�NN��E���fHr�6cJ@tJ�/�^�+�B9]!GPsjY����d<"{�)��L-g����lY�(��	��/���*�[7�M
��c;��o��=��z����nB/-e��"���a&B�\%7� �D<2��xT��?��xp��L��8����W2��_{"�
������m��6�~�$��_��������vo-c:z"}��?D������yUnl���t�w�����a���8nQ4�}�6+BCa�cA�� M=��f��[�>px�wm�B��Me�g���z|J@rK�_@������>�5XtG���`	%I���s��i��S�������<D$`|G��+,Y<��P�!�7�>���,�C8.j�h�)����'b�?��@
���������e+��;�)�&��}1��6�S���1dh4���>&�������� Ao��;�W�W�/=%�����3�9����0b��;�yZw�M�9�)_n�I2����Gt������
*����Z'X]����(gD��{b9�o�&��9�d��{���t�b���,��tL�g�h2lK���-�E�(dO1���q�T|g?�vx� e�B*�m����������*J�f�T(�DE��Iv2����7�x�}sx������H�(������;��������������c.��xxq,w������C�O����(�������9;��hD����	���������>�������'�@���������[��
�(�<����6�S��-�\Z6�����/�$;�Z��B\;�����"����v�	�+7&����{��;><-U<���k��\�x�v;q�)������_������9��8E3B���=�"N�Wn�#S���Y
'c�H��ws7�UV�Yh
�\e/��~��?��2p�Ba}��#t���&:�H�_��9g$���� ���gdz�0S����E��LQp�;!�)���Asca���K�]ft��Ba���>p`���Y7 ����?��&s�H���-$= ���u�A��4'�����H���w�#m%�	�O�=�^�u�n<��sd������N��S4��FO${�IE��d�����7������@]��!/���<��L�[Dp�!���>z�x8�[���	�)��A�`6O�u'�\B�����Q�j��|�C�8P�2I����F�$H(4��O���!�x���wB[�:������`�"�WW��<�C4��H��`���+3�q<@�s6�2�t?�T�,d���������%8��x��t��&��I9�1)��6~Kn �K�l��,k^��&,i�=xEM��#z�X4G���!��S0�����n��XjaG��4��
Sp��������iL�,��"�4��Pxr�<�c���Q�>yA�?s��oz'A�	��k�	���Ge&�)A��^�����Vci�)q���=�]�����9���������Cu8�.ZM�3&;`���z��..`���,�u5�Vo�K��l����gb����Uo8
�$x��V�������Uu5c`��vB�;Uo6�Y��5W$��T���
��>����k{���,�/�����gvz�~��+��*sD����T����{�,�RQ���9���{��.��;
�8q�y�C.� ?u?66b���I���W��#�K�H�3D���C��?2k�H�J�Ys��g�t�d��5���@��L��yNf0���^��-`x,&'�x��CH��8����ranq��Pj���{!4l?*�������P�7�N��o	via���d"kT������'�vh�L����w���e�m:�C�DX�*���:�=5l��r�P=O�;��KS��r���r�E��)��)�)���4������H���<"�:G��Da���|?|8��������
�0�N1�������d#e�(���w�:34Z�<�]�N�f���'B��da�M��������k:�#������l+�U�Y|�I�������%�n��b�ca��P|��W�_�=���cH���y�|�.�.����OJ�U�mT���W�{������������&�T�\����v����F�jz�0�BQ��{�ky�T�9��������t.�����^/����f�I����87X5[��w�Wa
�$OU���v��xM�J-�#�\|8|}<����;y�?�,A��-B��Rf7������YcQv�I���`8�#NK~�o#eBe���)�",�g)z5:��f�i���$�O(?���x��x�|��}��R�7�����������Q"_9��wY������EX���St+x�i~N(�� uo�:���V�����~��<����E:?��?�<>��
x}x��������_\�����}@Z�6@��1%4[X�g_�0r����}��\XA�f@�@���#R+�\�e���|��&Hw��`if(_���.9G��B
�L�"�69�p�,�QL��7�*wn4B��t�^��V����������������R�dD�� �'�M��'iA���\�
�����;}h��@������>��h5�p��%	���:��T=�kR���p.S,R���8	2���(�/{���e,�l�|*pj�U�P�^��7������{�|��k�1b�"!��&�5�9��^���^���6~�~Bn}�/����p��v�7%��U�O��riAl}����9�����_�z�r�\�������^�~�����^�j�+1�I�oz���	\x��Z�N(4`�2��O��3����\�����������:	�
�E|���?L�F�G����6�'�	F�pH��#8`����?$��s�
A�@H����(}��M@Nd��c�7\��e������'�7��@�m����������m�Ciy��tQ7h�m��#L��oy5Q��0������`����w�d��]Z�4/�pO{����Mk6�rQ�=v�������w �i2���M.%X%��_� �(��4D"kI|Ra�~����b�3d3��$���n��PI4Kd�0.�ZE��W�r��+m��tx�w������H����0q�����]���1��E��yh���#r�NkF�B���'t�U$"p���R������H�,W;}�c
"	�#���K����r�w��
x"�D�R��=>=X(M�z�T���Mn����V�����=4��Ph����A!�
�c9���3�)B��+�.�X"�����~s?w2�L�1}�6_x��Wd�8����y�C�����@d�=�EfG��Y�����(s��W"0�s�}[����wX
�����+��g����:$�����~�������MC,K�\6�����I�}&>�m�
�6\)�b�X+���H
\T"���)@��9�P�ZB�x|�=C�'��j�%���+�<\��}P���1��
6c����'I���b���l��Ie����I|�S�1I~S��0�w�K�s���~?�7VeN��J����n����M�1�!�4���4/2������l���_C��(~���)EA��0v�x�Q������CX�'d;���?q�q:+BU�
8Oo���8�mJ\"��@�I2�^t�G���w��X���?In�q���rG�,6�<t����ZF�'�=��|��U�#��xP�DW��bi���d{��X����/�r<��/:c�.�X<���@�bX��Q����1�Nx���K/����8����WW��%�����0�-�n�Ko*O�{yX��>t{�{����I
��A�K�[`�������������_���-W���UN!<�:fg�N��$���D5+�������Gg��`@��tO�d��(��}�T(����6z;Fw����<�G��L��)��J)�q���	0X�������^�V���Mx��b	�T*����B�H>;�T"������2�3���$����s�#]���R����������ttrn�[3z����p��3�cQ��H+O@!���l��|K���7)�q��D<y3?
R:�GH��7�������Fp����� F�H{��9��Y����Z�l�t�����e�~[���(z ��SjK���d�������.�Z
�P�Qm�<LtJP�6�g��gD�//��_�%�ksX3���
���S.4<���	�3�]q��S��eE=��<*����zn�����p��*	���)s���l��;N7�����:�g�
��P��t�j �1z�Y>�����vf���J�)���K�*=O�����7��>��������D_����B� {xam=}��R(���A�[��4t�ax@t+��bO�bs�a���4����e$�����8:L�'hL��c��G'�y@���h"�~ikc�l�=�3%e�K4�V�yd4�k/�9��`E�{B|�3����Y���� _�����`.ZO�d�B��y�5C��6��Rn���O�0
pz�>� �<����
m��D�-�F*M��%N��C���
������O���'�o=�u7;��������/��D���V~�w���d��9��
�RW�7�6��)��f�P'���7�o�U�]�&t9]���x2������{�^=3��r����S�u����0�o!�h%��G��M�MFZ#���|y��������O;��qwo�J�� �.�����B�9g�����y���3�-*�9��-�Q���xY��'"�$��H���Y�	���_�>i�����F�|����o8`�x����������yHN�Kv��8_����������"F�>������a��;��I��v��[HJx�7}�t����Z&#|\�Im�>|t�k��=��#�`L�3LDW\C����b�
��vGp��by�_zrM��E;�����bo[�g2`�	�C��������.$(�
"����n�������6���r��8(�*E^�)�s8&*�6v�7N�
�Dl6&"w5&�zN�n��6��g)���~%yz=���&^0L(l�$q���|�����+``^j�d�mX@7��E0.G�R���D��T����P��o�w�c|����}�AI�Dy�H�����S�Nw����u����I����FN����������j�G���f0�Py� ������x�9nZ���e{�)�ks��'$��"7�|
��*�QT2�!�"5�t���������.�`�H?�h�i���X<V�xD<=��
����]�`5�)��k��F��B��xtE�]��KX8s��K��C}��` P��L]Kaa�3���+d#(�9zi%�#�����g��?�2�5��x��E��=��1�'_��K�b���
�N
����X���=��3�$f����������K
E�\]/��K��1J=Q�'���	�A*0R�J�X<����A��A,���Y�i 4��������}��o?^���3�����
��i�L"���������a	�a?�]7�%��9?�����dhoh����z���D"�By�����ouF���&�����u�f��%�����/��/����2��wx����2�z�=���cJu��<���C!3?uP�����\^�`;��/;bnOp��'D�]2�,���wI�1�-���6^<F�5q����	(?�8��'�\B����������t����1?l�w�}�pkg�I���gdV,yA���3�M(��y
u�6K��2=����[)h�����\�/�.�������ib/�������u����o@7V
=�t,����B��v�'u�bt=Ue��
��f`�����I���J����Q]����<���y�L��}�3(�Z�D~~�?�R�X�s�&k�������2�- �k������Q�6H��kq="�4Lb���Z�*�(Y�U�b3��q�����.�XK-�V,W��7k�j8��x��a]����7�?��g>�I��Hn��[Y����/�Oy��.Zr���<~^
�3+h��*������d�����hm��
B|�R""�C~�Tq�A[^���������������w��8��?�������]�k	#����$�|5���kA�*c���I[J��9��E3�������E�e	����(I
$���a���}^/	�}����>��n���~�~Iw�����]��y��B���[�Y���^����dw�T�'�A7����R����
�r;q����rKs+��/n�?-�����!C�*�f�W]%��'
U�,Yc8�����
<����Ih�v��Q�J%e����������D�|T��#���!�[���@[����a�7��d�G��WXk$u2� ����O�
�{=�=��j��|�we
$<r���F�7�������;�#�OX�z�������;���@r1xM���[��>Q����p�-f0�����m��.�'����&N������D)��f����\2�2i��/����"�����7�����O�����*~)�%����B��1���
O�������=t��(S#�vL7�������?�������������f�przy|���t�T���/C?��R<3�WB�h�.�hb��tW����P���q�t!��'���s�jF��PZF;HB%�h4�a���u�<Z172o��j���B�&.R�S�M�Fj���&
2��p�#wi�r7���� #h�eN���J�	�pS���j'R����n|1����Gy�U�8��^I>�JM
��1��~��X$?�?�{�������_�"N-��F�t���#���7G?	}�hrA�Y��|v~|���}q|���s��#\��9�3��o�?�����-6���_����nc69:~s��������@�U����\��^)�V���23+k82N��
"�D!�O�#���3��U �M%D�M����Y()��!�����	��������?�{��E� �j(��	��JA��"9��0,<��h.�>x���aGu������L�Z*�C��:A%AL���c���������B���)���+�x	d(�zl��l(��"�Z�5�����M�s|��W�����"t�
�Wh��P!���0���j��wACZ��R`?��z�P��2��gJ�z&�?�RDzPT�U�r���Of��`:��Y���T���J?��<�>����j��Ec���6	��B�l��\k����j�$�9��B<����[�2����yW���4fT���=�#�>�����%s��=�J����T��}s��W��J(�z����a��p�/c�J�@�s
]a�q?���vy�����ZY�VD��W�3np��U������0�0{���3s��f}�n��:m�������h���h��X.����Q3�oD�W���a�)�Rs��t8V�����Z�sn�)�p��C�g�P�&,<8!����U�J]�����f�^��?�0�Vs��V+�N�d^����r���h�g�#@ nmi�inDd�$�EQ�8U��Y�,������g��������&C��A���?L�=�t��Fqz��q]���������Fo����{W��y"�9�������S��P�*ct���s~�����-��a���t={&'O2��
5����O���9'�w	�]���!?�"�G���w�>jb~����N���F���]��o�J��.������7�����,(�5`
Y|�Kb���
P=4�F�/]�n�>�,�3������Z�Z�eT�1��asrN�>�"I�\����$�5�����!{J�T�<���.:��
c��P�l9�G�j��	���\��\g��g�Q���
�T2�y]�u�y�	�a'��Ab�^�<�=��o��>�eu���L�@�f��I���'��bvQ����K��C}��~�
w�cv�o��p��~<��$�������d���3�);��H^I��+��
#&u�l7���x6j�i"�������{��6�%a�3�+�lb$<$q'���h��9y�<Z!
0�B��H��$���[����H�&�}����VWWWU�����	-o"2�o��s����)������{:]�V%3v'w�qC�ZB����3�5��d���T 5WE������]�q���0�7c��o
�\�f�O�F	5K���������oT��)/
��X��=�o�#[y�u*@s�J�A\�����tu��^HW�F��V�2t~g�U
T�cfI��c�#�#Lzv����^�`S�����|C�zreC���7]���rE��;���S:L��_��'�����@�U����)�%�5�X�w��0xl����{�T7��Z�����(��{�c$�I���[C2�;�B��K�=���Z�����L=Gq/u@��a�Q�c�3�nn|\������b�F"�!����wG����*�{���1�7������%��HL",���P\��:o��ur��?}sv�n]4;����s�/N�����KS�Y+~�{;3�po`������ZOK���V�,V�D"�����5�%�i��R��}2;����	,
1�v7������FA9�W��Q�_�L��?m�a{���\��w��X��L,�Q�z���������Q�[���	%����1�(�����9&�����7��P���"
n���t�'�6q��X.��Q�����n�~��~Gg�1��� ��
&��RD���u�����������p	�Q�.��9��4#��IDw���6�����B�-�(��ys����z�T�J%��"�f��!�=������O���B�T���R�'��p��O�m��9u:e����e�#1��e�J��������N#�Bgv����"<
H%1�O��v���2�M��������zt�8����OOt���Suv������������t.�@>�O�/�������X*t���!�"���Mr�����d��	F�����>���S`=��!{���6t�W*$dT�bK�d�m5�O��piA5�b�%��W�mcU*C9���������4�)���d����2�Gdr=�	^"�#��"���L����EgK:�E�^��]�_��������T[�>�X�Y&h�`;f�xAE�s��`j_��f�5K
�F���p�Q�a3��>E��%�E1��1�Z��<���"���`�o���� D���	O�J�`t�&_a-b�u������Y�q���U~R2}���G�U�nL����A�` ���vs��j�K�������;?=v<m��|��u�K������3��o�W,����7w�M�~�=i�7��7�1�Vg�#�����8S_���eQC6��.����~2)0:z���/XwOV�����\���k�']����W���1�f�S�� �����@<]-#w�+5m��9b�u�}�^�lf���>7�S�b�n�-t~�c���+��j�����}"�����B��1��d��A�����zh�!zx�(�Z��"��A����~��t�_B�s3q���r�C���E" ���O�z���T������o�S�B.w��%��������8G7����0
�P�����gl7��C�����Hz����9J������;�Q��%�V�����
�����uo}g���E�Y�\a��#��
oSB�j{I�����M�dps}���X�\�_��)\�:��y_b
w�:( t�������1�N�~"g�#�
e�k��e��a�+��H���.]5�����Y`R�7 R��9�4�?S��1R��[��|�.���1��������6�������������%&���"�x�2$f�*,��d��$�u����q����O����:=1Z���k�Q�4=����x�TN�(�T^���P��$����#�S��	|�"��^a*[��$N�tb��W����na�\h:��eN���IvF.��T��p#Q`�5R���	A4�/d[���W]7�)�zz?��R��}��-�����3�/�'����j1meT��e_V������,i~{�9_G��S����lJ���
��\��<����Dk���g9�7�6�z�P�d�u�>5(��Fc�;�-c�����9�;0��
������+�~|R�����������9LM����������n�s�Q�����s�ja���o�:,��������C�o�pJ��{��W��z���M�oT���'�Q'���}t�&s���^�F�Z�`\z��X�Y��1H���N���������7'���:����N�:�E"������+���c�NC
>~�>�`��CHaU�(���'�h
��Zf���q�����������>E����Xz����R�aw��c�O�=�N��� d+~�w�%$ov� �b8��d����� ��E�/U)������������b���\j��d@����se^i�/�Fe+M1�m�!�)( >��v��5����(+C �$pxnvz���$���j�!8�\�z�������W��OI����xh"&�b�}����2[b�%�9��w�Y0��R��/��
��S>��E�s����mT|���r
��g�aFf�vA�IX�j��u|�*��(Zy0����q%��v������e;��a<:1��V-�����vj�vBn�-��fXE�E���.�S�q�Q<u�G��(���%u0�kN�iK9�=��s����E8�^8�Mx���i��?�������Q�����=c�s�@u����� >7A�T��fj������������.[
����em������Jx����io��G�"��q	��&����H�Ma>���J�}*����,�2N��������q�|�~���p}wu��[�q�1��������N���]�����{���L����E�G�]�X��nx������}p~�s���S��r�������i��=��=��w>J'>V���_���X��BP��i��a7z��&�����m��7�-0���N�E�p����(�H&�el�$�H��mA�TX��\����7��x�[w$,��&�,�D�]�iVO�������)��������]�gs���cR�yUm�e[?�Ay<5�� ��f]3E��/���Q�����MR�����k�4b�D��p�����b��ij�E�-���Y%��r�L�YEW�Xe]V�p�
��L����x���D�w���������8�j���eSaT42
4q�t�(����`D5s�C���������W?a<l��4����U���e����`iei��^7ZfK!/�0�j7��'��h�B9�0|���EG�J����<@����4_���+b���@����'1��bV��P�W���	�oU��Z����=aS�W���-�s>J������5d{��cR��!��d+��o|�J��=���~��>�����5�8����w���	GC������j�\t��[o[���f" (��@�$�'xJ�V�2f�����6s\�Vs��s���dm0����(����G�� +�a�M���B��:�DI�����0�k)k���3��	�~�,a2���PH�Rc�x����������?i�{
�u��A��,�}���p2LC�d�����^u�.�'��[y����>iBl�c6��	e6H�b�z39���m������:����}�z���%_m�
���L��_�����ML�<x���	�3,^*a����moK��r7�@3AO(�	:�����SC]���	n���~�o�ka��{���&��@��#��%9�i���q6����������(�
xLA�qE52�2���lY�BZ��n3J&e�EAA�r�-R�^�R�����=���H���/R.'v�@��:�Eb���%2��v2}�{"0v���L�)�0'������~oBf������1O�x�G�C�����y��B��7���o��1�]4#[l���a�>���)��������9�=����R
)�:���hVS� ��7&?=��r�9>�l�h�F������l��p�R�@����3i�i�i��jJ:1`b�I�W�L�A<�Wn_���:����B����*��+��
�m-H��j����4�����r1�#�K$!}���t/�3����Pp�$l[�r��Q�}�&�w�>f��|���0�z�w�z��|������������&���������y1�'gd�����e��{��+��!j9��HaaZ��:�=)�5��&�wjD��YK1���( �O���o�p���i�m97:(0c��.��f��6���d�`�
��<��>{�:j����}��iL��bML��"���Q'�u��y���������h���m��bM�&w���c��sN���P�m��0��~M�&>��&����(�/(��}��=";���t��i���c�����LG2�\� �P������x�������^��fy�g�J�����Ks��%2�8\2a>A�|��	�a\��7�xec9��!�"����lJ8E2�d�f���9l����\�]��p�p��7!n�Q���};-�@{C������ f$��$(
�V��d��DNH.��{8��B'��er�`����$�Y�}q4�V~`�t4=���"�'Hl�}^��A�C��'F{|����z6�uj
q�R�T�4��r���0f��u�$����=������c6c�$�ya�NMM��KR������Wp����P9}{�w7$�!���A��_�5	&C�v|�"#Yy%2�����[�z`�8�K��(��,�v�,L-�tMG��1D=N��$���%nh�����(������9_A��i,�(��:�+YJ0�9,jY}��XK���i
�� ����Q2:1���
��1���Hw��w�AJGc\��X�)��t�H)�15p4��3�Q��d^gG��������r9����s$w)k������h��Y\�������GGX3�
d<c��`��m��`EH���T�����@����t�L3)�C���n\��y3t��(Q��0wl������~�L)�fe�>)
�����}������K�k��q��	��/e^�����ng3��^�S'�GB����8e�����L�dY���)��h�7�c3��D��F[X�%���_8�:�]6�[���y��<k�7.N��v�X�n$5�\,s����lz�FP�?�j5�
5I��nWVtj���?�u!�S[O�G�����}���������J�����#g0F��L������|ZL,���r��h����$	C�H�r��w�<qUF����b��mk�KC�U�0����(2��X1U/�A�)�(b1q��%��D�2������ru����p���nL������w��!�����B7L����W�6�f5��1��������r���0'�"Z����t�)��`YR�xB������+v�`���RK_c*����F���B��Tj$�M������K�<��� ��(*���(��D�rnG�AJ�X1q��iHO��G;�������s92���v���L������~�J�PS}M�����1%��������5*��z��Qf��|�Al��F��5��n<1Xr9���yZ������bpj�x��J���E(-�FUD��8bK���EE�H���V����Q�����
!4~��uI9�����������B-�&�b�������/��K�w,&��{L~u��Dj�lZ2��j�X����q�	��q�8�����i�q�u���/�#+X���bwl.@{�P���e�����S:���[���c%�b(:Et��B��%�"g_�^��;�k�/p�3����3����n8�`�rz���\@z��X-�Y��a6������=�#��^����n�����_c�[4��	D�MK�� J/+@$p�<���6�<X�P�����*
C�u������{5ap�P����o�u#W����i�1����9�Sk����Vx����V����%\�EE�4�����4I�D������t���=�<U*S"u�q�0��x�i��Z�����t��N�}~p��e��?�"y���:h����WjK���'�f�W�I9��St3�_�YJe#�B�e���A~����:���BrS�Pk�P���q���|�N�x�C5�?�N	�xIg_��]�*��7M�R��8E�@�e���m��P�K�*E���7��*��8eI
2�R��I^�Zu�,�S�ji(�:7H�t��������5i�Y��`����<l��Msw.1l�!�mp�r�>su��o/�h�46�s�V��h6��v�`��Kj��4)"���,Ba/�`�R!��7z�e��|����a��(!��;L�@Y�{]�+�SDhM�/��\h���/���q���s�\TX��
�p��������~$�����r��X�y7}Q�Y�� F�>zL
V�����(�������x��u����p\���5���'��Y����\�a�F�������>�v������jR~��-���:���t��86�a���n�'��cB��t�r^���V������v;\�>Aa��~���62�a�+��>�X��y��l�=Hd����9�E�
��LF]2n�I���g9N���6#��'�r(	r"�������aa�DPv��S9A�v(�u�9�>�pT���
�79a#M� �b�NQ�c.-�Z���������H��w��Zv��[�=']�>�e?�b����/�ho�q���2��1���jh-Z��@��+�@n�*r�
�G����@��p�`�n���?6�	�����E���&��1K���F�S/_�57W1��]K��a�R��Lr������@\�o�Dv~�4;w`�bJ�/Y�tH��*S�f�v�:�g�h����m{.����D�c��C
U���z���t���[��o
1�;Z��k��1�j�lt���3�Y����$�Y����3u�T��#�[���-�>R�n�`x��EA6�y/U"kd
��XL��s2�Z�#���$*/�p��d0cyTG��r�mYa.&��g6������A����UVcM�,�|����>��U
�=%����(��\j<s����1�uHW�1%�qF��6�te�s���!\��=����\HUy����DJ��g���P��a�l�o2w��0<H��CY�
�`��Y�����%����F�9e���So,�eFR�}�oQ6�D����^���f��=_O9�)���)K~`m�Pj
N��h���L��h���������.�=��������f�^?oX�MC�1J�*�m�?3t���%�@���nI��mjg�F[��1����Mq�����N �v
p�V�|��'z9��Zl�*��~Zu@3~��.����-a���<�a��b�f����">YT���>���b�A2������}�jbI0����Y����&A~�`�����1�A
rc��_�Z�Gz9��d�S�${���I�N�	�%@��(����'��@#J�2R��;[�A�G�4L�~A��Pz���7�����96)����_�����ny�@'�|�-��3���}��o���N����ZTl�RX�S����m��U}�����uluq���Y�����K�v��\�����1������i��5pN�����wDgvP�,=j�s�.��� ��u��t����3�6/�1�����A�Y`p-�\�6+;���[+j��v��o��<BV&k�}ff��r��z9��Q^�biQ�@�CI}shP���&v��{O�\�c��U"���-I3 |����>��S�)���Ni)����M�c�cE��(�����v���9Qqf�����f45+1�I�c��U��k~��&^��i���3^�;�,���R��b���]�4['���.h�2s���i���r�D�5����e�2�N9�3*Q�g�������]���}t����x�E��p*>�B���>$��$��%��<��$�T����;j��#��S���-[�TR�����4Y)U$' �A9_'{.=���B�u�����(�U+�~K�I`
�@����O[����Jv�"C���QxOM���u���:�����	I���r��hr-�Z:'�Z]��l���?��+����O�k�?K�WIz��h���5��)�]w��8�_�NL��4��S��!��p:�\���
���0��2F�~�F^��;��?�Q���O+We�F�0��3��E]��F@{���:rt��t	��"R"�.�z����j�#-I\x���-9���S�IJ,E$%H]�(}+��
GV�e���6����&��7Z���\��K����G�;�w)�����y��gSs�r����Q�������u��]�w�1)j�k8����W�(���4t�+�����@#��
r�)k/QDYm��	h�,�s�������������u�+��@����;H���O^?���f��M1�M1���oz�t����$�L��sjMg�(����lr5z����� �Q67TdR�g}D������c3h�"���2���"dn�5��l����L ��0���N�*��������rO}���V��*�"����(�}�y=�R��n��a��R�4��!G4�#���8b�|GQ������W���%o�����H�dK���j�]n���=�\�C��������gM����nd!sE�����e��C�y����������4�f��x�D���������FrLb8J��y��@����?"�9��}�'�t�H:#���P��,���{�������:��>]&��S�%�t�aC]a���RE5b����h�$h�����H��&��=oN&�L(9���b;�F}��{"�J%������K���C/Mk��'�N�u������i5W�F�4}���&oT��1@!������Z��R-o���B�G4�\of1K��{���!�A�W_�s"+�:t�P"E���A����������_��u���|��{������6������?-j?[��_��B ����\�H��oCk�����r8]���D0p�o�u��qv���/�dX�Tu�Qe�%	J���c�R#������t3�}��&%q�({f�>��c|�	!���p7pk��J����|��hD��(M����J�,&I��;!7��8�.�����`��@�8�/�H���6'y�����upG�� ��'���3��A>Zn"�i�8�����T�L��hM�6����
 Xk��~|�� ������v��0Q���{�!�6=x�GQ_��
Z�f������L��\_�T��^��<�D7���jv~����D�K����S���v��<���y3KN�|qR����o3����i����B���{�[��0���sEK����,��a�gi��9�4-�$��m�W���d���q�#.���a���s������Nt�"�>����RAV8ett�p���Z:�G��s�'t���t����YO,��h6�#r6I�omijn�tqhj$"�)F������M=��0V�{�����	|9>�v�P������3��;_���(���0�R&���qh����n�gOu�^��p�`�Ch��fxH��JbW�7�`���x�j���m�;�PjH�?�D�45��&Y`��1�()!`�i�;�����~���d����@�h�����-h�!���q�\���rb+�������a�������;(�J����EX�f���?� �����x��������l��6��hfj�dn��S�6<UB�2+���D�KN�����J5�1s�Y������a����O	�����[����YX�E�G��Q6�4l#P|��#���$/�%�V�W(��S��2t����2*kc����bY*���p��!-����f�iV@�*&���Y��sU�����R��T�db
k�w6��\/�y������8���x�e�M9�l�!��,����ghNB�2��)�v�<'��n^\�	���R��`Unz Yi����9���5�z�5Q��5��;������)m�Y��c�|I����RFtv��7����;l�X�`����$>��������-U�L��lsE��q���Ve�^�3��'�(������!�1�z���?�	\N��9�J�dZ����?�gtn�QAm�j3G�t��C-��g��>{/HZ���H�N��ha��:�S�-g^��T�}���aD�RKl��P&�]Q d2���-�%��J2����|���"��K�UI�R}�A,�g)���9��oT�����2Dj
B�}�*I������������u\��i�E��Sl�����8�����	pW�b:1:�@�?�Q6+t&\�j������v����G5u+�fC#W:�
t^6J"YV�(�FU<��1�{�\����YZ����%�R|)�����l[N����/�	<�J�����?F�x��{>?1l��0B��dp�<���&�	N���&?M#�6��M|3S���(�����\$���,&A��>�^*���n�Z�y���`#a�f��Cd�;
��4.��W?.qi�JD�L������	,���Y������+�4��E�PsMA��o�	��b�X�VXC���hu�<YvDYL�:�QG?+�Vb���F���'<�gc0S:��v=2����~mn;gY0%�?.��w]�R!KkVi��Q(qn����O�O!�s{����pTSl�Y��&�Nte@����V��n�����;��*J���N����Iy�����s�����^�����5�,�z���G����M�^�y�5+M8Y�`<����w��'/!L���.g�}~��_���a����z�yRy�
'2)v��Ft�1=KI�rK;m<�R���B��S)�����b��R~l�&������]�����m�U�������������V����zq�1�c6������\��f���S���d]��V�K��P���Es�zgs���������o��U\�=��yg{����&{hu��E������
�L�}�b�g/���~����ZRt9�� �����T�6��$�V�Iu�	yK1���@K`�e�rv��oI/�"��c�R��~��f����]��yF�>���?x����n��{�k�����x\J>f����X��O8�4���7S>�r�4�����G����p��N�X
������~2�T_�����9{::������w�����k�J��W��oKV���{G���;�mA�f�y2Mg��E��)��t����B��k����h�K�/�Xp���U����h�T�6w�y	��`K�O������<{��Q�i�zH������u~c6�Awx3��X��[s$K�������sI���������!e	p��l*�>�to��$�G�S>1Oh'L�������4��cr
�}��:�r���p�~8hsao��|�.7���h�����v�]B=7��l��pZ���2�F��x)������W����GE��+������{O��/>S�\-����@��xp��n�0�@b�����r���n��6i�Y8��y�M�jx��;�G��LWigs���ZKV���o�f��������3kg����W����Y��K[�jm{���v���H��i�a�Wjq:��	[��m{�zu�fE{���)��+����O���`5��![��xD��t���c�H��o����.�����O)l97-"����'7�����|i���Ap5�e����<���.|�����wDuG�(��������Em��Sg8����W]Lc���De�p���$���l@��)w=yi����c�nr�F���0/a��6��K���Y.M�]�5~JRZ+�W+�W��%m�q8����+(�� ��@.�p����K\H���*>�n�����!�`�7jC����W��f�m$�i��;f��&��&�l�15�������A�3��YZ���&�7�]#����3L�����?Z���U���~�Hy���`eq�b�TCkW/H�)�G��|s���v����� �:a��-=�����p{��;�+=z@�����q�y���W�����`��X����j������\�+\m�n1@�0����!:�����	^S�����O<1�r���u�N+�r�Q�?=����8o�u[���p/y��8n���t,�g�������a���s�\1�Ssv�w�8W���kE,(Q&4�F���h�O`�l\H�-��0d@�};[[���HT�6_yS�@����[Pr#�����  �����9r�J���S�B,r"�:PrW���
�6�����rn��������g�9�z\���.��7:�g�T!�I���r��P8��X��#�������>W���������@Rs�sS8����*!{�o�����XJ�E�29A]���N	��z�y�(TD���M����j�+�m�H,�i%��ITmkc��_B�h<B���A����U����y������g��������Bt��?�.`��Y��m
9���(�Fx��"Fv'-By�P#@��F�@/� �
h)f��"%C���Z��h�vl���p������Z�������A��1q�?�����`D��0�����4����_4S>�Tt���Q��)Z

�bQ���a�*�Bd��5����yP�;C=r���qp�1��F?'��K5�B�B�BE)������G���%a���i��	=�$��nx���$�
N�c�#Z��d@9�1���hI�ax_)+
���xW��O�	���/��P��.]W�)O'����z>>o����X�V�)�i���x2��D��_�����~b���ps�h����V<��[4�3�s\���!�Z����$���G�=����!���B6)�F�I����~U	z	g���Y����~���q vM}�����d���.���c�����D�u0.6
���39{LqxM���F|C���IA��qe�0��|o&���R
�XG��'���K��}�j����&#����n
R��Q���D������0��"� �C���aU����xR�	�h@z�fCf:hr���	cC{�%��A
2��Cu����mcl��Z���%�K�t���y��t��p4#q���PI�D�] �8�"���S>Qort�Z��,/b����R0��R�J�l
<��'��%�6�/��4B2��
�g�Gb�_��$�����q
��_4���#��^���v,|o��X���L#H��LD�*��p,�x1�j��p#��c{0���4��!c��G��#8g&&o4����Fn�O�p�&{~$�%����,����\��g��U����1�B�S��+*���C���MVaG�k��7NrJa'/Y����0G!�%��q?V,����Qkx���@%X1O�Y�K/���!
s��6�����xFB�L���Z������zx��#�/�'/�5N�'��4��{K�Va��"��+�F�%���?h���}����8���G>S��?���~�B�����LE�0��l[4�\��K03����	�p����,a��AtM��D����c���;�	*iv�IC��7�c��Pk�H.����o�����8������;4+����E��`���,�����q�Yz*�����G�GS��������$�pa������R����m,EF��S#��h�\Zn~t�Au��QNF�w�R��LdRA�i�	:;��i�U������V�����-�����&��L����8&����mMY~�5���J������{C�>d���<��� E�r�a��$����G��� 4p@�V�N�������j���-��)RL����d��/�0�������A�y���Wt�#YA3���m�����xF�6������'=4Ej�d�xL�
�}�Z#�S8T���K�~?�����*��1���/tn��:�/~����������GDZ�Xi���Z����f�?�7@B�����up
��/�PE����:��R���L,����oU���T�i�y�z�:9��?�]4����|���3���<�@��O����RM��n��g��K��k���c��G�#�p�����6`$��a�M��u����Q�|x|��������c7�����~7[G'��{�u�l�M�r��RZ+e>���V�Lo�%sv���NG��5os{Co�e��G�-�j��,W�~%V�]��������IW����a�Pe^����8l�mx[���\��6�y-5W��o)�m?5�x�<s�+{��GEsJ�;nV�U~������'Qw�+�R�u42����O�C
V�,,|w�<�a7�<�~r�:�9s���;�g��~N����g��A����)�������(�����Di�$�>4�1�J�~n�����y��Wh[F�{����uOk��tm�{BY[��K�?��Z��,�+X��&�����:
T��YUY������,(&\�h�����l�f[_�ZP+�q*D%��cy�j#q�N�u�g?����<��7
'����{����]�g
v��C��p�V���}�ZZ�����p5y�������|�e����^��&�R�F����x7Z� ��`�?�Hz_7�A�M8)��:9h������}|G��?��������7n��~����m�J���'G��#,yz�<o\�B����9��$���3��F�(����l���m�MCw���M���@��1���q<�Y���<�:�C��,�x�|�����a�����s^���7��?�_A�.�mc�#���@<!6�)��;��	�E}����>�L�AZ__�B��z��C0k��?Q%\����
�i��9sO�[����$o�����L���b�Z�����ot�=��h���m�[�RNk����W[�	�:���gI��k�O(�Wd{�n@�#��}O�J�e6W0vRD�Y%�)�Nk�q��k��n�o��}rey��+�QW��*j����V��*j����V>�*JnV�M-��	��C_����N�p�6�G��D����z�\pc'�Zum+���>�0��gP����;�k<��+��d�pMX��`uY�Q�-T�r�1�����*����E�G����xX��?�;.[]����;�r8{���}��#n��gJ\��_�>������_���=���g.��gns��K�g���ks�g)C��[f��Y����_��1�z6����M=�h��gHi�LM6�����o|o���|���g�j��������2�zf����~����BIm���m���#�����f���)=~����_��������=s�=S6b}�8,��
��w�(S�����s����W9z����F����n�.A;B�_&.�J�����K|G�|G=��HN��G��y��P��O�0A�*����e{?�����"����~�h��%���.�s��ml�.(m��em�w�~��w���*�C/�=##�1�o�$�C8mD��KF�����`�R�b@���21���cB����b^�a��t��������&>�y�q�O�$7d\Z�~Fxa�!y��_P����c��5�i�8�KJ�.+����`,_F� }��z���2`_qi�����l�����@
>
�[
����%Jw��B���'�ZG��R����/�c|��dV��a���P1����'����#Lv�����rR4���7y5�'��t>�A�h���q�J!��(=���������/�0���^|���j�����(�jsl���8^��!$�A���")��7�<��j��/��uz��Qps���d~.m�m�B.����h	x
�2�N�}kSe�2to��%�#Y}��lT���l��I��
�G������^u{S��s�@B:�1�4����CQO����syv��h�B��p�uY�z���{Z�N��?s��2�����>e���%c|�^x�)�d��n������x�x�������*_�sE�K'+��!�|��g���|�����*���N>S���X� �}�:����)��lmZ��g��ycX:J 'CQ8�
���}z��� ��0�p�m����>�A��$���Vx[�:��&�����v�����"13��� W�6s��F��W��u���}����<:;�xc�Y���v�/�5��Gk�#*��>�((g�DN�wc�����w������{�/�6���-����T`��u�ySr�i)'�u��p����%W<��=��,"Rw�wU]��t�_
��pt�� �9��E���m�T�)=R'���lc��z*�����M�o��@��mp9�� �c�yq���9���_�������V��h�)�������(k;5~���?�7��n@�� ���5�3ba���Q��/����?��f�����s��$�����CL���	\��f�^�����n�����X�zeU[[[_��U�<u����ow�������������e^���o�hW5�}�T������������=��X1�����_��p��@�C����o����k��w�n7*��+���A���u�b�6�-�1�UM�y�����C���X����.�%�����nu��	^�uGA���V�G�=�
�-T������~P�DA/SS�vo����;���$@����w�}(�S�����H"��dC����5�����W�Q��u�D�������+*g1��,br�E���C��|3!�!xG@7Ch�B���P�O���1N6�Qkk��Zf_�������[�#�)���s���u��)
N�� ��J:���oV�zr��4�8���&p>���.
5.CQ�����B����V\q�Y��A����,#�h�h�u��*����������5�N��������UzAA��\����d�b������������~��M��_PQ��uSZ�N��,����$I����p"#��t���m�����f�u��<*�"G���[�Q�l��`�����x�FW�2� "�,�K�(�h�b�\�"��oX�S�h���\s2;��2��IC��F�pV�A���~������!g��o+�1�C�=�qi���K���Ns����/�:;"���_c�B�K++1,����~D��f��T�H�q���z�|*����m�y��5�D�FR�N_@�-J���%e)Y2�b��#��O�_�?-����=C�����B�5�FK��e��J<�`��<hP�1��?�]��6����v��H�QO<�pD���Xeu���	��7����i���d����L��-��j��;X2��
P>_]�
1�A_R�0P$�m
h��x�\v-��������fSI�A�0��'���t�Y����b��
�������E�<[��F��S�����4t�n'�=|Rx<&OB]�^�2T#�0�I[hi3T�C#���i��4�*�+���p�����
��O����~�:i��%����/'���	Z.w��|#M??��eJ��pSH�����0�h�C,�!s�0����>��|�|�./Z������;|�"�h�c��E�pg`%��^*cb��De��
Y�� @1��lp�1IRK�!!L�1�����������j��xI%{��7����z���0�oS��%.�����0CoO[������a��UdL���4[����x��D&��]u������9M�U���@���Y\2�z����StA�u���+W�)����oTtNZ�$�<��S��I���]��4"��
�$��mT� �����Lh��_N���N����v�ll���;��k��i��iw�����;wI����gu&�1� i�t������������%��>c��M�]
T
�������0Z)��0�6�+dW^��KV/��j�]_��2(��b�-�����.�
���N��W�1:C�qpuejV�Q�!�>��/j�����Gm�l|��U�*X��o�������}���W�`$�$|��)?�d�&1|68����#
|�	���'�B=^%������;�(1pv_��\@+<D.��c�vj	NI`���D��`�"W�,���+C����!)��BG�����P�g�}���p2LG[3\7�^KH6���F�y��~,�Wk0[Z_�k�/�k��M���r���CLP'1�B��h)������j���(�B��y�pe�=�T-����Ds���s$���$��L���U�����0�{F1[���|�bvFS�h�s�v:��0�[��>�~�6���2��E��,"��G��>�3���������!�)�z>%�b4�������tK�;J,��xA����%���ZszE9%�OH�������m�����&�m*~���1������qS������0p���nyb��;��N��%5%�DZ�ZD���r3@���w8���������T����� n�g:����7���I��a"��Ld�%�A�����0����X�d�mX� 9 A�jr
��}�t�r\M`f���`]l��5!����tc �d�����	����<F�M�i��F�7GiE �y�~-�Pc��#KF�jf�z��	���� ��.�"e�@����]�������p|�FAi,��Y9�Fi�ox�w��x����*�0�'� P�X)*���%�����=����<Np-	�E�D����d���
~�xa�']h�|q="��6A�&�*:^y:D0�����nm����2���OkK�������Ke���J="m�����aXb�c
�%���s5}P��DDYt���3_Z��-���N�N%�h���eWIU�,�LP�*�V2'&[<)^&L%wa_d�h�q�6���'4�����`\��*p��6�}B��%�����'��{�UdT\�6���^u�e���0(fhE2�\Z�+.�����&f�/������L�^����A��l�,�+	t�����G���d_.�t��PS(#���$��}��6��v�����FW����5�zL��	�X[�he����2����\f>��(&�2�X�s�&'=�|h� �
�#3d���?}��iqV��X�@���I�o:"�/[e����C�&���I\q��� ��wum�J��"E��m����u��)OE��]��e%Z"�C�G�[7QQ���!�c���u����b�`=0N�x"�;N���$�L�Ho�I	Sm�����H�(�I�}�,P���Z	���Zb���C^�4['������s]d�x���k��6X��i�1���_�����a2�`����Q�j�p5hy����>G�`��tK.w�T"�v��$��C�cD�����D�������K|]C�F<�Z��810i�.��j-M�F�4����8����?^�y��]�g-�]%~���������������������oj�e�z��}N5�>��r$���z�����$����$i�6��a�T����=!����a�~�����C��E��F�c�����
��	�QE�<�&J��4�;����}������K��m)]7���H<�3yk�|C����{�������G��=��ay����D#f�����N��m:4F8�Qz�1�wL��-��,�]��XL��\3�e|fK5������j����<2����
������FTJ.Pz��	7�<~+����L3�kb�0�
�*��o����ye�F�df��������9��F)�,@V�?�!lj+��C;�D=���?���}��������jw�Yc��h���4�
���d��"���37���k�Ih��'?���/KM��=��5�A�^���������p�(������Q�D�3��Vq������^��p��	���������K_K��y��&�}@8��{�7n�!Y:����1}�q�@n4����w��'�t����>: '8_�}�Z�$
���`M	B��^����R��6}�8��!��D�=H���e�"�
��i
��s���dL��;?)HZ[{�%���0�����Q��X�a��3s��mS�oY61�t@������W��*��"9��nhnn{�[u�/�����|�(�V/��[TMj��],�L-J��E/]�y����qNE��� 
���p���dE����
�l�d5m��Q����Go�EZ*�pe���4�������X���K������,��Q�Q=��}w������Qf���X�+��u�zj��}q��sJ�m9�	�,o���"���[S�r4
f���E}O��s�j_tZ'g�"�<�����)F�m;%�)>��mI� ����)�����0o�K:C���|�P���cD�6EP�0|7�t�`�xcR%I1�����c+B�V7����i���~O��qP���$e������J�#R�!y��G--_���������#_T4�}�?�q-�[n��u{x��m���u`
8{��X����8��M�Hr��J1����B��>����� i��P��6��1�#e�5?T:�Cx@g��/�
%I�]V���������9�
�����:���b��:�����Iaf���"�A�H|��T��D����J���3��S�)?ee�H�"�P��g����or�+l�M9��D�;�!fK�}��Tm��Tt�d�K����W�:��36rlb%��u��K4�&�o�5KH��0*w���Tzc2�5�Q����
�^���*_D�"��B��*��=/��2��X^���8�����Y0��7Urs�=:���E_���K���><�0a!7���X+	�����T%��1�[����|F�a��_�<j>!|v)�����W����#���-���cS��0Bk�
��
�n�IwkB�l*��tK(��&wh����uY>���s��QM��r�eP��5e6B2���8+2
8��h��r�e���w�]=$q��<:����S��T�s[����	#j���3���6�|F��M]��}14QB0���N;B�6�PG���);�$��Sh�����,�P*k<	�������>�a������`��dY����$	nN���y����	[lk�8��'o��l�Czc`�r�fm�g����� \�
��~J��L��#���(���t�dh��kW2�)���pe��v77���d8�8��k!4���Y_���v���c�����:��h;�-�O�u�����c�E�����?�6������q�QdM_eB�\4��26@���cc:e�g������-��:�}kk�C1#�K�BM����kkx��������d�?f��������Y�>z�00<���u6��o��WK��
vm@�K��2�L!+��zb���K05�]��%q@����{�p|�^ZM����/l�
�-�S�U87�I���,><�Ne�5�������i��.p)����I�i�3�[�\�9(�Ed&�Mc�%V����x+����F�v]�r���6rM�K}��{.f��]�pQ����
p�^�~7��{j�# ����N���o���&>�����)q�hK�}X�C�2g8@��L���4�v-cz
�Aws���0��u�_��AZ���VJF!�AQ�n(��gKw��fR�%8�R�`N��Cj��1nk�F|�;����E��!��@��"�f�X*�;�c�]<����C�)0C�y�4��f�	����s�m`5����M��"9$��"�5+�����!���w����a�y�i7����9�mI��?*��#��r������d���6A�����e��5�A�����h��W-�w=_"�R�f��]"f�����e?M���9�Wv�
FRzK��)�o�%f�/��.i��!����F�����i�m"#R%��?�,�m�C���^�����V���U%�[X|���/+�`k�;<�w����Wt�]#�����A�ge��J��d'�3��������&�I�B�g�i�B*`B�c�j �hP8�X9}���c�,�u��b�V!�D��S@AXz�br�R���-��v:��g0f����� pk���/�A�d��"�3��<��$S8g��dF�nm��v��6��������%�5|���mO4
h�/,�	��

�,�k����\K���uN���o{[�[�i	��x3v���34I(|��<w���#�(�+h���J��9�O�{��#.;����5��^�=��?$fCo4���#�iK�2Ij���9^R�K<��I�A�uv'a�t�������E��'}�RP�*�w �l�����gx���A�/�Q�����V������ra'c�lY_U� uz.���vG�
���m�y6�G�R��E�������:��~�A����hX�!�:Z2&�x�h���Y0���`Zn�,���
�7�@���r+8g9�3�����{�z��f��Ar�.�������)�,������A�
��x�i����OG!ZV\�\��bQ��[I<5*��������O��L@N.W�����g��;���4�e�}:;6Cf;J�.��)StNG�}�v���hr7"]7�:������<����9�]�Ek�@~.u�P{
�:p|,�|�Oo�h=(�qkx�iw���=�]�,D����g�L|6����C�pM 1���B���z�WPJ�i���3�a�77�ykxk��[~��������(Ntg����@3��{��u���F>�m?z�G<�8zo^0���?�w�no�/_��z���Hle�������e�0`;����@5�Z�z��u�ZK��MG���e]b���d$����
��eQ���T�rl��������<�TX4�75����)]!/'=%�����lrk����]��w�����V��1Q���{��+�(�����:{�	<	0;(}��A�B9&6i�# 7�Z�|������q�@Y��v<�
�,�l����+�h�6�~���?��
�i&IT�i�M5�C1��m#G^F�mPV�Lu|�5��_wc��N�����W�ok{�`��:��M��S�;�QCq�> ��w����Cb�&��
�h�1�ly��
s���Hy��~�:��N����]�#n��dL�-n!_�����".�m"vk�Wj���e�IL��a����Q�f��)��uU�z��#�^��Hi�7�����������{���JE��Q	)���0�����L2�d�h���L�!�v*U�#�Dc2���tq1?�o�;��w��kk�������<�9��h+��Bj>�%K���*�UD�
���_QV"�.��$�,���VZD�g�\�k���kS�#B
���|td0N�C2/�J�2���J���0zE������b}Oju/6�I���'�-�R��V#�����}<0wHl��h�qx~�&%�% a���w��D��!EG�����((�e�����[O9����y����<;�?�3�����Z�G~r�'[���<e��0�K�bV�T����k&<�E�J8�i�bv	�����K�J��K�<oUGq{%�����D����Q 	�m��
���/N��=gY�0�~�`w�8c�������?g�]���v�&��BN��/=�4X����x�d�M���&��������/�4����w�����X��}�9�`�!Gt�*Ow�4JN�����YT�	�"��9����e��Nf�Ek���F&��q}N[<9=�VG���Y1�."��o2�^L��.����Ex?���j�U�����@iRn��h%.E�H
�O�4�(��I��'�yb-7@^�ml�YV[��b�4�^j�����\��(hR���pl.pyla�����2�;|��Mav�h�o��\��1z���T,+qKI#����T�EZ\��NQ�eP�6t)��0�����p�zV�N�	Gl��`������4�[SJ�]�]����fs����Q���j��0s�+��V�O~���hUd�����R�L���m�7��)���|���_�6�'j=���U�����p\�&[<��G&��g��ab�����Kp#$�(6�|�TV��Zqs8���[��v)]������@����z���1�<SP1O�G@��)�gF��������X��sC#�B@p��9���xy����c��G��"��W���(��z6k�}�?�������=b��=�~%3
�n+���HsJ8���uJT����dK���#��8h�3.e�������������M�O�R��Y(�@	�5�Ro��O/��������m�m�������>��K���+z�$fM5�i�����"M��X7��8�W�:d�\�����
��qL}�������0��G=8����O9��
�}�!��$0Qu��o�U���e�m�X�
=��4)in���^��z={�=��?
=����qg���C)1Ped���m��Y��k���'8�|�7#�]2����I2EOC|�T�K��������PlO� �f�?:�9&�� �����$FQi��^�=h�o����p�]m��m�-A�l)b�
[p��.��N>�q��y�/Oh���p�1Y�FQ����P�Z�o���,
�C6�
��6�8����_\8���`m�T���D�{�Qj���M�[�xR�te;�Y}
����U�hnW�B�m{������y�i3��Dru�(O���+F��O[����=���:{�?1��k���h��9gWx=�p�6��h2��E�
�WI�d�(?�A����?����HD���GY�YR������?-�+���1��R�0����/������H��kZ8tw��+��k����Y"i�LY�5z�^��#��z���������2} 1�L��n&���yx���-����q"p�C����@N���V3����%�KB�~��Y�������6��->�yU�Ir��md�5�d�(Z{'��t�5�z���"X�i�R��;�����P����aE�Ifl!�y^D���,JMm`�pC}���0��E*|4q�M��]�����B�!q���	q�O�@
�s������z��W7v�x
�+�������E;�w��7�_���An�G�9Ds�d�_w��A�������?���M�<�"����)x�Bk���gu�Z�m`������%J�D�z:Q���(����*�����u|�U�T��*���n%����PPx�����'�����k�%�f��O:6�V��j@��D
��PR*�.�7$�������h����N�"�3�7ue�\����X�������<��%pq����3D��������Z�_������tiy�,������c�X��S�6���dbK�'����[�������s*'T���$[�=�n����g6 �a��r��f����\��1c��9���-�t]�?f�Q���|m���4����?f}��w����E�����G������#�&?i����-�pj��nC'�Y�������/	9�~�����FW�=z���%]
F�&_FJO8��
a? c*1��,0�q���co���c>����!���j����HnZ1��"��6%{U=qK
"�I�l��������Xd���S�f`�	�!b���cq��X5./N�j=�V��Zy.Pi�Y��W�?�����9�I��wt�p���X8*D�E,M�vc	;���k�����&�Y��8+^G������+O�5�o^�KO�i��RI��$r'��x�#����-:�|��*	����c������(���icq��$��2�����"���C	PbP��>�x	���������6�2��q+]�,�x���������OY}([}Q�N��}�������M��8#_���7��Jr�&���%'x���e���u�FgN�\�3����&� �p�<'������s@�O�����a&o��ObN>|w�cF���^���T�wb���s��%��o�y�<�[����n~w�<�o��������*����>)�������hv.~8k>����Ntg7�~����n�LOzQ���'��F}��" �l�~�Fnj���0���:�c�U�6�na�Z�}6:��'=��i`��J�;�h���8��I<���;�D�.��c��zo��{^�CV^$����>Z���B�(��A-�I	?�����I�vvj�J|*��(M}��5�?I;����p4s
������Q���$U��U��5o����9$�����D%���J�6���m�/�Q�(��D��/AD�����/>R9�W��3v�������c9���Q�[�����b0�b����Ma�b*8��C��)	�'���)s�d�#i:�*��l���lkX=X���/��idQ�QE�$+Z)XSZ�JvY+Ek�[k[q�n�p}��F�&��	c�'�'��w�y?e�n����`css������H�A@����m���?�����m��SYg�^���XXd������-c\Z���`,�Y�rm�l�0T���+�mo��_1�K	��G��]5Yh���������wT�I����c7�G������}����ml��x[[�����4�<�\���������0~���s����:�+�k�����k����������_q�,O%�@��%*X���>OF+���v������Z��z�"v�vF���������o��NZ�<qg�T$�����?+>t����mV7�l�g��_8�\��v��9������Ob*�����=�����9�j��r.���}�p����w�M����+J�����z���qf�O��z��5k��8?o�P�jqaA^� A6�h����X��	�� �QwP�2�������G
�_�M������'y�<
�y�:��{^���T���T�m~A�fr����������xT���R�S2igRV���0+=����������y0�K�����/���ahgg3{�sF��.�?��;���CR��jz(kJM�1|0���gu��tH�<�z���h��	^�oz���/�~�:������qpN$$�'�O���Y�>t���+��������	�|�1���b�D}��|��I0�8�7�7����-Qj~dF3&�h&���n���+�0B	�����,bF�����J���)|�I���|�+E��U�w�qZ��I�M�}��o��Y:K�4���fUV�N�ml�=�������*�lo$'KC�&����NUp��"l���%!2U'�2����IBg�����r���������aY����%I�����&�p@~�J��L��8�Uc���e+��n���d[8����~I� Iv���q�_J�#I���m��M<8?=S����Q�}Lik��7�T}EY	NN�A��Q��,cs8�����`���}JE��q��t�+�+M����E���������
�:g����E�-����j93H����hz��(v�����1�k��q�c�7 ��8��4����%�Ds�uY==��6ps���{��,Y�l7���|J����eG����i�C|�Q	���Vg����k��I����q�F����L���~�X�c����cq��|^����{f}����x�T�S�Q�9������%��Av�0Wz���������J?@�0���m��X�83��k^����/
 9t8mj��U�cZ�(R�9�8{��A_)���CGf{���3�T�g5�f��o��������c��i?�S@��v:2B�����Y���g�^m���J�*���
�
9!*.x����2>�A��e�����Nc8:��m�j��a�I�G��-���N�vl�X6�=���=3(.�T��D33�YU����������u�g��NdgM���;n�WO�
����v�|a`��T������m��,���f���j'����3!��^���U�E:�,!���kC1�u-����MBM��:[ok���� >v���������#�P/�C��p��U���ZM��^\�&?��a7�l�W�k����r�3�]�}]]��9��G�I������D}I?I&�k�T	�~�%�Gy1)G���N��/I���4�=M�*��k��|�p�*��A�]����{=�������kv����/�{�w'����f:��%[.pJ��!�����ai7���j�}�|%f����2s��E�G�1EO������.��F��4���b���;���9�\��������O��n�Zo�Ts�������H�<X��@j&t�e����,3q�X3��{Z~a����\��@�[i��X����p�W^�E�D���$�������t4Vw!�~�F ;TF����)a�9�YN���sdB�P��~Z�:�	���X1���g������H�:�:[_�Xl�@6\ �>I��������K��<���������%�i*��LE��T��`�!����8�
����"Z�klq��o&aG���y�1v��W����tw���z�0b�?Tt�����L��X��fu/���I:F(Ya)����C�sc*A
��I���n���2�g��v�w~5���:�E����%C�����-?;���GhwK�A\o;�>�Zo<R���M�3��A"���(�H�Z����������4�-�n�{;)��������l�%����YK���Y�|���<�)F*����SX�����{`����*.��w@�������q�\��V�@&�a��e���K�!��Fa�{��F�<���j%������Y{���������?����������HI���~b�3��������(D�BiB~��z_8����G����T��@�ogc@���P+�!s�����kl���dyM�2K�K�.��V�M~�����5C�##F�D��imU��$�N�L-V l�P��V���I�aK���J8������A��y�<9h����i��m�R��n�US��W�����x|������8"gP��G�V0��Uze#���dkg���������l��aC6��=��� ���x�}	�"H��U�-? ��x��O�Yr���/��x|v�}B����Z�����m.5W�X����z��rHk�z����j�����ZC�i�����e�1O�;_�aCSY�R��x2!��[��S;B���u�1�Q�\���k�n�U����
�QR�
�����������0��v�JC@��ge��gN�����0� �1�����y6q�xQ�8��p�8����'��`��q��sz�:j�4��>���UY�97��
��p7rrz���6j�F���`����2a���`PHic=�FwB�1���	�)=��\#�|x����;��Bf�;!�!��_�P@T����Wp��VyI'_t ��2�l��\�<Dp�P"��4d�vO�D%��7�"�u�W�V�s"��=�k�u?�ki�wz�����'&rne��W]�;�		IBL7�<Z�R����v�sxz�l�t�����sOk�����L�\B�����Y���T@.�s�Eb�*~�<l�{�j#�Wt0�;�A�dr�Rz��u�������:�7�����������`���<�����F^k��0�w�Xu�����C���K���G��u����K����q��3=@��]���b�yK�\!���,�5C)36�����TA������������v����(�����=��s�v���Z-�Z�C��v�7~�<������$4t��p�^�^O��Ni����Z���}j�ZE����*�B�q!�������8�t�4��q�$�X�fS��� y�J�/���B/�-u����C0�(���2]f�8�������}������K
�g56���P�@D���V!�=�����M�����I���&����[�:(A��1Z�=�>>�H�������y�M
���v�X�G��R[���4Z�4�Fu8��t^�����h��]'a�dD���Vb���>@����K;�kh��c�H�G��#��hq��a�o�����1�I�sj��Y��C1z@	5�:��� ��d[����v�V�XP[g
f���}�3z�'?1��A�+��*�"�R��>�<��|dya�"Ls���$��l��Z���Vd�0�~�dS���[�����zoS� ���<���r��GL�Y!���f�^���A�Z����H0$O���`�o��Lfuggs��V]��D<����w��h^�{������>���hVS��:�o��G�z9H��<��5&ht����4I���Xd
v�]�*��`
�����I_
�0�( P
��h�B�IQxG���T���Y+U���v��I���������O�|km���$��5�����H9{\XHm��@T��F
u�: ����h ����K���/N�4�d�\�h%P�M�>����(���6�FHM��m��@��� \��%m��1�{i����u�`Ze�2��_�/t?�&�$]=�Z��
��Yk��R58�C<
�}����QH�(OF�����wK��p����4���X8������gN���`WFl��EJ���G���N'�1�X���V��4@jK���@���r�$��%m�����@;E4�T>n��2�C�u$W��D ��		_D��'�%t� ��	��8W�i'�)L�Z�A�Qe7��L��3Fmozu����l���1�AusV���1;��<%]cm�VS%�["��������I�fu	�@D�������1����W7������'���s�zTD��qvr������3���	�W;j�����Y�����
ls����X ���?z�=�
��t�L�#���G`�X�����q?�~_��b�<H�K[�-G�q�e]���y�RG�l TT
>��^Y��\3����x��Y���o��`,Zp����fO`�v��u����9�mnh�2���&H)a�0e�����-���[�jqk�:s�����rx4��f������<<M0�Tv��0����f���g�2=���Y��������|XS�421\_3�+�7���	]���)��N��Z��1��Nm�'�6G���|�R����k�xr/�����Z!$����I��v�k��H���>�����+
����s*����f�9P�V�^�����l�16����n�am�w�>*����wLs@'����6=/m��5�u��-s���MLR�$�	�1�
�6���#���]`���=�g�S'�e{9��,�����RB-m����s��������Or�Vw4�jk�M���>9(<����>~���zTs�D��}�_V������%�����y���D.���"	�x)��j;�FZ
�MN��zn�����`�N�5�x~����9&lYt��@Z�%h�U��m8��
�<�$��~}��~��
�/�TD4CQ�>De�@�4���b�@V�W��><�������0(����0������Z���_��8G��V�|�%�����	�����;�;���F�5���8������F��Bq3I�u�UX�F������0�����
Y�L�D����M�w��r�(hj�~o�KE>y�R�m�]�������m��#��S�s��$^}�m�v�����]�,$��0��
]/(=xU������QR�l]?|Q9�5�.R��(s\������L���$[h*�{�i�E��]��k)�� o��=��iM����r���
��ZJ���
�B�J�x���$��g|�CX&�jF�
�|�H�Os2]k���u���BN��D�L�}�S����L�`�m��C���(���������jC2
����8B���f0e*�E���`��e�����i3����q)���2�����8
���"��}�el��]dq���X�~��7X�>��{f�X���c���
`1�i��p��$Wa.�$�F�I?#?�
t]����4+�l�8��1;����p�n���������k�STs���L^?�F�`7�tS��t�����d�*�6,D����*�we�"�	u�������UZ��������cZW�VX�\e��U���6
`���,_�y���R�lh8�������[��
�E�L����s�s~E����
u'���`O���vn&�X;�����������'Hc�'"��W�|o��`c��;W�X��^�[��C��4�@N����rp���ltI�@�
��YjCv�O������E��4�;�����j����U�)�o�����z�a�:����7��S������tp0��0�o�M��m���?&�L;"%�������x�)O��t�<h�x������ n��_	:L�N��8�\�t�A��2�������
C�,����Kr�8���"��*��R���J�	0�3F����o����p��:��j ������~2�(���Q�p65��w�����(��n��|��4�[�t�����~OY(���HU�����'P��{�KC��L$3��A�a�~$4�i�>hc��S�h�F���>r;y�#g�DC����@�8j���su�8o7��y��NN�mU��Lf_.�]���Bn���M���Y-��0^M������>���D�^<�)��^7�����7��	�H��F�����S_�������V`a��]���;���Q������^uc-�Th�0*35E"^X`|X���"���U���(oj�i��mc;���3Q~R������q�scP�A�G���yY�$�Q�,�����tH1����k����������
�G�u�#��������\NVB����"�`/�����pz_r���w�(k9wn���7�Y�������������4�1�B����������.O���}tQ	�C��+�tKt�H�4�//
Z�V�1�T���h{j���"�W
`�Q��c$�^^d*ilOY�V����*m��u1o!�:o�_����
���v�s��|y�`}��As"G��SU�<�z��9�����b��I$,�4�����8?j�Rje;���v�����rt+6�g3W����rwL�����_W�!Q!�W#'�:��E>2���1n/�7������������&ZEo�����s��&&QFs'z���R�9�&���K�V�=����{lE�������%����E����i�.:x	z�L�����n����X���P�D���D���O������8P8�/�^#�����!1���+��C���>�q��cR;��kk0o�����ot��iE���I�#������'����J�,`��q�G�1N$�F~�:j�q������4�4������U[[[{$r�Tz�5O[������oP�<���4e���)���{�:'%�2��7�����t3Cqs��Io
3�o���7����`����]���h����������_��
�f/�\�y����qn����t'��Q8$�*�TU���`U�T�B�����\�v
�.`����y�j[%G/�T#&�:QZX%��
� �J�i�U��/�nx�j�|������H�F��q�������D�U�����[�Zsn�� ��a����*�$�Q�	x+��`���I�S����$��r���W�����aN!����d�w`t�F���c6%��������<72S��0�|w�X� N�����?��@��H�!��Q�"���O�|3��c����o�A�6=�|���O�C�3������<A�^cH�U�7�Y��������F>n�8Eb�)�$:^U��!�@W�[��j��Z`[�gtC!��W%h����k���\�OX��]|{�lt�;���v�^�Je�!k��UntE��������[^��B2Y�zW}
X�L-|U���n��(u��?�?��y.�*%\	�W�-�Vz������K�YaG8t�`�O}�
�c�jb���\��hX��p&H����w��������r�����;'N��m!�������@��g��_��B)� �������`���7�`A<R:�"�o�����E;y[>�\_��q������2b������f�\f��H�a�*��M�b
4��U��P��;��.�S��s�U�PENp,a�����k�w����{�Ui�:z� ���B��
M~��W����������J�/�nL������0+�@G����Dq�a��&g���y�r�{p����C���P-~UQ��q|L���Z^�j����0dh�Y�����������C3h.8L����pM~:J��_<����OkG���C4p�2����^����[r����52��>s8d�;�r�����$��1�HI�i��mB�G���l�����wca������Q��o�^;V���'�[�P@����J`�cvm{�5�Q���������F����vfx�:������x!=$������3��Rl&Y{��R��gF�J�H�Z��*(Z��y��$U9�lK�'��������W@�amV�Z�n��K|El������z����J0$��G�2D�������~�C��PgTR�K�OF��_�/&B��P3������!�'aw
GD��Q�R�\o����$�����*��(�T���bb�+�ds�0������7��L�I�G����s���L�A��tP-s��<??=�eS���l�#`�}"����/��;hJ������|�t(w��������\��0��g����]e�"\���b���m,7��Fp]uM�Uy�
@��1�&p�>�Nz����8$������dkr�"�d���/��1q6�������/��w��I<ma�4��p6U�M�6�Z�,�i���T���j����A��j�Gz��mU��F�Bk�y�[���g�(	�b��RK��T�j
^��V�R��(x��UW���[��]E�P��*��B4\�*��v��6_7�Z'��F���ML���UI�f}�n���@�Zo�?��5��e�7����OO����e�3��tr���g���B����JwJ��F:�	����N����E��@@Ayx�P�J�_,�a����?�Z�;���+��(�-h!F�����s�XPC��#�9��|K����L}S�T�;l��K���yqy~�N7��W��dQ��Z�d�_}��'G���&z�����dA��{�J�f&�B�@�d���T]���w�W=�l��B.h/�����1�_h������R��;�-��u(��d8���Y���U�V�����������.�Z��8cJ��U���8��2kf�
-3!�W���7=��d�LIVV^�������B�k�o�F��1�������~����U�9C���Hj(!
��i��t{E��UY�|/����N�b#���-�*�	�}����R
c�<+���&C��SP��3�&U�)�U������v� �z59����|��6����ag��U0����`E��r�����p�#���������1K�G��<����Wy���"6;�'s$Oz����'������������O���?�v4o��
#80Robert Haas
robertmhaas@gmail.com
In reply to: Dimitri Fontaine (#75)
Re: Extensions, this time with a patch

On Thu, Oct 21, 2010 at 11:12 AM, Dimitri Fontaine
<dimitri@2ndquadrant.fr> wrote:

"David E. Wheeler" <david@kineticode.com> writes:

Sure. The reason to do it, though, is so that extension authors can create
just one metadata file, instead of two (or three, if one must also put such
data into the Makefile).

That's a good idea, but my guess is that the implementation cost of
supporting the control format in your perl infrastructure is at least an
order of magnitude lower than the cost for me to support your current
JSON file format, so I lean towards you having an automated way to fill
in the json file from the control one...

Ah, truth to power. :-)

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#81Itagaki Takahiro
itagaki.takahiro@gmail.com
In reply to: Dimitri Fontaine (#79)
Re: Extensions, this time with a patch

On Fri, Oct 22, 2010 at 1:31 AM, Dimitri Fontaine
<dimitri@2ndquadrant.fr> wrote:

Of course, you what that means? Yes, another version of the patch, that
will build the control file out of the control.in at build time rather
than install time, and that's back to using EXTVERSION both in the
Makefile and in the .control.in file.

Here are detailed report for v9 patch.

* extension.v9.patch.gz seems to contain other changes in the repo.
Did you use old master to get the diff?

* Typo in doc/xfunc.sgml. They are to be "replaceable" probably.
openjade:xfunc.sgml:2510:32:E: element "REPLACABLE" undefined
openjade:xfunc.sgml:2523:46:E: element "REPLACABLE" undefined

* There are some inconsistency between extension names in \dx+ view
and actual name used by CREATE EXTENSION.
- auto_username => insert_username
- intarray => _int
- xml2 => pgxml
We might need to rename them, or add 'installer'/'uninstaller' entries
into control files to support different extension names from .so name.

* pg_execute_from_file() and encoding
It expects the file is in server encoding, but it is not always true
because we support multiple encodings in the same installation.
How about adding encoding parameter to the function?
=> pg_execute_from_file( file, encoding )
CREATE EXTENSION could have optional ENCODING option.
=> CREATE EXTENSION name [ ENCODING 'which' ]

I strongly hope the multi-encoding support for my Japanese textsearch
extension. Comments in the extension is written in UTF-8, but both
UTF-8 and EUC_JP are equally used for database encodings.

* Error messages in pg_execute_from_file()
- "must be superuser to get file information" would be
"must be superuser to execute file" .
- "File '%s' could not be executed" would be
"could not execute file: '%s'". Our message style guide is here:
http://www.postgresql.org/docs/9.0/static/error-style-guide.html
Many messages in extension.c are also to be adjusted.

commands/extension.c needs to be cleaned up a bit more:
* fsize in read_extension_control_file() is not used.
* ferror() test just after AllocateFile() is not needed;
NULL checking is enough.
* malloc() in add_extension_custom_variable_classes().
I think the README says nothing about malloc() except assign_hook
cases; palloc would be better here.

BTW, did you register your patch to the next commitfest?
It would be better to do so for tracking the activities.
https://commitfest.postgresql.org/action/commitfest_view?id=8

--
Itagaki Takahiro

#82Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Itagaki Takahiro (#81)
1 attachment(s)
Re: Extensions, this time with a patch

Itagaki Takahiro <itagaki.takahiro@gmail.com> writes:

* extension.v9.patch.gz seems to contain other changes in the repo.
Did you use old master to get the diff?

Sorry about that, I've been using git diff master.. but this time, I did
merge pgmaster (upstream) into extension directly rather than into my
local master then merge into extension. So that the patch contained all
the accumulated diffs between previous git pull and this one.

Please find attached a clean v9 patch, that is easy to get from the git
repository too.

http://git.postgresql.org/gitweb?p=postgresql-extension.git;a=shortlog;h=refs/heads/extension

* Typo in doc/xfunc.sgml. They are to be "replaceable" probably.
openjade:xfunc.sgml:2510:32:E: element "REPLACABLE" undefined
openjade:xfunc.sgml:2523:46:E: element "REPLACABLE" undefined

Fixing now, will be ok in v10 later today.

* There are some inconsistency between extension names in \dx+ view
and actual name used by CREATE EXTENSION.
- auto_username => insert_username
- intarray => _int
- xml2 => pgxml
We might need to rename them, or add 'installer'/'uninstaller' entries
into control files to support different extension names from .so name.

The problem here is that the design and the implementation differ. The
design is to have no flexibility on the SQL script name, it has to share
the .control base name. Now, the implementation of \dx+ will report the
name field read into the control file, which can be different from the
file name.

Please note that given DROP EXTENSION name [ RESTRICT | CASCADE ]; I
think that uninstall script are deprecated.

So the fix is to either change the design so that the script file to use
is to be found in the control file, or change the implementation to
remove the name field into the control file: the name of the extension
would then be the control file name sans extension.

I lean toward adding support for a script variable into the control file
which defaults to script = '${name}.sql' and will have to be edited only
in those 3 cases you're reporting about. I'm going to work on that this
morning, it looks simple enough to get reworked if necessary.

(yes it means we have to scan all control files to find the one where
the name property is the one given in the CREATE EXTENSION command,
but the code already exists --- it still has to be refactored)

* pg_execute_from_file() and encoding
It expects the file is in server encoding, but it is not always true
because we support multiple encodings in the same installation.
How about adding encoding parameter to the function?
=> pg_execute_from_file( file, encoding )
CREATE EXTENSION could have optional ENCODING option.
=> CREATE EXTENSION name [ ENCODING 'which' ]

I strongly hope the multi-encoding support for my Japanese textsearch
extension. Comments in the extension is written in UTF-8, but both
UTF-8 and EUC_JP are equally used for database encodings.

That's just a part of the problem that is yet to be addressed, be
assured that I also want to fix it. Will go discover some more code and
find the APIs to make your proposal happen, should get implemented in
v10.

You're not saying it, but I think the encoding argument of the function
should be optional and default to database encoding, as the check is
already implemented:

pg_verifymbstr(query_string, nbytes, false);

* Error messages in pg_execute_from_file()
- "must be superuser to get file information" would be
"must be superuser to execute file" .
- "File '%s' could not be executed" would be
"could not execute file: '%s'". Our message style guide is here:
http://www.postgresql.org/docs/9.0/static/error-style-guide.html
Many messages in extension.c are also to be adjusted.

Will do.

commands/extension.c needs to be cleaned up a bit more:
* fsize in read_extension_control_file() is not used.
* ferror() test just after AllocateFile() is not needed;
NULL checking is enough.
* malloc() in add_extension_custom_variable_classes().
I think the README says nothing about malloc() except assign_hook
cases; palloc would be better here.

I didn't read it as assign_hook only, because at least in firsts
incarnations of the patch it looked like I was creating guc entries by
myself. So I compared to guc_malloc, guc_realloc, guc_strdup. Will
happily switch to memory context dependent allocations if that's what to
do here.

BTW, did you register your patch to the next commitfest?
It would be better to do so for tracking the activities.
https://commitfest.postgresql.org/action/commitfest_view?id=8

Will do starting with v10, but until about v8 the patch was missing
features: I wanted to make sure the direction taken was ok, but it
didn't feel ready to submit. It's taking shape now ;)

Regards, and thanks again for your reviewing time!
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

Attachments:

extension.v9.patch.gzapplication/octet-streamDownload
��9�Lextension.v9.patch�]ks������
�43�kI�/�8u<����=��c9�����@$$��H��,����]� ��L�iO�im
X@����@���O��yq���p��� J�w��+�� ?t�]6\F���?����}����w7��Yf�-3�{�[H=�����H�h��x\r����_��%��v��o7g����K�|������g�a0�Qu�>�6��k�m0���}��
�|��_>>\]~:���x\��1���go�9a�
�?��b|�w{�,�V�����D�2|N�~�;ZH��G�;����LD2�#(��T���L�[P�����P�[�*�Y���N��,�2h)�(N��Xf�T��u���D��b�Bz[4�)�,b0�&���q�����BA�����Y5���%��8���(�9<���lGb���g�U��>_��z2�����y<e�>cw<�&<e�c��g������e4��4�6d����� 3X�2e%h}>�dr�$q���
��-!��h��D��T��/�=0��a��c���g��+c����~������l9 =.'��,�
��iR<e�3��g������NEz�C�z5pYg�m����0(�R��b��Q�o�?���m��l�p�z�b������������$h(a��KI���������f��������Vojr1�c_/���]R�J  	}{G�I�q���gq#*J�u<���?���TSc�������h�����f���N�W�����v���u*()l������"
M/�p�WDc��0�F�;uu���.9M��:��W���?�������w��E/����O9�-���X��e�X
&�*��k+%a�>��	k,'}��OE�
Pl�Z���,�aq&�V`l�~f��0���r�r�:3[553�^6����9S[���s���^�a-=�5���)��
��6�"�e,�Y�M������0�!J�)��B
n��+=���l�j���4�������{]�1���ai]���[P�E%��{p�C��F�|��GZ��\o�F�����%g\
��WC��\DK�������`�5`4�0����������@3�H����B!��A�����c������}�O�������G^���@��������6�Q��>������������=W`.'k"�e���S^q1CoA7�(���rz�0 ��S��l��;m�����?��Ny�M�0�@�C:A��������'�8��Hwn����h3,�+T��5q�tb��)/{���4�G�4y

`�G�A�l.D�c����Dbp���rmm��s�xe��74�wa� ������,NS�����z=("/��g����3]��a<��9�F�"�E�n�!��A��s�����>E��E�����^�������t�dL���5�Wc�_w��C�������'�ny"�6L��vi�R6�J4�M���W��J�g%j�!Z���]E��b""?���~�a��5���4����4��W)2����X���p&X�����"���i6�k��8��*89����LhpB7�'cp�(h-�Z��B�!���z-��5�^f1��S^�� ��3����D����*���2a9�Y	i��@������skc���o�������������i-�!���"����b�������F��D6��NU�G���/m	��z9����u�&������6
�. �������������#�)\$i��<�C
���G��U�7��I�vMK���r��q��M���]S� �B�a\R��G|�����X������VP&��6�����k����ZZ>��tvAj{*�k��8YW����h��tcc�O�I���F�n�
��`����	���h#�4!���6��G]�|��'oY���$�\�$<�V}�a$��Q	:9�����m��UU��@�-h��5Qeuaa�*���TJ+~%��o�P����q�Ks�;��9�������4Q-�4	m��1� GU��	�M��V1T�d+W�%V�����/��&���)�tNx*|��Q���R�
��U�5{"��)O�@���-���-`��/�U����h����ew�b��Y.$cD���m����
�@;���\��F�6e�����ihg��5��&��,�~��X�'f�����{��w�{��0��P*dx�d�r�C(z���_�Y:�.��[��F��7l���\�!��t]�Ix"�����b���4n��b�o�,hgO�g:��R0����I�tL1n�G����������!� cS�.��l2�^?�
��r������"����7c��$����)D+��=�H=?���	��eps�����M$Hf�[�p����)�(��y2���G�j�@K����p���"��v�x������J�2�+�>��E@�����$�bl��,�F������C4��,��,V�N�-FX`��3���Q�7Dv�*�6.h�,	�p5$��c��b���c -��*����B�6k#�t�@�����������[Ok�H����T-�5�4����c���Q"���F���O��I�t~D��o�0<�����*��(3W�V�|4j���uQ:����b#��2���E�
fw=��i���+����7���rV������6�}9��{���c�&�����C�L������|s}uQ���,S���A@�\�@2�/�k����b�$�dBf�g}�Fi?��
Gl���G���6�[�e��L�X!C����h-j-��X�����JJ�u/�j���,�(,k��Z��(��1��NJL�����(s>��NJ4�VK�2��I�.oVgVi;�B.3�\O�'-�~�XS�]�+��bH,�%���C�E��<T[��=���V���e�
���������Q;�/Ct=
���	T�����9Y���[�!��W>�u��vTIe7��������p�����/��R�����0�[8j���
��4���V8d��?VPH�<�m%��,#���`q�� �I�5��g`��Y���`��D�����97�Vw"���������Z��:�N�Y��N+7��V��[�6o�"�����t���-2��L>�2M��qS�_a�&o�n���v��+6���3����m���!��s<��U���-�B��A<���6�������fq���;��$�
��M�y+|������xp����������Xf���O����8��-��Q~o�mS�y������:��3�;�-���Fq7�=���d��?�8��pyQy[���}�O-p��O�P�����,���6K�!X�J���v?��.�U��e�����9<�	#���uA�w`�"/+�b,"<1-P��s�����|�F�m�lo~��a���=�z���.�-^vd�0�fk7��&����	J��&��������l�@��]Z�l�[���AE��G���x�A|��x,{�I3��������G�ql����Wf���=)�l�TM}";������;@�]��b��)��,�Bqz����4NAQ�
�-*N��J^B[����n�G����N�4��]-u����L����c�u7���`�F��.����TOEh�',����N!��}
h�WP��H���:��;_d<������f+�A���\,���}���4�e�C�q��l(�>=�
����;I����N� �K���=�\��Y$�u�*��	�D�QH/
������YCv�a�qc�|�ZN��=)E��]�4���8U���H�Pn^�����V��#�p�����[[-�z#}��`�����"wn*,�$��'q����8o��\���aRp�%��h��x*��5�9zX�����j��������3!8����OO�,ss���8��	�)�=��|@���5'����Xv�XjI�A���%�q���;Oy�������H7`��nP�&0VL���8>������������f���%�r�r�����:�-e��&��;0�(]Z�k��rB�V�'�VFN�KG]���g�q�I���Xkf��G�a�/��_t^��/�:���k�����!Ws5�����kx�F�p$�R�I��*[��������������l�-�%��_����XU�J�V�?g��!�-_���J^]��;/���8#20�8��!�2���l���Fg%�+�*B�s�����,�	�+�
7C��[a�����1��_/.
B<���dsp��|���U�2��P���~��
�A�\9��1<L��0(���E?#J�#:�2��$��C{�,!C,��E�Ba{��vA�N�g���p��rrC����
����������j`����/��dU�����;��YF����hd�X���0_�����Y�;���p;���>��	��/0I0=����d�����`,x��4�����.p���Up�`z�<����5����_F=�l3	�A��Cwg�8����(��BfbJM�)������&���t/d�����S%j�����A%I����������"oiMi��������e5�#��]%)K���<�y�Mm�}x���x{ �!��{�^���yw}���:O\Z7��'L�?���]aw��X��HI$'�������H?�OmX������8er�`m�Z�	���H$<!%���x�,RB6�q�W�����(��1����T���X��E_[�
�-K�r���l�z*�K6�'%Fu���2����k~������]�W3OKR1��'���*���*g��`@���J���-�����]�D��b��
6���o-���,c�
k����wc�2�!Q+�#��fJ��@IEO��%��#��Gv,`�o
��r��s��k�Zs�����[P���M�\,;�F��n�5t����C�A"h��&M�YId�q����8%Uz+���T�+�3��&�#7%���]��s��S�������\?Za�mJ5,��(������w�r`��,�
���d�W*�#>yA~~��d4g�l�������l��DP�#N�!-��2�0����/F|f����
�$��6�����-�0F����1�\�=���kTv=�|a�i��x�c����W�a���&0��:��N�5y�Ee��y}���x�\��'������vq�����>]_��{��a<F`�8����6]�3�;;?��1�w|��6KqO*�8��aO��Z������jp����O�`��D9�����'��g��L�S��l�)�#����=�I����v�@���o��\>
L�:��V�UHya%A��3�ng>�.��B�z�!��#}^L�g;�IN�������������9>:��=a6s1p�k�S�8����c�JR������Z�
���7��,�;�Ji�f)�0�g�������mr���w2l����R�
AB%�*������|s������R�^F�B�>]��,�G;c���'(o���p���Pn��0H�n�hM��b�b��Z��6��7���e)�rNX���\p�T���)����&��Y��mZe��,Yh~r�F���
S[Y�O0gI��j����ds�b&�gkj���{@��'FYt������i�5e1;������,:Rp4�,��8���6����]$����?u���fQ��%'?;/�'���BW��:*�q�e�5�������-.0���tR�Y�R��g��,#c�7_[I�>���8��Ydk�;�W�v�X2D�-�%^�Rw�[������5|#,���D�a�����0��8��Q��<z�	���,p�a�}�� 1z�s`���Q ��3R���1Ct������eU>�I�
�RquD����Vk��=��,�8�&������:�H���5�hF���W�&{	�
p�51M���1�o��������a���%����}#��o��S����p��� ��� ���eN��RbDN�+�2�����3V��+�14]M2�Ww��6�j����,������}�m���<��LK�k1o��7�AD�)�k{7���[6���tK3
��i������H��9����_��Kfdn�hx��s���*H��s~�����Q���Q|q�2��{�*2�!xG�F���o��
���{�~�?�|~	��?��^������\o������l+S��!O����^�L����jQ
��3]�1������u�R����������}mY�8�7�**lb$�������� ;��� Hf�$����P+j�������s�[w�$l'�g�� u��N�:�#����_/�6vn��c{��}���+�W�D��u�k�WF���$cu3�pO���-�QS��q ��vG��Q$@Z?�z)0�t���E�^6�]�]@�lWm���;V�@C���i��!�����#/�3������h:�������k�E��J%������$K��O(��22���u,������8b��G��K3�����$��X6I�������6�X�wf��BR�|�����
�B|�S/���'�Y����Aw��n\��N���	��}�h��0j)+��S8���,��-A��M�\9H����
z��YP�~�BeV����~?�n����B[&���_�3��^0�A:�&Pp�X5����E��8�"W�P�N�bumVY^9�E�P���NW]�W�b�bU���u���tq�?��n���%oys#�!���k*���-]3�i3�]�DP���6
P�L���$��
#X���\�BU]��W-�|�AG��,��3`��/���a��|�AE�j*
n_t<����Z1�J� �g��W�4�io������!���P0��IVE�0�lHf�p�f��������N��u��}������>�_L��@��Y9�Du��S���6�i�]S�M��q�q�^=yg[&��� ��)7��Gi��c4��8�a|�<��)n�tL���X����[��ti��9�;���,���o�w�R���?wX��*����;������4&r����"7&3T�-5�O����w� h-~�����6���<���2l������F���=3"y
Z��

��;����@�h[
R�(����L��'�7%%��:I�&������A�(w����Yy��Kr��K
�&�ga�Nw����T�[��_}��T|[2x:`�OXJG�#@�w����H�bD�{���(�	���O�����Q���8]��w3l��t�r-��h�u���S�A�SG��A�(��Y�w<���9/<s��qp%&3rrj��'gA0$z��� SDM�*?��FLG�_fs���L��������oN�_��~&��x����$'v�{/�,����W��D����1���$��'D�dQH���b2�.��N[���8XI�/I��}�g>JZ�5YF����ZBb��p��8����+Tv�J���+�����uYtb���?��Fi9�|���k:f����0��������d����"�6Fc��0��]�Es0��i!.��4.ht��~��H:�6�����Vx�"�,CxY-Y� �`��wt@�r~'���r;�g'��u��g����s8R��*R�glrG�gk�C�H��o���+��j���-	�
��g�o�������~�`����������������W�ap$��Kt��)�4�oL4u7T���`mq��N�`�E�Q��g3=�JAf$���
�`�^�Ux
Y��&,�^!Y����r*�>�q����3�����
�;���l	��*�$�XQ��l
�d������9�M��)�`g-�����Q��
���i|����N�{.�����C;!a��?`Z�}����+�@�<'3xb�z�v~��&����t��?|��;9~�
���O���j$F�{�T����Y�����K���K���Mj�j�-����[��n8j�Gy����\+srOI����a*��E�J��p�S����]��I~Q�c��5wN�k�y����:��x�$�X�L\�����"���g��������h�E�k��3�r����3��b;%�����y<�q>|����
��\���z������iS�m���mr}3A�XMGd��������$�vL�����,��N��\�t9�*�`�ku(�����_N��rb��|��<����sY�8��8KHO<I�n�/F����OA=���O�fP:l�9��N��x�����qt9�fRS����g�rS�:f�xX���l?Agvq�u�D��7"$�M�]�R@vv,�j�_����?D��B�����bH\��U�j-�HO;��
�]��h����c�-q�B�58l�
�����a���%f�2qq��w�3�3��������w�]S�]S�]���
�FR�)�^����qw�uo��Cf�'���rAS��h�
H_M0����sy:I���i�v�x1��m��_���-�R����������U������yiT���N�Y�o3�����8Z���Pz��������0S��S���(A#�b�	�G8�����m_��u?����9�Q8BM��^�Yoz�d���jQ��I�hl<Es{��<���1����<Z��"��m|����1�3~7�I�q"����"����p�8B
%���8�/�	��	����1X�L����v�R����r�i�IG���-�;in������k�Qq�������F��d����p�6���*(��D��{/ �$JHMaR�.b�Y�^�>�	`0'�
M-�<����R��m���G����`E����7��*�W�����/<M�u��F�h�@��#%w5��E�w�Z���]E�=�%X�����0'+��Q`������9A����&�ai�2�������)��Rr�*4J����U�������>�?�N��z������j�?��.��7�e|��r�s��YP����d��x��Z]�����0�"[�T�C�_�U�R�*�|����4��d\��!0�I���?�������V�?�����\�u��1o��y������%��)j�2P�w#w����=
(A�
 ��`,�W��������1���	���KF=�/pf�]<f���i:1q�����x���n��W���+���]2��{Ruuu��I��[
�J��,&�ZYQq���br����uZ�Qq=g�	���r�c�Y���C[*+��[��)������S#��31&�"�F����K��;�+�2ou����S����v��)O���At��.�os ��&��5������uw|�j0
rD���s�vF8wQ>����G?_�_��.�������0�4x�(9�:`�w�!o�������J��+���v(���r$�$74�7�RV������q��l$]�d�^��6�"�
Rr��m��6�m�nC�.j 9�d��I���Wx��G����������a[��Q�n~��KL�5I��\�\�PA����Yn	�C��"��d���Ud"$v>���"���������I��gt^���i��4�,���"z�'�*�$T��)���B�&�����N�u��+�J#�d�?I���6��o8,?#7dHBm#�B�t{.���A�2P�U*cE�Ro�v�"�����M�O����ua2q�b���������Z�����T��g��r���1�-��9�+m$�{s��---8�,���m5�&�Z�
cS�P��_���E��q|[X.��|�t��8���J��&���AX�/�6f
�u)���_������v�0����1�2���e�`���5B&��L����:!�~����9L85���0W"QE����PG�����s��'(��\�1������E�Qicu����;NB�����p��q�lY=�aj
�94�1��
d����e2��fo���(��O������U���v������&�b^���Pc}1���o�<�8�=M����%�����w�a��vN�Z�[��GKk�����4��'@��>��L�c��6r|r���6���F�.�q6���L(�`+��'��F�
��7����y��Lf����2�u�d�j|��p%���;
&�^?F!o��@^!O��g���c�lO���S.r��z�L�0������V'��a%��1�j�v�ke�?bf)�!T��@�?}h�K��nw�.�[�F�^Q�$����6"	%
�b{�,v�FT������i���F[�^N��7Q}sGKY�g�Lt�?���7������?%D�ZQ�H�0�V?�e�
�W�?��u�6]tni���Y�����n�}�<�zp5�a����(�y�z�:~����<��7�u�*��W�G��o{��N�������EN4
�J��-���V]��L���H��������D� 	@�XB��0U�]I���V��R�����5���7��*nE>�`5���"�6�\^4���~>�"���w�+^Z�-���
���yf:���kj_MQH�f��w� B������V%5U�!��PQ�Wt�#��q�F���"1�t�k8����t4Z�����z��j����^���s]�p�# �u���|�v��R�����A5�^����V(g�)�(���0�5�R�q6a�zW�N�e�f�
�Q�2�F(��x%�x�{���d+�M�y^�i�p��\����1z�0�rN��:��lUs�RG/�PE�+c�5S��|B-�S��;@������J-=���<p�����[�\ic�D��O5O4��b����������s���o�+\��DE�U��=d�#H*E=�W�����VW�VW�V��|�U��G��4h�������(sa�y����bcmk�oo:�7���+_F���x�k{z�P���,�Vo�{l }����.V�]�M����'��8\��/�v�|ssk=�_;��t�X���:7�`'�:�m����X�gl��d�����sl��/Q���Uy�,�u�yvvr�X����4};��n�6�~�o������J;C�v�o��5�J+�X�Ne�i-[�F�uS��'Z���:y�>�?h
x���_H�Pr~�w1��������m2�w7��^��3�B�:�:��c�0�	'����p)+�����OG�������%1/�N�#�J�HW���h��+{�s�P�,�>{Ef=����^M���$(�5`4&T�e)��Z'�M����~7�`���4�<�B�����(���
dN�I[UtB4�X�k>��fy�4���0�e@"fH"r<���
�S���l��K�b�1�f�M;�
��h����h��O�����T6�9��/����M�up^��Rg�]S�:�}�&��#9�������?i�������F_�]LZ�����s���8oQ�K�$�������J&��3'���q����?@�J����_�
-�T�$�l`7Mi�`E�Lbp�F�|����7c2�o�����D��``/�����NW�U��=H�kB������y��'����!C!���x�	UD����A����x	�z�4�+�����4|r8J�������x	.��>����]RB"{�6dmO%�[dO^hA�J��dU�%�� ��;����c]�*]i��T�����V��8S���6/;"�"���sV���\��6S�%B�
P6����)35](���"����R�����6�;S�C@/]��R����oD�u��1~�xM�S� �|�"%����7���~�+�yQ���cD%�I���[C2�;�B��K��g��J��h��D}Sz���\%26`��������uQb����p��H��4�"���m�����.��de��&�������T�I����p����E�����_Z�����7�'��y�s���&Z])2]*<?�o��ta
}��^��N��
���V����Hs�F@;M+\�����G{nAM5CI�z)��>������+��v�+�w5�5J�9�2��B��������������r�E��sf���g`9��s�;�,vva�w7>�B�::�Y�����1�?��l��4�z�8�������|Or�aK��H��yR�:~"��+��I4k��4�w�:S2���&��nt��I��bY/pH����'�����A^�yq��hF�X�a���k�5������W(\�Y��������j��G�"��c
"��!}x��f�'��#A^*_?
��-�#T:��6���9g�#����qF�L�a29�R���3��] �Q�(ve)Y>��0,�c�m�ct���4 ��:�!X��S�dP2��;���uc&�dW`���a$�X}���k�SZhRa*���'�����B���Cp� �Yn#� ��>CsV,/2N����SC���S��P���-
���HR�3�	a
$�e�n��/Ay!O��"\�����#g��D�w���O*�����\ ��������y=�^����&�q��JP������wg'G������o����%J���ek����
�{��b��H��v�MW��4�o�k^a�d�������,���C4%�����^YO�Y�������f�t�����G�'{(�Sp�vs����Z_����E�w��Ko�[u�+����Hlw�fm��G]Pm��es�t����������Z���H�q��!�l�.c
���f�����Sh��pW:�-�����67rz������R��x����uX���j'�Gf�������:K-$�����L�Hz]1���|Z�Hc�r{PuU9Y������5ZU��jr����h}�1|��;e����>�e-�Q
�p�-r!��b �jV:�\L\�
c���1���tR���p4�t8dn	Z�1��E: �cV��c��4v��9��Y���Co?��=R�->5hB[q#�9^���pL����'q���Lv(�9@��B�Z���@1|�*����t�����]�l`���$u"�$S�_�G���\��� �&1!9�zr7����+#�����h��'�~e2U��`�t�b���������9$�@a�gI��3�Y2.Q�tC�~s ��
��@t�������_��
�9��c3��h���;�cHZ�jP'���b��:3�z�Q�i�s��6&��h��cj�f�<������d������+|���|7�#����[�o���aT��0��9�T����K|�v��5������>�7��W�"H=�d������kn��O�#G�j\u���,F�A��(����O��&��#����z��a;;z��j�<o"�kO�p����i�#���S�����l
 	X��m�n\��C}�<H��3��!4f����QV���h
'�����
zU�gD"w�#5�����8���%����(�I'�Z�Lv8,#�� [���B� zs�Z�2TpU��X��"[d(�\U�7U��y�>?�88�)�G������vY���#e^i�/�Aec#�n�c�����	�T]���8�e:�7�l�$#px�"vz������X5����C=NG}���v>�v���t�~���f��f3��DH����-<���}g<�I�'�4����1�:'O
�i��������;���/4�������w�!F�]�n��MX�[���n��s�
����<>���=7�m�+�L��f�cC�m���m7��U %�������*�����"_�>�.��O������"�?�������{A��jPi���5|��)�S@�9��N�2��8��,�:r��ZG�����?�9����=7�����6���
9Rx%������|�v6�����r-9E��9�&}�N���X�M9f�p��L�}&��Ne�J� pnF��8~�>���D�--]�^�;�u�Cv8��E�����9������v�X��mik"�������wV��U���[��
�}p�q��,R�s���3<�?/x��m/�p��G2J��R&��h��/���,�\Pd���d��w$o�����+����qm5��E��:"���P&�el��$m5�����4���b>,bo$����n�<K�6�e�D&��,�9"u�:
GW?S�_L����;���.�����\(v�^��&!.�d���?�9���Y@H������
&}��,��������7�D�
E��P��F�:Q;��vl!n�)W��$�T����F��q��8>'q(|�F�����EN*d����SRb�t'�V���g��	��>��b���j#j���*cp�m1�}t��Az�zx���hu��
����pq��#�O�����;|%Uw��N��Z,-"/
~B[4�$#��&%����	�����J�H�u���+����|���JD�[�h�y"�K���C�9�[Re����_��=U��8u���1���8�wd�{?���B�*�����Q�lo�y����&�&O��qI�����v�g�[|M����O�U���������	WC�����A�y|�9=k}�:j�nZ��f��e���$�Ac@�����`��W�p-W���%�����R�^~��.�j#t\|6����Q�Vg�+���K�(�9r�l�N��+y���	������D{[Z��T�C��+�G�5�Dt��q�,R��C���u��2{{���^_����>~��~��;|���Vflk:LFS�gKfN���������cpNGBG}�����$���Yr9$�V}�1�o)?n|_�����`������i%S�K���Y��m�����/����t��hD}d���z�}�j�h���
�wH�.]Z��9���T�2&�6&3@�?�������vRl,����k��/��QJ{�=)]BI	A(��jY�/S����OH��A��}��t'�YN�	�������?�`�7�|I���N�O������8���-0�������:MA�>�M'�������+`���t(�����pP(��F���[3rvO�f$����>#�=��tLY4*�����n=i���"�_y�1�G:"$E�H�5�\k|�q���b��=>���h����I����l�p�T��"}8������O=�t��	O���v:L�I�v��{�v�h��?�������s�k��K�-��w(6(}	Mw'�q�[d �����Y�62e���9��v�[�>q�B�4��m�"HvGI�1�3S�P������/����U?�������gP|�r:�J�J�������>���gua9N�,�������m�=e��]�����B��_���q;�PHVJ��C�z#��P�1�U�]Z���{�%���;��^�,���a��d�����

��Dh_�h��M���s�&Z��=P��x{����D�4���Xs �B���}8��~U�r�2�#��M���/��uT�vl�c-Y	��:x�Q�
W���N�[������]
�A����� �/�P�����g�5�au���6CdW6���C�2~s:��7���+����(z�C��=��Taw��}6�C)u�!g�d����Z"�L�o%A���=��6�:�B����d,�\������l��B<�d��y
� K�0;6r(@��&�
��t�M�8��8��f���s<������~o(qx�b���Z��HM`��	��kd�c+E�T��W4�I�2�W��|(E�M��`������aPx_0=
���!:�k����3���3oQ����I�Q�$AJ^�,�j5	��a{�p���{�����N�Loj�����.T����M�������u7��!���A�����s�H*&V��H����|����G�)�����{.!K�xa_0�:�5a���H�8��3�qV���Ql�B��y��vK:M��+{��XFV<�<�;Y��6���Uk,%������tu�������2f��9����g���e!����B(4V��*]-T
eL
��j�C������u�e�x�����j����sD�Vw�^����H��Y\���������f�0�x�n�1C��������'�Z�)
���yI��gR<�)F�1Q��^����":%���hMl������T���������G�K%�[���9/��x�t�������Y9O��*oBU�g�l.�j1�~$��q?�x���� �� �%j('�jN���i��O�G24|�����X�~�j���b�����<�������������#�A�r���������ZJ����>��+;:�i��m]����������t�JB<���K���,m��Sm��MK.5'a�~Z��K�tO�Z[v��R�I��wz�h�
�M�MZ<q�D��qBJV]k�Kq��U�$��RjC-��
$b�T=���H�������J��re�����C�r%X2K����	�nF������v���@|��M+�!��`tO��%:��p7�lS���1v��Q�B^Z2v�9����$��{Tg��x�pB����j�n�A�������7��r���\�[Z.��Hy#����B�
q�6�y�H��I�qAT�#�}��s���Y����R��c3
�������vpo�v>�0�\����E�y>&����y��	h%��PS}�����L��}�E|R�d�*���)�:7�eVu��A�dO�iN�\�?#y���h*	�g�0��`<LCy������(��&�J	|��AQ-��8e����t��nRDh�^���
�k5�,�%x�pH�.�f�u@��	�Hgn�Y� �`�.��q�T^�#�����&��&�����"-W����8��~�U��k��M��l�+�����72_�#+�mO���hO|@
�IbL�~�Z
B}�pk�"o�D)����n]�@HR3�X��\� ��q|����78s��H�i{�6J��D~�A��5_ds	�����cg�0��6��0����yE�����H�/1v�(����A#>��8�����1��
+�8��M���aF�����u�Ue��� \�5��t���)/',��2#~����_7r������v�)�������>]��x�Z�=/SR��!i�[������i,���mb�D�PxU-|;����{�*�+�]�q�+Lr%�/7U����=����{���X�O+�|�	���>-��F�����g���_qr>���1	'��D�4����%/�f1�o/���l�y���y������J�Z�-�;AY�8�(�d[���������d���U��.�) ��7M:Rw7�8E�@�,�Z`��%�>��U����^�.�,t���)��+��UHVU���W��)��4��T�$QzD�VI���M�iC�X���&_�����O.l���68F8���������.Ni�44��V	�h���m����-uN�OC
�$��@�K=�C*]��C���j	1. �����,*��	�&����^��#�kI�/��\j��K_�[�,�����**��f�p��?�u��w,�������cH������	-�O��Y=&�����q�s����>���e�;���U8�hy���{�8w�P�M|s���2��KqzhJks7���1'��o�%�����n����#�y��i}I�Q�BR:</tv��E>�>1=���toP���_����\a����~Z��y����=$�4=u���$�n�e�0��q+��*|�r���ZlFvO���|}�M!��a.L���P���S9���P��Vp*'x�����q�or�F��A�C�����(-Y��+�(pn��J��kq-�~�Vl/����O�����S�2��Vi�a_-SP�c�<-����Q�%�|��G�h�<"
.�z$�%vS��0����iL?�|��5�f��U���g*_57�����$��0a�@@��'��q��cdy�7A��$.{|~�q�C�V��u,��ul��9�8=B��f�m���������86Z0xK����L8S���rr+mEl!�bN&v�f��g����?0��xr�%��z��9D^, ���3�U�����uy��a���;�7��X�"#kC7Cey���]��i\?bJE�p����X�%���<�D<���@�n�*��""����Xj�A�X�^e��U�Y,
��%��@F�)�c2��\\F=�*���xF��I���+���2���f�W�3gGm.�J~x���*��&�Uy����'o�3��<*v�1���������p��:pBL[0�C5����8�(�|��{����Z��6:��c����.�����4�U��F*xL�LY�k��RKp�xF#?Df�G;���]���L~|q~�"������~��8�M+g>��,�o�?7�m����@@��.�n�����
���l�����B����������N tLf-n�*��U�DO� 0P�-%�\3�E�1��uQ��m	���l��r�����������zn�S:�,�8��0n���U��%O�����-S��������c:���2-�&�x��rw�$VgX�g�����K�����8������Q��f>S���m	������o��b� !e(KHv�
�I�����y�i�7k�+��$���
���Og��@o�n��t��}�~�?;n����5��8����N%�25��C��������u�haq���Y��J������l[�o����~~���=P�A��r|jq_�Dt&i����9�"���^qM�}O9��!���tP,�l�-Ch�Zh��sz���p���������@��#$e
�F�wf�.� }���C�>]�8R,`e(�����+`����'��1TQ���?gIK|�/��ZRA�?�����hOJa/TV_�����e1K��z1Sy���c�������o�6hj�c�Q�C�����k~��E^_��\[��W���lY�I�z��r��.���Z����,h�0���lG��e�]T-�H�F�i���9�p��QV�������7�B��)��k�Q�E��P*1�CA���.%���t�{i�pav	,6��������=P�>?��a*��8_�sJ%��|�I
����"	0��:gi��d����6T�^D��z"�7/NKH1]��������~���Qd�2`�}��QSl=u�7�E��J��~MA#b`�A�9	�(?�$�q��{�(�
�z�����w����.���5��E�O�{�c%��e�c�e���;�Q�
���3u��q�0�����~e���0��{�b���|��?�{)���U�Y��
U�mx��"���aqu���
�6����
��IH�D������o��T�i��B}����}����8�HJ�)h���t���Uv5�I��7r�(���~�4���R�hI ���;�*�$"d�G�������P��;t"4�r�N�C���n6!AM|7���z�B�:����A�F��d(�o�K-H�P{�2��������9�>�wy����9�7���3��u�w7�qe����;k������8��o��o��c����������3A��5��������� ��
$CT��F�h��� ��,��W����c3h�"���2���� �86�������I$�$����hkkT�PC�)JS}|�0C�^�/��\d���j��x��L�����i��!G4�H��:b�|G��?��;����
y;t��?���-�|T�]4Dc���3���&�D(��b~�DY��b��Y�\"��
~���Fv�<�8;��<99����U�[3Q���zs��mG	aX0����cr.���L�?V$��eu�\YIV�~p��=A~��F�0+N��~LjC{�8�����4s*�
��Wjj?��=�FO��
���11���Sp�����	%�)�m6"�K��
V�&���.��<{6����i
8���djR_IJ��D?����HVf�<�����*zP�:�����V
�T+�~����:�A�,f��{om��dy���#9!w����1
UNzh�N� ��^�v�����������O�*��@�z ����l�Cc|>7�
-�Aw�*��D���h���o����2�V%���X|�J��������^��B���K&:���-v���F:�=?��6f�9�LM*��Q-���spE��P#BD�ct������K(�����1M�4�ct���<��2����n�q|]��Sg�b��@�8�/��HCKu�M^!�,�y<��?�;�	�/��w�@0�����Rn1��������+T�	�Z8��O^�
Tg&��<�����;�c�]�G?LA���k������L����V��}x�<�$� ����U����[d�H��oVs�C>|%��(	m�?�S���v��"���y3�Os|q�S�,�9os����Y���%�B���;�[�����RpeK��+�,�����4s�Q����6���\2�T��������z���&���r�>��H�O�a��R�N�)N��X�|�=�A���^� ��N�8�.���2��tD�����������0.@w����
yE12�4�5��l�7�����n�Mf^L��d��1���#����������j���;�8����K�|F�$f6�q�W�^����z��W3���BS<4�Cl�V�q'z��n�I����c�6�s����M�*3�}�m��l 4C�c`�:�v�U��}~�tTq�m�P G�h����|,��!���q8\���Sb*���e�0���[�9��;(�
�
�{��<�H3p���t�J�p��<��D����y|;�8�f�Mm<��
����T��HUP�����2������z�}>�[�p�
'���90�����@m���
'��'l6h����0�f��] ���t^b4������T|����!?,�AWH,��](��6��5���R�W�������&�hV��j&���y��U�����J�m��������M(��Ku��� ��1N9*'���P��������h��C�X����P�i��Z�y~q*�/T�7�r�����>�Zgp��{$���D��|����&�{��3�������LqqL)�:�����} ���[�I0I��J|�P�P)
��[���������c�:�;-�ZY��x/���nQu�'N���C�C<R�����'p9]��T �w�t8
�����N�2���4�� ����fZB#X�3���#�yA�����D�t��D��.��<��i]R��������������L:1RQ d2p��-�%��J<����&�
E�{���l�R}�B���<�����oU��*��rHj
7B�}�*q���*�_ra�r�;�'h��%��9"n�Yt.A��%�iD@}����'F����ec���e�e��O��������|�Q=� 0#�
�\��60�l�D�,�Q�FQ<��1i=X!ymG������z���+�t�R0����m���)�����L���u	���pb9�b����c�6>�
#��9���k�!�q�����44�q��'ls���7s���`��D���2�-�	��� Gx�����)��r]���~������v"�9I\��W?�p>C%,G�{w=Q���?�g�m�|�E���&�
a&��A�e���}�?y��x�
,���;�����y�8e�gu6��~V��d&���\`��L1x4��@�t�'�zd!�]�����v��dJ.}\^G�:�R)Ikvq��Q(qn����O��B�x|��f$�v���L��e@^oD��62~��9���n�EU��Q-��1�%�����O�Q�G�-�NT�+�%ZO�0���c	]���O�QoD
'M8Y�`<����w��#/!L��.��}~��_�a�������V#��V�e�����A�;��CvL_y��hig�'��7(m-d8��_����	&��w��8���?��N|h_�v����z`_�����R�Y~s���������F���������*��k�����_�����]���d<NE��mZh�w�����������M���+�'�e��Y�vw��=}L������������2��	o�����[����{Q����^��%�G�m	N�@,����~%�Y,�(&�>Zk*����M+z3-��F�)� ��l����9u�<��������E�;O3�}�m�8�����n6����#)u���������a*��S��F���q������_0H�I��P�u������������w���^uo����J�[	oN���%�E�&����>>���3-��k����yM��*�@�5p�]<"�&���3��p�0Z�J���p�i��q&ZF*�G[�;����U2�-<�|OzC�[�<Z���i6�z�����������1��;���J�R������������G���_��nF�k��8�N������������O,�	�^������4�M2r
����[�m:0�_?t��7��1G���	�h����������(LkmnD��]�\LcHI����x�#��a�ks3��6G�0gO^�/��>���D@�����j����Hm���!G������9��hc��_�%�'(����&����O �)����������.�nmF��kv���Z37�v�w����y'c�=y��1)�u.����7vv�zcw�����fm��_1�����7l}}m'���w]2P�7+��"�������,i��m2dK2�FP�����d�.���v.�i�c�����R�r$n�DtY�Son�MsS���4g]���n��=Jr��u�L�\��7��j�9�`��n�q����Y=FUa������G���w���������c�e8q�@l*�%d�����=P�������w��g����W�����z�g_����k���q�037�,2�@�V=���P�Wo����J}�|��c%%J�
D��p���m���(���p^+V�46�ja�m��������Q�����V���!��y����`�����cn��:�Hnnyi7�����m8t0���p����[�@��:0Ylf�!����(/Z�2�#�?�y�����������B`�����n�B�/5��-oY]�o�!J���1t|~i���������S,%B\�����/��d�>���(P����������W8[+�_R�J^����6���KP�G��&`klo�C���"��Wt�0��9�94{�qh�����K%�hV�G<E;C���r�9-��^�����"��q"�]���a��Q������
����E[vs#qy���(+a5�q�%$�����c!���w� �������>�D��������Pjt��'���O1�4f�o�L�P7��H�v<�"�{�@���9_���?�2�x�[���y=N�#��#t�.=���zO���Q���N�v�&��:���/�7����	m�Qf9�'������	�.
�~sr����Q���������;hD�R��7�����8K�L�a,1�t�����"U�O��U��wg7��������G�N��l:���c�7)�!����1o�<��>�4#(��n�56i�
4���'����q���Ozh�2��'3G���[9J�}�
���q:I���.i��|��W�9�{�]�#���2�;�����~H��E�(�gW������f�?���������:<��.�PD��f�uL.�����(������Q�����\�����y�p�����!|:�o��v������������_�������Y�����������C��Pv����e4��a$����7����W�s������}�{rtt�v����4�w�����KAm�5���&{�B��v�1�,*�=���_i�9��g��A����hkgS8e�����;���?�T��4�gg��%z���g�������}�GT��bV���6���Za��k[������������f5V�������~T6'��)���G�Kx�t4!�~�����gb�����h�Tx��S��z��/�_/�g�����B�I�������ap�@��g�p�)���"��H������;���m����S������C���n��7����F�l=y��9C��^��to�������� ��\���l����&��E���<
X���U���D��g6s�u�`+��!=�|
��
u��x����v�C~��Wxg��S:������f����|q�[��?c|�`�H;�O���	N����>��+O1|��I���}�bQ�����vfg3�������Q��c��K���%�H9)�������@ �S()��:>l�
����.���V�b����[��?�A�`=89��yF �Ou���b�5�<9m����BN���^p�h�+���6a����7�J�����}�9/�?��	���ga��g@C�����gX�y������o�v;��4��U�����������_@6�k��#��F�UV�"��t��'b}x��-��$y^M'�@��X�F��u���e�z�_a%^���#��f�;�����S�.k��}��~�T�]�P����(<|���m!&�t����}PO�+������_����a�}�����1�
�^����dYV`D2��EH@A��a������q1:z�}��H����dr���c2�c�B�U��zV,������=���*Tvy��k-�^y����FT��}���nE��F��@�f��/��B�V�-`� �9i.T��]��blnnG�[
#���;���e��������Z������<]�R�i'so*a�zU����)QU�I�C*��=,��<c�����;���q�_I��tt��Z.y�Z��o�Rb:��4��;Q}sm�� c�q��.����]��p���5 to��{��~K�&Y�����|�d�7-0E���j�h��mD���%�a���k���B7������p	K������u��
L����V9����w~#����9�v��'O��!���RA)\ToX���[R������f�*��j��m<i���"u�f��q��u^���@�N�#��F�2G�?����gj��Ie��j0�V������d��FY��_0�����_�b���6���*bd�X}��'��1��u�����v5~�t�:������ �J}�5�����2��h���,=v���ttM�m�:AC��(�?���
w��Sx��.T��6�s��������1�W�:~z�{�\N\�,��6��%*�-���#�Ay�w�z�~�62����O[g�P<��i��7�T"�<�n��H`MTW-cX�e
�=��w�S��I�L:T���o��J����������K������4+n�g������D|���v/xR��lhpX�A���D�����G���n��@3*�#�b�-�4��}���EQ�1aB0�]��VV���*v�j�C��F��%��W�3��O!k�9������9�^��)h�^��d8����W/	���g�����������"��nS��wx�r9��`��EN�}/��I�u���m]������`�'���iK�I���!����S�����r��]M�c��a:�������M�I�^9�pN��������P��c�������I�������e��s�������W����7����#����vn���!���p��`�78�����kBLU~���l��2�<hN�n�������:g�����<G������H /
����7����cw�S��q-��s��W�
Z�WQ�$���Er�U�cT���r�#QTCS��#��p
�jI��o/�|�&7��'�0���^b��,��m�FLU:���1���{���q�_I�'M�����w�e��N%��� ��q}me�F(DBK[]LA�N�����>�@��-��*UNw���������_P
z�<8���W��V�;Bj�	�>�X�&�������KID%1e2cB����)(*�D�.�`m�aK��!'�4��Kr�i@�2����Q������;��PR^�����+AKCt|���D���$'E-;5&���g�p���$	*�����C�5D��f���x2)���7���g�26�>�z�W�0�n���-.Y._X���{�kYn��>�N}��)~��������h}����u3 ���8*�1aCA����S@�����,/i��_���/O�������'��. (�_��U�����xC��q�^��������F��w�kG�Fa���w� A�L<x�uZ� %���<%+��q9}}�y�����!���v���j�����Z��;i@����D�l��,+r����C�x�b������N�n �^�Q�����K$�����,^�I��P9|U-� ���.��,��T��+j��X����	�����J��6j�$����0o(?8=}u����������+P�BS�m�����������^-��l0b�H�)�6~�:H�M3�l`
>O���!��'�b����x�T�gN�er���DIR��:���o�	^"���1B'�tG�������Y2A�+�p�NC�nj"���P�WG����?o�����wO1��l�5�
��5���Q�.(y�'����q����Wn^o|�T��c^9��i�2QN���K�[A)$����G�
F)Q��C"�4����0w����1������kc�D���������������C�#|�l}ww���9MmD�j�P<�N8��aBZ�	eu:z�6L���r���2yk�L�F��������������A���a@�p�A�=$�d��[��Rh������(>�*��kN��(
L�(�+�yn�#��x<����|�)E�������	��:��7��m|�>&��/@�c�v���;�[�a��TRc"a�9g����3����P��)�^���U����
6�"�� ����;i=Ld���9;��k�������e�$e{.�tC�^N��!��H����Jm6��
8�4&� Y����0>��N��8#�
0E'��C
��7f1�h2��0���Z�Pc"�#o
�j��z�P��2��
#��.��j@W�(V��.(������7)f�L'7>Jc> ��@�+��a��l����N_��M�*�6��@��'j�MF6��	�=ra/	�E�H�����y��x��Z]�%m�*�2V\O�H��}Z�*U���^����u=����(e��S�p�������Oe���JU��x���&��g��-��a������Gj�4��b@D^t��
�3_Z��=���:!8Ux��G��,�K�����2Ei��ZvN��xR�M���f�}E<�=#����Kf��id�W�{�q�a���
��(�	�VVfl���E�iMF����&�������f�vb5l��TVp)��++{:u�N��-�ajDp^9������K�/�X�frq�<xV�W�]]�s���b'(�N�E�{�C�@F'�7��:i<��qHh�����hz���Y����������]��O}�r����W�W2*��5:wp�E��x3Y�x�=2�'p�E9k[QC��b/������tn&�g��
,���h(1!�0N
m*2��Y``��g��i�N|��b��d$N�����`W"�������)9xb+xu�e5��N�"9��wX����P �	��4�&B��t�����kH�2�W����i�l��(�I�]�*��+�f�������i@yq|�|�:nv��j�Y�t���0���U�A�6X��a*f�$���)n'f���:���5�{j7�!-��z}q@��{^p�n)l�p��t$���!	���G_"X�L9-8�������l:��.��qH8��]���� Z��F�4���K8����x����{��t1S��X�.��2V#>���,���������l�BdD��k����6a�x�/BBSU�\
Q�
�Hx���]���B��L�����"	,��]���n�O^�@�����
'b�=N2�����I��%�^K��0}$� �|G�)�2p��R�#�*)X�n&&�Y���rm����9���~�h|�����h�a=�eK=�fx<�M�1G�L���|m�aV��3#�c�����X ����f�fb�F����MH�K�+�EZC�n����T�X�<�5��1
�R�)|���B��]aF����_�3T@N���{������J���S���	JI��V�VV����
���N�=T���k>���d7�]g8��`d����rk�c����Nw:IQd�L*��8���f�P_�S��e3����;�~|9��t�����B���t��P����������B=_y;�X_�`L"����L��cQ��PBe���%��N�[V�o�����d�\V�c�����`�4��t�}��r�Nw|z���G���K��%WI<�����fO|�8l}�jb*I�����!�`'I���c�L|/���B-��4"(V��u2�����1q�>�� Im�]$�����R@s�x�����`.0T�Z��X|���0���'E.aBR+�3U��({+UKB��D���E�t�l@��:�����s	A�M�������bafj����#�H������gT$�K����z���dE��}~A6dr�6�r��^�|��g���i�H��/d�4i*����@oG,�:����Rt���f��Q�=�8���!�����H;�Q&�~].�����W#up�>?y�9!����P�d�7L�QeH�L��L��Q�0��(�-,ke<Q���Q�}�i�^�G�y��������u�`��)1O�y�(lKR&p��c������:@�u�.���B�u2@�6,��b��d
�� M�NG8ks$b5F�U�����D��H0��0U{��zj����0����qP��=�sVyMb�n�R���J�������e5���2���H������m�:y��{n��u{�P��O~�:0�=�h,���~�����&J$�#����)���K$����[U����

���1��2���Z�
��!<��c����B�*�0f[�-3�����l}5�
��{���6��������qa�����?�f��q}*@��F�����&��W�K{�)?v�1������_�h��&��������Z�NXP�Q1~}��}y���H	�e����f�P��s��S������������(�3G�����0��Dt|Y��A����A���d"�+�z��|a�����*9�u~Yx^6���;cy������
5�x������j�@g��h��]�2�U?��@�`�����a-������ss^%��'���������>���Q�	���X����"T�g�L{������C�,ZJVz�Q�v�M�[��b�O��S}"�-Q�\�!,���zQ�����H���-�(E�[CQ� ��,v��LNgZ;-�Sa]f:��~�?����G�v���8�7,���pVW&XS�$��+]���q:!d���-�bp����1�/�c� %�	j
�KA�w`N
��lG����������X�-W��}=����![VX;�'�r�7V��-�z>�-d����m�������0�i�
h��Wz����:W����r����x����4H�H������G���G�9}��G0R�!,Rf���,����]L�J�	bn���8��!F�1n�}�B]�:f:h����"������u����b�^����**�r��7��h�P���N�����_
�LH���)�����`����"�___[Cm��i�C��^������	���~����`�q�6�Q����!��ZR�EXpj�8�v��c
YS�0���Z{	�F"������&�O��NN�,��t�>�������"6�'G��u���Y�?~=�Ne[
X�mI�@�i�I������c���!`�&���Gb��07�%�(���@U�~��J7"�����M�b���6r������V�Y��+���M��10^���(���ww�����
7����<l,���d�$�d������
����a%o���;����cLO�>H77��	�	9��*�
�Z����R2�q�1S�nh�E�����f<���D01e��NKUe)����6&��qkC��C���2��l������8��e��I<��[O�9��j�\�x��'l:�,v������c6d����!c	*��Y��]6�;��?j�j5�:���>�P5�G_1��r�(#gO�m��W9
����A���������]��d!����}��2P{���20�L��0��;�q<������&�`�l�	6��� 7W����K��_r�C�m�/6g�5��R�����f�&2�U����Q�!��:�a�`����9�xA��D�(!��QU	��
�'6|y�����S�9q�a2I`���Cpj���Z�1���F�<>��}��/Dsgw7��]��Oi��,i!�j�(�C���������X?Sb������T�����E�@�P��|���������8��M+��<��^���n��#�%���x+�Q|R����2�v�+:�p{g���	�M�O���8#���5�Y��+i@C!98�L2�V���f���M��������sZ����h{g{�M������p����Ca}�J_�<���N^#EyU���D~������;��#.;L����E��<��0��
��P��<{�-	�D�N�x9�I0&�0F{��J�.�g��n@�"E	���S
�Q����M���5���'b�Kb�2������E�_�#2��N&�8��0�BA�U�`d,����0lB5Z���l0��J=��:�OF�x�}^��=\�	����c��e�2�i� K&��L+�������D}R�D�0��[�=��e��~L~����u2�6O���������)�<�����������3>a���z��(D�1jWB,b{�(��F������PS3�)����
5�`��|J�C�.��.g�g�cd���$�_�p��sN�yj_��{���MoG$��[�����;��z�~��5�Gz��F(=�:P|��|�o���r�����UJ��u<���Y�����2��d�����k��H��.d���7~�����kz�4l���<o
o��dx��C������s�����k&���������g��r�����;�Z�����~�7���s��JfEXme����=��c@��sX8���$2z����X�z}#�7�����N�X����]��#�1�����7d��e}�[%�i����
�����G��xa������2��t��Hf{�+L=���|������Si��CAmGu'��>�(�i����e<�*������;G�		�	���o����g���M���MD��"�����W��`�, eJ;��Ip������=���o������cj���
r-��Fp(f�m�������Q�:>������/�Y�`��i���V�X������;��LG��n��u�����}���6��^r]�}�J0*�M��
t�566�j��Hx��g:��N����]v�!l��dF��n!]���qo�m*vk��w���r��Xgg4�dhD�9@�'>F����*Z�y�Z�e$R�M�#�;����SD�����	�������3���w���&i��{��EQ&�S��z����q2O�_wp�����@|����V_[sHv-����q���D;����*�?��b��2�Jr�$���y%B�2�,&Fl*�i�e�{���������C��%2]�������������\��d���^R|��,��j�{l����21b}w��0F���r�y���4�D�~	H���Cbc�e�
�W��rvIX�|�W�AZ�r�
��#vu�qL��2���@�m���}�����_���������PW�	���������OU�����W���j��u�����_	�A'-P�-a�.�K����D���t��Q
ww��U�&�dX`>�����������s~yx��0�������NR�w P����������.il~;N�$�BJ���<�5X����0x�d���lNR0��w�����.�h|x�W�����X��}2N���C����wm�%7��s��,*����Cnxj�#��>H���hccc��$=��i�&gC�O�����o1F����m�7m/��
��+�d�"�����o�q:(�b��Q��
��P1?�����RC����h^}$�'�r������c���lV9I����i���O9,
�A���;�.O��!�����u����� �N�����+@����h�^#$2�J<R����#�/,�").W�(��Re��?���v�lz���p�zVBN�	Gl��`�S����3iF���0��t�?i�fk����Q���j��0��k��k��x�<At�jr����x��0W����gM�=��s��tx�+�����!&~mb��"3������M�x�G�Lb�+���h���K�.�	�@1�q����z����;��r���_�2��5.������8�%�TL����D���F�V�Q�B�Yahd\��g�y�*^����Qx��
zT���kv�4���y6o�}��'�c�d#�Q���K'���k�i���X9�?G�S�a����S�JD�P��1Xy�l_��7���+��=����5�p�{}]���Tob6*0Px�D���g'������������u�Z������#~_�}FX��eVb�T�O�8�
i�^�@�Bh���^	��Gsy,FrN4��1�%�2��zT���e0���������0z*H.ob��MI�H&�V����a^<\�a������z��4)i4SV��;V����o�����r�C�x:��^<��XU\���[��y��4t31H�ax3r�%�)!K���)Fz��O5����	mnmN�v��7�����1��a��	rb��2�u��&�P�~�T�c�jk.nKl	2$K���W���B�B�[7�)�'�������w��e�h4N���s:��V��x0�J������ZaP��';�9V�[���#B[��:I;���m��a�b�8��">��ty;�Y}�.*�U�Hn�+O)���������y�m3���U�-�S�����n���������S����'&5������s3���
��]��F2M'��!����A�$L2W��� ���q<��{MX��A�(30+
��������e�"�l��jo�"5S�L����^%r�G*�B������L^�b���N�+������_�G�6�m�����&"}���U��,r�qM�]���Q��93/.7��/�p�C����@����Vs����0%���[�����=
/@���V�4s�!^�g���hzI�M"�>L���U�������eIG����SS�n9�����`����iM��3v��</���z��60l����=�R�"�>9��F�)�����S���	9hsp���/C��k�+`���F=Z�o�~��Wr�{�1D��!�N>���?��/�A��5r�����_uo�A��|���:���UQ*��m��j\{`M�������M��a
��X�tU���f#����������*h/��O��_��}���w��V>km�
�GY��\@�<Y=x����gd�a&um��-����7�`��-��K��S�	x����`��u�N�&�3uum�\������
Eh[�yx;���&z��`��&�Q�����Z#�,@��
�����������acEM��6�b�Nl��I�>�Q
��ob�$K�aNe����\�������E	]wf3�>��k����l}s}{!t���/����+']����}�sp*_9h��G3�uOX�|%$\��>���������_�������/�#�������~�cD5������:E%!����=6�����;�C����h���H�	��!�cL%��f<��lp����� 6�Q|5Q����6U���VMG�i��6������U��-)3N���`��[9��<�2C����4�pH8���V�c ��&3�q~����J����l@�����^M����E�	M�'����eH�
�#����4y��$�X*�)7�-=�R�f�f�x5N���Py����~�B+=��yJ%I�I�N��x�#��j�6}>J�~�JxDa�����*.	#���6���}_���'n�l�7I%,@���\�a�W���y�_O��l#(�^����5�����>�lR���T����U����[��������������R��k��������W��ru���������Ct����y	&=� ���Nk�'4��+��������x?�6y�'�?�:)#v��Wp�#���[���������,n�y�<�K����n���y|������&*Z������������v���9��i�A$��w���a�����
C�`z�# �,�c�T��m~=�d�b��4rS�'���������2+�����z�������B���Czji�J��i&��h0O�I�7�1$�UIE���;�e��EfIxa	�%|��5<���K0u�sV9
Jp�\L����w=A$��������<���"(��'jn|�t����p4�s
L&�1L�?���?I�N��[k�fc�2
p>9v�[���5g5j��(�qOn�~	���@QF"�	$F������&�c�n�lE�����A��;�b�X�<�\�F�L���Cx����a1��o��|����m9��E�l�`����@�5��:#Z��?�Zv�og�2a9�lT�?vGk%{J[U�ok�lcMqgok�����op����p�/al���	�����9������3�����6��w�|�P`��������
V��`��Z���3r/�hN�,2�oG��$��1.-���P0���L�6w�*|������P�����%������j7Z?	mv`����UxR�6���C���z�c=�\��a�����������e�.�6�>�=��1�~*;�_q��^��p�&��M����������hkm{�Kw�,���������Z�����Y��O�+�6��N���� ���:���
��\��/>|���.�;y��g�$p��S�KP��������f�U���E��Zq�Dr���������r�?��,��?�s���K3r���c���0"�c\WGx������������g����F��p4>���t�k�0g��l��.������%1ds�V:+��u~�2��I<�*R����p�x|i�z������$+8��q-{���Up���Ap���v}^�Bcn����hK��W�7�!����k�y�(E> m;�������X��F�����>E.�_2t~*��n��`�vw���8g*\���s�����pHjx1GL%`O�	6 �&|���q�N(����xF��y����[�����_���<���E\���� �Gq�������0ip��g9|�,~�g���?Y>P�.?��$����[[�����5?0��f4���0�`d���%��������,bF���Y�%iy�)|��h�w1K��"e�rw�rZ�����f�t�������,i�h.;�iT9��nsk=�_;�`�v�avwvJ��y��s�Qx�L�*��V6���%����9����$�3���|`�a�x���S���`y����K�^��&#�#��p@q�I���t+q�)�,��h?��N�-��/����pX�sq�b� Iv���I�_��a���s[a?��NN�r�M�GA�u3��g����W_QV��u��_��lcs8�����`���}JE��q��t�+�+M����e���������
�:�g���E�-\/��k97H������?��x��fY��h�Z�����
�j<�6��&�;�����p��.[��'���f��;��$�%���?�O�4�.|5�7�C�:m�C���`�5�tN���9�Fo��yg'�gl$:�k������h���8��,�<7"A]!��f�W?`0����\�s��$�"����8��8IaQ��z�$R2�N��'��W[�eGN��E��K�����yt$O��U�Q}};8_��t8m��zT����Q�|sxp����R�L=�������gf�~�]j��>?���?������k���>tS@��n:2b��������
��_�vp`A!H]�D�s9!*n��J��	���_�������Nc8:��m���a�I�G��-���
n�1�>8Z��U=���=7(n�T��Ds3�9Um���`�NY��b������Fr�f�](78Z�s������O��0Bt*��tTY�6�x���B3������I��>�8��� 8G�u~�z���N��E�������Fl~c��`�am�e�,�����s�a�����^��]��Z}'��5@������8G���3�f�M�rz��>P.yF�kp��k�kA>��lR6�{g8��P_������&JU0�G_rT�m9���w�($ �G���D�,V�d`J�}�]��~xT�3���PP�a�x���a���hC��^w�|�=��[I������o��)M�CZZ���+�l���N�gMW��L{��Rf��p0��;A���&�39��}o}NC/+��GlJ���_�:o;�9>��X'�	�����:o�Xs{}3jlo��y�85���L�2M�$�'��-8f�:��x�ix�0�?���\��P��9i�
[������.0�"3�V�{eI�	���R���D�������P�g��w�x��#h�j���dB~_��~Z�&�	���5M81�q�g������;Nau$�u���*0���l������HC3�^9�%w{S
��������%h*�{��:�����0C��e'q2�	GiG��50��b��5�p�����N�*��Hq}t��H�c=`�p��k:����"3d2V��Y�+$��a��J����q0<�o��J+��z�$�TS�O�_N�S�j7�;��$�RR��2�^~>{�-%��a����m��hwK�A/�>v,�|D�">x$}E�6}��J�'.$�0�p�Z�������73qh�E���6w=��������|�%��F����{��O�k��H���[�p+<64l08��*���F��K�o��n�hH ����"�^���sp#�{�=�l#x>���j��lWB���=�J��S�����������������xq^$��\S?1�����Gh$��8E�BeJ~��zW:�wI|�G�Z��{�j������t�?�
F�����i~C������iM�K�K�.'�V�M	~���?Th��G,�I�$��S�����:�Y����`����I��H��C�r8��w�g���a��y|�<>�{�u���P����fB5E�����l���@�1�0��> G�*�l�P��� B�^�����)����{>V=�4�
��!h��\��|u�d��/<�/�P)X���9�� ��h�AL�Yr���o�2M�����X���1%��1��7W�X��%��z��rHj��x;��5�^A���I����:��O2#���<}oc��
M�����d��1iM3N�
�_��&D$��<�5U�;x@�a^
��(DN�2�]���8�/�����8}R�_�����:�9�f_�0,^3��a�������cGE)�l������2���,gV���_wN�Z�[��GKk�����,��%�	��
��7r|r���6���F�.���[j���
��A!���B�)��Z����dv
������8��O<%��wN��K��!�U����p��TyE'_��c�lO��E��D��4d�vO�H�v����1�V/1���D�����u�}_�����*Y5������;Q}c=pO�H�b����1�:98�o�;�N������v����Y��5�ljXd&U.�l����i�����TH����+~�|�p���lpH^����,��V�<��g�m���[�ki�����z�T��\k#�5�tcqw����fQj?�{���ad����v\���9�c�5�6��
g��w�q;�s=@��S�_e�����Bx��]65C%76���<��e����cEy�P������ 8J�
,��lO���/�[��Qc���� �P�7��ul�����A�T�e�ACW-��5��=��	�����5���W'��St�9�r;�zl�����{o�l�}+���*�� z�J�/���Bo��u����C0�(���cRf�8���������������
��46���P��Df��N��vvG���&���f������&\����UZ/;�A��L0Z�f=�>>�H�����f�y�M
Q�@��,�#�a+�,�g\]�0�Fu��u^NPu�Y������0R2���������G@��KP<Br.x�����q�;h����78��i�����$vw��X�����YK=#'���Ps,��?
2�����=m�C�����:g0��
0���#���������b)�3�u�'�V�$/l���\��bF�//ZG�8��A����{�r������Y��W}�%l�i�|�G�����Y^W
Ch�eSx�^:oK�N����?q����0���
�����'���Z%���8n��H���@���0�4��_���h^S�:����G�z1LH��<9�="h�t��-�5����9l�,
v�]�Mj��@
�����I]
�0�(P
��h�L�I����C��J�99�V���?��#�����OW��-����9H�TI2�/���E�Y�VE*����w�-d����he��R''��{��6�-���i�����+�J�
[<�X����G�����`�K�le�p	!"8��%i��	�{i����:���UY�����"���#l�N���}����K�7o���W��<d�t�'���������t��R	�q7$<'��mI������{��z\��I�����1+tS�|�����qw�1���D��;E��k�Jtt�\}^9������.dp�3�@.;E4U>l�(2�K�uDW��D@���/#����:xv�����+0�t|�[�2��fR��L8c��V��Z������2�1��l���A�;c�!w�'$kll5���6NUwD��t���}
���r�"��_SW�������-��,��Sh9\��A=
�,�5
�8;�����������	�W�j�����mlFh�R�9�k�7,�D����`Od�B�"]2����2���G�[EdX������[v��y�%%&�v�[�6\�
�V��]�u\�M\��Ok�wV7��2�" �k��d��<�/F__[s'0����:g����6�:G(L�*�	\J:��1�-��A�������V�;|jv��s9���e3Ld'��)�A,0���:LE10w"������Y OO�00�w8"�w)_��&��7��z����7�+At_8���Q���VY�ml�D��h
'��A�3Z`C
��OnUON���|}�<l����M������7��^��N�����Bjmm����:�5����m�rL�r4how�ao�wc}���%�~z�1=,�:vb��^��)o���]�f��1�L<\�����@��D���\��@����.1���32F�����^�}FK�����'��PK�6�����������[{��w�5�`}���M���KDK�7~�����v����ZxM�u�.v�eE�4k)�K�F	�|� �FZ����+t����B�u^��E����c-��&g�t�0S���.�)�f�-N��u����N���^�Z$����A_���S�L��������e:@i�""��RV
tN�g�YF:$�buw��=�~|�������9��X~�uQ��2M'�d��cu�G[a[�q<8&�^��P{7��:?�o����;����U�e����`�\A���	�*,������}k�M�6����g����"4����p�/
������R��<Y���.iNk�H��C�wE�9S���&��isv��6���Y���0��5������x��#[����(ENhO�EJX���
�5]�`t.%�BS@P�a��5����w���A���{4���p<e��*D"���T2���R�N\���J�W��kU�����]]`����6$N�-���y���@�?A����N��D�L�]:~��=o���������}����R�{�ssw	�L�d�������d��=�`������������z��`O���_��K�z�e�����8����,��
��d�RdqG��=���}�K�g�x���xr,� ��l&>����DZ�4
O�9��l�)���n��w�e%��6�s����:�j,��;?!����s3r
z�b�=o���������7�N�?�:mO���|��4�j���+���M��;����>���2���y0n �Y]}\e 9
�u�Nmg%(��i�F�N����TJ�^���p���<���g���"W��[�Y�x��(<`�0�W=�h~?�u�Z�����i'2��6=�����h�B��m|�����F�5�����o�Z�?���M��'v���C'}����A6L6f��:�6}g������Eml��Nw�Bw�K������>����G��[�c�u��f
��	�;��pq�a�f\����`G�kjc������v�h�X�v1� p�����_����3u��n���fS�{�*t�e����sK���W(���������������8\���>y���X��c��G�S�.��o7�g'o�[��F����#_��}����7�l���xc���p������6v�����
���0�5�XZbxx����jc��mE��"?�"��$�v6�*��C�@~<���P�a����1�� ���R%�!��a\�x<���/I	:}����K���q���z�ia{]�1���4jB��]n%$;�
�IxAbp�S���|�+}.9����wHbOn�D�7�XE�������89;D�4���W���.������8>k��9�E%R	�c$�~K�:�v��%��+��3��j��#uprD�C�+�p����"�`/��J��4R���"�c�)�������]L����������������v�s�bE�`6��'@{��P$(!�hD�����;�N�0f��F""��ig��u[�������t��
t+������5Tv�P��w�LO��x
	�d|B����p-n�,�h�<��c�[����&����?o�����):�v��mp�(����t\2w�w�X��H8Kj�����l��������WC�	�>E����q��������+F���v�;��l������x�Y�K������x��N�����}i�������WD��y�\���]Q"��a|D��6�k���=~�wF�i�:#�?��i�X8��w>l6>���7��n`���TF����F�Es�*������45�����|�j���=8�@���G����ZGM��g��[�
��{1KFf(|
���d��Hn��@�S���.nf(ajU�i��A���`���hw��XrDVxg;��>��[�����+�������:y��0�m��~��� $�vI�%�tH �%U^U�p��T�~��\�����\���p��n�p�|��-�����W�����D��W]�mP�4�N�������}��+4Bcr
��{7�
������y���7vsd!�������a�sOn�~E�BR&�V���$R��=���2�c)>���I�:'�����OH!����t���#���C��CI����%�L��,�"P���3,�3��$�<����+�$��~:y#�C������g'���%=rC���o�A�6=�b��a<@���>a"����� �P0�	�G>]��j�otR|D&�hG8����S���~�H��@���z�\}]q
U������f���`����>���Xv���o�����y�>�e��WW�{,z��x����j_��^5��*3<R~��L�q�TF1j\���{��`}�8��SN��6+�PM� �0��g������x�����2IG����=ozj�
��������p���P��	��<TZ�v3�0�EU"^���t���!�-���*������E��������U?�X���T-]v�|����(�5�����s��ax)P���#����=h
A��8�/�3a�Yw{WC��
2��O:cj����f��u�z����D8�(�������+��:���c���RK�[���������4���������G����?;���!L�����m�"�n�z�:^�8�i�]j���g.f8��N�X��F�s�L���G��a	������.�4-�Z�J�����(��V�%�f���cu��t<7���#=j�	��hc����,�H��d��;�?�����_���y����zE}��z���s��r$����3���	��Q="��J���.���P�3��Ye�D^y;"%��*1�j�!`�C9�6'/��H�%���2�bB����>����;�F��
V��.��N��
#83Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Dimitri Fontaine (#82)
Re: Extensions, this time with a patch

Dimitri Fontaine <dimitri@2ndQuadrant.fr> writes:

I lean toward adding support for a script variable into the control file
which defaults to script = '${name}.sql' and will have to be edited only
in those 3 cases you're reporting about. I'm going to work on that this
morning, it looks simple enough to get reworked if necessary.

(yes it means we have to scan all control files to find the one where
the name property is the one given in the CREATE EXTENSION command,
but the code already exists --- it still has to be refactored)

That's done now and for those paying attention, of course those examples
won't need to add a script property in their control files, as soon as
we both scan the SHAREDIR to find the proper control file and that the
default script is ${name}.sql, which is what's used everywhere in our
contribs.

New patch to follow later, including the other modifications on the
table (error message, script file encoding, etc).

Note that the control files being parsed to check their name property
against the extension name that's been asked by the user, I think that
means they have to be in a fixed known encoding.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#84Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Dimitri Fontaine (#83)
Re: Extensions, this time with a patch

Dimitri Fontaine <dimitri@2ndQuadrant.fr> writes:

That's done now and for those paying attention, of course those examples
won't need to add a script property in their control files, as soon as
we both scan the SHAREDIR to find the proper control file and that the
default script is ${name}.sql, which is what's used everywhere in our
contribs.

Ok, that's confusing. The ${name} above is the name sans extension of
the control file (`basename foo.control .control`), not the name of the
extension. That's why it works without having to override the script
property in the contribs control files: _int.control and _int.sql are
the files for the extension named 'intarray'.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#85Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Itagaki Takahiro (#81)
1 attachment(s)
Re: Extensions, this time with a patch

Itagaki Takahiro <itagaki.takahiro@gmail.com> writes:

Here are detailed report for v9 patch.

And here's v10 patch, to fix reported problems.

* Typo in doc/xfunc.sgml. They are to be "replaceable" probably.
openjade:xfunc.sgml:2510:32:E: element "REPLACABLE" undefined
openjade:xfunc.sgml:2523:46:E: element "REPLACABLE" undefined

Fixed.

* There are some inconsistency between extension names in \dx+ view
and actual name used by CREATE EXTENSION.
- auto_username => insert_username
- intarray => _int
- xml2 => pgxml
We might need to rename them, or add 'installer'/'uninstaller' entries
into control files to support different extension names from .so name.

Fixed by having the CREATE EXTENSION command consider it's being given
the name of the extension as found in the control file, rather than the
control file base name.

* pg_execute_from_file() and encoding
It expects the file is in server encoding, but it is not always true
because we support multiple encodings in the same installation.
How about adding encoding parameter to the function?
=> pg_execute_from_file( file, encoding )
CREATE EXTENSION could have optional ENCODING option.
=> CREATE EXTENSION name [ ENCODING 'which' ]

So after adding support for this option in the command, I realized it
might be useless. What I've done is implemented an 'encoding' parameter
in the control file, so that the extension's author / maintainer are
able to set that from there.

As I began by implementing the syntax, I didn't remove it yet, and maybe
there's a use case for it. Some strange encoding setting might require
the DBA to override what the author thinks the script encoding is?

The implementation of the encoding parameter is that the command takes
precedence over the control file, and the given encoding will be used in
a SetClientEncoding() call surrounding the call to pg_execute_from_file.

Now, I didn't change anything in this SQL callable function on the
grounds that when using it directly, you can always SET client_encoding
TO whatever you need. That's much less practical when executing an
extension's script because you're not in a position to know what is the
right value (well there's the encoding parameter set in the control file
now).

I didn't (yet) specified any encoding for the contrib control files, as
I'm not sure lots of thoughts have been given to them. Should I set
SQL_ASCII, following what file(1) tells me, or do we aim for another
encoding here, or is it useless (as I think) to set SQL_ASCII when the
default is the database encoding?

As a side effect, juggling with the syntax here allowed me to support
the full WITH [ NO ] USER DATA form that I had in mind before to find
opt_with_data ready to use. So pg_dump output had to change too.

* Error messages in pg_execute_from_file()
Many messages in extension.c are also to be adjusted.

I've been edited some of them, the ones that I didn't paste from
existing places, but I'm yet to stop by the error style guide...

commands/extension.c needs to be cleaned up a bit more:
* fsize in read_extension_control_file() is not used.
* ferror() test just after AllocateFile() is not needed;
NULL checking is enough.

Cleaning done in v10, attached.

* malloc() in add_extension_custom_variable_classes().
I think the README says nothing about malloc() except assign_hook
cases; palloc would be better here.

Not sure about that, as I said, because all guc.c memory allocation is
done with malloc() rather than palloc().

BTW, did you register your patch to the next commitfest?
It would be better to do so for tracking the activities.
https://commitfest.postgresql.org/action/commitfest_view?id=8

Will do when I get this email in the archives.

Thanks again, regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

Attachments:

extension.v10.patch.gzapplication/octet-streamDownload
#86Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Itagaki Takahiro (#81)
Re: Extensions, this time with a patch

Itagaki Takahiro <itagaki.takahiro@gmail.com> writes:

BTW, did you register your patch to the next commitfest?
It would be better to do so for tracking the activities.
https://commitfest.postgresql.org/action/commitfest_view?id=8

https://commitfest.postgresql.org/action/patch_view?id=404

--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#87Alvaro Herrera
alvherre@commandprompt.com
In reply to: Dimitri Fontaine (#85)
Re: Extensions, this time with a patch

Excerpts from Dimitri Fontaine's message of vie oct 22 10:43:58 -0300 2010:

Itagaki Takahiro <itagaki.takahiro@gmail.com> writes:

* There are some inconsistency between extension names in \dx+ view
and actual name used by CREATE EXTENSION.
- auto_username => insert_username
- intarray => _int
- xml2 => pgxml
We might need to rename them, or add 'installer'/'uninstaller' entries
into control files to support different extension names from .so name.

Fixed by having the CREATE EXTENSION command consider it's being given
the name of the extension as found in the control file, rather than the
control file base name.

Hang on. Did I miss something? Why doesn't the control file name equal
the extension name? I think the idea that you have to scan the whole
share directory and parse all control files to find the one you need is
a bit strange.

It seems more sensible to allow the variation to occur in the Makefile,
i.e. _int/Makefile should contain
EXTENSION=intarray

Was this discussed previously? If so, why was this idea rejected?

* pg_execute_from_file() and encoding
It expects the file is in server encoding, but it is not always true
because we support multiple encodings in the same installation.
How about adding encoding parameter to the function?
=> pg_execute_from_file( file, encoding )

This seems sensible ... at least as sensible as it is to allow COPY to
specify the encoding.

CREATE EXTENSION could have optional ENCODING option.
=> CREATE EXTENSION name [ ENCODING 'which' ]

This seems like putting the burden on the user on getting the command
right. That seems prone to failure.

So after adding support for this option in the command, I realized it
might be useless. What I've done is implemented an 'encoding' parameter
in the control file, so that the extension's author / maintainer are
able to set that from there.

That makes more sense.

As I began by implementing the syntax, I didn't remove it yet, and maybe
there's a use case for it. Some strange encoding setting might require
the DBA to override what the author thinks the script encoding is?

I doubt this is necessary. If the DBA needs to override it (why?), it's
possible to edit the file.

I didn't (yet) specified any encoding for the contrib control files, as
I'm not sure lots of thoughts have been given to them. Should I set
SQL_ASCII, following what file(1) tells me, or do we aim for another
encoding here, or is it useless (as I think) to set SQL_ASCII when the
default is the database encoding?

I think it is OK to have the control files be pure ASCII (this doesn't
mean they are in SQL_ASCII though). The problems will start when we
decide to allow translations for extension descriptions. But we can
leave that for another day.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#88Tom Lane
tgl@sss.pgh.pa.us
In reply to: Alvaro Herrera (#87)
Re: Extensions, this time with a patch

Alvaro Herrera <alvherre@commandprompt.com> writes:

Hang on. Did I miss something? Why doesn't the control file name equal
the extension name? I think the idea that you have to scan the whole
share directory and parse all control files to find the one you need is
a bit strange.

Yes. It's horrid for performance, and it's worse for understandability,
and there's no obvious benefit to set against those. Please let's make
the rule that the control file name equals the extension name.

It expects the file is in server encoding, but it is not always true
because we support multiple encodings in the same installation.
How about adding encoding parameter to the function?
=> pg_execute_from_file( file, encoding )

This seems sensible ... at least as sensible as it is to allow COPY to
specify the encoding.

But then you have to figure out what encoding it is, and you have to
know that before you start reading the file. I think a better idea
would be to do the same thing we did for text search config files:
mandate that these files have to be in utf8.

I think it is OK to have the control files be pure ASCII (this doesn't
mean they are in SQL_ASCII though). The problems will start when we
decide to allow translations for extension descriptions. But we can
leave that for another day.

Well, we can see the issue now, and anyway who's to say an extension
might not want to load up some non-ASCII data?

regards, tom lane

#89Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Alvaro Herrera (#87)
Re: Extensions, this time with a patch

Alvaro Herrera <alvherre@commandprompt.com> writes:

Fixed by having the CREATE EXTENSION command consider it's being given
the name of the extension as found in the control file, rather than the
control file base name.

Hang on. Did I miss something? Why doesn't the control file name equal
the extension name? I think the idea that you have to scan the whole
share directory and parse all control files to find the one you need is
a bit strange.

The default behavior is to have the control file name the same as the
extension's file name, so there's no directory scanning in this case. It
so happen that the existing "extensions" are not currently following the
rule, even when we limit ourselves to contrib, so I adjusted the code to
be able to be more friendly.

Performance wise, it only concerns the command CREATE EXTENSION and the
queries to the pg_extensions system view, that's used in \dx. Does not
seem that critical.

Now, if we want to do it the other way round and force extension name to
be the filename, we will have to live with all the restrictions that
filename imposes and that are not the same depending on the OS and the
filesystem, I think, and with systems where we have no way to know what
is the filesystem encoding. Am I wring in thinking that this might be a
problem?

If that's not a problem and we want to have the extension name into the
control file name, then I'll go remove the name property there.

It seems more sensible to allow the variation to occur in the Makefile,
i.e. _int/Makefile should contain
EXTENSION=intarray

Was this discussed previously? If so, why was this idea rejected?

It's already possible to do this. But the contrib uses _int.sql.in and
MODULE_big = _int and I didn't change that. I had the control file named
the same as the script file, but with the current implementation we can
change that (there's a script property in the control file).

* pg_execute_from_file() and encoding
It expects the file is in server encoding, but it is not always true
because we support multiple encodings in the same installation.
How about adding encoding parameter to the function?
=> pg_execute_from_file( file, encoding )

This seems sensible ... at least as sensible as it is to allow COPY to
specify the encoding.

Well why not, for convenience, but you can surround the call to this
function with SET client_encoding and get the same behaviour. Will add
the argument if required, though.

CREATE EXTENSION could have optional ENCODING option.
=> CREATE EXTENSION name [ ENCODING 'which' ]

This seems like putting the burden on the user on getting the command
right. That seems prone to failure.

Given that the control file now supports an encoding parameter, I don't
see what this is good for, but I might be missing something obvious for
people working with different encodings etc. Japan seems to be quite
special as far as encodings are concerned.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#90Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Tom Lane (#88)
Re: Extensions, this time with a patch

Tom Lane <tgl@sss.pgh.pa.us> writes:

Yes. It's horrid for performance, and it's worse for understandability,
and there's no obvious benefit to set against those. Please let's make
the rule that the control file name equals the extension name.

Performance… well if you say that CREATE EXTENSION performance prevails
on its flexibility, let's say that I didn't expect it. See also my
previous mail for details (directory scanning only happens for those
extensions having chosen not to name their control file sensibly) and
the filesystem/OS problems (are they real?).

But then you have to figure out what encoding it is, and you have to
know that before you start reading the file. I think a better idea
would be to do the same thing we did for text search config files:
mandate that these files have to be in utf8.

Agreed, if as I understand it you're talking about the control file
itself, which supports an 'encoding' parameter that applies to the
SQL script.

I think it is OK to have the control files be pure ASCII (this doesn't
mean they are in SQL_ASCII though). The problems will start when we
decide to allow translations for extension descriptions. But we can
leave that for another day.

Well, we can see the issue now, and anyway who's to say an extension
might not want to load up some non-ASCII data?

The case is covered with the 'script' and 'encoding' parameters present
in v10 I think. If we force the control file itself to be UTF8 then we
can allow translations embedded in the file, it seems.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#91Alvaro Herrera
alvherre@commandprompt.com
In reply to: Dimitri Fontaine (#89)
Re: Extensions, this time with a patch

Excerpts from Dimitri Fontaine's message of vie oct 22 12:25:07 -0300 2010:

Now, if we want to do it the other way round and force extension name to
be the filename, we will have to live with all the restrictions that
filename imposes and that are not the same depending on the OS and the
filesystem, I think, and with systems where we have no way to know what
is the filesystem encoding. Am I wring in thinking that this might be a
problem?

I don't see a problem limiting extension names to use only ASCII chars,
and a subset of those, at that. They're just names. If you want to get
fancy you can use the description.

If that's not a problem and we want to have the extension name into the
control file name, then I'll go remove the name property there.

It seems more sensible to allow the variation to occur in the Makefile,
i.e. _int/Makefile should contain
EXTENSION=intarray

Was this discussed previously? If so, why was this idea rejected?

It's already possible to do this. But the contrib uses _int.sql.in and
MODULE_big = _int and I didn't change that. I had the control file named
the same as the script file, but with the current implementation we can
change that (there's a script property in the control file).

I think it makes more sense to change the script name in the control
file. That way you never need to scan anything. As for this being
seldom used, consider the case where the user mistypes the extension name.
You will be scanning the directory without good reason.

* pg_execute_from_file() and encoding
It expects the file is in server encoding, but it is not always true
because we support multiple encodings in the same installation.
How about adding encoding parameter to the function?
=> pg_execute_from_file( file, encoding )

This seems sensible ... at least as sensible as it is to allow COPY to
specify the encoding.

Well why not, for convenience, but you can surround the call to this
function with SET client_encoding and get the same behaviour. Will add
the argument if required, though.

I don't think it is. In fact, it seems better to leave it out. CREATE
EXTENSION will have its own mechanism for specifying the encoding (which
it'll obtain from the control file), so I don't see the need.

CREATE EXTENSION could have optional ENCODING option.
=> CREATE EXTENSION name [ ENCODING 'which' ]

This seems like putting the burden on the user on getting the command
right. That seems prone to failure.

Given that the control file now supports an encoding parameter, I don't
see what this is good for, but I might be missing something obvious for
people working with different encodings etc. Japan seems to be quite
special as far as encodings are concerned.

Seems we're both arging the say way, but neither of us is Japanese ;-)

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#92Alvaro Herrera
alvherre@commandprompt.com
In reply to: Alvaro Herrera (#91)
Re: Extensions, this time with a patch

Excerpts from Alvaro Herrera's message of vie oct 22 13:17:41 -0300 2010:

Given that the control file now supports an encoding parameter, I don't
see what this is good for, but I might be missing something obvious for
people working with different encodings etc. Japan seems to be quite
special as far as encodings are concerned.

Seems we're both arging the say way, but neither of us is Japanese ;-)

Argh. "arguing the same way". Blame it on typing on a high-latency
link :-(

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#93Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Alvaro Herrera (#91)
Re: Extensions, this time with a patch

Alvaro Herrera <alvherre@commandprompt.com> writes:

Excerpts from Dimitri Fontaine's message of vie oct 22 12:25:07 -0300 2010:

Now, if we want to do it the other way round and force extension name to
be the filename, we will have to live with all the restrictions that
filename imposes and that are not the same depending on the OS and the
filesystem, I think, and with systems where we have no way to know what
is the filesystem encoding. Am I wring in thinking that this might be a
problem?

I don't see a problem limiting extension names to use only ASCII chars,
and a subset of those, at that. They're just names. If you want to get
fancy you can use the description.

So extension names are forced into English? I would live with that, I
just don't find the answer friendly to the users.

I'd think we can live with some directory scanning that only happens
when installing extensions, or checking available extensions. All of
which being superuser only.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#94Andrew Dunstan
andrew@dunslane.net
In reply to: Dimitri Fontaine (#93)
Re: Extensions, this time with a patch

On 10/22/2010 12:30 PM, Dimitri Fontaine wrote:

Alvaro Herrera<alvherre@commandprompt.com> writes:

Excerpts from Dimitri Fontaine's message of vie oct 22 12:25:07 -0300 2010:

Now, if we want to do it the other way round and force extension name to
be the filename, we will have to live with all the restrictions that
filename imposes and that are not the same depending on the OS and the
filesystem, I think, and with systems where we have no way to know what
is the filesystem encoding. Am I wring in thinking that this might be a
problem?

I don't see a problem limiting extension names to use only ASCII chars,
and a subset of those, at that. They're just names. If you want to get
fancy you can use the description.

So extension names are forced into English? I would live with that, I
just don't find the answer friendly to the users.

ASCII is not the same thing as English. You can create the names in Pig
Latin or Redneck if you want. It's the charset that's being restricted
here, and we restrict many more things than this to ASCII.

cheers

andrew

#95Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Andrew Dunstan (#94)
Re: Extensions, this time with a patch

Andrew Dunstan <andrew@dunslane.net> writes:

ASCII is not the same thing as English. You can create the names in Pig
Latin or Redneck if you want. It's the charset that's being restricted here,
and we restrict many more things than this to ASCII.

I'd like to read Itagaki's opinion here, will wait :)

--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#96Alvaro Herrera
alvherre@commandprompt.com
In reply to: Dimitri Fontaine (#93)
Re: Extensions, this time with a patch

Excerpts from Dimitri Fontaine's message of vie oct 22 13:30:22 -0300 2010:

So extension names are forced into English? I would live with that, I
just don't find the answer friendly to the users.

Well, things like pgfoundry project names are also restricted AFAICT and
I don't think anyone has a problem with that.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#97Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Alvaro Herrera (#96)
Re: Extensions, this time with a patch

Alvaro Herrera <alvherre@commandprompt.com> writes:

Well, things like pgfoundry project names are also restricted AFAICT and
I don't think anyone has a problem with that.

For things you publish, sure. But extensions will also handle in-house
developments of functions and other in-database API-like stuff, in fact
any SQL script you have that you don't want to maintain in the database
dumps is an extension.

So it's broader than just public Open Source projects.

Anyway, my point is that by default there's no directory scanning: the
lookup is first directed towards ${extension-name}.control, of
course. Only when this file does not exists or its name property is
different from the extension name do we get to scan the directory, and
we stop as soon as we find the .control file with the right name in it.

So I think it's a good compromise, and as it's superuser-only I would
think it's acceptable as-is. Apparently it's not, which ain't the end of
the world but an unexpected surprise for me. And when I don't
understand, I tend to insist until I do or until I resign, whichever
comes first, but you would know that by now :)

I'll go rework the patch sometime later to drop the name from the
control file, but that also means fixing several contrib modules by
changing their file names, operation for which the project has no policy
yet as far as I understand (we used CVS before).

For information, when we talk about performance problem, please note
that on my workstation with a default setup (not that it's important
here) we're talking about 86,420 ms for a loop of 100
perform * from pg_extensions;

That displays 36 extensions and needs to parse their files twice and for
some of them need to scan the directory and parse other extension
control files before to get to the right one. Average less than 1ms to
do all that on my workstation, and typically less than 3ms if you
include psql side of things.

Superuser only. To manage extensions, nothing to do with live load or
user queries. But if you say performance is important here and 3ms to
display all available extensions sucks, so be it.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#98Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Dimitri Fontaine (#97)
Re: Extensions, this time with a patch

Dimitri Fontaine <dimitri@2ndQuadrant.fr> writes:

For information, when we talk about performance problem, please note
that on my workstation with a default setup (not that it's important
here) we're talking about 86,420 ms for a loop of 100
perform * from pg_extensions;

That's right, but

That displays 36 extensions and needs to parse their files twice and for
some of them need to scan the directory and parse other extension
control files before to get to the right one. Average less than 1ms to
do all that on my workstation, and typically less than 3ms if you
include psql side of things.

That's not what happens, the pg_extensions() SRF will scan the directory
once and parse each control file once, of course. I'm tired enough to
mix the behaviour of finding the control file given *one* extension name
at CREATE EXTENSION time with listing all available extensions. Sorry
for the noise.

In my mind though, the baseline remains the same. Now I will have a
sleep and prepare for holidays, in some meaningful order…

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#99Alvaro Herrera
alvherre@commandprompt.com
In reply to: Dimitri Fontaine (#97)
Re: Extensions, this time with a patch

Excerpts from Dimitri Fontaine's message of vie oct 22 16:21:14 -0300 2010:

So I think it's a good compromise, and as it's superuser-only I would
think it's acceptable as-is. Apparently it's not, which ain't the end of
the world but an unexpected surprise for me. And when I don't
understand, I tend to insist until I do or until I resign, whichever
comes first, but you would know that by now :)

Yeah, well, this is just my personal opinion. Others are welcome to
chime in.

I'll go rework the patch sometime later to drop the name from the
control file, but that also means fixing several contrib modules by
changing their file names, operation for which the project has no policy
yet as far as I understand (we used CVS before).

Change what file names? You lost me there. I thought the extension
name was going to be equal to the control file name, and said control
file doesn't exist yet, so you don't need to rename any existant file.
Am I confusing something?

That said, I don't think there's any reason now to stop a committer from
renaming files (as long as this has been previously discussed and agreed
to, just like any other commit).

Superuser only. To manage extensions, nothing to do with live load or
user queries. But if you say performance is important here and 3ms to
display all available extensions sucks, so be it.

To be honest I'm not all that concerned with raw performance here. I
just think that scanning a directory is a strange way for the search to
behave.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#100Robert Haas
robertmhaas@gmail.com
In reply to: Alvaro Herrera (#99)
Re: Extensions, this time with a patch

On Fri, Oct 22, 2010 at 4:02 PM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

That said, I don't think there's any reason now to stop a committer from
renaming files (as long as this has been previously discussed and agreed
to, just like any other commit).

Yeah - what is the feasibility of cleaning up the things where there
are naming inconsistencies right now?

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#101Alvaro Herrera
alvherre@commandprompt.com
In reply to: Robert Haas (#100)
Re: Extensions, this time with a patch

Excerpts from Robert Haas's message of vie oct 22 17:07:10 -0300 2010:

On Fri, Oct 22, 2010 at 4:02 PM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

That said, I don't think there's any reason now to stop a committer from
renaming files (as long as this has been previously discussed and agreed
to, just like any other commit).

Yeah - what is the feasibility of cleaning up the things where there
are naming inconsistencies right now?

We're in Git-land now. Why should we worry unnecessarily about old
restrictions? It wasn't a policy, just a tool limitation.

Do you have concrete proposals? If so please start a new thread :-)

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#102Josh Berkus
josh@agliodbs.com
In reply to: Robert Haas (#100)
Re: Extensions, this time with a patch

Yeah - what is the feasibility of cleaning up the things where there
are naming inconsistencies right now?

Easy. Heck, the only reason we didn't do it 2 years ago was that we
were waiting for extensions before bothering.

Go Dimitri! Yaaay.

--
-- Josh Berkus
PostgreSQL Experts Inc.
http://www.pgexperts.com

#103Tom Lane
tgl@sss.pgh.pa.us
In reply to: Dimitri Fontaine (#97)
Re: Extensions, this time with a patch

Dimitri Fontaine <dimitri@2ndQuadrant.fr> writes:

Anyway, my point is that by default there's no directory scanning: the
lookup is first directed towards ${extension-name}.control, of
course. Only when this file does not exists or its name property is
different from the extension name do we get to scan the directory, and
we stop as soon as we find the .control file with the right name in it.

This seems (a) way overcomplicated, and (b) sorely in need of a
unique-index mechanism.

Please just use the filenames and have done. This is lily-gilding
at its finest. People who want to use non-ascii filenames are welcome
to do so, and deal with any ensuing fallout themselves.

regards, tom lane

#104Itagaki Takahiro
itagaki.takahiro@gmail.com
In reply to: Josh Berkus (#102)
Re: Extensions, this time with a patch

On Sat, Oct 23, 2010 at 5:26 AM, Josh Berkus <josh@agliodbs.com> wrote:

Yeah - what is the feasibility of cleaning up the things where there
are naming inconsistencies right now?

Easy.  Heck, the only reason we didn't do it 2 years ago was that we
were waiting for extensions before bothering.

We could rename the module name, directory, and documentation path,
but could not rename .so files because pg_restore would fail to restore
functions written in C because they contains previous name of .so files.

The issue will be solved by the EXTENSION patch, but the feature cannot
be used to upgrade to 9.1 from older versions, no?

--
Itagaki Takahiro

#105Tom Lane
tgl@sss.pgh.pa.us
In reply to: Itagaki Takahiro (#104)
Re: Extensions, this time with a patch

Itagaki Takahiro <itagaki.takahiro@gmail.com> writes:

On Sat, Oct 23, 2010 at 5:26 AM, Josh Berkus <josh@agliodbs.com> wrote:

Yeah - what is the feasibility of cleaning up the things where there
are naming inconsistencies right now?

Easy.  Heck, the only reason we didn't do it 2 years ago was that we
were waiting for extensions before bothering.

We could rename the module name, directory, and documentation path,
but could not rename .so files because pg_restore would fail to restore
functions written in C because they contains previous name of .so files.

The issue will be solved by the EXTENSION patch, but the feature cannot
be used to upgrade to 9.1 from older versions, no?

Hmm, there seems to be some confusion here as to whether we are talking
about "move the source code around" or "change user-visible module names".
I'm distinctly not for the latter, but I'm not sure if that's what Josh
meant.

regards, tom lane

#106Alvaro Herrera
alvherre@commandprompt.com
In reply to: Alvaro Herrera (#99)
Re: Extensions, this time with a patch

Excerpts from Alvaro Herrera's message of vie oct 22 17:02:22 -0300 2010:

Excerpts from Dimitri Fontaine's message of vie oct 22 16:21:14 -0300 2010:

I'll go rework the patch sometime later to drop the name from the
control file, but that also means fixing several contrib modules by
changing their file names, operation for which the project has no policy
yet as far as I understand (we used CVS before).

Change what file names? You lost me there. I thought the extension
name was going to be equal to the control file name, and said control
file doesn't exist yet, so you don't need to rename any existant file.
Am I confusing something?

Hmm, after reading the latest blog post, it seems that the patch
requires that the control file is equal to the .sql install script. Is
this the case? I don't see a reason for this requirement; why not
simply have a line for the install script in the control file? That
way, you don't need to rename the .sql files.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#107Alvaro Herrera
alvherre@commandprompt.com
In reply to: Alvaro Herrera (#106)
Re: Extensions, this time with a patch

Excerpts from Alvaro Herrera's message of lun oct 25 10:37:22 -0300 2010:

Excerpts from Alvaro Herrera's message of vie oct 22 17:02:22 -0300 2010:

I'll go rework the patch sometime later to drop the name from the
control file, but that also means fixing several contrib modules by
changing their file names, operation for which the project has no policy
yet as far as I understand (we used CVS before).

Change what file names? You lost me there. I thought the extension
name was going to be equal to the control file name, and said control
file doesn't exist yet, so you don't need to rename any existant file.
Am I confusing something?

Hmm, after reading the latest blog post, it seems that the patch
requires that the control file is equal to the .sql install script. Is
this the case? I don't see a reason for this requirement; why not
simply have a line for the install script in the control file? That
way, you don't need to rename the .sql files.

Ah, some reading of the patch reveals that the "script" defaults to the
control file name, but it can be overridden.

I noticed that you're using ExclusiveLock when creating an extension,
citing the handling of the global variable create_extension for this.
There are two problems here: one is that you're releasing the lock way
too early: if you wanted this to be effective, you'd need to hold on to
the lock until after you've registered the extension.

The other is that there is no need for this at all, because this backend
cannot be concurrently running another CREATE EXTENSION comand, and
this is a backend-local variable. So there's no point.

Why palloc create_extension every time? Isn't it better to initialize
it properly and have a boolean value telling whether it's to be used?
Also, if an extension fails partway through creation, the var will be
left set. I think you need a PG_TRY block to reset it.

(I find the repeated coding pattern that tests create_extension for
NULL-ness before calling recordDependencyOn a bit awkward; maybe hide it
in a function or macro? But then maybe that's just me. Also, why
palloc it? Seems better to have it static. Notice your new calls to
recordDependencyOn are the only ones with operands not using the &
operator.)

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#108Alvaro Herrera
alvherre@commandprompt.com
In reply to: Dimitri Fontaine (#98)
1 attachment(s)
Re: Extensions, this time with a patch

Excerpts from Dimitri Fontaine's message of vie oct 22 16:43:56 -0300 2010:

Dimitri Fontaine <dimitri@2ndQuadrant.fr> writes:

For information, when we talk about performance problem, please note
that on my workstation with a default setup (not that it's important
here) we're talking about 86,420 ms for a loop of 100
perform * from pg_extensions;

BTW it strikes me that it would be easier on the code that there were
just a couple of simple functions, one returning the list of installed
extensions and another one returning the list of installable
extensions. The rest of SRF functions needn't be implemented in C, you
could implement them in SQL instead by joining to pg_depend and whatnot.

Also, PFA a couple of minor fixes.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

Attachments:

0001-A-bunch-of-minor-fixes.patchapplication/octet-stream; name=0001-A-bunch-of-minor-fixes.patchDownload
From d0fdfe10f48cb81680e51641ff00b43f681a9bd2 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Mon, 25 Oct 2010 16:38:42 -0300
Subject: [PATCH] A bunch of minor fixes

---
 src/backend/commands/extension.c |   22 +++++++++++-----------
 src/include/commands/extension.h |   11 +++--------
 2 files changed, 14 insertions(+), 19 deletions(-)

diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 55ecc4f..64bc74f 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -58,6 +58,13 @@
 #include "utils/syscache.h"
 #include "utils/tqual.h"
 
+
+typedef struct extension_fctx
+{
+	directory_fctx dir;
+	ExtensionList *installed;
+} extension_fctx;
+
 /*
  * See commands/extension.h for details.
  */
@@ -640,7 +647,7 @@ CreateExtension(CreateExtensionStmt *stmt)
 		 "Installing extension '%s' from '%s', %s user data",
 		 stmt->extname,
 		 control->script,
-		 create_extension_with_user_data ? "with" : "whithout");
+		 create_extension_with_user_data ? "with" : "without");
 
 	/*
 	 * Force the min message settings here, because SPI will dump the
@@ -801,7 +808,7 @@ get_extension_oid(const char *extname, bool missing_ok)
 /*
  * get_extension_name - given an extension OID, look up the name
  *
- * Returns a palloc'd string, or NULL if no such tablespace.
+ * Returns a palloc'd string, or NULL if no such extension.
  */
 char *
 get_extension_name(Oid ext_oid)
@@ -841,10 +848,9 @@ get_extension_name(Oid ext_oid)
 /*
  * Drop extension by OID.  This is called to clean up dependencies.
  */
-char *
+void
 RemoveExtensionById(Oid extId)
 {
-	char	   *result;
 	Relation	rel;
 	HeapScanDesc scandesc;
 	HeapTuple	tuple;
@@ -866,16 +872,10 @@ RemoveExtensionById(Oid extId)
 
 	/* We assume that there can be at most one matching tuple */
 	if (HeapTupleIsValid(tuple))
-		result = pstrdup(NameStr(((Form_pg_extension) GETSTRUCT(tuple))->extname));
-	else
-		result = NULL;
-
-	simple_heap_delete(rel, &tuple->t_self);
+		simple_heap_delete(rel, &tuple->t_self);
 
 	heap_endscan(scandesc);
 	heap_close(rel, AccessShareLock);
-
-	return result;
 }
 
 /*
diff --git a/src/include/commands/extension.h b/src/include/commands/extension.h
index cdb7cdc..6220513 100644
--- a/src/include/commands/extension.h
+++ b/src/include/commands/extension.h
@@ -49,11 +49,6 @@ typedef struct ExtensionList
 	int	maxrefs;		/* current size of palloc'd array */
 } ExtensionList;
 
-typedef struct extension_fctx
-{
-	directory_fctx dir;
-	ExtensionList *installed;
-} extension_fctx;
 
 typedef struct
 {
@@ -65,9 +60,9 @@ extern void ExtensionSetCVC(void);
 extern void CreateExtension(CreateExtensionStmt *stmt);
 extern void DropExtension(DropExtensionStmt *stmt);
 
-Oid get_extension_oid(const char *extname, bool missing_ok);
-char * get_extension_name(Oid ext_oid);
-char * RemoveExtensionById(Oid extId);
+extern Oid get_extension_oid(const char *extname, bool missing_ok);
+extern char *get_extension_name(Oid ext_oid);
+extern void RemoveExtensionById(Oid extId);
 
 
 #endif   /* EXTENSION_H */
-- 
1.7.1

#109Dimitri Fontaine
dimitri@2ndquadrant.fr
In reply to: Alvaro Herrera (#107)
Re: Extensions, this time with a patch

Le 25 oct. 2010 à 17:26, Alvaro Herrera a écrit :

Ah, some reading of the patch reveals that the "script" defaults to the
control file name, but it can be overridden.

Yes, that's new in v10. In v11 I've kept that and removed the name property in the control file, so that we have:

cat contrib/intarray/intarray.control.in
# intarray
comment = 'one-dimensional arrays of integers: functions, operators, index support'
version = 'EXTVERSION'
script = '_int.sql'

I noticed that you're using ExclusiveLock when creating an extension,
citing the handling of the global variable create_extension for this.
There are two problems here: one is that you're releasing the lock way
too early: if you wanted this to be effective, you'd need to hold on to
the lock until after you've registered the extension.

The other is that there is no need for this at all, because this backend
cannot be concurrently running another CREATE EXTENSION comand, and
this is a backend-local variable. So there's no point.

I wanted to protect from another backend trying to create the same extension at the same time. So the critical path is the inserting into the catalog. I now see I failed to include the duplicate object check into the critical path, when I added that later.

Do you confirm protecting the insertion in the catalog is not worthy of special locking? To get proper locking requires some more thinking than I did put in, but if you say I'd better remove it...

Why palloc create_extension every time? Isn't it better to initialize
it properly and have a boolean value telling whether it's to be used?
Also, if an extension fails partway through creation, the var will be
left set. I think you need a PG_TRY block to reset it.

Good catches. I'm still uneasy with which memory context what allocation belongs too, hence the palloc()ing here.

(I find the repeated coding pattern that tests create_extension for
NULL-ness before calling recordDependencyOn a bit awkward; maybe hide it
in a function or macro? But then maybe that's just me. Also, why
palloc it? Seems better to have it static. Notice your new calls to
recordDependencyOn are the only ones with operands not using the &
operator.)

In fact the goal of the test is to check if we're in the code path for CREATE EXTENSION, rather than pointer validity per-say. I'll go have it static, too, with a bool to determine the code path.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

PS: I hope to be able to send this email, but uploading the git repo will be uneasy from the wifi here at best. Will send patches if email is ok.

#110Dimitri Fontaine
dimitri@2ndquadrant.fr
In reply to: Alvaro Herrera (#107)
Re: Extensions, this time with a patch

Le 25 oct. 2010 à 17:26, Alvaro Herrera a écrit :

Ah, some reading of the patch reveals that the "script" defaults to the
control file name, but it can be overridden.

Yes, that's new in v10. In v11 I've kept that and removed the name property in the control file, so that we have:

cat contrib/intarray/intarray.control.in
# intarray
comment = 'one-dimensional arrays of integers: functions, operators, index support'
version = 'EXTVERSION'
script = '_int.sql'

I noticed that you're using ExclusiveLock when creating an extension,
citing the handling of the global variable create_extension for this.
There are two problems here: one is that you're releasing the lock way
too early: if you wanted this to be effective, you'd need to hold on to
the lock until after you've registered the extension.

The other is that there is no need for this at all, because this backend
cannot be concurrently running another CREATE EXTENSION comand, and
this is a backend-local variable. So there's no point.

I wanted to protect from another backend trying to create the same extension at the same time. So the critical path is the inserting into the catalog. I now see I failed to include the duplicate object check into the critical path, when I added that later.

Do you confirm protecting the insertion in the catalog is not worthy of special locking? To get proper locking requires some more thinking than I did put in, but if you say I'd better remove it...

Why palloc create_extension every time? Isn't it better to initialize
it properly and have a boolean value telling whether it's to be used?
Also, if an extension fails partway through creation, the var will be
left set. I think you need a PG_TRY block to reset it.

Good catches. I'm still uneasy with which memory context what allocation belongs too, hence the palloc()ing here.

(I find the repeated coding pattern that tests create_extension for
NULL-ness before calling recordDependencyOn a bit awkward; maybe hide it
in a function or macro? But then maybe that's just me. Also, why
palloc it? Seems better to have it static. Notice your new calls to
recordDependencyOn are the only ones with operands not using the &
operator.)

In fact the goal of the test is to check if we're in the code path for CREATE EXTENSION, rather than pointer validity per-say. I'll go have it static, too, with a bool to determine the code path.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

PS: I hope to be able to send this email, but uploading the git repo will be uneasy from the wifi here at best. Will send patches if email is ok.

#111Dimitri Fontaine
dimitri@2ndquadrant.fr
In reply to: Alvaro Herrera (#107)
1 attachment(s)
Re: Extensions, this time with a patch

Hi,

Thank you for your detailed reviewing, including a patch! Attached is current version of the patch, v11, with your work and comments included. Unfortunately it looks like the git repository won't receive any update until I'm back home, next tuesday.

Le 25 oct. 2010 à 17:26, Alvaro Herrera a écrit :

Ah, some reading of the patch reveals that the "script" defaults to the
control file name, but it can be overridden.

Yes. The other mail seems to have made it finally :) twice :(

I noticed that you're using ExclusiveLock when creating an extension,
citing the handling of the global variable create_extension for this.
There are two problems here: one is that you're releasing the lock way
too early: if you wanted this to be effective, you'd need to hold on to
the lock until after you've registered the extension.

The other is that there is no need for this at all, because this backend
cannot be concurrently running another CREATE EXTENSION comand, and
this is a backend-local variable. So there's no point.

I'm now holding the lock just while inserting in the catalogs, and rechecking for duplicates while I have the lock.

Why palloc create_extension every time? Isn't it better to initialize
it properly and have a boolean value telling whether it's to be used?
Also, if an extension fails partway through creation, the var will be
left set. I think you need a PG_TRY block to reset it.

Done in v11. I wasn't exactly sure about what to have in the CATCH block, so I've put the minimum, reseting the bool.

(I find the repeated coding pattern that tests create_extension for
NULL-ness before calling recordDependencyOn a bit awkward; maybe hide it
in a function or macro? But then maybe that's just me. Also, why
palloc it? Seems better to have it static. Notice your new calls to
recordDependencyOn are the only ones with operands not using the &
operator.)

Updated to fit a better style, with bool create_extension set to true in the command only, and AddressObject extension that's used everywhere (&extension). No more palloc()ing here.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

Attachments:

extension.v11.patch.gzapplication/x-gzip; name=extension.v11.patch.gz; x-unix-mode=0644Download
#112Itagaki Takahiro
itagaki.takahiro@gmail.com
In reply to: Dimitri Fontaine (#111)
Re: Extensions, this time with a patch

On Wed, Oct 20, 2010 at 01:36, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

Ah yes, thinking it's an easy patch is not helping. Please find attached
a revised version of it.

I checked cfparser.v2.patch.

It exports the static parseRecoveryCommandFileLine() in xlog.c
as the global cfParseOneLine() in cfparser.c without modification.

It generates one warning, but it can be easily fixed.
cfparser.c:34: warning: no previous prototype for 'cfParseOneLine'

Some discussions about the patch:

* Is "cf" the best name for the prefix? Less abbreviated forms might
be less confusable. Personally, I prefer "conf".

* Can we export ParseConfigFile() in guc-file.l rather than
parseRecoveryCommandFileLine()? It can solve the issue that unquoted
parameter values in recovery.conf are not recognized. Even if we
won't merge them, just allowing unquoted values would be useful.

--
Itagaki Takahiro

#113Robert Haas
robertmhaas@gmail.com
In reply to: Itagaki Takahiro (#112)
Re: Extensions, this time with a patch

On Sun, Nov 21, 2010 at 8:10 PM, Itagaki Takahiro
<itagaki.takahiro@gmail.com> wrote:

On Wed, Oct 20, 2010 at 01:36, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

Ah yes, thinking it's an easy patch is not helping. Please find attached
a revised version of it.

I checked cfparser.v2.patch.

It exports the static parseRecoveryCommandFileLine() in xlog.c
as the global cfParseOneLine() in cfparser.c without modification.

It generates one warning, but it can be easily fixed.
 cfparser.c:34: warning: no previous prototype for 'cfParseOneLine'

Some discussions about the patch:

* Is "cf" the best name for the prefix? Less abbreviated forms might
 be less confusable. Personally, I prefer "conf".

* Can we export ParseConfigFile() in guc-file.l rather than
 parseRecoveryCommandFileLine()? It can solve the issue that unquoted
 parameter values in recovery.conf are not recognized. Even if we
 won't merge them, just allowing unquoted values would be useful.

I'd really like to see postgresql.conf and recovery.conf parsing
merged, and I suspect, as Itagaki-san says, that postgresql.conf
parsing is the better model for any new code.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#114Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Itagaki Takahiro (#112)
Re: Extensions, this time with a patch

Itagaki Takahiro <itagaki.takahiro@gmail.com> writes:

I checked cfparser.v2.patch.

Thanks for reviewing it!

It exports the static parseRecoveryCommandFileLine() in xlog.c
as the global cfParseOneLine() in cfparser.c without modification.

It generates one warning, but it can be easily fixed.
cfparser.c:34: warning: no previous prototype for 'cfParseOneLine'

Mmmm, that must have been a cherry-picking error on my side, it seems I
forgot to include this patch in the cfparser branch. Sorry about that.

http://git.postgresql.org/gitweb?p=postgresql-extension.git;a=commitdiff;h=e6b4c670a0189ee8a799521af06c1ee63f9e530e

Some discussions about the patch:

* Is "cf" the best name for the prefix? Less abbreviated forms might
be less confusable. Personally, I prefer "conf".

Will change accordingly if that's the choice.

* Can we export ParseConfigFile() in guc-file.l rather than
parseRecoveryCommandFileLine()? It can solve the issue that unquoted
parameter values in recovery.conf are not recognized. Even if we
won't merge them, just allowing unquoted values would be useful.

Should we then consider recovery.conf entries as ordinary GUCs? That
would allow to connect to a standby and issue 'show primary_conninfo'
there. This has been asked before, already.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#115Itagaki Takahiro
itagaki.takahiro@gmail.com
In reply to: Dimitri Fontaine (#114)
Re: Extensions, this time with a patch

On Mon, Nov 22, 2010 at 18:36, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

* Can we export ParseConfigFile() in guc-file.l rather than
  parseRecoveryCommandFileLine()?

Should we then consider recovery.conf entries as ordinary GUCs? That
would allow to connect to a standby and issue 'show primary_conninfo'
there. This has been asked before, already.

No. My suggestion was just to use the internal parser.
ParseConfigFile() returns parsed parameters as head_p and tail_p.
So, we can use it independently from GUC variables.

static bool
ParseConfigFile(const char *config_file, const char *calling_file,
int depth, GucContext context, int elevel,
struct name_value_pair **head_p,
struct name_value_pair **tail_p)

Special codes for "include" and "custom_variable_classes" in it
might not be needed to parse recovery.conf and extensions.
We would disable them when we parse non-guc configuration files.
Or, we could allow "include" in recovery.conf if we think it is
useful in some cases.

--
Itagaki Takahiro

#116Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Itagaki Takahiro (#115)
1 attachment(s)
Re: Extensions, this time with a patch

Itagaki Takahiro <itagaki.takahiro@gmail.com> writes:

No. My suggestion was just to use the internal parser.

What about something like the attached, cfparser.v3.patch?

If that looks ok, do we want to add some documentation about the new
lexer capabilities? Also, for what good reason would we want to prevent
people from using the include facility?

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

Attachments:

cfparser.v3.patchtext/x-patchDownload
*** a/src/backend/access/transam/xlog.c
--- b/src/backend/access/transam/xlog.c
***************
*** 5024,5200 **** str_time(pg_time_t tnow)
  }
  
  /*
-  * Parse one line from recovery.conf. 'cmdline' is the raw line from the
-  * file. If the line is parsed successfully, returns true, false indicates
-  * syntax error. On success, *key_p and *value_p are set to the parameter
-  * name and value on the line, respectively. If the line is an empty line,
-  * consisting entirely of whitespace and comments, function returns true
-  * and *keyp_p and *value_p are set to NULL.
-  *
-  * The pointers returned in *key_p and *value_p point to an internal buffer
-  * that is valid only until the next call of parseRecoveryCommandFile().
-  */
- static bool
- parseRecoveryCommandFileLine(char *cmdline, char **key_p, char **value_p)
- {
- 	char	   *ptr;
- 	char	   *bufp;
- 	char	   *key;
- 	char	   *value;
- 	static char *buf = NULL;
- 
- 	*key_p = *value_p = NULL;
- 
- 	/*
- 	 * Allocate the buffer on first use. It's used to hold both the parameter
- 	 * name and value.
- 	 */
- 	if (buf == NULL)
- 		buf = malloc(MAXPGPATH + 1);
- 	bufp = buf;
- 
- 	/* Skip any whitespace at the beginning of line */
- 	for (ptr = cmdline; *ptr; ptr++)
- 	{
- 		if (!isspace((unsigned char) *ptr))
- 			break;
- 	}
- 	/* Ignore empty lines */
- 	if (*ptr == '\0' || *ptr == '#')
- 		return true;
- 
- 	/* Read the parameter name */
- 	key = bufp;
- 	while (*ptr && !isspace((unsigned char) *ptr) &&
- 		   *ptr != '=' && *ptr != '\'')
- 		*(bufp++) = *(ptr++);
- 	*(bufp++) = '\0';
- 
- 	/* Skip to the beginning quote of the parameter value */
- 	ptr = strchr(ptr, '\'');
- 	if (!ptr)
- 		return false;
- 	ptr++;
- 
- 	/* Read the parameter value to *bufp. Collapse any '' escapes as we go. */
- 	value = bufp;
- 	for (;;)
- 	{
- 		if (*ptr == '\'')
- 		{
- 			ptr++;
- 			if (*ptr == '\'')
- 				*(bufp++) = '\'';
- 			else
- 			{
- 				/* end of parameter */
- 				*bufp = '\0';
- 				break;
- 			}
- 		}
- 		else if (*ptr == '\0')
- 			return false;		/* unterminated quoted string */
- 		else
- 			*(bufp++) = *ptr;
- 
- 		ptr++;
- 	}
- 	*(bufp++) = '\0';
- 
- 	/* Check that there's no garbage after the value */
- 	while (*ptr)
- 	{
- 		if (*ptr == '#')
- 			break;
- 		if (!isspace((unsigned char) *ptr))
- 			return false;
- 		ptr++;
- 	}
- 
- 	/* Success! */
- 	*key_p = key;
- 	*value_p = value;
- 	return true;
- }
- 
- /*
   * See if there is a recovery command file (recovery.conf), and if so
   * read in parameters for archive recovery and XLOG streaming.
   *
!  * XXX longer term intention is to expand this to
!  * cater for additional parameters and controls
!  * possibly use a flex lexer similar to the GUC one
   */
  static void
  readRecoveryCommandFile(void)
  {
  	FILE	   *fd;
- 	char		cmdline[MAXPGPATH];
  	TimeLineID	rtli = 0;
  	bool		rtliGiven = false;
! 	bool		syntaxError = false;
  
  	fd = AllocateFile(RECOVERY_COMMAND_FILE, "r");
! 	if (fd == NULL)
! 	{
! 		if (errno == ENOENT)
! 			return;				/* not there, so no archive recovery */
! 		ereport(FATAL,
! 				(errcode_for_file_access(),
! 				 errmsg("could not open recovery command file \"%s\": %m",
! 						RECOVERY_COMMAND_FILE)));
! 	}
  
  	/*
! 	 * Parse the file...
  	 */
! 	while (fgets(cmdline, sizeof(cmdline), fd) != NULL)
! 	{
! 		char	   *tok1;
! 		char	   *tok2;
! 
! 		if (!parseRecoveryCommandFileLine(cmdline, &tok1, &tok2))
! 		{
! 			syntaxError = true;
! 			break;
! 		}
! 		if (tok1 == NULL)
! 			continue;
  
! 		if (strcmp(tok1, "restore_command") == 0)
  		{
! 			recoveryRestoreCommand = pstrdup(tok2);
  			ereport(DEBUG2,
  					(errmsg("restore_command = '%s'",
  							recoveryRestoreCommand)));
  		}
! 		else if (strcmp(tok1, "recovery_end_command") == 0)
  		{
! 			recoveryEndCommand = pstrdup(tok2);
  			ereport(DEBUG2,
  					(errmsg("recovery_end_command = '%s'",
  							recoveryEndCommand)));
  		}
! 		else if (strcmp(tok1, "archive_cleanup_command") == 0)
  		{
! 			archiveCleanupCommand = pstrdup(tok2);
  			ereport(DEBUG2,
  					(errmsg("archive_cleanup_command = '%s'",
  							archiveCleanupCommand)));
  		}
! 		else if (strcmp(tok1, "recovery_target_timeline") == 0)
  		{
  			rtliGiven = true;
! 			if (strcmp(tok2, "latest") == 0)
  				rtli = 0;
  			else
  			{
  				errno = 0;
! 				rtli = (TimeLineID) strtoul(tok2, NULL, 0);
  				if (errno == EINVAL || errno == ERANGE)
  					ereport(FATAL,
  							(errmsg("recovery_target_timeline is not a valid number: \"%s\"",
! 									tok2)));
  			}
  			if (rtli)
  				ereport(DEBUG2,
--- 5024,5096 ----
  }
  
  /*
   * See if there is a recovery command file (recovery.conf), and if so
   * read in parameters for archive recovery and XLOG streaming.
   *
!  * The file is parsed using the main configuration parser.
   */
  static void
  readRecoveryCommandFile(void)
  {
  	FILE	   *fd;
  	TimeLineID	rtli = 0;
  	bool		rtliGiven = false;
! 	ConfigNameValuePair item, head, tail;
  
+ 	/*
+ 	 * See if recovery.conf file is present
+ 	 */
  	fd = AllocateFile(RECOVERY_COMMAND_FILE, "r");
! 	if (!fd)
! 		return;			/* it's not there, all is fine */
! 
! 	FreeFile(fd);
  
  	/*
! 	 * Pretend we're reading recovery.conf from postgresql.conf in this
! 	 * version of the patch, just so that we avoid either finding the
! 	 * data_directory from xlog.c or changing the APIs.
  	 */
! 	if (!ParseConfigFile(RECOVERY_COMMAND_FILE, ConfigFileName, 1,
! 						 PGC_INTERNAL, FATAL, &head, &tail))
! 		goto cleanup_list;
  
! 	for (item = head; item; item = item->next)
! 	{
! 		if (strcmp(item->name, "restore_command") == 0)
  		{
! 			recoveryRestoreCommand = pstrdup(item->value);
  			ereport(DEBUG2,
  					(errmsg("restore_command = '%s'",
  							recoveryRestoreCommand)));
  		}
! 		else if (strcmp(item->name, "recovery_end_command") == 0)
  		{
! 			recoveryEndCommand = pstrdup(item->value);
  			ereport(DEBUG2,
  					(errmsg("recovery_end_command = '%s'",
  							recoveryEndCommand)));
  		}
! 		else if (strcmp(item->name, "archive_cleanup_command") == 0)
  		{
! 			archiveCleanupCommand = pstrdup(item->value);
  			ereport(DEBUG2,
  					(errmsg("archive_cleanup_command = '%s'",
  							archiveCleanupCommand)));
  		}
! 		else if (strcmp(item->name, "recovery_target_timeline") == 0)
  		{
  			rtliGiven = true;
! 			if (strcmp(item->value, "latest") == 0)
  				rtli = 0;
  			else
  			{
  				errno = 0;
! 				rtli = (TimeLineID) strtoul(item->value, NULL, 0);
  				if (errno == EINVAL || errno == ERANGE)
  					ereport(FATAL,
  							(errmsg("recovery_target_timeline is not a valid number: \"%s\"",
! 									item->value)));
  			}
  			if (rtli)
  				ereport(DEBUG2,
***************
*** 5203,5222 **** readRecoveryCommandFile(void)
  				ereport(DEBUG2,
  						(errmsg("recovery_target_timeline = latest")));
  		}
! 		else if (strcmp(tok1, "recovery_target_xid") == 0)
  		{
  			errno = 0;
! 			recoveryTargetXid = (TransactionId) strtoul(tok2, NULL, 0);
  			if (errno == EINVAL || errno == ERANGE)
  				ereport(FATAL,
  				 (errmsg("recovery_target_xid is not a valid number: \"%s\"",
! 						 tok2)));
  			ereport(DEBUG2,
  					(errmsg("recovery_target_xid = %u",
  							recoveryTargetXid)));
  			recoveryTarget = RECOVERY_TARGET_XID;
  		}
! 		else if (strcmp(tok1, "recovery_target_time") == 0)
  		{
  			/*
  			 * if recovery_target_xid specified, then this overrides
--- 5099,5118 ----
  				ereport(DEBUG2,
  						(errmsg("recovery_target_timeline = latest")));
  		}
! 		else if (strcmp(item->name, "recovery_target_xid") == 0)
  		{
  			errno = 0;
! 			recoveryTargetXid = (TransactionId) strtoul(item->value, NULL, 0);
  			if (errno == EINVAL || errno == ERANGE)
  				ereport(FATAL,
  				 (errmsg("recovery_target_xid is not a valid number: \"%s\"",
! 						 item->value)));
  			ereport(DEBUG2,
  					(errmsg("recovery_target_xid = %u",
  							recoveryTargetXid)));
  			recoveryTarget = RECOVERY_TARGET_XID;
  		}
! 		else if (strcmp(item->name, "recovery_target_time") == 0)
  		{
  			/*
  			 * if recovery_target_xid specified, then this overrides
***************
*** 5231,5274 **** readRecoveryCommandFile(void)
  			 */
  			recoveryTargetTime =
  				DatumGetTimestampTz(DirectFunctionCall3(timestamptz_in,
! 														CStringGetDatum(tok2),
  												ObjectIdGetDatum(InvalidOid),
  														Int32GetDatum(-1)));
  			ereport(DEBUG2,
  					(errmsg("recovery_target_time = '%s'",
  							timestamptz_to_str(recoveryTargetTime))));
  		}
! 		else if (strcmp(tok1, "recovery_target_inclusive") == 0)
  		{
  			/*
  			 * does nothing if a recovery_target is not also set
  			 */
! 			if (!parse_bool(tok2, &recoveryTargetInclusive))
  				ereport(ERROR,
  						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  						 errmsg("parameter \"%s\" requires a Boolean value", "recovery_target_inclusive")));
  			ereport(DEBUG2,
! 					(errmsg("recovery_target_inclusive = %s", tok2)));
  		}
! 		else if (strcmp(tok1, "standby_mode") == 0)
  		{
! 			if (!parse_bool(tok2, &StandbyMode))
  				ereport(ERROR,
  						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  						 errmsg("parameter \"%s\" requires a Boolean value", "standby_mode")));
  			ereport(DEBUG2,
! 					(errmsg("standby_mode = '%s'", tok2)));
  		}
! 		else if (strcmp(tok1, "primary_conninfo") == 0)
  		{
! 			PrimaryConnInfo = pstrdup(tok2);
  			ereport(DEBUG2,
  					(errmsg("primary_conninfo = '%s'",
  							PrimaryConnInfo)));
  		}
! 		else if (strcmp(tok1, "trigger_file") == 0)
  		{
! 			TriggerFile = pstrdup(tok2);
  			ereport(DEBUG2,
  					(errmsg("trigger_file = '%s'",
  							TriggerFile)));
--- 5127,5170 ----
  			 */
  			recoveryTargetTime =
  				DatumGetTimestampTz(DirectFunctionCall3(timestamptz_in,
! 														CStringGetDatum(item->value),
  												ObjectIdGetDatum(InvalidOid),
  														Int32GetDatum(-1)));
  			ereport(DEBUG2,
  					(errmsg("recovery_target_time = '%s'",
  							timestamptz_to_str(recoveryTargetTime))));
  		}
! 		else if (strcmp(item->name, "recovery_target_inclusive") == 0)
  		{
  			/*
  			 * does nothing if a recovery_target is not also set
  			 */
! 			if (!parse_bool(item->value, &recoveryTargetInclusive))
  				ereport(ERROR,
  						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  						 errmsg("parameter \"%s\" requires a Boolean value", "recovery_target_inclusive")));
  			ereport(DEBUG2,
! 					(errmsg("recovery_target_inclusive = %s", item->value)));
  		}
! 		else if (strcmp(item->name, "standby_mode") == 0)
  		{
! 			if (!parse_bool(item->value, &StandbyMode))
  				ereport(ERROR,
  						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  						 errmsg("parameter \"%s\" requires a Boolean value", "standby_mode")));
  			ereport(DEBUG2,
! 					(errmsg("standby_mode = '%s'", item->value)));
  		}
! 		else if (strcmp(item->name, "primary_conninfo") == 0)
  		{
! 			PrimaryConnInfo = pstrdup(item->value);
  			ereport(DEBUG2,
  					(errmsg("primary_conninfo = '%s'",
  							PrimaryConnInfo)));
  		}
! 		else if (strcmp(item->name, "trigger_file") == 0)
  		{
! 			TriggerFile = pstrdup(item->value);
  			ereport(DEBUG2,
  					(errmsg("trigger_file = '%s'",
  							TriggerFile)));
***************
*** 5276,5292 **** readRecoveryCommandFile(void)
  		else
  			ereport(FATAL,
  					(errmsg("unrecognized recovery parameter \"%s\"",
! 							tok1)));
  	}
  
- 	FreeFile(fd);
- 
- 	if (syntaxError)
- 		ereport(FATAL,
- 				(errmsg("syntax error in recovery command file: %s",
- 						cmdline),
- 			  errhint("Lines should have the format parameter = 'value'.")));
- 
  	/*
  	 * Check for compulsory parameters
  	 */
--- 5172,5180 ----
  		else
  			ereport(FATAL,
  					(errmsg("unrecognized recovery parameter \"%s\"",
! 							item->name)));
  	}
  
  	/*
  	 * Check for compulsory parameters
  	 */
***************
*** 5332,5337 **** readRecoveryCommandFile(void)
--- 5220,5228 ----
  			recoveryTargetTLI = findNewestTimeLine(recoveryTargetTLI);
  		}
  	}
+ 
+  cleanup_list:
+ 	free_name_value_list(head);
  }
  
  /*
*** a/src/backend/utils/misc/guc-file.l
--- b/src/backend/utils/misc/guc-file.l
***************
*** 35,59 **** enum {
  	GUC_ERROR = 100
  };
  
- struct name_value_pair
- {
- 	char       *name;
- 	char       *value;
- 	char	   *filename;
- 	int			sourceline;
- 	struct name_value_pair *next;
- };
- 
  static unsigned int ConfigFileLineno;
  
  /* flex fails to supply a prototype for yylex, so provide one */
  int GUC_yylex(void);
  
- static bool ParseConfigFile(const char *config_file, const char *calling_file,
- 							int depth, GucContext context, int elevel,
- 							struct name_value_pair **head_p,
- 							struct name_value_pair **tail_p);
- static void free_name_value_list(struct name_value_pair * list);
  static char *GUC_scanstr(const char *s);
  
  %}
--- 35,45 ----
***************
*** 118,124 **** void
  ProcessConfigFile(GucContext context)
  {
  	int			elevel;
! 	struct name_value_pair *item, *head, *tail;
  	char	   *cvc = NULL;
  	struct config_string *cvc_struct;
  	const char *envvar;
--- 104,110 ----
  ProcessConfigFile(GucContext context)
  {
  	int			elevel;
! 	ConfigNameValuePair item, head, tail;
  	char	   *cvc = NULL;
  	struct config_string *cvc_struct;
  	const char *envvar;
***************
*** 387,393 **** ProcessConfigFile(GucContext context)
   * where an error will lead to immediate process exit anyway; so there is
   * no point in contorting the code so it can clean up nicely.
   */
! static bool
  ParseConfigFile(const char *config_file, const char *calling_file,
  				int depth, GucContext context, int elevel,
  				struct name_value_pair **head_p,
--- 373,379 ----
   * where an error will lead to immediate process exit anyway; so there is
   * no point in contorting the code so it can clean up nicely.
   */
! bool
  ParseConfigFile(const char *config_file, const char *calling_file,
  				int depth, GucContext context, int elevel,
  				struct name_value_pair **head_p,
***************
*** 590,596 **** cleanup_exit:
  /*
   * Free a list of name/value pairs, including the names and the values
   */
! static void
  free_name_value_list(struct name_value_pair *list)
  {
  	struct name_value_pair *item;
--- 576,582 ----
  /*
   * Free a list of name/value pairs, including the names and the values
   */
! void
  free_name_value_list(struct name_value_pair *list)
  {
  	struct name_value_pair *item;
*** a/src/include/utils/guc.h
--- b/src/include/utils/guc.h
***************
*** 17,23 ****
  #include "tcop/dest.h"
  #include "utils/array.h"
  
- 
  /*
   * Certain options can only be set at certain times. The rules are
   * like this:
--- 17,22 ----
***************
*** 97,102 **** typedef enum
--- 96,122 ----
  } GucSource;
  
  /*
+  * Parsing the configuation file will return a list of name-value pairs
+  */
+ struct name_value_pair
+ {
+ 	char       *name;
+ 	char       *value;
+ 	char	   *filename;
+ 	int			sourceline;
+ 	struct name_value_pair *next;
+ } name_value_pair;
+ 
+ typedef struct name_value_pair *ConfigNameValuePair;
+ 
+ bool ParseConfigFile(const char *config_file, const char *calling_file,
+ 							int depth, GucContext context, int elevel,
+ 							ConfigNameValuePair *head_p,
+ 							ConfigNameValuePair *tail_p);
+ 
+ void free_name_value_list(struct name_value_pair * list);
+ 
+ /*
   * Enum values are made up of an array of name-value pairs
   */
  struct config_enum_entry
#117Alvaro Herrera
alvherre@commandprompt.com
In reply to: Dimitri Fontaine (#116)
Re: Extensions, this time with a patch

Excerpts from Dimitri Fontaine's message of lun nov 22 18:12:39 -0300 2010:

Itagaki Takahiro <itagaki.takahiro@gmail.com> writes:

No. My suggestion was just to use the internal parser.

What about something like the attached, cfparser.v3.patch?

If that looks ok, do we want to add some documentation about the new
lexer capabilities? Also, for what good reason would we want to prevent
people from using the include facility?

Hmm, the first thought that comes to mind is that the GucContext param
to ParseConfigFile is unused and can be removed. This is probably an
oversight from when include files were introduced in 2006.

I don't like the fact that this code handles custom_variable_classes
internally. I think this would be exposed to the parsing of extension
control files, which is obviously wrong.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#118Alvaro Herrera
alvherre@commandprompt.com
In reply to: Alvaro Herrera (#117)
Re: Extensions, this time with a patch

Excerpts from Alvaro Herrera's message of lun nov 22 18:59:52 -0300 2010:

Hmm, the first thought that comes to mind is that the GucContext param
to ParseConfigFile is unused and can be removed. This is probably an
oversight from when include files were introduced in 2006.

Committed and pushed.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#119Alvaro Herrera
alvherre@commandprompt.com
In reply to: Dimitri Fontaine (#116)
Re: Extensions, this time with a patch

Excerpts from Dimitri Fontaine's message of lun nov 22 18:12:39 -0300 2010:

Itagaki Takahiro <itagaki.takahiro@gmail.com> writes:

No. My suggestion was just to use the internal parser.

What about something like the attached, cfparser.v3.patch?

the handling of relative vs absolute paths is bogus here. I think it'd
make more sense to have a bool "are we including"; and if that's false and
the path is not absolute, then the file is relative to CWD; or maybe we
make it absolute by prepending PGDATA; maybe something else? (need to
think of something that makes sense for both recovery.conf and extension
control files)

If that looks ok, do we want to add some documentation about the new
lexer capabilities?

beyond extra code comments? probably not.

Also, for what good reason would we want to prevent
people from using the include facility?

Not sure about this

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#120Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Alvaro Herrera (#117)
Re: Extensions, this time with a patch

Alvaro Herrera <alvherre@commandprompt.com> writes:

Hmm, the first thought that comes to mind is that the GucContext param
to ParseConfigFile is unused and can be removed. This is probably an
oversight from when include files were introduced in 2006.

Thanks for caring about that part.

I don't like the fact that this code handles custom_variable_classes
internally. I think this would be exposed to the parsing of extension
control files, which is obviously wrong.

Well, in fact, not that much. The extension code has a special error
case when dealing with custom variables if the class hasn't been already
parsed, and what ParseConfigFile() is doing is pushing the
custom_variable_classes setting in front of the list.

guc-file.l says:
/*
* This variable must be processed first as it controls
* the validity of other variables; so it goes at the head
* of the result list. If we already found a value for it,
* replace with this one.
*/

extension.c says:
ereport(ERROR,
(errmsg("Unsupported parameter '%s' in file: %s",
tok1, filename),
errhint("Be sure to have 'custom_variable_classes' set "
"in a line before any custom variable.")));

So if we don't change the code in ParseConfigFile() that will push
custom_variable_classes in front of the list, all I have to change in
the extension.c file is the error message.

I fail to see a future usage of custom_variable_classes where it
wouldn't help to have that in the list before any user setting that
depends on it.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#121Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Alvaro Herrera (#119)
1 attachment(s)
Re: Extensions, this time with a patch

Alvaro Herrera <alvherre@commandprompt.com> writes:

the handling of relative vs absolute paths is bogus here. I think it'd
make more sense to have a bool "are we including"; and if that's false and
the path is not absolute, then the file is relative to CWD; or maybe we
make it absolute by prepending PGDATA; maybe something else? (need to
think of something that makes sense for both recovery.conf and extension
control files)

Current coding in extensions prepend any control or script file with
sharepath, so that we're only dealing with absolute filename here. The
idea is that it's no business for any other part of the code to have to
know where we decide to install control and script files.

My feeling is that when !is_absolute_path(config_file) and calling_file
is NULL we should make the config_file absolute by prepending PGDATA.
Please find that done in attached v4 of the cfparser patch.

If that looks ok, do we want to add some documentation about the new
lexer capabilities?

beyond extra code comments? probably not.

Great.

Also, for what good reason would we want to prevent
people from using the include facility?

Not sure about this

Ok, nothing special here.
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

Attachments:

cfparser.v4.patchtext/x-patchDownload
*** a/src/backend/access/transam/xlog.c
--- b/src/backend/access/transam/xlog.c
***************
*** 5024,5200 **** str_time(pg_time_t tnow)
  }
  
  /*
-  * Parse one line from recovery.conf. 'cmdline' is the raw line from the
-  * file. If the line is parsed successfully, returns true, false indicates
-  * syntax error. On success, *key_p and *value_p are set to the parameter
-  * name and value on the line, respectively. If the line is an empty line,
-  * consisting entirely of whitespace and comments, function returns true
-  * and *keyp_p and *value_p are set to NULL.
-  *
-  * The pointers returned in *key_p and *value_p point to an internal buffer
-  * that is valid only until the next call of parseRecoveryCommandFile().
-  */
- static bool
- parseRecoveryCommandFileLine(char *cmdline, char **key_p, char **value_p)
- {
- 	char	   *ptr;
- 	char	   *bufp;
- 	char	   *key;
- 	char	   *value;
- 	static char *buf = NULL;
- 
- 	*key_p = *value_p = NULL;
- 
- 	/*
- 	 * Allocate the buffer on first use. It's used to hold both the parameter
- 	 * name and value.
- 	 */
- 	if (buf == NULL)
- 		buf = malloc(MAXPGPATH + 1);
- 	bufp = buf;
- 
- 	/* Skip any whitespace at the beginning of line */
- 	for (ptr = cmdline; *ptr; ptr++)
- 	{
- 		if (!isspace((unsigned char) *ptr))
- 			break;
- 	}
- 	/* Ignore empty lines */
- 	if (*ptr == '\0' || *ptr == '#')
- 		return true;
- 
- 	/* Read the parameter name */
- 	key = bufp;
- 	while (*ptr && !isspace((unsigned char) *ptr) &&
- 		   *ptr != '=' && *ptr != '\'')
- 		*(bufp++) = *(ptr++);
- 	*(bufp++) = '\0';
- 
- 	/* Skip to the beginning quote of the parameter value */
- 	ptr = strchr(ptr, '\'');
- 	if (!ptr)
- 		return false;
- 	ptr++;
- 
- 	/* Read the parameter value to *bufp. Collapse any '' escapes as we go. */
- 	value = bufp;
- 	for (;;)
- 	{
- 		if (*ptr == '\'')
- 		{
- 			ptr++;
- 			if (*ptr == '\'')
- 				*(bufp++) = '\'';
- 			else
- 			{
- 				/* end of parameter */
- 				*bufp = '\0';
- 				break;
- 			}
- 		}
- 		else if (*ptr == '\0')
- 			return false;		/* unterminated quoted string */
- 		else
- 			*(bufp++) = *ptr;
- 
- 		ptr++;
- 	}
- 	*(bufp++) = '\0';
- 
- 	/* Check that there's no garbage after the value */
- 	while (*ptr)
- 	{
- 		if (*ptr == '#')
- 			break;
- 		if (!isspace((unsigned char) *ptr))
- 			return false;
- 		ptr++;
- 	}
- 
- 	/* Success! */
- 	*key_p = key;
- 	*value_p = value;
- 	return true;
- }
- 
- /*
   * See if there is a recovery command file (recovery.conf), and if so
   * read in parameters for archive recovery and XLOG streaming.
   *
!  * XXX longer term intention is to expand this to
!  * cater for additional parameters and controls
!  * possibly use a flex lexer similar to the GUC one
   */
  static void
  readRecoveryCommandFile(void)
  {
  	FILE	   *fd;
- 	char		cmdline[MAXPGPATH];
  	TimeLineID	rtli = 0;
  	bool		rtliGiven = false;
! 	bool		syntaxError = false;
  
  	fd = AllocateFile(RECOVERY_COMMAND_FILE, "r");
  	if (fd == NULL)
  	{
  		if (errno == ENOENT)
! 			return;				/* not there, so no archive recovery */
  		ereport(FATAL,
  				(errcode_for_file_access(),
  				 errmsg("could not open recovery command file \"%s\": %m",
  						RECOVERY_COMMAND_FILE)));
  	}
  
! 	/*
! 	 * Parse the file...
! 	 */
! 	while (fgets(cmdline, sizeof(cmdline), fd) != NULL)
! 	{
! 		char	   *tok1;
! 		char	   *tok2;
! 
! 		if (!parseRecoveryCommandFileLine(cmdline, &tok1, &tok2))
! 		{
! 			syntaxError = true;
! 			break;
! 		}
! 		if (tok1 == NULL)
! 			continue;
  
! 		if (strcmp(tok1, "restore_command") == 0)
  		{
! 			recoveryRestoreCommand = pstrdup(tok2);
  			ereport(DEBUG2,
  					(errmsg("restore_command = '%s'",
  							recoveryRestoreCommand)));
  		}
! 		else if (strcmp(tok1, "recovery_end_command") == 0)
  		{
! 			recoveryEndCommand = pstrdup(tok2);
  			ereport(DEBUG2,
  					(errmsg("recovery_end_command = '%s'",
  							recoveryEndCommand)));
  		}
! 		else if (strcmp(tok1, "archive_cleanup_command") == 0)
  		{
! 			archiveCleanupCommand = pstrdup(tok2);
  			ereport(DEBUG2,
  					(errmsg("archive_cleanup_command = '%s'",
  							archiveCleanupCommand)));
  		}
! 		else if (strcmp(tok1, "recovery_target_timeline") == 0)
  		{
  			rtliGiven = true;
! 			if (strcmp(tok2, "latest") == 0)
  				rtli = 0;
  			else
  			{
  				errno = 0;
! 				rtli = (TimeLineID) strtoul(tok2, NULL, 0);
  				if (errno == EINVAL || errno == ERANGE)
  					ereport(FATAL,
  							(errmsg("recovery_target_timeline is not a valid number: \"%s\"",
! 									tok2)));
  			}
  			if (rtli)
  				ereport(DEBUG2,
--- 5024,5096 ----
  }
  
  /*
   * See if there is a recovery command file (recovery.conf), and if so
   * read in parameters for archive recovery and XLOG streaming.
   *
!  * The file is parsed using the main configuration parser.
   */
  static void
  readRecoveryCommandFile(void)
  {
  	FILE	   *fd;
  	TimeLineID	rtli = 0;
  	bool		rtliGiven = false;
! 	ConfigNameValuePair item, head, tail;
  
+ 	/*
+ 	 * See if recovery.conf file is present
+ 	 */
  	fd = AllocateFile(RECOVERY_COMMAND_FILE, "r");
  	if (fd == NULL)
  	{
  		if (errno == ENOENT)
! 			return;                         /* not there, so no archive recovery */
  		ereport(FATAL,
  				(errcode_for_file_access(),
  				 errmsg("could not open recovery command file \"%s\": %m",
  						RECOVERY_COMMAND_FILE)));
  	}
+ 	FreeFile(fd);
  
! 	if (!ParseConfigFile(RECOVERY_COMMAND_FILE, NULL, 0, FATAL, &head, &tail))
! 		goto cleanup_list;
  
! 	for (item = head; item; item = item->next)
! 	{
! 		if (strcmp(item->name, "restore_command") == 0)
  		{
! 			recoveryRestoreCommand = pstrdup(item->value);
  			ereport(DEBUG2,
  					(errmsg("restore_command = '%s'",
  							recoveryRestoreCommand)));
  		}
! 		else if (strcmp(item->name, "recovery_end_command") == 0)
  		{
! 			recoveryEndCommand = pstrdup(item->value);
  			ereport(DEBUG2,
  					(errmsg("recovery_end_command = '%s'",
  							recoveryEndCommand)));
  		}
! 		else if (strcmp(item->name, "archive_cleanup_command") == 0)
  		{
! 			archiveCleanupCommand = pstrdup(item->value);
  			ereport(DEBUG2,
  					(errmsg("archive_cleanup_command = '%s'",
  							archiveCleanupCommand)));
  		}
! 		else if (strcmp(item->name, "recovery_target_timeline") == 0)
  		{
  			rtliGiven = true;
! 			if (strcmp(item->value, "latest") == 0)
  				rtli = 0;
  			else
  			{
  				errno = 0;
! 				rtli = (TimeLineID) strtoul(item->value, NULL, 0);
  				if (errno == EINVAL || errno == ERANGE)
  					ereport(FATAL,
  							(errmsg("recovery_target_timeline is not a valid number: \"%s\"",
! 									item->value)));
  			}
  			if (rtli)
  				ereport(DEBUG2,
***************
*** 5203,5222 **** readRecoveryCommandFile(void)
  				ereport(DEBUG2,
  						(errmsg("recovery_target_timeline = latest")));
  		}
! 		else if (strcmp(tok1, "recovery_target_xid") == 0)
  		{
  			errno = 0;
! 			recoveryTargetXid = (TransactionId) strtoul(tok2, NULL, 0);
  			if (errno == EINVAL || errno == ERANGE)
  				ereport(FATAL,
  				 (errmsg("recovery_target_xid is not a valid number: \"%s\"",
! 						 tok2)));
  			ereport(DEBUG2,
  					(errmsg("recovery_target_xid = %u",
  							recoveryTargetXid)));
  			recoveryTarget = RECOVERY_TARGET_XID;
  		}
! 		else if (strcmp(tok1, "recovery_target_time") == 0)
  		{
  			/*
  			 * if recovery_target_xid specified, then this overrides
--- 5099,5118 ----
  				ereport(DEBUG2,
  						(errmsg("recovery_target_timeline = latest")));
  		}
! 		else if (strcmp(item->name, "recovery_target_xid") == 0)
  		{
  			errno = 0;
! 			recoveryTargetXid = (TransactionId) strtoul(item->value, NULL, 0);
  			if (errno == EINVAL || errno == ERANGE)
  				ereport(FATAL,
  				 (errmsg("recovery_target_xid is not a valid number: \"%s\"",
! 						 item->value)));
  			ereport(DEBUG2,
  					(errmsg("recovery_target_xid = %u",
  							recoveryTargetXid)));
  			recoveryTarget = RECOVERY_TARGET_XID;
  		}
! 		else if (strcmp(item->name, "recovery_target_time") == 0)
  		{
  			/*
  			 * if recovery_target_xid specified, then this overrides
***************
*** 5231,5274 **** readRecoveryCommandFile(void)
  			 */
  			recoveryTargetTime =
  				DatumGetTimestampTz(DirectFunctionCall3(timestamptz_in,
! 														CStringGetDatum(tok2),
  												ObjectIdGetDatum(InvalidOid),
  														Int32GetDatum(-1)));
  			ereport(DEBUG2,
  					(errmsg("recovery_target_time = '%s'",
  							timestamptz_to_str(recoveryTargetTime))));
  		}
! 		else if (strcmp(tok1, "recovery_target_inclusive") == 0)
  		{
  			/*
  			 * does nothing if a recovery_target is not also set
  			 */
! 			if (!parse_bool(tok2, &recoveryTargetInclusive))
  				ereport(ERROR,
  						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  						 errmsg("parameter \"%s\" requires a Boolean value", "recovery_target_inclusive")));
  			ereport(DEBUG2,
! 					(errmsg("recovery_target_inclusive = %s", tok2)));
  		}
! 		else if (strcmp(tok1, "standby_mode") == 0)
  		{
! 			if (!parse_bool(tok2, &StandbyMode))
  				ereport(ERROR,
  						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  						 errmsg("parameter \"%s\" requires a Boolean value", "standby_mode")));
  			ereport(DEBUG2,
! 					(errmsg("standby_mode = '%s'", tok2)));
  		}
! 		else if (strcmp(tok1, "primary_conninfo") == 0)
  		{
! 			PrimaryConnInfo = pstrdup(tok2);
  			ereport(DEBUG2,
  					(errmsg("primary_conninfo = '%s'",
  							PrimaryConnInfo)));
  		}
! 		else if (strcmp(tok1, "trigger_file") == 0)
  		{
! 			TriggerFile = pstrdup(tok2);
  			ereport(DEBUG2,
  					(errmsg("trigger_file = '%s'",
  							TriggerFile)));
--- 5127,5170 ----
  			 */
  			recoveryTargetTime =
  				DatumGetTimestampTz(DirectFunctionCall3(timestamptz_in,
! 														CStringGetDatum(item->value),
  												ObjectIdGetDatum(InvalidOid),
  														Int32GetDatum(-1)));
  			ereport(DEBUG2,
  					(errmsg("recovery_target_time = '%s'",
  							timestamptz_to_str(recoveryTargetTime))));
  		}
! 		else if (strcmp(item->name, "recovery_target_inclusive") == 0)
  		{
  			/*
  			 * does nothing if a recovery_target is not also set
  			 */
! 			if (!parse_bool(item->value, &recoveryTargetInclusive))
  				ereport(ERROR,
  						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  						 errmsg("parameter \"%s\" requires a Boolean value", "recovery_target_inclusive")));
  			ereport(DEBUG2,
! 					(errmsg("recovery_target_inclusive = %s", item->value)));
  		}
! 		else if (strcmp(item->name, "standby_mode") == 0)
  		{
! 			if (!parse_bool(item->value, &StandbyMode))
  				ereport(ERROR,
  						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  						 errmsg("parameter \"%s\" requires a Boolean value", "standby_mode")));
  			ereport(DEBUG2,
! 					(errmsg("standby_mode = '%s'", item->value)));
  		}
! 		else if (strcmp(item->name, "primary_conninfo") == 0)
  		{
! 			PrimaryConnInfo = pstrdup(item->value);
  			ereport(DEBUG2,
  					(errmsg("primary_conninfo = '%s'",
  							PrimaryConnInfo)));
  		}
! 		else if (strcmp(item->name, "trigger_file") == 0)
  		{
! 			TriggerFile = pstrdup(item->value);
  			ereport(DEBUG2,
  					(errmsg("trigger_file = '%s'",
  							TriggerFile)));
***************
*** 5276,5292 **** readRecoveryCommandFile(void)
  		else
  			ereport(FATAL,
  					(errmsg("unrecognized recovery parameter \"%s\"",
! 							tok1)));
  	}
  
- 	FreeFile(fd);
- 
- 	if (syntaxError)
- 		ereport(FATAL,
- 				(errmsg("syntax error in recovery command file: %s",
- 						cmdline),
- 			  errhint("Lines should have the format parameter = 'value'.")));
- 
  	/*
  	 * Check for compulsory parameters
  	 */
--- 5172,5180 ----
  		else
  			ereport(FATAL,
  					(errmsg("unrecognized recovery parameter \"%s\"",
! 							item->name)));
  	}
  
  	/*
  	 * Check for compulsory parameters
  	 */
***************
*** 5332,5337 **** readRecoveryCommandFile(void)
--- 5220,5228 ----
  			recoveryTargetTLI = findNewestTimeLine(recoveryTargetTLI);
  		}
  	}
+ 
+  cleanup_list:
+ 	free_name_value_list(head);
  }
  
  /*
*** a/src/backend/utils/misc/guc-file.l
--- b/src/backend/utils/misc/guc-file.l
***************
*** 35,59 **** enum {
  	GUC_ERROR = 100
  };
  
- struct name_value_pair
- {
- 	char       *name;
- 	char       *value;
- 	char	   *filename;
- 	int			sourceline;
- 	struct name_value_pair *next;
- };
- 
  static unsigned int ConfigFileLineno;
  
  /* flex fails to supply a prototype for yylex, so provide one */
  int GUC_yylex(void);
  
- static bool ParseConfigFile(const char *config_file, const char *calling_file,
- 							int depth, int elevel,
- 							struct name_value_pair **head_p,
- 							struct name_value_pair **tail_p);
- static void free_name_value_list(struct name_value_pair * list);
  static char *GUC_scanstr(const char *s);
  
  %}
--- 35,45 ----
***************
*** 118,124 **** void
  ProcessConfigFile(GucContext context)
  {
  	int			elevel;
! 	struct name_value_pair *item, *head, *tail;
  	char	   *cvc = NULL;
  	struct config_string *cvc_struct;
  	const char *envvar;
--- 104,110 ----
  ProcessConfigFile(GucContext context)
  {
  	int			elevel;
! 	ConfigNameValuePair item, head, tail;
  	char	   *cvc = NULL;
  	struct config_string *cvc_struct;
  	const char *envvar;
***************
*** 384,390 **** ProcessConfigFile(GucContext context)
   * where an error will lead to immediate process exit anyway; so there is
   * no point in contorting the code so it can clean up nicely.
   */
! static bool
  ParseConfigFile(const char *config_file, const char *calling_file,
  				int depth, int elevel,
  				struct name_value_pair **head_p,
--- 370,376 ----
   * where an error will lead to immediate process exit anyway; so there is
   * no point in contorting the code so it can clean up nicely.
   */
! bool
  ParseConfigFile(const char *config_file, const char *calling_file,
  				int depth, int elevel,
  				struct name_value_pair **head_p,
***************
*** 416,427 **** ParseConfigFile(const char *config_file, const char *calling_file,
  	 */
  	if (!is_absolute_path(config_file))
  	{
! 		Assert(calling_file != NULL);
! 		strlcpy(abs_path, calling_file, sizeof(abs_path));
! 		get_parent_directory(abs_path);
! 		join_path_components(abs_path, abs_path, config_file);
! 		canonicalize_path(abs_path);
! 		config_file = abs_path;
  	}
  
  	fp = AllocateFile(config_file, "r");
--- 402,424 ----
  	 */
  	if (!is_absolute_path(config_file))
  	{
! 		if (calling_file != NULL)
! 		{
! 			strlcpy(abs_path, calling_file, sizeof(abs_path));
! 			get_parent_directory(abs_path);
! 			join_path_components(abs_path, abs_path, config_file);
! 			canonicalize_path(abs_path);
! 			config_file = abs_path;
! 		}
! 		else
! 		{
! 			/*
! 			 * calling_file is NULL, we make an absolute path from $PGDATA
! 			 */
! 			join_path_components(abs_path, data_directory, config_file);
! 			canonicalize_path(abs_path);
! 			config_file = abs_path;
! 		}
  	}
  
  	fp = AllocateFile(config_file, "r");
***************
*** 587,593 **** cleanup_exit:
  /*
   * Free a list of name/value pairs, including the names and the values
   */
! static void
  free_name_value_list(struct name_value_pair *list)
  {
  	struct name_value_pair *item;
--- 584,590 ----
  /*
   * Free a list of name/value pairs, including the names and the values
   */
! void
  free_name_value_list(struct name_value_pair *list)
  {
  	struct name_value_pair *item;
*** a/src/backend/utils/misc/guc.c
--- b/src/backend/utils/misc/guc.c
***************
*** 403,408 **** int			tcp_keepalives_interval;
--- 403,414 ----
  int			tcp_keepalives_count;
  
  /*
+  * This is part of the dummy variables, but it needs to be exported so that
+  * guc-file.l can create $PGDATA/some.conf out of relative path.
+  */
+ char *data_directory;
+ 
+ /*
   * These variables are all dummies that don't do anything, except in some
   * cases provide the value for SHOW to display.  The real state is elsewhere
   * and is kept in sync by assign_hooks.
***************
*** 426,432 **** static char *timezone_string;
  static char *log_timezone_string;
  static char *timezone_abbreviations_string;
  static char *XactIsoLevel_string;
- static char *data_directory;
  static char *custom_variable_classes;
  static int	max_function_args;
  static int	max_index_keys;
--- 432,437 ----
*** a/src/include/utils/guc.h
--- b/src/include/utils/guc.h
***************
*** 17,23 ****
  #include "tcop/dest.h"
  #include "utils/array.h"
  
- 
  /*
   * Certain options can only be set at certain times. The rules are
   * like this:
--- 17,22 ----
***************
*** 97,102 **** typedef enum
--- 96,122 ----
  } GucSource;
  
  /*
+  * Parsing the configuation file will return a list of name-value pairs
+  */
+ struct name_value_pair
+ {
+ 	char       *name;
+ 	char       *value;
+ 	char	   *filename;
+ 	int			sourceline;
+ 	struct name_value_pair *next;
+ } name_value_pair;
+ 
+ typedef struct name_value_pair *ConfigNameValuePair;
+ 
+ bool ParseConfigFile(const char *config_file, const char *calling_file,
+ 							int depth, int elevel,
+ 							ConfigNameValuePair *head_p,
+ 							ConfigNameValuePair *tail_p);
+ 
+ void free_name_value_list(struct name_value_pair * list);
+ 
+ /*
   * Enum values are made up of an array of name-value pairs
   */
  struct config_enum_entry
***************
*** 187,192 **** extern int	tcp_keepalives_idle;
--- 207,215 ----
  extern int	tcp_keepalives_interval;
  extern int	tcp_keepalives_count;
  
+ /* guc-file.l needs that to make absolute paths from relative ones */
+ extern char *data_directory;
+ 
  extern void SetConfigOption(const char *name, const char *value,
  				GucContext context, GucSource source);
  
#122Heikki Linnakangas
heikki.linnakangas@enterprisedb.com
In reply to: Robert Haas (#113)
Re: Extensions, this time with a patch

On 22.11.2010 03:35, Robert Haas wrote:

On Sun, Nov 21, 2010 at 8:10 PM, Itagaki Takahiro
<itagaki.takahiro@gmail.com> wrote:

On Wed, Oct 20, 2010 at 01:36, Dimitri Fontaine<dimitri@2ndquadrant.fr> wrote:

Ah yes, thinking it's an easy patch is not helping. Please find attached
a revised version of it.

I checked cfparser.v2.patch.

It exports the static parseRecoveryCommandFileLine() in xlog.c
as the global cfParseOneLine() in cfparser.c without modification.

It generates one warning, but it can be easily fixed.
cfparser.c:34: warning: no previous prototype for 'cfParseOneLine'

Some discussions about the patch:

* Is "cf" the best name for the prefix? Less abbreviated forms might
be less confusable. Personally, I prefer "conf".

* Can we export ParseConfigFile() in guc-file.l rather than
parseRecoveryCommandFileLine()? It can solve the issue that unquoted
parameter values in recovery.conf are not recognized. Even if we
won't merge them, just allowing unquoted values would be useful.

I'd really like to see postgresql.conf and recovery.conf parsing
merged, and I suspect, as Itagaki-san says, that postgresql.conf
parsing is the better model for any new code.

+1. There was unanimous agreement in the synchronous replication threads
that recovery.conf should be parsed with the GUC parser. The current
recovery.conf parser doesn't support escaping, for example.

--
Heikki Linnakangas
EnterpriseDB http://www.enterprisedb.com

#123Itagaki Takahiro
itagaki.takahiro@gmail.com
In reply to: Dimitri Fontaine (#121)
Re: Extensions, this time with a patch

On Tue, Nov 23, 2010 at 18:19, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

Please find that done in attached v4 of the cfparser patch.

RECOVERY_COMMAND_FILE is opened twice in the patch. The first time
is for checking the existence, and the second time is for parsing.
Instead of the repeat, how about adding FILE* version of parser?
It will be also called from ParseConfigFile() as a sub routine.

bool ParseConfigFd(FILE *fd, const char *config_file, int depth, ...)

BTW, the parser supports "include" and "custom_variable_classes"
not only for postgresql.conf but also for all files. Is it an
intended behavior? I think they are harmless, so we don't have
to change the codes; "include" might be useful even in recovery.conf,
and "custom_variable_classes" will be "unrecognized recovery
parameter" error after all.

--
Itagaki Takahiro

#124Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Itagaki Takahiro (#123)
1 attachment(s)
Re: Extensions, this time with a patch

Itagaki Takahiro <itagaki.takahiro@gmail.com> writes:

RECOVERY_COMMAND_FILE is opened twice in the patch. The first time
is for checking the existence, and the second time is for parsing.
Instead of the repeat, how about adding FILE* version of parser?
It will be also called from ParseConfigFile() as a sub routine.

bool ParseConfigFd(FILE *fd, const char *config_file, int depth, ...)

Something like the attached, version 5 of the patch? I've been using the
function name ParseConfigFp because the internal parameter was called fp
in the previous function body. I suppose that could easily be changed at
commit time if necessary.

BTW, the parser supports "include" and "custom_variable_classes"
not only for postgresql.conf but also for all files. Is it an
intended behavior? I think they are harmless, so we don't have
to change the codes; "include" might be useful even in recovery.conf,
and "custom_variable_classes" will be "unrecognized recovery
parameter" error after all.

Extensions will need the support for custom_variable_classes as it is
done now, and as you say, the recovery will just error out. You have to
clean your recovery.conf file then try again (I just tried and confirm).

I personally don't see any harm to have the features in all currently
known uses-cases, and I don't see any point in walking an extra mile
here to remove a feature in some cases.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

Attachments:

cfparser.v5.patchtext/x-patchDownload
*** a/src/backend/access/transam/xlog.c
--- b/src/backend/access/transam/xlog.c
***************
*** 5024,5200 **** str_time(pg_time_t tnow)
  }
  
  /*
-  * Parse one line from recovery.conf. 'cmdline' is the raw line from the
-  * file. If the line is parsed successfully, returns true, false indicates
-  * syntax error. On success, *key_p and *value_p are set to the parameter
-  * name and value on the line, respectively. If the line is an empty line,
-  * consisting entirely of whitespace and comments, function returns true
-  * and *keyp_p and *value_p are set to NULL.
-  *
-  * The pointers returned in *key_p and *value_p point to an internal buffer
-  * that is valid only until the next call of parseRecoveryCommandFile().
-  */
- static bool
- parseRecoveryCommandFileLine(char *cmdline, char **key_p, char **value_p)
- {
- 	char	   *ptr;
- 	char	   *bufp;
- 	char	   *key;
- 	char	   *value;
- 	static char *buf = NULL;
- 
- 	*key_p = *value_p = NULL;
- 
- 	/*
- 	 * Allocate the buffer on first use. It's used to hold both the parameter
- 	 * name and value.
- 	 */
- 	if (buf == NULL)
- 		buf = malloc(MAXPGPATH + 1);
- 	bufp = buf;
- 
- 	/* Skip any whitespace at the beginning of line */
- 	for (ptr = cmdline; *ptr; ptr++)
- 	{
- 		if (!isspace((unsigned char) *ptr))
- 			break;
- 	}
- 	/* Ignore empty lines */
- 	if (*ptr == '\0' || *ptr == '#')
- 		return true;
- 
- 	/* Read the parameter name */
- 	key = bufp;
- 	while (*ptr && !isspace((unsigned char) *ptr) &&
- 		   *ptr != '=' && *ptr != '\'')
- 		*(bufp++) = *(ptr++);
- 	*(bufp++) = '\0';
- 
- 	/* Skip to the beginning quote of the parameter value */
- 	ptr = strchr(ptr, '\'');
- 	if (!ptr)
- 		return false;
- 	ptr++;
- 
- 	/* Read the parameter value to *bufp. Collapse any '' escapes as we go. */
- 	value = bufp;
- 	for (;;)
- 	{
- 		if (*ptr == '\'')
- 		{
- 			ptr++;
- 			if (*ptr == '\'')
- 				*(bufp++) = '\'';
- 			else
- 			{
- 				/* end of parameter */
- 				*bufp = '\0';
- 				break;
- 			}
- 		}
- 		else if (*ptr == '\0')
- 			return false;		/* unterminated quoted string */
- 		else
- 			*(bufp++) = *ptr;
- 
- 		ptr++;
- 	}
- 	*(bufp++) = '\0';
- 
- 	/* Check that there's no garbage after the value */
- 	while (*ptr)
- 	{
- 		if (*ptr == '#')
- 			break;
- 		if (!isspace((unsigned char) *ptr))
- 			return false;
- 		ptr++;
- 	}
- 
- 	/* Success! */
- 	*key_p = key;
- 	*value_p = value;
- 	return true;
- }
- 
- /*
   * See if there is a recovery command file (recovery.conf), and if so
   * read in parameters for archive recovery and XLOG streaming.
   *
!  * XXX longer term intention is to expand this to
!  * cater for additional parameters and controls
!  * possibly use a flex lexer similar to the GUC one
   */
  static void
  readRecoveryCommandFile(void)
  {
  	FILE	   *fd;
- 	char		cmdline[MAXPGPATH];
  	TimeLineID	rtli = 0;
  	bool		rtliGiven = false;
! 	bool		syntaxError = false;
  
  	fd = AllocateFile(RECOVERY_COMMAND_FILE, "r");
  	if (fd == NULL)
  	{
  		if (errno == ENOENT)
! 			return;				/* not there, so no archive recovery */
  		ereport(FATAL,
  				(errcode_for_file_access(),
  				 errmsg("could not open recovery command file \"%s\": %m",
  						RECOVERY_COMMAND_FILE)));
  	}
  
! 	/*
! 	 * Parse the file...
! 	 */
! 	while (fgets(cmdline, sizeof(cmdline), fd) != NULL)
! 	{
! 		char	   *tok1;
! 		char	   *tok2;
  
! 		if (!parseRecoveryCommandFileLine(cmdline, &tok1, &tok2))
! 		{
! 			syntaxError = true;
! 			break;
! 		}
! 		if (tok1 == NULL)
! 			continue;
! 
! 		if (strcmp(tok1, "restore_command") == 0)
  		{
! 			recoveryRestoreCommand = pstrdup(tok2);
  			ereport(DEBUG2,
  					(errmsg("restore_command = '%s'",
  							recoveryRestoreCommand)));
  		}
! 		else if (strcmp(tok1, "recovery_end_command") == 0)
  		{
! 			recoveryEndCommand = pstrdup(tok2);
  			ereport(DEBUG2,
  					(errmsg("recovery_end_command = '%s'",
  							recoveryEndCommand)));
  		}
! 		else if (strcmp(tok1, "archive_cleanup_command") == 0)
  		{
! 			archiveCleanupCommand = pstrdup(tok2);
  			ereport(DEBUG2,
  					(errmsg("archive_cleanup_command = '%s'",
  							archiveCleanupCommand)));
  		}
! 		else if (strcmp(tok1, "recovery_target_timeline") == 0)
  		{
  			rtliGiven = true;
! 			if (strcmp(tok2, "latest") == 0)
  				rtli = 0;
  			else
  			{
  				errno = 0;
! 				rtli = (TimeLineID) strtoul(tok2, NULL, 0);
  				if (errno == EINVAL || errno == ERANGE)
  					ereport(FATAL,
  							(errmsg("recovery_target_timeline is not a valid number: \"%s\"",
! 									tok2)));
  			}
  			if (rtli)
  				ereport(DEBUG2,
--- 5024,5095 ----
  }
  
  /*
   * See if there is a recovery command file (recovery.conf), and if so
   * read in parameters for archive recovery and XLOG streaming.
   *
!  * The file is parsed using the main configuration parser.
   */
  static void
  readRecoveryCommandFile(void)
  {
  	FILE	   *fd;
  	TimeLineID	rtli = 0;
  	bool		rtliGiven = false;
! 	ConfigNameValuePair item, head, tail;
  
+ 	/*
+ 	 * See if recovery.conf file is present
+ 	 */
  	fd = AllocateFile(RECOVERY_COMMAND_FILE, "r");
  	if (fd == NULL)
  	{
  		if (errno == ENOENT)
! 			return;                         /* not there, so no archive recovery */
  		ereport(FATAL,
  				(errcode_for_file_access(),
  				 errmsg("could not open recovery command file \"%s\": %m",
  						RECOVERY_COMMAND_FILE)));
  	}
  
! 	if (!ParseConfigFp(fd, RECOVERY_COMMAND_FILE, NULL, 0, FATAL, &head, &tail))
! 		goto cleanup_list;
  
! 	for (item = head; item; item = item->next)
! 	{
! 		if (strcmp(item->name, "restore_command") == 0)
  		{
! 			recoveryRestoreCommand = pstrdup(item->value);
  			ereport(DEBUG2,
  					(errmsg("restore_command = '%s'",
  							recoveryRestoreCommand)));
  		}
! 		else if (strcmp(item->name, "recovery_end_command") == 0)
  		{
! 			recoveryEndCommand = pstrdup(item->value);
  			ereport(DEBUG2,
  					(errmsg("recovery_end_command = '%s'",
  							recoveryEndCommand)));
  		}
! 		else if (strcmp(item->name, "archive_cleanup_command") == 0)
  		{
! 			archiveCleanupCommand = pstrdup(item->value);
  			ereport(DEBUG2,
  					(errmsg("archive_cleanup_command = '%s'",
  							archiveCleanupCommand)));
  		}
! 		else if (strcmp(item->name, "recovery_target_timeline") == 0)
  		{
  			rtliGiven = true;
! 			if (strcmp(item->value, "latest") == 0)
  				rtli = 0;
  			else
  			{
  				errno = 0;
! 				rtli = (TimeLineID) strtoul(item->value, NULL, 0);
  				if (errno == EINVAL || errno == ERANGE)
  					ereport(FATAL,
  							(errmsg("recovery_target_timeline is not a valid number: \"%s\"",
! 									item->value)));
  			}
  			if (rtli)
  				ereport(DEBUG2,
***************
*** 5203,5222 **** readRecoveryCommandFile(void)
  				ereport(DEBUG2,
  						(errmsg("recovery_target_timeline = latest")));
  		}
! 		else if (strcmp(tok1, "recovery_target_xid") == 0)
  		{
  			errno = 0;
! 			recoveryTargetXid = (TransactionId) strtoul(tok2, NULL, 0);
  			if (errno == EINVAL || errno == ERANGE)
  				ereport(FATAL,
  				 (errmsg("recovery_target_xid is not a valid number: \"%s\"",
! 						 tok2)));
  			ereport(DEBUG2,
  					(errmsg("recovery_target_xid = %u",
  							recoveryTargetXid)));
  			recoveryTarget = RECOVERY_TARGET_XID;
  		}
! 		else if (strcmp(tok1, "recovery_target_time") == 0)
  		{
  			/*
  			 * if recovery_target_xid specified, then this overrides
--- 5098,5117 ----
  				ereport(DEBUG2,
  						(errmsg("recovery_target_timeline = latest")));
  		}
! 		else if (strcmp(item->name, "recovery_target_xid") == 0)
  		{
  			errno = 0;
! 			recoveryTargetXid = (TransactionId) strtoul(item->value, NULL, 0);
  			if (errno == EINVAL || errno == ERANGE)
  				ereport(FATAL,
  				 (errmsg("recovery_target_xid is not a valid number: \"%s\"",
! 						 item->value)));
  			ereport(DEBUG2,
  					(errmsg("recovery_target_xid = %u",
  							recoveryTargetXid)));
  			recoveryTarget = RECOVERY_TARGET_XID;
  		}
! 		else if (strcmp(item->name, "recovery_target_time") == 0)
  		{
  			/*
  			 * if recovery_target_xid specified, then this overrides
***************
*** 5231,5274 **** readRecoveryCommandFile(void)
  			 */
  			recoveryTargetTime =
  				DatumGetTimestampTz(DirectFunctionCall3(timestamptz_in,
! 														CStringGetDatum(tok2),
  												ObjectIdGetDatum(InvalidOid),
  														Int32GetDatum(-1)));
  			ereport(DEBUG2,
  					(errmsg("recovery_target_time = '%s'",
  							timestamptz_to_str(recoveryTargetTime))));
  		}
! 		else if (strcmp(tok1, "recovery_target_inclusive") == 0)
  		{
  			/*
  			 * does nothing if a recovery_target is not also set
  			 */
! 			if (!parse_bool(tok2, &recoveryTargetInclusive))
  				ereport(ERROR,
  						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  						 errmsg("parameter \"%s\" requires a Boolean value", "recovery_target_inclusive")));
  			ereport(DEBUG2,
! 					(errmsg("recovery_target_inclusive = %s", tok2)));
  		}
! 		else if (strcmp(tok1, "standby_mode") == 0)
  		{
! 			if (!parse_bool(tok2, &StandbyMode))
  				ereport(ERROR,
  						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  						 errmsg("parameter \"%s\" requires a Boolean value", "standby_mode")));
  			ereport(DEBUG2,
! 					(errmsg("standby_mode = '%s'", tok2)));
  		}
! 		else if (strcmp(tok1, "primary_conninfo") == 0)
  		{
! 			PrimaryConnInfo = pstrdup(tok2);
  			ereport(DEBUG2,
  					(errmsg("primary_conninfo = '%s'",
  							PrimaryConnInfo)));
  		}
! 		else if (strcmp(tok1, "trigger_file") == 0)
  		{
! 			TriggerFile = pstrdup(tok2);
  			ereport(DEBUG2,
  					(errmsg("trigger_file = '%s'",
  							TriggerFile)));
--- 5126,5169 ----
  			 */
  			recoveryTargetTime =
  				DatumGetTimestampTz(DirectFunctionCall3(timestamptz_in,
! 														CStringGetDatum(item->value),
  												ObjectIdGetDatum(InvalidOid),
  														Int32GetDatum(-1)));
  			ereport(DEBUG2,
  					(errmsg("recovery_target_time = '%s'",
  							timestamptz_to_str(recoveryTargetTime))));
  		}
! 		else if (strcmp(item->name, "recovery_target_inclusive") == 0)
  		{
  			/*
  			 * does nothing if a recovery_target is not also set
  			 */
! 			if (!parse_bool(item->value, &recoveryTargetInclusive))
  				ereport(ERROR,
  						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  						 errmsg("parameter \"%s\" requires a Boolean value", "recovery_target_inclusive")));
  			ereport(DEBUG2,
! 					(errmsg("recovery_target_inclusive = %s", item->value)));
  		}
! 		else if (strcmp(item->name, "standby_mode") == 0)
  		{
! 			if (!parse_bool(item->value, &StandbyMode))
  				ereport(ERROR,
  						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  						 errmsg("parameter \"%s\" requires a Boolean value", "standby_mode")));
  			ereport(DEBUG2,
! 					(errmsg("standby_mode = '%s'", item->value)));
  		}
! 		else if (strcmp(item->name, "primary_conninfo") == 0)
  		{
! 			PrimaryConnInfo = pstrdup(item->value);
  			ereport(DEBUG2,
  					(errmsg("primary_conninfo = '%s'",
  							PrimaryConnInfo)));
  		}
! 		else if (strcmp(item->name, "trigger_file") == 0)
  		{
! 			TriggerFile = pstrdup(item->value);
  			ereport(DEBUG2,
  					(errmsg("trigger_file = '%s'",
  							TriggerFile)));
***************
*** 5276,5292 **** readRecoveryCommandFile(void)
  		else
  			ereport(FATAL,
  					(errmsg("unrecognized recovery parameter \"%s\"",
! 							tok1)));
  	}
  
- 	FreeFile(fd);
- 
- 	if (syntaxError)
- 		ereport(FATAL,
- 				(errmsg("syntax error in recovery command file: %s",
- 						cmdline),
- 			  errhint("Lines should have the format parameter = 'value'.")));
- 
  	/*
  	 * Check for compulsory parameters
  	 */
--- 5171,5179 ----
  		else
  			ereport(FATAL,
  					(errmsg("unrecognized recovery parameter \"%s\"",
! 							item->name)));
  	}
  
  	/*
  	 * Check for compulsory parameters
  	 */
***************
*** 5332,5337 **** readRecoveryCommandFile(void)
--- 5219,5228 ----
  			recoveryTargetTLI = findNewestTimeLine(recoveryTargetTLI);
  		}
  	}
+ 
+  cleanup_list:
+ 	free_name_value_list(head);
+ 	FreeFile(fd);
  }
  
  /*
*** a/src/backend/utils/misc/guc-file.l
--- b/src/backend/utils/misc/guc-file.l
***************
*** 35,59 **** enum {
  	GUC_ERROR = 100
  };
  
- struct name_value_pair
- {
- 	char       *name;
- 	char       *value;
- 	char	   *filename;
- 	int			sourceline;
- 	struct name_value_pair *next;
- };
- 
  static unsigned int ConfigFileLineno;
  
  /* flex fails to supply a prototype for yylex, so provide one */
  int GUC_yylex(void);
  
- static bool ParseConfigFile(const char *config_file, const char *calling_file,
- 							int depth, int elevel,
- 							struct name_value_pair **head_p,
- 							struct name_value_pair **tail_p);
- static void free_name_value_list(struct name_value_pair * list);
  static char *GUC_scanstr(const char *s);
  
  %}
--- 35,45 ----
***************
*** 118,124 **** void
  ProcessConfigFile(GucContext context)
  {
  	int			elevel;
! 	struct name_value_pair *item, *head, *tail;
  	char	   *cvc = NULL;
  	struct config_string *cvc_struct;
  	const char *envvar;
--- 104,110 ----
  ProcessConfigFile(GucContext context)
  {
  	int			elevel;
! 	ConfigNameValuePair item, head, tail;
  	char	   *cvc = NULL;
  	struct config_string *cvc_struct;
  	const char *envvar;
***************
*** 356,400 **** ProcessConfigFile(GucContext context)
  		free(cvc);
  }
  
- 
  /*
!  * Read and parse a single configuration file.  This function recurses
!  * to handle "include" directives.
!  *
!  * Input parameters:
!  *	config_file: absolute or relative path of file to read
!  *	calling_file: absolute path of file containing the "include" directive,
!  *		or NULL at outer level (config_file must be absolute at outer level)
!  *	depth: recursion depth (used only to prevent infinite recursion)
!  *	elevel: error logging level determined by ProcessConfigFile()
!  * Output parameters:
!  *	head_p, tail_p: head and tail of linked list of name/value pairs
!  *
!  * *head_p and *tail_p must be initialized to NULL before calling the outer
!  * recursion level.  On exit, they contain a list of name-value pairs read
!  * from the input file(s).
!  *
!  * Returns TRUE if successful, FALSE if an error occurred.  The error has
!  * already been ereport'd, it is only necessary for the caller to clean up
!  * its own state and release the name/value pairs list.
!  *
!  * Note: if elevel >= ERROR then an error will not return control to the
!  * caller, and internal state such as open files will not be cleaned up.
!  * This case occurs only during postmaster or standalone-backend startup,
!  * where an error will lead to immediate process exit anyway; so there is
!  * no point in contorting the code so it can clean up nicely.
   */
! static bool
  ParseConfigFile(const char *config_file, const char *calling_file,
  				int depth, int elevel,
  				struct name_value_pair **head_p,
  				struct name_value_pair **tail_p)
  {
  	bool		OK = true;
- 	char		abs_path[MAXPGPATH];
  	FILE	   *fp;
! 	YY_BUFFER_STATE lex_buffer;
! 	int			token;
  
  	/*
  	 * Reject too-deep include nesting depth.  This is just a safety check
--- 342,360 ----
  		free(cvc);
  }
  
  /*
!  * See next function for details. This one will just work with a config_file
!  * name rather than an already opened File Descriptor
   */
! bool
  ParseConfigFile(const char *config_file, const char *calling_file,
  				int depth, int elevel,
  				struct name_value_pair **head_p,
  				struct name_value_pair **tail_p)
  {
  	bool		OK = true;
  	FILE	   *fp;
! 	char		abs_path[MAXPGPATH];
  
  	/*
  	 * Reject too-deep include nesting depth.  This is just a safety check
***************
*** 416,427 **** ParseConfigFile(const char *config_file, const char *calling_file,
  	 */
  	if (!is_absolute_path(config_file))
  	{
! 		Assert(calling_file != NULL);
! 		strlcpy(abs_path, calling_file, sizeof(abs_path));
! 		get_parent_directory(abs_path);
! 		join_path_components(abs_path, abs_path, config_file);
! 		canonicalize_path(abs_path);
! 		config_file = abs_path;
  	}
  
  	fp = AllocateFile(config_file, "r");
--- 376,398 ----
  	 */
  	if (!is_absolute_path(config_file))
  	{
! 		if (calling_file != NULL)
! 		{
! 			strlcpy(abs_path, calling_file, sizeof(abs_path));
! 			get_parent_directory(abs_path);
! 			join_path_components(abs_path, abs_path, config_file);
! 			canonicalize_path(abs_path);
! 			config_file = abs_path;
! 		}
! 		else
! 		{
! 			/*
! 			 * calling_file is NULL, we make an absolute path from $PGDATA
! 			 */
! 			join_path_components(abs_path, data_directory, config_file);
! 			canonicalize_path(abs_path);
! 			config_file = abs_path;
! 		}
  	}
  
  	fp = AllocateFile(config_file, "r");
***************
*** 434,439 **** ParseConfigFile(const char *config_file, const char *calling_file,
--- 405,456 ----
  		return false;
  	}
  
+ 	OK = ParseConfigFp(fp, config_file, calling_file,
+ 					   depth, elevel, head_p, tail_p);
+ 
+ 	FreeFile(fp);
+ 
+ 	return OK;
+ }
+ 
+ /*
+  * Read and parse a single configuration file.  This function recurses
+  * to handle "include" directives.
+  *
+  * Input parameters:
+  *	fp: file pointer from AllocateFile for the configuration file to parse
+  *	config_file: absolute or relative path of file to read
+  *	calling_file: absolute path of file containing the "include" directive,
+  *		or NULL at outer level (config_file must be absolute at outer level)
+  *	depth: recursion depth (used only to prevent infinite recursion)
+  *	elevel: error logging level determined by ProcessConfigFile()
+  * Output parameters:
+  *	head_p, tail_p: head and tail of linked list of name/value pairs
+  *
+  * *head_p and *tail_p must be initialized to NULL before calling the outer
+  * recursion level.  On exit, they contain a list of name-value pairs read
+  * from the input file(s).
+  *
+  * Returns TRUE if successful, FALSE if an error occurred.  The error has
+  * already been ereport'd, it is only necessary for the caller to clean up
+  * its own state and release the name/value pairs list.
+  *
+  * Note: if elevel >= ERROR then an error will not return control to the
+  * caller, and internal state such as open files will not be cleaned up.
+  * This case occurs only during postmaster or standalone-backend startup,
+  * where an error will lead to immediate process exit anyway; so there is
+  * no point in contorting the code so it can clean up nicely.
+  */
+ bool
+ ParseConfigFp(FILE *fp, const char *config_file, const char *calling_file,
+ 				int depth, int elevel,
+ 				struct name_value_pair **head_p,
+ 				struct name_value_pair **tail_p)
+ {
+ 	bool		OK = true;
+ 	YY_BUFFER_STATE lex_buffer;
+ 	int			token;
+ 
  	/*
  	 * Parse
  	 */
***************
*** 579,585 **** ParseConfigFile(const char *config_file, const char *calling_file,
  
  cleanup_exit:
  	yy_delete_buffer(lex_buffer);
- 	FreeFile(fp);
  	return OK;
  }
  
--- 596,601 ----
***************
*** 587,593 **** cleanup_exit:
  /*
   * Free a list of name/value pairs, including the names and the values
   */
! static void
  free_name_value_list(struct name_value_pair *list)
  {
  	struct name_value_pair *item;
--- 603,609 ----
  /*
   * Free a list of name/value pairs, including the names and the values
   */
! void
  free_name_value_list(struct name_value_pair *list)
  {
  	struct name_value_pair *item;
*** a/src/backend/utils/misc/guc.c
--- b/src/backend/utils/misc/guc.c
***************
*** 403,408 **** int			tcp_keepalives_interval;
--- 403,414 ----
  int			tcp_keepalives_count;
  
  /*
+  * This is part of the dummy variables, but it needs to be exported so that
+  * guc-file.l can create $PGDATA/some.conf out of relative path.
+  */
+ char *data_directory;
+ 
+ /*
   * These variables are all dummies that don't do anything, except in some
   * cases provide the value for SHOW to display.  The real state is elsewhere
   * and is kept in sync by assign_hooks.
***************
*** 426,432 **** static char *timezone_string;
  static char *log_timezone_string;
  static char *timezone_abbreviations_string;
  static char *XactIsoLevel_string;
- static char *data_directory;
  static char *custom_variable_classes;
  static int	max_function_args;
  static int	max_index_keys;
--- 432,437 ----
*** a/src/include/utils/guc.h
--- b/src/include/utils/guc.h
***************
*** 17,23 ****
  #include "tcop/dest.h"
  #include "utils/array.h"
  
- 
  /*
   * Certain options can only be set at certain times. The rules are
   * like this:
--- 17,22 ----
***************
*** 97,102 **** typedef enum
--- 96,127 ----
  } GucSource;
  
  /*
+  * Parsing the configuation file will return a list of name-value pairs
+  */
+ struct name_value_pair
+ {
+ 	char       *name;
+ 	char       *value;
+ 	char	   *filename;
+ 	int			sourceline;
+ 	struct name_value_pair *next;
+ } name_value_pair;
+ 
+ typedef struct name_value_pair *ConfigNameValuePair;
+ 
+ bool ParseConfigFile(const char *config_file, const char *calling_file,
+ 							int depth, int elevel,
+ 							ConfigNameValuePair *head_p,
+ 							ConfigNameValuePair *tail_p);
+ 
+ bool ParseConfigFp(FILE *fp, const char *config_file, const char *calling_file,
+ 							int depth, int elevel,
+ 							ConfigNameValuePair *head_p,
+ 							ConfigNameValuePair *tail_p);
+ 
+ void free_name_value_list(struct name_value_pair * list);
+ 
+ /*
   * Enum values are made up of an array of name-value pairs
   */
  struct config_enum_entry
***************
*** 187,192 **** extern int	tcp_keepalives_idle;
--- 212,220 ----
  extern int	tcp_keepalives_interval;
  extern int	tcp_keepalives_count;
  
+ /* guc-file.l needs that to make absolute paths from relative ones */
+ extern char *data_directory;
+ 
  extern void SetConfigOption(const char *name, const char *value,
  				GucContext context, GucSource source);
  
#125Itagaki Takahiro
itagaki.takahiro@gmail.com
In reply to: Dimitri Fontaine (#124)
Re: Extensions, this time with a patch

On Thu, Nov 25, 2010 at 05:02, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:

Something like the attached, version 5 of the patch? I've been using the
function name ParseConfigFp because the internal parameter was called fp
in the previous function body. I suppose that could easily be changed at
commit time if necessary.

Thanks. I'll move the patch to Ready for Committer.

Here is a list of items for committers, including only cosmetic changes.

- Comments in recovery.conf.sample needs to be adjusted.
# (The quotes around the value are NOT optional, but the "=" is.)
It seems to be the only description about quotes are not omissible before.
- We might not need to check the result of ParseConfigFp() because
it always raises FATAL on errors.
- We could remove the unused argument "calling_file" in ParseConfigFp().
- I feel "struct name_value_pair" and "ConfigNameValuePair" a bit
redundant names. I'd prefer something like ConfigVariable.
- "fp" might be a better name than FILE *fd. There are 4 usages in xlog.c.

Extensions will need the support for custom_variable_classes as it is
done now, and as you say, the recovery will just error out. You have to
clean your recovery.conf file then try again (I just tried and confirm).

I personally don't see any harm to have the features in all currently
known uses-cases, and I don't see any point in walking an extra mile
here to remove a feature in some cases.

Sure. We will leave them.

--
Itagaki Takahiro

#126Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Itagaki Takahiro (#125)
Re: Extensions, this time with a patch

Itagaki Takahiro <itagaki.takahiro@gmail.com> writes:

Thanks. I'll move the patch to Ready for Committer.

Thanks!
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#127Robert Haas
robertmhaas@gmail.com
In reply to: Dimitri Fontaine (#126)
Re: Extensions, this time with a patch

On Thu, Nov 25, 2010 at 4:06 PM, Dimitri Fontaine
<dimitri@2ndquadrant.fr> wrote:

Itagaki Takahiro <itagaki.takahiro@gmail.com> writes:

Thanks. I'll move the patch to Ready for Committer.

Thanks!

I have committed the cfparser patch to which the above comments refer.
One patch per thread makes things easier!

I adopted most of Itagaki Takahiro's suggestions, which were very helpful.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#128Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Robert Haas (#127)
Re: Extensions, this time with a patch

Robert Haas <robertmhaas@gmail.com> writes:

I have committed the cfparser patch to which the above comments refer.

Excellent, thank you! On to merging that into the extension's main
branch, will still wait until after pg_execute_sql_file() is in to
produce extension.v15.patch, unless advised otherwise.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support