summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Johnson <benbjohnson@yahoo.com>2022-03-30 19:47:18 -0600
committerBen Johnson <benbjohnson@yahoo.com>2022-03-30 20:04:59 -0600
commitf4873824c37339dc66eaa4ea35c31b8457b3c241 (patch)
tree568abb7ec3fa8b2cfbd7aa446b3c90b8577b9de2
parentf3f3928a96bb5705300164c7575aa5599f56a669 (diff)
Add virtual tables & functions
-rw-r--r--pg_class.go207
-rw-r--r--pg_database.go131
-rw-r--r--pg_description.go95
-rw-r--r--pg_namespace.go96
-rw-r--r--pg_settings.go143
-rw-r--r--pg_type.go199
-rw-r--r--server.go93
7 files changed, 956 insertions, 8 deletions
diff --git a/pg_class.go b/pg_class.go
new file mode 100644
index 0000000..c781f88
--- /dev/null
+++ b/pg_class.go
@@ -0,0 +1,207 @@
+package postlite
+
+import (
+ "fmt"
+
+ "github.com/mattn/go-sqlite3"
+)
+
+type pgClassModule struct{}
+
+func (m *pgClassModule) Create(c *sqlite3.SQLiteConn, args []string) (sqlite3.VTab, error) {
+ err := c.DeclareVTab(fmt.Sprintf(`
+ CREATE TABLE %s (
+ oid INTEGER,
+ relname TEXT,
+ relnamespace INTEGER,
+ reltype INTEGER,
+ reloftype INTEGER,
+ relowner INTEGER,
+ relam INTEGER,
+ relfilenode INTEGER,
+ reltablespace INTEGER,
+ relpages INTEGER,
+ reltuples REAL,
+ relallvisible INTEGER,
+ reltoastrelid INTEGER,
+ relhasindex INTEGER,
+ relisshared INTEGER,
+ relpersistence TEXT,
+ relkind TEXT,
+ relnatts INTEGER,
+ relchecks INTEGER,
+ relhasrules INTEGER,
+ relhastriggers INTEGER,
+ relhassubclass INTEGER,
+ relrowsecurity INTEGER,
+ relforcerowsecurity INTEGER,
+ relispopulated INTEGER,
+ relreplident TEXT,
+ relispartition INTEGER,
+ relrewrite INTEGER,
+ relfrozenxid INTEGER,
+ relminmxid INTEGER,
+ relacl TEXT,
+ reloptions TEXT,
+ relpartbound TEXT
+ )`, args[0]))
+ if err != nil {
+ return nil, err
+ }
+ return &pgClassTable{}, nil
+}
+
+func (m *pgClassModule) Connect(c *sqlite3.SQLiteConn, args []string) (sqlite3.VTab, error) {
+ return m.Create(c, args)
+}
+
+func (m *pgClassModule) DestroyModule() {}
+
+type pgClassTable struct{}
+
+func (t *pgClassTable) Open() (sqlite3.VTabCursor, error) {
+ return &pgClassCursor{}, nil
+}
+
+func (t *pgClassTable) BestIndex(cst []sqlite3.InfoConstraint, ob []sqlite3.InfoOrderBy) (*sqlite3.IndexResult, error) {
+ return &sqlite3.IndexResult{Used: make([]bool, len(cst))}, nil
+}
+
+func (t *pgClassTable) Disconnect() error { return nil }
+func (t *pgClassTable) Destroy() error { return nil }
+
+type pgClassCursor struct {
+ index int
+}
+
+func (c *pgClassCursor) Column(sctx *sqlite3.SQLiteContext, col int) error {
+ switch col {
+ case 0:
+ sctx.ResultInt(pgClasses[c.index].oid)
+ case 1:
+ sctx.ResultText(pgClasses[c.index].relname)
+ case 2:
+ sctx.ResultInt(pgClasses[c.index].relnamespace)
+ case 3:
+ sctx.ResultInt(pgClasses[c.index].reltype)
+ case 4:
+ sctx.ResultInt(pgClasses[c.index].reloftype)
+ case 5:
+ sctx.ResultInt(pgClasses[c.index].relowner)
+ case 6:
+ sctx.ResultInt(pgClasses[c.index].relam)
+ case 7:
+ sctx.ResultInt(pgClasses[c.index].relfilenode)
+ case 8:
+ sctx.ResultInt(pgClasses[c.index].reltablespace)
+ case 9:
+ sctx.ResultInt(pgClasses[c.index].relpages)
+ case 10:
+ sctx.ResultDouble(pgClasses[c.index].reltuples)
+ case 11:
+ sctx.ResultInt(pgClasses[c.index].relallvisible)
+ case 12:
+ sctx.ResultInt(pgClasses[c.index].reltoastrelid)
+ case 13:
+ sctx.ResultInt(pgClasses[c.index].relhasindex)
+ case 14:
+ sctx.ResultInt(pgClasses[c.index].relisshared)
+ case 15:
+ sctx.ResultText(pgClasses[c.index].relpersistence)
+ case 16:
+ sctx.ResultText(pgClasses[c.index].relkind)
+ case 17:
+ sctx.ResultInt(pgClasses[c.index].relnatts)
+ case 18:
+ sctx.ResultInt(pgClasses[c.index].relchecks)
+ case 19:
+ sctx.ResultInt(pgClasses[c.index].relhasrules)
+ case 20:
+ sctx.ResultInt(pgClasses[c.index].relhastriggers)
+ case 21:
+ sctx.ResultInt(pgClasses[c.index].relhassubclass)
+ case 22:
+ sctx.ResultInt(pgClasses[c.index].relrowsecurity)
+ case 23:
+ sctx.ResultInt(pgClasses[c.index].relforcerowsecurity)
+ case 24:
+ sctx.ResultInt(pgClasses[c.index].relispopulated)
+ case 25:
+ sctx.ResultText(pgClasses[c.index].relreplident)
+ case 26:
+ sctx.ResultInt(pgClasses[c.index].relispartition)
+ case 27:
+ sctx.ResultInt(pgClasses[c.index].relrewrite)
+ case 28:
+ sctx.ResultInt(pgClasses[c.index].relfrozenxid)
+ case 29:
+ sctx.ResultInt(pgClasses[c.index].relminmxid)
+ case 30:
+ sctx.ResultText(pgClasses[c.index].relacl)
+ case 31:
+ sctx.ResultText(pgClasses[c.index].reloptions)
+ case 32:
+ sctx.ResultText(pgClasses[c.index].relpartbound)
+ }
+ return nil
+}
+
+func (c *pgClassCursor) Filter(idxNum int, idxStr string, vals []interface{}) error {
+ c.index = 0
+ return nil
+}
+
+func (c *pgClassCursor) Next() error {
+ c.index++
+ return nil
+}
+
+func (c *pgClassCursor) EOF() bool {
+ return c.index >= len(pgClasses)
+}
+
+func (c *pgClassCursor) Rowid() (int64, error) {
+ return int64(c.index), nil
+}
+
+func (c *pgClassCursor) Close() error {
+ return nil
+}
+
+type pgClass struct {
+ oid int
+ relname string
+ relnamespace int
+ reltype int
+ reloftype int
+ relowner int
+ relam int
+ relfilenode int
+ reltablespace int
+ relpages int
+ reltuples float64
+ relallvisible int
+ reltoastrelid int
+ relhasindex int
+ relisshared int
+ relpersistence string
+ relkind string
+ relnatts int
+ relchecks int
+ relhasrules int
+ relhastriggers int
+ relhassubclass int
+ relrowsecurity int
+ relforcerowsecurity int
+ relispopulated int
+ relreplident string
+ relispartition int
+ relrewrite int
+ relfrozenxid int
+ relminmxid int
+ relacl string
+ reloptions string
+ relpartbound string
+}
+
+var pgClasses = []pgClass{}
diff --git a/pg_database.go b/pg_database.go
new file mode 100644
index 0000000..3c9c592
--- /dev/null
+++ b/pg_database.go
@@ -0,0 +1,131 @@
+package postlite
+
+import (
+ "fmt"
+
+ "github.com/mattn/go-sqlite3"
+)
+
+type pgDatabaseModule struct{}
+
+func (m *pgDatabaseModule) Create(c *sqlite3.SQLiteConn, args []string) (sqlite3.VTab, error) {
+ err := c.DeclareVTab(fmt.Sprintf(`
+ CREATE TABLE %s (
+ oid INTEGER,
+ datname TEXT,
+ datdba INTEGER,
+ encoding INTEGER,
+ datcollate TEXT,
+ datctype TEXT,
+ datistemplate INTEGER,
+ datallowconn INTEGER,
+ datconnlimit INTEGER,
+ datlastsysoid INTEGER,
+ datfrozenxid INTEGER,
+ datminmxid INTEGER,
+ dattablespace INTEGER,
+ datacl TEXT
+ )`, args[0]))
+ if err != nil {
+ return nil, err
+ }
+ return &pgDatabaseTable{}, nil
+}
+
+func (m *pgDatabaseModule) Connect(c *sqlite3.SQLiteConn, args []string) (sqlite3.VTab, error) {
+ return m.Create(c, args)
+}
+
+func (m *pgDatabaseModule) DestroyModule() {}
+
+type pgDatabaseTable struct{}
+
+func (t *pgDatabaseTable) Open() (sqlite3.VTabCursor, error) {
+ return &pgDatabaseCursor{}, nil
+}
+
+func (t *pgDatabaseTable) BestIndex(cst []sqlite3.InfoConstraint, ob []sqlite3.InfoOrderBy) (*sqlite3.IndexResult, error) {
+ return &sqlite3.IndexResult{Used: make([]bool, len(cst))}, nil
+}
+
+func (t *pgDatabaseTable) Disconnect() error { return nil }
+func (t *pgDatabaseTable) Destroy() error { return nil }
+
+type pgDatabaseCursor struct {
+ index int
+}
+
+func (c *pgDatabaseCursor) Column(sctx *sqlite3.SQLiteContext, col int) error {
+ switch col {
+ case 0:
+ sctx.ResultInt(pgDatabases[c.index].oid)
+ case 1:
+ sctx.ResultText(pgDatabases[c.index].datname)
+ case 2:
+ sctx.ResultInt(pgDatabases[c.index].datdba)
+ case 3:
+ sctx.ResultInt(pgDatabases[c.index].encoding)
+ case 4:
+ sctx.ResultText(pgDatabases[c.index].datcollate)
+ case 5:
+ sctx.ResultText(pgDatabases[c.index].datctype)
+ case 6:
+ sctx.ResultInt(pgDatabases[c.index].datistemplate)
+ case 7:
+ sctx.ResultInt(pgDatabases[c.index].datallowconn)
+ case 8:
+ sctx.ResultInt(pgDatabases[c.index].datconnlimit)
+ case 9:
+ sctx.ResultInt(pgDatabases[c.index].datlastsysoid)
+ case 10:
+ sctx.ResultInt(pgDatabases[c.index].datfrozenxid)
+ case 11:
+ sctx.ResultInt(pgDatabases[c.index].datminmxid)
+ case 12:
+ sctx.ResultInt(pgDatabases[c.index].dattablespace)
+ case 13:
+ sctx.ResultText(pgDatabases[c.index].datacl)
+ }
+ return nil
+}
+
+func (c *pgDatabaseCursor) Filter(idxNum int, idxStr string, vals []interface{}) error {
+ c.index = 0
+ return nil
+}
+
+func (c *pgDatabaseCursor) Next() error {
+ c.index++
+ return nil
+}
+
+func (c *pgDatabaseCursor) EOF() bool {
+ return c.index >= len(pgDatabases)
+}
+
+func (c *pgDatabaseCursor) Rowid() (int64, error) {
+ return int64(c.index), nil
+}
+
+func (c *pgDatabaseCursor) Close() error {
+ return nil
+}
+
+type pgDatabase struct {
+ oid int
+ datname string
+ datdba int
+ encoding int
+ datcollate string
+ datctype string
+ datistemplate int
+ datallowconn int
+ datconnlimit int
+ datlastsysoid int
+ datfrozenxid int
+ datminmxid int
+ dattablespace int
+ datacl string
+}
+
+var pgDatabases = []pgDatabase{}
diff --git a/pg_description.go b/pg_description.go
new file mode 100644
index 0000000..40bc3fe
--- /dev/null
+++ b/pg_description.go
@@ -0,0 +1,95 @@
+package postlite
+
+import (
+ "fmt"
+
+ "github.com/mattn/go-sqlite3"
+)
+
+type pgDescriptionModule struct{}
+
+func (m *pgDescriptionModule) Create(c *sqlite3.SQLiteConn, args []string) (sqlite3.VTab, error) {
+ err := c.DeclareVTab(fmt.Sprintf(`
+ CREATE TABLE %s (
+ objoid INTEGER,
+ classoid INTEGER,
+ objsubid INTEGER,
+ description TEXT
+ )`, args[0]))
+ if err != nil {
+ return nil, err
+ }
+ return &pgDescriptionTable{}, nil
+}
+
+func (m *pgDescriptionModule) Connect(c *sqlite3.SQLiteConn, args []string) (sqlite3.VTab, error) {
+ return m.Create(c, args)
+}
+
+func (m *pgDescriptionModule) DestroyModule() {}
+
+type pgDescriptionTable struct{}
+
+func (t *pgDescriptionTable) Open() (sqlite3.VTabCursor, error) {
+ return &pgDescriptionCursor{}, nil
+}
+
+func (t *pgDescriptionTable) BestIndex(cst []sqlite3.InfoConstraint, ob []sqlite3.InfoOrderBy) (*sqlite3.IndexResult, error) {
+ return &sqlite3.IndexResult{Used: make([]bool, len(cst))}, nil
+}
+
+func (t *pgDescriptionTable) Disconnect() error { return nil }
+func (t *pgDescriptionTable) Destroy() error { return nil }
+
+type pgDescriptionCursor struct {
+ index int
+}
+
+func (c *pgDescriptionCursor) Column(sctx *sqlite3.SQLiteContext, col int) error {
+ switch col {
+ case 0:
+ sctx.ResultInt(pgDescriptions[c.index].objoid)
+ case 1:
+ sctx.ResultInt(pgDescriptions[c.index].classoid)
+ case 2:
+ sctx.ResultInt(pgDescriptions[c.index].objsubid)
+ case 3:
+ sctx.ResultText(pgDescriptions[c.index].description)
+ }
+ return nil
+}
+
+func (c *pgDescriptionCursor) Filter(idxNum int, idxStr string, vals []interface{}) error {
+ c.index = 0
+ return nil
+}
+
+func (c *pgDescriptionCursor) Next() error {
+ c.index++
+ return nil
+}
+
+func (c *pgDescriptionCursor) EOF() bool {
+ return c.index >= len(pgDescriptions)
+}
+
+func (c *pgDescriptionCursor) Rowid() (int64, error) {
+ return int64(c.index), nil
+}
+
+func (c *pgDescriptionCursor) Close() error {
+ return nil
+}
+
+type pgDescription struct {
+ objoid int
+ classoid int
+ objsubid int
+ description string
+}
+
+var pgDescriptions = []pgDescription{
+ {11, 2615, 0, "system catalog schema"},
+ {99, 2615, 0, "reserved schema for TOAST tables"},
+ {2200, 2615, 0, "standard public schema"},
+}
diff --git a/pg_namespace.go b/pg_namespace.go
new file mode 100644
index 0000000..57164be
--- /dev/null
+++ b/pg_namespace.go
@@ -0,0 +1,96 @@
+package postlite
+
+import (
+ "fmt"
+
+ "github.com/mattn/go-sqlite3"
+)
+
+type pgNamespaceModule struct{}
+
+func (m *pgNamespaceModule) Create(c *sqlite3.SQLiteConn, args []string) (sqlite3.VTab, error) {
+ err := c.DeclareVTab(fmt.Sprintf(`
+ CREATE TABLE %s (
+ oid INT,
+ nspname TEXT,
+ nspowner INTEGER,
+ nspacl TEXT
+ )`, args[0]))
+ if err != nil {
+ return nil, err
+ }
+ return &pgNamespaceTable{}, nil
+}
+
+func (m *pgNamespaceModule) Connect(c *sqlite3.SQLiteConn, args []string) (sqlite3.VTab, error) {
+ return m.Create(c, args)
+}
+
+func (m *pgNamespaceModule) DestroyModule() {}
+
+type pgNamespaceTable struct{}
+
+func (t *pgNamespaceTable) Open() (sqlite3.VTabCursor, error) {
+ return &pgNamespaceCursor{}, nil
+}
+
+func (t *pgNamespaceTable) BestIndex(cst []sqlite3.InfoConstraint, ob []sqlite3.InfoOrderBy) (*sqlite3.IndexResult, error) {
+ return &sqlite3.IndexResult{Used: make([]bool, len(cst))}, nil
+}
+
+func (t *pgNamespaceTable) Disconnect() error { return nil }
+func (t *pgNamespaceTable) Destroy() error { return nil }
+
+type pgNamespaceCursor struct {
+ index int
+}
+
+func (c *pgNamespaceCursor) Column(sctx *sqlite3.SQLiteContext, col int) error {
+ switch col {
+ case 0:
+ sctx.ResultInt(pgNamespaces[c.index].oid)
+ case 1:
+ sctx.ResultText(pgNamespaces[c.index].nspname)
+ case 2:
+ sctx.ResultInt(pgNamespaces[c.index].nspowner)
+ case 3:
+ sctx.ResultText(pgNamespaces[c.index].nspacl)
+ }
+ return nil
+}
+
+func (c *pgNamespaceCursor) Filter(idxNum int, idxStr string, vals []interface{}) error {
+ c.index = 0
+ return nil
+}
+
+func (c *pgNamespaceCursor) Next() error {
+ c.index++
+ return nil
+}
+
+func (c *pgNamespaceCursor) EOF() bool {
+ return c.index >= len(pgNamespaces)
+}
+
+func (c *pgNamespaceCursor) Rowid() (int64, error) {
+ return int64(c.index), nil
+}
+
+func (c *pgNamespaceCursor) Close() error {
+ return nil
+}
+
+type pgNamespace struct {
+ oid int
+ nspname string
+ nspowner int
+ nspacl string
+}
+
+var pgNamespaces = []pgNamespace{
+ {99, "pg_toast", 10, ""},
+ {11, "pg_catalog", 10, ""},
+ {2200, "public", 10, ""},
+ {13427, "information_schema", 10, ""},
+}
diff --git a/pg_settings.go b/pg_settings.go
new file mode 100644
index 0000000..cfd2ab4
--- /dev/null
+++ b/pg_settings.go
@@ -0,0 +1,143 @@
+package postlite
+
+import (
+ "fmt"
+
+ "github.com/mattn/go-sqlite3"
+)
+
+type pgSettingsModule struct{}
+
+func (m *pgSettingsModule) Create(c *sqlite3.SQLiteConn, args []string) (sqlite3.VTab, error) {
+ err := c.DeclareVTab(fmt.Sprintf(`
+ CREATE TABLE %s (
+ name TEXT,
+ setting TEXT,
+ unit TEXT,
+ category TEXT,
+ short_desc TEXT,
+ extra_desc TEXT,
+ context TEXT,
+ vartype TEXT,
+ source TEXT,
+ min_val TEXT,
+ max_val TEXT,
+ enumvals TEXT,
+ boot_val TEXT,
+ reset_val TEXT,
+ sourcefile TEXT,
+ sourceline INTEGER,
+ pending_restart INTEGER
+ )`, args[0]))
+ if err != nil {
+ return nil, err
+ }
+ return &pgSettingsTable{}, nil
+}
+
+func (m *pgSettingsModule) Connect(c *sqlite3.SQLiteConn, args []string) (sqlite3.VTab, error) {
+ return m.Create(c, args)
+}
+
+func (m *pgSettingsModule) DestroyModule() {}
+
+type pgSettingsTable struct{}
+
+func (t *pgSettingsTable) Open() (sqlite3.VTabCursor, error) {
+ return &pgSettingsCursor{}, nil
+}
+
+func (t *pgSettingsTable) BestIndex(cst []sqlite3.InfoConstraint, ob []sqlite3.InfoOrderBy) (*sqlite3.IndexResult, error) {
+ return &sqlite3.IndexResult{Used: make([]bool, len(cst))}, nil
+}
+
+func (t *pgSettingsTable) Disconnect() error { return nil }
+func (t *pgSettingsTable) Destroy() error { return nil }
+
+type pgSettingsCursor struct {
+ index int
+}
+
+func (c *pgSettingsCursor) Column(sctx *sqlite3.SQLiteContext, col int) error {
+ switch col {
+ case 0:
+ sctx.ResultText(pgSettings[c.index].name)
+ case 1:
+ sctx.ResultText(pgSettings[c.index].setting)
+ case 2:
+ sctx.ResultText(pgSettings[c.index].unit)
+ case 3:
+ sctx.ResultText(pgSettings[c.index].category)
+ case 4:
+ sctx.ResultText(pgSettings[c.index].short_desc)
+ case 5:
+ sctx.ResultText(pgSettings[c.index].extra_desc)
+ case 6:
+ sctx.ResultText(pgSettings[c.index].context)
+ case 7:
+ sctx.ResultText(pgSettings[c.index].vartype)
+ case 8:
+ sctx.ResultText(pgSettings[c.index].source)
+ case 9:
+ sctx.ResultText(pgSettings[c.index].min_val)
+ case 10:
+ sctx.ResultText(pgSettings[c.index].max_val)
+ case 11:
+ sctx.ResultText(pgSettings[c.index].enumvals)
+ case 12:
+ sctx.ResultText(pgSettings[c.index].boot_val)
+ case 13:
+ sctx.ResultText(pgSettings[c.index].reset_val)
+ case 14:
+ sctx.ResultText(pgSettings[c.index].sourcefile)
+ case 15:
+ sctx.ResultInt(pgSettings[c.index].sourceline)
+ case 16:
+ sctx.ResultInt(pgSettings[c.index].pending_restart)
+ }
+ return nil
+}
+
+func (c *pgSettingsCursor) Filter(idxNum int, idxStr string, vals []interface{}) error {
+ c.index = 0
+ return nil
+}
+
+func (c *pgSettingsCursor) Next() error {
+ c.index++
+ return nil
+}
+
+func (c *pgSettingsCursor) EOF() bool {
+ return c.index >= len(pgSettings)
+}
+
+func (c *pgSettingsCursor) Rowid() (int64, error) {
+ return int64(c.index), nil
+}
+
+func (c *pgSettingsCursor) Close() error {
+ return nil
+}
+
+type pgSetting struct {
+ name string
+ setting string
+ unit string
+ category string
+ short_desc string
+ extra_desc string
+ context string
+ vartype string
+ source string
+ min_val string
+ max_val string
+ enumvals string
+ boot_val string
+ reset_val string
+ sourcefile string
+ sourceline int
+ pending_restart int
+}
+
+var pgSettings = []pgSetting{}
diff --git a/pg_type.go b/pg_type.go
new file mode 100644
index 0000000..daa4934
--- /dev/null
+++ b/pg_type.go
@@ -0,0 +1,199 @@
+package postlite
+
+import (
+ "fmt"
+
+ "github.com/mattn/go-sqlite3"
+)
+
+type pgTypeModule struct{}
+
+func (m *pgTypeModule) Create(c *sqlite3.SQLiteConn, args []string) (sqlite3.VTab, error) {
+ err := c.DeclareVTab(fmt.Sprintf(`
+ CREATE TABLE %s (
+ oid INTEGER,
+ typname TEXT,
+ typnamespace INTEGER,
+ typowner INTEGER,
+ typlen INTEGER,
+ typbyval INTEGER,
+ typtype TEXT,
+ typcategory TEXT,
+ typispreferred INTEGER,
+ typisdefined INTEGER,
+ typdelim TEXT,
+ typrelid INTEGER,
+ typelem INTEGER,
+ typarray INTEGER,
+ typinput TEXT,
+ typoutput TEXT,
+ typreceive TEXT,
+ typsend TEXT,
+ typmodin TEXT,
+ typmodout TEXT,
+ typanalyze TEXT,
+ typalign TEXT,
+ typstorage TEXT,
+ typnotnull INTEGER,
+ typbasetype INTEGER,
+ typtypmod INTEGER,
+ typndims INTEGER,
+ typcollation INTEGER,
+ typdefaultbin TEXT,
+ typdefault TEXT,
+ typacl TEXT
+ )`, args[0]))
+ if err != nil {
+ return nil, err
+ }
+ return &pgTypeTable{}, nil
+}
+
+func (m *pgTypeModule) Connect(c *sqlite3.SQLiteConn, args []string) (sqlite3.VTab, error) {
+ return m.Create(c, args)
+}
+
+func (m *pgTypeModule) DestroyModule() {}
+
+type pgTypeTable struct{}
+
+func (t *pgTypeTable) Open() (sqlite3.VTabCursor, error) {
+ return &pgTypeCursor{}, nil
+}
+
+func (t *pgTypeTable) BestIndex(cst []sqlite3.InfoConstraint, ob []sqlite3.InfoOrderBy) (*sqlite3.IndexResult, error) {
+ return &sqlite3.IndexResult{Used: make([]bool, len(cst))}, nil
+}
+
+func (t *pgTypeTable) Disconnect() error { return nil }
+func (t *pgTypeTable) Destroy() error { return nil }
+
+type pgTypeCursor struct {
+ index int
+}
+
+func (c *pgTypeCursor) Column(sctx *sqlite3.SQLiteContext, col int) error {
+ switch col {
+ case 0:
+ sctx.ResultInt(pgTypes[c.index].oid)
+ case 1:
+ sctx.ResultText(pgTypes[c.index].typname)
+ case 2:
+ sctx.ResultInt(pgTypes[c.index].typnamespace)
+ case 3:
+ sctx.ResultInt(pgTypes[c.index].typowner)
+ case 4:
+ sctx.ResultInt(pgTypes[c.index].typlen)
+ case 5:
+ sctx.ResultInt(pgTypes[c.index].typbyval)
+ case 6:
+ sctx.ResultText(pgTypes[c.index].typtype)
+ case 7:
+ sctx.ResultText(pgTypes[c.index].typcategory)
+ case 8:
+ sctx.ResultInt(pgTypes[c.index].typispreferred)
+ case 9:
+ sctx.ResultInt(pgTypes[c.index].typisdefined)
+ case 10:
+ sctx.ResultText(pgTypes[c.index].typdelim)
+ case 11:
+ sctx.ResultInt(pgTypes[c.index].typrelid)
+ case 12:
+ sctx.ResultInt(pgTypes[c.index].typelem)
+ case 13:
+ sctx.ResultInt(pgTypes[c.index].typarray)
+ case 14:
+ sctx.ResultText(pgTypes[c.index].typinput)
+ case 15:
+ sctx.ResultText(pgTypes[c.index].typoutput)
+ case 16:
+ sctx.ResultText(pgTypes[c.index].typreceive)
+ case 17:
+ sctx.ResultText(pgTypes[c.index].typsend)
+ case 18:
+ sctx.ResultText(pgTypes[c.index].typmodin)
+ case 19:
+ sctx.ResultText(pgTypes[c.index].typmodout)
+ case 20:
+ sctx.ResultText(pgTypes[c.index].typanalyze)
+ case 21:
+ sctx.ResultText(pgTypes[c.index].typalign)
+ case 22:
+ sctx.ResultText(pgTypes[c.index].typstorage)
+ case 23:
+ sctx.ResultInt(pgTypes[c.index].typnotnull)
+ case 24:
+ sctx.ResultInt(pgTypes[c.index].typbasetype)
+ case 25:
+ sctx.ResultInt(pgTypes[c.index].typtypmod)
+ case 26:
+ sctx.ResultInt(pgTypes[c.index].typndims)
+ case 27:
+ sctx.ResultInt(pgTypes[c.index].typcollation)
+ case 28:
+ sctx.ResultText(pgTypes[c.index].typdefaultbin)
+ case 29:
+ sctx.ResultText(pgTypes[c.index].typdefault)
+ case 30:
+ sctx.ResultText(pgTypes[c.index].typacl)
+ }
+ return nil
+}
+
+func (c *pgTypeCursor) Filter(idxNum int, idxStr string, vals []interface{}) error {
+ c.index = 0
+ return nil
+}
+
+func (c *pgTypeCursor) Next() error {
+ c.index++
+ return nil
+}
+
+func (c *pgTypeCursor) EOF() bool {
+ return c.index >= len(pgTypes)
+}
+
+func (c *pgTypeCursor) Rowid() (int64, error) {
+ return int64(c.index), nil
+}
+
+func (c *pgTypeCursor) Close() error {
+ return nil
+}
+
+type pgType struct {
+ oid int
+ typname string
+ typnamespace int
+ typowner int
+ typlen int
+ typbyval int
+ typtype string
+ typcategory string
+ typispreferred int
+ typisdefined int
+ typdelim string
+ typrelid int
+ typelem int
+ typarray int
+ typinput string
+ typoutput string
+ typreceive string
+ typsend string
+ typmodin string
+ typmodout string
+ typanalyze string
+ typalign string
+ typstorage string
+ typnotnull int
+ typbasetype int
+ typtypmod int
+ typndims int
+ typcollation int
+ typdefaultbin string
+ typdefault string
+ typacl string
+}
+
+var pgTypes = []pgType{}
diff --git a/server.go b/server.go
index 931b745..85b6fb5 100644
--- a/server.go
+++ b/server.go
@@ -42,6 +42,34 @@ func init() {
if err := conn.RegisterFunc("user", user, true); err != nil {
return fmt.Errorf("cannot register user() function")
}
+ if err := conn.RegisterFunc("show", show, true); err != nil {
+ return fmt.Errorf("cannot register show() function")
+ }
+ if err := conn.RegisterFunc("format_type", formatType, true); err != nil {
+ return fmt.Errorf("cannot register format_type() function")
+ }
+ if err := conn.RegisterFunc("version", version, true); err != nil {
+ return fmt.Errorf("cannot register version() function")
+ }
+
+ if err := conn.CreateModule("pg_namespace_module", &pgNamespaceModule{}); err != nil {
+ return fmt.Errorf("cannot register pg_namespace module")
+ }
+ if err := conn.CreateModule("pg_description_module", &pgDescriptionModule{}); err != nil {
+ return fmt.Errorf("cannot register pg_description module")
+ }
+ if err := conn.CreateModule("pg_database_module", &pgDatabaseModule{}); err != nil {
+ return fmt.Errorf("cannot register pg_database module")
+ }
+ if err := conn.CreateModule("pg_settings_module", &pgSettingsModule{}); err != nil {
+ return fmt.Errorf("cannot register pg_settings module")
+ }
+ if err := conn.CreateModule("pg_type_module", &pgTypeModule{}); err != nil {
+ return fmt.Errorf("cannot register pg_type module")
+ }
+ if err := conn.CreateModule("pg_class_module", &pgClassModule{}); err != nil {
+ return fmt.Errorf("cannot register pg_class module")
+ }
return nil
},
})
@@ -49,9 +77,19 @@ func init() {
func currentCatalog() string { return "public" }
func currentSchema() string { return "public" }
-func currentUser() string { return "sqlite3" }
-func sessionUser() string { return "sqlite3" }
-func user() string { return "sqlite3" }
+
+func currentUser() string { return "sqlite3" }
+func sessionUser() string { return "sqlite3" }
+func user() string { return "sqlite3" }
+
+func version() string { return "postlite v0.0.0" }
+
+func formatType(type_oid, typemod string) string { return "" }
+
+func show(name string) string {
+ println("dbg/show", name)
+ return ""
+}
type Server struct {
mu sync.Mutex
@@ -244,6 +282,26 @@ func (s *Server) handleStartupMessage(ctx context.Context, c *Conn, msg *pgproto
return err
}
+ // Register virtual tables to imitate postgres.
+ if _, err := c.db.Exec("CREATE VIRTUAL TABLE IF NOT EXISTS pg_namespace USING pg_namespace_module (oid, nspname, nspowner, nspacl)"); err != nil {
+ return fmt.Errorf("create pg_namespace: %w", err)
+ }
+ if _, err := c.db.Exec("CREATE VIRTUAL TABLE IF NOT EXISTS pg_description USING pg_description_module (objoid, classoid, objsubid, description)"); err != nil {
+ return fmt.Errorf("create pg_description: %w", err)
+ }
+ if _, err := c.db.Exec("CREATE VIRTUAL TABLE IF NOT EXISTS pg_database USING pg_database_module (oid, datname, datdba, encoding, datcollate, datctype, datistemplate, datallowconn, datconnlimit, datlastsysoid, datfrozenxid, datminmxid, dattablespace, datacl)"); err != nil {
+ return fmt.Errorf("create pg_database: %w", err)
+ }
+ if _, err := c.db.Exec("CREATE VIRTUAL TABLE IF NOT EXISTS pg_settings USING pg_settings_module (name, setting, unit, category, short_desc, extra_desc, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, sourcefile, sourceline, pending_restart)"); err != nil {
+ return fmt.Errorf("create pg_settings: %w", err)
+ }
+ if _, err := c.db.Exec("CREATE VIRTUAL TABLE IF NOT EXISTS pg_type USING pg_type_module (oid, typname, typnamespace, typowner, typlen, typbyval, typtype, typcategory, typispreferred, typisdefined, typdelim, typrelid, typelem, typarray, typinput, typoutput, typreceive, typsend, typmodin, typmodout, typanalyze, typalign, typstorage, typnotnull, typbasetype, typtypmod, typndims, typcollation, typdefaultbin, typdefault, typacl)"); err != nil {
+ return fmt.Errorf("create pg_type: %w", err)
+ }
+ if _, err := c.db.Exec("CREATE VIRTUAL TABLE IF NOT EXISTS pg_class USING pg_class_module (oid, relname, relnamespace, reltype, reloftype, relowner, relam, relfilenode, reltablespace, relpages, reltuples, relallvisible, reltoastrelid, relhasindex, relisshared, relpersistence, relkind, relnatts, relchecks, relhasrules, relhastriggers, relhassubclass, relrowsecurity, relforcerowsecurity, relispopulated, relreplident, relispartition, relrewrite, relfrozenxid, relminmxid, relacl, reloptions, relpartbound)"); err != nil {
+ return fmt.Errorf("create pg_class: %w", err)
+ }
+
return writeMessages(c,
&pgproto3.AuthenticationOk{},
&pgproto3.ParameterStatus{Name: "server_version", Value: ServerVersion},
@@ -346,20 +404,21 @@ func (s *Server) handleParseMessage(ctx context.Context, c *Conn, pmsg *pgproto3
// Prepare the query.
stmt, err := c.db.PrepareContext(ctx, query)
if err != nil {
- return err
+ return fmt.Errorf("prepare: %w", err)
}
var rows *sql.Rows
var cols []*sql.ColumnType
+ var binds []interface{}
exec := func() (err error) {
if rows != nil {
return nil
}
- if rows, err = stmt.QueryContext(ctx); err != nil {
- return err
+ if rows, err = stmt.QueryContext(ctx, binds...); err != nil {
+ return fmt.Errorf("query: %w", err)
}
if cols, err = rows.ColumnTypes(); err != nil {
- return err
+ return fmt.Errorf("column types: %w", err)
}
return nil
}
@@ -375,7 +434,10 @@ func (s *Server) handleParseMessage(ctx context.Context, c *Conn, pmsg *pgproto3
switch msg := msg.(type) {
case *pgproto3.Bind:
- // ignore
+ binds = make([]interface{}, len(msg.Parameters))
+ for i := range msg.Parameters {
+ binds[i] = string(msg.Parameters[i])
+ }
case *pgproto3.Describe:
if err := exec(); err != nil {
@@ -458,6 +520,11 @@ func rewriteQuery(q string) string {
return `SELECT 'SET'`
}
+ // Ignore this god forsaken query for pulling keywords.
+ if strings.Contains(q, `select string_agg(word, ',') from pg_catalog.pg_get_keywords()`) {
+ return `SELECT '' AS "string_agg" WHERE 1 = 2`
+ }
+
// Rewrite system information variables so they are functions so we can inject them.
// https://www.postgresql.org/docs/9.1/functions-info.html
q = systemFunctionRegex.ReplaceAllString(q, "$1()$2")
@@ -466,6 +533,12 @@ func rewriteQuery(q string) string {
// https://www.postgresql.org/docs/7.3/sql-expressions.html#SQL-SYNTAX-TYPE-CASTS
q = castRegex.ReplaceAllString(q, "")
+ // Remove references to the pg_catalog.
+ q = pgCatalogRegex.ReplaceAllString(q, "")
+
+ // Rewrite "SHOW" commands into function calls.
+ q = showRegex.ReplaceAllString(q, "SELECT show('$1')")
+
return q
}
@@ -473,4 +546,8 @@ var (
systemFunctionRegex = regexp.MustCompile(`\b(current_catalog|current_schema|current_user|session_user|user)\b([^\(]|$)`)
castRegex = regexp.MustCompile(`::(regclass)`)
+
+ pgCatalogRegex = regexp.MustCompile(`\bpg_catalog\.`)
+
+ showRegex = regexp.MustCompile(`^SHOW (\w+)`)
)