#!/usr/bin/env ruby require 'shellopts' require 'pg_graph.rb' require "pg_graph/timer.rb" include ShellOpts SPEC = %( @ Load, dump, and clean databases pg_graph is a utility that uses the PgGraph module to load, dump, or clean a database. It uses a variety of formats that can be executed by psql(1) ('psql') or by the Postgres library exec() call ('exec', 'sql'). It can also read and write Yaml data ('yaml'). The dump command can also write the meta data and type of the database in yaml format which is useful for debugging. The default is to dump the type in yaml format, this is also the typical use of pg_graph because dump of data is better done using 'pg_dump -a ' except in the special case when the data contains circular foreign-key constraints and the load command is run by an ordinary user. The load command is useful when you want to read Yaml data from other programs into the database -m,meta=EFILE @ Load meta data from YAML file Make pg_graph loads its meta data in YAML format from FILE instead of querying the database -M,marshal=EFILE @ Load meta data from marshalled file Make pg_graph loads its meta data from a marshalled PgMeta object instead of quering the database -r,reflections=EFILE Load reflections from FILE -f,format=FORMAT:sql,exec,psql,yaml Input/output format. Can be one of 'sql', 'exec', 'psql', or 'yaml' (default) The 'psql' format is meant to be fed to the psql(1) command and contains psql(1) meta-commands to silence the output and to terminate on any error. The 'sql' format expects the database triggers to have been disabled beforehand while the 'exec' format includes statements to disble triggers. The 'exec' format also resets serials The yaml format only contains the data and can be loaded by pg_graph. Triggers will be disabled while loading and ID's restored afterwards -k,kind=KIND:meta,type,data Output kind. Can be one of 'meta', 'type' (the default), or 'data' -t,time Emit timings for process load! -- DATABASE [FILE] Loads data into the database. The file format is determined by the file's extension but can also be set explicitly using the --format option. Reads from standard input if FILE is missing dump! -- DATABASE Dumps data on standard output. Default is to dump the type system in yaml format but this can be explicitly set using the --kind and --format options clean! -- DATABASE Cleans the database by emptying all tables ) # Returns a connection/type tuple # def load_type(timer, opts, database) tg = timer.group("initialization") connection = tg.time("connect") { PgConn.new(database) if !opts.meta? && !opts.marshal? } meta = tg.time("meta") { if opts.meta? PgMeta.load_file(opts.meta) elsif opts.marshal? PgMeta.load_marshal(opts.marshal) else PgMeta.new(connection) end } reflector = tg.time("reflector") { opts.reflections? ? PgGraph::Reflector.load_file(opts.reflections) : nil } type = timer.time("type") { PgGraph::Type.new(meta, reflector) } [connection, type] end opts, args = ShellOpts::ShellOpts.process(SPEC, ARGV, :version => PgGraph::VERSION) timing = opts.time? timer = Timer::Timer.new case opts.subcommand || :dump! when :load! database = args.extract(1) file = args.expect(0..1) || "/dev/stdin" if opts.format? format = opts.format else format = case File.extname(file) when ".sql"; "sql" when ".yaml", ".yml"; "yaml" else "yaml" end end case format when "sql", "exec"; connection = timer.time("connect") { PgConn.new(database) } timer.time("load file") { connection.exec(IO.read(file)) } when "psql" timer.time("psql") { system "psql -d #{database} < #{file} >/dev/null" } when "yaml" connection, type = load_type(timer, opts, database) tg = timer.group("read data") data = tg.time("data") { PgGraph::Data.new(type, YAML.load(IO.read(file))) } tg = timer.group("write data") for label, sql in PgGraph::Data::SqlRender.new(data, :exec).to_h tg.time(label) { connection.exec(sql.join) } end end when :dump! database = args.expect(1) case opts.kind || "type" when "meta" connection = timer.time("connect") { PgConn.new(database) if !opts.meta? } meta = timer.time("meta") { opts.meta? ? PgMeta.load_file(opts.meta) : PgMeta.new(connection) } meta.dump when "type" connection, type = load_type(timer, opts, database) type.dump when "data" connection, type = load_type(timer, opts, database) data = timer.time("instantiate") { type.instantiate(connection) } timer.time("dump") { case opts.format || "yaml" when "sql"; puts data.to_sql when "exec"; puts data.to_exec_sql when "psql"; puts data.to_psql_sql when "yaml"; puts data.to_yaml.to_yaml end } end when :clean! database = args.expect(1) connection, type = load_type(timer, opts, database) tg = timer.group("data") data = tg.time("data") { type.instantiate } tg = timer.group("clean data") for label, sql in PgGraph::Data::SqlRender.new(data, :exec).to_h tg.time(label) { connection.exec(sql.join) } end else raise ArgumentError, "Case not matched" end timer.dump($stderr) if timing