diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..cc1ea8b --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,36 @@ +name: ci + +on: + workflow_dispatch: + push: + branches: + - 'master' + tags: + - 'v*' + pull_request: + branches: + - 'master' + +jobs: + docker: + runs-on: ubuntu-latest + steps: + - + name: Checkout + uses: actions/checkout@v3 + - + name: Login to DockerHub + if: github.event_name != 'pull_request' + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - + name: Build and push + if: ${{ github.event_name != 'pull_request' }} + run: | + docker buildx build --platform=linux/amd64 -t ${{ secrets.DOCKERHUB_USERNAME }}/aquery:${{github.sha}} . + docker push ${{ secrets.DOCKERHUB_USERNAME }}/aquery:${{github.sha}} + docker tag ${{ secrets.DOCKERHUB_USERNAME }}/aquery:${{github.sha}} ${{ secrets.DOCKERHUB_USERNAME }}/aquery:latest + docker push ${{ secrets.DOCKERHUB_USERNAME }}/aquery:latest diff --git a/README.md b/README.md index 36d2182..2e6f0f2 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ AQuery++ Database is a cross-platform, In-Memory Column-Store Database that incorporates compiled query execution. (**Note**: If you encounter any problems, feel free to contact me via ys3540@nyu.edu) -`## Docker (Recommended): +## Docker (Recommended): - See installation instructions from [docker.com](https://www.docker.com). Run **docker desktop** to start docker engine. - In AQuery root directory, type `make docker` to build the docker image from scratch. - For Arm-based Mac users, you would have to build and run the **x86_64** docker image because MonetDB doesn't offer official binaries for arm64 Linux. (Run `docker buildx build --platform=linux/amd64 -t aquery .` instead of `make docker`) diff --git a/reconstruct/ast.py b/reconstruct/ast.py index 0fa978d..14ab94e 100644 --- a/reconstruct/ast.py +++ b/reconstruct/ast.py @@ -296,7 +296,7 @@ class projection(ast_node): if 'outfile' in node: self.outfile = outfile(self, node['outfile'], sql = self.sql) if not self.has_postproc: - self.sql += self.outfile.sql + self.sql = self.outfile.sql else: self.outfile = None @@ -398,10 +398,10 @@ class projection(ast_node): else: self.context.emitc(f'{self.out_table.contextname_cpp}->printall(" ","\\n", nullptr, nullptr, {self.limit});') - if self.outfile: - self.outfile.finalize() + if self.outfile and self.has_postproc: + self.outfile.finalize() - if 'into' in node: + if 'into' in node: self.context.emitc(select_into(self, node['into']).ccode) self.has_postproc = True if not self.distinct: @@ -417,7 +417,7 @@ class projection(ast_node): self.context.postproc_end(self.postproc_fname) else: self.context.ccode = '' - if self.limit != 0: + if self.limit != 0 and not self.outfile: self.context.direct_output() class select_distinct(projection): @@ -1186,8 +1186,8 @@ class outfile(ast_node): name="_outfile" def __init__(self, parent, node, context = None, *, sql = None): self.node = node - super().__init__(parent, node, context) self.sql = sql if sql else '' + super().__init__(parent, node, context) def init(self, _): assert(isinstance(self.parent, projection)) @@ -1207,10 +1207,10 @@ class outfile(ast_node): def produce_monetdb(self, node): filename = node['loc']['literal'] if 'loc' in node else node['literal'] import os - p = os.path.abspath('.').replace('\\', '/') + '/' + filename - self.sql = f'COPY {self.sql} INTO "{p}"' - d = '\t' - e = '\n' + p = os.path.abspath('.').replace('\\', '/') + '/' + filename + self.sql = f'COPY {self.parent.sql} INTO \'{p}\'' + d = ',' + e = '\\n' if 'term' in node: d = node['term']['literal'] self.sql += f' delimiters \'{d}\', \'{e}\'' diff --git a/reconstruct/storage.py b/reconstruct/storage.py index 983f866..c8f5e69 100644 --- a/reconstruct/storage.py +++ b/reconstruct/storage.py @@ -253,6 +253,10 @@ class Context: def direct_output(self): self.queries.append('O') + def abandon_postproc(self): + self.ccode = '' + self.finalize_query() + def finalize_udf(self): if self.udf is not None: return (Context.udf_head