prick 0.34.0 → 0.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/exe/prick +27 -10
- data/lib/prick/builder/batch.rb +8 -12
- data/lib/prick/builder/builder.rb +33 -12
- data/lib/prick/builder/node.rb +3 -3
- data/lib/prick/builder/parser.rb +53 -53
- data/lib/prick/share/init/schema/prick/tables.sql +9 -0
- data/lib/prick/subcommand/prick-build.rb +2 -2
- data/lib/prick/subcommand/prick-drop.rb +29 -0
- data/lib/prick/subcommand/prick-make.rb +2 -2
- data/lib/prick/subcommand/prick-run.rb +26 -22
- data/lib/prick/subcommand/prick-snapshot.rb +29 -0
- data/lib/prick/subcommand/subcommand.rb +1 -1
- data/lib/prick/version.rb +1 -1
- data/lib/prick.rb +1 -0
- metadata +3 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 49ed6762f3557cc979f89bb1aaee3d5aa1d8d11a5ba9c7880a8b692d917dc733
         | 
| 4 | 
            +
              data.tar.gz: 16436521e2f9a51ed6c2076ba2a54f968735868a711d170253691a69a4b34fb4
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: eedba5e0757b8f19c94d4bf898ea156b93090cd2466e333860d7a4da9ce91d75fbc4acab596bbc06b0a8f260909c9770715299c55040c017d45b7f679d5528f0
         | 
| 7 | 
            +
              data.tar.gz: 7d98f2a0a8e6ed8230bc44b3d16d1c46f4baa85956bdbcd893325e2d062d8a6d46c4277cf85af8c5e833fb20bbf8324a75d8788d05418cd87185976c6c16a76a
         | 
    
        data/exe/prick
    CHANGED
    
    | @@ -119,19 +119,19 @@ SPEC = %( | |
| 119 119 |  | 
| 120 120 | 
             
                  TODO
         | 
| 121 121 |  | 
| 122 | 
            -
              build! -f,force -t,time --dump=KIND? -- [SCHEMA]
         | 
| 122 | 
            +
              build! --step -f,force -t,time --dump=KIND? -- [SCHEMA]
         | 
| 123 123 | 
             
                  Build the project. If SCHEMA is defined, later schemas are excluded. KIND
         | 
| 124 124 | 
             
                  can be 'nodes', 'allnodes' or 'batches' (the default). Schemas marked
         | 
| 125 125 | 
             
                  with 'no refresh' is not built unless the --force is present
         | 
| 126 126 |  | 
| 127 | 
            -
              make! -t,time --dump=KIND? -- [SCHEMA]
         | 
| 127 | 
            +
              make! --step -t,time --dump=KIND? -- [SCHEMA]
         | 
| 128 128 | 
             
                  @ Only rebuild changed files
         | 
| 129 129 |  | 
| 130 130 | 
             
                  Checks file timestamps against the time of the last build and only
         | 
| 131 131 | 
             
                  rebuild affected parts of the project. KIND can be 'nodes', 'allnodes' or
         | 
| 132 132 | 
             
                  'batches' (the default)
         | 
| 133 133 |  | 
| 134 | 
            -
              run! -t,time --dump=KIND? --schema=SCHEMA -- PATH
         | 
| 134 | 
            +
              run! --step -t,time --dump=KIND? --schema=SCHEMA -- PATH
         | 
| 135 135 | 
             
                  @ Execute path in Prick environment
         | 
| 136 136 |  | 
| 137 137 | 
             
                  Execute a single directory or file within the Prick environment. If given
         | 
| @@ -150,6 +150,12 @@ SPEC = %( | |
| 150 150 | 
             
                  different data sets
         | 
| 151 151 |  | 
| 152 152 | 
             
                  # TODO: A --clean option that resets data
         | 
| 153 | 
            +
                  #
         | 
| 154 | 
            +
             | 
| 155 | 
            +
              snapshot!
         | 
| 156 | 
            +
                  Records maximum ID for all tables and stores them in prick.snapshot. It is
         | 
| 157 | 
            +
                  used by 'drop data' to reset data to the point in time of the snapshot.
         | 
| 158 | 
            +
                  Only one snapshot can be created at a time
         | 
| 153 159 |  | 
| 154 160 | 
             
              version!
         | 
| 155 161 | 
             
                  Print project version
         | 
| @@ -296,7 +302,6 @@ begin | |
| 296 302 | 
             
              database = state.database
         | 
| 297 303 | 
             
              username = state.username
         | 
| 298 304 | 
             
              environment = state.environment
         | 
| 299 | 
            -
             | 
| 300 305 | 
             
              # Process subcommands
         | 
| 301 306 | 
             
              case opts.subcommand
         | 
| 302 307 | 
             
                when :version!
         | 
| @@ -361,27 +366,36 @@ begin | |
| 361 366 | 
             
                  dump = cmd.dump? ? cmd.dump("batches")&.to_sym || :batches : nil
         | 
| 362 367 | 
             
            #     exclude = cmd.exclude? ? cmd.exclude.split(",") : []
         | 
| 363 368 | 
             
                  Prick::SubCommand.build(
         | 
| 364 | 
            -
                      database, username, args.expect(0..1),  | 
| 369 | 
            +
                      database, username, args.expect(0..1), 
         | 
| 370 | 
            +
                      force: cmd.force?, step: cmd.step?, timer: cmd.time?, dump: dump)
         | 
| 365 371 |  | 
| 366 372 | 
             
                when :make!
         | 
| 367 373 | 
             
                  require_db
         | 
| 368 374 | 
             
                  dump = cmd.dump? ? cmd.dump("batches")&.to_sym || :batches : nil
         | 
| 369 | 
            -
                  Prick::SubCommand.make( | 
| 375 | 
            +
                  Prick::SubCommand.make(
         | 
| 376 | 
            +
                      database, username, args.expect(0..1), 
         | 
| 377 | 
            +
                      step: cmd.step?, timer: cmd.time?, dump: dump)
         | 
| 370 378 |  | 
| 371 379 | 
             
                when :run!
         | 
| 372 380 | 
             
                  require_db
         | 
| 373 381 | 
             
                  dump = cmd.dump? ? cmd.dump("batches")&.to_sym || :batches : nil
         | 
| 374 382 | 
             
                  Prick::SubCommand.run(
         | 
| 375 383 | 
             
                      database, username, args.expect(1), 
         | 
| 376 | 
            -
                      timer: cmd.time?, dump: dump, schema: cmd.schema)
         | 
| 384 | 
            +
                      step: cmd.step?, timer: cmd.time?, dump: dump, schema: cmd.schema)
         | 
| 377 385 |  | 
| 378 386 | 
             
                when :bash!
         | 
| 387 | 
            +
                  args.expect(0)
         | 
| 379 388 | 
             
                  Prick::SubCommand.bash(main: cmd.main?)
         | 
| 380 389 |  | 
| 381 390 | 
             
                when :fox!
         | 
| 382 391 | 
             
                  require_db
         | 
| 383 392 | 
             
                  Prick::SubCommand.fox(database, username, args)
         | 
| 384 393 |  | 
| 394 | 
            +
                when :snapshot!
         | 
| 395 | 
            +
                  require_db
         | 
| 396 | 
            +
                  args.expect(0)
         | 
| 397 | 
            +
                  Prick::SubCommand.snapshot(database, username)
         | 
| 398 | 
            +
             | 
| 385 399 | 
             
                when :drop!
         | 
| 386 400 | 
             
                  case cmd.subcommand
         | 
| 387 401 | 
             
                    when :users!
         | 
| @@ -397,7 +411,9 @@ begin | |
| 397 411 | 
             
                      database = args.expect(0..1) || database
         | 
| 398 412 | 
             
                      Prick::SubCommand.drop_database(database)
         | 
| 399 413 | 
             
                    when :data!
         | 
| 400 | 
            -
                       | 
| 414 | 
            +
                      args.expect(0)
         | 
| 415 | 
            +
                      require_db
         | 
| 416 | 
            +
                      Prick::SubCommand.drop_data(database)
         | 
| 401 417 | 
             
                    when :schema!
         | 
| 402 418 | 
             
                      require_db
         | 
| 403 419 | 
             
                      schemas = args.to_a
         | 
| @@ -493,7 +509,8 @@ rescue ShellOpts::Error, Prick::Error => ex | |
| 493 509 | 
             
              raise
         | 
| 494 510 | 
             
              ShellOpts.error(ex.message)
         | 
| 495 511 |  | 
| 496 | 
            -
             | 
| 497 | 
            -
             | 
| 512 | 
            +
            # FIXME
         | 
| 513 | 
            +
            #rescue RuntimeError, IOError, ShellOpts::Failure, Prick::Failure, Prick::Build::PostgresError => ex 
         | 
| 514 | 
            +
            # ShellOpts.failure(ex.message)
         | 
| 498 515 | 
             
            end
         | 
| 499 516 |  | 
    
        data/lib/prick/builder/batch.rb
    CHANGED
    
    | @@ -28,7 +28,8 @@ module Prick | |
| 28 28 | 
             
                end
         | 
| 29 29 |  | 
| 30 30 | 
             
                class SqlBatch < BuildBatch
         | 
| 31 | 
            -
                  def execute | 
| 31 | 
            +
                  def execute
         | 
| 32 | 
            +
            #       puts "#execute"
         | 
| 32 33 | 
             
                    super {
         | 
| 33 34 | 
             
                      begin
         | 
| 34 35 | 
             
                        sql = []
         | 
| @@ -41,22 +42,16 @@ module Prick | |
| 41 42 | 
             
                          sql = [node.source]
         | 
| 42 43 | 
             
                        end
         | 
| 43 44 |  | 
| 44 | 
            -
                        if  | 
| 45 | 
            -
             | 
| 46 | 
            -
             | 
| 47 | 
            -
             | 
| 48 | 
            -
                          end
         | 
| 49 | 
            -
                        else
         | 
| 50 | 
            -
                          node = nil
         | 
| 51 | 
            -
                          conn.execute sql + nodes[sql.size..-1].map(&:source), silent: true
         | 
| 45 | 
            +
                        conn.execute sql, silent: true if !sql.empty?
         | 
| 46 | 
            +
                        for node in nodes # This is marginally faster than concatenating all nodes
         | 
| 47 | 
            +
            #             puts node.source
         | 
| 48 | 
            +
                          conn.execute node.source, silent: true
         | 
| 52 49 | 
             
                        end
         | 
| 53 50 |  | 
| 54 51 | 
             
                      rescue PG::Error => ex
         | 
| 55 52 | 
             
                        error, line, char = conn.err
         | 
| 56 53 | 
             
                        file = nil
         | 
| 57 | 
            -
                        if  | 
| 58 | 
            -
                          ;
         | 
| 59 | 
            -
                        elsif line
         | 
| 54 | 
            +
                        if line
         | 
| 60 55 | 
             
                          # Locate error node and make line number relative
         | 
| 61 56 | 
             
                          if line < nodes.first.lines
         | 
| 62 57 | 
             
                            node = nodes.first
         | 
| @@ -81,6 +76,7 @@ module Prick | |
| 81 76 | 
             
                        elsif node
         | 
| 82 77 | 
             
                          message = "#{error} from #{node.path}"
         | 
| 83 78 | 
             
                        else
         | 
| 79 | 
            +
                          raise ArgumentError, "Oops"
         | 
| 84 80 | 
             
                          message = conn.errmsg + " in SQL batch"
         | 
| 85 81 | 
             
                        end
         | 
| 86 82 | 
             
                        raise PostgresError.new(message, file, line, char)
         | 
| @@ -45,6 +45,9 @@ module Prick | |
| 45 45 | 
             
                  # True if this is a single-file build
         | 
| 46 46 | 
             
                  attr_reader :single
         | 
| 47 47 |  | 
| 48 | 
            +
                  # True if SQL entries should be single-stepped. Default false
         | 
| 49 | 
            +
                  attr_reader :step
         | 
| 50 | 
            +
             | 
| 48 51 | 
             
                  # Pool of nodes. Initialized by #load_pool
         | 
| 49 52 | 
             
                  attr_reader :pool
         | 
| 50 53 |  | 
| @@ -55,9 +58,9 @@ module Prick | |
| 55 58 | 
             
                                    :pg_graph_ignore_schemas, 
         | 
| 56 59 | 
             
                                    :refresh_schemas, :keep_schemas
         | 
| 57 60 |  | 
| 58 | 
            -
                  def batches() @batches  | 
| 61 | 
            +
                  def batches() @batches || create_batches end
         | 
| 59 62 |  | 
| 60 | 
            -
                  def initialize(conn, path, clean = true, single: false, touched: false)
         | 
| 63 | 
            +
                  def initialize(conn, path, clean = true, single: false, touched: false, load_pool: true, step: false)
         | 
| 61 64 | 
             
                    File.exist?(path) or raise Error, "Can't find #{path}"
         | 
| 62 65 | 
             
                    single || File.directory?(path) or raise Error, "Can't find directory #{path}"
         | 
| 63 66 |  | 
| @@ -66,14 +69,20 @@ module Prick | |
| 66 69 | 
             
                    @reflections_file = REFLECTIONS_PATH
         | 
| 67 70 | 
             
                    @clean = clean
         | 
| 68 71 | 
             
                    @single = single
         | 
| 69 | 
            -
                    @ | 
| 72 | 
            +
                    @step = step
         | 
| 70 73 | 
             
                    @root = Parser.parse(conn, path, single: single)
         | 
| 71 | 
            -
                     | 
| 72 | 
            -
                    @batches = nil # Initialized by # | 
| 74 | 
            +
                    @pool = nil # Initialized by #load_pool
         | 
| 75 | 
            +
                    @batches = nil # Initialized by #create_batches
         | 
| 76 | 
            +
                    self.load_pool if load_pool
         | 
| 77 | 
            +
                  end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                  def load_pool
         | 
| 80 | 
            +
                    @pool = NodePool.new
         | 
| 81 | 
            +
                    load_pool_impl(@root)
         | 
| 73 82 | 
             
                  end
         | 
| 74 83 |  | 
| 75 84 | 
             
                  # Group sources into batches
         | 
| 76 | 
            -
                  def  | 
| 85 | 
            +
                  def create_batches
         | 
| 77 86 | 
             
                    @batches = []
         | 
| 78 87 | 
             
                    kind = nil
         | 
| 79 88 | 
             
                    batch = nil
         | 
| @@ -88,8 +97,11 @@ module Prick | |
| 88 97 | 
             
                        when :exe # Exe sources always create a new batch
         | 
| 89 98 | 
             
                          @batches << batch if batch
         | 
| 90 99 | 
             
                          batch = SqlBatch.new(self)
         | 
| 91 | 
            -
                        when batch&.kind
         | 
| 92 | 
            -
                           | 
| 100 | 
            +
                        when batch&.kind # Same kind as current batch
         | 
| 101 | 
            +
                          if node.kind == :sql && step
         | 
| 102 | 
            +
                            @batches << batch if batch
         | 
| 103 | 
            +
                            batch = SqlBatch.new(self)
         | 
| 104 | 
            +
                          end
         | 
| 93 105 | 
             
                        when :sql || node.kind == :inline
         | 
| 94 106 | 
             
                          if batch&.kind != :exe
         | 
| 95 107 | 
             
                            @batches << batch if batch
         | 
| @@ -108,13 +120,17 @@ module Prick | |
| 108 120 | 
             
                      end
         | 
| 109 121 | 
             
                      batch.nodes << node
         | 
| 110 122 | 
             
                    end
         | 
| 111 | 
            -
             | 
| 112 123 | 
             
                    @batches << batch if batch
         | 
| 124 | 
            +
             | 
| 125 | 
            +
                    @batches
         | 
| 113 126 | 
             
                  end
         | 
| 114 127 |  | 
| 115 128 | 
             
                  def execute(conn, create_schemas: schemas)
         | 
| 129 | 
            +
                    # Load pool
         | 
| 130 | 
            +
                    load_pool if pool.nil?
         | 
| 131 | 
            +
             | 
| 116 132 | 
             
                    # Group batches
         | 
| 117 | 
            -
                     | 
| 133 | 
            +
                    create_batches if batches.nil?
         | 
| 118 134 |  | 
| 119 135 | 
             
                    # Register build in database
         | 
| 120 136 | 
             
                    Prick.state.save_build_begin
         | 
| @@ -124,6 +140,10 @@ module Prick | |
| 124 140 |  | 
| 125 141 | 
             
                    # Execute batch groups
         | 
| 126 142 | 
             
                    t0 = Time.now
         | 
| 143 | 
            +
            #       for batch in batches
         | 
| 144 | 
            +
            #         puts "Executing #{batch.class} batch"
         | 
| 145 | 
            +
            #         batch.execute
         | 
| 146 | 
            +
            #       end
         | 
| 127 147 | 
             
                    batches.each(&:execute)
         | 
| 128 148 | 
             
                    t1 = Time.now
         | 
| 129 149 |  | 
| @@ -133,15 +153,16 @@ module Prick | |
| 133 153 | 
             
                  end
         | 
| 134 154 |  | 
| 135 155 | 
             
                  def dump
         | 
| 156 | 
            +
                    load_pool if pool.nil?
         | 
| 136 157 | 
             
                    batches ? batches.each(&:dump) : pool.dump
         | 
| 137 158 | 
             
                  end
         | 
| 138 159 |  | 
| 139 160 | 
             
                private
         | 
| 140 | 
            -
                  def  | 
| 161 | 
            +
                  def load_pool_impl(build_node)
         | 
| 141 162 | 
             
                    pool.add(build_node.init_nodes)
         | 
| 142 163 | 
             
                    build_node.decl_nodes.each { |node| 
         | 
| 143 164 | 
             
                      pool.add node
         | 
| 144 | 
            -
                       | 
| 165 | 
            +
                      load_pool_impl(node) if node.kind == :yml
         | 
| 145 166 | 
             
                    }
         | 
| 146 167 | 
             
                    pool.add(build_node.seed_nodes)
         | 
| 147 168 | 
             
                    pool.add(build_node.term_nodes)
         | 
    
        data/lib/prick/builder/node.rb
    CHANGED
    
    | @@ -11,7 +11,7 @@ module Prick | |
| 11 11 | 
             
                  def name() @name = File.basename(path) end
         | 
| 12 12 |  | 
| 13 13 | 
             
                  # Note that schema defaults to 'public' which may not be what you want in
         | 
| 14 | 
            -
                  #  | 
| 14 | 
            +
                  # some cases
         | 
| 15 15 | 
             
                  def schema() @schema ||= parent&.schema || "public" end
         | 
| 16 16 | 
             
                  def schema=(s) @schema = s end
         | 
| 17 17 |  | 
| @@ -46,7 +46,7 @@ module Prick | |
| 46 46 |  | 
| 47 47 | 
             
                  def to_s() @to_s ||= [path, args].compact.join(" ") end
         | 
| 48 48 | 
             
                  def inspect() to_s end
         | 
| 49 | 
            -
                  def dump() puts "#{inspect} (#{schema})" end
         | 
| 49 | 
            +
                  def dump() puts "#{inspect} (#{@schema || 'nil'})" end
         | 
| 50 50 |  | 
| 51 51 | 
             
                protected
         | 
| 52 52 | 
             
                  def read_source
         | 
| @@ -194,7 +194,7 @@ module Prick | |
| 194 194 | 
             
                    puts "BuildNode #{path}"
         | 
| 195 195 | 
             
                    indent {
         | 
| 196 196 | 
             
                      puts "has_schema: #{has_schema}"
         | 
| 197 | 
            -
                      puts "schema: #{schema}" | 
| 197 | 
            +
                      puts "schema: #{@schema || 'nil'}"
         | 
| 198 198 | 
             
                      puts "refresh: #{refresh_schema.to_s}"
         | 
| 199 199 | 
             
                      puts "pg_graph_ignore_schema: #{pg_graph_ignore_schema}"
         | 
| 200 200 | 
             
                      decl_nodes.each(&:dump)
         | 
    
        data/lib/prick/builder/parser.rb
    CHANGED
    
    | @@ -25,7 +25,6 @@ module Prick | |
| 25 25 | 
             
                  end
         | 
| 26 26 |  | 
| 27 27 | 
             
                private
         | 
| 28 | 
            -
             | 
| 29 28 | 
             
                  # First built unit is a RootBuildNode, the rest are regular BuildNode objects
         | 
| 30 29 | 
             
                  def make_build_unit(parent, path)
         | 
| 31 30 | 
             
                    if @unit
         | 
| @@ -35,62 +34,13 @@ module Prick | |
| 35 34 | 
             
                    end
         | 
| 36 35 | 
             
                  end
         | 
| 37 36 |  | 
| 38 | 
            -
                   | 
| 39 | 
            -
                  #
         | 
| 40 | 
            -
                  # Note that "." is ignored in the search path 
         | 
| 41 | 
            -
                  def find_executable(filename) # ChatGPT
         | 
| 42 | 
            -
                    Prick.state.executable_search_path.split(File::PATH_SEPARATOR).each do |directory|
         | 
| 43 | 
            -
                      next if directory == "."
         | 
| 44 | 
            -
                      path = File.join(directory, filename)
         | 
| 45 | 
            -
                      return path if File.file?(path) && File.executable?(path)
         | 
| 46 | 
            -
                    end
         | 
| 47 | 
            -
                    nil
         | 
| 48 | 
            -
                  end
         | 
| 49 | 
            -
             | 
| 50 | 
            -
                  # Expand environment variables in the given file name
         | 
| 51 | 
            -
                  #
         | 
| 52 | 
            -
                  # #expend_filename substitute '$<variable>' expressions in the filename
         | 
| 53 | 
            -
                  # with the corresponding value in the current environment. If the file
         | 
| 54 | 
            -
                  # was not found, inherited environments are processed hierarchly with the
         | 
| 55 | 
            -
                  # special environment variable ENVIRONMENT set to each PRICK_ENVIRONMENT
         | 
| 56 | 
            -
                  # value in the inherited environments
         | 
| 57 | 
            -
                  #
         | 
| 58 | 
            -
                  # Return the resulting path to the file and nil if not found
         | 
| 59 | 
            -
                  #
         | 
| 60 | 
            -
                  def expand_filename(dir, filename)
         | 
| 61 | 
            -
                    envs = Prick.state.environments
         | 
| 62 | 
            -
                    env = envs[Prick.state.environment]
         | 
| 63 | 
            -
                    bash_vars = Prick.state.bash_environment.dup
         | 
| 64 | 
            -
             | 
| 65 | 
            -
                    last = nil
         | 
| 66 | 
            -
                    for env in [env] + env.ancestors.reverse
         | 
| 67 | 
            -
                      bash_vars["ENVIRONMENT"] = env.name
         | 
| 68 | 
            -
                      file = expand_variables(filename, bash_vars)
         | 
| 69 | 
            -
            #         last ||= (file != last and file) or return nil # return if no ENVIRONMENT substitution
         | 
| 70 | 
            -
                      path = (file.start_with?("/") ? file : File.join(dir, file))
         | 
| 71 | 
            -
             | 
| 72 | 
            -
                      # Check for file (may be executable)
         | 
| 73 | 
            -
                      return path if File.exist?(path)
         | 
| 74 | 
            -
             | 
| 75 | 
            -
                      # Check for executable in search path if file doesn't contain a '/'
         | 
| 76 | 
            -
                      path = find_executable(file) and return path if file !~ /\//
         | 
| 77 | 
            -
                    end
         | 
| 78 | 
            -
             | 
| 79 | 
            -
                    # Return nil if not found
         | 
| 80 | 
            -
                    return nil
         | 
| 81 | 
            -
                  end
         | 
| 82 | 
            -
             | 
| 83 | 
            -
                  # Expand $ENVIRONMENT variable
         | 
| 84 | 
            -
                  def expand_string(string)
         | 
| 85 | 
            -
                    expand_variables(string, Prick.state.bash_environment)
         | 
| 86 | 
            -
                  end
         | 
| 87 | 
            -
             | 
| 88 | 
            -
                  def parse_path(path)
         | 
| 37 | 
            +
                  def parse_path(path, schema: nil)
         | 
| 89 38 | 
             
                    File.exist?(path) or raise Error, "Can't find #{file}"
         | 
| 90 39 | 
             
                    dir = File.dirname(path)
         | 
| 91 40 | 
             
                    file = File.basename(path)
         | 
| 92 41 | 
             
                    unit = make_build_unit(nil, nil)
         | 
| 93 | 
            -
                     | 
| 42 | 
            +
                    unit.schema = schema if schema
         | 
| 43 | 
            +
                    entry = parse_build_entry(unit, dir, file)
         | 
| 94 44 | 
             
                    unit
         | 
| 95 45 | 
             
                  end
         | 
| 96 46 |  | 
| @@ -211,6 +161,56 @@ module Prick | |
| 211 161 | 
             
                      end
         | 
| 212 162 | 
             
                    end
         | 
| 213 163 | 
             
                  end
         | 
| 164 | 
            +
             | 
| 165 | 
            +
                  # Search for an executable in path. Return nil if not found
         | 
| 166 | 
            +
                  #
         | 
| 167 | 
            +
                  # Note that "." is ignored in the search path 
         | 
| 168 | 
            +
                  def find_executable(filename) # ChatGPT
         | 
| 169 | 
            +
                    Prick.state.executable_search_path.split(File::PATH_SEPARATOR).each do |directory|
         | 
| 170 | 
            +
                      next if directory == "."
         | 
| 171 | 
            +
                      path = File.join(directory, filename)
         | 
| 172 | 
            +
                      return path if File.file?(path) && File.executable?(path)
         | 
| 173 | 
            +
                    end
         | 
| 174 | 
            +
                    nil
         | 
| 175 | 
            +
                  end
         | 
| 176 | 
            +
             | 
| 177 | 
            +
                  # Expand environment variables in the given file name
         | 
| 178 | 
            +
                  #
         | 
| 179 | 
            +
                  # #expend_filename substitute '$<variable>' expressions in the filename
         | 
| 180 | 
            +
                  # with the corresponding value in the current environment. If the file
         | 
| 181 | 
            +
                  # was not found, inherited environments are processed hierarchly with the
         | 
| 182 | 
            +
                  # special environment variable ENVIRONMENT set to each PRICK_ENVIRONMENT
         | 
| 183 | 
            +
                  # value in the inherited environments
         | 
| 184 | 
            +
                  #
         | 
| 185 | 
            +
                  # Return the resulting path to the file and nil if not found
         | 
| 186 | 
            +
                  #
         | 
| 187 | 
            +
                  def expand_filename(dir, filename)
         | 
| 188 | 
            +
                    envs = Prick.state.environments
         | 
| 189 | 
            +
                    env = envs[Prick.state.environment]
         | 
| 190 | 
            +
                    bash_vars = Prick.state.bash_environment.dup
         | 
| 191 | 
            +
             | 
| 192 | 
            +
                    last = nil
         | 
| 193 | 
            +
                    for env in [env] + env.ancestors.reverse
         | 
| 194 | 
            +
                      bash_vars["ENVIRONMENT"] = env.name
         | 
| 195 | 
            +
                      file = expand_variables(filename, bash_vars)
         | 
| 196 | 
            +
            #         last ||= (file != last and file) or return nil # return if no ENVIRONMENT substitution
         | 
| 197 | 
            +
                      path = (file.start_with?("/") ? file : File.join(dir, file))
         | 
| 198 | 
            +
             | 
| 199 | 
            +
                      # Check for file (may be executable)
         | 
| 200 | 
            +
                      return path if File.exist?(path)
         | 
| 201 | 
            +
             | 
| 202 | 
            +
                      # Check for executable in search path if file doesn't contain a '/'
         | 
| 203 | 
            +
                      path = find_executable(file) and return path if file !~ /\//
         | 
| 204 | 
            +
                    end
         | 
| 205 | 
            +
             | 
| 206 | 
            +
                    # Return nil if not found
         | 
| 207 | 
            +
                    return nil
         | 
| 208 | 
            +
                  end
         | 
| 209 | 
            +
             | 
| 210 | 
            +
                  # Expand $ENVIRONMENT variable
         | 
| 211 | 
            +
                  def expand_string(string)
         | 
| 212 | 
            +
                    expand_variables(string, Prick.state.bash_environment)
         | 
| 213 | 
            +
                  end
         | 
| 214 214 | 
             
                end
         | 
| 215 215 | 
             
              end
         | 
| 216 216 | 
             
            end
         | 
| @@ -35,3 +35,12 @@ create table builds ( | |
| 35 35 | 
             
              make_duration float -- Duration of enclosing make script
         | 
| 36 36 | 
             
            );
         | 
| 37 37 |  | 
| 38 | 
            +
            create table snapshots (
         | 
| 39 | 
            +
              id integer generated by default as identity primary key,
         | 
| 40 | 
            +
              schema_name varchar not null,
         | 
| 41 | 
            +
              table_name varchar not null,
         | 
| 42 | 
            +
              max_id integer, -- may be null
         | 
| 43 | 
            +
             | 
| 44 | 
            +
              unique(schema_name, table_name)
         | 
| 45 | 
            +
            );
         | 
| 46 | 
            +
             | 
| @@ -7,7 +7,7 @@ module Prick::SubCommand | |
| 7 7 | 
             
                  database, username, schema, 
         | 
| 8 8 | 
             
                  builddir: Prick.state.schema_dir, 
         | 
| 9 9 | 
             
                  force: false, # Build all schemas
         | 
| 10 | 
            -
                  timer: nil, dump: nil)
         | 
| 10 | 
            +
                  step: false, timer: nil, dump: nil)
         | 
| 11 11 |  | 
| 12 12 | 
             
                Timer.on! if timer
         | 
| 13 13 |  | 
| @@ -30,7 +30,7 @@ module Prick::SubCommand | |
| 30 30 | 
             
                      end
         | 
| 31 31 | 
             
                      conn = Prick.state.connection
         | 
| 32 32 |  | 
| 33 | 
            -
                      builder = Prick::Build::Builder.new(conn, builddir)
         | 
| 33 | 
            +
                      builder = Prick::Build::Builder.new(conn, builddir, step: step)
         | 
| 34 34 |  | 
| 35 35 | 
             
                      if exist # Empty (part of) the database
         | 
| 36 36 | 
             
                        if force
         | 
| @@ -41,6 +41,35 @@ module Prick::SubCommand | |
| 41 41 | 
             
                State.connection { |conn| conn.rdbms.drop database }
         | 
| 42 42 | 
             
              end
         | 
| 43 43 |  | 
| 44 | 
            +
              # Drop data not in snapshot
         | 
| 45 | 
            +
              def self.drop_data(database, schemas = nil)
         | 
| 46 | 
            +
                conn = Prick.state.conn
         | 
| 47 | 
            +
                conn.session.triggers(false) {
         | 
| 48 | 
            +
                  if schemas.nil?
         | 
| 49 | 
            +
                    builder = Prick::Build::Builder.new(conn, Prick.state.schema_dir)
         | 
| 50 | 
            +
                    pool = builder.pool
         | 
| 51 | 
            +
                    schemas = pool.refresh_schemas
         | 
| 52 | 
            +
                  end
         | 
| 53 | 
            +
                  schema_list = conn.quote_literal_list(schemas)
         | 
| 54 | 
            +
                  schema_expr = schemas ? "true = true" : "schema_name in #{schema_list}"
         | 
| 55 | 
            +
                  tables = conn.tuples %(
         | 
| 56 | 
            +
                    select schema_name, table_name, max_id 
         | 
| 57 | 
            +
                    from prick.snapshots
         | 
| 58 | 
            +
                    where #{schema_expr}
         | 
| 59 | 
            +
                  )
         | 
| 60 | 
            +
                  tables.each { |schema, table, max_id|
         | 
| 61 | 
            +
                    uid = "#{schema}.#{table}"
         | 
| 62 | 
            +
                    if max_id
         | 
| 63 | 
            +
                      conn.exec "delete from #{uid} where id > #{max_id}"
         | 
| 64 | 
            +
                      conn.schema.set_serial(schema, table, max_id) if conn.schema.sequence(schema, table)
         | 
| 65 | 
            +
                    else
         | 
| 66 | 
            +
                      conn.exec "delete from #{uid}"
         | 
| 67 | 
            +
                      conn.schema.set_serial(schema, table, nil) if conn.schema.sequence(schema, table)
         | 
| 68 | 
            +
                    end
         | 
| 69 | 
            +
                  }
         | 
| 70 | 
            +
                }
         | 
| 71 | 
            +
              end
         | 
| 72 | 
            +
             | 
| 44 73 | 
             
              # Empty the database
         | 
| 45 74 | 
             
              def self.drop_schema(database, schemas = [])
         | 
| 46 75 | 
             
                constrain database, String
         | 
| @@ -3,7 +3,7 @@ | |
| 3 3 | 
             
            require_relative '../builder/builder.rb'
         | 
| 4 4 |  | 
| 5 5 | 
             
            module Prick::SubCommand
         | 
| 6 | 
            -
              def self.make(database, username, schema, timer: nil, dump: nil)
         | 
| 6 | 
            +
              def self.make(database, username, schema, step: false, timer: nil, dump: nil)
         | 
| 7 7 | 
             
                Timer.on! if timer
         | 
| 8 8 | 
             
                Timer.time "Prick::Command#make" do
         | 
| 9 9 | 
             
                  begin
         | 
| @@ -26,7 +26,7 @@ module Prick::SubCommand | |
| 26 26 | 
             
                        clean = true
         | 
| 27 27 | 
             
                      end
         | 
| 28 28 |  | 
| 29 | 
            -
                      builder = Prick::Build::Builder.new(conn, "schema", clean)
         | 
| 29 | 
            +
                      builder = Prick::Build::Builder.new(conn, "schema", clean, step: step)
         | 
| 30 30 |  | 
| 31 31 | 
             
                      if schema 
         | 
| 32 32 | 
             
                        after_schemas = builder.pool.after_schema(schema)
         | 
| @@ -3,7 +3,7 @@ | |
| 3 3 | 
             
            require_relative '../builder/builder.rb'
         | 
| 4 4 |  | 
| 5 5 | 
             
            module Prick::SubCommand
         | 
| 6 | 
            -
              def self.run(database, username, path, timer: nil, dump: nil, schema: nil)
         | 
| 6 | 
            +
              def self.run(database, username, path, step: false, timer: nil, dump: nil, schema: nil)
         | 
| 7 7 |  | 
| 8 8 | 
             
                Timer.on! if timer
         | 
| 9 9 |  | 
| @@ -27,7 +27,7 @@ module Prick::SubCommand | |
| 27 27 | 
             
                      conn = Prick.state.connection
         | 
| 28 28 |  | 
| 29 29 | 
             
                      # Parse
         | 
| 30 | 
            -
                      builder = Prick::Build::Builder.new(conn, path, single: true)
         | 
| 30 | 
            +
                      builder = Prick::Build::Builder.new(conn, path, single: true, load_pool: false, step: step)
         | 
| 31 31 |  | 
| 32 32 | 
             
                      # Register if a build file is referenced and normalize path to
         | 
| 33 33 | 
             
                      # include build.yml of directories
         | 
| @@ -43,9 +43,7 @@ module Prick::SubCommand | |
| 43 43 | 
             
                      # Read schema from build file if possible and decide if schema should
         | 
| 44 44 | 
             
                      # be dropped beforehand
         | 
| 45 45 | 
             
                      if is_build_file
         | 
| 46 | 
            -
                         | 
| 47 | 
            -
                        build_node = pool.all_nodes.first
         | 
| 48 | 
            -
                        if build_node.has_schema
         | 
| 46 | 
            +
                        if builder.root.has_schema
         | 
| 49 47 | 
             
                          !schema or Prick.error "Can't use --schema when doing a schema build"
         | 
| 50 48 | 
             
                          is_schema_rebuild = true
         | 
| 51 49 | 
             
                          schema = build_node.schema
         | 
| @@ -67,6 +65,9 @@ module Prick::SubCommand | |
| 67 65 | 
             
                        end
         | 
| 68 66 | 
             
                      end
         | 
| 69 67 |  | 
| 68 | 
            +
                      # Write back schema to builder
         | 
| 69 | 
            +
                      builder.root.schema = schema
         | 
| 70 | 
            +
             | 
| 70 71 | 
             
                      # Drop schema if needed
         | 
| 71 72 | 
             
                      if is_schema_rebuild
         | 
| 72 73 | 
             
                        conn.schema.drop schema, cascade: true 
         | 
| @@ -78,26 +79,29 @@ module Prick::SubCommand | |
| 78 79 | 
             
                      end
         | 
| 79 80 | 
             
                    end
         | 
| 80 81 |  | 
| 81 | 
            -
                     | 
| 82 | 
            -
                       | 
| 83 | 
            -
                       | 
| 84 | 
            -
             | 
| 85 | 
            -
             | 
| 82 | 
            +
                    if dump
         | 
| 83 | 
            +
                      builder.load_pool
         | 
| 84 | 
            +
                      case dump
         | 
| 85 | 
            +
                        when :nodes; builder.nodes.reject { |node| node.is_a?(Build::BuildNode) }.map &:dump
         | 
| 86 | 
            +
                        when :allnodes; builder.nodes.map &:dump
         | 
| 87 | 
            +
                        when :batches; builder.dump
         | 
| 88 | 
            +
                      else
         | 
| 89 | 
            +
                        Prick.error "Illegal dump type: #{dump.inspect}"
         | 
| 90 | 
            +
                      end
         | 
| 86 91 | 
             
                    else
         | 
| 87 | 
            -
                       | 
| 88 | 
            -
             | 
| 89 | 
            -
             | 
| 90 | 
            -
                    Timer.time "Execute build object" do
         | 
| 91 | 
            -
                      builder.execute conn
         | 
| 92 | 
            +
                      Timer.time "Execute build object" do
         | 
| 93 | 
            +
                        builder.execute conn
         | 
| 94 | 
            +
                      end
         | 
| 92 95 | 
             
                    end
         | 
| 93 96 |  | 
| 94 | 
            -
             | 
| 95 | 
            -
             | 
| 96 | 
            -
             | 
| 97 | 
            -
             | 
| 98 | 
            -
             | 
| 99 | 
            -
             | 
| 100 | 
            -
             | 
| 97 | 
            +
            #     FIXME
         | 
| 98 | 
            +
            #     rescue Prick::Error => ex
         | 
| 99 | 
            +
            #       $stderr.puts ex.message
         | 
| 100 | 
            +
            #       exit 1
         | 
| 101 | 
            +
            #
         | 
| 102 | 
            +
            #     rescue ::Command::Error => ex
         | 
| 103 | 
            +
            #       $stderr.puts ex.message
         | 
| 104 | 
            +
            #       exit 1
         | 
| 101 105 |  | 
| 102 106 | 
             
                  ensure
         | 
| 103 107 | 
             
                    super_conn&.terminate
         | 
| @@ -0,0 +1,29 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require_relative '../builder/builder.rb'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Prick::SubCommand
         | 
| 6 | 
            +
              def self.snapshot(
         | 
| 7 | 
            +
                  database, username, 
         | 
| 8 | 
            +
                  builddir: Prick.state.schema_dir)
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                conn = Prick.state.connection
         | 
| 11 | 
            +
                builder = Prick::Build::Builder.new(conn, builddir)
         | 
| 12 | 
            +
                pool = builder.pool
         | 
| 13 | 
            +
                schemas = pool.refresh_schemas
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                # Clear snapshots table
         | 
| 16 | 
            +
                conn.exec "delete from prick.snapshots"
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                # Fill it again
         | 
| 19 | 
            +
                records = []
         | 
| 20 | 
            +
                for schema in schemas
         | 
| 21 | 
            +
                  conn.schema.list_tables(schema).each { |table|
         | 
| 22 | 
            +
                    max_id = conn.value "select max(id) from #{schema}.#{table}"
         | 
| 23 | 
            +
                    records << { schema_name: schema, table_name: table, max_id: max_id}
         | 
| 24 | 
            +
                  }
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
                conn.insert("prick", "snapshots", records)
         | 
| 27 | 
            +
              end
         | 
| 28 | 
            +
            end
         | 
| 29 | 
            +
             | 
    
        data/lib/prick/version.rb
    CHANGED
    
    
    
        data/lib/prick.rb
    CHANGED
    
    | @@ -53,6 +53,7 @@ require_relative 'prick/subcommand/prick-release.rb' | |
| 53 53 | 
             
            require_relative 'prick/subcommand/prick-run.rb'
         | 
| 54 54 | 
             
            require_relative 'prick/subcommand/prick-set.rb'
         | 
| 55 55 | 
             
            require_relative 'prick/subcommand/prick-setup.rb'
         | 
| 56 | 
            +
            require_relative 'prick/subcommand/prick-snapshot.rb'
         | 
| 56 57 | 
             
            require_relative 'prick/subcommand/prick-teardown.rb'
         | 
| 57 58 |  | 
| 58 59 | 
             
            module Prick
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: prick
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.36.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Claus Rasmussen
         | 
| 8 8 | 
             
            autorequire:
         | 
| 9 9 | 
             
            bindir: exe
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2024- | 
| 11 | 
            +
            date: 2024-05-06 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: semantic
         | 
| @@ -233,6 +233,7 @@ files: | |
| 233 233 | 
             
            - lib/prick/subcommand/prick-run.rb
         | 
| 234 234 | 
             
            - lib/prick/subcommand/prick-set.rb
         | 
| 235 235 | 
             
            - lib/prick/subcommand/prick-setup.rb
         | 
| 236 | 
            +
            - lib/prick/subcommand/prick-snapshot.rb
         | 
| 236 237 | 
             
            - lib/prick/subcommand/prick-teardown.rb
         | 
| 237 238 | 
             
            - lib/prick/subcommand/subcommand.rb
         | 
| 238 239 | 
             
            - lib/prick/version.rb
         |