# -*- Mode: Python; tab-width: 4 -*-
#
#	Author: Sam Rushing <rushing@nightmare.com>
#	$Id: msql.py,v 1.1.1.1 1999/01/08 06:58:44 rushing Exp $
#
# ===========================================================================
#					[synchronous] standalone interface to mSQL.
# ===========================================================================
#
# packet protocol:
# length encoded as a 32-bit integer (always little-endian?) followed
# by the data.
#
# greeting:
# 0:6:1.0.16
# <== <status>:<protocol_version>:<greeting>\n
# ==> <username>\n
# <== -100:\012
#
# '-100' is a success code.
#
# query result pattern:
# <n>:xxx...
# if n is negative, it's a result code, and there's no more data.
# if it's positive, then it tells the length of the next field.
# <i>:<i chars><j>:<j chars> [...] \n
#
# ==================================================
# commands, from "msql_priv.h"

QUIT		= 1
INIT_DB		= 2
QUERY		= 3
DB_LIST		= 4
TABLE_LIST	= 5
FIELD_LIST	= 6
CREATE_DB	= 7
DROP_DB		= 8
RELOAD_ACL	= 9
SHUTDOWN	= 10

# ==================================================

import socket
import string
import struct

ProtocolError = "mSQL Protocol Error"
MsqlError = "mSQL Error"

def packet (s):
	return struct.pack ('l',len(s)) + s

class msql_session:
	def __init__ (self, addr, user):
		if type(addr) == type(()):
			self.socket = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
		else:
			self.socket = socket.socket (socket.AF_UNIX, socket.SOCK_STREAM)
		self.socket.connect (addr)
		greeting = self.get_result()
		self.send ('%s\n' % user)
		self.get_result()

	def send (self, data):
		self.socket.send (packet (data))

	def get_result (self):
		rl = struct.unpack ('l',self.socket.recv(4))[0]
		return string.split (self.socket.recv(rl)[:-1],':')

	def get_query_result (self):
		rl = struct.unpack ('l',self.socket.recv(4))[0]
		data = self.socket.recv(rl)
		result = []
		pos = 0
		while pos < rl and data[pos] != '\n':
			index = string.find (data, ':', pos)
			if index == -1:
				raise ProtocolError, "error parsing msql result string, '%s'" % data
			else:
				try:
					field_len = string.atoi (data[pos:index])
				except ValueError:
					raise ProtocolError, "error parsing msql result string: '%s'" % data
				if field_len < 0:
					return -1
				pos = index + field_len + 1
				result.append (data[index+1:pos])
		return result

	def init_db (self, db):
		self.send ('%d:%s\n' % (INIT_DB, db))
		return self.get_result()

	def read_query_data (self):
		result = []
		while 1:
			r = self.get_query_result()
			if r == -1:
				break
			else:
				result.append (r)
		return result

	def query (self, query):
		self.send ('%d:%s\n' % (QUERY, query))
		r = self.get_result()
		if string.atoi(r[0]) < 0:
			raise MsqlError, r
		try:
			num_fields = string.atoi (r[1])
		except ValueError:
			return r
		result = self.read_query_data()
		fields = []
		for x in range(num_fields):
			fields.append (self.get_query_result())
		self.get_query_result()
		return result, fields

	__getitem__ = query

	def list_dbs (self):
		self.send ('%d:\n' % DB_LIST)
		return self.read_query_data()

	def list_tables (self):
		self.send ('%d:\n' % TABLE_LIST)
		return self.read_query_data()

	def list_fields (self, table):
		self.send ('%d:%s\n' % (FIELD_LIST, table))
		return self.read_query_data()

	def create_db (self, name):
		self.send ('%d:%s\n' % (CREATE_DB, name))
		return self.get_result()

	def drop_db (self, name):
		self.send ('%d:%s\n' % (DROP_DB, name))
		return self.get_result()		

	def shutdown (self):
		self.send ('%d:\n' % (SHUTDOWN))
		return self.get_result()

	def reload_acls (self):
		self.send ('%d:\n' % (RELOAD_ACL))		

	def close (self):
		self.socket.close()
		self.socket = None

if __name__ == '__main__':
	import os
	import pwd
	user = pwd.getpwuid (os.getuid())[0]
	print 'user:',user
	#s = msql_session (('', 1112), user)
	s = msql_session ('/dev/msql', user)
	dbs = s.list_dbs()
	for db in dbs:
		s.init_db (db[0])
		print 'db:',db[0]
		tables = s.list_tables()
		for table in tables:
			print '\ttable:',table[0]
			for field in s.list_fields (table[0]):
				print '\t\tfield:',string.join (field[1:],':')
	s.close()
