
#simulation of the real icondb.icn
#using the C mysql and postgresql interfaces for Icon 9.4.
#use with cgi.icn (instead of web.icn)
#until loadfuncpp becomes reliable

#WARNING: can only connect to one mysql and one postgresql database at a time

#CS 2008/7/27


link io

#the C interface
procedure _mysqldb(arg[])
    return ( _mysqldb := pathload("mysqldb.so","mysqldb") )!arg
end

procedure _postgresqldb(arg[])
    return ( _postgresqldb := pathload("postgresqldb.so","postgresqldb") )!arg
end

#simulated external value
record database_handle(connection, c_interface)

#simulated mysql connection procedure
procedure _connectmysql(dbname, user, pwd, host, port)
	local connection, result
	icondb_error := &null
	connection := [dbname, user, pwd]
	if put(connection, \host) then put(connection, \port)
	result := _mysqldb(connection)
	if /result then return database_handle(connection, _mysqldb)
	icondb_error := result
	fail
end

#simulated postgresql connection procedure
procedure _connectpostgresql(dbname, user, pwd, host, port)
	local connection, result
	icondb_error := &null
	connection := [dbname, user, pwd]
	if put(connection, \host) then put(connection, \port)
	result := _postgresqldb(connection)
	if /result then return database_handle(connection, _postgresqldb)
	icondb_error := result
	fail
end

global icondb_error

#icondb returns a connection procedure for a known kind of dbms
#which may then be called following the pattern 
#dbhandle := connect(dbname, user, pwd, host, port)
#where host and port are optional

procedure icondb(kind)
	case kind of {
		"mysql" : return _connectmysql
		"postgresql" : return _connectpostgresql
		default : stop("icondb: unknown dbms\nerror: ", image(kind)) | fail
	}
end

procedure dbclose(db)
	if type(db) ~== "database_handle" then 
		stop("dbclose: not a database handle: ", image(db))
	icondb_error := &null
	db.c_interface()
	return
end

procedure dbquery(db, query, constructor)
	local result, rec
	if type(db) ~== "database_handle" then 
		stop("dbquery: not a database handle: ", image(db))
	case type(constructor) of {
		"null" : 
			&null
		"procedure" :
			image(constructor) ? {
				="record constructor" | 
					stop("dbquery: not a record constructor: ", image(constructor))
			}
		default : 
			stop("dbquery: not a record constructor: ", image(constructor))
	}
	icondb_error := &null
	result := db.c_interface(query)
    case type(result) of {
        "integer" | "null" : return result
        "list" : case type(result[1]) of {
            "list":	
				if /constructor then
					suspend !result
				else {
					if result[1] & *constructor() ~= *result[1] then
						stop("dbquery: ",image(constructor)," needs at least ",*rec[1]," fields." )
					suspend constructor!!result
				}
			"integer" : icondb_error := result
        }
    }	
end

