v1-0001-official-devcontainer-config.patch
application/octet-stream
Filename: v1-0001-official-devcontainer-config.patch
Type: application/octet-stream
Part: 0
Message:
Official devcontainer config
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v1-0001
Subject: official devcontainer config
| File | + | − |
|---|---|---|
| .devcontainer/devcontainer.json | 46 | 0 |
| .devcontainer/Dockerfile | 95 | 0 |
| .devcontainer/.gdbinit | 32 | 0 |
| .devcontainer/gdbpg.py | 386 | 0 |
| .devcontainer/launch.json | 20 | 0 |
| .devcontainer/postCreateCommand.sh | 86 | 0 |
| .devcontainer/.psqlrc | 7 | 0 |
| .devcontainer/tasks.json | 219 | 0 |
| .gitignore | 3 | 0 |
From cf7cc066ed86f36f46b2726e97c6c745eed21a51 Mon Sep 17 00:00:00 2001
From: Zhao Junwang <zhjwpku@gmail.com>
Date: Thu, 1 Aug 2024 13:49:53 +0000
Subject: [PATCH v1] official devcontainer config
Signed-off-by: Zhao Junwang <zhjwpku@gmail.com>
---
.devcontainer/.gdbinit | 32 +++
.devcontainer/.psqlrc | 7 +
.devcontainer/Dockerfile | 95 +++++++
.devcontainer/devcontainer.json | 46 ++++
.devcontainer/gdbpg.py | 386 +++++++++++++++++++++++++++++
.devcontainer/launch.json | 20 ++
.devcontainer/postCreateCommand.sh | 86 +++++++
.devcontainer/tasks.json | 219 ++++++++++++++++
.gitignore | 3 +
9 files changed, 894 insertions(+)
create mode 100644 .devcontainer/.gdbinit
create mode 100644 .devcontainer/.psqlrc
create mode 100644 .devcontainer/Dockerfile
create mode 100644 .devcontainer/devcontainer.json
create mode 100644 .devcontainer/gdbpg.py
create mode 100644 .devcontainer/launch.json
create mode 100644 .devcontainer/postCreateCommand.sh
create mode 100644 .devcontainer/tasks.json
diff --git a/.devcontainer/.gdbinit b/.devcontainer/.gdbinit
new file mode 100644
index 0000000000..00ac2119d9
--- /dev/null
+++ b/.devcontainer/.gdbinit
@@ -0,0 +1,32 @@
+# gdbpg.py contains scripts to nicely print the postgres datastructures
+# while in a gdb session. Since the vscode debugger is based on gdb this
+# actually also works when debugging with vscode.
+source /root/gdbpg.py
+
+# when debugging postgres it is convenient to _always_ have a breakpoint
+# trigger when an error is logged. Because .gdbinit is sourced before gdb
+# is fully attached and has the sources loaded. To make sure the breakpoint
+# is added when the library is loaded we temporary set the breakpoint pending
+# to on. After we have added out breakpoint we revert back to the default
+# configuration for breakpoint pending.
+# The breakpoint is hard to read, but at entry of the function we don't have
+# the level loaded in elevel. Instead we hardcode the location where the
+# level of the current error is stored. Also gdb doesn't understand the
+# ERROR symbol so we hardcode this to the value of ERROR. It is very unlikely
+# this value will ever change in postgres, but if it does we might need to
+# find a way to conditionally load the correct breakpoint.
+set breakpoint pending on
+break elog.c:errfinish if errordata[errordata_stack_depth].elevel == 21
+set breakpoint pending auto
+
+echo \n
+echo ----------------------------------------------------------------------------------\n
+echo when attaching to a postgres backend a breakpoint will be set on elog.c:errfinish \n
+echo it will only break on errors being raised in postgres \n
+echo \n
+echo to disable this breakpoint from vscode run `-exec disable 1` in the debug console \n
+echo this assumes it's the first breakpoint loaded as it is loaded from .gdbinit \n
+echo this can be verified with `-exec info break`, enabling can be done with \n
+echo `-exec enable 1` \n
+echo ----------------------------------------------------------------------------------\n
+echo \n
diff --git a/.devcontainer/.psqlrc b/.devcontainer/.psqlrc
new file mode 100644
index 0000000000..7642a97149
--- /dev/null
+++ b/.devcontainer/.psqlrc
@@ -0,0 +1,7 @@
+\timing on
+\pset linestyle unicode
+\pset border 2
+\setenv PAGER 'pspg --no-mouse -bX --no-commandbar --no-topbar'
+\set HISTSIZE 100000
+\set PROMPT1 '\n%[%033[1m%]%M %n@%/:%>-%p%R%[%033[0m%]%# '
+\set PROMPT2 ' '
diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
new file mode 100644
index 0000000000..3a09855faf
--- /dev/null
+++ b/.devcontainer/Dockerfile
@@ -0,0 +1,95 @@
+FROM debian:bookworm AS base
+
+ENV TZ=UTC
+RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
+
+# install build tools(alphabetic order)
+RUN apt update && apt install -y \
+ bash-completion \
+ bison \
+ bzip2 \
+ ccache \
+ cpanminus \
+ cron \
+ curl \
+ flex \
+ fswatch \
+ gcc g++ \
+ gdb \
+ git \
+ htop \
+ iproute2 \
+ libcurl4-gnutls-dev \
+ libgdal-dev \
+ libgeos-dev \
+ libicu-dev \
+ libkrb5-dev \
+ liblz4-dev \
+ libpam0g-dev \
+ libperl-dev \
+ libproj-dev \
+ libprotobuf-c-dev \
+ libreadline-dev \
+ libselinux1-dev \
+ libssl-dev \
+ libxml2-dev \
+ libxslt-dev \
+ libzstd-dev \
+ linux-perf \
+ locales \
+ lsb-release \
+ lsof \
+ make \
+ man \
+ meson \
+ neovim \
+ ninja-build \
+ perl \
+ pkg-config \
+ procps \
+ protobuf-c-compiler \
+ pspg \
+ python3 python3-pip \
+ strace \
+ sudo \
+ tmux \
+ tree \
+ unzip \
+ uuid-dev \
+ valgrind \
+ wget \
+ zlib1g-dev \
+ && apt remove libpq-dev -y \
+ && apt autoremove -y \
+ && apt clean
+
+RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen
+ENV LANG en_US.UTF-8
+ENV LANGUAGE en_US:en
+ENV LC_ALL en_US.UTF-8
+
+RUN cpanm install IPC::Run
+RUN cpanm https://cpan.metacpan.org/authors/id/S/SH/SHANCOCK/Perl-Tidy-20230309.tar.gz
+
+# Since gdb will run in the context of the root user when debugging, we need add
+# both .gdbinit and gdbpg.py script into root home
+# gdbpg.py is adapted from https://github.com/tvondra/gdbpg
+COPY --chown=root:root .gdbinit /root/
+COPY --chown=root:root gdbpg.py /root/
+
+# add the postgres user to sudoers and allow all sudoers to login without a password prompt
+RUN useradd -ms /bin/bash postgres \
+ && usermod -aG sudo postgres \
+ && echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
+
+WORKDIR /home/postgres
+USER postgres
+
+COPY --chown=postgres:postgres .psqlrc .
+
+RUN sudo mkdir -p /opt/freedom \
+ && sudo chown -R postgres:postgres /opt/freedom
+
+ENV PATH="/home/postgres/pgsql/bin:$PATH"
+ENV PGPORT=5432
+EXPOSE 5432
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
new file mode 100644
index 0000000000..f92d116bcf
--- /dev/null
+++ b/.devcontainer/devcontainer.json
@@ -0,0 +1,46 @@
+{
+ "build": {
+ "dockerfile": "Dockerfile"
+ },
+ "runArgs": [
+ "--ulimit=core=-1",
+ "--cap-add=SYS_ADMIN",
+ "--cap-add=SYS_PTRACE",
+ "--security-opt",
+ "seccomp=unconfined",
+ "--privileged"
+ ],
+ "mounts": [
+ // You have to make sure source directory is avaliable on your host file system
+ "source=${localEnv:HOME}/freedom,target=/opt/freedom,type=bind,consistency=cached"
+ ],
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/postgres/postgres,type=bind,consistency=cached",
+ "workspaceFolder": "/home/postgres/postgres",
+ "forwardPorts": [
+ 5432
+ ],
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "eamodio.gitlens",
+ "github.vscode-github-actions",
+ "GitHub.copilot",
+ "ms-vscode.cpptools-extension-pack",
+ "ms-vscode.hexeditor",
+ "ms-vsliveshare.vsliveshare",
+ "rioj7.command-variable",
+ "74th.scrollkey"
+ ],
+ "settings": {
+ "files.exclude": {
+ "**/*.o": true,
+ "**/.deps/": true
+ },
+ "C_Cpp.default.cStandard": "c99",
+ "C_Cpp.default.cppStandard": "c++17",
+ "C_Cpp.default.browse.databaseFilename": "${workspaceFolder}/.vscode/.browse.c_cpp.db"
+ }
+ }
+ },
+ "postCreateCommand": "bash ./.devcontainer/postCreateCommand.sh"
+}
diff --git a/.devcontainer/gdbpg.py b/.devcontainer/gdbpg.py
new file mode 100644
index 0000000000..776570b2ce
--- /dev/null
+++ b/.devcontainer/gdbpg.py
@@ -0,0 +1,386 @@
+import gdb
+
+def format_plan_tree(tree, indent=0):
+ 'formats a plan (sub)tree, with custom indentation'
+
+ # if the pointer is NULL, just return (null) string
+ if (str(tree) == '0x0'):
+ return '-> (NULL)'
+
+ # format all the important fields (similarly to EXPLAIN)
+ retval = '''
+-> %(type)s (cost=%(startup).3f...%(total).3f rows=%(rows)s width=%(width)s)
+\ttarget list:
+%(target)s
+\t%(left)s
+\t%(right)s''' % {
+ 'type' : format_type(tree['type']), # type of the Node
+ 'startup' : float(tree['startup_cost']), # startup cost
+ 'total' : float(tree['total_cost']), # total cost
+ 'rows' : str(tree['plan_rows']), # number of rows
+ 'width' : str(tree['plan_width']), # tuple width (no header)
+
+ # format target list
+ 'target' : format_node_list(tree['targetlist'], 2, True),
+
+ # left subtree
+ 'left' : format_plan_tree(tree['lefttree'], 0),
+
+ # right subtree
+ 'right' : format_plan_tree(tree['righttree'], 0)
+ }
+
+ return add_indent(retval, indent+1)
+
+
+def format_type(t, indent=0):
+ 'strip the leading T_ from the node type tag'
+
+ t = str(t)
+
+ if t.startswith('T_'):
+ t = t[2:]
+
+ return add_indent(t, indent)
+
+
+def format_int_list(lst, indent=0):
+ 'format list containing integer values directly (not warapped in Node)'
+
+ # handle NULL pointer (for List we return NIL
+ if (str(lst) == '0x0'):
+ return '(NIL)'
+
+ # we'll collect the formatted items into a Python list
+ tlist = []
+ item = lst['head']
+
+ # walk the list until we reach the last item
+ while str(item) != '0x0':
+
+ # get item from the list and just grab 'int_value as int'
+ tlist.append(int(item['data']['int_value']))
+
+ # next item
+ item = item['next']
+
+ return add_indent(str(tlist), indent)
+
+
+def format_oid_list(lst, indent=0):
+ 'format list containing Oid values directly (not warapped in Node)'
+
+ # handle NULL pointer (for List we return NIL)
+ if (str(lst) == '0x0'):
+ return '(NIL)'
+
+ # we'll collect the formatted items into a Python list
+ tlist = []
+ item = lst['head']
+
+ # walk the list until we reach the last item
+ while str(item) != '0x0':
+
+ # get item from the list and just grab 'oid_value as int'
+ tlist.append(int(item['data']['oid_value']))
+
+ # next item
+ item = item['next']
+
+ return add_indent(str(tlist), indent)
+
+
+def format_node_list(lst, indent=0, newline=False):
+ 'format list containing Node values'
+
+ # handle NULL pointer (for List we return NIL)
+ if (str(lst) == '0x0'):
+ return '(NIL)'
+
+ # we'll collect the formatted items into a Python list
+ tlist = []
+ item = lst['head']
+
+ # walk the list until we reach the last item
+ while str(item) != '0x0':
+
+ # we assume the list contains Node instances, so grab a reference
+ # and cast it to (Node*)
+ node = cast(item['data']['ptr_value'], 'Node')
+
+ # append the formatted Node to the result list
+ tlist.append(format_node(node))
+
+ # next item
+ item = item['next']
+
+ retval = str(tlist)
+ if newline:
+ retval = "\n".join([str(t) for t in tlist])
+
+ return add_indent(retval, indent)
+
+
+def format_char(value):
+ '''convert the 'value' into a single-character string (ugly, maybe there's a better way'''
+
+ str_val = str(value.cast(gdb.lookup_type('char')))
+
+ # remove the quotes (start/end)
+ return str_val.split(' ')[1][1:-1]
+
+
+def format_relids(relids):
+ return '(not implemented)'
+
+
+def format_node_array(array, start_idx, length, indent=0):
+
+ items = []
+ for i in range(start_idx,start_idx + length - 1):
+ items.append(str(i) + " => " + format_node(array[i]))
+
+ return add_indent(("\n".join(items)), indent)
+
+
+def format_node(node, indent=0):
+ 'format a single Node instance (only selected Node types supported)'
+
+ if str(node) == '0x0':
+ return add_indent('(NULL)', indent)
+
+ retval = '';
+ type_str = str(node['type'])
+
+ if is_a(node, 'TargetEntry'):
+
+ # we assume the list contains Node instances (probably safe for Plan fields)
+ node = cast(node, 'TargetEntry')
+
+ name_ptr = node['resname'].cast(gdb.lookup_type('char').pointer())
+ name = "(NULL)"
+ if str(name_ptr) != '0x0':
+ name = '"' + (name_ptr.string()) + '"'
+
+ retval = 'TargetEntry (resno=%(resno)s resname=%(name)s origtbl=%(tbl)s origcol=%(col)s junk=%(junk)s expr=[%(expr)s])' % {
+ 'resno' : node['resno'],
+ 'name' : name,
+ 'tbl' : node['resorigtbl'],
+ 'col' : node['resorigcol'],
+ 'junk' : (int(node['resjunk']) == 1),
+ 'expr' : format_node(node['expr'])
+ }
+
+ elif is_a(node, 'Var'):
+
+ # we assume the list contains Node instances (probably safe for Plan fields)
+ node = cast(node, 'Var')
+
+ retval = 'Var (varno=%(no)s varattno=%(attno)s levelsup=%(levelsup)s)' % {
+ 'no' : node['varno'],
+ 'attno' : node['varattno'],
+ 'levelsup' : node['varlevelsup']
+ }
+
+ elif is_a(node, 'RangeTblRef'):
+
+ node = cast(node, 'RangeTblRef')
+
+ retval = 'RangeTblRef (rtindex=%d)' % (int(node['rtindex']),)
+
+ elif is_a(node, 'RelOptInfo'):
+
+ node = cast(node, 'RelOptInfo')
+
+ retval = 'RelOptInfo (kind=%(kind)s relids=%(relids)s rtekind=%(rtekind)s relid=%(relid)s rows=%(rows)s width=%(width)s fk=%(fk)s)' % {
+ 'kind' : node['reloptkind'],
+ 'rows' : node['rows'],
+ 'width' : node['width'],
+ 'relid' : node['relid'],
+ 'relids' : format_relids(node['relids']),
+ 'rtekind' : node['rtekind'],
+ 'fk' : (int(node['has_fk_join']) == 1)
+ }
+
+ elif is_a(node, 'RangeTblEntry'):
+
+ node = cast(node, 'RangeTblEntry')
+
+ retval = 'RangeTblEntry (kind=%(rtekind)s relid=%(relid)s relkind=%(relkind)s)' % {
+ 'relid' : node['relid'],
+ 'rtekind' : node['rtekind'],
+ 'relkind' : format_char(node['relkind'])
+ }
+
+ elif is_a(node, 'PlannerInfo'):
+
+ retval = format_planner_info(node)
+
+ elif is_a(node, 'PlannedStmt'):
+
+ retval = format_planned_stmt(node)
+
+ elif is_a(node, 'List'):
+
+ retval = format_node_list(node, 0, True)
+
+ elif is_a(node, 'Plan'):
+
+ retval = format_plan_tree(node)
+
+ elif is_a(node, 'RestrictInfo'):
+
+ node = cast(node, 'RestrictInfo')
+
+ retval = '''RestrictInfo (pushed_down=%(push_down)s can_join=%(can_join)s delayed=%(delayed)s)
+%(clause)s
+%(orclause)s''' % {
+ 'clause' : format_node(node['clause'], 1),
+ 'orclause' : format_node(node['orclause'], 1),
+ 'push_down' : (int(node['is_pushed_down']) == 1),
+ 'can_join' : (int(node['can_join']) == 1),
+ 'delayed' : (int(node['outerjoin_delayed']) == 1)
+ }
+
+ elif is_a(node, 'OpExpr'):
+
+ node = cast(node, 'OpExpr')
+
+ retval = format_op_expr(node)
+
+ elif is_a(node, 'BoolExpr'):
+
+ node = cast(node, 'BoolExpr')
+
+ print node
+
+ retval = format_bool_expr(node)
+
+ else:
+ # default - just print the type name
+ retval = format_type(type_str)
+
+ return add_indent(str(retval), indent)
+
+
+def format_planner_info(info, indent=0):
+
+ # Query *parse; /* the Query being planned */
+ # *glob; /* global info for current planner run */
+ # Index query_level; /* 1 at the outermost Query */
+ # struct PlannerInfo *parent_root; /* NULL at outermost Query */
+ # List *plan_params; /* list of PlannerParamItems, see below */
+
+ retval = '''rel:
+%(rel)s
+rte:
+%(rte)s
+''' % {'rel' : format_node_array(info['simple_rel_array'], 1, int(info['simple_rel_array_size'])),
+ 'rte' : format_node_array(info['simple_rte_array'], 1, int(info['simple_rel_array_size']))}
+
+ return add_indent(retval, indent)
+
+
+def format_planned_stmt(plan, indent=0):
+
+ retval = ''' type: %(type)s
+ query ID: %(qid)s
+ param exec: %(nparam)s
+ returning: %(has_returning)s
+ modifying CTE: %(has_modify_cte)s
+ can set tag: %(can_set_tag)s
+ transient: %(transient)s
+ row security: %(row_security)s
+
+ plan tree: %(tree)s
+ range table:
+%(rtable)s
+ relation OIDs: %(relation_oids)s
+ result rels: %(result_rels)s
+ utility stmt: %(util_stmt)s
+ subplans: %(subplans)s''' % {
+ 'type' : plan['commandType'],
+ 'qid' : plan['queryId'],
+ 'nparam' : plan['nParamExec'],
+ 'has_returning' : (int(plan['hasReturning']) == 1),
+ 'has_modify_cte' : (int(plan['hasModifyingCTE']) == 1),
+ 'can_set_tag' : (int(plan['canSetTag']) == 1),
+ 'transient' : (int(plan['transientPlan']) == 1),
+ 'row_security' : (int(plan['hasRowSecurity']) == 1),
+ 'tree' : format_plan_tree(plan['planTree']),
+ 'rtable' : format_node_list(plan['rtable'], 1, True),
+ 'relation_oids' : format_oid_list(plan['relationOids']),
+ 'result_rels' : format_int_list(plan['resultRelations']),
+ 'util_stmt' : format_node(plan['utilityStmt']),
+ 'subplans' : format_node_list(plan['subplans'], 1, True)
+ }
+
+ return add_indent(retval, indent)
+
+def format_op_expr(node, indent=0):
+
+ return """OpExpr [opno=%(opno)s]
+%(clauses)s""" % { 'opno' : node['opno'],
+ 'clauses' : format_node_list(node['args'], 1, True)}
+
+def format_bool_expr(node, indent=0):
+
+ return """BoolExpr [op=%(op)s]
+%(clauses)s""" % { 'op' : node['boolop'],
+ 'clauses' : format_node_list(node['args'], 1, True)}
+
+def is_a(n, t):
+ '''checks that the node has type 't' (just like IsA() macro)'''
+
+ if not is_node(n):
+ return False
+
+ return (str(n['type']) == ('T_' + t))
+
+
+def is_node(l):
+ '''return True if the value looks like a Node (has 'type' field)'''
+
+ try:
+ x = l['type']
+ return True
+ except:
+ return False
+
+def cast(node, type_name):
+ '''wrap the gdb cast to proper node type'''
+
+ # lookup the type with name 'type_name' and cast the node to it
+ t = gdb.lookup_type(type_name)
+ return node.cast(t.pointer())
+
+
+def add_indent(val, indent):
+
+ return "\n".join([(("\t"*indent) + l) for l in val.split("\n")])
+
+
+class PgPrintCommand(gdb.Command):
+ "print PostgreSQL structures"
+
+ def __init__ (self):
+ super (PgPrintCommand, self).__init__ ("pgprint",
+ gdb.COMMAND_SUPPORT,
+ gdb.COMPLETE_NONE, False)
+
+ def invoke (self, arg, from_tty):
+
+ arg_list = gdb.string_to_argv(arg)
+ if len(arg_list) != 1:
+ print "usage: pgprint var"
+ return
+
+ l = gdb.parse_and_eval(arg_list[0])
+
+ if not is_node(l):
+ print "not a node type"
+
+ print format_node(l)
+
+
+PgPrintCommand()
diff --git a/.devcontainer/launch.json b/.devcontainer/launch.json
new file mode 100644
index 0000000000..a3c0ae98fc
--- /dev/null
+++ b/.devcontainer/launch.json
@@ -0,0 +1,20 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "(gdb) Attach",
+ "type": "cppdbg",
+ "request": "attach",
+ "processId": "${command:pickProcess}",
+ "program": "/home/postgres/pgsql/bin/postgres",
+ "additionalSOLibSearchPath": "/home/postgres/pgsql/lib",
+ "setupCommands": [
+ {
+ "text": "handle SIGUSR1 noprint nostop pass",
+ "description": "let gdb not stop when SIGUSR1 is sent to process",
+ "ignoreFailures": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/.devcontainer/postCreateCommand.sh b/.devcontainer/postCreateCommand.sh
new file mode 100644
index 0000000000..ef84027fc4
--- /dev/null
+++ b/.devcontainer/postCreateCommand.sh
@@ -0,0 +1,86 @@
+#!/bin/bash
+
+function configure_coredumps {
+ sudo sh -c "echo 'core.%p.sig%s.%ts' > /proc/sys/kernel/core_pattern"
+}
+
+function configure_git {
+ git config --global user.email email@example.com
+ git config --global user.name "Your Name"
+ git config --global core.editor vim
+
+ # alias
+ git config --global alias.br "branch -vv"
+ git config --global alias.ci "commit -s"
+ git config --global alias.co checkout
+ git config --global alias.cob "checkout -b"
+ git config --global alias.cp cherry-pick
+ git config --global alias.dh "diff --no-prefix HEAD"
+ git config --global alias.ps push
+ git config --global alias.st status
+ git config --global alias.lg "log --graph --format='%C(auto)%h%C(reset) %C(dim white)%an%C(reset) %C(green)%ai%C(reset) %C(auto)%d%C(reset)%n %s'"
+ git config --global alias.lg10 "log --graph --pretty=format:'%C(yellow)%h%C(auto)%d%Creset %s %C(white)- %an, %ar%Creset' -10"
+ git config --global alias.lg20 "log --graph --pretty=format:'%C(yellow)%h%C(auto)%d%Creset %s %C(white)- %an, %ar%Creset' -20"
+ git config --global alias.lg30 "log --graph --pretty=format:'%C(yellow)%h%C(auto)%d%Creset %s %C(white)- %an, %ar%Creset' -30"
+ git config --global alias.fp "format-patch --stdout --no-prefix"
+
+ git config --global --add safe.directory /home/postgres/postgres
+}
+
+function configure_vscode {
+ mkdir -p .vscode
+ cat <<EOL > ".vscode/c_cpp_properties.json"
+{
+ "configurations": [
+ {
+ "name": "Postgres Development Configuration",
+ "includePath": [
+ "\${workspaceFolder}/**",
+ "\${workspaceFolder}/src/include/",
+ "\${workspaceFolder}/../build/src/include/"
+ ],
+ "cStandard": "c99",
+ "configurationProvider": "ms-vscode.makefile-tools"
+ }
+ ],
+ "version": 4
+}
+EOL
+
+ # Copy the launch.json and tasks.json files if they don't exist
+ if [ ! -f .vscode/launch.json ]; then
+ cp .devcontainer/launch.json .vscode/
+ fi
+ if [ ! -f .vscode/tasks.json ]; then
+ cp .devcontainer/tasks.json .vscode/
+ fi
+}
+
+function configure_perf {
+ sudo sh -c "echo 0 > /proc/sys/kernel/kptr_restrict"
+ sudo su -c "echo -1 > /proc/sys/kernel/perf_event_paranoid"
+
+ if [ ! -d /opt/freedom/tools/FlameGraph ]; then
+ git clone https://github.com/brendangregg/FlameGraph.git /opt/freedom/tools/FlameGraph
+ fi
+}
+
+function configure_pg_plugins {
+ if [ ! -d /opt/freedom/extensions/pg_plugins ]; then
+ git clone https://github.com/michaelpq/pg_plugins.git /opt/freedom/extensions/pg_plugins
+ fi
+}
+
+function main {
+ mkdir -p /opt/freedom/extensions
+ mkdir -p /opt/freedom/patches
+ mkdir -p /opt/freedom/tools
+
+ configure_coredumps
+ configure_git
+ configure_perf
+ configure_pg_plugins
+ configure_vscode
+}
+
+main $@
diff --git a/.devcontainer/tasks.json b/.devcontainer/tasks.json
new file mode 100644
index 0000000000..9759b8ee4b
--- /dev/null
+++ b/.devcontainer/tasks.json
@@ -0,0 +1,219 @@
+{
+ // See https://go.microsoft.com/fwlink/?LinkId=733558
+ // for the documentation about the tasks.json format
+ // See PostgreSQL meson doc link https://wiki.postgresql.org/wiki/Meson
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "meson setup",
+ "type": "shell",
+ "command": "meson",
+ "args": [
+ "setup",
+ "../build", // out of source tree
+ "--prefix=/home/postgres/pgsql",
+ "--buildtype=debug", // default is debugoptimized
+ "-Dc_args=-fno-inline-functions -fno-omit-frame-pointer -DCOPY_PARSE_PLAN_TREES -DWRITE_READ_PARSE_PLAN_TREES -DRAW_EXPRESSION_COVERAGE_TEST -DENFORCE_REGRESSION_TEST_NAME_RESTRICTIONS",
+ "-Dcassert=true",
+ "-Dtap_tests=enabled",
+ "--werror"
+ ],
+ "problemMatcher": [],
+ "detail": "meson setup debug configuration"
+ },
+ {
+ "label": "meson setup release",
+ "type": "shell",
+ "command": "meson",
+ "args": [
+ "setup",
+ "../build",
+ "--prefix=/home/postgres/pgsql",
+ "--buildtype=release"
+ ],
+ "problemMatcher": [],
+ "detail": "meson setup release configuration"
+ },
+ {
+ "label": "clear build directory",
+ "type": "shell",
+ "command": "rm -rf ../build",
+ "problemMatcher": [],
+ "detail": "clear build directory"
+ },
+ {
+ "label": "ninja build",
+ "type": "shell",
+ "command": "ninja",
+ "args":[
+ "-C",
+ "../build",
+ "-j",
+ "4"
+ ],
+ "problemMatcher": [],
+ "detail": "build postgres"
+ },
+ {
+ "label": "regression tests",
+ "type": "shell",
+ "command": "meson test -C ../build -q --print-errorlogs --suite setup --suite regress",
+ "problemMatcher": [],
+ "detail": "run main regression tests"
+ },
+ {
+ "label": "check-world",
+ "type": "shell",
+ "command": "meson test -C ../build -q --print-errorlogs",
+ "problemMatcher": [],
+ "detail": "run all tests"
+ },
+ {
+ "label": "ninja install",
+ "type": "shell",
+ "command": "ninja install -C ../build",
+ "problemMatcher": [],
+ "detail": "install pgsql"
+ },
+ {
+ "label": "init cluster",
+ "type": "shell",
+ "command": "/home/postgres/pgsql/bin/initdb",
+ "args": [
+ "-D",
+ "/home/postgres/pgdata"
+ ],
+ "problemMatcher": [],
+ "detail": "init cluster using initdb"
+ },
+ {
+ "label": "start cluster",
+ "type": "shell",
+ "command": "/home/postgres/pgsql/bin/pg_ctl",
+ "args": [
+ "-D",
+ "/home/postgres/pgdata",
+ "-l",
+ "/home/postgres/pgdata/logfile",
+ "start"
+ ],
+ "problemMatcher": [],
+ "detail": "start db cluster"
+ },
+ {
+ "label": "restart cluster",
+ "type": "shell",
+ "command": "/home/postgres/pgsql/bin/pg_ctl",
+ "args": [
+ "-D",
+ "/home/postgres/pgdata",
+ "-l",
+ "/home/postgres/pgdata/logfile",
+ "restart"
+ ],
+ "problemMatcher": [],
+ "detail": "restart db cluster"
+ },
+ {
+ "label": "stop cluster",
+ "type": "shell",
+ "command": "/home/postgres/pgsql/bin/pg_ctl",
+ "args": [
+ "-D",
+ "/home/postgres/pgdata",
+ "stop"
+ ],
+ "problemMatcher": [],
+ "detail": "stop db cluster"
+ },
+ {
+ "label": "clear pgdata",
+ "type": "shell",
+ "command": "rm -rf /home/postgres/pgdata",
+ "problemMatcher": [],
+ "detail": "clear pgdata directory"
+ },
+ {
+ "label": "install pg_bsd_indent",
+ "type": "shell",
+ "command": "sudo cp ../build/src/tools/pg_bsd_indent/pg_bsd_indent /usr/local/bin",
+ "problemMatcher": [],
+ "detail": "install pg_bsd_indent to /usr/local/bin"
+ },
+ {
+ "label": "pgindent",
+ "type": "shell",
+ "command": "src/tools/pgindent/pgindent ${input:selectDir}",
+ "problemMatcher": [],
+ "detail": "run pgindent on selected directory"
+ },
+ {
+ "label": "format patch",
+ "type": "shell",
+ "command": "git format-patch -o /opt/freedom/patches -${input:formatPatchNumber} -v ${input:formatPatchVersion}",
+ "problemMatcher": [],
+ "detail": "generate patches from the topmost <n> commits"
+ },
+ {
+ "label": "apply patch",
+ "type": "shell",
+ "command": "git am /opt/freedom/patches/${input:patchFile}",
+ "problemMatcher": [],
+ "detail": "apply patch"
+ },
+ {
+ "label": "generate flamegraph",
+ "type": "shell",
+ "command": "perf record -o ../perf_${input:processId}.data -g -F 99 -p ${input:processId} -- sleep 60 && perf script -i ../perf_${input:processId}.data | /opt/freedom/tools/FlameGraph/stackcollapse-perf.pl | /opt/freedom/tools/FlameGraph/flamegraph.pl > /opt/freedom/perf_${input:processId}.svg",
+ "problemMatcher": [],
+ "detail": "generate flamegraph"
+ }
+ ],
+ "inputs": [
+ {
+ "id": "selectDir",
+ "type": "command",
+ "command": "extension.commandvariable.pickStringRemember",
+ "args": {
+ "description": "Which directory to run for pgindent?",
+ "options": [
+ ["Use previous directory", "${remember:srcSubDir}"],
+ ["Pick directory", "${pickFile:srcSubDir}"]
+ ],
+ "default": null,
+ "pickFile": {
+ "srcSubDir": {
+ "description": "Which directory?",
+ "include": "src/**",
+ "showDirs": true,
+ "keyRemember": "srcSubDir"
+ }
+ }
+ }
+ },
+ {
+ "id": "formatPatchNumber",
+ "description": "patches from the topmost <n> commits",
+ "type": "promptString",
+ "default": "1"
+ },
+ {
+ "id": "formatPatchVersion",
+ "description": "<n>-th iteration of the topic",
+ "type": "promptString",
+ "default": "1"
+ },
+ {
+ "id": "patchFile",
+ "description": "Which patch file to apply?",
+ "type": "promptString",
+ "default": "*"
+ },
+ {
+ "id": "processId",
+ "description": "Which process to perf?",
+ "type": "promptString",
+ "default": ""
+ }
+ ]
+}
diff --git a/.gitignore b/.gitignore
index 4e911395fe..952e4c93ef 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,6 +31,7 @@ win32ver.rc
*.exe
lib*dll.def
lib*.pc
+.DS_Store
# Local excludes in root directory
/GNUmakefile
@@ -43,3 +44,5 @@ lib*.pc
/Release/
/tmp_install/
/portlock/
+/.devcontainer/
+/.vscode/
--
2.39.2