Very seriously considering switching from bsddb to PostgreSQL. If that
happens, I'd use this schema and this conversion script (incomplete).
This commit is contained in:
parent
76e036eab6
commit
937f4bd0eb
217
model/schema.sql
Normal file
217
model/schema.sql
Normal file
|
@ -0,0 +1,217 @@
|
||||||
|
--
|
||||||
|
-- PostgreSQL database dump
|
||||||
|
--
|
||||||
|
|
||||||
|
SET client_encoding = 'UTF8';
|
||||||
|
SET check_function_bodies = false;
|
||||||
|
SET client_min_messages = warning;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: luminotes; Type: DATABASE; Schema: -; Owner: luminotes
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE DATABASE luminotes WITH TEMPLATE = template0 ENCODING = 'UTF8';
|
||||||
|
|
||||||
|
|
||||||
|
ALTER DATABASE luminotes OWNER TO luminotes;
|
||||||
|
|
||||||
|
\connect luminotes
|
||||||
|
|
||||||
|
SET client_encoding = 'UTF8';
|
||||||
|
SET check_function_bodies = false;
|
||||||
|
SET client_min_messages = warning;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: SCHEMA public; Type: COMMENT; Schema: -; Owner: postgres
|
||||||
|
--
|
||||||
|
|
||||||
|
COMMENT ON SCHEMA public IS 'Standard public schema';
|
||||||
|
|
||||||
|
|
||||||
|
SET search_path = public, pg_catalog;
|
||||||
|
|
||||||
|
SET default_tablespace = '';
|
||||||
|
|
||||||
|
SET default_with_oids = false;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: luminotes_user; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE TABLE luminotes_user (
|
||||||
|
id text NOT NULL,
|
||||||
|
revision timestamp with time zone NOT NULL,
|
||||||
|
username text,
|
||||||
|
salt text,
|
||||||
|
password_hash text,
|
||||||
|
email_address text,
|
||||||
|
storage_bytes integer,
|
||||||
|
rate_plan integer
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
ALTER TABLE public.luminotes_user OWNER TO luminotes;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: luminotes_user_current; Type: VIEW; Schema: public; Owner: luminotes
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE VIEW luminotes_user_current AS
|
||||||
|
SELECT DISTINCT ON (luminotes_user.id) luminotes_user.id, luminotes_user.revision, luminotes_user.username, luminotes_user.salt, luminotes_user.password_hash, luminotes_user.email_address, luminotes_user.storage_bytes, luminotes_user.rate_plan FROM luminotes_user ORDER BY luminotes_user.id, luminotes_user.revision DESC;
|
||||||
|
|
||||||
|
|
||||||
|
ALTER TABLE public.luminotes_user_current OWNER TO luminotes;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: note; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE TABLE note (
|
||||||
|
id text NOT NULL,
|
||||||
|
revision timestamp with time zone NOT NULL,
|
||||||
|
title text,
|
||||||
|
contents text,
|
||||||
|
notebook_id text,
|
||||||
|
startup boolean DEFAULT false,
|
||||||
|
deleted_from_id text,
|
||||||
|
rank numeric
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
ALTER TABLE public.note OWNER TO luminotes;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: note_current; Type: VIEW; Schema: public; Owner: luminotes
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE VIEW note_current AS
|
||||||
|
SELECT DISTINCT ON (note.id) note.id, note.revision, note.title, note.contents, note.notebook_id, note.startup, note.deleted_from_id, note.rank FROM note ORDER BY note.id, note.revision DESC;
|
||||||
|
|
||||||
|
|
||||||
|
ALTER TABLE public.note_current OWNER TO luminotes;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: notebook; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE TABLE notebook (
|
||||||
|
id text NOT NULL,
|
||||||
|
revision timestamp with time zone NOT NULL,
|
||||||
|
name text,
|
||||||
|
trash_id text
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
ALTER TABLE public.notebook OWNER TO luminotes;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: notebook_current; Type: VIEW; Schema: public; Owner: luminotes
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE VIEW notebook_current AS
|
||||||
|
SELECT DISTINCT ON (notebook.id) notebook.id, notebook.revision, notebook.name, notebook.trash_id FROM notebook ORDER BY notebook.id, notebook.revision DESC;
|
||||||
|
|
||||||
|
|
||||||
|
ALTER TABLE public.notebook_current OWNER TO luminotes;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: password_reset; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE TABLE password_reset (
|
||||||
|
email_address text NOT NULL,
|
||||||
|
redeemed boolean,
|
||||||
|
id text NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
ALTER TABLE public.password_reset OWNER TO luminotes;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: user_notebook; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE TABLE user_notebook (
|
||||||
|
user_id text NOT NULL,
|
||||||
|
notebook_id text NOT NULL,
|
||||||
|
read_write boolean DEFAULT false
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
ALTER TABLE public.user_notebook OWNER TO luminotes;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: luminotes_user_pkey; Type: CONSTRAINT; Schema: public; Owner: luminotes; Tablespace:
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY luminotes_user
|
||||||
|
ADD CONSTRAINT luminotes_user_pkey PRIMARY KEY (id, revision);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: note_pkey; Type: CONSTRAINT; Schema: public; Owner: luminotes; Tablespace:
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY note
|
||||||
|
ADD CONSTRAINT note_pkey PRIMARY KEY (id, revision);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: notebook_pkey; Type: CONSTRAINT; Schema: public; Owner: luminotes; Tablespace:
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY notebook
|
||||||
|
ADD CONSTRAINT notebook_pkey PRIMARY KEY (id, revision);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: password_reset_pkey; Type: CONSTRAINT; Schema: public; Owner: luminotes; Tablespace:
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY password_reset
|
||||||
|
ADD CONSTRAINT password_reset_pkey PRIMARY KEY (id);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: user_notebook_pkey; Type: CONSTRAINT; Schema: public; Owner: luminotes; Tablespace:
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY user_notebook
|
||||||
|
ADD CONSTRAINT user_notebook_pkey PRIMARY KEY (user_id, notebook_id);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: note_notebook_id_startup_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE INDEX note_notebook_id_startup_index ON note USING btree (notebook_id, startup);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: note_notebook_id_title_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE INDEX note_notebook_id_title_index ON note USING btree (notebook_id, title);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: password_reset_email_address_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE INDEX password_reset_email_address_index ON password_reset USING btree (email_address);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: public; Type: ACL; Schema: -; Owner: postgres
|
||||||
|
--
|
||||||
|
|
||||||
|
REVOKE ALL ON SCHEMA public FROM PUBLIC;
|
||||||
|
REVOKE ALL ON SCHEMA public FROM postgres;
|
||||||
|
GRANT ALL ON SCHEMA public TO postgres;
|
||||||
|
GRANT ALL ON SCHEMA public TO PUBLIC;
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- PostgreSQL database dump complete
|
||||||
|
--
|
||||||
|
|
127
tools/convertdb.py
Executable file
127
tools/convertdb.py
Executable file
|
@ -0,0 +1,127 @@
|
||||||
|
#!/usr/bin/python2.5
|
||||||
|
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
import psycopg2 as psycopg
|
||||||
|
from controller.Database import Database
|
||||||
|
from controller.Scheduler import Scheduler
|
||||||
|
|
||||||
|
|
||||||
|
def quote( value ):
|
||||||
|
if value is None:
|
||||||
|
return "null"
|
||||||
|
|
||||||
|
value = unicode( value )
|
||||||
|
return "'%s'" % value.replace( "'", "''" ).replace( "\\", r"\\\\" )
|
||||||
|
|
||||||
|
|
||||||
|
class Dumper( object ):
|
||||||
|
def __init__( self, scheduler, database ):
|
||||||
|
self.scheduler = scheduler
|
||||||
|
self.database = database
|
||||||
|
|
||||||
|
self.conn = psycopg.connect( "dbname=luminotes user=luminotes password=dev" )
|
||||||
|
self.cursor = self.conn.cursor()
|
||||||
|
|
||||||
|
thread = self.dump_database()
|
||||||
|
self.scheduler.add( thread )
|
||||||
|
self.scheduler.wait_for( thread )
|
||||||
|
|
||||||
|
def dump_database( self ):
|
||||||
|
inserts = set()
|
||||||
|
|
||||||
|
for key in self.database._Database__db.keys():
|
||||||
|
if not self.database._Database__db.get( key ):
|
||||||
|
continue
|
||||||
|
|
||||||
|
self.database.load( key, self.scheduler.thread )
|
||||||
|
value = ( yield Scheduler.SLEEP )
|
||||||
|
|
||||||
|
class_name = value.__class__.__name__
|
||||||
|
|
||||||
|
if class_name == "Notebook":
|
||||||
|
if ( value.object_id, value.revision ) in inserts: continue
|
||||||
|
inserts.add( ( value.object_id, value.revision ) )
|
||||||
|
|
||||||
|
self.cursor.execute(
|
||||||
|
"insert into notebook " +
|
||||||
|
"( id, revision, name, trash_id ) " +
|
||||||
|
"values ( %s, %s, %s, %s );" %
|
||||||
|
( quote( value.object_id ), quote( value.revision ), quote( value.name ), quote( value.trash and value.trash.object_id or "null" ) )
|
||||||
|
)
|
||||||
|
elif class_name == "Note":
|
||||||
|
if ( value.object_id, value.revision ) in inserts: continue
|
||||||
|
inserts.add( ( value.object_id, value.revision ) )
|
||||||
|
|
||||||
|
self.cursor.execute(
|
||||||
|
"insert into note " +
|
||||||
|
"( id, revision, title, contents, notebook_id, startup, deleted_from_id, rank ) " +
|
||||||
|
"values ( %s, %s, %s, %s, %s, %s, %s, %s );" %
|
||||||
|
( quote( value.object_id ), quote( value.revision ), quote( value.title ), quote( value.contents ), quote( None ), quote( None ), quote( value.deleted_from ), quote( None ) )
|
||||||
|
)
|
||||||
|
# TODO: need to set notebook_id field
|
||||||
|
# TODO: need to set startup field
|
||||||
|
# TODO: need to set rank field based on startup_notes ordering
|
||||||
|
elif class_name == "User":
|
||||||
|
if value.username == None: continue
|
||||||
|
|
||||||
|
if ( value.object_id, value.revision ) in inserts: continue
|
||||||
|
inserts.add( ( value.object_id, value.revision ) )
|
||||||
|
|
||||||
|
self.cursor.execute(
|
||||||
|
"insert into luminotes_user " +
|
||||||
|
"( id, revision, username, salt, password_hash, email_address, storage_bytes, rate_plan ) " +
|
||||||
|
"values ( %s, %s, %s, %s, %s, %s, %s, %s );" %
|
||||||
|
( quote( value.object_id ), quote( value.revision ), quote( value.username ), quote( value._User__salt ), quote( value._User__password_hash ), quote( value.email_address ), value.storage_bytes, value.rate_plan )
|
||||||
|
)
|
||||||
|
for notebook in value.notebooks:
|
||||||
|
if notebook is None: continue
|
||||||
|
|
||||||
|
read_only = ( notebook.__class__.__name__ == "Read_only_notebook" )
|
||||||
|
if read_only:
|
||||||
|
notebook_id = notebook._Read_only_notebook__wrapped.object_id
|
||||||
|
else:
|
||||||
|
notebook_id = notebook.object_id
|
||||||
|
|
||||||
|
if ( value.object_id, notebook_id ) in inserts: continue
|
||||||
|
inserts.add( ( value.object_id, notebook_id ) )
|
||||||
|
|
||||||
|
self.cursor.execute(
|
||||||
|
"insert into user_notebook " +
|
||||||
|
"( user_id, notebook_id, read_write ) " +
|
||||||
|
"values ( %s, %s, %s );" %
|
||||||
|
( quote( value.object_id ), quote( notebook_id ),
|
||||||
|
quote( read_only and "f" or "t" ) )
|
||||||
|
)
|
||||||
|
elif class_name == "Read_only_notebook":
|
||||||
|
pass
|
||||||
|
elif class_name == "Password_reset":
|
||||||
|
if value.redeemed == True: continue
|
||||||
|
|
||||||
|
if ( value.object_id, value.revision ) in inserts: continue
|
||||||
|
inserts.add( ( value.object_id, value.revision ) )
|
||||||
|
|
||||||
|
self.cursor.execute(
|
||||||
|
"insert into password_reset " +
|
||||||
|
"( id, email_address, redeemed ) " +
|
||||||
|
"values ( %s, %s, %s );" %
|
||||||
|
( quote( value.object_id ), quote( value.email_address ), quote( value.redeemed and "t" or "f" ) )
|
||||||
|
)
|
||||||
|
elif class_name == "User_list":
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise Exception( "Unconverted value of type %s" % class_name )
|
||||||
|
|
||||||
|
self.conn.commit()
|
||||||
|
yield None
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
scheduler = Scheduler()
|
||||||
|
database = Database( scheduler, "data.db" )
|
||||||
|
initializer = Dumper( scheduler, database )
|
||||||
|
scheduler.wait_until_idle()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
Reference in New Issue
Block a user