-/* $Id: js_pgsql.c,v 1.2 2006/08/05 20:26:25 mmondor Exp $ */
+/* $Id: js_pgsql.c,v 1.3 2006/10/03 19:12:01 mmondor Exp $ */
/*
* Copyright (c) 2006, Matthew Mondor
* generated objects instances's private data (using structures as necessary
* instead of simply wrapping around the native object's pointer
* (actually PGconn object).
- * - 28.10. Notice Processing
+ * - 28.10. Notice Processing XXX
* Either place one(s) that use syslog(3) transparently, or somehow allow
* the user to set a custom handler
* - Large objects API
* - Compare to PHP library to verify if missing any nice functions ideas
* - See what to do about the following functions:
* - PQgetssl() (returns an SSL object!)
- * - PQprint() (writes to a supplied FILE *)
+ * - PQprint() (writes to a supplied FILE *) XXX
*/
#include <libpq-fe.h>
#include <js_pgsql.h>
+#include <js_file.h>
static JSBool pg_constructor(JSContext *, JSObject *, uintN, jsval *,
jsval *);
+static void pg_finalize(JSContext *, JSObject *);
static JSBool pg_sm_PQconndefaults(JSContext *, JSObject *, uintN, jsval *,
jsval *);
jsval *);
static JSBool pgresult_m_PQoidValue(JSContext *, JSObject *, uintN, jsval *,
jsval *);
+static JSBool pgresult_m_PQprint(JSContext *, JSObject *, uintN, jsval *,
+ jsval *);
static JSObject *js_InitPGcancelClass(JSContext *, JSObject *);
static JSBool pgcancel_constructor(JSContext *, JSObject *, uintN, jsval *,
static JSBool pgcancel_m_PQcancel(JSContext *, JSObject *, uintN, jsval *,
jsval *);
+static JSObject *js_InitPGPrintOptClass(JSContext *, JSObject *);
+static JSBool pgprintopt_constructor(JSContext *, JSObject *, uintN, jsval *,
+ jsval *);
+
/*
/*
* General purpose string buffer (note that this is not thread-safe).
* Allows to optimize functions such as PQescapeStringConn().
+ * XXX To be thread-safe, these would need to be tied to objects rather than
+ * being shared globals. This obviously would require more memory and add
+ * additional object creation overhead.
*/
static char *buffer = NULL;
static size_t buffer_size = 0;
static int *param_formats = NULL;
static int param_entries = 0;
+/*
+ * This object is crated by js_InitPGClass() and rooted, so that we may easily
+ * root objects by adding them as properties into it. To be thread-safe, a
+ * critical section or mutex would be needed when accessing this object.
+ */
+static JSObject *root_object = NULL;
+
+
/* PG class */
static JSClass pg_class = {
"PG", 0, JS_PropertyStub, JS_PropertyStub,
JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub,
- JS_ConvertStub, JS_FinalizeStub
+ JS_ConvertStub, pg_finalize
};
/* Provided static methods */
{ "isNonBlocking", pgconn_m_PQisnonblocking, 0, 0, 0 },
{ "flush", pgconn_m_PQflush, 0, 0, 0 },
{ "setErrorVerbosity", pgconn_m_PQsetErrorVerbosity, 1, 0, 0 },
- { "trace", pgconn_m_PQtrace, 0, 0, 0 },
+ { "trace", pgconn_m_PQtrace, 1, 0, 0 },
{ "untrace", pgconn_m_PQuntrace, 0, 0, 0 },
{ "putCopyData", pgconn_m_PQputCopyData, 1, 0, 0 },
{ "putCopyEnd", pgconn_m_PQputCopyEnd, 1, 0, 0 },
{ "cmdStatus", pgresult_m_PQcmdStatus, 0, 0, 0 },
{ "cmdTuples", pgresult_m_PQcmdTuples, 0, 0, 0 },
{ "oidValue", pgresult_m_PQoidValue, 0, 0, 0 },
+ { "print", pgresult_m_PQprint, 2, 0, 0 },
{ NULL, NULL, 0, 0, 0 }
};
};
+/* PGPrintOpt class */
+static JSClass pgprintopt_class = {
+ "PGPrintOpt", 0, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
+ JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub,
+ JS_FinalizeStub
+};
+
+
static int
buffer_grow(size_t required)
int new;
void *types, *values, *lengths, *formats;
- if (required <= param_entries)
+ /* Account space for NULL */
+ if (++required <= param_entries)
return 0;
for (new = param_entries; new < required; new *= 2) ;
}
}
- /* Initialize PGconn class since we'll need to instanciate it from C */
+ /*
+ * Initialize object/classes which we'll need to instanciate objects
+ * from
+ */
if (js_InitPGconnClass(cx, obj) == NULL) {
(void) fprintf(stderr, "PG: InitPGconnClass()\n");
goto err;
}
- /* Same for PGresult class */
if (js_InitPGresultClass(cx, obj) == NULL) {
(void) fprintf(stderr, "PG: InitPGresultClass()\n");
goto err;
}
- /* And PGcancel class */
if (js_InitPGcancelClass(cx, obj) == NULL) {
(void) fprintf(stderr, "PG: InitPGcancelClass()\n");
goto err;
}
+ if (js_InitPGPrintOptClass(cx, obj) == NULL) {
+ (void) fprintf(stderr, "PG: InitPGPrintOptClass()\n");
+ goto err;
+ }
/*
* Note that the following buffers, although allowing optimizations,
}
param_entries = 16;
+ /* Create our rooted object */
+ if ((root_object = JS_NewObject(cx, NULL, NULL, NULL)) == NULL) {
+ (void) fprintf(stderr, "PG: JS_NewObject(root_object)\n");
+ goto err;
+ }
+ if (!JS_AddRoot(cx, &root_object)) { /* Pointer to pointer */
+ (void) fprintf(stderr, "PG: JS_AddRoot(root_object)\n");
+ goto err;
+ }
+
return proto;
err:
return JS_FALSE;
}
+static void
+pg_finalize(JSContext *cx, JSObject *obj)
+{
+
+ if (root_object != NULL)
+ (void) JS_RemoveRoot(cx, &root_object); /* Ptr to ptr */
+}
+
/*
* PG object static methods
return JS_FALSE;
}
+/*
+ * Requires a File object, which we root to ensure that it doesn't get
+ * unexpectedly finalized. We unroot any previously provided File object when
+ * supplied with a new one.
+ */
static JSBool
pgconn_m_PQtrace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
jsval *rval)
{
PGconn *pgc;
+ FILE *fh;
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
+ *rval = OBJECT_TO_JSVAL(NULL);
+
+ if (argc != 1) {
+ QUEUE_EXCEPTION("Wrong number of arguments");
+ return JS_FALSE;
+ }
+ if ((fh = file_fh(cx, argv[0])) == NULL) {
+ QUEUE_EXCEPTION("Argument 1 not a File");
return JS_FALSE;
}
pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
assert(pgc != NULL);
- *rval = OBJECT_TO_JSVAL(NULL);
- PQtrace(pgc, stderr);
+ /*
+ * Unroot previously rooted File object, if any, then root newly
+ * provided File object.
+ */
+ (void) JS_DeleteProperty(cx, root_object, "trace");
+ if (!JS_DefineProperty(cx, root_object, "trace", argv[0],
+ NULL, NULL, 0)) {
+ QUEUE_EXCEPTION("Internal error!");
+ return JS_FALSE;
+ }
+
+ PQtrace(pgc, fh);
return JS_TRUE;
}
+/*
+ * We unroot any previously rooted File object supplied to trace().
+ */
static JSBool
pgconn_m_PQuntrace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
jsval *rval)
{
PGconn *pgc;
+ *rval = OBJECT_TO_JSVAL(NULL);
+
if (argc != 0) {
QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
return JS_FALSE;
}
pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
assert(pgc != NULL);
- *rval = OBJECT_TO_JSVAL(NULL);
+ /*
+ * Unroot previously rooted File object
+ */
+ (void) JS_DeleteProperty(cx, root_object, "trace");
+
PQuntrace(pgc);
return JS_TRUE;
return JS_FALSE;
}
+/*
+ * XXX Not reentrant as it uses a global buffer.
+ * For simplicity, we currently use param_grow() and param_values.
+ */
+static JSBool
+pgresult_m_PQprint(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+ jsval *rval)
+{
+ PGresult *pgr;
+ FILE *fh;
+ JSObject *o;
+ PQprintOpt opt;
+ jsval v;
+ JSIdArray *a = NULL;
+
+ *rval = OBJECT_TO_JSVAL(NULL);
+
+ if (argc != 2) {
+ QUEUE_EXCEPTION("Wrong number of arguments");
+ return JS_FALSE;
+ }
+ if ((fh = file_fh(cx, argv[0])) == NULL) {
+ QUEUE_EXCEPTION("Argument 1 not a File");
+ return JS_FALSE;
+ }
+ if (!JSVAL_IS_OBJECT(argv[1])) {
+ QUEUE_EXCEPTION("Argument 2 not a PGPrintOpt");
+ return JS_FALSE;
+ }
+ o = JSVAL_TO_OBJECT(argv[1]);
+ if (!JS_InstanceOf(cx, o, &pgprintopt_class, NULL)) {
+ QUEUE_EXCEPTION("Argument 2 not a PGPrintOpt");
+ return JS_FALSE;
+ }
+
+ pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL);
+ assert(pgr != NULL);
+
+ /* Fill opt according to supplied object properties */
+
+ /* Boolean properties */
+ if (!JS_GetProperty(cx, o, "header", &v) || !JSVAL_IS_BOOLEAN(v)) {
+ QUEUE_EXCEPTION("property 'header' not a Boolean");
+ return JS_FALSE;
+ }
+ opt.header = JSVAL_TO_BOOLEAN(v);
+
+ if (!JS_GetProperty(cx, o, "align", &v) || !JSVAL_IS_BOOLEAN(v)) {
+ QUEUE_EXCEPTION("property 'align' not a Boolean");
+ return JS_FALSE;
+ }
+ opt.align = JSVAL_TO_BOOLEAN(v);
+
+ if (!JS_GetProperty(cx, o, "standard", &v) || !JSVAL_IS_BOOLEAN(v)) {
+ QUEUE_EXCEPTION("property 'standard' not a Boolean");
+ return JS_FALSE;
+ }
+ opt.standard = JSVAL_TO_BOOLEAN(v);
+
+ if (!JS_GetProperty(cx, o, "html3", &v) || !JSVAL_IS_BOOLEAN(v)) {
+ QUEUE_EXCEPTION("property 'html3' not a Boolean");
+ return JS_FALSE;
+ }
+ opt.html3 = JSVAL_TO_BOOLEAN(v);
+
+ if (!JS_GetProperty(cx, o, "expanded", &v) || !JSVAL_IS_BOOLEAN(v)) {
+ QUEUE_EXCEPTION("property 'expanded' not a Boolean");
+ return JS_FALSE;
+ }
+ opt.expanded = JSVAL_TO_BOOLEAN(v);
+
+ if (!JS_GetProperty(cx, o, "pager", &v) || !JSVAL_IS_BOOLEAN(v)) {
+ QUEUE_EXCEPTION("property 'pager' not a Boolean");
+ return JS_FALSE;
+ }
+ opt.pager = JSVAL_TO_BOOLEAN(v);
+
+ /* String properties */
+ if (!JS_GetProperty(cx, o, "fieldSep", &v) || !JSVAL_IS_STRING(v)) {
+ QUEUE_EXCEPTION("property 'fieldSep' not a String");
+ return JS_FALSE;
+ }
+ if ((opt.fieldSep = JS_GetStringBytes(JSVAL_TO_STRING(v))) == NULL) {
+ QUEUE_EXCEPTION("Internal error!");
+ return JS_FALSE;
+ }
+
+ if (!JS_GetProperty(cx, o, "tableOpt", &v) || !JSVAL_IS_STRING(v)) {
+ QUEUE_EXCEPTION("property 'tableOpt' not a String");
+ return JS_FALSE;
+ }
+ if ((opt.tableOpt = JS_GetStringBytes(JSVAL_TO_STRING(v))) == NULL) {
+ QUEUE_EXCEPTION("Internal error!");
+ return JS_FALSE;
+ }
+
+ if (!JS_GetProperty(cx, o, "caption", &v) || !JSVAL_IS_STRING(v)) {
+ QUEUE_EXCEPTION("property 'caption' not a String");
+ return JS_FALSE;
+ }
+ if ((opt.caption = JS_GetStringBytes(JSVAL_TO_STRING(v))) == NULL) {
+ QUEUE_EXCEPTION("Internal error!");
+ return JS_FALSE;
+ }
+
+ /* String Array property */
+ if (!JS_GetProperty(cx, o, "fieldName", &v) || !JSVAL_IS_OBJECT(v)) {
+ QUEUE_EXCEPTION("property 'fieldName' not an Array");
+ return JS_FALSE;
+ }
+ o = JSVAL_TO_OBJECT(v);
+ if (!JS_IsArrayObject(cx, o)) {
+ QUEUE_EXCEPTION("property 'fieldName' not an Array");
+ return JS_FALSE;
+ }
+ {
+ jsint len;
+
+ if (!JS_GetArrayLength(cx, o, &len)) {
+ QUEUE_EXCEPTION("Internal error!");
+ return JS_FALSE;
+ }
+ if (len == 0)
+ opt.fieldName = NULL;
+ else {
+ if (param_grow(len) == -1) {
+ QUEUE_EXCEPTION("Out of memory!");
+ return JS_FALSE;
+ }
+ opt.fieldName = param_values;
+ }
+ }
+ if (opt.fieldName != NULL) {
+ jsval id;
+ int i, i2;
+ char str[256];
+
+ if ((a = JS_Enumerate(cx, o)) == NULL) {
+ QUEUE_EXCEPTION("Internal error!");
+ return JS_FALSE;
+ }
+ for (i2 = i = 0; i < a->length; i++) {
+ JS_IdToValue(cx, a->vector[i], &id);
+ if (JS_LookupElement(cx, o, JSVAL_TO_INT(id), &v) &&
+ !JSVAL_IS_VOID(v)) {
+ if (JSVAL_IS_NULL(v) || !JSVAL_IS_STRING(v)) {
+ (void) snprintf(str, 255,
+ "fieldName Array element %d "
+ "not a String", i2);
+ QUEUE_EXCEPTION(str);
+ goto err;
+ }
+ if ((opt.fieldName[i2++] = JS_GetStringBytes(
+ JSVAL_TO_STRING(v))) == NULL) {
+ QUEUE_EXCEPTION("Internal error!");
+ goto err;
+ }
+ }
+ }
+ JS_DestroyIdArray(cx, a);
+ a = NULL;
+ opt.fieldName[i2] = NULL;
+ }
+
+ /* Finally call our function */
+ PQprint(fh, pgr, &opt);
+
+ return JS_TRUE;
+
+err:
+ if (a != NULL)
+ JS_DestroyIdArray(cx, a);
+
+ return JS_FALSE;
+}
/*
return JS_FALSE;
}
+
+
+/*
+ * PGPrintOpt object control
+ */
+static JSObject *
+js_InitPGPrintOptClass(JSContext *cx, JSObject *obj)
+{
+ JSObject *proto;
+ JSString *str;
+ JSObject *array;
+
+ if ((proto = JS_InitClass(cx, obj, NULL, &pgprintopt_class,
+ pgprintopt_constructor, 0, NULL, NULL, NULL, NULL)) == NULL)
+ return NULL;
+
+ /*
+ * Add default properties to this object's prototype
+ */
+ if (!JS_DefineProperty(cx, proto, "header", BOOLEAN_TO_JSVAL(JS_TRUE),
+ NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT) ||
+ !JS_DefineProperty(cx, proto, "align", BOOLEAN_TO_JSVAL(JS_TRUE),
+ NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT) ||
+ !JS_DefineProperty(cx, proto, "standard",
+ BOOLEAN_TO_JSVAL(JS_FALSE), NULL, NULL,
+ JSPROP_ENUMERATE | JSPROP_PERMANENT) ||
+ !JS_DefineProperty(cx, proto, "html3", BOOLEAN_TO_JSVAL(JS_FALSE),
+ NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT) ||
+ !JS_DefineProperty(cx, proto, "expanded",
+ BOOLEAN_TO_JSVAL(JS_FALSE), NULL, NULL,
+ JSPROP_ENUMERATE | JSPROP_PERMANENT) ||
+ !JS_DefineProperty(cx, proto, "pager", BOOLEAN_TO_JSVAL(JS_FALSE),
+ NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT))
+ return NULL;
+
+ if ((str = JS_NewStringCopyZ(cx, "|")) == NULL)
+ return NULL;
+ if (!JS_DefineProperty(cx, proto, "fieldSep", STRING_TO_JSVAL(str),
+ NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT))
+ return NULL;
+
+ if ((str = JS_NewStringCopyZ(cx, "")) == NULL)
+ return NULL;
+ if (!JS_DefineProperty(cx, proto, "tableOpt", STRING_TO_JSVAL(str),
+ NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT))
+ return NULL;
+
+ if ((str = JS_NewStringCopyZ(cx, "")) == NULL)
+ return NULL;
+ if (!JS_DefineProperty(cx, proto, "caption", STRING_TO_JSVAL(str),
+ NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT))
+ return NULL;
+
+ if ((array = JS_NewArrayObject(cx, 0, NULL)) == NULL)
+ return NULL;
+ if (!JS_DefineProperty(cx, proto, "fieldName", OBJECT_TO_JSVAL(array),
+ NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT))
+ return NULL;
+
+ return proto;
+}
+
+static JSBool
+pgprintopt_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+ jsval *rval)
+{
+
+ if (!JS_IsConstructing(cx)) {
+ QUEUE_EXCEPTION("PGPrintOpt constuctor called as a function");
+ *rval = OBJECT_TO_JSVAL(NULL);
+ return JS_FALSE;
+ }
+
+ return JS_TRUE;
+}