view cmd/oct2str/main.go @ 4796:199c49f967d2

Some Go clean-ups.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Fri, 25 Oct 2019 17:28:00 +0200
parents 8f745c353784
children
line wrap: on
line source

// This is Free Software under GNU Affero General Public License v >= 3.0
// without warranty, see README.md and license for details.
//
// SPDX-License-Identifier: AGPL-3.0-or-later
// License-Filename: LICENSES/AGPL-3.0.txt
//
// Copyright (C) 2019 by via donau
//   – Österreichische Wasserstraßen-Gesellschaft mbH
// Software engineering by Intevation GmbH
//
// Author(s):
//  * Sascha L. Teichmann <sascha.teichmann@intevation.de>

package main

import (
	"context"
	"crypto/sha1"
	"database/sql"
	"encoding/hex"
	"flag"
	"log"

	"gemma.intevation.de/gemma/pkg/octree"
	"github.com/jackc/pgx"
	"github.com/jackc/pgx/stdlib"
)

const (
	fetchOneOctreeSQL = `
SELECT
  id,
  octree_index
FROM waterway.sounding_results
WHERE mesh_index IS NULL
LIMIT 1`

	storeSTRTreeSQL = `
UPDATE waterway.sounding_results
SET mesh_index = $1, mesh_checksum = $2
WHERE id = $3`
)

func process(ctx context.Context, conn *sql.Conn, count int) error {

	fetch, err := conn.PrepareContext(ctx, fetchOneOctreeSQL)
	if err != nil {
		return err
	}
	defer fetch.Close()

	store, err := conn.PrepareContext(ctx, storeSTRTreeSQL)
	if err != nil {
		return err
	}
	defer store.Close()

	var next func() bool
	if count < 0 {
		next = func() bool { return true }
	} else {
		var c int
		next = func() bool {
			if c < count {
				c++
				return true
			}
			return false
		}
	}

loop:
	for next() {
		switch err := func() error {
			tx, err := conn.BeginTx(ctx, nil)
			if err != nil {
				return err
			}
			defer tx.Rollback()

			var id int64
			var data []byte

			if err = tx.Stmt(fetch).QueryRowContext(ctx).Scan(&id, &data); err != nil {
				return err
			}

			otree, err := octree.Deserialize(data)
			if err != nil {
				return err
			}

			unused := otree.FindUnused()

			log.Printf("unused: %d\n", len(unused))

			str := octree.STRTree{Entries: 16}
			str.BuildWithout(otree.Tin(), unused)

			out, err := str.Bytes()
			if err != nil {
				return err
			}
			h := sha1.New()
			h.Write(out)
			checksum := hex.EncodeToString(h.Sum(nil))
			log.Printf("hash: %s\n", checksum)

			if _, err := tx.Stmt(store).ExecContext(ctx, out, checksum, id); err != nil {
				return err
			}

			return tx.Commit()
		}(); {
		case err == sql.ErrNoRows:
			break loop
		case err != nil:
			return err
		}
	}

	return nil
}

func connect(cc pgx.ConnConfig, count int) error {
	db := stdlib.OpenDB(cc)
	defer db.Close()

	ctx := context.Background()

	conn, err := db.Conn(ctx)
	if err != nil {
		return err
	}
	defer conn.Close()

	return process(ctx, conn, count)
}

func main() {
	var (
		db       = flag.String("database", "gemma", "database name")
		user     = flag.String("user", "sophie", "database user")
		host     = flag.String("host", "localhost", "database host")
		password = flag.String("password", "so2Phie4", "database password")
		port     = flag.Uint("port", 5432, "database port")
		ssl      = flag.String("ssl", "prefer", "SSL mode")
		count    = flag.Int("count", -1, "how many sounding results to convert")
	)

	flag.Parse()
	cc, err := pgx.ParseConnectionString("sslmode=" + *ssl)
	if err != nil {
		log.Fatalf("error: %v\n", err)
	}

	// Do the rest manually to allow whitespace in user/password.
	cc.Host = *host
	cc.Port = uint16(*port)
	cc.User = *user
	cc.Password = *password
	cc.Database = *db

	if err := connect(cc, *count); err != nil {
		log.Fatalf("error: %v\n", err)
	}
}