summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Jamroot.jam0
-rw-r--r--project2/Jamfile.jam20
-rw-r--r--project2/envproc.cpp78
-rw-r--r--project2/envproc.h33
-rw-r--r--project2/p2web.cpp278
-rw-r--r--project2/p2webMain.cpp42
-rw-r--r--project2/rdbmsDataSource.cpp18
-rw-r--r--project2/rdbmsDataSource.h23
-rw-r--r--project2/sourceObject.cpp8
-rw-r--r--project2/sourceObject.h13
-rw-r--r--project2/sqlView.cpp78
-rw-r--r--project2/sqlView.h23
-rw-r--r--project2/ustring.h9
13 files changed, 623 insertions, 0 deletions
diff --git a/Jamroot.jam b/Jamroot.jam
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Jamroot.jam
diff --git a/project2/Jamfile.jam b/project2/Jamfile.jam
new file mode 100644
index 0000000..7505f92
--- /dev/null
+++ b/project2/Jamfile.jam
@@ -0,0 +1,20 @@
+alias libxml2 : : : :
+ <cflags>"`pkg-config --cflags libxml-2.0`"
+ <linkflags>"`pkg-config --libs libxml-2.0`" ;
+
+lib fcgi : : <name>fcgi ;
+lib odbc : : <name>odbc ;
+
+exe p2web :
+ p2webMain.cpp
+ sourceObject.cpp
+ rdbmsDataSource.cpp
+ sqlView.cpp
+ envproc.cpp
+ libxml2
+ ../libmisc
+ ../libodbcpp :
+ <include>../libmisc/
+ <include>../libodbcpp/
+ <library>odbc
+ <library>fcgi ;
diff --git a/project2/envproc.cpp b/project2/envproc.cpp
new file mode 100644
index 0000000..0313c0f
--- /dev/null
+++ b/project2/envproc.cpp
@@ -0,0 +1,78 @@
+#include "envproc.h"
+#include "rdbmsDataSource.h"
+#include "sqlView.h"
+#include <map>
+#include <libxml/tree.h>
+#include <libxml/xinclude.h>
+#include <boost/shared_ptr.hpp>
+#include <boost/any.hpp>
+#include <boost/foreach.hpp>
+
+void
+EnvironmentProcessor::init()
+{
+ elems = regexExtractSet("/([^/?]+)", request_uri.c_str());
+ params = regexExtractMulti("&?([^=]+)=?([^&]*)", query_string.c_str(), 2);
+ page = elems.size() > 0 ? elems[0] : "index";
+}
+
+template <class X, class Y>
+void
+collectAll(std::map<ustring, Y> & objs, xmlNodePtr node, const ustring & name)
+{
+ if (name == node->name) {
+ fprintf(stderr, "Found a %s\n", name.c_str());
+ try {
+ objs[xmlGetProp(node, BAD_CAST "name")] = Y(new X(node));
+ fprintf(stderr, "Load succeeded\n");
+ }
+ catch (const std::exception & e) {
+ // Assume the XML node is what we thought it was
+ fprintf(stderr, "Load failed (%s)\n", e.what());
+ }
+ catch (...) {
+ // Assume the XML node is what we thought it was
+ fprintf(stderr, "Load failed\n");
+ }
+ }
+ else {
+ for (xmlNodePtr child = node->children; child; child = child->next) {
+ collectAll<X, Y>(objs, child, name);
+ }
+ }
+}
+
+xmlDocPtr
+EnvironmentProcessor::process()
+{
+ xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
+ xmlDocPtr pageXDoc = xmlReadFile(("present/" + page + ".xml").c_str(), NULL, XML_PARSE_XINCLUDE);
+ while (xmlXIncludeProcess(pageXDoc) > 0);
+ xmlNodePtr root = xmlDocGetRootElement(pageXDoc);
+ // Collect datasources
+ RdbmsDataSources rdbmsDataSources;
+ collectAll<_RdbmsDataSource>(rdbmsDataSources, root, BAD_CAST "rdbmsdatasource");
+ // Collect views
+ SqlViews sqlViews;
+ collectAll<_SqlView>(sqlViews, root, BAD_CAST "sqlview");
+ //
+ xmlNodePtr root_node = xmlNewNode(NULL, xmlGetProp(root, BAD_CAST "root"));
+ try {
+ BOOST_FOREACH(SqlViews::value_type s, sqlViews) {
+ s.second->execute(rdbmsDataSources, root_node);
+ }
+ }
+ catch (...) {
+ }
+ xmlDocSetRootElement(doc, root_node);
+ char * buf;
+ if (asprintf(&buf, "type=\"text/xsl\" href=\"%s\"",
+ xmlGetProp(root, BAD_CAST "style")) > 0) {
+ xmlAddPrevSibling(root_node,
+ xmlNewDocPI(doc, BAD_CAST "xml-stylesheet", BAD_CAST buf));
+ }
+ free(buf);
+ xmlFreeDoc(pageXDoc);
+ return doc;
+}
+
diff --git a/project2/envproc.h b/project2/envproc.h
new file mode 100644
index 0000000..64e13c1
--- /dev/null
+++ b/project2/envproc.h
@@ -0,0 +1,33 @@
+#ifndef ENVPROC_H
+#define ENVPROC_H
+
+#include <string>
+#include <libxml/tree.h>
+#include "regexex.h"
+
+class EnvironmentProcessor {
+ public:
+ template <class getenvFunc>
+ EnvironmentProcessor(const getenvFunc & getenv) :
+ request_uri(getenv("REQUEST_URI")),
+ query_string(getenv("QUERY_STRING")),
+ http_host(getenv("HTTP_HOST")),
+ request_method(getenv("REQUEST_METHOD"))
+ {
+ init();
+ }
+ virtual xmlDoc * process();
+ private:
+ void init();
+
+ std::string request_uri;
+ std::string query_string;
+ std::string http_host;
+ std::string request_method;
+
+ StringSet elems;
+ RegMultiMatch params;
+ std::string page;
+};
+
+#endif
diff --git a/project2/p2web.cpp b/project2/p2web.cpp
new file mode 100644
index 0000000..3d1b320
--- /dev/null
+++ b/project2/p2web.cpp
@@ -0,0 +1,278 @@
+#include <stdio.h>
+#include <string.h>
+#include <map>
+#include <stdlib.h>
+#include <libxml/tree.h>
+#include "connection.h"
+#include "selectcommand.h"
+#include "column.h"
+#include "smartpointer.h"
+#include "dsn.h"
+#include "xml.h"
+#include "regexex.h"
+
+template <class X>
+class Handle : public X, public IsRefCounted {
+ public:
+ template <class P1>
+ Handle(const P1 & p1) : X(p1)
+ {
+ }
+ Handle()
+ {
+ }
+};
+
+typedef Handle<ODBC::Connection> ConnHandle;
+typedef SmartPointer<ConnHandle> ConnHandlePtr;
+typedef std::map<int, ConnHandlePtr> ConnectionPool;
+
+void bindQueryParams(ODBC::Connection & db, ODBC::SelectCommand & sel, int pqid, ODBC::SelectCommand * parent,
+ const StringSet & elems, const RegMultiMatch & qs);
+
+void
+addQueryResultsToXml(ODBC::Connection & wdb, ConnectionPool cp, xmlNode * root_node, int dsn, const ODBC::String & sql,
+ const ODBC::String & name, const ODBC::String & recordName, int pqid, ODBC::SelectCommand * parent,
+ const StringSet & elems, const RegMultiMatch & qs)
+{
+ typedef std::map<ODBC::String, xmlNode *> Columns;
+ xmlNode * resultXml = xmlNewChild(root_node, NULL, name.c_str(), NULL);
+ ODBC::SelectCommand query(*cp[dsn], sql);
+ bindQueryParams(wdb, query, pqid, parent, elems, qs);
+ while (query.fetch()) {
+ Columns columns;
+ unsigned int cols = query.columnCount();
+ xmlNode * record = xmlNewChild(resultXml, NULL, recordName.c_str(), NULL);
+ for (unsigned int col = 0; col < cols; col += 1) {
+ const unsigned char * nameattr = query[col].name.c_str();
+ unsigned char * name, * attr = NULL;
+ switch (sscanf((const char *)nameattr, "%a[^_]_%as", &name, &attr))
+ {
+ case 0:
+ return; // Make me an exception
+ break;
+ case 1:
+ attr = NULL;
+ break;
+ }
+ char * buf = NULL;
+ query[col].writeToBuf(&buf);
+ if (buf) {
+ if (attr) {
+ if (strcmp((const char *)attr, ".") == 0) {
+ xmlNewProp(record, name, BAD_CAST buf);
+ }
+ else {
+ Columns::iterator i = columns.find(attr);
+ if (i != columns.end()) {
+ xmlNewProp(i->second, name, BAD_CAST buf);
+ }
+ }
+ }
+ else if (strcmp((const char *)name, "value") == 0) {
+ xmlNodeAddContent(record, BAD_CAST buf);
+ }
+ else {
+ columns[name] = xmlNewTextChild(record, NULL, name, BAD_CAST buf);
+ }
+ free(buf);
+ }
+ free(name);
+ free(attr);
+ }
+ ODBC::SelectCommand queries(wdb,
+ "SELECT d.id AS dsn, q.id AS query, q.sql, q.name, q.recordname, pq.id \
+ FROM dsn d, page_query pq, query q \
+ WHERE pq.parent = ? \
+ AND q.id = pq.query \
+ AND d.id = q.dsn");
+ queries.bindParamI(0, pqid);
+ while (queries.fetch()) {
+ addQueryResultsToXml(wdb, cp, record, queries["dsn"],
+ queries["sql"], queries["name"], queries["recordname"], queries["id"], &query, elems, qs);
+ }
+ }
+}
+
+void
+bindQueryParams(ODBC::Connection & db, ODBC::SelectCommand & sel, int pqid, ODBC::SelectCommand * parent,
+ const StringSet & elems, const RegMultiMatch & qs)
+{
+ ODBC::SelectCommand params(db,
+ "SELECT paramidx, source, key \
+ FROM page_query_params \
+ WHERE pqid = ?");
+ params.bindParamI(0, pqid);
+ while (params.fetch()) {
+ const char * key = params["key"];
+ const char * src = params["source"];
+ int idx = params["paramidx"];
+ switch (*src) {
+ case 'v': // Value
+ sel.bindParamS(idx, key);
+ break;
+ case 'u': // URL token
+ sel.bindParamS(idx, elems[atoi(key)]);
+ break;
+ case 's': // Session variable
+ break;
+ case 'q': // Query string param
+ for(RegMultiMatch::const_iterator u = qs.begin(); u != qs.end(); u++) {
+ if ((*u)[0] == key) {
+ sel.bindParamS(idx, (*u)[1]);
+ }
+ }
+ break;
+ case 'p': // Parent column value
+ if (parent) {
+ (*parent)[key].rebind(&sel, idx);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+}
+
+
+int
+main(int, char**)
+{
+ try {
+ std::string request_uri(getenv("REQUEST_URI"));
+ std::string query_string(getenv("QUERY_STRING"));
+ StringSet elems(regexExtractSet("/([^/?]+)", request_uri.c_str()));
+ RegMultiMatch params(regexExtractMulti("&?([^=]+)=?([^&]*)", query_string.c_str(), 2));
+ xmlDoc * doc = xmlNewDoc(BAD_CAST "1.0");
+ xmlNode * root_node = NULL;
+ ODBC::String fqdn(getenv("HTTP_HOST"));
+ ODBC::DSN main("dbweb", "dbweb", "dbweb");
+ ODBC::Connection db(main);
+ int pageId = 0;
+ std::string page(elems.size() > 0 ? elems[0] : "index");
+ {
+ ODBC::SelectCommand pageHeaders(db,
+ "SELECT p.id, p.name, p.style, p.documentname \
+ FROM site s, domainname dn, page p \
+ WHERE s.id = dn.site \
+ AND p.pattern = ? \
+ AND dn.fqdn = ? \
+ AND p.site = s.id");
+ pageHeaders.bindParamS(0, page);
+ pageHeaders.bindParamS(1, fqdn);
+ while (pageHeaders.fetch()) {
+ pageId = pageHeaders["id"];
+ root_node = xmlNewNode(NULL, pageHeaders["documentname"]);
+ xmlDocSetRootElement(doc, root_node);
+ if (!pageHeaders["style"].isNull()) {
+ printf("Content-type: text/xml-xslt\r\n");
+ char * buf;
+ if (asprintf(&buf, "type=\"text/xsl\" href=\"%s\"",
+ pageHeaders["style"].operator const char *()) > 0) {
+ xmlAddPrevSibling(root_node,
+ xmlNewDocPI(doc, BAD_CAST "xml-stylesheet", BAD_CAST buf));
+ }
+ free(buf);
+ }
+ else {
+ printf("Content-type: text/xml\r\n");
+ }
+ xmlNewProp(root_node, BAD_CAST "title", pageHeaders["name"]);
+ }
+ }
+ // These were for debug... but why not pass them on?
+ xmlNsPtr dbwebns = xmlNewNs(root_node, BAD_CAST "http://dbweb.randomdan.homeip.net/", BAD_CAST "dbweb");
+ xmlNewChild(root_node, dbwebns, BAD_CAST "fqdn", fqdn.c_str());
+ xmlNewChild(root_node, dbwebns, BAD_CAST "requesturi", BAD_CAST request_uri.c_str());
+ // URL elements
+ xmlNode * uriElems = xmlNewChild(root_node, dbwebns, BAD_CAST "uriElems", NULL);
+ for(StringSet::const_iterator u = elems.begin(); u != elems.end(); u++) {
+ xmlNewChild(uriElems, dbwebns, BAD_CAST "uriElem", BAD_CAST u->c_str());
+ }
+ // Parameters
+ xmlNode * paramsXml = xmlNewChild(root_node, dbwebns, BAD_CAST "params", NULL);
+ for(RegMultiMatch::const_iterator u = params.begin(); u != params.end(); u++) {
+ xmlNode * param = xmlNewChild(paramsXml, NULL, BAD_CAST "param", BAD_CAST (*u)[1].c_str());
+ xmlNewProp(param, BAD_CAST "name", BAD_CAST (*u)[0].c_str());
+ }
+ ConnectionPool cp;
+ // Load DSNs
+#ifdef DEBUG
+ xmlNode * dsnsXml = xmlNewChild(root_node, dbwebns, BAD_CAST "DSNs", NULL);
+#endif
+ {
+ ODBC::SelectCommand dsns(db,
+ "SELECT DISTINCT d.id AS id, d.connectionstring AS connstr \
+ FROM dsn d, page_query pq, query q \
+ WHERE pq.page = ? \
+ AND q.id = pq.query \
+ AND d.id = q.dsn");
+ dsns.bindParamI(0, pageId);
+ while (dsns.fetch()) {
+ int id = dsns["id"];
+ ODBC::String connstr = dsns["connstr"];
+#ifdef DEBUG
+ xmlNode * dsnXml = xmlNewChild(dsnsXml, NULL, BAD_CAST "DSN", NULL);
+ xmlNewTextChildf(dsnXml, "id", "%d", id);
+ xmlNewTextChildf(dsnXml, "connectionstring", "%s", connstr.c_str());
+#endif
+ cp[id] = new ConnHandle(connstr);
+ }
+ }
+ // Exec queries
+ {
+ ODBC::SelectCommand queries(db,
+ "SELECT d.id AS dsn, q.id AS query, q.sql, q.name, q.recordname, pq.id \
+ FROM dsn d, page_query pq, query q \
+ WHERE pq.page = ? \
+ AND q.id = pq.query \
+ AND d.id = q.dsn \
+ AND pq.parent IS NULL");
+ queries.bindParamI(0, pageId);
+#ifdef DEBUG
+ xmlNode * queriesXml = xmlNewChild(root_node, dbwebns, BAD_CAST "queries", NULL);
+#endif
+ while (queries.fetch()) {
+#ifdef DEBUG
+ int query = queries["query"];
+#endif
+ int dsn = queries["dsn"];
+ ODBC::String sql = queries["sql"];
+ ODBC::String name = queries["name"];
+ ODBC::String recordName = queries["recordname"];
+#ifdef DEBUG
+ xmlNode * queryXml = xmlNewChild(queriesXml, NULL, BAD_CAST "query", NULL);
+ xmlNewTextChildf(queryXml, "query", "%d", query);
+ xmlNewTextChildf(queryXml, "name", "%s", name.c_str());
+ xmlNewTextChildf(queryXml, "dsn", "%d", dsn);
+ xmlNewTextChildf(queryXml, "sql", "%s", sql.c_str());
+#endif
+ addQueryResultsToXml(db, cp, root_node, dsn, sql, name, recordName, queries["id"], NULL, elems, params);
+ }
+ }
+ printf("\r\n");
+ xmlDocFormatDump(stdout, doc, 1);
+ xmlFreeDoc(doc);
+ }
+ catch (std::exception & e) {
+ printf("Content-type: text/xml\r\n");
+ printf("\r\n");
+ xmlDoc * doc = xmlNewDoc(BAD_CAST "1.0");
+ xmlNode * root_node = xmlNewNode(NULL, BAD_CAST "error");
+ xmlDocSetRootElement(doc, root_node);
+ xmlNewTextChildf(root_node, "what", "%s", e.what());
+ xmlDocFormatDump(stdout, doc, 1);
+ }
+ catch (...) {
+ printf("Content-type: text/xml\r\n");
+ printf("\r\n");
+ xmlDoc * doc = xmlNewDoc(BAD_CAST "1.0");
+ xmlNode * root_node = xmlNewNode(NULL, BAD_CAST "error");
+ xmlNewTextChildf(root_node, "what", "Unknown");
+ xmlNewTextChildf(root_node, "detail", "Exception thrown does not inherit from std::exception");
+ xmlDocSetRootElement(doc, root_node);
+ xmlDocFormatDump(stdout, doc, 1);
+ }
+ return 0;
+}
+
diff --git a/project2/p2webMain.cpp b/project2/p2webMain.cpp
new file mode 100644
index 0000000..716495f
--- /dev/null
+++ b/project2/p2webMain.cpp
@@ -0,0 +1,42 @@
+#include <libxml/tree.h>
+#include <fcgi_stdio.h>
+#include "envproc.h"
+#include <boost/bind.hpp>
+
+int
+xmlWrite(void * _out, const char * buf, int len)
+{
+ return FCGX_PutStr(buf, len, (FCGX_Stream*)_out);
+}
+
+int main(void)
+{
+ FCGX_Stream *in, *_out, *err;
+ FCGX_ParamArray envp;
+
+ while (FCGX_Accept(&in, &_out, &err, &envp) >= 0)
+ {
+ try {
+ EnvironmentProcessor ep(boost::bind(FCGX_GetParam, _1, envp));
+
+ xmlDoc * doc = ep.process();
+
+ FCGX_FPrintF(_out, "Content-type: text/xml-xslt\r\n\r\n");
+ xmlOutputBufferPtr out = xmlOutputBufferCreateIO(
+ xmlWrite, NULL, _out, xmlGetCharEncodingHandler(XML_CHAR_ENCODING_UTF8));
+ xmlSaveFileTo(out, doc, NULL);
+ xmlFreeDoc(doc);
+ }
+ catch (const std::exception & e) {
+ FCGX_FPrintF(_out, "Content-type: text/plain\r\n\r\n");
+ FCGX_FPrintF(_out, "Kaboom!\r\n\r\n");
+ FCGX_FPrintF(_out, "%s\r\n\r\n", e.what());
+ }
+ catch (...) {
+ FCGX_FPrintF(_out, "Content-type: text/plain\r\n\r\n");
+ FCGX_FPrintF(_out, "Kaboom!\r\n\r\n");
+ FCGX_FPrintF(_out, "Unknown exception.\r\n\r\n");
+ }
+ }
+ return 0;
+}
diff --git a/project2/rdbmsDataSource.cpp b/project2/rdbmsDataSource.cpp
new file mode 100644
index 0000000..f4b3b4e
--- /dev/null
+++ b/project2/rdbmsDataSource.cpp
@@ -0,0 +1,18 @@
+#include "rdbmsDataSource.h"
+#include "xml.h"
+
+_RdbmsDataSource::_RdbmsDataSource(xmlNodePtr p) :
+ _Project2SourceObject(p),
+ masterDsn(xmlGetNodeValue<ustring>(p, BAD_CAST "masterdsn", BAD_CAST ""))
+{
+ fprintf(stderr, "Created RDBMS Datasource %s (%s)\n", name.c_str(), masterDsn.c_str());
+}
+
+_RdbmsDataSource::operator ODBC::Connection &()
+{
+ if (!database) {
+ database = boost::shared_ptr<ODBC::Connection>(new ODBC::Connection(masterDsn));
+ }
+ return *database;
+}
+
diff --git a/project2/rdbmsDataSource.h b/project2/rdbmsDataSource.h
new file mode 100644
index 0000000..74ed829
--- /dev/null
+++ b/project2/rdbmsDataSource.h
@@ -0,0 +1,23 @@
+#ifndef RDBMSDATASOURCE_H
+#define RDBMSDATASOURCE_H
+
+#include <libxml/tree.h>
+#include <boost/shared_ptr.hpp>
+#include <map>
+#include "sourceObject.h"
+#include "connection.h"
+#include "ustring.h"
+
+class _RdbmsDataSource : public _Project2SourceObject {
+ public:
+ _RdbmsDataSource(xmlNodePtr p);
+ operator ODBC::Connection &();
+ const ustring masterDsn;
+ private:
+ boost::shared_ptr<ODBC::Connection> database;
+};
+typedef boost::shared_ptr<_RdbmsDataSource> RdbmsDataSource;
+typedef std::map<ustring, RdbmsDataSource> RdbmsDataSources;
+
+#endif
+
diff --git a/project2/sourceObject.cpp b/project2/sourceObject.cpp
new file mode 100644
index 0000000..a441e77
--- /dev/null
+++ b/project2/sourceObject.cpp
@@ -0,0 +1,8 @@
+#include "sourceObject.h"
+
+_Project2SourceObject::_Project2SourceObject(xmlNodePtr p) :
+ name(xmlGetProp(p, BAD_CAST "name"))
+{
+ fprintf(stderr, "Created object %s\n", name.c_str());
+}
+
diff --git a/project2/sourceObject.h b/project2/sourceObject.h
new file mode 100644
index 0000000..d6531ff
--- /dev/null
+++ b/project2/sourceObject.h
@@ -0,0 +1,13 @@
+#ifndef SOURCEOBJECT_H
+#define SOURCEOBJECT_H
+
+#include "ustring.h"
+#include <libxml/tree.h>
+
+class _Project2SourceObject {
+ public:
+ _Project2SourceObject(xmlNodePtr p);
+ const ustring name;
+};
+
+#endif
diff --git a/project2/sqlView.cpp b/project2/sqlView.cpp
new file mode 100644
index 0000000..ca1c135
--- /dev/null
+++ b/project2/sqlView.cpp
@@ -0,0 +1,78 @@
+#include "sqlView.h"
+#include "xml.h"
+#include "selectcommand.h"
+#include "column.h"
+#include <string.h>
+
+_SqlView::_SqlView(xmlNodePtr p) :
+ _Project2SourceObject(p),
+ sql(xmlGetNodeValue<ustring>(p, BAD_CAST "sql", BAD_CAST "")),
+ dataSource(xmlGetProp(p, BAD_CAST "datasource")),
+ recordName(xmlGetProp(p, BAD_CAST "recordname"))
+{
+}
+
+void _SqlView::execute(RdbmsDataSources s, xmlNodePtr par) const
+{
+ typedef std::map<ODBC::String, xmlNode *> Columns;
+ fprintf(stderr, "executing\n");
+ ODBC::SelectCommand query(*s[dataSource], sql);
+ xmlNode * set = xmlNewChild(par, NULL, name.c_str(), NULL);
+ while (query.fetch()) {
+ Columns columns;
+ unsigned int cols = query.columnCount();
+ xmlNode * record = xmlNewChild(set, NULL, recordName.c_str(), NULL);
+ for (unsigned int col = 0; col < cols; col += 1) {
+ const unsigned char * nameattr = query[col].name.c_str();
+ unsigned char * name, * attr = NULL;
+ switch (sscanf((const char *)nameattr, "%a[^_]_%as", &name, &attr))
+ {
+ case 0:
+ return; // Make me an exception
+ break;
+ case 1:
+ attr = NULL;
+ break;
+ }
+ char * buf = NULL;
+ query[col].writeToBuf(&buf);
+ if (buf) {
+ if (attr) {
+ if (strcmp((const char *)attr, ".") == 0) {
+ xmlNewProp(record, name, BAD_CAST buf);
+ }
+ else {
+ Columns::iterator i = columns.find(attr);
+ if (i != columns.end()) {
+ xmlNewProp(i->second, name, BAD_CAST buf);
+ }
+ }
+ }
+ else if (strcmp((const char *)name, "value") == 0) {
+ xmlNodeAddContent(record, BAD_CAST buf);
+ }
+ else {
+ columns[name] = xmlNewTextChild(record, NULL, name, BAD_CAST buf);
+ }
+ free(buf);
+ }
+ free(name);
+ free(attr);
+ }
+ /*
+ ODBC::SelectCommand queries(wdb,
+ "SELECT d.id AS dsn, q.id AS query, q.sql, q.name, q.recordname, pq.id \
+ FROM dsn d, page_query pq, query q \
+ WHERE pq.parent = ? \
+ AND q.id = pq.query \
+ AND d.id = q.dsn");
+ queries.bindParamI(0, pqid);
+ while (queries.fetch()) {
+ addQueryResultsToXml(wdb, cp, record, queries["dsn"],
+ queries["sql"], queries["name"], queries["recordname"], queries["id"], &query, elems, qs);
+ }
+ */
+ }
+ fprintf(stderr, "finished\n");
+}
+
diff --git a/project2/sqlView.h b/project2/sqlView.h
new file mode 100644
index 0000000..16812d2
--- /dev/null
+++ b/project2/sqlView.h
@@ -0,0 +1,23 @@
+#ifndef SQLVIEW_H
+#define SQLVIEW_H
+
+#include <libxml/tree.h>
+#include <boost/shared_ptr.hpp>
+#include <map>
+#include "sourceObject.h"
+#include "ustring.h"
+#include "rdbmsDataSource.h"
+
+class _SqlView : public _Project2SourceObject {
+ public:
+ _SqlView(xmlNodePtr p);
+ void execute(RdbmsDataSources s, xmlNodePtr) const;
+ const ustring sql;
+ const ustring dataSource;
+ const ustring recordName;
+};
+typedef boost::shared_ptr<_SqlView> SqlView;
+typedef std::map<ustring, SqlView> SqlViews;
+
+#endif
+
diff --git a/project2/ustring.h b/project2/ustring.h
new file mode 100644
index 0000000..cfed0c4
--- /dev/null
+++ b/project2/ustring.h
@@ -0,0 +1,9 @@
+#ifndef USTRING_H
+#define USTRING_H
+
+#include <string>
+
+typedef std::basic_string<unsigned char> ustring;
+
+#endif
+