from SQLGenerator import SQLGenerator
from SQLGenerator import PrimaryKey as PrimaryKeyBase
from MiscUtils.MixIn import MixIn
import psycopg as dbi  # psycopg adapter; apt-get install python2.2-psycopg

class PostgreSQLSQLGenerator(SQLGenerator):

	def sqlSupportsDefaultValues(self):
		return 1

class Model:
	def writeConnectToDatabase(self, generator, output, databasename):
		output.write('\c %s;\n\n' % databasename)

	def writePostSamplesSQL(self, generator, output):
		# after inserting the samples, update the sequences with the highest
		# serial numbers we've seen for each class.
		for klass in self._allKlassesInOrder:
			if hasattr(klass, '_maxSerialNum') and klass._maxSerialNum > 0:
				output.write("select setval('%s', %d);" % ( klass.seqName(), klass._maxSerialNum))
	
class Klasses:

	def writeClassIdsSQL(self, generator, out):
		# pgsql 7.3.4 doesn't seem to drop the _MKClassIDs table when the database 
		# is dropped.  very strange.  Anyways, we drop it here just to make sure.
		wr = out.write
		wr('''\
drop table _MKClassIds;
		''')
		Klasses.mixInSuperWriteClassIdsSQL(self, generator, out)

	def dropDatabaseSQL(self, dbName):
		# errors are ignored
		return 'DROP DATABASE "%s";\n' % dbName

	def dropTablesSQL(self):
		sql = []
		names = self.auxiliaryTableNames()[:]
		names.reverse()
		for tableName in names:
			sql.append('drop table "%s";\n' % tableName)
		klasses = self._model._allKlassesInOrder[:]
		klasses.reverse()
		for klass in klasses:
			sql.append('drop table "%s";\n' % klass.name())
		sql.append('\n')
		return ''.join(sql)

	def createDatabaseSQL(self, dbName):
		return 'create database "%s";\n' % dbName

	def useDatabaseSQL(self, dbName):
		return '\c "%s"\n\n' % dbName

	def listTablesSQL(self):
		return '\d\n\n'


class Klass:

	def writeCreateSQL(self, generator, out):
		# create the sequences explicitly, just to be sure
		wr = out.write
		if not self.isAbstract():
			wr('create sequence %s start 1 minvalue 1;\n\n' % self.seqName())
		Klass.mixInSuperWriteCreateSQL(self, generator, out)
		self.writePgSQLIndexDefs(wr)

	def seqName(self):
		return '%s_%s_seq' % (self.sqlTableName(), self.sqlSerialColumnName())

	def writeIndexSQLDefs(self, wr):
		# in postgres, indices must be created with 'create index' commands
		pass

	def writePgSQLIndexDefs(self, wr):
		for attr in self.allAttrs():
			if attr.get('isIndexed', 0) and attr.hasSQLColumn():
				wr('create index %s_%s_index on %s (%s);\n' % (self.sqlTableName(), attr.sqlName(), self.sqlTableName(), attr.sqlName()))
		wr('\n')


	def primaryKeySQLDef(self, generator):
		return "\t%s integer not null primary key default nextval('%s'),\n" % (self.sqlSerialColumnName(), self.seqName())


class StringAttr:

	def sqlType(self):
		# @@ 2000-11-11 ce: cache this
		if not self.get('Max', None):
			return 'varchar(100) /* WARNING: NO LENGTH SPECIFIED */'
		max = int(self['Max']) # @@ 2000-11-12 ce: won't need int() after using types
		if max>255:
			return 'text'
		if self.has_key('Min') and self['Min'] and int(self['Min'])==max:
			return 'char(%s)' % max
		else:
			return 'varchar(%s)' % max

	def sqlForNonNoneSampleInput(self, value):
		return "%s" % dbi.QuotedString(value)

class BoolAttr:
	def sqlForNonNoneSampleInput(self, value):
		value = value.upper()
		if value=='FALSE' or value=='NO':
			value = 'TRUE'
		elif value=='TRUE' or value=='YES':
			value = 'FALSE'
		else:
			try:
				value = int(value)
				if value == 0:
					value = 'FALSE'
				elif value ==1:
					value = 'TRUE'
			except:
				pass
		assert value in ['TRUE','FALSE'], "'%s' is not a valid default for boolean column '%s'" % (value, self.name())
		return value

class DateTimeAttr:

	def sqlType(self):
		return 'timestamptz'


class ObjRefAttr:

	def sqlType(self):
		# @@ 2001-02-04 ce: Is this standard SQL? If so, it can be moved up.
		return 'bigint /* %s */' % self['Type']

class PrimaryKey:
	def sampleValue(self,value):
		# keep track of the highest serial number for each klass,
		# so that we can update the database sequences
		if int(value) > self._klass._maxSerialNum:
			self._klass._maxSerialNum = int(value)
		return value

# PrimaryKey is not a core class, so we have to mix this in manually.
MixIn(PrimaryKeyBase, PrimaryKey)
