GH-41246: [Docs][C++][Python] Improve docs on column encryption for nested fields - #45411

Merged
AlenkaF merged 16 commits into
apache:mainfrom
EnricoMi:docs-column-encryption-nested-fields
Mar 26, 2025
Merged

GH-41246: [Docs][C++][Python] Improve docs on column encryption for nested fields#45411
AlenkaF merged 16 commits into
apache:mainfrom
EnricoMi:docs-column-encryption-nested-fields

Conversation

@EnricoMi

@EnricoMiEnricoMi commented Feb 1, 2025

Copy link
Copy Markdown
Collaborator

Rationale for this change

Encrypting columns with nested fields with a column key is not trivial since only leaf fields are allowed in the column key map. Documentation emphasizes this fact and provides examples.

What changes are included in this PR?

This amends the documentation on encryption for C++ and Python.

Are these changes tested?

Only documentation.

Are there any user-facing changes?

Only documentation.

@github-actions

Copy link
Copy Markdown

⚠️ GitHub issue #41246has been automatically assigned in GitHub to PR creator.

@EnricoMi
EnricoMiforce-pushed the docs-column-encryption-nested-fields branch from 543bd7b to 56e803dCompareFebruary 1, 2025 16:58

@pitroupitrou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for noticing and document this @EnricoMi . Here are assorted comments.

Comment threaddocs/source/cpp/parquet.rst Outdated
Comment on lines +658 to +661
encryption_config->column_keys = "column_key_name: "
"ListColumn.list.element, "
"MapColumn.key_value.key, MapColumn.key_value.value, "
"StructColumn.f1, StructColumn.f2"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are the spaces embedded in the string actually supported? Also, it seems to lack a semicolon at the end of the line.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it supports extra whitespaces, it trims the strings. The final semicolon is not needed, it would introduce a next empty section:

ColumnPathToEncryptionPropertiesMap CryptoFactory::GetColumnEncryptionProperties(
int dek_length, const std::string& column_keys, FileKeyWrapper* key_wrapper) {
ColumnPathToEncryptionPropertiesMap encrypted_columns;
std::vector<::std::string_view> key_to_columns =
::arrow::internal::SplitString(column_keys, ';');
for (size_t i = 0; i < key_to_columns.size(); ++i) {
std::string cur_key_to_columns =
::arrow::internal::TrimString(std::string(key_to_columns[i]));
if (cur_key_to_columns.empty()) {
continue;
}
std::vector<::std::string_view> parts =
::arrow::internal::SplitString(cur_key_to_columns, ':');
if (parts.size() != 2) {
std::ostringstream message;
message << "Incorrect key to columns mapping in column keys property"
<< ": [" << cur_key_to_columns << "]";
throwParquetException(message.str());
}
std::string column_key_id = ::arrow::internal::TrimString(std::string(parts[0]));
if (column_key_id.empty()) {
throwParquetException("Empty key name in column keys property.");
}
std::string column_names_str = ::arrow::internal::TrimString(std::string(parts[1]));
std::vector<::std::string_view> column_names =
::arrow::internal::SplitString(column_names_str, ',');
if (0 == column_names.size()) {
throwParquetException("No columns to encrypt defined for key: " + column_key_id);
}
for (size_t j = 0; j < column_names.size(); ++j) {
std::string column_name =
::arrow::internal::TrimString(std::string(column_names[j]));
if (column_name.empty()) {
std::ostringstream message;
message << "Empty column name in column keys property for key: " << column_key_id;
throwParquetException(message.str());
}
if (encrypted_columns.find(column_name) != encrypted_columns.end()) {
throwParquetException("Multiple keys defined for the same column: " +
column_name);
}
std::string column_key(dek_length, '\0');
RandBytes(reinterpret_cast<uint8_t*>(column_key.data()), column_key.size());
std::string column_key_key_metadata =
key_wrapper->GetEncryptionKeyMetadata(column_key, column_key_id, false);
std::shared_ptr<ColumnEncryptionProperties> cmd =
ColumnEncryptionProperties::Builder(column_name)
.key(column_key)
->key_metadata(column_key_key_metadata)
->build();
encrypted_columns.insert({column_name, cmd});
}
}
if (encrypted_columns.empty()) {
throwParquetException("No column keys configured in column keys property.");
}
return encrypted_columns;
}

Comment threaddocs/source/cpp/parquet.rst Outdated

An example for writing a dataset using encrypted Parquet file format:

.. code-block:: cpp

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jorisvandenbossche@AlenkaF@raulcd What is our preferred policy for code examples? Do we put them inline in the docs? Do we use separate files?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: it seems we use literalinclude directives from C++ example files that are compiled as part of CI runs. See for example https://github.com/apache/arrow/blob/main/docs/source/cpp/dataset.rst#reading-datasets

Comment threaddocs/source/cpp/parquet.rst Outdated
Comment threaddocs/source/cpp/parquet.rst Outdated
Comment threaddocs/source/python/parquet.rst Outdated
Comment threaddocs/source/python/parquet.rst Outdated
@github-actionsgithub-actionsBot added awaiting committer review Awaiting committer review and removed awaiting review Awaiting review labels Feb 6, 2025
@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

There is some improvement for this non-intuitive naming scheme: #45462

@pitroupitrou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This LGTM except that we probably want to live in a separate file (see comment below).

@EnricoMi
EnricoMiforce-pushed the docs-column-encryption-nested-fields branch from 6e27abf to 30c6184CompareFebruary 12, 2025 15:29
@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou moved the C++ code into a file and referenced that from parquet.rst.

@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou addressed your comments, can this docs improvement be merged?

@pitrou

Copy link
Copy Markdown
Member

The CI failures seem unexpected, can you rebase/merge from latest git main?

@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

Rebased with latest main commit d88ef57.

@pitrou

Copy link
Copy Markdown
Member

Ok, the CI failures are certainly unrelated.

@pitrou

Copy link
Copy Markdown
Member

@github-actions crossbow submit docs-preview

@github-actions

Copy link
Copy Markdown
Unable to match any tasks for `docs-preview`
The Archery job run can be found at: https://github.com/apache/arrow/actions/runs/13717903134

@AlenkaF

Copy link
Copy Markdown
Member

@github-actions crossbow submit preview-docs

@github-actions

Copy link
Copy Markdown

Revision: 52fb219

Submitted crossbow builds: ursacomputing/crossbow @ actions-3c034c02a1

TaskStatus
preview-docsGitHub Actions

@AlenkaFAlenkaF left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AlenkaF
AlenkaF requested a review from pitrouMarch 18, 2025 12:16
@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou are you happy with this?

Comment threaddocs/source/cpp/parquet.rst Outdated
Comment threaddocs/source/cpp/parquet.rst Outdated
Comment on lines +620 to +622
Encrypting columns that have nested fields (struct, map, or even list data types)
requires column keys for the inner fields, not the column itself.
Configuring a column key for the column itself causes this error (here column name is ``col``):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Encrypting columns that have nested fields (struct, map, or even list data types)
requires column keys for the inner fields, not the column itself.
Configuring a column key for the column itself causes this error (here column name is ``col``):
Encrypting columns that have nested fields (struct, map or list data types)
requires column keys for the inner fields, not the nested column itself.
Configuring a column key for the nested column causes
this error (here the column name is ``col``):

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find nested column ambiguous, it sounds like the inner column, the nested field.

What about outer column instead of nested column?

Suggested change
Encrypting columns that have nested fields (struct, map, or even list data types)
requires column keys for the inner fields, not the column itself.
Configuring a column key for the column itself causes this error (here column name is ``col``):
Encrypting columns that have nested fields (struct, map, or even list data types)
requires column keys for the inner fields, not the outer column itself.
Configuring a column key for the outer column causes this error (here column name is ``col``):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, that's better!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also two wording nits:

  • "struct, map, or even list data types" -> "struct, map, or list data types" (there's nothing special about lists)
  • "here column name is" -> "here the column name is"

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, missed those of your changes when creating my suggestion. All incorporated now.

Comment threaddocs/source/cpp/parquet.rst Outdated
Comment threaddocs/source/python/parquet.rst Outdated
Comment threaddocs/source/python/parquet.rst Outdated
)

.. note::
Encrypting columns that have nested fields (for instance struct, map, or even list data types)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(same suggestions as for C++)

// specific language governing permissions and limitations
// under the License.

#include <arrow/util/logging.h>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO We should avoid using this header in public examples. std::cerr is good enough here.

#include "arrow/result.h"
#include "parquet/arrow/reader.h"

#include <arrow/filesystem/path_util.h>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move this include together with other Arrow includes above?

Comment threadcpp/examples/arrow/parquet_column_encryption.cc
EnricoMiand others added 2 commits March 25, 2025 14:21
Co-authored-by: Antoine Pitrou <pitrou@free.fr>
@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou all comments addressed, one alternative suggested at #45411 (comment)

#include "arrow/api.h"
#include "arrow/dataset/file_parquet.h"
#include "arrow/dataset/parquet_encryption_config.h"
#include "arrow/filesystem//localfs.h"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small nit

Suggested change
#include"arrow/filesystem//localfs.h"
#include"arrow/filesystem/localfs.h"

@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou comments addressed

@pitroupitrou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for doing this @EnricoMi !

@pitrou

Copy link
Copy Markdown
Member

@github-actions crossbow submit preview-docs

@github-actions

Copy link
Copy Markdown

Revision: b47168d

Submitted crossbow builds: ursacomputing/crossbow @ actions-ea617ad0a3

TaskStatus
preview-docsGitHub Actions

@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

Thank you for your time, @pitrou!

@AlenkaF
AlenkaF merged commit 5d0149d into apache:mainMar 26, 2025
@AlenkaFAlenkaF removed the awaiting committer review Awaiting committer review label Mar 26, 2025
@conbench-apache-arrow

Copy link
Copy Markdown

After merging your PR, Conbench analyzed the 0 benchmarking runs that have been run so far on merge-commit 5d0149d.

None of the specified runs were found on the Conbench server.

The full Conbench report has more details.

zanmato1984 pushed a commit to zanmato1984/arrow that referenced this pull request Apr 15, 2025
… for nested fields (apache#45411)
### Rationale for this change
Encrypting columns with nested fields with a column key is not trivial since only leaf fields are allowed in the column key map. Documentation emphasizes this fact and provides examples.
### What changes are included in this PR?
This amends the documentation on encryption for C++ and Python.
### Are these changes tested?
Only documentation.
### Are there any user-facing changes?
Only documentation.
* GitHub Issue: apache#41246
Lead-authored-by: Enrico Minack <github@enrico.minack.dev>
Co-authored-by: Antoine Pitrou <pitrou@free.fr>
Signed-off-by: AlenkaF <frim.alenka@gmail.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@EnricoMi@pitrou@AlenkaF
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

GH-41246: [Docs][C++][Python] Improve docs on column encryption for nested fields - #45411

Merged
AlenkaF merged 16 commits into
apache:mainfrom
EnricoMi:docs-column-encryption-nested-fields
Mar 26, 2025
Merged

GH-41246: [Docs][C++][Python] Improve docs on column encryption for nested fields#45411
AlenkaF merged 16 commits into
apache:mainfrom
EnricoMi:docs-column-encryption-nested-fields

Conversation

@EnricoMi

@EnricoMiEnricoMi commented Feb 1, 2025

Copy link
Copy Markdown
Collaborator

Rationale for this change

Encrypting columns with nested fields with a column key is not trivial since only leaf fields are allowed in the column key map. Documentation emphasizes this fact and provides examples.

What changes are included in this PR?

This amends the documentation on encryption for C++ and Python.

Are these changes tested?

Only documentation.

Are there any user-facing changes?

Only documentation.

@github-actions

Copy link
Copy Markdown

⚠️ GitHub issue #41246has been automatically assigned in GitHub to PR creator.

@EnricoMi
EnricoMiforce-pushed the docs-column-encryption-nested-fields branch from 543bd7b to 56e803dCompareFebruary 1, 2025 16:58

@pitroupitrou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for noticing and document this @EnricoMi . Here are assorted comments.

Comment threaddocs/source/cpp/parquet.rst Outdated
Comment on lines +658 to +661
encryption_config->column_keys = "column_key_name: "
"ListColumn.list.element, "
"MapColumn.key_value.key, MapColumn.key_value.value, "
"StructColumn.f1, StructColumn.f2"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are the spaces embedded in the string actually supported? Also, it seems to lack a semicolon at the end of the line.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it supports extra whitespaces, it trims the strings. The final semicolon is not needed, it would introduce a next empty section:

ColumnPathToEncryptionPropertiesMap CryptoFactory::GetColumnEncryptionProperties(
int dek_length, const std::string& column_keys, FileKeyWrapper* key_wrapper) {
ColumnPathToEncryptionPropertiesMap encrypted_columns;
std::vector<::std::string_view> key_to_columns =
::arrow::internal::SplitString(column_keys, ';');
for (size_t i = 0; i < key_to_columns.size(); ++i) {
std::string cur_key_to_columns =
::arrow::internal::TrimString(std::string(key_to_columns[i]));
if (cur_key_to_columns.empty()) {
continue;
}
std::vector<::std::string_view> parts =
::arrow::internal::SplitString(cur_key_to_columns, ':');
if (parts.size() != 2) {
std::ostringstream message;
message << "Incorrect key to columns mapping in column keys property"
<< ": [" << cur_key_to_columns << "]";
throwParquetException(message.str());
}
std::string column_key_id = ::arrow::internal::TrimString(std::string(parts[0]));
if (column_key_id.empty()) {
throwParquetException("Empty key name in column keys property.");
}
std::string column_names_str = ::arrow::internal::TrimString(std::string(parts[1]));
std::vector<::std::string_view> column_names =
::arrow::internal::SplitString(column_names_str, ',');
if (0 == column_names.size()) {
throwParquetException("No columns to encrypt defined for key: " + column_key_id);
}
for (size_t j = 0; j < column_names.size(); ++j) {
std::string column_name =
::arrow::internal::TrimString(std::string(column_names[j]));
if (column_name.empty()) {
std::ostringstream message;
message << "Empty column name in column keys property for key: " << column_key_id;
throwParquetException(message.str());
}
if (encrypted_columns.find(column_name) != encrypted_columns.end()) {
throwParquetException("Multiple keys defined for the same column: " +
column_name);
}
std::string column_key(dek_length, '\0');
RandBytes(reinterpret_cast<uint8_t*>(column_key.data()), column_key.size());
std::string column_key_key_metadata =
key_wrapper->GetEncryptionKeyMetadata(column_key, column_key_id, false);
std::shared_ptr<ColumnEncryptionProperties> cmd =
ColumnEncryptionProperties::Builder(column_name)
.key(column_key)
->key_metadata(column_key_key_metadata)
->build();
encrypted_columns.insert({column_name, cmd});
}
}
if (encrypted_columns.empty()) {
throwParquetException("No column keys configured in column keys property.");
}
return encrypted_columns;
}

Comment threaddocs/source/cpp/parquet.rst Outdated

An example for writing a dataset using encrypted Parquet file format:

.. code-block:: cpp

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jorisvandenbossche@AlenkaF@raulcd What is our preferred policy for code examples? Do we put them inline in the docs? Do we use separate files?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: it seems we use literalinclude directives from C++ example files that are compiled as part of CI runs. See for example https://github.com/apache/arrow/blob/main/docs/source/cpp/dataset.rst#reading-datasets

Comment threaddocs/source/cpp/parquet.rst Outdated
Comment threaddocs/source/cpp/parquet.rst Outdated
Comment threaddocs/source/python/parquet.rst Outdated
Comment threaddocs/source/python/parquet.rst Outdated
@github-actionsgithub-actionsBot added awaiting committer review Awaiting committer review and removed awaiting review Awaiting review labels Feb 6, 2025
@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

There is some improvement for this non-intuitive naming scheme: #45462

@pitroupitrou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This LGTM except that we probably want to live in a separate file (see comment below).

@EnricoMi
EnricoMiforce-pushed the docs-column-encryption-nested-fields branch from 6e27abf to 30c6184CompareFebruary 12, 2025 15:29
@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou moved the C++ code into a file and referenced that from parquet.rst.

@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou addressed your comments, can this docs improvement be merged?

@pitrou

Copy link
Copy Markdown
Member

The CI failures seem unexpected, can you rebase/merge from latest git main?

@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

Rebased with latest main commit d88ef57.

@pitrou

Copy link
Copy Markdown
Member

Ok, the CI failures are certainly unrelated.

@pitrou

Copy link
Copy Markdown
Member

@github-actions crossbow submit docs-preview

@github-actions

Copy link
Copy Markdown
Unable to match any tasks for `docs-preview`
The Archery job run can be found at: https://github.com/apache/arrow/actions/runs/13717903134

@AlenkaF

Copy link
Copy Markdown
Member

@github-actions crossbow submit preview-docs

@github-actions

Copy link
Copy Markdown

Revision: 52fb219

Submitted crossbow builds: ursacomputing/crossbow @ actions-3c034c02a1

TaskStatus
preview-docsGitHub Actions

@AlenkaFAlenkaF left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AlenkaF
AlenkaF requested a review from pitrouMarch 18, 2025 12:16
@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou are you happy with this?

Comment threaddocs/source/cpp/parquet.rst Outdated
Comment threaddocs/source/cpp/parquet.rst Outdated
Comment on lines +620 to +622
Encrypting columns that have nested fields (struct, map, or even list data types)
requires column keys for the inner fields, not the column itself.
Configuring a column key for the column itself causes this error (here column name is ``col``):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Encrypting columns that have nested fields (struct, map, or even list data types)
requires column keys for the inner fields, not the column itself.
Configuring a column key for the column itself causes this error (here column name is ``col``):
Encrypting columns that have nested fields (struct, map or list data types)
requires column keys for the inner fields, not the nested column itself.
Configuring a column key for the nested column causes
this error (here the column name is ``col``):

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find nested column ambiguous, it sounds like the inner column, the nested field.

What about outer column instead of nested column?

Suggested change
Encrypting columns that have nested fields (struct, map, or even list data types)
requires column keys for the inner fields, not the column itself.
Configuring a column key for the column itself causes this error (here column name is ``col``):
Encrypting columns that have nested fields (struct, map, or even list data types)
requires column keys for the inner fields, not the outer column itself.
Configuring a column key for the outer column causes this error (here column name is ``col``):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, that's better!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also two wording nits:

  • "struct, map, or even list data types" -> "struct, map, or list data types" (there's nothing special about lists)
  • "here column name is" -> "here the column name is"

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, missed those of your changes when creating my suggestion. All incorporated now.

Comment threaddocs/source/cpp/parquet.rst Outdated
Comment threaddocs/source/python/parquet.rst Outdated
Comment threaddocs/source/python/parquet.rst Outdated
)

.. note::
Encrypting columns that have nested fields (for instance struct, map, or even list data types)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(same suggestions as for C++)

// specific language governing permissions and limitations
// under the License.

#include <arrow/util/logging.h>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO We should avoid using this header in public examples. std::cerr is good enough here.

#include "arrow/result.h"
#include "parquet/arrow/reader.h"

#include <arrow/filesystem/path_util.h>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move this include together with other Arrow includes above?

Comment threadcpp/examples/arrow/parquet_column_encryption.cc
EnricoMiand others added 2 commits March 25, 2025 14:21
Co-authored-by: Antoine Pitrou <pitrou@free.fr>
@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou all comments addressed, one alternative suggested at #45411 (comment)

#include "arrow/api.h"
#include "arrow/dataset/file_parquet.h"
#include "arrow/dataset/parquet_encryption_config.h"
#include "arrow/filesystem//localfs.h"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small nit

Suggested change
#include"arrow/filesystem//localfs.h"
#include"arrow/filesystem/localfs.h"

@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou comments addressed

@pitroupitrou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for doing this @EnricoMi !

@pitrou

Copy link
Copy Markdown
Member

@github-actions crossbow submit preview-docs

@github-actions

Copy link
Copy Markdown

Revision: b47168d

Submitted crossbow builds: ursacomputing/crossbow @ actions-ea617ad0a3

TaskStatus
preview-docsGitHub Actions

@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

Thank you for your time, @pitrou!

@AlenkaF
AlenkaF merged commit 5d0149d into apache:mainMar 26, 2025
@AlenkaFAlenkaF removed the awaiting committer review Awaiting committer review label Mar 26, 2025
@conbench-apache-arrow

Copy link
Copy Markdown

After merging your PR, Conbench analyzed the 0 benchmarking runs that have been run so far on merge-commit 5d0149d.

None of the specified runs were found on the Conbench server.

The full Conbench report has more details.

zanmato1984 pushed a commit to zanmato1984/arrow that referenced this pull request Apr 15, 2025
… for nested fields (apache#45411)
### Rationale for this change
Encrypting columns with nested fields with a column key is not trivial since only leaf fields are allowed in the column key map. Documentation emphasizes this fact and provides examples.
### What changes are included in this PR?
This amends the documentation on encryption for C++ and Python.
### Are these changes tested?
Only documentation.
### Are there any user-facing changes?
Only documentation.
* GitHub Issue: apache#41246
Lead-authored-by: Enrico Minack <github@enrico.minack.dev>
Co-authored-by: Antoine Pitrou <pitrou@free.fr>
Signed-off-by: AlenkaF <frim.alenka@gmail.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@EnricoMi@pitrou@AlenkaF
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

GH-41246: [Docs][C++][Python] Improve docs on column encryption for nested fields - #45411

Merged
AlenkaF merged 16 commits into
apache:mainfrom
EnricoMi:docs-column-encryption-nested-fields
Mar 26, 2025
Merged

GH-41246: [Docs][C++][Python] Improve docs on column encryption for nested fields#45411
AlenkaF merged 16 commits into
apache:mainfrom
EnricoMi:docs-column-encryption-nested-fields

Conversation

@EnricoMi

@EnricoMiEnricoMi commented Feb 1, 2025

Copy link
Copy Markdown
Collaborator

Rationale for this change

Encrypting columns with nested fields with a column key is not trivial since only leaf fields are allowed in the column key map. Documentation emphasizes this fact and provides examples.

What changes are included in this PR?

This amends the documentation on encryption for C++ and Python.

Are these changes tested?

Only documentation.

Are there any user-facing changes?

Only documentation.

@github-actions

Copy link
Copy Markdown

⚠️ GitHub issue #41246has been automatically assigned in GitHub to PR creator.

@EnricoMi
EnricoMiforce-pushed the docs-column-encryption-nested-fields branch from 543bd7b to 56e803dCompareFebruary 1, 2025 16:58

@pitroupitrou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for noticing and document this @EnricoMi . Here are assorted comments.

Comment threaddocs/source/cpp/parquet.rst Outdated
Comment on lines +658 to +661
encryption_config->column_keys = "column_key_name: "
"ListColumn.list.element, "
"MapColumn.key_value.key, MapColumn.key_value.value, "
"StructColumn.f1, StructColumn.f2"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are the spaces embedded in the string actually supported? Also, it seems to lack a semicolon at the end of the line.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it supports extra whitespaces, it trims the strings. The final semicolon is not needed, it would introduce a next empty section:

ColumnPathToEncryptionPropertiesMap CryptoFactory::GetColumnEncryptionProperties(
int dek_length, const std::string& column_keys, FileKeyWrapper* key_wrapper) {
ColumnPathToEncryptionPropertiesMap encrypted_columns;
std::vector<::std::string_view> key_to_columns =
::arrow::internal::SplitString(column_keys, ';');
for (size_t i = 0; i < key_to_columns.size(); ++i) {
std::string cur_key_to_columns =
::arrow::internal::TrimString(std::string(key_to_columns[i]));
if (cur_key_to_columns.empty()) {
continue;
}
std::vector<::std::string_view> parts =
::arrow::internal::SplitString(cur_key_to_columns, ':');
if (parts.size() != 2) {
std::ostringstream message;
message << "Incorrect key to columns mapping in column keys property"
<< ": [" << cur_key_to_columns << "]";
throwParquetException(message.str());
}
std::string column_key_id = ::arrow::internal::TrimString(std::string(parts[0]));
if (column_key_id.empty()) {
throwParquetException("Empty key name in column keys property.");
}
std::string column_names_str = ::arrow::internal::TrimString(std::string(parts[1]));
std::vector<::std::string_view> column_names =
::arrow::internal::SplitString(column_names_str, ',');
if (0 == column_names.size()) {
throwParquetException("No columns to encrypt defined for key: " + column_key_id);
}
for (size_t j = 0; j < column_names.size(); ++j) {
std::string column_name =
::arrow::internal::TrimString(std::string(column_names[j]));
if (column_name.empty()) {
std::ostringstream message;
message << "Empty column name in column keys property for key: " << column_key_id;
throwParquetException(message.str());
}
if (encrypted_columns.find(column_name) != encrypted_columns.end()) {
throwParquetException("Multiple keys defined for the same column: " +
column_name);
}
std::string column_key(dek_length, '\0');
RandBytes(reinterpret_cast<uint8_t*>(column_key.data()), column_key.size());
std::string column_key_key_metadata =
key_wrapper->GetEncryptionKeyMetadata(column_key, column_key_id, false);
std::shared_ptr<ColumnEncryptionProperties> cmd =
ColumnEncryptionProperties::Builder(column_name)
.key(column_key)
->key_metadata(column_key_key_metadata)
->build();
encrypted_columns.insert({column_name, cmd});
}
}
if (encrypted_columns.empty()) {
throwParquetException("No column keys configured in column keys property.");
}
return encrypted_columns;
}

Comment threaddocs/source/cpp/parquet.rst Outdated

An example for writing a dataset using encrypted Parquet file format:

.. code-block:: cpp

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jorisvandenbossche@AlenkaF@raulcd What is our preferred policy for code examples? Do we put them inline in the docs? Do we use separate files?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: it seems we use literalinclude directives from C++ example files that are compiled as part of CI runs. See for example https://github.com/apache/arrow/blob/main/docs/source/cpp/dataset.rst#reading-datasets

Comment threaddocs/source/cpp/parquet.rst Outdated
Comment threaddocs/source/cpp/parquet.rst Outdated
Comment threaddocs/source/python/parquet.rst Outdated
Comment threaddocs/source/python/parquet.rst Outdated
@github-actionsgithub-actionsBot added awaiting committer review Awaiting committer review and removed awaiting review Awaiting review labels Feb 6, 2025
@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

There is some improvement for this non-intuitive naming scheme: #45462

@pitroupitrou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This LGTM except that we probably want to live in a separate file (see comment below).

@EnricoMi
EnricoMiforce-pushed the docs-column-encryption-nested-fields branch from 6e27abf to 30c6184CompareFebruary 12, 2025 15:29
@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou moved the C++ code into a file and referenced that from parquet.rst.

@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou addressed your comments, can this docs improvement be merged?

@pitrou

Copy link
Copy Markdown
Member

The CI failures seem unexpected, can you rebase/merge from latest git main?

@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

Rebased with latest main commit d88ef57.

@pitrou

Copy link
Copy Markdown
Member

Ok, the CI failures are certainly unrelated.

@pitrou

Copy link
Copy Markdown
Member

@github-actions crossbow submit docs-preview

@github-actions

Copy link
Copy Markdown
Unable to match any tasks for `docs-preview`
The Archery job run can be found at: https://github.com/apache/arrow/actions/runs/13717903134

@AlenkaF

Copy link
Copy Markdown
Member

@github-actions crossbow submit preview-docs

@github-actions

Copy link
Copy Markdown

Revision: 52fb219

Submitted crossbow builds: ursacomputing/crossbow @ actions-3c034c02a1

TaskStatus
preview-docsGitHub Actions

@AlenkaFAlenkaF left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AlenkaF
AlenkaF requested a review from pitrouMarch 18, 2025 12:16
@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou are you happy with this?

Comment threaddocs/source/cpp/parquet.rst Outdated
Comment threaddocs/source/cpp/parquet.rst Outdated
Comment on lines +620 to +622
Encrypting columns that have nested fields (struct, map, or even list data types)
requires column keys for the inner fields, not the column itself.
Configuring a column key for the column itself causes this error (here column name is ``col``):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Encrypting columns that have nested fields (struct, map, or even list data types)
requires column keys for the inner fields, not the column itself.
Configuring a column key for the column itself causes this error (here column name is ``col``):
Encrypting columns that have nested fields (struct, map or list data types)
requires column keys for the inner fields, not the nested column itself.
Configuring a column key for the nested column causes
this error (here the column name is ``col``):

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find nested column ambiguous, it sounds like the inner column, the nested field.

What about outer column instead of nested column?

Suggested change
Encrypting columns that have nested fields (struct, map, or even list data types)
requires column keys for the inner fields, not the column itself.
Configuring a column key for the column itself causes this error (here column name is ``col``):
Encrypting columns that have nested fields (struct, map, or even list data types)
requires column keys for the inner fields, not the outer column itself.
Configuring a column key for the outer column causes this error (here column name is ``col``):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, that's better!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also two wording nits:

  • "struct, map, or even list data types" -> "struct, map, or list data types" (there's nothing special about lists)
  • "here column name is" -> "here the column name is"

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, missed those of your changes when creating my suggestion. All incorporated now.

Comment threaddocs/source/cpp/parquet.rst Outdated
Comment threaddocs/source/python/parquet.rst Outdated
Comment threaddocs/source/python/parquet.rst Outdated
)

.. note::
Encrypting columns that have nested fields (for instance struct, map, or even list data types)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(same suggestions as for C++)

// specific language governing permissions and limitations
// under the License.

#include <arrow/util/logging.h>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO We should avoid using this header in public examples. std::cerr is good enough here.

#include "arrow/result.h"
#include "parquet/arrow/reader.h"

#include <arrow/filesystem/path_util.h>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move this include together with other Arrow includes above?

Comment threadcpp/examples/arrow/parquet_column_encryption.cc
EnricoMiand others added 2 commits March 25, 2025 14:21
Co-authored-by: Antoine Pitrou <pitrou@free.fr>
@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou all comments addressed, one alternative suggested at #45411 (comment)

#include "arrow/api.h"
#include "arrow/dataset/file_parquet.h"
#include "arrow/dataset/parquet_encryption_config.h"
#include "arrow/filesystem//localfs.h"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small nit

Suggested change
#include"arrow/filesystem//localfs.h"
#include"arrow/filesystem/localfs.h"

@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou comments addressed

@pitroupitrou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for doing this @EnricoMi !

@pitrou

Copy link
Copy Markdown
Member

@github-actions crossbow submit preview-docs

@github-actions

Copy link
Copy Markdown

Revision: b47168d

Submitted crossbow builds: ursacomputing/crossbow @ actions-ea617ad0a3

TaskStatus
preview-docsGitHub Actions

@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

Thank you for your time, @pitrou!

@AlenkaF
AlenkaF merged commit 5d0149d into apache:mainMar 26, 2025
@AlenkaFAlenkaF removed the awaiting committer review Awaiting committer review label Mar 26, 2025
@conbench-apache-arrow

Copy link
Copy Markdown

After merging your PR, Conbench analyzed the 0 benchmarking runs that have been run so far on merge-commit 5d0149d.

None of the specified runs were found on the Conbench server.

The full Conbench report has more details.

zanmato1984 pushed a commit to zanmato1984/arrow that referenced this pull request Apr 15, 2025
… for nested fields (apache#45411)
### Rationale for this change
Encrypting columns with nested fields with a column key is not trivial since only leaf fields are allowed in the column key map. Documentation emphasizes this fact and provides examples.
### What changes are included in this PR?
This amends the documentation on encryption for C++ and Python.
### Are these changes tested?
Only documentation.
### Are there any user-facing changes?
Only documentation.
* GitHub Issue: apache#41246
Lead-authored-by: Enrico Minack <github@enrico.minack.dev>
Co-authored-by: Antoine Pitrou <pitrou@free.fr>
Signed-off-by: AlenkaF <frim.alenka@gmail.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@EnricoMi@pitrou@AlenkaF
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

GH-41246: [Docs][C++][Python] Improve docs on column encryption for nested fields - #45411

Merged
AlenkaF merged 16 commits into
apache:mainfrom
EnricoMi:docs-column-encryption-nested-fields
Mar 26, 2025
Merged

GH-41246: [Docs][C++][Python] Improve docs on column encryption for nested fields#45411
AlenkaF merged 16 commits into
apache:mainfrom
EnricoMi:docs-column-encryption-nested-fields

Conversation

@EnricoMi

@EnricoMiEnricoMi commented Feb 1, 2025

Copy link
Copy Markdown
Collaborator

Rationale for this change

Encrypting columns with nested fields with a column key is not trivial since only leaf fields are allowed in the column key map. Documentation emphasizes this fact and provides examples.

What changes are included in this PR?

This amends the documentation on encryption for C++ and Python.

Are these changes tested?

Only documentation.

Are there any user-facing changes?

Only documentation.

@github-actions

Copy link
Copy Markdown

⚠️ GitHub issue #41246has been automatically assigned in GitHub to PR creator.

@EnricoMi
EnricoMiforce-pushed the docs-column-encryption-nested-fields branch from 543bd7b to 56e803dCompareFebruary 1, 2025 16:58

@pitroupitrou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for noticing and document this @EnricoMi . Here are assorted comments.

Comment threaddocs/source/cpp/parquet.rst Outdated
Comment on lines +658 to +661
encryption_config->column_keys = "column_key_name: "
"ListColumn.list.element, "
"MapColumn.key_value.key, MapColumn.key_value.value, "
"StructColumn.f1, StructColumn.f2"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are the spaces embedded in the string actually supported? Also, it seems to lack a semicolon at the end of the line.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it supports extra whitespaces, it trims the strings. The final semicolon is not needed, it would introduce a next empty section:

ColumnPathToEncryptionPropertiesMap CryptoFactory::GetColumnEncryptionProperties(
int dek_length, const std::string& column_keys, FileKeyWrapper* key_wrapper) {
ColumnPathToEncryptionPropertiesMap encrypted_columns;
std::vector<::std::string_view> key_to_columns =
::arrow::internal::SplitString(column_keys, ';');
for (size_t i = 0; i < key_to_columns.size(); ++i) {
std::string cur_key_to_columns =
::arrow::internal::TrimString(std::string(key_to_columns[i]));
if (cur_key_to_columns.empty()) {
continue;
}
std::vector<::std::string_view> parts =
::arrow::internal::SplitString(cur_key_to_columns, ':');
if (parts.size() != 2) {
std::ostringstream message;
message << "Incorrect key to columns mapping in column keys property"
<< ": [" << cur_key_to_columns << "]";
throwParquetException(message.str());
}
std::string column_key_id = ::arrow::internal::TrimString(std::string(parts[0]));
if (column_key_id.empty()) {
throwParquetException("Empty key name in column keys property.");
}
std::string column_names_str = ::arrow::internal::TrimString(std::string(parts[1]));
std::vector<::std::string_view> column_names =
::arrow::internal::SplitString(column_names_str, ',');
if (0 == column_names.size()) {
throwParquetException("No columns to encrypt defined for key: " + column_key_id);
}
for (size_t j = 0; j < column_names.size(); ++j) {
std::string column_name =
::arrow::internal::TrimString(std::string(column_names[j]));
if (column_name.empty()) {
std::ostringstream message;
message << "Empty column name in column keys property for key: " << column_key_id;
throwParquetException(message.str());
}
if (encrypted_columns.find(column_name) != encrypted_columns.end()) {
throwParquetException("Multiple keys defined for the same column: " +
column_name);
}
std::string column_key(dek_length, '\0');
RandBytes(reinterpret_cast<uint8_t*>(column_key.data()), column_key.size());
std::string column_key_key_metadata =
key_wrapper->GetEncryptionKeyMetadata(column_key, column_key_id, false);
std::shared_ptr<ColumnEncryptionProperties> cmd =
ColumnEncryptionProperties::Builder(column_name)
.key(column_key)
->key_metadata(column_key_key_metadata)
->build();
encrypted_columns.insert({column_name, cmd});
}
}
if (encrypted_columns.empty()) {
throwParquetException("No column keys configured in column keys property.");
}
return encrypted_columns;
}

Comment threaddocs/source/cpp/parquet.rst Outdated

An example for writing a dataset using encrypted Parquet file format:

.. code-block:: cpp

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jorisvandenbossche@AlenkaF@raulcd What is our preferred policy for code examples? Do we put them inline in the docs? Do we use separate files?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: it seems we use literalinclude directives from C++ example files that are compiled as part of CI runs. See for example https://github.com/apache/arrow/blob/main/docs/source/cpp/dataset.rst#reading-datasets

Comment threaddocs/source/cpp/parquet.rst Outdated
Comment threaddocs/source/cpp/parquet.rst Outdated
Comment threaddocs/source/python/parquet.rst Outdated
Comment threaddocs/source/python/parquet.rst Outdated
@github-actionsgithub-actionsBot added awaiting committer review Awaiting committer review and removed awaiting review Awaiting review labels Feb 6, 2025
@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

There is some improvement for this non-intuitive naming scheme: #45462

@pitroupitrou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This LGTM except that we probably want to live in a separate file (see comment below).

@EnricoMi
EnricoMiforce-pushed the docs-column-encryption-nested-fields branch from 6e27abf to 30c6184CompareFebruary 12, 2025 15:29
@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou moved the C++ code into a file and referenced that from parquet.rst.

@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou addressed your comments, can this docs improvement be merged?

@pitrou

Copy link
Copy Markdown
Member

The CI failures seem unexpected, can you rebase/merge from latest git main?

@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

Rebased with latest main commit d88ef57.

@pitrou

Copy link
Copy Markdown
Member

Ok, the CI failures are certainly unrelated.

@pitrou

Copy link
Copy Markdown
Member

@github-actions crossbow submit docs-preview

@github-actions

Copy link
Copy Markdown
Unable to match any tasks for `docs-preview`
The Archery job run can be found at: https://github.com/apache/arrow/actions/runs/13717903134

@AlenkaF

Copy link
Copy Markdown
Member

@github-actions crossbow submit preview-docs

@github-actions

Copy link
Copy Markdown

Revision: 52fb219

Submitted crossbow builds: ursacomputing/crossbow @ actions-3c034c02a1

TaskStatus
preview-docsGitHub Actions

@AlenkaFAlenkaF left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AlenkaF
AlenkaF requested a review from pitrouMarch 18, 2025 12:16
@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou are you happy with this?

Comment threaddocs/source/cpp/parquet.rst Outdated
Comment threaddocs/source/cpp/parquet.rst Outdated
Comment on lines +620 to +622
Encrypting columns that have nested fields (struct, map, or even list data types)
requires column keys for the inner fields, not the column itself.
Configuring a column key for the column itself causes this error (here column name is ``col``):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Encrypting columns that have nested fields (struct, map, or even list data types)
requires column keys for the inner fields, not the column itself.
Configuring a column key for the column itself causes this error (here column name is ``col``):
Encrypting columns that have nested fields (struct, map or list data types)
requires column keys for the inner fields, not the nested column itself.
Configuring a column key for the nested column causes
this error (here the column name is ``col``):

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find nested column ambiguous, it sounds like the inner column, the nested field.

What about outer column instead of nested column?

Suggested change
Encrypting columns that have nested fields (struct, map, or even list data types)
requires column keys for the inner fields, not the column itself.
Configuring a column key for the column itself causes this error (here column name is ``col``):
Encrypting columns that have nested fields (struct, map, or even list data types)
requires column keys for the inner fields, not the outer column itself.
Configuring a column key for the outer column causes this error (here column name is ``col``):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, that's better!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also two wording nits:

  • "struct, map, or even list data types" -> "struct, map, or list data types" (there's nothing special about lists)
  • "here column name is" -> "here the column name is"

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, missed those of your changes when creating my suggestion. All incorporated now.

Comment threaddocs/source/cpp/parquet.rst Outdated
Comment threaddocs/source/python/parquet.rst Outdated
Comment threaddocs/source/python/parquet.rst Outdated
)

.. note::
Encrypting columns that have nested fields (for instance struct, map, or even list data types)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(same suggestions as for C++)

// specific language governing permissions and limitations
// under the License.

#include <arrow/util/logging.h>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO We should avoid using this header in public examples. std::cerr is good enough here.

#include "arrow/result.h"
#include "parquet/arrow/reader.h"

#include <arrow/filesystem/path_util.h>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move this include together with other Arrow includes above?

Comment threadcpp/examples/arrow/parquet_column_encryption.cc
EnricoMiand others added 2 commits March 25, 2025 14:21
Co-authored-by: Antoine Pitrou <pitrou@free.fr>
@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou all comments addressed, one alternative suggested at #45411 (comment)

#include "arrow/api.h"
#include "arrow/dataset/file_parquet.h"
#include "arrow/dataset/parquet_encryption_config.h"
#include "arrow/filesystem//localfs.h"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small nit

Suggested change
#include"arrow/filesystem//localfs.h"
#include"arrow/filesystem/localfs.h"

@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou comments addressed

@pitroupitrou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for doing this @EnricoMi !

@pitrou

Copy link
Copy Markdown
Member

@github-actions crossbow submit preview-docs

@github-actions

Copy link
Copy Markdown

Revision: b47168d

Submitted crossbow builds: ursacomputing/crossbow @ actions-ea617ad0a3

TaskStatus
preview-docsGitHub Actions

@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

Thank you for your time, @pitrou!

@AlenkaF
AlenkaF merged commit 5d0149d into apache:mainMar 26, 2025
@AlenkaFAlenkaF removed the awaiting committer review Awaiting committer review label Mar 26, 2025
@conbench-apache-arrow

Copy link
Copy Markdown

After merging your PR, Conbench analyzed the 0 benchmarking runs that have been run so far on merge-commit 5d0149d.

None of the specified runs were found on the Conbench server.

The full Conbench report has more details.

zanmato1984 pushed a commit to zanmato1984/arrow that referenced this pull request Apr 15, 2025
… for nested fields (apache#45411)
### Rationale for this change
Encrypting columns with nested fields with a column key is not trivial since only leaf fields are allowed in the column key map. Documentation emphasizes this fact and provides examples.
### What changes are included in this PR?
This amends the documentation on encryption for C++ and Python.
### Are these changes tested?
Only documentation.
### Are there any user-facing changes?
Only documentation.
* GitHub Issue: apache#41246
Lead-authored-by: Enrico Minack <github@enrico.minack.dev>
Co-authored-by: Antoine Pitrou <pitrou@free.fr>
Signed-off-by: AlenkaF <frim.alenka@gmail.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@EnricoMi@pitrou@AlenkaF
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

GH-41246: [Docs][C++][Python] Improve docs on column encryption for nested fields - #45411

Merged
AlenkaF merged 16 commits into
apache:mainfrom
EnricoMi:docs-column-encryption-nested-fields
Mar 26, 2025
Merged

GH-41246: [Docs][C++][Python] Improve docs on column encryption for nested fields#45411
AlenkaF merged 16 commits into
apache:mainfrom
EnricoMi:docs-column-encryption-nested-fields

Conversation

@EnricoMi

@EnricoMiEnricoMi commented Feb 1, 2025

Copy link
Copy Markdown
Collaborator

Rationale for this change

Encrypting columns with nested fields with a column key is not trivial since only leaf fields are allowed in the column key map. Documentation emphasizes this fact and provides examples.

What changes are included in this PR?

This amends the documentation on encryption for C++ and Python.

Are these changes tested?

Only documentation.

Are there any user-facing changes?

Only documentation.

@github-actions

Copy link
Copy Markdown

⚠️ GitHub issue #41246has been automatically assigned in GitHub to PR creator.

@EnricoMi
EnricoMiforce-pushed the docs-column-encryption-nested-fields branch from 543bd7b to 56e803dCompareFebruary 1, 2025 16:58

@pitroupitrou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for noticing and document this @EnricoMi . Here are assorted comments.

Comment threaddocs/source/cpp/parquet.rst Outdated
Comment on lines +658 to +661
encryption_config->column_keys = "column_key_name: "
"ListColumn.list.element, "
"MapColumn.key_value.key, MapColumn.key_value.value, "
"StructColumn.f1, StructColumn.f2"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are the spaces embedded in the string actually supported? Also, it seems to lack a semicolon at the end of the line.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it supports extra whitespaces, it trims the strings. The final semicolon is not needed, it would introduce a next empty section:

ColumnPathToEncryptionPropertiesMap CryptoFactory::GetColumnEncryptionProperties(
int dek_length, const std::string& column_keys, FileKeyWrapper* key_wrapper) {
ColumnPathToEncryptionPropertiesMap encrypted_columns;
std::vector<::std::string_view> key_to_columns =
::arrow::internal::SplitString(column_keys, ';');
for (size_t i = 0; i < key_to_columns.size(); ++i) {
std::string cur_key_to_columns =
::arrow::internal::TrimString(std::string(key_to_columns[i]));
if (cur_key_to_columns.empty()) {
continue;
}
std::vector<::std::string_view> parts =
::arrow::internal::SplitString(cur_key_to_columns, ':');
if (parts.size() != 2) {
std::ostringstream message;
message << "Incorrect key to columns mapping in column keys property"
<< ": [" << cur_key_to_columns << "]";
throwParquetException(message.str());
}
std::string column_key_id = ::arrow::internal::TrimString(std::string(parts[0]));
if (column_key_id.empty()) {
throwParquetException("Empty key name in column keys property.");
}
std::string column_names_str = ::arrow::internal::TrimString(std::string(parts[1]));
std::vector<::std::string_view> column_names =
::arrow::internal::SplitString(column_names_str, ',');
if (0 == column_names.size()) {
throwParquetException("No columns to encrypt defined for key: " + column_key_id);
}
for (size_t j = 0; j < column_names.size(); ++j) {
std::string column_name =
::arrow::internal::TrimString(std::string(column_names[j]));
if (column_name.empty()) {
std::ostringstream message;
message << "Empty column name in column keys property for key: " << column_key_id;
throwParquetException(message.str());
}
if (encrypted_columns.find(column_name) != encrypted_columns.end()) {
throwParquetException("Multiple keys defined for the same column: " +
column_name);
}
std::string column_key(dek_length, '\0');
RandBytes(reinterpret_cast<uint8_t*>(column_key.data()), column_key.size());
std::string column_key_key_metadata =
key_wrapper->GetEncryptionKeyMetadata(column_key, column_key_id, false);
std::shared_ptr<ColumnEncryptionProperties> cmd =
ColumnEncryptionProperties::Builder(column_name)
.key(column_key)
->key_metadata(column_key_key_metadata)
->build();
encrypted_columns.insert({column_name, cmd});
}
}
if (encrypted_columns.empty()) {
throwParquetException("No column keys configured in column keys property.");
}
return encrypted_columns;
}

Comment threaddocs/source/cpp/parquet.rst Outdated

An example for writing a dataset using encrypted Parquet file format:

.. code-block:: cpp

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jorisvandenbossche@AlenkaF@raulcd What is our preferred policy for code examples? Do we put them inline in the docs? Do we use separate files?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: it seems we use literalinclude directives from C++ example files that are compiled as part of CI runs. See for example https://github.com/apache/arrow/blob/main/docs/source/cpp/dataset.rst#reading-datasets

Comment threaddocs/source/cpp/parquet.rst Outdated
Comment threaddocs/source/cpp/parquet.rst Outdated
Comment threaddocs/source/python/parquet.rst Outdated
Comment threaddocs/source/python/parquet.rst Outdated
@github-actionsgithub-actionsBot added awaiting committer review Awaiting committer review and removed awaiting review Awaiting review labels Feb 6, 2025
@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

There is some improvement for this non-intuitive naming scheme: #45462

@pitroupitrou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This LGTM except that we probably want to live in a separate file (see comment below).

@EnricoMi
EnricoMiforce-pushed the docs-column-encryption-nested-fields branch from 6e27abf to 30c6184CompareFebruary 12, 2025 15:29
@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou moved the C++ code into a file and referenced that from parquet.rst.

@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou addressed your comments, can this docs improvement be merged?

@pitrou

Copy link
Copy Markdown
Member

The CI failures seem unexpected, can you rebase/merge from latest git main?

@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

Rebased with latest main commit d88ef57.

@pitrou

Copy link
Copy Markdown
Member

Ok, the CI failures are certainly unrelated.

@pitrou

Copy link
Copy Markdown
Member

@github-actions crossbow submit docs-preview

@github-actions

Copy link
Copy Markdown
Unable to match any tasks for `docs-preview`
The Archery job run can be found at: https://github.com/apache/arrow/actions/runs/13717903134

@AlenkaF

Copy link
Copy Markdown
Member

@github-actions crossbow submit preview-docs

@github-actions

Copy link
Copy Markdown

Revision: 52fb219

Submitted crossbow builds: ursacomputing/crossbow @ actions-3c034c02a1

TaskStatus
preview-docsGitHub Actions

@AlenkaFAlenkaF left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AlenkaF
AlenkaF requested a review from pitrouMarch 18, 2025 12:16
@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou are you happy with this?

Comment threaddocs/source/cpp/parquet.rst Outdated
Comment threaddocs/source/cpp/parquet.rst Outdated
Comment on lines +620 to +622
Encrypting columns that have nested fields (struct, map, or even list data types)
requires column keys for the inner fields, not the column itself.
Configuring a column key for the column itself causes this error (here column name is ``col``):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Encrypting columns that have nested fields (struct, map, or even list data types)
requires column keys for the inner fields, not the column itself.
Configuring a column key for the column itself causes this error (here column name is ``col``):
Encrypting columns that have nested fields (struct, map or list data types)
requires column keys for the inner fields, not the nested column itself.
Configuring a column key for the nested column causes
this error (here the column name is ``col``):

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find nested column ambiguous, it sounds like the inner column, the nested field.

What about outer column instead of nested column?

Suggested change
Encrypting columns that have nested fields (struct, map, or even list data types)
requires column keys for the inner fields, not the column itself.
Configuring a column key for the column itself causes this error (here column name is ``col``):
Encrypting columns that have nested fields (struct, map, or even list data types)
requires column keys for the inner fields, not the outer column itself.
Configuring a column key for the outer column causes this error (here column name is ``col``):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, that's better!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also two wording nits:

  • "struct, map, or even list data types" -> "struct, map, or list data types" (there's nothing special about lists)
  • "here column name is" -> "here the column name is"

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, missed those of your changes when creating my suggestion. All incorporated now.

Comment threaddocs/source/cpp/parquet.rst Outdated
Comment threaddocs/source/python/parquet.rst Outdated
Comment threaddocs/source/python/parquet.rst Outdated
)

.. note::
Encrypting columns that have nested fields (for instance struct, map, or even list data types)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(same suggestions as for C++)

// specific language governing permissions and limitations
// under the License.

#include <arrow/util/logging.h>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO We should avoid using this header in public examples. std::cerr is good enough here.

#include "arrow/result.h"
#include "parquet/arrow/reader.h"

#include <arrow/filesystem/path_util.h>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move this include together with other Arrow includes above?

Comment threadcpp/examples/arrow/parquet_column_encryption.cc
EnricoMiand others added 2 commits March 25, 2025 14:21
Co-authored-by: Antoine Pitrou <pitrou@free.fr>
@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou all comments addressed, one alternative suggested at #45411 (comment)

#include "arrow/api.h"
#include "arrow/dataset/file_parquet.h"
#include "arrow/dataset/parquet_encryption_config.h"
#include "arrow/filesystem//localfs.h"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small nit

Suggested change
#include"arrow/filesystem//localfs.h"
#include"arrow/filesystem/localfs.h"

@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou comments addressed

@pitroupitrou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for doing this @EnricoMi !

@pitrou

Copy link
Copy Markdown
Member

@github-actions crossbow submit preview-docs

@github-actions

Copy link
Copy Markdown

Revision: b47168d

Submitted crossbow builds: ursacomputing/crossbow @ actions-ea617ad0a3

TaskStatus
preview-docsGitHub Actions

@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

Thank you for your time, @pitrou!

@AlenkaF
AlenkaF merged commit 5d0149d into apache:mainMar 26, 2025
@AlenkaFAlenkaF removed the awaiting committer review Awaiting committer review label Mar 26, 2025
@conbench-apache-arrow

Copy link
Copy Markdown

After merging your PR, Conbench analyzed the 0 benchmarking runs that have been run so far on merge-commit 5d0149d.

None of the specified runs were found on the Conbench server.

The full Conbench report has more details.

zanmato1984 pushed a commit to zanmato1984/arrow that referenced this pull request Apr 15, 2025
… for nested fields (apache#45411)
### Rationale for this change
Encrypting columns with nested fields with a column key is not trivial since only leaf fields are allowed in the column key map. Documentation emphasizes this fact and provides examples.
### What changes are included in this PR?
This amends the documentation on encryption for C++ and Python.
### Are these changes tested?
Only documentation.
### Are there any user-facing changes?
Only documentation.
* GitHub Issue: apache#41246
Lead-authored-by: Enrico Minack <github@enrico.minack.dev>
Co-authored-by: Antoine Pitrou <pitrou@free.fr>
Signed-off-by: AlenkaF <frim.alenka@gmail.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@EnricoMi@pitrou@AlenkaF
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

GH-41246: [Docs][C++][Python] Improve docs on column encryption for nested fields - #45411

Merged
AlenkaF merged 16 commits into
apache:mainfrom
EnricoMi:docs-column-encryption-nested-fields
Mar 26, 2025
Merged

GH-41246: [Docs][C++][Python] Improve docs on column encryption for nested fields#45411
AlenkaF merged 16 commits into
apache:mainfrom
EnricoMi:docs-column-encryption-nested-fields

Conversation

@EnricoMi

@EnricoMiEnricoMi commented Feb 1, 2025

Copy link
Copy Markdown
Collaborator

Rationale for this change

Encrypting columns with nested fields with a column key is not trivial since only leaf fields are allowed in the column key map. Documentation emphasizes this fact and provides examples.

What changes are included in this PR?

This amends the documentation on encryption for C++ and Python.

Are these changes tested?

Only documentation.

Are there any user-facing changes?

Only documentation.

@github-actions

Copy link
Copy Markdown

⚠️ GitHub issue #41246has been automatically assigned in GitHub to PR creator.

@EnricoMi
EnricoMiforce-pushed the docs-column-encryption-nested-fields branch from 543bd7b to 56e803dCompareFebruary 1, 2025 16:58

@pitroupitrou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for noticing and document this @EnricoMi . Here are assorted comments.

Comment threaddocs/source/cpp/parquet.rst Outdated
Comment on lines +658 to +661
encryption_config->column_keys = "column_key_name: "
"ListColumn.list.element, "
"MapColumn.key_value.key, MapColumn.key_value.value, "
"StructColumn.f1, StructColumn.f2"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are the spaces embedded in the string actually supported? Also, it seems to lack a semicolon at the end of the line.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it supports extra whitespaces, it trims the strings. The final semicolon is not needed, it would introduce a next empty section:

ColumnPathToEncryptionPropertiesMap CryptoFactory::GetColumnEncryptionProperties(
int dek_length, const std::string& column_keys, FileKeyWrapper* key_wrapper) {
ColumnPathToEncryptionPropertiesMap encrypted_columns;
std::vector<::std::string_view> key_to_columns =
::arrow::internal::SplitString(column_keys, ';');
for (size_t i = 0; i < key_to_columns.size(); ++i) {
std::string cur_key_to_columns =
::arrow::internal::TrimString(std::string(key_to_columns[i]));
if (cur_key_to_columns.empty()) {
continue;
}
std::vector<::std::string_view> parts =
::arrow::internal::SplitString(cur_key_to_columns, ':');
if (parts.size() != 2) {
std::ostringstream message;
message << "Incorrect key to columns mapping in column keys property"
<< ": [" << cur_key_to_columns << "]";
throwParquetException(message.str());
}
std::string column_key_id = ::arrow::internal::TrimString(std::string(parts[0]));
if (column_key_id.empty()) {
throwParquetException("Empty key name in column keys property.");
}
std::string column_names_str = ::arrow::internal::TrimString(std::string(parts[1]));
std::vector<::std::string_view> column_names =
::arrow::internal::SplitString(column_names_str, ',');
if (0 == column_names.size()) {
throwParquetException("No columns to encrypt defined for key: " + column_key_id);
}
for (size_t j = 0; j < column_names.size(); ++j) {
std::string column_name =
::arrow::internal::TrimString(std::string(column_names[j]));
if (column_name.empty()) {
std::ostringstream message;
message << "Empty column name in column keys property for key: " << column_key_id;
throwParquetException(message.str());
}
if (encrypted_columns.find(column_name) != encrypted_columns.end()) {
throwParquetException("Multiple keys defined for the same column: " +
column_name);
}
std::string column_key(dek_length, '\0');
RandBytes(reinterpret_cast<uint8_t*>(column_key.data()), column_key.size());
std::string column_key_key_metadata =
key_wrapper->GetEncryptionKeyMetadata(column_key, column_key_id, false);
std::shared_ptr<ColumnEncryptionProperties> cmd =
ColumnEncryptionProperties::Builder(column_name)
.key(column_key)
->key_metadata(column_key_key_metadata)
->build();
encrypted_columns.insert({column_name, cmd});
}
}
if (encrypted_columns.empty()) {
throwParquetException("No column keys configured in column keys property.");
}
return encrypted_columns;
}

Comment threaddocs/source/cpp/parquet.rst Outdated

An example for writing a dataset using encrypted Parquet file format:

.. code-block:: cpp

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jorisvandenbossche@AlenkaF@raulcd What is our preferred policy for code examples? Do we put them inline in the docs? Do we use separate files?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: it seems we use literalinclude directives from C++ example files that are compiled as part of CI runs. See for example https://github.com/apache/arrow/blob/main/docs/source/cpp/dataset.rst#reading-datasets

Comment threaddocs/source/cpp/parquet.rst Outdated
Comment threaddocs/source/cpp/parquet.rst Outdated
Comment threaddocs/source/python/parquet.rst Outdated
Comment threaddocs/source/python/parquet.rst Outdated
@github-actionsgithub-actionsBot added awaiting committer review Awaiting committer review and removed awaiting review Awaiting review labels Feb 6, 2025
@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

There is some improvement for this non-intuitive naming scheme: #45462

@pitroupitrou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This LGTM except that we probably want to live in a separate file (see comment below).

@EnricoMi
EnricoMiforce-pushed the docs-column-encryption-nested-fields branch from 6e27abf to 30c6184CompareFebruary 12, 2025 15:29
@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou moved the C++ code into a file and referenced that from parquet.rst.

@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou addressed your comments, can this docs improvement be merged?

@pitrou

Copy link
Copy Markdown
Member

The CI failures seem unexpected, can you rebase/merge from latest git main?

@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

Rebased with latest main commit d88ef57.

@pitrou

Copy link
Copy Markdown
Member

Ok, the CI failures are certainly unrelated.

@pitrou

Copy link
Copy Markdown
Member

@github-actions crossbow submit docs-preview

@github-actions

Copy link
Copy Markdown
Unable to match any tasks for `docs-preview`
The Archery job run can be found at: https://github.com/apache/arrow/actions/runs/13717903134

@AlenkaF

Copy link
Copy Markdown
Member

@github-actions crossbow submit preview-docs

@github-actions

Copy link
Copy Markdown

Revision: 52fb219

Submitted crossbow builds: ursacomputing/crossbow @ actions-3c034c02a1

TaskStatus
preview-docsGitHub Actions

@AlenkaFAlenkaF left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AlenkaF
AlenkaF requested a review from pitrouMarch 18, 2025 12:16
@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou are you happy with this?

Comment threaddocs/source/cpp/parquet.rst Outdated
Comment threaddocs/source/cpp/parquet.rst Outdated
Comment on lines +620 to +622
Encrypting columns that have nested fields (struct, map, or even list data types)
requires column keys for the inner fields, not the column itself.
Configuring a column key for the column itself causes this error (here column name is ``col``):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Encrypting columns that have nested fields (struct, map, or even list data types)
requires column keys for the inner fields, not the column itself.
Configuring a column key for the column itself causes this error (here column name is ``col``):
Encrypting columns that have nested fields (struct, map or list data types)
requires column keys for the inner fields, not the nested column itself.
Configuring a column key for the nested column causes
this error (here the column name is ``col``):

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find nested column ambiguous, it sounds like the inner column, the nested field.

What about outer column instead of nested column?

Suggested change
Encrypting columns that have nested fields (struct, map, or even list data types)
requires column keys for the inner fields, not the column itself.
Configuring a column key for the column itself causes this error (here column name is ``col``):
Encrypting columns that have nested fields (struct, map, or even list data types)
requires column keys for the inner fields, not the outer column itself.
Configuring a column key for the outer column causes this error (here column name is ``col``):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, that's better!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also two wording nits:

  • "struct, map, or even list data types" -> "struct, map, or list data types" (there's nothing special about lists)
  • "here column name is" -> "here the column name is"

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, missed those of your changes when creating my suggestion. All incorporated now.

Comment threaddocs/source/cpp/parquet.rst Outdated
Comment threaddocs/source/python/parquet.rst Outdated
Comment threaddocs/source/python/parquet.rst Outdated
)

.. note::
Encrypting columns that have nested fields (for instance struct, map, or even list data types)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(same suggestions as for C++)

// specific language governing permissions and limitations
// under the License.

#include <arrow/util/logging.h>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO We should avoid using this header in public examples. std::cerr is good enough here.

#include "arrow/result.h"
#include "parquet/arrow/reader.h"

#include <arrow/filesystem/path_util.h>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move this include together with other Arrow includes above?

Comment threadcpp/examples/arrow/parquet_column_encryption.cc
EnricoMiand others added 2 commits March 25, 2025 14:21
Co-authored-by: Antoine Pitrou <pitrou@free.fr>
@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou all comments addressed, one alternative suggested at #45411 (comment)

#include "arrow/api.h"
#include "arrow/dataset/file_parquet.h"
#include "arrow/dataset/parquet_encryption_config.h"
#include "arrow/filesystem//localfs.h"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small nit

Suggested change
#include"arrow/filesystem//localfs.h"
#include"arrow/filesystem/localfs.h"

@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou comments addressed

@pitroupitrou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for doing this @EnricoMi !

@pitrou

Copy link
Copy Markdown
Member

@github-actions crossbow submit preview-docs

@github-actions

Copy link
Copy Markdown

Revision: b47168d

Submitted crossbow builds: ursacomputing/crossbow @ actions-ea617ad0a3

TaskStatus
preview-docsGitHub Actions

@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

Thank you for your time, @pitrou!

@AlenkaF
AlenkaF merged commit 5d0149d into apache:mainMar 26, 2025
@AlenkaFAlenkaF removed the awaiting committer review Awaiting committer review label Mar 26, 2025
@conbench-apache-arrow

Copy link
Copy Markdown

After merging your PR, Conbench analyzed the 0 benchmarking runs that have been run so far on merge-commit 5d0149d.

None of the specified runs were found on the Conbench server.

The full Conbench report has more details.

zanmato1984 pushed a commit to zanmato1984/arrow that referenced this pull request Apr 15, 2025
… for nested fields (apache#45411)
### Rationale for this change
Encrypting columns with nested fields with a column key is not trivial since only leaf fields are allowed in the column key map. Documentation emphasizes this fact and provides examples.
### What changes are included in this PR?
This amends the documentation on encryption for C++ and Python.
### Are these changes tested?
Only documentation.
### Are there any user-facing changes?
Only documentation.
* GitHub Issue: apache#41246
Lead-authored-by: Enrico Minack <github@enrico.minack.dev>
Co-authored-by: Antoine Pitrou <pitrou@free.fr>
Signed-off-by: AlenkaF <frim.alenka@gmail.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@EnricoMi@pitrou@AlenkaF
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

GH-41246: [Docs][C++][Python] Improve docs on column encryption for nested fields - #45411

Merged
AlenkaF merged 16 commits into
apache:mainfrom
EnricoMi:docs-column-encryption-nested-fields
Mar 26, 2025
Merged

GH-41246: [Docs][C++][Python] Improve docs on column encryption for nested fields#45411
AlenkaF merged 16 commits into
apache:mainfrom
EnricoMi:docs-column-encryption-nested-fields

Conversation

@EnricoMi

@EnricoMiEnricoMi commented Feb 1, 2025

Copy link
Copy Markdown
Collaborator

Rationale for this change

Encrypting columns with nested fields with a column key is not trivial since only leaf fields are allowed in the column key map. Documentation emphasizes this fact and provides examples.

What changes are included in this PR?

This amends the documentation on encryption for C++ and Python.

Are these changes tested?

Only documentation.

Are there any user-facing changes?

Only documentation.

@github-actions

Copy link
Copy Markdown

⚠️ GitHub issue #41246has been automatically assigned in GitHub to PR creator.

@EnricoMi
EnricoMiforce-pushed the docs-column-encryption-nested-fields branch from 543bd7b to 56e803dCompareFebruary 1, 2025 16:58

@pitroupitrou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for noticing and document this @EnricoMi . Here are assorted comments.

Comment threaddocs/source/cpp/parquet.rst Outdated
Comment on lines +658 to +661
encryption_config->column_keys = "column_key_name: "
"ListColumn.list.element, "
"MapColumn.key_value.key, MapColumn.key_value.value, "
"StructColumn.f1, StructColumn.f2"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are the spaces embedded in the string actually supported? Also, it seems to lack a semicolon at the end of the line.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it supports extra whitespaces, it trims the strings. The final semicolon is not needed, it would introduce a next empty section:

ColumnPathToEncryptionPropertiesMap CryptoFactory::GetColumnEncryptionProperties(
int dek_length, const std::string& column_keys, FileKeyWrapper* key_wrapper) {
ColumnPathToEncryptionPropertiesMap encrypted_columns;
std::vector<::std::string_view> key_to_columns =
::arrow::internal::SplitString(column_keys, ';');
for (size_t i = 0; i < key_to_columns.size(); ++i) {
std::string cur_key_to_columns =
::arrow::internal::TrimString(std::string(key_to_columns[i]));
if (cur_key_to_columns.empty()) {
continue;
}
std::vector<::std::string_view> parts =
::arrow::internal::SplitString(cur_key_to_columns, ':');
if (parts.size() != 2) {
std::ostringstream message;
message << "Incorrect key to columns mapping in column keys property"
<< ": [" << cur_key_to_columns << "]";
throwParquetException(message.str());
}
std::string column_key_id = ::arrow::internal::TrimString(std::string(parts[0]));
if (column_key_id.empty()) {
throwParquetException("Empty key name in column keys property.");
}
std::string column_names_str = ::arrow::internal::TrimString(std::string(parts[1]));
std::vector<::std::string_view> column_names =
::arrow::internal::SplitString(column_names_str, ',');
if (0 == column_names.size()) {
throwParquetException("No columns to encrypt defined for key: " + column_key_id);
}
for (size_t j = 0; j < column_names.size(); ++j) {
std::string column_name =
::arrow::internal::TrimString(std::string(column_names[j]));
if (column_name.empty()) {
std::ostringstream message;
message << "Empty column name in column keys property for key: " << column_key_id;
throwParquetException(message.str());
}
if (encrypted_columns.find(column_name) != encrypted_columns.end()) {
throwParquetException("Multiple keys defined for the same column: " +
column_name);
}
std::string column_key(dek_length, '\0');
RandBytes(reinterpret_cast<uint8_t*>(column_key.data()), column_key.size());
std::string column_key_key_metadata =
key_wrapper->GetEncryptionKeyMetadata(column_key, column_key_id, false);
std::shared_ptr<ColumnEncryptionProperties> cmd =
ColumnEncryptionProperties::Builder(column_name)
.key(column_key)
->key_metadata(column_key_key_metadata)
->build();
encrypted_columns.insert({column_name, cmd});
}
}
if (encrypted_columns.empty()) {
throwParquetException("No column keys configured in column keys property.");
}
return encrypted_columns;
}

Comment threaddocs/source/cpp/parquet.rst Outdated

An example for writing a dataset using encrypted Parquet file format:

.. code-block:: cpp

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jorisvandenbossche@AlenkaF@raulcd What is our preferred policy for code examples? Do we put them inline in the docs? Do we use separate files?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: it seems we use literalinclude directives from C++ example files that are compiled as part of CI runs. See for example https://github.com/apache/arrow/blob/main/docs/source/cpp/dataset.rst#reading-datasets

Comment threaddocs/source/cpp/parquet.rst Outdated
Comment threaddocs/source/cpp/parquet.rst Outdated
Comment threaddocs/source/python/parquet.rst Outdated
Comment threaddocs/source/python/parquet.rst Outdated
@github-actionsgithub-actionsBot added awaiting committer review Awaiting committer review and removed awaiting review Awaiting review labels Feb 6, 2025
@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

There is some improvement for this non-intuitive naming scheme: #45462

@pitroupitrou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This LGTM except that we probably want to live in a separate file (see comment below).

@EnricoMi
EnricoMiforce-pushed the docs-column-encryption-nested-fields branch from 6e27abf to 30c6184CompareFebruary 12, 2025 15:29
@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou moved the C++ code into a file and referenced that from parquet.rst.

@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou addressed your comments, can this docs improvement be merged?

@pitrou

Copy link
Copy Markdown
Member

The CI failures seem unexpected, can you rebase/merge from latest git main?

@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

Rebased with latest main commit d88ef57.

@pitrou

Copy link
Copy Markdown
Member

Ok, the CI failures are certainly unrelated.

@pitrou

Copy link
Copy Markdown
Member

@github-actions crossbow submit docs-preview

@github-actions

Copy link
Copy Markdown
Unable to match any tasks for `docs-preview`
The Archery job run can be found at: https://github.com/apache/arrow/actions/runs/13717903134

@AlenkaF

Copy link
Copy Markdown
Member

@github-actions crossbow submit preview-docs

@github-actions

Copy link
Copy Markdown

Revision: 52fb219

Submitted crossbow builds: ursacomputing/crossbow @ actions-3c034c02a1

TaskStatus
preview-docsGitHub Actions

@AlenkaFAlenkaF left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AlenkaF
AlenkaF requested a review from pitrouMarch 18, 2025 12:16
@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou are you happy with this?

Comment threaddocs/source/cpp/parquet.rst Outdated
Comment threaddocs/source/cpp/parquet.rst Outdated
Comment on lines +620 to +622
Encrypting columns that have nested fields (struct, map, or even list data types)
requires column keys for the inner fields, not the column itself.
Configuring a column key for the column itself causes this error (here column name is ``col``):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Encrypting columns that have nested fields (struct, map, or even list data types)
requires column keys for the inner fields, not the column itself.
Configuring a column key for the column itself causes this error (here column name is ``col``):
Encrypting columns that have nested fields (struct, map or list data types)
requires column keys for the inner fields, not the nested column itself.
Configuring a column key for the nested column causes
this error (here the column name is ``col``):

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find nested column ambiguous, it sounds like the inner column, the nested field.

What about outer column instead of nested column?

Suggested change
Encrypting columns that have nested fields (struct, map, or even list data types)
requires column keys for the inner fields, not the column itself.
Configuring a column key for the column itself causes this error (here column name is ``col``):
Encrypting columns that have nested fields (struct, map, or even list data types)
requires column keys for the inner fields, not the outer column itself.
Configuring a column key for the outer column causes this error (here column name is ``col``):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, that's better!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also two wording nits:

  • "struct, map, or even list data types" -> "struct, map, or list data types" (there's nothing special about lists)
  • "here column name is" -> "here the column name is"

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, missed those of your changes when creating my suggestion. All incorporated now.

Comment threaddocs/source/cpp/parquet.rst Outdated
Comment threaddocs/source/python/parquet.rst Outdated
Comment threaddocs/source/python/parquet.rst Outdated
)

.. note::
Encrypting columns that have nested fields (for instance struct, map, or even list data types)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(same suggestions as for C++)

// specific language governing permissions and limitations
// under the License.

#include <arrow/util/logging.h>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO We should avoid using this header in public examples. std::cerr is good enough here.

#include "arrow/result.h"
#include "parquet/arrow/reader.h"

#include <arrow/filesystem/path_util.h>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move this include together with other Arrow includes above?

Comment threadcpp/examples/arrow/parquet_column_encryption.cc
EnricoMiand others added 2 commits March 25, 2025 14:21
Co-authored-by: Antoine Pitrou <pitrou@free.fr>
@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou all comments addressed, one alternative suggested at #45411 (comment)

#include "arrow/api.h"
#include "arrow/dataset/file_parquet.h"
#include "arrow/dataset/parquet_encryption_config.h"
#include "arrow/filesystem//localfs.h"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small nit

Suggested change
#include"arrow/filesystem//localfs.h"
#include"arrow/filesystem/localfs.h"

@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou comments addressed

@pitroupitrou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for doing this @EnricoMi !

@pitrou

Copy link
Copy Markdown
Member

@github-actions crossbow submit preview-docs

@github-actions

Copy link
Copy Markdown

Revision: b47168d

Submitted crossbow builds: ursacomputing/crossbow @ actions-ea617ad0a3

TaskStatus
preview-docsGitHub Actions

@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

Thank you for your time, @pitrou!

@AlenkaF
AlenkaF merged commit 5d0149d into apache:mainMar 26, 2025
@AlenkaFAlenkaF removed the awaiting committer review Awaiting committer review label Mar 26, 2025
@conbench-apache-arrow

Copy link
Copy Markdown

After merging your PR, Conbench analyzed the 0 benchmarking runs that have been run so far on merge-commit 5d0149d.

None of the specified runs were found on the Conbench server.

The full Conbench report has more details.

zanmato1984 pushed a commit to zanmato1984/arrow that referenced this pull request Apr 15, 2025
… for nested fields (apache#45411)
### Rationale for this change
Encrypting columns with nested fields with a column key is not trivial since only leaf fields are allowed in the column key map. Documentation emphasizes this fact and provides examples.
### What changes are included in this PR?
This amends the documentation on encryption for C++ and Python.
### Are these changes tested?
Only documentation.
### Are there any user-facing changes?
Only documentation.
* GitHub Issue: apache#41246
Lead-authored-by: Enrico Minack <github@enrico.minack.dev>
Co-authored-by: Antoine Pitrou <pitrou@free.fr>
Signed-off-by: AlenkaF <frim.alenka@gmail.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@EnricoMi@pitrou@AlenkaF
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

GH-41246: [Docs][C++][Python] Improve docs on column encryption for nested fields - #45411

Merged
AlenkaF merged 16 commits into
apache:mainfrom
EnricoMi:docs-column-encryption-nested-fields
Mar 26, 2025
Merged

GH-41246: [Docs][C++][Python] Improve docs on column encryption for nested fields#45411
AlenkaF merged 16 commits into
apache:mainfrom
EnricoMi:docs-column-encryption-nested-fields

Conversation

@EnricoMi

@EnricoMiEnricoMi commented Feb 1, 2025

Copy link
Copy Markdown
Collaborator

Rationale for this change

Encrypting columns with nested fields with a column key is not trivial since only leaf fields are allowed in the column key map. Documentation emphasizes this fact and provides examples.

What changes are included in this PR?

This amends the documentation on encryption for C++ and Python.

Are these changes tested?

Only documentation.

Are there any user-facing changes?

Only documentation.

@github-actions

Copy link
Copy Markdown

⚠️ GitHub issue #41246has been automatically assigned in GitHub to PR creator.

@EnricoMi
EnricoMiforce-pushed the docs-column-encryption-nested-fields branch from 543bd7b to 56e803dCompareFebruary 1, 2025 16:58

@pitroupitrou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for noticing and document this @EnricoMi . Here are assorted comments.

Comment threaddocs/source/cpp/parquet.rst Outdated
Comment on lines +658 to +661
encryption_config->column_keys = "column_key_name: "
"ListColumn.list.element, "
"MapColumn.key_value.key, MapColumn.key_value.value, "
"StructColumn.f1, StructColumn.f2"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are the spaces embedded in the string actually supported? Also, it seems to lack a semicolon at the end of the line.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it supports extra whitespaces, it trims the strings. The final semicolon is not needed, it would introduce a next empty section:

ColumnPathToEncryptionPropertiesMap CryptoFactory::GetColumnEncryptionProperties(
int dek_length, const std::string& column_keys, FileKeyWrapper* key_wrapper) {
ColumnPathToEncryptionPropertiesMap encrypted_columns;
std::vector<::std::string_view> key_to_columns =
::arrow::internal::SplitString(column_keys, ';');
for (size_t i = 0; i < key_to_columns.size(); ++i) {
std::string cur_key_to_columns =
::arrow::internal::TrimString(std::string(key_to_columns[i]));
if (cur_key_to_columns.empty()) {
continue;
}
std::vector<::std::string_view> parts =
::arrow::internal::SplitString(cur_key_to_columns, ':');
if (parts.size() != 2) {
std::ostringstream message;
message << "Incorrect key to columns mapping in column keys property"
<< ": [" << cur_key_to_columns << "]";
throwParquetException(message.str());
}
std::string column_key_id = ::arrow::internal::TrimString(std::string(parts[0]));
if (column_key_id.empty()) {
throwParquetException("Empty key name in column keys property.");
}
std::string column_names_str = ::arrow::internal::TrimString(std::string(parts[1]));
std::vector<::std::string_view> column_names =
::arrow::internal::SplitString(column_names_str, ',');
if (0 == column_names.size()) {
throwParquetException("No columns to encrypt defined for key: " + column_key_id);
}
for (size_t j = 0; j < column_names.size(); ++j) {
std::string column_name =
::arrow::internal::TrimString(std::string(column_names[j]));
if (column_name.empty()) {
std::ostringstream message;
message << "Empty column name in column keys property for key: " << column_key_id;
throwParquetException(message.str());
}
if (encrypted_columns.find(column_name) != encrypted_columns.end()) {
throwParquetException("Multiple keys defined for the same column: " +
column_name);
}
std::string column_key(dek_length, '\0');
RandBytes(reinterpret_cast<uint8_t*>(column_key.data()), column_key.size());
std::string column_key_key_metadata =
key_wrapper->GetEncryptionKeyMetadata(column_key, column_key_id, false);
std::shared_ptr<ColumnEncryptionProperties> cmd =
ColumnEncryptionProperties::Builder(column_name)
.key(column_key)
->key_metadata(column_key_key_metadata)
->build();
encrypted_columns.insert({column_name, cmd});
}
}
if (encrypted_columns.empty()) {
throwParquetException("No column keys configured in column keys property.");
}
return encrypted_columns;
}

Comment threaddocs/source/cpp/parquet.rst Outdated

An example for writing a dataset using encrypted Parquet file format:

.. code-block:: cpp

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jorisvandenbossche@AlenkaF@raulcd What is our preferred policy for code examples? Do we put them inline in the docs? Do we use separate files?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: it seems we use literalinclude directives from C++ example files that are compiled as part of CI runs. See for example https://github.com/apache/arrow/blob/main/docs/source/cpp/dataset.rst#reading-datasets

Comment threaddocs/source/cpp/parquet.rst Outdated
Comment threaddocs/source/cpp/parquet.rst Outdated
Comment threaddocs/source/python/parquet.rst Outdated
Comment threaddocs/source/python/parquet.rst Outdated
@github-actionsgithub-actionsBot added awaiting committer review Awaiting committer review and removed awaiting review Awaiting review labels Feb 6, 2025
@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

There is some improvement for this non-intuitive naming scheme: #45462

@pitroupitrou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This LGTM except that we probably want to live in a separate file (see comment below).

@EnricoMi
EnricoMiforce-pushed the docs-column-encryption-nested-fields branch from 6e27abf to 30c6184CompareFebruary 12, 2025 15:29
@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou moved the C++ code into a file and referenced that from parquet.rst.

@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou addressed your comments, can this docs improvement be merged?

@pitrou

Copy link
Copy Markdown
Member

The CI failures seem unexpected, can you rebase/merge from latest git main?

@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

Rebased with latest main commit d88ef57.

@pitrou

Copy link
Copy Markdown
Member

Ok, the CI failures are certainly unrelated.

@pitrou

Copy link
Copy Markdown
Member

@github-actions crossbow submit docs-preview

@github-actions

Copy link
Copy Markdown
Unable to match any tasks for `docs-preview`
The Archery job run can be found at: https://github.com/apache/arrow/actions/runs/13717903134

@AlenkaF

Copy link
Copy Markdown
Member

@github-actions crossbow submit preview-docs

@github-actions

Copy link
Copy Markdown

Revision: 52fb219

Submitted crossbow builds: ursacomputing/crossbow @ actions-3c034c02a1

TaskStatus
preview-docsGitHub Actions

@AlenkaFAlenkaF left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AlenkaF
AlenkaF requested a review from pitrouMarch 18, 2025 12:16
@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou are you happy with this?

Comment threaddocs/source/cpp/parquet.rst Outdated
Comment threaddocs/source/cpp/parquet.rst Outdated
Comment on lines +620 to +622
Encrypting columns that have nested fields (struct, map, or even list data types)
requires column keys for the inner fields, not the column itself.
Configuring a column key for the column itself causes this error (here column name is ``col``):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Encrypting columns that have nested fields (struct, map, or even list data types)
requires column keys for the inner fields, not the column itself.
Configuring a column key for the column itself causes this error (here column name is ``col``):
Encrypting columns that have nested fields (struct, map or list data types)
requires column keys for the inner fields, not the nested column itself.
Configuring a column key for the nested column causes
this error (here the column name is ``col``):

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find nested column ambiguous, it sounds like the inner column, the nested field.

What about outer column instead of nested column?

Suggested change
Encrypting columns that have nested fields (struct, map, or even list data types)
requires column keys for the inner fields, not the column itself.
Configuring a column key for the column itself causes this error (here column name is ``col``):
Encrypting columns that have nested fields (struct, map, or even list data types)
requires column keys for the inner fields, not the outer column itself.
Configuring a column key for the outer column causes this error (here column name is ``col``):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, that's better!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also two wording nits:

  • "struct, map, or even list data types" -> "struct, map, or list data types" (there's nothing special about lists)
  • "here column name is" -> "here the column name is"

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, missed those of your changes when creating my suggestion. All incorporated now.

Comment threaddocs/source/cpp/parquet.rst Outdated
Comment threaddocs/source/python/parquet.rst Outdated
Comment threaddocs/source/python/parquet.rst Outdated
)

.. note::
Encrypting columns that have nested fields (for instance struct, map, or even list data types)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(same suggestions as for C++)

// specific language governing permissions and limitations
// under the License.

#include <arrow/util/logging.h>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO We should avoid using this header in public examples. std::cerr is good enough here.

#include "arrow/result.h"
#include "parquet/arrow/reader.h"

#include <arrow/filesystem/path_util.h>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move this include together with other Arrow includes above?

Comment threadcpp/examples/arrow/parquet_column_encryption.cc
EnricoMiand others added 2 commits March 25, 2025 14:21
Co-authored-by: Antoine Pitrou <pitrou@free.fr>
@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou all comments addressed, one alternative suggested at #45411 (comment)

#include "arrow/api.h"
#include "arrow/dataset/file_parquet.h"
#include "arrow/dataset/parquet_encryption_config.h"
#include "arrow/filesystem//localfs.h"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small nit

Suggested change
#include"arrow/filesystem//localfs.h"
#include"arrow/filesystem/localfs.h"

@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

@pitrou comments addressed

@pitroupitrou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for doing this @EnricoMi !

@pitrou

Copy link
Copy Markdown
Member

@github-actions crossbow submit preview-docs

@github-actions

Copy link
Copy Markdown

Revision: b47168d

Submitted crossbow builds: ursacomputing/crossbow @ actions-ea617ad0a3

TaskStatus
preview-docsGitHub Actions

@EnricoMi

Copy link
Copy Markdown
CollaboratorAuthor

Thank you for your time, @pitrou!

@AlenkaF
AlenkaF merged commit 5d0149d into apache:mainMar 26, 2025
@AlenkaFAlenkaF removed the awaiting committer review Awaiting committer review label Mar 26, 2025
@conbench-apache-arrow

Copy link
Copy Markdown

After merging your PR, Conbench analyzed the 0 benchmarking runs that have been run so far on merge-commit 5d0149d.

None of the specified runs were found on the Conbench server.

The full Conbench report has more details.

zanmato1984 pushed a commit to zanmato1984/arrow that referenced this pull request Apr 15, 2025
… for nested fields (apache#45411)
### Rationale for this change
Encrypting columns with nested fields with a column key is not trivial since only leaf fields are allowed in the column key map. Documentation emphasizes this fact and provides examples.
### What changes are included in this PR?
This amends the documentation on encryption for C++ and Python.
### Are these changes tested?
Only documentation.
### Are there any user-facing changes?
Only documentation.
* GitHub Issue: apache#41246
Lead-authored-by: Enrico Minack <github@enrico.minack.dev>
Co-authored-by: Antoine Pitrou <pitrou@free.fr>
Signed-off-by: AlenkaF <frim.alenka@gmail.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@EnricoMi@pitrou@AlenkaF